Commit 3daeffde authored by 罗林杰's avatar 罗林杰

用户界面修改

parent 7546b3a2
......@@ -11,9 +11,9 @@ import {
RoleListGetResultModel,
} from '../../demo/model/systemModel';
import { defHttp } from '@/utils/http/axios';
import {UploadFileParams} from "#/axios";
import {AxiosProgressEvent} from "axios";
import {UploadApiResult} from "@/api/sys/model/uploadModel";
import { UploadFileParams } from '#/axios';
import { AxiosProgressEvent } from 'axios';
import { UploadApiResult } from '@/api/sys/model/uploadModel';
enum Api {
addUser = '/system/user/add',
......@@ -34,15 +34,17 @@ export const getAccountList = (params: AccountParams) =>
/** 用户列表导出*/
export const exportUserList = (params: AccountParams) =>
defHttp.get<AccountListGetResultModel>({ url: Api.exportApi, params , responseType: 'blob'});
defHttp.get<AccountListGetResultModel>({ url: Api.exportApi, params, responseType: 'blob' });
/** 用户导入模板下载*/
export const downImportTemplate = () =>
defHttp.get<any>({ url: Api.importTemplateApi, responseType: 'blob'});
defHttp.get<any>({ url: Api.importTemplateApi, responseType: 'blob' });
/** 用户导入*/
export const userImport = (params: UploadFileParams,
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,) =>
export const userImport = (
params: UploadFileParams,
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
) =>
defHttp.uploadFile<UploadApiResult>(
{
url: Api.userImportApi,
......@@ -52,7 +54,7 @@ export const userImport = (params: UploadFileParams,
);
/** 用户删除*/
export const deleteUser = (params?: any) =>
defHttp.delete<any>({ url: Api.deleteUserApi +params.id });
defHttp.delete<any>({ url: Api.deleteUserApi + params.id });
/** 用户add*/
export const addUserApi = (params: any) =>
......@@ -63,8 +65,7 @@ export const addUserApi = (params: any) =>
});
/** 获取用户详情信息*/
export const UserDetailApi = (params: any) =>
defHttp.get<any>({ url: Api.userDetail + params, });
export const UserDetailApi = (params: any) => defHttp.get<any>({ url: Api.userDetail + params });
/** 用户信息编辑*/
export const UserUpdataApi = (params: any) =>
......
......@@ -61,11 +61,34 @@ export const DictRoute: AppRouteRecordRaw = {
],
};
export const UserRoute: AppRouteRecordRaw = {
path: '/user',
name: 'User',
component: LAYOUT,
meta: {
title: '用户管理',
icon: '',
hidden: true,
currentActiveMenu: '/system/user',
},
children: [
{
path: 'detail',
name: 'Detail',
component: () => import('@/views/system/user/AccountDetail.vue'),
meta: {
title: '用户详情',
icon: '',
},
},
],
};
// Basic routing without permission
// 没有权限要求的基本路由
export const basicRoutes = [
LoginRoute,
DictRoute,
UserRoute,
// RootRoute,
...mainOutRoutes,
REDIRECT_ROUTE,
......
<template>
<PageWrapper
:title="`用户` + userId + `的资料`"
content="这是用户资料详情页面。本页面仅用于演示相同路由在tab中打开多个页面并且显示不同的数据"
title="用户详情"
:content="`账号:${userName}&nbsp;&nbsp;&nbsp;&nbsp;
姓名:${name}&nbsp;&nbsp;&nbsp;&nbsp;
邮箱:${email}&nbsp;&nbsp;&nbsp;&nbsp;
创建时间:${createDate}&nbsp;&nbsp;&nbsp;&nbsp;
所属机构:${dept}`"
class="content-padding"
contentBackground
@back="goBack"
>
<template #extra>
<a-button type="primary" danger> 禁用账号 </a-button>
<a-button type="primary"> 修改密码 </a-button>
</template>
<template #footer>
<a-tabs default-active-key="detail" v-model:activeKey="currentKey">
<a-tab-pane key="detail" tab="用户资料" />
<a-tab-pane key="logs" tab="操作日志" />
<a-tabs default-active-key="global" v-model:activeKey="currentKey">
<a-tab-pane key="global" tab="全局角色" />
<a-tab-pane key="group" tab="工作组角色" />
</a-tabs>
</template>
<div class="pt-4 m-4 desc-wrap">
<template v-if="currentKey == 'detail'">
<div v-for="i in 10" :key="i">这是用户{{ userId }}资料Tab</div>
<template v-if="currentKey == 'global'">
<BasicTable @register="registerTable" :searchInfo="searchInfo">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:delete-outlined',
color: 'error',
label: '',
popConfirm: {
title: '是否确认删除',
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
</template>
<template v-if="currentKey == 'logs'">
<div v-for="i in 10" :key="i">这是用户{{ userId }}操作日志Tab</div>
<template v-if="currentKey == 'group'">
<BasicTable @register="registerGroupTable" :searchInfo="searchInfo">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
icon: 'ant-design:delete-outlined',
color: 'error',
label: '',
popConfirm: {
title: '是否确认删除',
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
</template>
<AccountModal @register="registerModal" @success="handleSuccess" />
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { reactive, ref } from 'vue';
import { useRoute } from 'vue-router';
import { PageWrapper } from '@/components/Page';
import { useGo } from '@/hooks/web/usePage';
import { useTabs } from '@/hooks/web/useTabs';
import { Tabs } from 'ant-design-vue';
import { BasicTable, TableAction, useTable } from '@/components/Table';
import { deleteUser, getAccountList } from '@/api/system/user/user';
import { useMessage } from '@/hooks/web/useMessage';
import { useModal } from '@/components/Modal';
import AccountModal from '@/views/system/user/AccountModal.vue';
import {
detailColumns,
groupColumns,
searchDetailFormSchema,
} from '@/views/system/user/account.data';
defineOptions({ name: 'AccountDetail' });
const [registerTable, { reload, updateTableDataRecord, getForm }] = useTable({
title: '角色列表',
api: getAccountList,
columns: detailColumns,
formConfig: {
labelWidth: 100,
schemas: searchDetailFormSchema,
},
useSearchForm: true,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
fixed: undefined,
},
});
const [registerGroupTable, { reloadGroup, getGroupForm }] = useTable({
title: '工作组角色列表',
api: getAccountList,
columns: groupColumns,
formConfig: {
labelWidth: 100,
schemas: searchDetailFormSchema,
},
useSearchForm: true,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 80,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
fixed: undefined,
},
});
const [registerModal, { openModal }] = useModal();
const searchInfo = reactive<Recordable>({});
const { createMessage } = useMessage();
const ATabs = Tabs;
const ATabPane = Tabs.TabPane;
const route = useRoute();
const go = useGo();
// 此处可以得到用户ID
const userId = ref(route.params?.id);
const currentKey = ref('detail');
const userName = ref(route.query.userName);
const name = ref(route.query.name);
const email = ref(route.query.email);
const createDate = ref(route.query.createDate);
const dept = ref(route.query.dept);
const currentKey = ref('global');
const { setTitle } = useTabs();
// TODO
// 本页代码仅作演示,实际应当通过userId从接口获得用户的相关资料
// 设置Tab的标题(不会影响页面标题)
setTitle('详情:用户' + userId.value);
setTitle('详情:用户' + userName.value);
/** 删除按钮*/
function handleDelete(record: Recordable) {
console.log(record);
deleteUser({ id: record.businessId });
createMessage.success('删除成功!');
reload();
}
/** 编辑按钮*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
}
function handleSuccess({ isUpdate, values }) {
if (isUpdate) {
// 演示不刷新表格直接更新内部数据。
// 注意:updateTableDataRecord要求表格的rowKey属性为string并且存在于每一行的record的keys中
const result = updateTableDataRecord(values.id, values);
console.log(result);
reload();
} else {
reload();
}
}
// 页面左侧点击返回链接时的操作
function goBack() {
// 本例的效果时点击返回始终跳转到账号列表页,实际应用时可返回上一页
go('/system/account');
go('/system/user');
}
</script>
......
import {getAllRoleList} from '@/api/system/role/role';
import { getAllRoleList } from '@/api/system/role/role';
import { BasicColumn, FormSchema } from '@/components/Table';
import {h} from "vue";
import {Tag} from "ant-design-vue";
import { h } from 'vue';
import { Switch } from 'ant-design-vue';
import {useMessage} from "@/hooks/web/useMessage";
import {changeFlagApi} from "@/api/system/user/user"; // 引入开关组件
import { useMessage } from '@/hooks/web/useMessage';
import { changeFlagApi } from '@/api/system/user/user';
// 引入开关组件
type CheckedType = boolean | string | number;
/**
* transform mock data
......@@ -30,7 +30,7 @@ export const deptMap = (() => {
export const columns: BasicColumn[] = [
{
title: '登录名',
title: '账号',
dataIndex: 'username',
width: 120,
},
......@@ -40,14 +40,14 @@ export const columns: BasicColumn[] = [
width: 120,
},
{
title: '手机号',
dataIndex: 'phone',
title: '邮箱',
dataIndex: 'email',
width: 120,
},
{
title: '状态',
dataIndex: 'flag',
width: 180,
width: 30,
customRender: ({ record }) => {
if (!Reflect.has(record, 'pendingStatus')) {
record.pendingStatus = false;
......@@ -64,12 +64,12 @@ export const columns: BasicColumn[] = [
const params = {
businessId: record.businessId,
flag: newStatus,
}
};
changeFlagApi(params)
.then(() => {
record.flag = newStatus;
const text = record.flag === '1' ? '启用' : '停用'
createMessage.success( text + `成功`);
const text = record.flag === '1' ? '启用' : '停用';
createMessage.success(text + `成功`);
})
.catch(() => {
// createMessage.error('操作失败');
......@@ -84,7 +84,7 @@ export const columns: BasicColumn[] = [
{
title: '创建时间',
dataIndex: 'createDate',
width: 200,
width: 140,
},
// {
// title: '所属部门',
......@@ -98,7 +98,7 @@ export const columns: BasicColumn[] = [
export const searchFormSchema: FormSchema[] = [
{
field: 'username',
label: '登录名',
label: '账号',
component: 'Input',
colProps: { span: 8 },
},
......@@ -126,18 +126,28 @@ export const resetPasswordFormSchema: any[] = [
required: true,
// ifShow: false,
},
]
const passwordCheck = (rule, value, callback) => {
const pattern = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/
if (!pattern.test(value)) {
callback(new Error('新密码必须为数字与字母的组合'))
];
function passwordCheck(rule, value, callback) {
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSpecialChar = /[~`!@#$%^&*()_\-={}\[\]\\|;:"'<,>.?\/]/.test(value);
// 检查密码组成
let count = 0;
if (hasUpperCase) count++;
if (hasLowerCase) count++;
if (hasNumber) count++;
if (hasSpecialChar) count++;
if (count < 3) {
return callback(new Error('密码中需至少包含大写字母、小写字母、数字及特殊字符中的 3 种'));
}
callback()
// 如果所有检查都通过,则调用callback无参数表示验证成功
callback();
}
export const accountFormSchema: any[] = [
{
field: 'username',
label: '登录名',
label: '账号',
component: 'Input',
// helpMessage: ['本字段演示异步验证', '不能输入带有admin的用户名'],
rules: [
......@@ -156,24 +166,22 @@ export const accountFormSchema: any[] = [
required: true,
message: '请输入用户密码',
},
{ min: 8, max: 16, message: '长度在 8 到 16 个字符', trigger: 'blur' },
{ min: 8, max: 25, message: '长度在 8 到 25 个字符', trigger: 'blur' },
{
validator: passwordCheck,
trigger: 'blur'
}
trigger: 'blur',
},
],
},
{
field: 'name',
label: '姓名',
component: 'Input',
},
{
field: 'nickName',
label: '昵称',
component: 'Input',
},
{
field: 'sex',
......@@ -195,8 +203,8 @@ export const accountFormSchema: any[] = [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
trigger: 'blur',
},
],
},
{
......@@ -207,9 +215,9 @@ export const accountFormSchema: any[] = [
{
required: false,
message: '请输入身份证号',
trigger: 'blur'
trigger: 'blur',
},
{ min: 18, max: 18, message: '长度18字符', trigger: 'blur' }
{ min: 18, max: 18, message: '长度18字符', trigger: 'blur' },
],
},
{
......@@ -221,8 +229,8 @@ export const accountFormSchema: any[] = [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change']
}
trigger: ['blur', 'change'],
},
],
},
{
......@@ -263,16 +271,43 @@ export const accountFormSchema: any[] = [
api: getAllRoleList,
labelField: 'roleName',
valueField: 'businessId',
resultField:'data',
resultField: 'data',
},
required: true,
},
];
export const searchDetailFormSchema: FormSchema[] = [
{
label: '备注',
field: 'remarks',
component: 'InputTextArea',
colProps: { lg: 24, md: 24 },
field: 'username',
label: '角色名称',
component: 'Input',
colProps: { span: 8 },
},
];
export const detailColumns: BasicColumn[] = [
{
title: '角色名称',
dataIndex: 'roleNames',
width: 200,
},
{
title: '角色描述',
dataIndex: 'phone',
width: 200,
},
];
export const groupColumns: BasicColumn[] = [
{
title: '工作区名称',
dataIndex: 'username',
width: 200,
},
{
title: '赋予角色',
dataIndex: 'phone',
width: 200,
},
];
<template>
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<DeptTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
<BasicTable @register="registerTable" class="w-3/4 xl:w-4/5" :searchInfo="searchInfo">
<!-- <DeptTree class="w-1/4 xl:w-1/5" @select="handleSelect" />-->
<!-- <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5" :searchInfo="searchInfo">-->
<BasicTable @register="registerTable" :searchInfo="searchInfo">
<template #toolbar>
<a-button type="primary" @click="handleCreate">新增</a-button>
<a-button type="primary" @click="handleExport">导出</a-button>
......@@ -12,19 +13,24 @@
<TableAction
:actions="[
{
// icon: 'clarity:note-edit-line',
label: '编辑',
icon: 'clarity:contract-line',
label: '',
onClick: handleView.bind(null, record),
},
{
icon: 'clarity:note-edit-line',
label: '',
onClick: handleEdit.bind(null, record),
},
{
// icon: 'clarity:note-edit-line',
label: '重置密码',
icon: 'clarity:sync-line',
label: '',
onClick: handleResetPassword.bind(null, record),
},
{
// icon: 'ant-design:delete-outlined',
icon: 'ant-design:delete-outlined',
color: 'error',
label: '删除',
label: '',
popConfirm: {
title: '是否确认删除',
placement: 'left',
......@@ -42,32 +48,30 @@
</PageWrapper>
</template>
<script lang="ts" setup>
import { reactive,unref,onDeactivated,onMounted } from 'vue';
import { reactive, onMounted } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import {getAccountList, deleteUser,exportUserList} from '@/api/system/user/user';
import { getAccountList, deleteUser, exportUserList } from '@/api/system/user/user';
import { PageWrapper } from '@/components/Page';
import DeptTree from './DeptTree.vue';
import { useMessage } from '@/hooks/web/useMessage';
import { useModal } from '@/components/Modal';
import AccountModal from './AccountModal.vue';
import importModal from './importModal.vue';
import resetPassword from './resetPassword.vue';
import { columns, searchFormSchema } from './account.data';
import { useGo } from '@/hooks/web/usePage';
import { downloadByData } from '@/utils/file/download';
import { useRoute,onBeforeRouteLeave } from 'vue-router';
import { useRoute, onBeforeRouteLeave } from 'vue-router';
import { useFilterStore } from '@/store/modules/filterData';
import {useUserStore} from "@/store/modules/user";
import { router } from '@/router';
defineOptions({ name: 'AccountManagement' });
const { createMessage } = useMessage();
const filterStore = useFilterStore();
const route = useRoute();
const go = useGo();
const [registerModal, { openModal }] = useModal();
const [registerResetPassword, { openModal: openResetPasswordModal }] = useModal();
const [registerImport, { openModal: openImportModal }] = useModal();
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, updateTableDataRecord, getSearchInfo,getForm }] = useTable({
const [registerTable, { reload, updateTableDataRecord, getSearchInfo, getForm }] = useTable({
title: '用户管理列表',
api: getAccountList,
rowKey: 'id',
......@@ -77,7 +81,7 @@
schemas: searchFormSchema,
autoSubmitOnEnter: true,
resetFunc: () => {
searchInfo.deptId=''
searchInfo.deptId = '';
},
},
useSearchForm: true,
......@@ -100,7 +104,7 @@
isUpdate: false,
});
}
/** 编辑按钮*/
/** 编辑按钮*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
......@@ -109,14 +113,13 @@
}
/** 重置密码按钮*/
function handleResetPassword(record: Recordable) {
openResetPasswordModal(true,{
record
})
openResetPasswordModal(true, {
record,
});
}
/** 导入按钮*/
function handleImport() {
openImportModal(true,{
})
openImportModal(true, {});
}
/** 重置密码弹窗确定按钮*/
/** 删除按钮*/
......@@ -128,7 +131,7 @@
}
/** 导出按钮*/
async function handleExport() {
const params = Object.assign({},getSearchInfo(),getForm().getFieldsValue());
const params = Object.assign({}, getSearchInfo(), getForm().getFieldsValue());
const data = await exportUserList(params);
downloadByData(data, '用户列表' + '.xlsx');
}
......@@ -148,43 +151,50 @@
reload();
}
}
/** 部门树的select*/
function handleSelect(deptId = '') {
searchInfo.deptId = deptId;
reload();
}
// /** 部门树的select*/
// function handleSelect(deptId = '') {
// searchInfo.deptId = deptId;
// reload();
// }
/** 查看按钮*/
function handleView(record: Recordable) {
go('/system/account_detail/' + record.id);
router.push({
path: '/user/detail',
query: {
userName: record.username,
name: record.name,
email: record.email,
createDate: record.createDate,
dept: record.deptName
},
});
}
onMounted(() => {
const path = route.path
if(filterStore.getSearchParams[path]) {
if(JSON.parse(filterStore.getSearchParams[path] !== {})){
const params = JSON.parse(filterStore.getSearchParams[path])
console.log('11111111111111111111111111111',params)
const path = route.path;
if (filterStore.getSearchParams[path]) {
if (JSON.parse(filterStore.getSearchParams[path] !== {})) {
const params = JSON.parse(filterStore.getSearchParams[path]);
console.log('11111111111111111111111111111', params);
getForm().setFieldsValue({
page: params.page,
pageSize: params.pageSize,
username: params.username,
flag: params.flag,
})
searchInfo.deptId = params.deptId
});
searchInfo.deptId = params.deptId;
}
}
});
onBeforeRouteLeave((to, from, next) => {
const params = Object.assign({},getSearchInfo(),getForm().getFieldsValue());
console.log('path',from.path)
console.log('params',params)
filterStore.setSearchParams(
{
path: from.path,
param: {
...params
}
}
)
const params = Object.assign({}, getSearchInfo(), getForm().getFieldsValue());
console.log('path', from.path);
console.log('params', params);
filterStore.setSearchParams({
path: from.path,
param: {
...params,
},
});
next(); // 允许导航
});
</script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment