Files
estate/src/views/system/user/index.vue

568 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="w-full h-full flex flex-col p-2">
<pageheader></pageheader>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" @submit.prevent="handleQuery" :inline="true">
<el-form-item label="员工姓名" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入员工姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card class="flex-1" shadow="hover">
<template #header>
<el-row :gutter="10">
<right-toolbar
:show-add="checkPermi(['system:user:add'])"
:show-delete="false"
:show-edit="false"
@update:add="handleAdd"
more-title="更多"
:columns="columns"
>
<!-- <template #dropdown>-->
<!-- <el-dropdown-item @click="importTemplate">下载模板</el-dropdown-item>-->
<!-- &lt;!&ndash; 注意 由于el-dropdown-item标签是延迟加载的 所以v-has-permi自定义标签不生效 需要使用v-if调用方法执行 &ndash;&gt;-->
<!-- <el-dropdown-item v-if="checkPermi(['system:user:import'])" @click="handleImport">导入数据</el-dropdown-item>-->
<!-- <el-dropdown-item v-if="checkPermi(['system:user:export'])" @click="handleExport">导出数据</el-dropdown-item>-->
<!-- </template>-->
</right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column v-if="columns[0].visible" key="userId" label="员工编号" align="center" prop="userId" />
<el-table-column v-if="columns[2].visible" key="nickName" label="员工姓名" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[1].visible" key="userName" label="员工账号" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column v-if="columns[6].visible" label="创建时间" align="center" prop="createTime" width="160">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="操作" fixed="right" width="200">
<template #default="scope">
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
<el-button v-has-permi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top">
<el-button v-has-permi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
<el-button v-has-permi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="分配角色" placement="top">
<el-button v-has-permi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改员工配置对话框 -->
<el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="员工姓名" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入员工姓名" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="管理小区" prop="villageIds">
<el-select @change="handleChangeVillage" v-model="villags" multiple placeholder="请选择管理小区">
<el-option v-for="item in vilageList" :key="item.villageId" :label="item.villageName" :value="item.villageId" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="员工账号" prop="userName">
<el-input v-model="form.userName" placeholder="请输入员工账号" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="员工密码" prop="password">
<el-input v-model="form.password" placeholder="请输入员工密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="员工性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="角色" prop="roleIds">
<el-select v-model="form.roleIds" filterable multiple placeholder="请选择">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
:disabled="item.status == '1'"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
</template>
</el-dialog>
<!-- 员工导入对话框 -->
<el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
:on-change="handleChangeFile"
drag
>
<el-icon class="el-icon--upload">
<i-ep-upload-filled />
</el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="text-center el-upload__tip">
<div class="el-upload__tip"><el-checkbox v-model="upload.updateSupport" />是否更新已经存在的员工数据</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="User" lang="ts">
import api from '@/api/system/user';
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { RoleVO } from '@/api/system/role/types';
import { PostVO } from '@/api/system/post/types';
import { globalHeaders } from '@/utils/request';
import { to } from 'await-to-js';
import { checkPermi } from '@/utils/permission';
import { useUserStore } from '@/store/modules/user';
import { getCommunitylistAPI } from '@/api/system/community';
import { communitylist_rows_type } from '@/api/system/community/type';
import { UploadRawFile } from 'element-plus';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const userList = ref<UserVO[]>();
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const vilageList = ref<communitylist_rows_type[]>([]);
const initPassword = ref<string>('');
const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]);
/*** 员工导入参数 */
const upload = reactive<ImportOption>({
// 是否显示弹出层(员工导入)
open: false,
// 弹出层标题(员工导入)
title: '',
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的员工数据
updateSupport: 0,
// 设置上传的请求头部
headers: globalHeaders(),
// 上传的地址
url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
});
// 列显隐信息
const columns = ref<FieldOption[]>([
{ key: 0, label: `员工编号`, visible: false, children: [] },
{ key: 1, label: `员工账号`, visible: true, children: [] },
{ key: 2, label: `员工姓名`, visible: true, children: [] },
{ key: 3, label: `部门`, visible: true, children: [] },
{ key: 4, label: `手机号码`, visible: true, children: [] },
{ key: 5, label: `状态`, visible: true, children: [] },
{ key: 6, label: `创建时间`, visible: true, children: [] }
]);
const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref<ElFormInstance>();
const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: UserForm = {
userId: undefined,
deptId: undefined,
userName: '',
nickName: undefined,
password: '',
villageIds: '',
phonenumber: undefined,
email: undefined,
sex: undefined,
status: '0',
remark: '',
postIds: [],
roleIds: []
};
const initData: PageData<UserForm, UserQuery> = {
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
userName: '',
villageIds: '',
phonenumber: '',
status: '',
deptId: '',
roleId: ''
},
rules: {
userName: [
{ required: true, message: '员工账号不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
message: '员工账号长度必须介于 2 和 20 之间',
trigger: 'blur'
}
],
nickName: [{ required: true, message: '员工姓名不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '员工密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
message: '员工密码长度必须介于 5 和 20 之间',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
],
email: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
],
phonenumber: [
{
pattern: /^1[3456789][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
],
roleIds: [{ required: true, message: '员工角色不能为空', trigger: 'blur' }]
}
};
const villags = ref([]);
const data = reactive<PageData<UserForm, UserQuery>>(initData);
const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data);
/** 查询员工列表 */
const getList = async () => {
loading.value = true;
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
loading.value = false;
userList.value = res.rows;
total.value = res.total;
};
/** 查询小区列表 */
const getViliageList = async () => {
const res = await getCommunitylistAPI();
vilageList.value = res.rows;
};
function handleChangeVillage(val: string[] | number[]) {
form.value.villageIds = val.join(',');
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
deptTreeRef.value?.setCurrentKey(undefined);
handleQuery();
};
/** 删除按钮操作 */
const handleDelete = async (row?: UserVO) => {
const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除?') as any);
if (!err) {
await api.delUser(userIds);
await getList();
proxy?.$modal.msgSuccess('删除成功');
}
};
/** 员工状态修改 */
const handleStatusChange = async (row: UserVO) => {
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"员工吗?');
await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + '成功');
} catch (err) {
row.status = row.status === '0' ? '1' : '0';
}
};
/** 跳转角色分配 */
const handleAuthRole = (row: UserVO) => {
const userId = row.userId;
router.push('/system/user-auth/role/' + userId);
};
/** 重置密码按钮操作 */
const handleResetPwd = async (row: UserVO) => {
const [err, res] = await to(
ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: '员工密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\ |';
}
}
})
);
if (!err && res) {
await api.resetUserPwd(row.userId, res.value);
proxy?.$modal.msgSuccess('修改成功');
}
};
/** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 导入按钮操作 */
const handleImport = () => {
upload.title = '员工导入';
upload.open = true;
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'system/user/export',
{
...queryParams.value
},
`user_${new Date().getTime()}.xlsx`
);
};
/** 下载模板操作 */
const importTemplate = () => {
proxy?.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
};
/**文件上传中处理 */
const handleFileUploadProgress = () => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response: any, file: UploadFile) => {
upload.open = false;
upload.isUploading = false;
uploadRef.value?.handleRemove(file);
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
dangerouslyUseHTMLString: true
});
getList();
};
const forbidSuffix = ['exe', 'msi', 'bat', 'cmd', 'sh', 'apk', 'dmg', 'zip', 'rar', '7z', 'tar', 'gz'];
const handleChangeFile = (uploadFile: UploadFile) => {
const type = uploadFile.raw.name.split('.')[1];
if (forbidSuffix.includes(type)) {
ElMessage.warning('不允许上传exe、安装包等文件');
return;
}
};
/** 提交上传文件 */
function submitFileForm() {
uploadRef.value?.submit();
}
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
userFormRef.value?.resetFields();
};
/** 取消按钮 */
const cancel = () => {
dialog.visible = false;
reset();
};
/** 新增按钮操作 */
const handleAdd = () => {
router.push({
path: '/system/addUser'
});
return;
};
/** 修改按钮操作 */
const handleUpdate = (row?: UserForm) => {
const userId = row?.userId || ids.value[0];
router.push({
path: '/system/editUser',
query: {
id: userId
}
});
return;
};
/** 提交按钮 */
const submitForm = () => {
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (form.value.userId) {
// 自己编辑自己的情况下 不允许编辑角色部门岗位
if (form.value.userId == useUserStore().userId) {
form.value.roleIds = null;
form.value.deptId = null;
form.value.postIds = null;
}
await api.updateUser(form.value);
} else {
await api.addUser(form.value);
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/**
* 关闭员工弹窗
*/
const closeDialog = () => {
dialog.visible = false;
resetForm();
};
/**
* 重置表单
*/
const resetForm = () => {
userFormRef.value?.resetFields();
userFormRef.value?.clearValidate();
form.value.id = undefined;
form.value.status = '1';
};
onMounted(() => {
getViliageList(); // 初始化部门数据
getList(); // 初始化列表数据
proxy?.getConfigKey('sys.user.initPassword').then((response) => {
initPassword.value = response.data;
});
});
</script>
<style lang="scss" scoped>
.el-table {
height: calc(100% - 80px);
}
</style>