|
@@ -0,0 +1,444 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="full-height page-container">
|
|
|
|
|
+ <LayoutSeparate :show-line="false" first-size="280px" class="full-height">
|
|
|
|
|
+ <template #first>
|
|
|
|
|
+ <div class="full-height dept-container">
|
|
|
|
|
+ <SysDeptTree async show-search @select="handleDeptSelected" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #second>
|
|
|
|
|
+ <SmartTable @register="registerTable" :size="getTableSize">
|
|
|
|
|
+ <template #table-operation="{ row }">
|
|
|
|
|
+ <SmartVxeTableAction :actions="getTableActions(row)" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #table-userType="{ row }">
|
|
|
|
|
+ <span>
|
|
|
|
|
+ {{ getUserTypeMap[row.userType] }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #search-userType="{ model, size }">
|
|
|
|
|
+ <a-select style="width: 100px" :size="size" v-model:value="model.userType" allowClear>
|
|
|
|
|
+ <a-select-option
|
|
|
|
|
+ v-for="item in userTypeListRef"
|
|
|
|
|
+ :key="'userType_' + item.dictItemCode"
|
|
|
|
|
+ :value="item.dictItemCode"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ item.dictItemName }}
|
|
|
|
|
+ </a-select-option>
|
|
|
|
|
+ </a-select>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #table-accountStatus="{ row }">
|
|
|
|
|
+ <a-tooltip :title="getLockedMessage(row.userAccount?.accountStatus)">
|
|
|
|
|
+ <span
|
|
|
|
|
+ :style="{
|
|
|
|
|
+ color: getAccountData(row.userAccount?.accountStatus).color,
|
|
|
|
|
+ fontWeight: 'bold',
|
|
|
|
|
+ }"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ getAccountData(row.userAccount?.accountStatus).label }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </a-tooltip>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </SmartTable>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </LayoutSeparate>
|
|
|
|
|
+ <UserAccountUpdateModal @register="registerAccountModal" />
|
|
|
|
|
+ <UserSetRole @register="registerSetRoleModal" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+ import { computed, ref, unref } from 'vue';
|
|
|
|
|
+ import { useI18n } from '@/hooks/web/useI18n';
|
|
|
|
|
+
|
|
|
|
|
+ import { useLoadDictItem } from '@/modules/system/hooks/SysDictHooks';
|
|
|
|
|
+ import { useSizeSetting } from '@/hooks/setting/UseSizeSetting';
|
|
|
|
|
+ import { hasPermission } from '@/utils/auth';
|
|
|
|
|
+ import { useModal } from '@/components/Modal';
|
|
|
|
|
+
|
|
|
|
|
+ import { LayoutSeparate } from '@/components/LayoutSeparate';
|
|
|
|
|
+ import SysDeptTree from '@/modules/system/components/SysDept/SysDeptTree.vue';
|
|
|
|
|
+ import { useMessage } from '@/hooks/web/useMessage';
|
|
|
|
|
+
|
|
|
|
|
+ import {
|
|
|
|
|
+ SmartTable,
|
|
|
|
|
+ useSmartTable,
|
|
|
|
|
+ SmartVxeTableAction,
|
|
|
|
|
+ ActionItem,
|
|
|
|
|
+ } from '@/components/SmartTable';
|
|
|
|
|
+ import UserAccountUpdateModal from './account/UserAccountUpdateModal.vue';
|
|
|
|
|
+ import UserSetRole from './components/UserSetRole.vue';
|
|
|
|
|
+
|
|
|
|
|
+ import { getAddEditFormSchemas, getSearchSchemas, getTableColumns } from './UserListView.config';
|
|
|
|
|
+ import {
|
|
|
|
|
+ listApi,
|
|
|
|
|
+ deleteApi,
|
|
|
|
|
+ saveUpdateWithDataScopeApi,
|
|
|
|
|
+ getByIdWithDataScopeApi,
|
|
|
|
|
+ setUseYnApi,
|
|
|
|
|
+ createAccountApi,
|
|
|
|
|
+ unlockUserAccountApi,
|
|
|
|
|
+ resetPassword,
|
|
|
|
|
+ } from './UserListView.api';
|
|
|
|
|
+ import { SYS_USER_TYPE, SystemPermissions } from '@/modules/system/constants/SystemConstants';
|
|
|
|
|
+
|
|
|
|
|
+ const { t } = useI18n();
|
|
|
|
|
+ const { warnMessage, errorMessage, createConfirm, successMessage } = useMessage();
|
|
|
|
|
+ const { getTableSize } = useSizeSetting();
|
|
|
|
|
+
|
|
|
|
|
+ const { dictData: userTypeListRef } = useLoadDictItem(ref('SYSTEM_USER_TYPE'));
|
|
|
|
|
+ const getUserTypeMap = computed(() => {
|
|
|
|
|
+ const result: { [index: string]: string } = {};
|
|
|
|
|
+ result[SYS_USER_TYPE] = '系统用户';
|
|
|
|
|
+ for (let userType of unref(userTypeListRef)) {
|
|
|
|
|
+ result[userType.dictItemCode] = userType.dictItemName;
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const accountLockedMessage = {
|
|
|
|
|
+ LOGIN_FAIL_LOCKED: '多次登录失败锁定',
|
|
|
|
|
+ LONG_TIME_LOCKED: '超出指定时间未登录锁定',
|
|
|
|
|
+ LONG_TIME_PASSWORD_MODIFY_LOCKED: '超出指定时间未修改密码锁定',
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const [registerSetRoleModal, { openModal: openSetRoleModal }] = useModal();
|
|
|
|
|
+
|
|
|
|
|
+ const getLockedMessage = (status: string | null | undefined) => {
|
|
|
|
|
+ if (!status || status === 'NORMAL') {
|
|
|
|
|
+ return '正常';
|
|
|
|
|
+ }
|
|
|
|
|
+ return accountLockedMessage[status];
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 账户状态
|
|
|
|
|
+ */
|
|
|
|
|
+ const accountStatusMap = {
|
|
|
|
|
+ empty: {
|
|
|
|
|
+ label: '未创建',
|
|
|
|
|
+ color: '#A9A9A9',
|
|
|
|
|
+ },
|
|
|
|
|
+ NORMAL: {
|
|
|
|
|
+ label: '正常',
|
|
|
|
|
+ color: '#228B22',
|
|
|
|
|
+ },
|
|
|
|
|
+ LOCKED: {
|
|
|
|
|
+ label: '锁定',
|
|
|
|
|
+ color: 'red',
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const getAccountData = (status: string | null | undefined) => {
|
|
|
|
|
+ if (status === undefined || status === null) {
|
|
|
|
|
+ return accountStatusMap.empty;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status === 'NORMAL') {
|
|
|
|
|
+ return accountStatusMap.NORMAL;
|
|
|
|
|
+ }
|
|
|
|
|
+ return accountStatusMap.LOCKED;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 权限处理
|
|
|
|
|
+ */
|
|
|
|
|
+ const permissions = SystemPermissions.user;
|
|
|
|
|
+ const hasPermissionUpdateSystemUser = hasPermission('sys:systemUser:update');
|
|
|
|
|
+ const hasSystemUserUpdate = (type: string) => {
|
|
|
|
|
+ return hasPermissionUpdateSystemUser || type !== SYS_USER_TYPE;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 选中组织架构操作
|
|
|
|
|
+ * @param selectedKeys
|
|
|
|
|
+ */
|
|
|
|
|
+ const currentDeptId = ref<number | null>(null);
|
|
|
|
|
+ const handleDeptSelected = (selectedKeys: Array<number>) => {
|
|
|
|
|
+ if (selectedKeys.length > 0) {
|
|
|
|
|
+ currentDeptId.value = selectedKeys[0];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ currentDeptId.value = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 重新加载数据
|
|
|
|
|
+ query();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 账户弹窗
|
|
|
|
|
+ */
|
|
|
|
|
+ const [registerAccountModal, { openModal }] = useModal();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * table行按钮
|
|
|
|
|
+ */
|
|
|
|
|
+ const getTableActions = (row): ActionItem[] => {
|
|
|
|
|
+ return [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: t('common.button.edit'),
|
|
|
|
|
+ onClick: () => editByRowModal(row),
|
|
|
|
|
+ disabled: !hasPermission(permissions.update) || !hasSystemUserUpdate(row.userType),
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: t('system.views.user.button.showAccount'),
|
|
|
|
|
+ disabled: !hasPermission('sys:account:query') || !hasSystemUserUpdate(row.userType),
|
|
|
|
|
+ onClick: () => openModal(true, row),
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: t('system.views.user.button.unlockUserAccount'),
|
|
|
|
|
+ auth: permissions.unlockUserAccount,
|
|
|
|
|
+ disabled:
|
|
|
|
|
+ !hasPermission(permissions.unlockUserAccount) ||
|
|
|
|
|
+ (row.userAccount && row.userAccount.accountStatus === 'NORMAL'),
|
|
|
|
|
+ onClick: () => handleUnlockUserAccount(row.userId),
|
|
|
|
|
+ },
|
|
|
|
|
+ ];
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleUnlockUserAccount = (id: number) => {
|
|
|
|
|
+ createConfirm({
|
|
|
|
|
+ iconType: 'warning',
|
|
|
|
|
+ content: t('system.views.user.message.confirmUnlockUserAccount'),
|
|
|
|
|
+ onOk: async () => {
|
|
|
|
|
+ await unlockUserAccountApi(id);
|
|
|
|
|
+ successMessage(t('system.views.user.message.unlockUserAccountSuccess'));
|
|
|
|
|
+ await query();
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 用户操作验证
|
|
|
|
|
+ * @param userList
|
|
|
|
|
+ */
|
|
|
|
|
+ const validateOperateUser = (userList: Array<any>) => {
|
|
|
|
|
+ if (userList.length === 0) {
|
|
|
|
|
+ warnMessage({
|
|
|
|
|
+ message: t('system.views.user.validate.selectUser'),
|
|
|
|
|
+ });
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!hasPermissionUpdateSystemUser) {
|
|
|
|
|
+ // 如果没有修改系统用户的权限,判断用户中是否有系统用户
|
|
|
|
|
+ const hasSysUser = userList.some(({ userType }: any) => userType === SYS_USER_TYPE);
|
|
|
|
|
+ if (hasSysUser) {
|
|
|
|
|
+ errorMessage(t('system.views.user.validate.noSysUserUpdatePermission'));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建账户
|
|
|
|
|
+ */
|
|
|
|
|
+ const handleCreateAccount = () => {
|
|
|
|
|
+ const userList = getCheckboxRecords(false);
|
|
|
|
|
+ if (userList.length === 0) {
|
|
|
|
|
+ warnMessage({
|
|
|
|
|
+ message: t('system.views.user.validate.selectUser'),
|
|
|
|
|
+ });
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!hasPermissionUpdateSystemUser) {
|
|
|
|
|
+ // 如果没有修改系统用户的权限,判断用户中是否有系统用户
|
|
|
|
|
+ const hasSysUser = userList.some(({ userType }: any) => userType === SYS_USER_TYPE);
|
|
|
|
|
+ if (hasSysUser) {
|
|
|
|
|
+ errorMessage(t('system.views.user.validate.noSysUserUpdatePermission'));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 判断是否有停用用户
|
|
|
|
|
+ const hasNoUse = userList.some((item) => item.useYn === false);
|
|
|
|
|
+ if (hasNoUse) {
|
|
|
|
|
+ warnMessage(t('system.views.user.message.noUseUserNotCreateAccount'));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ createConfirm({
|
|
|
|
|
+ iconType: 'warning',
|
|
|
|
|
+ title: t('system.views.user.validate.createAccountConfirm'),
|
|
|
|
|
+ onOk: () => createAccountApi(userList),
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const [
|
|
|
|
|
+ registerTable,
|
|
|
|
|
+ { editByRowModal, getCheckboxRecords, query, deleteByCheckbox, showAddModal },
|
|
|
|
|
+ ] = useSmartTable({
|
|
|
|
|
+ columns: getTableColumns(),
|
|
|
|
|
+ stripe: true,
|
|
|
|
|
+ height: 'auto',
|
|
|
|
|
+ border: true,
|
|
|
|
|
+ align: 'left',
|
|
|
|
|
+ rowConfig: {
|
|
|
|
|
+ isHover: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ pagerConfig: true,
|
|
|
|
|
+ useSearchForm: true,
|
|
|
|
|
+ sortConfig: {
|
|
|
|
|
+ remote: true,
|
|
|
|
|
+ defaultSort: {
|
|
|
|
|
+ field: 'seq',
|
|
|
|
|
+ order: 'asc',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ searchFormConfig: {
|
|
|
|
|
+ layout: 'inline',
|
|
|
|
|
+ schemas: getSearchSchemas(t),
|
|
|
|
|
+ colon: true,
|
|
|
|
|
+ searchWithSymbol: true,
|
|
|
|
|
+ actionColOptions: {
|
|
|
|
|
+ span: undefined,
|
|
|
|
|
+ },
|
|
|
|
|
+ compact: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ addEditConfig: {
|
|
|
|
|
+ modalConfig: {
|
|
|
|
|
+ width: '700px',
|
|
|
|
|
+ },
|
|
|
|
|
+ formConfig: {
|
|
|
|
|
+ colon: true,
|
|
|
|
|
+ schemas: getAddEditFormSchemas(t, userTypeListRef),
|
|
|
|
|
+ labelCol: {
|
|
|
|
|
+ span: 4,
|
|
|
|
|
+ },
|
|
|
|
|
+ wrapperCol: {
|
|
|
|
|
+ span: 19,
|
|
|
|
|
+ },
|
|
|
|
|
+ baseColProps: {
|
|
|
|
|
+ span: 24,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ proxyConfig: {
|
|
|
|
|
+ ajax: {
|
|
|
|
|
+ query: ({ ajaxParameter }) => {
|
|
|
|
|
+ const parameter = {
|
|
|
|
|
+ ...ajaxParameter,
|
|
|
|
|
+ };
|
|
|
|
|
+ const deptId = unref(currentDeptId);
|
|
|
|
|
+ if (deptId) {
|
|
|
|
|
+ parameter.deptIdList = [deptId];
|
|
|
|
|
+ }
|
|
|
|
|
+ return listApi(parameter);
|
|
|
|
|
+ },
|
|
|
|
|
+ delete: deleteApi,
|
|
|
|
|
+ save: saveUpdateWithDataScopeApi,
|
|
|
|
|
+ getById: getByIdWithDataScopeApi,
|
|
|
|
|
+ useYn: setUseYnApi,
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ toolbarConfig: {
|
|
|
|
|
+ refresh: true,
|
|
|
|
|
+ resizable: true,
|
|
|
|
|
+ buttons: [
|
|
|
|
|
+ {
|
|
|
|
|
+ code: 'ModalAdd',
|
|
|
|
|
+ auth: permissions.add,
|
|
|
|
|
+ props: {
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ showAddModal({ deptId: unref(currentDeptId) });
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: t('system.views.user.button.createAccount'),
|
|
|
|
|
+ customRender: 'ant',
|
|
|
|
|
+ auth: permissions.createAccount,
|
|
|
|
|
+ props: {
|
|
|
|
|
+ onClick: () => handleCreateAccount(),
|
|
|
|
|
+ type: 'primary',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ code: 'delete',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ const userList = getCheckboxRecords(false);
|
|
|
|
|
+ // 验证用户
|
|
|
|
|
+ const result = validateOperateUser(userList);
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 验证是否包含系统用户
|
|
|
|
|
+ const sysUserValidate = userList.some((item: any) => item.userType === SYS_USER_TYPE);
|
|
|
|
|
+ if (sysUserValidate) {
|
|
|
|
|
+ errorMessage(t('system.views.user.validate.sysUserNoDelete'));
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 执行删除操作
|
|
|
|
|
+ deleteByCheckbox();
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ code: 'useYnTrue',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ code: 'useYnFalse',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: t('system.views.user.button.resetPassword'),
|
|
|
|
|
+ auth: permissions.unlockPassword,
|
|
|
|
|
+ customRender: 'ant',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ type: 'primary',
|
|
|
|
|
+ preIcon: 'ant-design:unlock-outlined',
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ const selectRows = getCheckboxRecords(false);
|
|
|
|
|
+ if (selectRows.length !== 1) {
|
|
|
|
|
+ warnMessage('请选择一条数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ createConfirm({
|
|
|
|
|
+ iconType: 'warning',
|
|
|
|
|
+ title: '重置密码',
|
|
|
|
|
+ content: '确定要重置密码吗?',
|
|
|
|
|
+ onOk: async () => {
|
|
|
|
|
+ const newPassword = await resetPassword(selectRows[0].userId);
|
|
|
|
|
+ createConfirm({
|
|
|
|
|
+ iconType: 'warning',
|
|
|
|
|
+ title: '请保存密码',
|
|
|
|
|
+ content: newPassword,
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: t('system.views.user.button.setRole'),
|
|
|
|
|
+ auth: permissions.setRole,
|
|
|
|
|
+ customRender: 'ant',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ type: 'primary',
|
|
|
|
|
+ preIcon: 'ant-design:team-outlined',
|
|
|
|
|
+ onClick: () => {
|
|
|
|
|
+ const selectRows = getCheckboxRecords(false);
|
|
|
|
|
+ if (selectRows.length !== 1) {
|
|
|
|
|
+ warnMessage('请选择一条数据');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ openSetRoleModal(true, { userId: selectRows[0].userId });
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="less">
|
|
|
|
|
+ .page-container {
|
|
|
|
|
+ :deep(.smart-search-container) {
|
|
|
|
|
+ .ant-col {
|
|
|
|
|
+ //padding: 0 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .dept-container {
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ background: white;
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|