完成调查问卷

This commit is contained in:
Zy
2026-04-15 17:32:03 +08:00
parent 40f13be704
commit cf9a1f1585
25 changed files with 2325 additions and 254 deletions

View File

@@ -1,55 +1,25 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' } as any"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<div class="w-full p-2">
<el-row :gutter="24">
<el-col :lg="24" :xs="24">
<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" :inline="true">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
<el-form-item label="员工账号" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入员工账号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" />
<el-form-item label="员工姓名" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入员工姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable>
<el-select v-model="queryParams.status" placeholder="员工状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -97,14 +67,20 @@
<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[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<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[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column v-if="columns[0].visible" key="userId" label="员工编号" align="center" prop="userId" />
<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[2].visible" key="nickName" label="员工姓名" align="center" prop="nickName" :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>
<el-switch
v-model="scope.row.status"
active-text="启用"
active-value="0"
inactive-value="1"
inactive-text="停用"
@change="handleStatusChange(scope.row)"
></el-switch>
</template>
</el-table-column>
@@ -114,7 +90,7 @@
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="180" class-name="small-padding fixed-width">
<el-table-column align="center" label="操作" fixed="right" width="400">
<template #default="scope">
<el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top">
<el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
@@ -145,26 +121,20 @@
</el-col>
</el-row>
<!-- 添加或修改用户配置对话框 -->
<!-- 添加或修改员工配置对话框 -->
<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 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="deptId">
<el-tree-select
v-model="form.deptId"
:data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' } as any"
value-key="id"
placeholder="请选择归属部门"
check-strictly
@change="handleDeptChange"
/>
<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>
@@ -182,19 +152,19 @@
</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 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 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-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>
@@ -209,19 +179,6 @@
</el-col>
</el-row>
<el-row>
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:label="item.postName"
:value="item.postId"
:disabled="item.status == '1'"
></el-option>
</el-select>
</el-form-item>
</el-col>
<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="请选择">
@@ -252,7 +209,7 @@
</template>
</el-dialog>
<!-- 用户导入对话框 -->
<!-- 员工导入对话框 -->
<el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
<el-upload
ref="uploadRef"
@@ -272,7 +229,7 @@
<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>
<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>
@@ -291,14 +248,14 @@
<script setup name="User" lang="ts">
import api from '@/api/system/user';
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { DeptTreeVO, DeptVO } from '@/api/system/dept/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 { optionselect } from '@/api/system/post';
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';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -311,32 +268,32 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptTreeVO[]>([]);
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
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: 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: [] },
@@ -382,21 +339,21 @@ const initData: PageData<UserForm, UserQuery> = {
},
rules: {
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{ required: true, message: '员工账号不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
message: '用户名称长度必须介于 2 和 20 之间',
message: '员工账号长度必须介于 2 和 20 之间',
trigger: 'blur'
}
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
nickName: [{ required: true, message: '员工姓名不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{ required: true, message: '员工密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
message: '员工密码长度必须介于 5 和 20 之间',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
@@ -415,29 +372,15 @@ const initData: PageData<UserForm, UserQuery> = {
trigger: 'blur'
}
],
roleIds: [{ required: true, 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 filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
deptTreeRef.value?.filter(deptName.value);
},
{
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发此属性控制在DOM元素更新后运行
}
);
/** 查询用户列表 */
/** 查询员工列表 */
const getList = async () => {
loading.value = true;
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
@@ -446,32 +389,14 @@ const getList = async () => {
total.value = res.total;
};
/** 查询部门下拉树结构 */
const getDeptTree = async () => {
const res = await api.deptTreeSelect();
deptOptions.value = res.data;
enabledDeptOptions.value = filterDisabledDept(res.data);
/** 查询小区列表 */
const getViliageList = async () => {
const res = await getCommunitylistAPI();
vilageList.value = res.rows;
};
/** 过滤禁用的部门 */
const filterDisabledDept = (deptList: DeptTreeVO[]) => {
return deptList.filter((dept) => {
if (dept.disabled) {
return false;
}
if (dept.children && dept.children.length) {
dept.children = filterDisabledDept(dept.children);
}
return true;
});
};
/** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id;
handleQuery();
};
function handleChangeVillage(val: string[] | number[]) {
form.value.villageIds = val.join(',');
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
@@ -490,7 +415,7 @@ const resetQuery = () => {
/** 删除按钮操作 */
const handleDelete = async (row?: UserVO) => {
const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any);
const [err] = await to(proxy?.$modal.confirm('是否确认删除员工编号为"' + userIds + '"的数据项?') as any);
if (!err) {
await api.delUser(userIds);
await getList();
@@ -498,11 +423,11 @@ const handleDelete = async (row?: UserVO) => {
}
};
/** 用户状态修改 */
/** 员工状态修改 */
const handleStatusChange = async (row: UserVO) => {
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"员工吗?');
await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + '成功');
} catch (err) {
@@ -523,7 +448,7 @@ const handleResetPwd = async (row: UserVO) => {
cancelButtonText: '取消',
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
inputErrorMessage: '员工密码长度必须介于 5 和 20 之间',
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return '不能包含非法字符:< > " \' \\ |';
@@ -546,7 +471,7 @@ const handleSelectionChange = (selection: UserVO[]) => {
/** 导入按钮操作 */
const handleImport = () => {
upload.title = '用户导入';
upload.title = '员工导入';
upload.open = true;
};
/** 导出按钮操作 */
@@ -600,7 +525,7 @@ const handleAdd = async () => {
reset();
const { data } = await api.getUser();
dialog.visible = true;
dialog.title = '新增用户';
dialog.title = '新增员工';
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.password = initPassword.value.toString();
@@ -612,12 +537,10 @@ const handleUpdate = async (row?: UserForm) => {
const userId = row?.userId || ids.value[0];
const { data } = await api.getUser(userId);
dialog.visible = true;
dialog.title = '修改用户';
dialog.title = '修改员工';
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = Array.from(
new Map([...data.roles, ...data.user.roles].map(role => [role.roleId, role])).values()
);
roleOptions.value = Array.from(new Map([...data.roles, ...data.user.roles].map((role) => [role.roleId, role])).values());
form.value.postIds = data.postIds;
form.value.roleIds = data.roleIds;
form.value.password = '';
@@ -646,7 +569,7 @@ const submitForm = () => {
};
/**
* 关闭用户弹窗
* 关闭员工弹窗
*/
const closeDialog = () => {
dialog.visible = false;
@@ -664,7 +587,7 @@ const resetForm = () => {
form.value.status = '1';
};
onMounted(() => {
getDeptTree(); // 初始化部门数据
getViliageList(); // 初始化部门数据
getList(); // 初始化列表数据
proxy?.getConfigKey('sys.user.initPassword').then((response) => {
initPassword.value = response.data;
@@ -672,8 +595,6 @@ onMounted(() => {
});
async function handleDeptChange(value: number | string) {
const response = await optionselect(value);
postOptions.value = response.data;
form.value.postIds = [];
console.log(value);
}
</script>