<script lang="ts" setup>
|
import type { TreeActionType } from '@jnpf/ui';
|
import type { ActionItem, BasicColumn, FormProps } from '@jnpf/ui/vxeTable';
|
|
import { computed, nextTick, onMounted, reactive, ref, unref } from 'vue';
|
import { useRouter } from 'vue-router';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { BasicLeftTree } from '@jnpf/ui';
|
import { useModal } from '@jnpf/ui/modal';
|
import { usePopup } from '@jnpf/ui/popup';
|
import { BasicVxeTable, TableAction, useVxeTable } from '@jnpf/ui/vxeTable';
|
|
import { StorageManager } from '@vben-core/shared/cache';
|
|
import { DownOutlined } from '@ant-design/icons-vue';
|
|
import { getOrganizeSyncList } from '#/api/permission/organize';
|
import { batchSetUserPosition, batchUpdateState, getUserList, getUserPosition, setUserPosition, unlockUser } from '#/api/permission/user';
|
import { getUserFieldConfig } from '#/api/system/sysConfig';
|
import { $t } from '#/locales';
|
import { useBaseStore } from '#/store';
|
import { normalizeUserFieldConfig } from '#/utils/userFieldConfig';
|
import ExportModal from '#/views/permission/common/components/ExportModal.vue';
|
import ImportModal from '#/views/permission/common/components/ImportModal.vue';
|
|
import Form from './Form.vue';
|
import PermissionPopup from './Permission.vue';
|
import ResetPassword from './ResetPassword.vue';
|
import SocialsBind from './SocialsBind.vue';
|
import WorkHandoverModal from './WorkHandoverModal.vue';
|
|
defineOptions({ name: 'PermissionUser' });
|
|
interface State {
|
treeData: any[];
|
treeLoading: boolean;
|
currentUserId: string;
|
currentPosId: any[];
|
}
|
|
const { createMessage, createConfirm } = useMessage();
|
const baseStore = useBaseStore();
|
const router = useRouter();
|
|
const ls = new StorageManager();
|
const leftTreeRef = ref<Nullable<TreeActionType>>(null);
|
const state = reactive<State>({
|
treeData: [],
|
treeLoading: false,
|
currentUserId: '',
|
currentPosId: [],
|
});
|
const searchInfo = reactive({
|
organizeId: '',
|
showSubOrganize: 1,
|
});
|
const postSelectRef = ref<any>('');
|
const batchPostSelectRef = ref<any>('');
|
const [registerPsdModal, { openModal: openPsdModal }] = useModal();
|
const [registerExportModal, { openModal: openExportModal }] = useModal();
|
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
const [registerWorkHandoverModal, { openModal: openWorkHandoverModal }] = useModal();
|
const [registerForm, { openPopup: openFormPopup }] = usePopup();
|
const [registerSocialsBind, { openPopup: openSocialsBindPopup }] = usePopup();
|
const [registerPermission, { openPopup: openPermissionPopup }] = usePopup();
|
const columns: BasicColumn[] = [
|
{ title: '账号', dataIndex: 'account', width: 100 },
|
{ title: '姓名', dataIndex: 'realName', width: 100 },
|
{ title: '工号', dataIndex: 'jobNumber', width: 120 },
|
{ title: '性别', dataIndex: 'gender', width: 90, align: 'center' },
|
{ title: '手机', dataIndex: 'mobilePhone', width: 120 },
|
{ title: '所属岗位', dataIndex: 'position', minWidth: 120 },
|
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
{ title: '更新时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
{ title: '禁用时间', dataIndex: 'disableTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
{ title: '状态', dataIndex: 'enabledMark', width: 70, align: 'center', slots: { default: 'enabledMark' } },
|
];
|
const visibleListFields = ref(columns.map((column) => column.dataIndex as string));
|
const [registerTable, { reload, getForm, getSelectRowKeys, clearSelectedRowKeys, getFetchParams, setColumns }] = useVxeTable({
|
api: getUserList,
|
columns: [],
|
useSearchForm: true,
|
formConfig: getFormConfig(),
|
actionColumn: {
|
width: 150,
|
title: '操作',
|
dataIndex: 'action',
|
},
|
rowSelection: { type: 'checkbox' },
|
clickToRowSelect: false,
|
showTableSetting: false,
|
});
|
|
const getSelectRowLen = computed(() => !!getSelectRowKeys().length);
|
const getTreeBindValue = computed(() => ({
|
showToolbar: false,
|
showSearch: false,
|
defaultExpandAll: false,
|
treeData: state.treeData,
|
loading: state.treeLoading,
|
loadData: onLoadData,
|
onSelect: handleTreeSelect,
|
}));
|
|
function getFormConfig(): Partial<FormProps> {
|
return {
|
schemas: [
|
{
|
field: 'keyword',
|
label: $t('common.keyword'),
|
component: 'Input',
|
componentProps: {
|
placeholder: $t('common.enterKeyword'),
|
submitOnPressEnter: true,
|
},
|
},
|
{
|
field: 'gender',
|
label: '性别',
|
component: 'Select',
|
componentProps: {
|
fieldNames: { value: 'enCode' },
|
},
|
},
|
{
|
field: 'enabledMark',
|
label: '状态',
|
component: 'Select',
|
componentProps: {
|
options: [
|
{ fullName: '启用', id: 1 },
|
{ fullName: '禁用', id: 0 },
|
{ fullName: '锁定', id: 2 },
|
],
|
},
|
},
|
],
|
};
|
}
|
function getTableActions(record): ActionItem[] {
|
return [
|
{
|
label: $t('common.editText'),
|
onClick: addOrUpdateHandle.bind(null, record.id),
|
},
|
];
|
}
|
function getDropDownActions(record): ActionItem[] {
|
return [
|
{
|
label: '重置密码',
|
onClick: handleResetPwd.bind(null, record.id, record.account),
|
},
|
{
|
label: '解除锁定',
|
ifShow: record.enabledMark === 2,
|
modelConfirm: {
|
title: '解除锁定',
|
content: '此操作将解除该账户锁定, 是否继续?',
|
onOk: handleUnlock.bind(null, record.id),
|
},
|
},
|
{
|
label: '社交账号',
|
ifShow: !!ls.getItem('useSocials'),
|
onClick: handleSocialsBind.bind(null, record.id),
|
},
|
{
|
disabled: record.enabledMark == 1 || record.enabledMark == 2,
|
label: '工作交接',
|
onClick: handleWorkHandover.bind(null, record),
|
},
|
{
|
label: '查看权限',
|
onClick: handlePermission.bind(null, record.id, record.realName),
|
},
|
{
|
label: '调整岗位',
|
onClick: handleAdjustPosition.bind(null, record.id),
|
},
|
];
|
}
|
function addOrUpdateHandle(id = '') {
|
openFormPopup(true, { id, groupId: '' });
|
}
|
function handleResetPwd(id, account) {
|
openPsdModal(true, { id, account });
|
}
|
function handleSocialsBind(id) {
|
openSocialsBindPopup(true, { id });
|
}
|
function handleWorkHandover(record) {
|
openWorkHandoverModal(true, { userInfo: record });
|
}
|
function handlePermission(id, fullName) {
|
openPermissionPopup(true, { id, fullName, objectType: 'user' });
|
}
|
function handleUnlock(id) {
|
unlockUser(id).then((res) => {
|
createMessage.success(res.msg);
|
reload();
|
});
|
}
|
function handleExport() {
|
const listQuery = {
|
...getFetchParams(),
|
keyword: getFetchParams().keyword || '',
|
};
|
const defaultExportFields = visibleListFields.value.map((field) => (field === 'position' ? 'positionId' : field));
|
openExportModal(true, { listQuery, defaultExportFields });
|
}
|
function handleImport() {
|
openImportModal(true, {});
|
}
|
function handleSync({ key }) {
|
router.push(`/integrationCenter/${key === 0 ? 'dingTalk' : 'weCom'}`);
|
}
|
function handleBatchUpdateState(enabledMark) {
|
const ids = getSelectRowKeys();
|
if (!ids.length) return createMessage.error($t('common.selectDataTip'));
|
createConfirm({
|
iconType: 'warning',
|
title: $t('common.tipTitle'),
|
content: `您确定要${enabledMark == 1 ? '启用' : enabledMark == 2 ? '锁定' : '禁用'}这些数据吗, 是否继续?`,
|
onOk: () => {
|
batchUpdateState({ ids, enabledMark }).then((res) => {
|
createMessage.success(res.msg);
|
clearSelectedRowKeys();
|
reload();
|
});
|
},
|
});
|
}
|
function handleTreeSelect(id, _node, nodePath) {
|
if (searchInfo.organizeId !== id) {
|
searchInfo.organizeId = id;
|
clearSelectedRowKeys();
|
getForm().resetFields();
|
}
|
const expandedKeys = nodePath.map((item) => item.id).filter(Boolean);
|
unref(leftTreeRef)?.setExpandedKeys(expandedKeys);
|
}
|
function onLoadData(node) {
|
return getOrganizeSyncList({ parentId: node.id }).then((res) => {
|
const children = (res.data.list || []).map((item) => ({ ...item, children: item.hasChildren ? [] : undefined }));
|
unref(leftTreeRef)?.updateNodeByKey(node.eventKey, { children, isLeaf: !children.length });
|
});
|
}
|
function initOrganizeList() {
|
state.treeLoading = true;
|
getOrganizeSyncList({ parentId: '0' })
|
.then((res) => {
|
state.treeData = res.data.list || [];
|
state.treeLoading = false;
|
})
|
.catch(() => {
|
state.treeLoading = false;
|
});
|
}
|
async function initOptions() {
|
const sexRes = await baseStore.getDictionaryData('sex');
|
getForm().updateSchema({ field: 'gender', componentProps: { options: sexRes } });
|
}
|
function handleBatchAdjustPosition() {
|
unref(batchPostSelectRef)?.openSelectModal();
|
}
|
function handleAdjustPosition(id) {
|
state.currentUserId = id;
|
getUserPosition(id).then((res) => {
|
const data = res.data || [];
|
state.currentPosId = data.flatMap((o) => o.id);
|
nextTick(() => unref(postSelectRef)?.openSelectModal());
|
});
|
}
|
function onAdjustPositionChange(ids) {
|
setUserPosition({ id: state.currentUserId, ids }).then((res) => {
|
reload();
|
createMessage.success(res.msg);
|
});
|
}
|
function onBatchAdjustPositionChange(ids) {
|
const userIds = getSelectRowKeys();
|
if (!userIds.length) return createMessage.error($t('common.selectDataTip'));
|
batchSetUserPosition({ userIds, ids }).then((res) => {
|
reload();
|
clearSelectedRowKeys();
|
createMessage.success(res.msg);
|
});
|
}
|
onMounted(async () => {
|
initOrganizeList();
|
initOptions();
|
try {
|
const res = await getUserFieldConfig();
|
const config = normalizeUserFieldConfig(res.data);
|
const visibleColumns = columns.filter((column) => config.list[column.dataIndex as string]?.visible);
|
visibleListFields.value = visibleColumns.map((column) => column.dataIndex as string);
|
setColumns(visibleColumns);
|
} catch {
|
setColumns(columns);
|
createMessage.warning('用户字段配置加载失败,已显示默认列表');
|
}
|
});
|
</script>
|
<template>
|
<div class="relative flex h-full flex-col">
|
<a-alert show-icon type="warning" class="mb-[10px]">
|
<template #message>
|
组织、权限模块的详细说明介绍,请查阅使用帮助,<a class="link-text" target="_blank" href="https://usermanuals.jnpfsoft.com/go_organizational_design">
|
点击查看
|
</a>
|
</template>
|
</a-alert>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-left !w-[260px]">
|
<BasicLeftTree title="组织" ref="leftTreeRef" v-bind="getTreeBindValue" />
|
</div>
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-content">
|
<BasicVxeTable @register="registerTable" :search-info="searchInfo">
|
<template #tableTitle>
|
<div v-show="false">
|
<jnpf-pos-select ref="postSelectRef" v-model:value="state.currentPosId" multiple @change="onAdjustPositionChange" />
|
<jnpf-pos-select ref="batchPostSelectRef" multiple @change="onBatchAdjustPositionChange" />
|
</div>
|
<template v-if="!getSelectRowLen">
|
<a-button type="primary" pre-icon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ $t('common.addText') }}</a-button>
|
<a-dropdown>
|
<template #overlay>
|
<a-menu @click="handleSync">
|
<a-menu-item :key="0"><i class="icon-ym icon-ym-logo-dingding pr-[5px]"></i>钉钉同步</a-menu-item>
|
<a-menu-item :key="1"><i class="icon-ym icon-ym-logo-wxWork pr-[5px]"></i>企微同步</a-menu-item>
|
</a-menu>
|
</template>
|
<a-button type="primary" pre-icon="icon-ym icon-ym-synForThird">{{ $t('common.syncUser') }}<DownOutlined /></a-button>
|
</a-dropdown>
|
<a-button type="link" @click="handleExport"><i class="icon-ym icon-ym-btn-download"></i>{{ $t('common.exportText') }}</a-button>
|
<a-button type="link" @click="handleImport"><i class="icon-ym icon-ym-btn-upload"></i>{{ $t('common.importText') }}</a-button>
|
</template>
|
<template v-else>
|
<a-button type="link" class="!pt-[6px]" @click="handleBatchAdjustPosition">
|
<i class="icon-ym icon-ym-app-move text-[14px]"></i>调整岗位
|
</a-button>
|
<a-button type="link" @click="handleBatchUpdateState(1)" class="!pt-[6px]">
|
<i class="icon-ym icon-ym-flow-node-start text-[14px]"></i>启用
|
</a-button>
|
<a-button type="link" @click="handleBatchUpdateState(0)"><i class="ym-custom ym-custom-stop-circle-outline"></i>禁用</a-button>
|
<a-button type="link" @click="handleBatchUpdateState(2)"><i class="ym-custom ym-custom-lock-open-outline"></i>锁定</a-button>
|
</template>
|
</template>
|
<template #enabledMark="{ record }">
|
<a-tag :color="record.enabledMark == 1 ? 'success' : record.enabledMark == 2 ? 'warning' : 'error'">
|
{{ record.enabledMark == 1 ? '启用' : record.enabledMark == 2 ? '锁定' : '禁用' }}
|
</a-tag>
|
</template>
|
<template #action="{ record }">
|
<TableAction :actions="getTableActions(record)" :drop-down-actions="getDropDownActions(record)" v-if="!record.isAdministrator" />
|
</template>
|
</BasicVxeTable>
|
</div>
|
</div>
|
</div>
|
<Form @register="registerForm" @reload="reload" />
|
<ResetPassword @register="registerPsdModal" />
|
<SocialsBind @register="registerSocialsBind" />
|
<ExportModal @register="registerExportModal" />
|
<ImportModal @register="registerImportModal" @reload="reload" />
|
<WorkHandoverModal @register="registerWorkHandoverModal" @reload="reload" />
|
<PermissionPopup @register="registerPermission" />
|
</div>
|
</template>
|