feat base

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Ironsp
2026-05-19 09:43:15 +08:00
parent 415540ffc4
commit 8b85e1c441
6 changed files with 663 additions and 374 deletions

View File

@@ -7,9 +7,9 @@
<el-input v-model="form.name" /> <el-input v-model="form.name" />
</el-form-item> </el-form-item>
<el-form-item label="系统图标" prop="logo"> <el-form-item label="系统图标" prop="logo">
<el-upload v-model:file-list="fileList" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" list-type="picture-card"> <div class="text-center">
<el-icon><Plus /></el-icon> <userAvatar />
</el-upload> </div>
<br /> <br />
<p>只支持.jpg.png格式,最多上传1张单张大小不超过500kb</p> <p>只支持.jpg.png格式,最多上传1张单张大小不超过500kb</p>
</el-form-item> </el-form-item>
@@ -23,6 +23,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UserAvatar from '@/views/system/user/profile/userAvatar.vue';
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import type { UploadProps, UploadUserFile } from 'element-plus'; import type { UploadProps, UploadUserFile } from 'element-plus';
const form = reactive({ const form = reactive({

View File

@@ -1,78 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
// 查询菜单列表
export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
return request({
url: '/system/menu/list',
method: 'get',
params: query
});
};
// 查询菜单详细
export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
return request({
url: '/system/menu/' + menuId,
method: 'get'
});
};
// 查询菜单下拉树结构
export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
return request({
url: '/system/menu/treeselect',
method: 'get'
});
};
// 根据角色ID查询菜单下拉树结构
export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMenuTree> => {
return request({
url: '/system/menu/roleMenuTreeselect/' + roleId,
method: 'get'
});
};
// 根据角色ID查询菜单下拉树结构
export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise<RoleMenuTree> => {
return request({
url: '/system/menu/tenantPackageMenuTreeselect/' + packageId,
method: 'get'
});
};
// 新增菜单
export const addMenu = (data: MenuForm) => {
return request({
url: '/system/menu',
method: 'post',
data: data
});
};
// 修改菜单
export const updateMenu = (data: MenuForm) => {
return request({
url: '/system/menu',
method: 'put',
data: data
});
};
// 删除菜单
export const delMenu = (menuId: string | number) => {
return request({
url: '/system/menu/' + menuId,
method: 'delete'
});
};
// 级联删除菜单
export const cascadeDelMenu = (menuIds: Array<string | number>) => {
return request({
url: '/system/menu/cascade/' + menuIds,
method: 'delete'
});
};

View File

@@ -1,69 +0,0 @@
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
/**
* 菜单树形结构类型
*/
export interface MenuTreeOption {
id: string | number;
label: string;
parentId: string | number;
weight: number;
children?: MenuTreeOption[];
}
export interface RoleMenuTree {
menus: MenuTreeOption[];
checkedKeys: string[];
}
/**
* 菜单查询参数类型
*/
export interface MenuQuery {
keywords?: string;
menuName?: string;
status?: string;
}
/**
* 菜单视图对象类型
*/
export interface MenuVO extends BaseEntity {
parentName: string;
parentId: string | number;
children: MenuVO[];
menuId: string | number;
menuName: string;
orderNum: number;
path: string;
component: string;
queryParam: string;
isFrame: string;
isCache: string;
menuType: MenuTypeEnum;
visible: string;
status: string;
icon: string;
remark: string;
}
export interface MenuForm {
parentName?: string;
parentId?: string | number;
children?: MenuForm[];
menuId?: string | number;
menuName: string;
orderNum: number;
path: string;
component?: string;
queryParam?: string;
isFrame?: string;
isCache?: string;
menuType?: MenuTypeEnum;
visible?: string;
status?: string;
icon?: string;
remark?: string;
query?: string;
perms?: string;
}

View File

@@ -1,91 +1,92 @@
<template> <template>
<el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" status-icon :rules="rules" label-width="auto" class="demo-ruleForm"> <div class="base">
<el-form-item label="Password" prop="pass"> <div class="base-content">
<el-input v-model="ruleForm.pass" type="password" autocomplete="off" /> <el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
</el-form-item> </el-form-item>
<el-form-item label="Confirm" prop="checkPass"> <el-form-item label="新密码" prop="newPassword">
<el-input v-model="ruleForm.checkPass" type="password" autocomplete="off" /> <el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
</el-form-item> </el-form-item>
<el-form-item label="Age" prop="age"> <el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model.number="ruleForm.age" /> <el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)"> Submit </el-button> <el-button type="primary" @click="submit">保存</el-button>
<el-button @click="resetForm(ruleFormRef)">Reset</el-button> <el-button type="danger" @click="close">关闭</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script setup lang="ts">
import { reactive, ref } from 'vue'; import { updateUserPwd } from '@/api/system/user';
import type { ResetPwdForm } from '@/api/system/user/types';
import type { FormInstance, FormRules } from 'element-plus'; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pwdRef = ref<ElFormInstance>();
const ruleFormRef = ref<FormInstance>(); const user = ref<ResetPwdForm>({
oldPassword: '',
const checkAge = (rule: any, value: any, callback: any) => { newPassword: '',
if (!value) { confirmPassword: ''
return callback(new Error('Please input the age'));
}
setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error('Please input digits'));
} else {
if (value < 18) {
callback(new Error('Age must be greater than 18'));
} else {
callback();
}
}
}, 1000);
};
const validatePass = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('Please input the password'));
} else {
if (ruleForm.checkPass !== '') {
if (!ruleFormRef.value) return;
ruleFormRef.value.validateField('checkPass');
}
callback();
}
};
const validatePass2 = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('Please input the password again'));
} else if (value !== ruleForm.pass) {
callback(new Error("Two inputs don't match!"));
} else {
callback();
}
};
const ruleForm = reactive({
pass: '',
checkPass: '',
age: ''
}); });
const rules = reactive<FormRules<typeof ruleForm>>({ const equalToPassword = (rule: any, value: string, callback: any) => {
pass: [{ validator: validatePass, trigger: 'blur' }], if (user.value.newPassword !== value) {
checkPass: [{ validator: validatePass2, trigger: 'blur' }], callback(new Error('两次输入的密码不一致'));
age: [{ validator: checkAge, trigger: 'blur' }] } else {
callback();
}
};
const rules = ref({
oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }],
newPassword: [
{ required: true, message: '新密码不能为空', trigger: 'blur' },
{
min: 6,
max: 20,
message: '长度在 6 到 20 个字符',
trigger: 'blur'
},
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\ |', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{
required: true,
validator: equalToPassword,
trigger: 'blur'
}
]
}); });
const submitForm = (formEl: FormInstance | undefined) => { /** 提交按钮 */
if (!formEl) return; const submit = () => {
formEl.validate((valid) => { pwdRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
console.log('submit!'); await updateUserPwd(user.value.oldPassword, user.value.newPassword);
} else { proxy?.$modal.msgSuccess('修改成功');
console.log('error submit!');
} }
}); });
}; };
/** 关闭按钮 */
const resetForm = (formEl: FormInstance | undefined) => { const close = () => {
if (!formEl) return; proxy?.$tab.closePage();
formEl.resetFields();
}; };
</script> </script>
<style lang="scss" scoped>
.base {
margin: 0 auto;
padding: 20px;
height: 100vh;
width: 100vw;
background-color: #fff;
.base-content {
width: 800px;
margin: 0 auto;
padding: 20px;
}
}
</style>

View File

@@ -18,7 +18,7 @@
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10" class="flex justify-end mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button> <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col> </el-col>

View File

@@ -1,12 +1,54 @@
<template> <template>
<div class="p-2"> <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>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名" prop="equipmentNo"> <el-form-item label="用户名" prop="userName">
<el-input v-model="queryParams.testKey" placeholder="请输入" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<!-- <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-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-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -20,147 +62,413 @@
<template #header> <template #header>
<el-row :gutter="10" class="flex justify-end mb8"> <el-row :gutter="10" class="flex justify-end mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">添加</el-button> <el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col> </el-col>
<!-- <el-col :span="1.5"> <!-- <el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" <el-button v-has-permi="['system:user:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
>批量唤醒</el-button 修改
> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" <el-button v-has-permi="['system:user:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
>批量删除</el-button 删除
> </el-button>
</el-col>
<el-col :span="1.5">
<el-dropdown class="mt-[1px]">
<el-button plain type="info">
更多
<el-icon class="el-icon--right"><arrow-down /></el-icon
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="importTemplate">下载模板</el-dropdown-item> -->
<!-- 注意 由于el-dropdown-item标签是延迟加载的 所以v-has-permi自定义标签不生效 需要使用v-if调用方法执行 -->
<!-- <el-dropdown-item v-if="checkPermi(['system:user:import'])" icon="Top" @click="handleImport">导入数据</el-dropdown-item>
<el-dropdown-item v-if="checkPermi(['system:user:export'])" icon="Download" @click="handleExport">导出数据</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col> --> </el-col> -->
<!-- <el-col :span="1.5"> <!-- <right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar> -->
<el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
</el-col> -->
<!-- <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> -->
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" border :data="demoList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="50" align="center" />
<el-table-column v-if="true" label="用户" align="center" prop="id" /> <el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column label="用户账号" align="center" prop="deptId" /> <el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column 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 label="排序号" align="center" prop="orderNum" /> <el-table-column v-if="columns[3].visible" key="deptName" label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column label="操作人" align="center" prop="testKey" /> <el-table-column v-if="columns[4].visible" key="phonenumber" label="手机号码" align="center" prop="phonenumber" width="120" />
<el-table-column label="添加时间" align="center" prop="value" /> <el-table-column v-if="columns[5].visible" key="status" label="状态" align="center">
<el-table-column label="排序" align="center" prop="value" />
<el-table-column label="状态" align="center" prop="value" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template #default="scope"> <template #default="scope">
<el-button v-hasPermi="['demo:demo:edit']" type="primary" @click="handleUpdate(scope.row)">编辑</el-button> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
<el-button v-hasPermi="['demo:demo:remove']" type="primary" @click="handleDelete(scope.row)">删除</el-button> </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 label="操作" fixed="right" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<!-- <el-tooltip v-if="scope.row.userId !== 1" content="修改" placement="top"> -->
<el-button v-hasPermi="['system:user:edit']" type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
<!-- </el-tooltip>
<el-tooltip v-if="scope.row.userId !== 1" content="删除" placement="top"> -->
<el-button v-hasPermi="['system:user:remove']" type="primary" @click="handleDelete(scope.row)">删除</el-button>
<!-- </el-tooltip> -->
<!-- <el-tooltip v-if="scope.row.userId !== 1" content="重置密码" placement="top">
<el-button v-hasPermi="['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-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
</el-tooltip> -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" /> <pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card> </el-card>
<!-- 添加或修改测试单对话框 --> </el-col>
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body> </el-row>
<el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="部门id" prop="deptId"> <!-- 添加或修改用户配置对话框 -->
<el-input v-model="form.deptId" placeholder="请输入部门id" /> <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-form-item>
<el-form-item label="用户id" prop="userId"> </el-col>
<el-input v-model="form.userId" placeholder="请输入用户id" /> <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> </el-form-item>
<el-form-item label="排序号" prop="orderNum"> </el-col>
<el-input v-model="form.orderNum" placeholder="请输入排序号" /> </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-form-item>
<el-form-item label="key键" prop="testKey"> </el-col>
<el-input v-model="form.testKey" placeholder="请输入key键" /> <el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item> </el-form-item>
<el-form-item label="值" prop="value"> </el-col>
<el-input v-model="form.value" placeholder="请输入值" /> </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-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="岗位">
<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="请选择">
<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> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button> <el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </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"
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> </div>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup name="Demo" lang="ts"> <script setup name="User" lang="ts">
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo'; import api from '@/api/system/user';
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types'; 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';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const demoList = ref<DemoVO[]>([]); const userList = ref<UserVO[]>();
const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<number | string>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptTreeVO[]>([]);
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
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 queryFormRef = ref<ElFormInstance>();
const demoFormRef = ref<ElFormInstance>(); const userFormRef = ref<ElFormInstance>();
const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: DemoForm = { const initFormData: UserForm = {
id: undefined,
deptId: undefined,
userId: undefined, userId: undefined,
orderNum: undefined, deptId: undefined,
testKey: undefined, userName: '',
value: undefined nickName: undefined,
password: '',
phonenumber: undefined,
email: undefined,
sex: undefined,
status: '0',
remark: '',
postIds: [],
roleIds: []
}; };
const data = reactive<PageData<DemoForm, DemoQuery>>({
const initData: PageData<UserForm, UserQuery> = {
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
deptId: undefined, userName: '',
userId: undefined, phonenumber: '',
orderNum: undefined, status: '',
testKey: undefined, deptId: '',
value: undefined roleId: ''
}, },
rules: { rules: {
id: [{ required: true, message: '主键不能为空', trigger: 'blur' }], userName: [
deptId: [{ required: true, message: '部门id不能为空', trigger: 'blur' }], { required: true, message: '用户名称不能为空', trigger: 'blur' },
userId: [{ required: true, message: '用户id不能为空', trigger: 'blur' }], {
orderNum: [{ required: true, message: '排序号不能为空', trigger: 'blur' }], min: 2,
testKey: [{ required: true, message: 'key键不能为空', trigger: 'blur' }], max: 20,
value: [{ required: true, message: '值不能为空', trigger: 'blur' }] 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 data = reactive<PageData<UserForm, UserQuery>>(initData);
const { queryParams, form, rules } = toRefs(data); 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 () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listDemo(queryParams.value); const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
demoList.value = res.rows;
total.value = res.total;
loading.value = false; loading.value = false;
userList.value = res.rows;
total.value = res.total;
}; };
/** 取消按钮 */ /** 查询部门下拉树结构 */
const cancel = () => { const getDeptTree = async () => {
reset(); const res = await api.deptTreeSelect();
dialog.visible = false; deptOptions.value = res.data;
enabledDeptOptions.value = filterDisabledDept(res.data);
}; };
/** 表单重置 */ /** 过滤禁用的部门 */
const reset = () => { const filterDisabledDept = (deptList: DeptTreeVO[]) => {
form.value = { ...initFormData }; return deptList.filter((dept) => {
demoFormRef.value?.resetFields(); 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();
}; };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@@ -168,75 +476,201 @@ const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
}; };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
deptTreeRef.value?.setCurrentKey(undefined);
handleQuery(); handleQuery();
}; };
/** 多选框选中数据 */ /** 删除按钮操作 */
const handleSelectionChange = (selection: DemoVO[]) => { const handleDelete = async (row?: UserVO) => {
ids.value = selection.map((item) => item.id); const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') 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('修改成功,新密码是:' + res.value);
}
};
/** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
}; };
/** 新增按钮操作 */ /** 导入按钮操作 */
const handleAdd = () => { 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();
};
/** 提交上传文件 */
function submitFileForm() {
uploadRef.value?.submit();
}
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
userFormRef.value?.resetFields();
};
/** 取消按钮 */
const cancel = () => {
dialog.visible = false;
reset(); reset();
};
/** 新增按钮操作 */
const handleAdd = async () => {
reset();
const { data } = await api.getUser();
dialog.visible = true; dialog.visible = true;
dialog.title = '添加测试单'; dialog.title = '新增用户';
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.password = initPassword.value.toString();
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: DemoVO) => { const handleUpdate = async (row?: UserForm) => {
reset(); reset();
const _id = row?.id || ids.value[0]; const userId = row?.userId || ids.value[0];
const res = await getDemo(_id); const { data } = await api.getUser(userId);
Object.assign(form.value, res.data);
dialog.visible = true; 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());
form.value.postIds = data.postIds;
form.value.roleIds = data.roleIds;
form.value.password = '';
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
demoFormRef.value?.validate(async (valid: boolean) => { userFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; if (form.value.userId) {
if (form.value.id) { // 自己编辑自己的情况下 不允许编辑角色部门岗位
await updateDemo(form.value).finally(() => (buttonLoading.value = false)); if (form.value.userId == useUserStore().userId) {
} else { form.value.roleIds = null;
await addDemo(form.value).finally(() => (buttonLoading.value = false)); form.value.deptId = null;
form.value.postIds = null;
} }
proxy?.$modal.msgSuccess('修改成功'); await api.updateUser(form.value);
} else {
await api.addUser(form.value);
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}); });
}; };
/** 删除按钮操作 */ /**
const handleDelete = async (row?: DemoVO) => { * 关闭用户弹窗
const _ids = row?.id || ids.value; */
await proxy?.$modal.confirm('是否确认删除测试单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); const closeDialog = () => {
await delDemo(_ids); dialog.visible = false;
proxy?.$modal.msgSuccess('删除成功'); resetForm();
await getList();
}; };
/** 导出按钮操作 */ /**
const handleExport = () => { * 重置表单
proxy?.download( */
'demo/demo/export', const resetForm = () => {
{ userFormRef.value?.resetFields();
...queryParams.value userFormRef.value?.clearValidate();
},
`demo_${new Date().getTime()}.xlsx`
);
};
form.value.id = undefined;
form.value.status = '1';
};
onMounted(() => { onMounted(() => {
getList(); getDeptTree(); // 初始化部门数据
getList(); // 初始化列表数据
proxy?.getConfigKey('sys.user.initPassword').then((response) => {
initPassword.value = response.data;
}); });
});
async function handleDeptChange(value: number | string) {
const response = await optionselect(value);
postOptions.value = response.data;
form.value.postIds = [];
}
</script> </script>