!2 czh-20260414-实现分发功能的前端
Merge pull request !2 from 陈志浩/feature/distribution
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/officialdocDistribute/djOfficialdocDistribute/list',
|
||||||
|
save = '/officialdocDistribute/djOfficialdocDistribute/add',
|
||||||
|
edit = '/officialdocDistribute/djOfficialdocDistribute/edit',
|
||||||
|
deleteOne = '/officialdocDistribute/djOfficialdocDistribute/delete',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const list = (params?) => defHttp.get({ url: Api.list, params });
|
||||||
|
export const save = (params) => defHttp.post({ url: Api.save, params });
|
||||||
|
export const edit = (params) => defHttp.post({ url: Api.edit, params });
|
||||||
|
export const deleteOne = (params) => defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true });
|
||||||
|
export const saveOrUpdate = (params, isUpdate) => {
|
||||||
|
let url = isUpdate ? Api.edit : Api.save;
|
||||||
|
return defHttp.post({ url, params });
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DepartTreeNode {
|
||||||
|
id: string;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
title: string;
|
||||||
|
isLeaf?: boolean;
|
||||||
|
departType?: string | number;
|
||||||
|
distributable?: number;
|
||||||
|
disableCheckbox?: boolean;
|
||||||
|
assignType?: number;
|
||||||
|
children?: DepartTreeNode[];
|
||||||
|
description?: string;
|
||||||
|
parentId?: string;
|
||||||
|
orgCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserItem {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
realname: string;
|
||||||
|
orgCodeTxt?: string;
|
||||||
|
userSecurityLevel?: number;
|
||||||
|
userSecurityLevel_dictText?: string;
|
||||||
|
sex_dictText?: string;
|
||||||
|
sortno?: number;
|
||||||
|
departId?: string;
|
||||||
|
departName?: string;
|
||||||
|
assignType?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DistributeRecord {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
userId: string;
|
||||||
|
username: string;
|
||||||
|
selected: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssignType = 1 | 2 | 3;
|
||||||
|
|
||||||
|
export function deleteIfExist<T extends Record<string, any>>(array: T[], value: T, key: string): void {
|
||||||
|
const idx = array.findIndex((item) => item[key] === value[key]);
|
||||||
|
if (idx >= 0) {
|
||||||
|
array.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recurTree<T extends Record<string, any>, R>(tree: T[], callback: (node: T) => R[]): R[] {
|
||||||
|
let res: R[] = [];
|
||||||
|
tree.forEach((item) => {
|
||||||
|
res = res.concat(callback(item));
|
||||||
|
if (item.children) {
|
||||||
|
res = res.concat(recurTree(item.children, callback));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterDepartTree<T extends Record<string, any>>(departTree: T[], searchValue: string): T[] {
|
||||||
|
let filtered: T[] = [];
|
||||||
|
departTree.forEach((item) => {
|
||||||
|
if (item.title && item.title.includes(searchValue)) {
|
||||||
|
filtered.push(Object.assign({}, item, { children: null, isLeaf: true }));
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
const result = filterDepartTree(item.children, searchValue);
|
||||||
|
if (result.length > 0) {
|
||||||
|
filtered = filtered.concat(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
@@ -0,0 +1,696 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-button type="primary" @click="showModal" v-if="showButton">
|
||||||
|
分发对象
|
||||||
|
</a-button>
|
||||||
|
<a-row v-if="showButton" style="margin-top: 8px">
|
||||||
|
<a-col :span="24" style="width: 850px">
|
||||||
|
<a-button-group v-for="d in distributes" :key="d.id" style="margin: 0 8px 0 0">
|
||||||
|
<a-button type="primary" @click="setDistributeAndSubmit(d)" :title="d.name" style="min-width: 80px">
|
||||||
|
<span class="distribute-name">{{ d.name }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="deleteDistribute(d.id)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-card style="margin: 8px 0 8px 0; width: 850px; height: 170px; overflow-y: auto" :body-style="{ padding: '8px 8px 0 8px' }">
|
||||||
|
<SzSelectedDepartsAndUsers
|
||||||
|
key="out"
|
||||||
|
:disabled="true"
|
||||||
|
:selectedReadUsers="selectedReadUsers"
|
||||||
|
:selectedUsers="selectedUsers"
|
||||||
|
:selectedReadDeparts="selectedReadDeparts"
|
||||||
|
:selectedAssistDeparts="selectedAssistDeparts"
|
||||||
|
:selectedMainDeparts="selectedMainDeparts"
|
||||||
|
@update:selectedMainDeparts="handleMainDepartsChange"
|
||||||
|
@update:selectedAssistDeparts="handleAssistDepartsChange"
|
||||||
|
@update:selectedReadDeparts="handleReadDepartsChange"
|
||||||
|
@update:selectedUsers="handleUsersChange"
|
||||||
|
@update:selectedReadUsers="handleReadUsersChange"
|
||||||
|
/>
|
||||||
|
</a-card>
|
||||||
|
<a-modal
|
||||||
|
:width="width"
|
||||||
|
:title="titleText"
|
||||||
|
v-model:open="visible"
|
||||||
|
centered
|
||||||
|
:confirm-loading="confirmLoading"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
:mask-closable="false"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<a-row :gutter="8">
|
||||||
|
<a-col :span="3">
|
||||||
|
<h3 style="padding-left: 8px">已存方案:</h3>
|
||||||
|
<div style="height: 556px">
|
||||||
|
<a-empty v-if="distributes.length === 0" description="未保存方案" />
|
||||||
|
<a-button-group v-for="d in distributes" :key="d.id" class="small-space distribute-btn-group">
|
||||||
|
<a-button size="small" type="primary" @click="setDistribute(d)" :title="d.name" style="width: 100px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
|
||||||
|
<span class="distribute-name">{{ d.name }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button size="small" type="primary" @click="deleteDistribute(d.id)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-card
|
||||||
|
:tab-list="tabList"
|
||||||
|
:active-tab-key="activeTab"
|
||||||
|
@tabChange="(key) => onTabChange(key)"
|
||||||
|
style="overflow: hidden"
|
||||||
|
:body-style="{ height: '556px', overflow: 'hidden' }"
|
||||||
|
>
|
||||||
|
<div v-if="activeTab === '1'">
|
||||||
|
<SzSelectDepart
|
||||||
|
:selectedReadDeparts="selectedReadDeparts"
|
||||||
|
:selectedAssistDeparts="selectedAssistDeparts"
|
||||||
|
:selectedMainDeparts="selectedMainDeparts"
|
||||||
|
@update:selectedMainDeparts="handleMainDepartsChange"
|
||||||
|
@update:selectedAssistDeparts="handleAssistDepartsChange"
|
||||||
|
@update:selectedReadDeparts="handleReadDepartsChange"
|
||||||
|
:excludeDeparts="excludeDeparts"
|
||||||
|
:departId="departId"
|
||||||
|
:role="role"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeTab === '2'">
|
||||||
|
<SzSelectUser
|
||||||
|
key="selectUser"
|
||||||
|
v-model:selectedDepId="selectedUserDeptId"
|
||||||
|
@update:checkedUser="handleUsersChange"
|
||||||
|
:checkedUser="selectedUsers"
|
||||||
|
:secLevel="secLevel"
|
||||||
|
:departId="departId"
|
||||||
|
:role="role"
|
||||||
|
:isSpecial="isSpecial"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeTab === '3'">
|
||||||
|
<SzSelectUser
|
||||||
|
key="selectReadUser"
|
||||||
|
v-model:selectedDepId="selectedReadUserDeptId"
|
||||||
|
@update:checkedUser="handleReadUsersChange"
|
||||||
|
:checkedUser="selectedReadUsers"
|
||||||
|
:secLevel="secLevel"
|
||||||
|
:departId="departId"
|
||||||
|
:role="role"
|
||||||
|
:isSpecial="isSpecial"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="9">
|
||||||
|
<a-card :body-style="{ height: '556px', display: 'flex', flexDirection: 'column' }">
|
||||||
|
<template #title>
|
||||||
|
已选
|
||||||
|
<a-button type="danger" @click="clearSelected">清空</a-button>
|
||||||
|
</template>
|
||||||
|
<div style="flex: 1; overflow: auto; min-height: 0">
|
||||||
|
<SzSelectedDepartsAndUsers
|
||||||
|
key="in"
|
||||||
|
:selectedReadUsers="selectedReadUsers"
|
||||||
|
:selectedUsers="selectedUsers"
|
||||||
|
:selectedReadDeparts="selectedReadDeparts"
|
||||||
|
:selectedAssistDeparts="selectedAssistDeparts"
|
||||||
|
:selectedMainDeparts="selectedMainDeparts"
|
||||||
|
@update:selectedMainDeparts="handleMainDepartsChange"
|
||||||
|
@update:selectedAssistDeparts="handleAssistDepartsChange"
|
||||||
|
@update:selectedReadDeparts="handleReadDepartsChange"
|
||||||
|
@update:selectedUsers="handleUsersChange"
|
||||||
|
@update:selectedReadUsers="handleReadUsersChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0 8px 8px 8px">
|
||||||
|
<a-form-item label="分发意见">
|
||||||
|
<a-textarea placeholder="请在此处输入意见。" style="height: 100px" v-model:value="reason" />
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0 8px 0 8px">
|
||||||
|
<a-input-search v-model:value="activeDistribute" @search="saveDistributes" enter-button="保存为方案" />
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed, watch, onMounted, h } from 'vue';
|
||||||
|
import { CloseOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { Modal, message, Empty } from 'ant-design-vue';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import { filterDictTextByCache } from '/@/utils/dict/JDictSelectUtil';
|
||||||
|
import { deleteIfExist, list, save, edit, deleteOne } from './SzDistribute.api';
|
||||||
|
import type { DepartTreeNode, UserItem } from './SzDistribute.api';
|
||||||
|
import SzSelectDepart from './SzSelectDepart.vue';
|
||||||
|
import SzSelectUser from './SzSelectUser.vue';
|
||||||
|
import SzSelectedDepartsAndUsers from './SzSelectedDepartsAndUsers.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
width?: string | number;
|
||||||
|
title: string;
|
||||||
|
docId?: string;
|
||||||
|
departId?: string;
|
||||||
|
role: string | number;
|
||||||
|
secLevel: any;
|
||||||
|
checkedMainDepartIds?: string;
|
||||||
|
checkedAssistDepartIds?: string;
|
||||||
|
checkedReadDepartIds?: string;
|
||||||
|
checkedUsernames?: string;
|
||||||
|
checkedReadUsernames?: string;
|
||||||
|
reasons?: string;
|
||||||
|
isSpecial?: number;
|
||||||
|
modelValue?: string;
|
||||||
|
showButton?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
width: 1500,
|
||||||
|
title: '分发方案',
|
||||||
|
docId: '',
|
||||||
|
departId: '',
|
||||||
|
role: 1,
|
||||||
|
checkedMainDepartIds: '',
|
||||||
|
checkedAssistDepartIds: '',
|
||||||
|
checkedReadDepartIds: '',
|
||||||
|
checkedUsernames: '',
|
||||||
|
checkedReadUsernames: '',
|
||||||
|
reasons: '',
|
||||||
|
isSpecial: 0,
|
||||||
|
modelValue: '',
|
||||||
|
showButton: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:checkedDepartIds',
|
||||||
|
'update:checkedMainDepartIds',
|
||||||
|
'update:checkedAssistDepartIds',
|
||||||
|
'update:checkedReadDepartIds',
|
||||||
|
'update:checkedUsernames',
|
||||||
|
'update:checkedReadUsernames',
|
||||||
|
'update:reasons',
|
||||||
|
'update:modelValue',
|
||||||
|
'update:checkedDeparts',
|
||||||
|
'change',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const excludeDeparts = ref<string[]>([]);
|
||||||
|
const visible = ref(false);
|
||||||
|
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||||
|
const confirmLoading = ref(false);
|
||||||
|
const activeTab = ref('1');
|
||||||
|
const activeDistribute = ref('');
|
||||||
|
const selectedMainDeparts = ref<DepartTreeNode[]>([]);
|
||||||
|
const selectedAssistDeparts = ref<DepartTreeNode[]>([]);
|
||||||
|
const selectedReadDeparts = ref<DepartTreeNode[]>([]);
|
||||||
|
const selectedUsers = ref<UserItem[]>([]);
|
||||||
|
const selectedReadUsers = ref<UserItem[]>([]);
|
||||||
|
const selectedUserDeptId = ref('');
|
||||||
|
const selectedReadUserDeptId = ref('');
|
||||||
|
const reason = ref('');
|
||||||
|
const distributes = ref<any[]>([]);
|
||||||
|
|
||||||
|
const tabList = [
|
||||||
|
{ key: '1', tab: '选择部门' },
|
||||||
|
{ key: '2', tab: '选择办文人员' },
|
||||||
|
{ key: '3', tab: '选择阅文人员' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedMainDepartIds = computed(() => selectedMainDeparts.value.map((item) => item.id));
|
||||||
|
const selectedAssistDepartIds = computed(() => selectedAssistDeparts.value.map((item) => item.id));
|
||||||
|
const selectedReadDepartIds = computed(() => selectedReadDeparts.value.map((item) => item.id));
|
||||||
|
const selectedDepartIds = computed(() => selectedMainDepartIds.value.concat(selectedAssistDepartIds.value).concat(selectedReadDepartIds.value));
|
||||||
|
const selectedUsernames = computed(() => selectedUsers.value.map((item) => item.username));
|
||||||
|
const selectedReadUsernames = computed(() => selectedReadUsers.value.map((item) => item.username));
|
||||||
|
|
||||||
|
const distributeStr = computed(() => {
|
||||||
|
const mainIds = selectedMainDeparts.value.map((item) => item.id);
|
||||||
|
const assistIds = selectedAssistDeparts.value.map((item) => item.id);
|
||||||
|
const readIds = selectedReadDeparts.value.map((item) => item.id);
|
||||||
|
const usernames = selectedUsers.value.map((item) => item.username);
|
||||||
|
const readUsernames = selectedReadUsers.value.map((item) => item.username);
|
||||||
|
let res = '';
|
||||||
|
if (mainIds.length > 0) res += 'main: ' + mainIds.join(',');
|
||||||
|
if (assistIds.length > 0) res += 'assist: ' + assistIds.join(',');
|
||||||
|
if (readIds.length > 0) res += 'read:' + readIds.join(',');
|
||||||
|
if (usernames.length > 0) res += 'user:' + usernames.join(',');
|
||||||
|
if (readUsernames.length > 0) res += 'readUser:' + readUsernames.join(',');
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
const titleText = computed(() => {
|
||||||
|
const secLevelText = filterDictTextByCache('officialdoc_security_level', props.secLevel);
|
||||||
|
if (secLevelText && secLevelText !== 'undefined' && secLevelText !== props.secLevel) {
|
||||||
|
return '(' + secLevelText + ')' + props.title;
|
||||||
|
}
|
||||||
|
return props.title;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.docId,
|
||||||
|
() => {
|
||||||
|
getExcludeDepartIds();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedMainDepartIds,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (newVal !== oldVal) refreshDepartsAndUsersFromIds({ mainDepartIds: newVal });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedAssistDepartIds,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (newVal !== oldVal) refreshDepartsAndUsersFromIds({ assistDepartIds: newVal });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedReadDepartIds,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (newVal !== oldVal) refreshDepartsAndUsersFromIds({ readDepartIds: newVal });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedUsernames,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (newVal !== oldVal) refreshDepartsAndUsersFromIds({ usernames: newVal });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedReadUsernames,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (newVal !== oldVal) refreshDepartsAndUsersFromIds({ readUsernames: newVal });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function refreshDepartsAndUsersFromIds(params: {
|
||||||
|
mainDepartIds?: string;
|
||||||
|
assistDepartIds?: string;
|
||||||
|
readDepartIds?: string;
|
||||||
|
usernames?: string;
|
||||||
|
readUsernames?: string;
|
||||||
|
}) {
|
||||||
|
const { mainDepartIds, assistDepartIds, readDepartIds, usernames, readUsernames } = params;
|
||||||
|
if (mainDepartIds) {
|
||||||
|
defHttp.get({ url: '/sys/sysDepart/queryTreeList', params: { ids: mainDepartIds } }).then((res: any) => {
|
||||||
|
res.forEach((item: any) => (item.assignType = 1));
|
||||||
|
selectedMainDeparts.value = [...res];
|
||||||
|
}).catch(() => { selectedMainDeparts.value = []; });
|
||||||
|
} else {
|
||||||
|
selectedMainDeparts.value = [];
|
||||||
|
}
|
||||||
|
if (assistDepartIds) {
|
||||||
|
defHttp.get({ url: '/sys/sysDepart/queryTreeList', params: { ids: assistDepartIds } }).then((res: any) => {
|
||||||
|
res.forEach((item: any) => (item.assignType = 2));
|
||||||
|
selectedAssistDeparts.value = [...res];
|
||||||
|
}).catch(() => { selectedAssistDeparts.value = []; });
|
||||||
|
} else {
|
||||||
|
selectedAssistDeparts.value = [];
|
||||||
|
}
|
||||||
|
if (readDepartIds) {
|
||||||
|
defHttp.get({ url: '/sys/sysDepart/queryTreeList', params: { ids: readDepartIds } }).then((res: any) => {
|
||||||
|
selectedReadDeparts.value = [...res];
|
||||||
|
}).catch(() => { selectedReadDeparts.value = []; });
|
||||||
|
} else {
|
||||||
|
selectedReadDeparts.value = [];
|
||||||
|
}
|
||||||
|
if (usernames) {
|
||||||
|
defHttp.get({ url: '/sys/user/queryUserComponentData', params: { username: usernames, isMultiTranslate: 'true', pageNo: 1, pageSize: 9999 } }).then((res: any) => {
|
||||||
|
selectedUsers.value = [...(res.records || res)];
|
||||||
|
}).catch(() => { selectedUsers.value = []; });
|
||||||
|
} else {
|
||||||
|
selectedUsers.value = [];
|
||||||
|
}
|
||||||
|
if (readUsernames) {
|
||||||
|
defHttp.get({ url: '/sys/user/queryUserComponentData', params: { username: readUsernames, isMultiTranslate: 'true', pageNo: 1, pageSize: 9999 } }).then((res: any) => {
|
||||||
|
selectedReadUsers.value = [...(res.records || res)];
|
||||||
|
}).catch(() => { selectedReadUsers.value = []; });
|
||||||
|
} else {
|
||||||
|
selectedReadUsers.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
selectedMainDeparts.value = [];
|
||||||
|
selectedAssistDeparts.value = [];
|
||||||
|
selectedReadDeparts.value = [];
|
||||||
|
selectedUsers.value = [];
|
||||||
|
selectedReadUsers.value = [];
|
||||||
|
refreshDepartsAndUsersFromIds({
|
||||||
|
mainDepartIds: props.checkedMainDepartIds,
|
||||||
|
assistDepartIds: props.checkedAssistDepartIds,
|
||||||
|
readDepartIds: props.checkedReadDepartIds,
|
||||||
|
usernames: props.checkedUsernames,
|
||||||
|
readUsernames: props.checkedReadUsernames,
|
||||||
|
});
|
||||||
|
getExcludeDepartIds();
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitDistribute() {
|
||||||
|
emitChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOk() {
|
||||||
|
submitDistribute();
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
visible.value = false;
|
||||||
|
selectedMainDeparts.value = [];
|
||||||
|
selectedAssistDeparts.value = [];
|
||||||
|
selectedReadDeparts.value = [];
|
||||||
|
selectedUsers.value = [];
|
||||||
|
selectedReadUsers.value = [];
|
||||||
|
refreshDepartsAndUsersFromIds({
|
||||||
|
mainDepartIds: props.checkedMainDepartIds,
|
||||||
|
assistDepartIds: props.checkedAssistDepartIds,
|
||||||
|
readDepartIds: props.checkedReadDepartIds,
|
||||||
|
usernames: props.checkedUsernames,
|
||||||
|
readUsernames: props.checkedReadUsernames,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTabChange(key: string) {
|
||||||
|
activeTab.value = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateUser(savedUsers: UserItem[]) {
|
||||||
|
const uniqueUsers = Array.from(new Set(savedUsers));
|
||||||
|
const userIds = uniqueUsers.map((user) => user.id).join(',');
|
||||||
|
defHttp
|
||||||
|
.get({ url: '/sys/user/queryUserComponentData', params: { id: userIds, isMultiTranslate: 'true', pageNo: 1, pageSize: 9999 } })
|
||||||
|
.then((res: any) => {
|
||||||
|
const userList = res.records || res;
|
||||||
|
const refreshedUsersMap = new Map(userList.map((user: any) => [user.id, user]));
|
||||||
|
let secProhibitUser: any[] = [];
|
||||||
|
let departChangeUser: any[] = [];
|
||||||
|
let abnormalUser: any[] = [];
|
||||||
|
uniqueUsers.forEach((user) => {
|
||||||
|
const refreshedUser = refreshedUsersMap.get(user.id);
|
||||||
|
if (refreshedUser) {
|
||||||
|
if (refreshedUser.userSecurityLevel <= props.secLevel) {
|
||||||
|
secProhibitUser.push(refreshedUser);
|
||||||
|
}
|
||||||
|
if (user.orgCodeTxt !== refreshedUser.orgCodeTxt) {
|
||||||
|
departChangeUser.push(refreshedUser);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
abnormalUser.push(user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let confirmStr = '';
|
||||||
|
if (secProhibitUser.length > 0) {
|
||||||
|
confirmStr += '密级不满足要求的员工: ' + secProhibitUser.map((user) => user.realname).join(',') + '。';
|
||||||
|
}
|
||||||
|
if (departChangeUser.length > 0) {
|
||||||
|
confirmStr += '部门发生变动的员工:' + departChangeUser.map((user) => user.realname).join(',') + '。';
|
||||||
|
}
|
||||||
|
if (abnormalUser.length > 0) {
|
||||||
|
confirmStr += '已离职或状态为冻结的员工:' + abnormalUser.map((user) => user.realname).join(',');
|
||||||
|
}
|
||||||
|
if (confirmStr !== '') {
|
||||||
|
Modal.warning({
|
||||||
|
title: '方案保存的部分员工发生变动!将自动取消以下人员:',
|
||||||
|
content: h('div', confirmStr),
|
||||||
|
onOk: () => {
|
||||||
|
const deleteUsers = secProhibitUser.concat(departChangeUser).concat(abnormalUser);
|
||||||
|
deleteUsers.forEach((deleteUser) => {
|
||||||
|
const delIdx = selectedUsers.value.findIndex((user) => user.id === deleteUser.id);
|
||||||
|
if (delIdx >= 0) {
|
||||||
|
selectedUsers.value.splice(delIdx, 1);
|
||||||
|
}
|
||||||
|
const delIdx1 = selectedReadUsers.value.findIndex((user) => user.id === deleteUser.id);
|
||||||
|
if (delIdx1 >= 0) {
|
||||||
|
selectedReadUsers.value.splice(delIdx1, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return secProhibitUser.length === 0 && departChangeUser.length === 0 && abnormalUser.length === 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDistribute(distribute: any) {
|
||||||
|
validateUser(distribute.selected.selectedUsers.concat(distribute.selected.selectedReadUsers));
|
||||||
|
activeTab.value = '1';
|
||||||
|
activeDistribute.value = distribute.name;
|
||||||
|
selectedMainDeparts.value = [...distribute.selected.selectedMainDeparts];
|
||||||
|
selectedAssistDeparts.value = [...distribute.selected.selectedAssistDeparts];
|
||||||
|
selectedReadDeparts.value = [...distribute.selected.selectedReadDeparts];
|
||||||
|
selectedUsers.value = [...distribute.selected.selectedUsers];
|
||||||
|
selectedReadUsers.value = [...distribute.selected.selectedReadUsers];
|
||||||
|
reason.value = distribute.selected.reason;
|
||||||
|
message.success('加载方案成功!');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDistributeAndSubmit(distribute: any) {
|
||||||
|
setDistribute(distribute);
|
||||||
|
submitDistribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshDistributes() {
|
||||||
|
list().then((res: any) => {
|
||||||
|
distributes.value = [...res];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveDistributes(name: string) {
|
||||||
|
if (name === '') {
|
||||||
|
message.error('请输入方案名!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dIdx = distributes.value.findIndex((item) => item.name === name);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const userId = userStore.getUserInfo?.id;
|
||||||
|
const username = userStore.getUserInfo?.username;
|
||||||
|
const data = {
|
||||||
|
name,
|
||||||
|
userId,
|
||||||
|
username,
|
||||||
|
selected: {
|
||||||
|
selectedMainDeparts: selectedMainDeparts.value,
|
||||||
|
selectedAssistDeparts: selectedAssistDeparts.value,
|
||||||
|
selectedReadDeparts: selectedReadDeparts.value,
|
||||||
|
selectedUsers: selectedUsers.value,
|
||||||
|
selectedReadUsers: selectedReadUsers.value,
|
||||||
|
reason: reason.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (dIdx < 0) {
|
||||||
|
save(data).then(() => {
|
||||||
|
message.success('新增方案成功!');
|
||||||
|
refreshDistributes();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.id = distributes.value[dIdx].id;
|
||||||
|
edit(data).then(() => {
|
||||||
|
message.success('更新方案成功!');
|
||||||
|
refreshDistributes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDistribute(id: string) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '是否删除该方案?',
|
||||||
|
onOk: () => {
|
||||||
|
deleteOne({ id }).then(() => {
|
||||||
|
refreshDistributes();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateReasonText(): string {
|
||||||
|
let result = '';
|
||||||
|
if (selectedMainDeparts.value.length > 0) {
|
||||||
|
const departNames = selectedMainDeparts.value.map((item) => item.title).join(',');
|
||||||
|
result += '请' + departNames + '主办。';
|
||||||
|
}
|
||||||
|
if (selectedAssistDeparts.value.length > 0) {
|
||||||
|
const departNames = selectedAssistDeparts.value.map((item) => item.title).join(',');
|
||||||
|
result += '请' + departNames + '协办。';
|
||||||
|
}
|
||||||
|
if (selectedReadDeparts.value.length > 0) {
|
||||||
|
const departNames = selectedReadDeparts.value.map((item) => item.title).join(',');
|
||||||
|
result += '请' + departNames + '阅。';
|
||||||
|
}
|
||||||
|
if (selectedUsers.value.length > 0) {
|
||||||
|
const userNames = selectedUsers.value.map((item) => item.realname).join(',');
|
||||||
|
result += '请' + userNames + '办理。';
|
||||||
|
}
|
||||||
|
if (selectedReadUsers.value.length > 0) {
|
||||||
|
const readUserNames = selectedReadUsers.value.map((item) => item.realname).join(',');
|
||||||
|
result += '请' + readUserNames + '阅。';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSelected() {
|
||||||
|
selectedMainDeparts.value = [];
|
||||||
|
selectedAssistDeparts.value = [];
|
||||||
|
selectedReadDeparts.value = [];
|
||||||
|
selectedUsers.value = [];
|
||||||
|
selectedReadUsers.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExcludeDepartIds() {
|
||||||
|
if (props.docId !== '') {
|
||||||
|
defHttp
|
||||||
|
.get({ url: '/distributionProcessinfo/officialdocDistributionProcessinfo/queryDeptIds', params: { docId: props.docId } })
|
||||||
|
.then((res: any) => {
|
||||||
|
excludeDeparts.value = res;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
excludeDeparts.value = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMainDepartsChange(newVal: DepartTreeNode[]) {
|
||||||
|
selectedMainDeparts.value = [...newVal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAssistDepartsChange(newVal: DepartTreeNode[]) {
|
||||||
|
selectedAssistDeparts.value = [...newVal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReadDepartsChange(newVal: DepartTreeNode[]) {
|
||||||
|
selectedReadDeparts.value = [...newVal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUsersChange(newVal: UserItem[]) {
|
||||||
|
selectedUsers.value = [...newVal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReadUsersChange(newVal: UserItem[]) {
|
||||||
|
selectedReadUsers.value = [...newVal];
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitChanges() {
|
||||||
|
emit('update:checkedDepartIds', selectedDepartIds.value.join(','));
|
||||||
|
emit('update:checkedMainDepartIds', selectedMainDepartIds.value.join(','));
|
||||||
|
emit('update:checkedAssistDepartIds', selectedAssistDepartIds.value.join(','));
|
||||||
|
emit('update:checkedReadDepartIds', selectedReadDepartIds.value.join(','));
|
||||||
|
emit('update:checkedUsernames', selectedUsernames.value.join(','));
|
||||||
|
emit('update:checkedReadUsernames', selectedReadUsernames.value.join(','));
|
||||||
|
emit('update:modelValue', distributeStr.value);
|
||||||
|
emit('change', distributeStr.value);
|
||||||
|
if (reason.value) {
|
||||||
|
emit('update:reasons', reason.value);
|
||||||
|
} else {
|
||||||
|
emit('update:reasons', generateReasonText());
|
||||||
|
}
|
||||||
|
emit('update:checkedDeparts', selectedMainDeparts.value.concat(selectedAssistDeparts.value).concat(selectedReadDeparts.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.role === 2 || props.role === '2') {
|
||||||
|
activeTab.value = '3';
|
||||||
|
}
|
||||||
|
refreshDepartsAndUsersFromIds({
|
||||||
|
mainDepartIds: props.checkedMainDepartIds,
|
||||||
|
assistDepartIds: props.checkedAssistDepartIds,
|
||||||
|
readDepartIds: props.checkedReadDepartIds,
|
||||||
|
usernames: props.checkedUsernames,
|
||||||
|
readUsernames: props.checkedReadUsernames,
|
||||||
|
});
|
||||||
|
refreshDistributes();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.small-space {
|
||||||
|
margin: 0 8px 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribute-btn-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribute-name-btn {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distribute-name {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
max-width: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
&.btn-red {
|
||||||
|
&,
|
||||||
|
&:active,
|
||||||
|
&.active {
|
||||||
|
color: #f5222d;
|
||||||
|
background: #fff1f0;
|
||||||
|
border-color: #ffa39e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #f5222d;
|
||||||
|
background: #fff1f0;
|
||||||
|
border-color: #ffa39e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-blue {
|
||||||
|
&,
|
||||||
|
&:active,
|
||||||
|
&.active {
|
||||||
|
color: #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-color: #91d5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-color: #91d5ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-green {
|
||||||
|
&,
|
||||||
|
&:active,
|
||||||
|
&.active {
|
||||||
|
color: #52c41a;
|
||||||
|
background: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: #52c41a;
|
||||||
|
background: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,659 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-spin tip="Loading..." :spinning="false">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input-search v-model:value="searchDepartValue" style="margin-bottom: 1px" placeholder="请输入部门名称" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="18">
|
||||||
|
<a-space>
|
||||||
|
<a-input-group v-if="showCheckAll" compact style="display: inline-block; width: auto">
|
||||||
|
<a-button @click="checkAllDepart">全选部门</a-button>
|
||||||
|
<a-select v-model:value="checkAllAssignType" :options="assignTypeList" />
|
||||||
|
</a-input-group>
|
||||||
|
<a-button type="danger" v-if="showCancelCheckAll" @click="uncheckAllDepart">取消全选</a-button>
|
||||||
|
|
||||||
|
<a-input-group v-if="showCheckAllMange" compact style="display: inline-block; width: auto">
|
||||||
|
<a-button @click="checkAllManageDepart">全选职能部门</a-button>
|
||||||
|
<a-select v-model:value="checkAllManageAssignType" :options="assignTypeList" />
|
||||||
|
</a-input-group>
|
||||||
|
<a-button type="danger" v-if="showCancelCheckAllMange" @click="uncheckAllManageDepart">取消全选职能</a-button>
|
||||||
|
|
||||||
|
<a-input-group v-if="showCheckAllBusiness" compact style="display: inline-block; width: auto">
|
||||||
|
<a-button @click="checkAllBusinessesDepart">全选业务部门</a-button>
|
||||||
|
<a-select v-model:value="checkAllBusinessAssignType" :options="assignTypeList" />
|
||||||
|
</a-input-group>
|
||||||
|
<a-button type="danger" v-if="showCancelCheckAllBusiness" @click="uncheckAllBusinessesDepart">取消全选业务</a-button>
|
||||||
|
|
||||||
|
<a-input-group v-if="showCheckAllCurrentSubDepart" compact style="display: inline-block; width: auto">
|
||||||
|
<a-button @click="checkAllCurrentSubDepart">全选本部门科室</a-button>
|
||||||
|
<a-select v-model:value="checkAllCurrentSubDepartAssignType" :options="assignTypeList" />
|
||||||
|
</a-input-group>
|
||||||
|
<a-button type="danger" v-if="showCancelCheckAllCurrentSubDepart" @click="uncheckAllCurrentSubDepart">取消本部门科室</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<div v-if="filterDepartTreeData.length === 0" style="height: 470px; display: flex; align-items: center; justify-content: center">
|
||||||
|
<a-empty description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
<a-tree
|
||||||
|
v-else
|
||||||
|
ref="treeRef"
|
||||||
|
checkable
|
||||||
|
v-model:checkedKeys="checkedKeys"
|
||||||
|
v-model:selectedKeys="selectedKeys"
|
||||||
|
class="my-dept-select-tree"
|
||||||
|
:tree-data="filterDepartTreeData"
|
||||||
|
:check-strictly="true"
|
||||||
|
@check="onDepartCheck"
|
||||||
|
:default-expanded-keys="defaultExpandedKeys"
|
||||||
|
style="overflow-y: auto"
|
||||||
|
>
|
||||||
|
<template #title="{ key: treeKey, title, disableCheckbox, assignType }">
|
||||||
|
<a-dropdown :trigger="['contextmenu']">
|
||||||
|
<span v-if="disableCheckbox" style="color: rgba(0,0,0,0.25); cursor: not-allowed">{{ title }} </span>
|
||||||
|
<span v-else @click="toggleCheck(treeKey, assignType)">{{ title }} </span>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-button-group v-if="!disableCheckbox && checkedKeys.checked?.includes(treeKey)">
|
||||||
|
<a-button size="small" :class="assignType === 1 ? 'btn-red' : ''" @click="checkMainDepart(treeKey, assignType)">办</a-button>
|
||||||
|
<a-button size="small" :class="assignType === 2 ? 'btn-blue' : ''" @click="checkAssistDepart(treeKey, assignType)">协</a-button>
|
||||||
|
<a-button size="small" :class="assignType === 3 || !assignType ? 'btn-green' : ''" @click="checkReadDepart(treeKey, assignType)">阅</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
</template>
|
||||||
|
</a-tree>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
|
import { queryTreeList } from '/@/api/common/api';
|
||||||
|
import { pushIfNotExist } from '/@/utils/common/compUtils';
|
||||||
|
import { deleteIfExist, recurTree, filterDepartTree } from './SzDistribute.api';
|
||||||
|
import type { DepartTreeNode } from './SzDistribute.api';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
selectedMainDeparts: DepartTreeNode[];
|
||||||
|
selectedAssistDeparts: DepartTreeNode[];
|
||||||
|
selectedReadDeparts: DepartTreeNode[];
|
||||||
|
excludeDeparts?: string[];
|
||||||
|
departId: string;
|
||||||
|
role: string | number;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
excludeDeparts: () => [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:selectedMainDeparts',
|
||||||
|
'update:selectedAssistDeparts',
|
||||||
|
'update:selectedReadDeparts',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const treeRef = ref();
|
||||||
|
const treeData = ref<DepartTreeNode[]>([]);
|
||||||
|
const checkedKeys = ref<{ checked: string[]; halfChecked: string[] }>({ checked: [], halfChecked: [] });
|
||||||
|
const selectedKeys = ref<string[]>([]);
|
||||||
|
const searchDepartValue = ref('');
|
||||||
|
const departType = ref<string | number>('');
|
||||||
|
const defaultExpandedKeys = ref<string[]>([]);
|
||||||
|
const checkAllAssignType = ref<number>(3);
|
||||||
|
const checkAllManageAssignType = ref<number>(3);
|
||||||
|
const checkAllBusinessAssignType = ref<number>(3);
|
||||||
|
const checkAllCurrentSubDepartAssignType = ref<number>(3);
|
||||||
|
|
||||||
|
const assignTypeList = [
|
||||||
|
{ value: 1, label: '办' },
|
||||||
|
{ value: 2, label: '协' },
|
||||||
|
{ value: 3, label: '阅' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedDeparts = computed(() => [...props.selectedMainDeparts, ...props.selectedAssistDeparts, ...props.selectedReadDeparts]);
|
||||||
|
|
||||||
|
const filterDepartTreeData = computed(() => {
|
||||||
|
if (!searchDepartValue.value) return treeData.value;
|
||||||
|
return filterDepartTree(treeData.value, searchDepartValue.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const manageDeparts = computed(() => {
|
||||||
|
return recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '1' && !node.disableCheckbox) return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const businessesDeparts = computed(() => {
|
||||||
|
return recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '2' && !node.disableCheckbox) return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const subDeparts = computed(() => {
|
||||||
|
return recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '3' && !node.disableCheckbox) return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCheckAll = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return (manageDeparts.value.length + businessesDeparts.value.length > 0) && selectedDeparts.value.length < manageDeparts.value.length + businessesDeparts.value.length;
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& (manageDeparts.value.length + businessesDeparts.value.length > 0)
|
||||||
|
&& selectedDeparts.value.length < manageDeparts.value.length + businessesDeparts.value.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCancelCheckAll = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return selectedDeparts.value.length > 0 && !showCheckAll.value;
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& selectedDeparts.value.length > 0 && !showCheckAll.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCheckAllMange = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return manageDeparts.value.length > 0 && !manageDeparts.value.every((item) => selectedDeparts.value.some((i) => i.id === item.id));
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& manageDeparts.value.length > 0
|
||||||
|
&& !manageDeparts.value.every((item) => selectedDeparts.value.some((i) => i.id === item.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCancelCheckAllMange = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return selectedDeparts.value.length > 0 && !showCheckAllMange.value;
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& selectedDeparts.value.length > 0 && !showCheckAllMange.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCheckAllBusiness = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return businessesDeparts.value.length > 0 && !businessesDeparts.value.every((item) => selectedDeparts.value.some((i) => i.id === item.id));
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& businessesDeparts.value.length > 0
|
||||||
|
&& !businessesDeparts.value.every((item) => selectedDeparts.value.some((i) => i.id === item.id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCancelCheckAllBusiness = computed(() => {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
return selectedDeparts.value.length > 0 && !showCheckAllBusiness.value;
|
||||||
|
} else {
|
||||||
|
return (departType.value === 0 || departType.value === '0' || departType.value === 1 || departType.value === '1')
|
||||||
|
&& selectedDeparts.value.length > 0 && !showCheckAllBusiness.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCheckAllCurrentSubDepart = computed(() => {
|
||||||
|
return (props.role === 2 || props.role === '2')
|
||||||
|
&& (departType.value === 2 || departType.value === '2' || departType.value === 3 || departType.value === '3')
|
||||||
|
&& subDeparts.value.length > 0
|
||||||
|
&& !subDeparts.value.every((item) => selectedDeparts.value.some((i) => i.id === item.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCancelCheckAllCurrentSubDepart = computed(() => {
|
||||||
|
return (props.role === 2 || props.role === '2')
|
||||||
|
&& (departType.value === 2 || departType.value === '2' || departType.value === 3 || departType.value === '3')
|
||||||
|
&& selectedDeparts.value.length > 0
|
||||||
|
&& !showCheckAllCurrentSubDepart.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
selectedDeparts,
|
||||||
|
() => {
|
||||||
|
setDefaultCheckedKeys();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.excludeDeparts,
|
||||||
|
() => {
|
||||||
|
loadDepart();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function emitChanges(payload: { mainDeparts?: DepartTreeNode[]; assistDeparts?: DepartTreeNode[]; readDeparts?: DepartTreeNode[] }) {
|
||||||
|
if (payload.mainDeparts) emit('update:selectedMainDeparts', payload.mainDeparts);
|
||||||
|
if (payload.assistDeparts) emit('update:selectedAssistDeparts', payload.assistDeparts);
|
||||||
|
if (payload.readDeparts) emit('update:selectedReadDeparts', payload.readDeparts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFromInfo(treeKey: string, assignType: number) {
|
||||||
|
let fromArray: DepartTreeNode[] = [];
|
||||||
|
let fromKey: string;
|
||||||
|
if (assignType === 1) {
|
||||||
|
fromArray = [...props.selectedMainDeparts];
|
||||||
|
fromKey = 'mainDeparts';
|
||||||
|
} else if (assignType === 2) {
|
||||||
|
fromArray = [...props.selectedAssistDeparts];
|
||||||
|
fromKey = 'assistDeparts';
|
||||||
|
} else {
|
||||||
|
fromArray = [...props.selectedReadDeparts];
|
||||||
|
fromKey = 'readDeparts';
|
||||||
|
}
|
||||||
|
const fromIdx = fromArray.findIndex((item) => item.key === treeKey);
|
||||||
|
return { fromArray, fromIdx, fromKey };
|
||||||
|
}
|
||||||
|
|
||||||
|
function leaderTreeRender(tree: DepartTreeNode[]) {
|
||||||
|
let childDistributable = false;
|
||||||
|
let res: DepartTreeNode[] = [];
|
||||||
|
tree.forEach((item) => {
|
||||||
|
if (item.distributable === 1) {
|
||||||
|
childDistributable = true;
|
||||||
|
if (item.children) {
|
||||||
|
const r = leaderTreeRender(item.children);
|
||||||
|
if (r.res) {
|
||||||
|
item.children = r.res;
|
||||||
|
} else {
|
||||||
|
item.children = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.push(item);
|
||||||
|
} else {
|
||||||
|
if (item.children) {
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
const r = leaderTreeRender(item.children);
|
||||||
|
if (r.res && r.childDistributable) {
|
||||||
|
item.children = r.res;
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { childDistributable, res };
|
||||||
|
}
|
||||||
|
|
||||||
|
function manageTreeRender(tree: DepartTreeNode[]) {
|
||||||
|
let childDistributable = false;
|
||||||
|
let res: DepartTreeNode[] = [];
|
||||||
|
tree.forEach((item) => {
|
||||||
|
if (item.distributable === 1 && item.id === props.departId) {
|
||||||
|
childDistributable = true;
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
res.push(item);
|
||||||
|
} else if (item.distributable === 1 && (String(item.departType) === '1' || String(item.departType) === '2')) {
|
||||||
|
childDistributable = true;
|
||||||
|
if (item.children) {
|
||||||
|
const r = manageTreeRender(item.children);
|
||||||
|
if (r.res) {
|
||||||
|
item.children = r.res;
|
||||||
|
} else {
|
||||||
|
item.children = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.push(item);
|
||||||
|
} else {
|
||||||
|
if (item.children) {
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
const r = manageTreeRender(item.children);
|
||||||
|
if (r.res && r.childDistributable) {
|
||||||
|
item.children = r.res;
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { childDistributable, res };
|
||||||
|
}
|
||||||
|
|
||||||
|
function businessesRender(tree: DepartTreeNode[]) {
|
||||||
|
let childDistributable = false;
|
||||||
|
let res: DepartTreeNode[] = [];
|
||||||
|
tree.forEach((item) => {
|
||||||
|
if (item.distributable === 1 && item.id === props.departId) {
|
||||||
|
childDistributable = true;
|
||||||
|
defaultExpandedKeys.value.push(item.key);
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
res.push(item);
|
||||||
|
} else {
|
||||||
|
if (item.children) {
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
const r = businessesRender(item.children);
|
||||||
|
if (r.res && r.childDistributable) {
|
||||||
|
item.children = r.res;
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { childDistributable, res };
|
||||||
|
}
|
||||||
|
|
||||||
|
function subDepartsRender(tree: DepartTreeNode[]) {
|
||||||
|
let childDistributable = false;
|
||||||
|
let res: DepartTreeNode[] = [];
|
||||||
|
tree.forEach((item) => {
|
||||||
|
if (item.children && item.children.some((child) => child.id === props.departId)) {
|
||||||
|
childDistributable = true;
|
||||||
|
defaultExpandedKeys.value.push(item.key);
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
res.push(item);
|
||||||
|
} else {
|
||||||
|
if (item.children) {
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
const r = subDepartsRender(item.children);
|
||||||
|
if (r.res && r.childDistributable) {
|
||||||
|
item.children = r.res;
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { childDistributable, res };
|
||||||
|
}
|
||||||
|
|
||||||
|
function excludeDepartsTree(tree: DepartTreeNode[]) {
|
||||||
|
const excludes = props.excludeDeparts;
|
||||||
|
tree.forEach((item) => {
|
||||||
|
if (excludes.includes(item.id)) {
|
||||||
|
item.disableCheckbox = true;
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
excludeDepartsTree(item.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMainAssistRead() {
|
||||||
|
recurTree(treeData.value, (node) => {
|
||||||
|
selectedDeparts.value.some((item) => {
|
||||||
|
if (item.key === node.key) {
|
||||||
|
node.assignType = item.assignType;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadDepart() {
|
||||||
|
try {
|
||||||
|
const res = await queryTreeList();
|
||||||
|
let arr = [...(res as any)];
|
||||||
|
recurTree(arr, (node) => {
|
||||||
|
if (node.distributable === undefined || node.distributable === null) {
|
||||||
|
node.distributable = 1;
|
||||||
|
}
|
||||||
|
if (node.id === props.departId) {
|
||||||
|
departType.value = node.departType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
treeData.value = leaderTreeRender(arr).res;
|
||||||
|
} else {
|
||||||
|
if (departType.value === 0 || departType.value === '0') {
|
||||||
|
treeData.value = leaderTreeRender(arr).res;
|
||||||
|
} else if (departType.value === 1 || departType.value === '1') {
|
||||||
|
treeData.value = manageTreeRender(arr).res;
|
||||||
|
} else if (departType.value === 2 || departType.value === '2') {
|
||||||
|
treeData.value = businessesRender(arr).res;
|
||||||
|
} else if (departType.value === 3 || departType.value === '3') {
|
||||||
|
treeData.value = subDepartsRender(arr).res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
excludeDepartsTree(treeData.value);
|
||||||
|
setDefaultCheckedKeys();
|
||||||
|
treeData.value.forEach((item) => {
|
||||||
|
defaultExpandedKeys.value.push(item.key);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('loadDepart error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDepartCheck(checkedKeysValue: any, e: { checked: boolean; checkedNodes: any[]; node: any; event: Event }) {
|
||||||
|
const node = e.node.dataRef || e.node;
|
||||||
|
node.assignType = 3;
|
||||||
|
let mainDeparts = [...props.selectedMainDeparts];
|
||||||
|
let assistDeparts = [...props.selectedAssistDeparts];
|
||||||
|
let readDeparts = [...props.selectedReadDeparts];
|
||||||
|
if (e.checked) {
|
||||||
|
if (node.assignType === 1) {
|
||||||
|
mainDeparts.push(node);
|
||||||
|
} else if (node.assignType === 2) {
|
||||||
|
assistDeparts.push(node);
|
||||||
|
} else {
|
||||||
|
readDeparts.push(node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deleteIfExist(mainDeparts, node, 'id');
|
||||||
|
deleteIfExist(assistDeparts, node, 'id');
|
||||||
|
deleteIfExist(readDeparts, node, 'id');
|
||||||
|
}
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDefaultCheckedKeys() {
|
||||||
|
checkedKeys.value = { checked: selectedDeparts.value.map((item) => item.key), halfChecked: [] };
|
||||||
|
renderMainAssistRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCheck(treeKey: string, assignType: number) {
|
||||||
|
const selectedNode = recurTree(treeData.value, (node) => {
|
||||||
|
if (node.id === treeKey) return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
let readDeparts = [...props.selectedReadDeparts];
|
||||||
|
if (checkedKeys.value.checked.includes(treeKey)) {
|
||||||
|
const { fromArray, fromIdx, fromKey } = getFromInfo(treeKey, assignType);
|
||||||
|
fromArray.splice(fromIdx, 1);
|
||||||
|
emitChanges({ [fromKey]: fromArray });
|
||||||
|
} else {
|
||||||
|
selectedNode[0].assignType = 3;
|
||||||
|
readDeparts.push(selectedNode[0]);
|
||||||
|
emitChanges({ readDeparts });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAllDepart() {
|
||||||
|
const allDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (!node.disableCheckbox && (String(node.departType) === '1' || String(node.departType) === '2')) {
|
||||||
|
node.assignType = checkAllAssignType.value;
|
||||||
|
return [node];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
let mainDeparts: DepartTreeNode[] = [];
|
||||||
|
let assistDeparts: DepartTreeNode[] = [];
|
||||||
|
let readDeparts: DepartTreeNode[] = [];
|
||||||
|
if (checkAllAssignType.value === 1) {
|
||||||
|
mainDeparts = allDepart;
|
||||||
|
} else if (checkAllAssignType.value === 2) {
|
||||||
|
assistDeparts = allDepart;
|
||||||
|
} else if (checkAllAssignType.value === 3) {
|
||||||
|
readDeparts = allDepart;
|
||||||
|
}
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncheckAllDepart() {
|
||||||
|
emitChanges({ mainDeparts: [], assistDeparts: [], readDeparts: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAllManageDepart() {
|
||||||
|
const allManageDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (!node.disableCheckbox && String(node.departType) === '1') {
|
||||||
|
node.assignType = checkAllManageAssignType.value;
|
||||||
|
return [node];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
let mainDeparts = [...props.selectedMainDeparts];
|
||||||
|
let assistDeparts = [...props.selectedAssistDeparts];
|
||||||
|
let readDeparts = [...props.selectedReadDeparts];
|
||||||
|
if (checkAllManageAssignType.value === 1) {
|
||||||
|
mainDeparts = allManageDepart.concat(props.selectedMainDeparts);
|
||||||
|
} else if (checkAllManageAssignType.value === 2) {
|
||||||
|
assistDeparts = allManageDepart.concat(props.selectedAssistDeparts);
|
||||||
|
} else if (checkAllManageAssignType.value === 3) {
|
||||||
|
readDeparts = allManageDepart.concat(props.selectedReadDeparts);
|
||||||
|
}
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncheckAllManageDepart() {
|
||||||
|
const allManageDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '1') return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const mainDeparts = props.selectedMainDeparts.filter((item) => !allManageDepart.some((manage) => item.id === manage.id));
|
||||||
|
const assistDeparts = props.selectedAssistDeparts.filter((item) => !allManageDepart.some((manage) => item.id === manage.id));
|
||||||
|
const readDeparts = props.selectedReadDeparts.filter((item) => !allManageDepart.some((manage) => item.id === manage.id));
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAllBusinessesDepart() {
|
||||||
|
const allBusinessesDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (!node.disableCheckbox && String(node.departType) === '2') {
|
||||||
|
node.assignType = checkAllBusinessAssignType.value;
|
||||||
|
return [node];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
let mainDeparts = [...props.selectedMainDeparts];
|
||||||
|
let assistDeparts = [...props.selectedAssistDeparts];
|
||||||
|
let readDeparts = [...props.selectedReadDeparts];
|
||||||
|
if (checkAllBusinessAssignType.value === 1) {
|
||||||
|
mainDeparts = allBusinessesDepart.concat(props.selectedMainDeparts);
|
||||||
|
} else if (checkAllBusinessAssignType.value === 2) {
|
||||||
|
assistDeparts = allBusinessesDepart.concat(props.selectedAssistDeparts);
|
||||||
|
} else if (checkAllBusinessAssignType.value === 3) {
|
||||||
|
readDeparts = allBusinessesDepart.concat(props.selectedReadDeparts);
|
||||||
|
}
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncheckAllBusinessesDepart() {
|
||||||
|
const allBusinessesDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '2') return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const mainDeparts = props.selectedMainDeparts.filter((item) => !allBusinessesDepart.some((manage) => item.id === manage.id));
|
||||||
|
const assistDeparts = props.selectedAssistDeparts.filter((item) => !allBusinessesDepart.some((manage) => item.id === manage.id));
|
||||||
|
const readDeparts = props.selectedReadDeparts.filter((item) => !allBusinessesDepart.some((manage) => item.id === manage.id));
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAllCurrentSubDepart() {
|
||||||
|
const allSubDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (!node.disableCheckbox && String(node.departType) === '3') {
|
||||||
|
node.assignType = checkAllCurrentSubDepartAssignType.value;
|
||||||
|
return [node];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
let mainDeparts = [...props.selectedMainDeparts];
|
||||||
|
let assistDeparts = [...props.selectedAssistDeparts];
|
||||||
|
let readDeparts = [...props.selectedReadDeparts];
|
||||||
|
if (checkAllCurrentSubDepartAssignType.value === 1) {
|
||||||
|
mainDeparts = allSubDepart.concat(props.selectedMainDeparts);
|
||||||
|
} else if (checkAllCurrentSubDepartAssignType.value === 2) {
|
||||||
|
assistDeparts = allSubDepart.concat(props.selectedAssistDeparts);
|
||||||
|
} else if (checkAllCurrentSubDepartAssignType.value === 3) {
|
||||||
|
readDeparts = allSubDepart.concat(props.selectedReadDeparts);
|
||||||
|
}
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncheckAllCurrentSubDepart() {
|
||||||
|
const allCurrentSubDepart = recurTree(treeData.value, (node) => {
|
||||||
|
if (String(node.departType) === '3') return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
const mainDeparts = props.selectedMainDeparts.filter((item) => !allCurrentSubDepart.some((manage) => item.id === manage.id));
|
||||||
|
const assistDeparts = props.selectedAssistDeparts.filter((item) => !allCurrentSubDepart.some((manage) => item.id === manage.id));
|
||||||
|
const readDeparts = props.selectedReadDeparts.filter((item) => !allCurrentSubDepart.some((manage) => item.id === manage.id));
|
||||||
|
emitChanges({ mainDeparts, assistDeparts, readDeparts });
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkMainDepart(treeKey: string, assignType: number) {
|
||||||
|
const selected = props.selectedMainDeparts.some((item) => item.key === treeKey);
|
||||||
|
if (!selected) {
|
||||||
|
const { fromArray, fromIdx, fromKey } = getFromInfo(treeKey, assignType);
|
||||||
|
const toArray = [...props.selectedMainDeparts];
|
||||||
|
recurTree(treeData.value, (node) => {
|
||||||
|
if (node.key === treeKey) {
|
||||||
|
node.assignType = 1;
|
||||||
|
toArray.push(node);
|
||||||
|
fromArray.splice(fromIdx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emitChanges({ [fromKey]: fromArray, mainDeparts: toArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAssistDepart(treeKey: string, assignType: number) {
|
||||||
|
const selected = props.selectedAssistDeparts.some((item) => item.key === treeKey);
|
||||||
|
if (!selected) {
|
||||||
|
const { fromArray, fromIdx, fromKey } = getFromInfo(treeKey, assignType);
|
||||||
|
const toArray = [...props.selectedAssistDeparts];
|
||||||
|
recurTree(treeData.value, (node) => {
|
||||||
|
if (node.key === treeKey) {
|
||||||
|
node.assignType = 2;
|
||||||
|
toArray.push(node);
|
||||||
|
fromArray.splice(fromIdx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emitChanges({ [fromKey]: fromArray, assistDeparts: toArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkReadDepart(treeKey: string, assignType: number) {
|
||||||
|
const selected = props.selectedReadDeparts.some((item) => item.key === treeKey);
|
||||||
|
if (!selected) {
|
||||||
|
const { fromArray, fromIdx, fromKey } = getFromInfo(treeKey, assignType);
|
||||||
|
const toArray = [...props.selectedReadDeparts];
|
||||||
|
recurTree(treeData.value, (node) => {
|
||||||
|
if (node.key === treeKey) {
|
||||||
|
node.assignType = 3;
|
||||||
|
toArray.push(node);
|
||||||
|
fromArray.splice(fromIdx, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emitChanges({ [fromKey]: fromArray, readDeparts: toArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadDepart();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.my-dept-select-tree {
|
||||||
|
height: 470px;
|
||||||
|
&.fullscreen {
|
||||||
|
height: calc(100vh - 250px);
|
||||||
|
}
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.btn-red {
|
||||||
|
background-color: #ff4d4f;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #ff4d4f;
|
||||||
|
}
|
||||||
|
.btn-blue {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
.btn-green {
|
||||||
|
background-color: #52c41a;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #52c41a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,411 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-row>
|
||||||
|
<a-col :md="12" :sm="24">
|
||||||
|
<div style="height: 100%; border-right: 1px solid #e8e8e8; padding-right: 16px; margin-right: 16px">
|
||||||
|
<a-input-search v-model:value="searchDepartValue" style="margin-bottom: 16px" placeholder="请输入部门名称" />
|
||||||
|
<a-directory-tree
|
||||||
|
selectable
|
||||||
|
v-model:selectedKeys="selectedDepIds"
|
||||||
|
:check-strictly="true"
|
||||||
|
:dropdown-style="{ maxHeight: '200px', overflow: 'auto' }"
|
||||||
|
:tree-data="filterDepartTreeData"
|
||||||
|
:expand-action="false"
|
||||||
|
v-model:expandedKeys="expandedKeys"
|
||||||
|
@select="onDepSelect"
|
||||||
|
style="height: 470px; overflow: auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :md="12" :sm="24">
|
||||||
|
<a-input-search
|
||||||
|
:style="{ width: '100px', marginBottom: '15px' }"
|
||||||
|
placeholder="输入姓名"
|
||||||
|
v-model:value="queryParam.realname"
|
||||||
|
@search="onSearch"
|
||||||
|
/>
|
||||||
|
<a-button @click="searchReset(1)" style="margin-left: 8px">重置</a-button>
|
||||||
|
<a-button type="primary" @click="selectAll" style="margin-left: 8px">全选</a-button>
|
||||||
|
<a-button type="danger" @click="unselectAll" style="margin-left: 8px">清空</a-button>
|
||||||
|
<a-empty v-if="selectedDepIds.length === 0" description="请先选择部门!" />
|
||||||
|
<a-table
|
||||||
|
v-else
|
||||||
|
:scroll="{ y: 460, ...scrollTrigger }"
|
||||||
|
size="middle"
|
||||||
|
row-key="id"
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:pagination="ipagination"
|
||||||
|
:row-selection="{ selectedRowKeys: selectedRowKeys, onSelectAll: onSelectAll, onSelect: onSelect, type: getType }"
|
||||||
|
:loading="loading"
|
||||||
|
@change="handleTableChange"
|
||||||
|
:custom-row="customRow"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
||||||
|
import { queryTreeList } from '/@/api/common/api';
|
||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { pushIfNotExist, filterObj } from '/@/utils/common/compUtils';
|
||||||
|
import { deleteIfExist, recurTree, filterDepartTree } from './SzDistribute.api';
|
||||||
|
import type { UserItem, DepartTreeNode } from './SzDistribute.api';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
secLevel: any;
|
||||||
|
checkedUser?: UserItem[];
|
||||||
|
selectedDepId?: string;
|
||||||
|
departId: string;
|
||||||
|
role: string | number;
|
||||||
|
isSpecial: number;
|
||||||
|
multi?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
checkedUser: () => [],
|
||||||
|
selectedDepId: '',
|
||||||
|
multi: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:checkedUser', 'update:selectedDepId']);
|
||||||
|
|
||||||
|
const searchDepartValue = ref('');
|
||||||
|
const queryParam = reactive<Record<string, any>>({ realname: '' });
|
||||||
|
const dataSource = ref<UserItem[]>([]);
|
||||||
|
const allUserList = ref<UserItem[]>([]);
|
||||||
|
const selectedDepIds = ref<string[]>([]);
|
||||||
|
const selectedRows = ref<UserItem[]>([]);
|
||||||
|
const departTree = ref<DepartTreeNode[]>([]);
|
||||||
|
const fullDepartTree = ref<DepartTreeNode[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
const scrollTrigger = ref<Record<string, any>>({});
|
||||||
|
const isTreeLoaded = ref(false);
|
||||||
|
|
||||||
|
function findDepartIdByOrgCode(tree: DepartTreeNode[], orgCode: string): string | null {
|
||||||
|
for (const node of tree) {
|
||||||
|
if (node.orgCode === orgCode) {
|
||||||
|
return node.id;
|
||||||
|
}
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const found = findDepartIdByOrgCode(node.children, orgCode);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDepartId(departIdOrOrgCode: string): string {
|
||||||
|
if (!departIdOrOrgCode) return departIdOrOrgCode;
|
||||||
|
const uuidPattern = /^[0-9a-f]{32}$/i;
|
||||||
|
if (uuidPattern.test(departIdOrOrgCode.replace(/-/g, ''))) return departIdOrOrgCode;
|
||||||
|
const realId = findDepartIdByOrgCode(fullDepartTree.value, departIdOrOrgCode);
|
||||||
|
if (realId) {
|
||||||
|
console.log(`resolveDepartId: "${departIdOrOrgCode}" (orgCode) -> "${realId}" (UUID)`);
|
||||||
|
return realId;
|
||||||
|
}
|
||||||
|
console.warn(`resolveDepartId: cannot find UUID for orgCode "${departIdOrOrgCode}", using as-is`);
|
||||||
|
return departIdOrOrgCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: '用户姓名', align: 'center' as const, dataIndex: 'realname' },
|
||||||
|
{ title: '人员密级', align: 'center' as const, dataIndex: 'userSecurityLevel_dictText' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const ipagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 7,
|
||||||
|
showQuickJumper: true,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isorter = reactive({
|
||||||
|
column: 'createTime',
|
||||||
|
order: 'desc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = {
|
||||||
|
pageUserDepart: '/sys/user/queryUserComponentData',
|
||||||
|
listUserDepart: '/sys/user/queryUserComponentData',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getType = computed(() => (props.multi ? 'checkbox' : 'radio'));
|
||||||
|
|
||||||
|
const selectedRowKeys = computed(() => selectedRows.value.map((item) => item.id));
|
||||||
|
|
||||||
|
const filterDepartTreeData = computed(() => {
|
||||||
|
if (!searchDepartValue.value) return departTree.value;
|
||||||
|
return filterDepartTree(departTree.value, searchDepartValue.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.checkedUser,
|
||||||
|
() => {
|
||||||
|
selectedRows.value = [...(props.checkedUser || [])];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.departId,
|
||||||
|
() => {
|
||||||
|
queryDepartTree();
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.role,
|
||||||
|
() => {
|
||||||
|
queryDepartTree();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.secLevel,
|
||||||
|
() => {
|
||||||
|
if (selectedDepIds.value.length > 0) {
|
||||||
|
getAllUsersInDepart();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selectedDepId,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal && (props.role === 1 || props.role === '1')) {
|
||||||
|
selectedDepIds.value = [resolveDepartId(newVal)];
|
||||||
|
getAllUsersInDepart();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function initData() {
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
const depId = props.selectedDepId || props.departId;
|
||||||
|
if (depId) {
|
||||||
|
selectedDepIds.value = [resolveDepartId(depId)];
|
||||||
|
getAllUsersInDepart();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const resolvedId = resolveDepartId(props.departId);
|
||||||
|
selectedDepIds.value = [resolvedId];
|
||||||
|
getAllUsersInDepart();
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
if (props.checkedUser) {
|
||||||
|
selectedRows.value = [...props.checkedUser];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadData(arg?: number) {
|
||||||
|
if (selectedDepIds.value.length > 0) {
|
||||||
|
if (arg === 1) {
|
||||||
|
ipagination.current = 1;
|
||||||
|
}
|
||||||
|
const params = getQueryParams();
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res: any = await defHttp.get({ url: url.pageUserDepart, params });
|
||||||
|
dataSource.value = (res.records || []).filter((user: UserItem) => {
|
||||||
|
const userLevel = user.userSecurityLevel;
|
||||||
|
const docLevel = props.secLevel;
|
||||||
|
return userLevel !== null && userLevel > docLevel;
|
||||||
|
});
|
||||||
|
ipagination.total = res.total;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryParams() {
|
||||||
|
const param = Object.assign({}, queryParam, isorter);
|
||||||
|
param.searchSecurityLevel = props.secLevel;
|
||||||
|
param.field = getQueryField();
|
||||||
|
param.pageNo = ipagination.current;
|
||||||
|
param.pageSize = ipagination.pageSize;
|
||||||
|
param.departId = selectedDepIds.value.join(',');
|
||||||
|
param.isSpecial = props.isSpecial;
|
||||||
|
return filterObj(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryField() {
|
||||||
|
let str = 'id,';
|
||||||
|
for (let a = 0; a < columns.length; a++) {
|
||||||
|
str += ',' + columns[a].dataIndex;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchReset(num?: number) {
|
||||||
|
if (num !== 0) {
|
||||||
|
queryParam.realname = '';
|
||||||
|
loadData(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTableChange(pagination: any, _filters: any, sorter: any) {
|
||||||
|
if (Object.keys(sorter).length > 0) {
|
||||||
|
isorter.column = sorter.field;
|
||||||
|
isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc';
|
||||||
|
}
|
||||||
|
Object.assign(ipagination, pagination);
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAllUsersInDepart() {
|
||||||
|
if (selectedDepIds.value.length === 0 || (selectedDepIds.value.length === 1 && !selectedDepIds.value[0])) {
|
||||||
|
console.warn('getAllUsersInDepart: selectedDepIds is empty, skip loading');
|
||||||
|
allUserList.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = Object.assign({}, queryParam);
|
||||||
|
param.searchSecurityLevel = props.secLevel;
|
||||||
|
param.field = getQueryField();
|
||||||
|
param.departId = selectedDepIds.value.join(',');
|
||||||
|
|
||||||
|
param.isSpecial = props.isSpecial;
|
||||||
|
param.pageNo = 1;
|
||||||
|
param.pageSize = 9999;
|
||||||
|
const filteredParam = filterObj(param);
|
||||||
|
try {
|
||||||
|
const res: any = await defHttp.get({ url: url.listUserDepart, params: filteredParam });
|
||||||
|
allUserList.value = (res.records || res).filter((user: UserItem) => {
|
||||||
|
const userLevel = user.userSecurityLevel;
|
||||||
|
const docLevel = props.secLevel;
|
||||||
|
return userLevel !== null && userLevel > docLevel;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('getAllUsersInDepart error:', e);
|
||||||
|
allUserList.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDepSelect(keys: string[]) {
|
||||||
|
if (keys.length > 0 && keys[0] != null) {
|
||||||
|
selectedDepIds.value = [keys[0]];
|
||||||
|
emit('update:selectedDepId', keys[0]);
|
||||||
|
loadData(1);
|
||||||
|
getAllUsersInDepart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelect(record: UserItem, selected: boolean) {
|
||||||
|
if (selected) {
|
||||||
|
pushIfNotExist(selectedRows.value, record, 'id');
|
||||||
|
} else {
|
||||||
|
deleteIfExist(selectedRows.value, record, 'id');
|
||||||
|
}
|
||||||
|
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
|
||||||
|
emit('update:checkedUser', selectedRows.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelectAll(selected: boolean, selectedRecords: UserItem[], changeRecords: UserItem[]) {
|
||||||
|
if (selected) {
|
||||||
|
selectedRecords.forEach((item) => {
|
||||||
|
pushIfNotExist(selectedRows.value, item, 'id');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const rowsToRemove = changeRecords && changeRecords.length > 0 ? changeRecords : dataSource.value;
|
||||||
|
rowsToRemove.forEach((item) => {
|
||||||
|
deleteIfExist(selectedRows.value, item, 'id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
|
||||||
|
emit('update:checkedUser', selectedRows.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearch() {
|
||||||
|
loadData(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queryDepartTree() {
|
||||||
|
expandedKeys.value = [];
|
||||||
|
departTree.value = [];
|
||||||
|
try {
|
||||||
|
const res: any = await queryTreeList();
|
||||||
|
const arr = [...res];
|
||||||
|
const departId = props.departId;
|
||||||
|
|
||||||
|
fullDepartTree.value = [...arr];
|
||||||
|
|
||||||
|
if (props.role === 1 || props.role === '1') {
|
||||||
|
departTree.value = [...arr];
|
||||||
|
} else {
|
||||||
|
const resolvedDepartId = resolveDepartId(departId);
|
||||||
|
const tree = recurTree(arr, (node) => {
|
||||||
|
if (node.id === resolvedDepartId) return [node];
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
departTree.value = [...tree];
|
||||||
|
}
|
||||||
|
departTree.value.forEach((item) => {
|
||||||
|
expandedKeys.value.push(item.key);
|
||||||
|
});
|
||||||
|
isTreeLoaded.value = true;
|
||||||
|
await initData();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('queryDepartTree error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAll() {
|
||||||
|
allUserList.value.forEach((item) => {
|
||||||
|
pushIfNotExist(selectedRows.value, item, 'id');
|
||||||
|
});
|
||||||
|
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
|
||||||
|
emit('update:checkedUser', selectedRows.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unselectAll() {
|
||||||
|
allUserList.value.forEach((item) => {
|
||||||
|
deleteIfExist(selectedRows.value, item, 'id');
|
||||||
|
});
|
||||||
|
emit('update:checkedUser', selectedRows.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function customRow(record: UserItem) {
|
||||||
|
return {
|
||||||
|
onClick: (_event: Event) => {
|
||||||
|
const clickRowKey = record.id;
|
||||||
|
if (selectedRowKeys.value.includes(clickRowKey)) {
|
||||||
|
deleteIfExist(selectedRows.value, record, 'id');
|
||||||
|
} else {
|
||||||
|
pushIfNotExist(selectedRows.value, record, 'id');
|
||||||
|
}
|
||||||
|
selectedRows.value.sort((a, b) => (a.sortno || 0) - (b.sortno || 0));
|
||||||
|
emit('update:checkedUser', selectedRows.value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
resetScreenSize();
|
||||||
|
await queryDepartTree();
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetScreenSize() {
|
||||||
|
const screenWidth = document.body.clientWidth;
|
||||||
|
if (screenWidth < 500) {
|
||||||
|
scrollTrigger.value = { x: 800 };
|
||||||
|
} else {
|
||||||
|
scrollTrigger.value = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ant-table-tbody .ant-table-row td {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
<template>
|
||||||
|
<div style="margin-left: 1px">
|
||||||
|
<a-empty v-if="!haveSelected" />
|
||||||
|
<div v-else>
|
||||||
|
<a-button-group v-for="(depart, index) in selectedMainDeparts" :key="'main-' + depart.id + '-' + index" class="small-space selected-depart">
|
||||||
|
<a-dropdown>
|
||||||
|
<template v-if="!disabled" #overlay>
|
||||||
|
<a-menu @click="handMainDepartAssignType($event, index)">
|
||||||
|
<a-menu-item key="1">办</a-menu-item>
|
||||||
|
<a-menu-item key="2">协</a-menu-item>
|
||||||
|
<a-menu-item key="3">阅</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-red" size="small">
|
||||||
|
<ApartmentOutlined />
|
||||||
|
办
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<a-descriptions title="部门信息" :column="1" style="width: 300px">
|
||||||
|
<a-descriptions-item label="部门名">{{ depart.title }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="是否有子部门">{{ depart.children ? '是' : '否' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="部门描述">{{ depart.description }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-red" size="small">{{ depart.title }}</a-button>
|
||||||
|
</a-popover>
|
||||||
|
<a-button v-if="!disabled" class="btn-red" size="small" @click="deleteSelectedMainDepart(index)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
|
||||||
|
<a-button-group v-for="(depart, index) in selectedAssistDeparts" :key="'assist-' + depart.id + '-' + index" class="small-space selected-depart">
|
||||||
|
<a-dropdown>
|
||||||
|
<template v-if="!disabled" #overlay>
|
||||||
|
<a-menu @click="handAssistDepartAssignType($event, index)">
|
||||||
|
<a-menu-item key="1">办</a-menu-item>
|
||||||
|
<a-menu-item key="2">协</a-menu-item>
|
||||||
|
<a-menu-item key="3">阅</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-blue" size="small">
|
||||||
|
<ApartmentOutlined />
|
||||||
|
协
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<a-descriptions title="部门信息" :column="1" style="width: 300px">
|
||||||
|
<a-descriptions-item label="部门名">{{ depart.title }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="是否有子部门">{{ depart.children ? '是' : '否' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="部门描述">{{ depart.description }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-blue" size="small">{{ depart.title }}</a-button>
|
||||||
|
</a-popover>
|
||||||
|
<a-button v-if="!disabled" class="btn-blue" size="small" @click="deleteSelectedAssistDepart(index)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
|
||||||
|
<a-button-group v-for="(depart, index) in selectedReadDeparts" :key="'read-' + depart.id + '-' + index" class="small-space selected-depart">
|
||||||
|
<a-dropdown>
|
||||||
|
<template v-if="!disabled" #overlay>
|
||||||
|
<a-menu @click="handReadDepartAssignType($event, index)">
|
||||||
|
<a-menu-item key="1">办</a-menu-item>
|
||||||
|
<a-menu-item key="2">协</a-menu-item>
|
||||||
|
<a-menu-item key="3">阅</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-green" size="small">
|
||||||
|
<ApartmentOutlined />
|
||||||
|
阅
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<a-descriptions title="部门信息" :column="1" style="width: 300px">
|
||||||
|
<a-descriptions-item label="部门名">{{ depart.title }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="是否有子部门">{{ depart.children ? '是' : '否' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="部门描述">{{ depart.description }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-green" size="small">{{ depart.title }}</a-button>
|
||||||
|
</a-popover>
|
||||||
|
<a-button v-if="!disabled" class="btn-green" size="small" @click="deleteSelectedReadDepart(index)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
|
||||||
|
<a-button-group v-for="(user, index) in selectedUsers" :key="'user-' + user.id + '-' + index" class="small-space selected-user-do">
|
||||||
|
<a-dropdown>
|
||||||
|
<template v-if="!disabled" #overlay>
|
||||||
|
<a-menu @click="handUserAssignType($event, index)">
|
||||||
|
<a-menu-item key="1">办</a-menu-item>
|
||||||
|
<a-menu-item key="3">阅</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-red" size="small">
|
||||||
|
<UserOutlined />
|
||||||
|
办
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<a-descriptions title="人员信息" :column="1" style="width: 300px">
|
||||||
|
<a-descriptions-item label="姓名">{{ user.realname }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="性别">{{ user.sex_dictText }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="密级">{{ user.userSecurityLevel_dictText }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="所属部门">{{ user.orgCodeTxt }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-red" size="small">{{ user.realname }}</a-button>
|
||||||
|
</a-popover>
|
||||||
|
<a-button v-if="!disabled" class="btn-red" size="small" @click="deleteSelectedUsers(index)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
|
||||||
|
<a-button-group v-for="(user, index) in selectedReadUsers" :key="'readuser-' + user.id + '-' + index" class="small-space selected-user-do">
|
||||||
|
<a-dropdown>
|
||||||
|
<template v-if="!disabled" #overlay>
|
||||||
|
<a-menu @click="handReadUserAssignType($event, index)">
|
||||||
|
<a-menu-item key="1">办</a-menu-item>
|
||||||
|
<a-menu-item key="3">阅</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-green" size="small">
|
||||||
|
<UserOutlined />
|
||||||
|
阅
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<a-popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<a-descriptions title="人员信息" :column="1" style="width: 300px">
|
||||||
|
<a-descriptions-item label="姓名">{{ user.realname }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="性别">{{ user.sex_dictText }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="密级">{{ user.userSecurityLevel_dictText }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="所属部门">{{ user.orgCodeTxt }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</template>
|
||||||
|
<a-button class="btn-green" size="small">{{ user.realname }}</a-button>
|
||||||
|
</a-popover>
|
||||||
|
<a-button v-if="!disabled" class="btn-green" size="small" @click="deleteSelectedReadUser(index)">
|
||||||
|
<CloseOutlined />
|
||||||
|
</a-button>
|
||||||
|
</a-button-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { ApartmentOutlined, UserOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
|
import type { DepartTreeNode, UserItem } from './SzDistribute.api';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
disabled?: boolean;
|
||||||
|
selectedMainDeparts: DepartTreeNode[];
|
||||||
|
selectedAssistDeparts: DepartTreeNode[];
|
||||||
|
selectedReadDeparts: DepartTreeNode[];
|
||||||
|
selectedUsers: UserItem[];
|
||||||
|
selectedReadUsers: UserItem[];
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
selectedMainDeparts: () => [],
|
||||||
|
selectedAssistDeparts: () => [],
|
||||||
|
selectedReadDeparts: () => [],
|
||||||
|
selectedUsers: () => [],
|
||||||
|
selectedReadUsers: () => [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:selectedMainDeparts',
|
||||||
|
'update:selectedAssistDeparts',
|
||||||
|
'update:selectedReadDeparts',
|
||||||
|
'update:selectedUsers',
|
||||||
|
'update:selectedReadUsers',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const haveSelected = computed(() => {
|
||||||
|
return !(
|
||||||
|
props.selectedMainDeparts.length === 0 &&
|
||||||
|
props.selectedAssistDeparts.length === 0 &&
|
||||||
|
props.selectedReadDeparts.length === 0 &&
|
||||||
|
props.selectedUsers.length === 0 &&
|
||||||
|
props.selectedReadUsers.length === 0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function emitChanges(payload: {
|
||||||
|
mainDeparts?: DepartTreeNode[];
|
||||||
|
assistDeparts?: DepartTreeNode[];
|
||||||
|
readDeparts?: DepartTreeNode[];
|
||||||
|
users?: UserItem[];
|
||||||
|
readUsers?: UserItem[];
|
||||||
|
}) {
|
||||||
|
if (payload.mainDeparts) emit('update:selectedMainDeparts', payload.mainDeparts);
|
||||||
|
if (payload.assistDeparts) emit('update:selectedAssistDeparts', payload.assistDeparts);
|
||||||
|
if (payload.readDeparts) emit('update:selectedReadDeparts', payload.readDeparts);
|
||||||
|
if (payload.users) emit('update:selectedUsers', payload.users);
|
||||||
|
if (payload.readUsers) emit('update:selectedReadUsers', payload.readUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handMainDepartAssignType(e: { key: string }, index: number) {
|
||||||
|
if (e.key === '2') {
|
||||||
|
const fromArray = [...props.selectedMainDeparts];
|
||||||
|
const targetArray = [...props.selectedAssistDeparts];
|
||||||
|
fromArray[index].assignType = 2;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ mainDeparts: fromArray, assistDeparts: targetArray });
|
||||||
|
} else if (e.key === '3') {
|
||||||
|
const fromArray = [...props.selectedMainDeparts];
|
||||||
|
const targetArray = [...props.selectedReadDeparts];
|
||||||
|
fromArray[index].assignType = 3;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ mainDeparts: fromArray, readDeparts: targetArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handAssistDepartAssignType(e: { key: string }, index: number) {
|
||||||
|
if (e.key === '1') {
|
||||||
|
const fromArray = [...props.selectedAssistDeparts];
|
||||||
|
const targetArray = [...props.selectedMainDeparts];
|
||||||
|
fromArray[index].assignType = 1;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ assistDeparts: fromArray, mainDeparts: targetArray });
|
||||||
|
} else if (e.key === '3') {
|
||||||
|
const fromArray = [...props.selectedAssistDeparts];
|
||||||
|
const targetArray = [...props.selectedReadDeparts];
|
||||||
|
fromArray[index].assignType = 3;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ assistDeparts: fromArray, readDeparts: targetArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handReadDepartAssignType(e: { key: string }, index: number) {
|
||||||
|
if (e.key === '1') {
|
||||||
|
const fromArray = [...props.selectedReadDeparts];
|
||||||
|
const targetArray = [...props.selectedMainDeparts];
|
||||||
|
fromArray[index].assignType = 1;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ readDeparts: fromArray, mainDeparts: targetArray });
|
||||||
|
} else if (e.key === '2') {
|
||||||
|
const fromArray = [...props.selectedReadDeparts];
|
||||||
|
const targetArray = [...props.selectedAssistDeparts];
|
||||||
|
fromArray[index].assignType = 2;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ readDeparts: fromArray, assistDeparts: targetArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handUserAssignType(e: { key: string }, index: number) {
|
||||||
|
if (e.key === '3') {
|
||||||
|
const fromArray = [...props.selectedUsers];
|
||||||
|
const targetArray = [...props.selectedReadUsers];
|
||||||
|
fromArray[index].assignType = 3;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ users: fromArray, readUsers: targetArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handReadUserAssignType(e: { key: string }, index: number) {
|
||||||
|
if (e.key === '1') {
|
||||||
|
const fromArray = [...props.selectedReadUsers];
|
||||||
|
const targetArray = [...props.selectedUsers];
|
||||||
|
fromArray[index].assignType = 1;
|
||||||
|
targetArray.push(fromArray[index]);
|
||||||
|
fromArray.splice(index, 1);
|
||||||
|
emitChanges({ readUsers: fromArray, users: targetArray });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedMainDepart(index: number) {
|
||||||
|
const changeArray = [...props.selectedMainDeparts];
|
||||||
|
changeArray.splice(index, 1);
|
||||||
|
emitChanges({ mainDeparts: changeArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedAssistDepart(index: number) {
|
||||||
|
const changeArray = [...props.selectedAssistDeparts];
|
||||||
|
changeArray.splice(index, 1);
|
||||||
|
emitChanges({ assistDeparts: changeArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedReadDepart(index: number) {
|
||||||
|
const changeArray = [...props.selectedReadDeparts];
|
||||||
|
changeArray.splice(index, 1);
|
||||||
|
emitChanges({ readDeparts: changeArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedUsers(index: number) {
|
||||||
|
const changeArray = [...props.selectedUsers];
|
||||||
|
changeArray.splice(index, 1);
|
||||||
|
emitChanges({ users: changeArray });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSelectedReadUser(index: number) {
|
||||||
|
const changeArray = [...props.selectedReadUsers];
|
||||||
|
changeArray.splice(index, 1);
|
||||||
|
emitChanges({ readUsers: changeArray });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.small-space {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
.selected-depart {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.selected-user-do {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.btn-red {
|
||||||
|
background-color: #ff4d4f;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #ff4d4f;
|
||||||
|
}
|
||||||
|
.btn-blue {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
.btn-green {
|
||||||
|
background-color: #52c41a;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #52c41a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
# SzDistribute 分发方案组件 使用说明
|
||||||
|
|
||||||
|
## 组件概述
|
||||||
|
|
||||||
|
SzDistribute 是一个公文分发方案组件,支持按部门(主办/协办/阅文)和人员(办文/阅文)进行分发配置,并提供方案保存/加载功能。
|
||||||
|
|
||||||
|
基于 JeecgBoot 3.8.0 + Vue 3 + TypeScript 开发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
distributeComponent/
|
||||||
|
├── SzDistribute.vue # 主入口组件(分发方案弹窗)
|
||||||
|
├── SzSelectDepart.vue # 部门选择组件(树形多选 + 办/协/阅标记)
|
||||||
|
├── SzSelectUser.vue # 人员选择组件(部门树 + 人员表格)
|
||||||
|
├── SzSelectedDepartsAndUsers.vue # 已选部门和人员展示组件
|
||||||
|
├── SzDistribute.api.ts # 方案增删改查 API
|
||||||
|
├── types.ts # TypeScript 类型定义
|
||||||
|
├── utils.ts # 工具函数
|
||||||
|
└── USAGE.md # 本文件
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<SzDistribute
|
||||||
|
title="分发方案"
|
||||||
|
:secLevel="secLevel"
|
||||||
|
:role="role"
|
||||||
|
:departId="departId"
|
||||||
|
:docId="docId"
|
||||||
|
:isSpecial="isSpecial"
|
||||||
|
v-model:checkedMainDepartIds="checkedMainDepartIds"
|
||||||
|
v-model:checkedAssistDepartIds="checkedAssistDepartIds"
|
||||||
|
v-model:checkedReadDepartIds="checkedReadDepartIds"
|
||||||
|
v-model:checkedUsernames="checkedUsernames"
|
||||||
|
v-model:checkedReadUsernames="checkedReadUsernames"
|
||||||
|
v-model:reasons="reasons"
|
||||||
|
@change="handleDistributeChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import SzDistribute from '/@/components/semri/distributeComponent/SzDistribute.vue';
|
||||||
|
|
||||||
|
const secLevel = 3; // 密级:1=非密, 2=内部, 3=秘密, 4=机密
|
||||||
|
const role = 1; // 角色:1=领导, 2=部门负责人
|
||||||
|
const departId = 'A01'; // 当前用户部门(支持 orgCode 或 UUID)
|
||||||
|
const docId = ''; // 公文ID(可选,用于排除已分发部门)
|
||||||
|
const isSpecial = 0; // 是否特殊分发
|
||||||
|
|
||||||
|
const checkedMainDepartIds = ref('');
|
||||||
|
const checkedAssistDepartIds = ref('');
|
||||||
|
const checkedReadDepartIds = ref('');
|
||||||
|
const checkedUsernames = ref('');
|
||||||
|
const checkedReadUsernames = ref('');
|
||||||
|
const reasons = ref('');
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SzDistribute 主组件 API
|
||||||
|
|
||||||
|
### Props(输入)
|
||||||
|
|
||||||
|
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
||||||
|
|------|------|------|--------|------|
|
||||||
|
| `secLevel` | `Number` | 是 | - | 文件密级。1=非密, 2=内部, 3=秘密, 4=机密 |
|
||||||
|
| `role` | `Number \| String` | 是 | `1` | 当前用户角色。1=领导, 2=部门负责人 |
|
||||||
|
| `departId` | `String` | 是 | `''` | 当前用户部门ID。支持 orgCode(如 `"A01"`)或 UUID 格式,组件内部自动转换 |
|
||||||
|
| `docId` | `String` | 否 | `''` | 公文ID。传入后自动排除该公文已分发的部门 |
|
||||||
|
| `isSpecial` | `Number` | 否 | `0` | 是否特殊分发。0=否, 1=是 |
|
||||||
|
| `title` | `String` | 否 | `'分发方案'` | 弹窗标题(会自动追加密级文字) |
|
||||||
|
| `width` | `Number \| String` | 否 | `1500` | 弹窗宽度(px) |
|
||||||
|
| `showButton` | `Boolean` | 否 | `true` | 是否显示"分发对象"触发按钮。设为 `false` 时需通过 ref 调用 `showModal()` |
|
||||||
|
|
||||||
|
### v-model(双向绑定)
|
||||||
|
|
||||||
|
| 属性 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `checkedMainDepartIds` | `String` | 主办部门ID列表,逗号分隔,如 `"id1,id2,id3"` |
|
||||||
|
| `checkedAssistDepartIds` | `String` | 协办部门ID列表,逗号分隔 |
|
||||||
|
| `checkedReadDepartIds` | `String` | 阅文部门ID列表,逗号分隔 |
|
||||||
|
| `checkedUsernames` | `String` | 办文人员用户名列表,逗号分隔,如 `"zhangsan,lisi"` |
|
||||||
|
| `checkedReadUsernames` | `String` | 阅文人员用户名列表,逗号分隔 |
|
||||||
|
| `reasons` | `String` | 分发意见/备注文本 |
|
||||||
|
| `modelValue` | `String` | 完整分发数据字符串(内部格式) |
|
||||||
|
|
||||||
|
### Events(事件)
|
||||||
|
|
||||||
|
| 事件 | 参数 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `change` | `distributeStr: string` | 分发方案确认时触发 |
|
||||||
|
| `update:checkedDepartIds` | `ids: string` | 所有已选部门ID(逗号分隔) |
|
||||||
|
| `update:checkedDeparts` | `DepartTreeNode[]` | 所有已选部门节点对象数组 |
|
||||||
|
|
||||||
|
### Methods(通过 ref 调用)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const distributeRef = ref();
|
||||||
|
|
||||||
|
// 手动打开分发弹窗
|
||||||
|
distributeRef.value.showModal();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 角色权限说明
|
||||||
|
|
||||||
|
### 领导(role=1)
|
||||||
|
|
||||||
|
- 部门选择 Tab:显示全部部门树,可自由选择主办/协办/阅文
|
||||||
|
- 办文人员 Tab:显示全部部门树,选择部门后加载该部门人员
|
||||||
|
- 阅文人员 Tab:同办文人员
|
||||||
|
- 默认激活 Tab:选择部门
|
||||||
|
|
||||||
|
### 部门负责人(role=2)
|
||||||
|
|
||||||
|
- 部门选择 Tab:根据当前部门的类型(职能/业务/科室)显示可见部门
|
||||||
|
- 办文人员 Tab:只显示本部门及下级部门的人员
|
||||||
|
- 阅文人员 Tab:同办文人员
|
||||||
|
- 默认激活 Tab:选择阅文人员(因部门负责人通常负责阅文分发)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 子组件说明
|
||||||
|
|
||||||
|
### SzSelectDepart — 部门选择
|
||||||
|
|
||||||
|
树形结构展示部门,每个选中的部门可标记为「办」「协」「阅」三种类型。
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
|
||||||
|
| 属性 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `selectedMainDeparts` | `DepartTreeNode[]` | 当前主办部门列表 |
|
||||||
|
| `selectedAssistDeparts` | `DepartTreeNode[]` | 当前协办部门列表 |
|
||||||
|
| `selectedReadDeparts` | `DepartTreeNode[]` | 当前阅文部门列表 |
|
||||||
|
| `excludeDeparts` | `string[]` | 需排除的部门ID列表 |
|
||||||
|
| `departId` | `string` | 当前用户部门ID |
|
||||||
|
| `role` | `string \| number` | 用户角色 |
|
||||||
|
|
||||||
|
**Events:** `update:selectedMainDeparts`, `update:selectedAssistDeparts`, `update:selectedReadDeparts`
|
||||||
|
|
||||||
|
**快捷操作:**
|
||||||
|
- 全选部门 / 全选职能部门 / 全选业务部门 / 全选本部门科室
|
||||||
|
- 每种全选可指定办/协/阅类型
|
||||||
|
- 右键节点文字可快速切换选中状态
|
||||||
|
|
||||||
|
### SzSelectUser — 人员选择
|
||||||
|
|
||||||
|
左侧部门树 + 右侧人员表格,支持搜索、分页、全选。
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
|
||||||
|
| 属性 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `secLevel` | `any` | 文件密级,用于过滤人员(只显示密级 > 文件密级的人员) |
|
||||||
|
| `checkedUser` | `UserItem[]` | 已选中的人员列表 |
|
||||||
|
| `selectedDepId` | `string` | 当前选中的部门ID |
|
||||||
|
| `departId` | `string` | 当前用户部门ID(支持 orgCode 或 UUID) |
|
||||||
|
| `role` | `string \| number` | 用户角色 |
|
||||||
|
| `isSpecial` | `number` | 是否特殊分发 |
|
||||||
|
| `multi` | `boolean` | 是否多选(默认 `true`) |
|
||||||
|
|
||||||
|
**v-model:** `selectedDepId` — 当前选中部门
|
||||||
|
|
||||||
|
**Events:** `update:checkedUser`, `update:selectedDepId`
|
||||||
|
|
||||||
|
**密级过滤规则:** 只显示 `人员密级 > 文件密级` 的人员。例如文件密级为 3(秘密),则只显示密级为 4(机密)的人员。
|
||||||
|
|
||||||
|
### SzSelectedDepartsAndUsers — 已选展示
|
||||||
|
|
||||||
|
以标签形式展示已选择的部门和人员,支持删除和办/协/阅类型切换。
|
||||||
|
|
||||||
|
**Props:**
|
||||||
|
|
||||||
|
| 属性 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `disabled` | `boolean` | 是否禁用操作(默认 `false`) |
|
||||||
|
| `selectedMainDeparts` | `DepartTreeNode[]` | 主办部门 |
|
||||||
|
| `selectedAssistDeparts` | `DepartTreeNode[]` | 协办部门 |
|
||||||
|
| `selectedReadDeparts` | `DepartTreeNode[]` | 阅文部门 |
|
||||||
|
| `selectedUsers` | `UserItem[]` | 办文人员 |
|
||||||
|
| `selectedReadUsers` | `UserItem[]` | 阅文人员 |
|
||||||
|
|
||||||
|
**Events:** `update:selectedMainDeparts`, `update:selectedAssistDeparts`, `update:selectedReadDeparts`, `update:selectedUsers`, `update:selectedReadUsers`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 类型定义
|
||||||
|
|
||||||
|
### DepartTreeNode
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface DepartTreeNode {
|
||||||
|
id: string; // 部门 UUID
|
||||||
|
key: string; // 树节点 key(等于 id)
|
||||||
|
value: string; // 树节点 value(等于 id)
|
||||||
|
title: string; // 部门名称
|
||||||
|
isLeaf?: boolean; // 是否叶子节点
|
||||||
|
departType?: string | number; // 部门类型:0=根, 1=职能, 2=业务, 3=科室
|
||||||
|
distributable?: number; // 是否可分发:1=是
|
||||||
|
disableCheckbox?: boolean; // 是否禁用选择
|
||||||
|
assignType?: number; // 分配类型:1=办, 2=协, 3=阅
|
||||||
|
children?: DepartTreeNode[]; // 子部门
|
||||||
|
description?: string; // 部门描述
|
||||||
|
parentId?: string; // 父部门ID
|
||||||
|
orgCode?: string; // 部门编码(如 "A01", "A03A02")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### UserItem
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface UserItem {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
realname: string;
|
||||||
|
orgCodeTxt?: string; // 所属部门名称
|
||||||
|
userSecurityLevel?: number; // 人员密级数值
|
||||||
|
userSecurityLevel_dictText?: string; // 人员密级文字
|
||||||
|
sex_dictText?: string; // 性别
|
||||||
|
sortno?: number; // 排序号
|
||||||
|
departId?: string;
|
||||||
|
departName?: string;
|
||||||
|
assignType?: number; // 分配类型:1=办, 3=阅
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## departId 说明
|
||||||
|
|
||||||
|
`departId` 属性支持两种格式,组件内部会自动识别并转换:
|
||||||
|
|
||||||
|
| 格式 | 示例 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| orgCode | `"A01"`, `"A03A02"` | 部门编码,通常来自 `userInfo.orgCode` |
|
||||||
|
| UUID | `"f4d5979edc1d4785ba2322f0b8200a21"` | 部门唯一标识 |
|
||||||
|
|
||||||
|
推荐从 `useUserStore().getUserInfo.orgCode` 获取:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const departId = userStore.getUserInfo?.orgCode || '';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案管理
|
||||||
|
|
||||||
|
组件内置方案保存/加载功能,数据存储在后端 `djOfficialdocDistribute` 表中。
|
||||||
|
|
||||||
|
- **保存方案**:输入方案名 → 点击"保存为方案"
|
||||||
|
- **加载方案**:在弹窗左侧已存方案列表中点击方案名
|
||||||
|
- **快速应用**:在弹窗外部的方案按钮直接点击即可应用并提交
|
||||||
|
- **删除方案**:点击方案旁的关闭按钮
|
||||||
|
|
||||||
|
加载方案时会自动校验人员状态(密级变更、部门变动、离职冻结),异常人员会弹出提示并自动移除。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 依赖的后端接口
|
||||||
|
|
||||||
|
| 接口 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `GET /sys/sysDepart/queryTreeList` | 获取部门树 |
|
||||||
|
| `GET /sys/user/queryUserComponentData` | 查询部门下的人员 |
|
||||||
|
| `GET /officialdocDistribute/djOfficialdocDistribute/list` | 方案列表 |
|
||||||
|
| `POST /officialdocDistribute/djOfficialdocDistribute/add` | 新增方案 |
|
||||||
|
| `POST /officialdocDistribute/djOfficialdocDistribute/edit` | 编辑方案 |
|
||||||
|
| `DELETE /officialdocDistribute/djOfficialdocDistribute/delete` | 删除方案 |
|
||||||
|
| `GET /distributionProcessinfo/officialdocDistributionProcessinfo/queryDeptIds` | 查询公文已分发部门ID |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试页面
|
||||||
|
|
||||||
|
访问 `/semri/test/SzDistributeTest` 可查看组件演示和所有输入输出字段。
|
||||||
@@ -0,0 +1,351 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sz-distribute-demo-container">
|
||||||
|
<a-card title="SzDistribute 分发方案组件示例" style="margin-bottom: 16px">
|
||||||
|
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" style="max-width: 600px">
|
||||||
|
<a-form-item label="密级(secLevel)">
|
||||||
|
<a-radio-group v-model:value="formData.secLevel">
|
||||||
|
<a-radio :value="1">非密</a-radio>
|
||||||
|
<a-radio :value="2">内部</a-radio>
|
||||||
|
<a-radio :value="3">秘密</a-radio>
|
||||||
|
<a-radio :value="4">机密</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="角色(role)">
|
||||||
|
<a-radio-group v-model:value="formData.role">
|
||||||
|
<a-radio :value="1">领导(role=1)</a-radio>
|
||||||
|
<a-radio :value="2">部门负责人(role=2)</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="当前部门ID">
|
||||||
|
<a-input v-model:value="formData.departId" placeholder="输入部门ID" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="公文ID(docId)">
|
||||||
|
<a-input v-model:value="formData.docId" placeholder="输入公文ID(可选,用于排除已分发部门)" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="是否特殊">
|
||||||
|
<a-switch v-model:checked="isSpecialChecked" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<SzDistribute
|
||||||
|
ref="distributeRef"
|
||||||
|
title="分发方案"
|
||||||
|
:secLevel="formData.secLevel"
|
||||||
|
:role="formData.role"
|
||||||
|
:departId="formData.departId"
|
||||||
|
:docId="formData.docId"
|
||||||
|
:isSpecial="isSpecialChecked ? 1 : 0"
|
||||||
|
v-model:checkedMainDepartIds="formData.checkedMainDepartIds"
|
||||||
|
v-model:checkedAssistDepartIds="formData.checkedAssistDepartIds"
|
||||||
|
v-model:checkedReadDepartIds="formData.checkedReadDepartIds"
|
||||||
|
v-model:checkedUsernames="formData.checkedUsernames"
|
||||||
|
v-model:checkedReadUsernames="formData.checkedReadUsernames"
|
||||||
|
v-model:reasons="formData.reasons"
|
||||||
|
@change="handleDistributeChange"
|
||||||
|
@update:checkedDepartIds="handleCheckedDepartIdsChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div style="margin-top: 16px">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="handleSave" :loading="saving">模拟保存</a-button>
|
||||||
|
<a-button @click="handleReset">重置</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="输入字段(Input)" style="margin-bottom: 16px">
|
||||||
|
<a-descriptions :column="1" size="small" bordered>
|
||||||
|
<a-descriptions-item label="密级(secLevel)">
|
||||||
|
{{ formData.secLevel }} - {{ formData.secLevel === 1 ? '非密' : formData.secLevel === 2 ? '内部' : formData.secLevel === 3 ? '秘密' : '机密' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="角色(role)">
|
||||||
|
{{ formData.role }} - {{ formData.role === 1 ? '领导' : '部门负责人' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="当前部门ID(departId)">
|
||||||
|
{{ formData.departId || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="公文ID(docId)">
|
||||||
|
{{ formData.docId || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="是否特殊(isSpecial)">
|
||||||
|
{{ isSpecialChecked ? '是(1)' : '否(0)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="输出字段(Output)" style="margin-bottom: 16px">
|
||||||
|
<a-descriptions :column="1" size="small" bordered>
|
||||||
|
<a-descriptions-item label="主办部门IDs(checkedMainDepartIds)">
|
||||||
|
{{ formData.checkedMainDepartIds || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="协办部门IDs(checkedAssistDepartIds)">
|
||||||
|
{{ formData.checkedAssistDepartIds || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="阅文部门IDs(checkedReadDepartIds)">
|
||||||
|
{{ formData.checkedReadDepartIds || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="办文人员用户名(checkedUsernames)">
|
||||||
|
{{ formData.checkedUsernames || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="阅文人员用户名(checkedReadUsernames)">
|
||||||
|
{{ formData.checkedReadUsernames || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="分发意见(reasons)">
|
||||||
|
{{ formData.reasons || '(空)' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="字段含义说明" style="margin-bottom: 16px">
|
||||||
|
<a-collapse>
|
||||||
|
<a-collapse-panel key="input" header="输入字段说明">
|
||||||
|
<a-table :columns="inputColumns" :data-source="inputFields" :pagination="false" size="small" />
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="output" header="输出字段说明">
|
||||||
|
<a-table :columns="outputColumns" :data-source="outputFields" :pagination="false" size="small" />
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="事件日志">
|
||||||
|
<div class="event-log-wrapper">
|
||||||
|
<a-button size="small" @click="eventLogs = []" style="margin-bottom: 8px">清空日志</a-button>
|
||||||
|
<div class="event-log-list">
|
||||||
|
<div v-for="(log, idx) in eventLogs" :key="idx" class="event-log-item">
|
||||||
|
<a-tag :color="log.type === 'success' ? 'green' : log.type === 'error' ? 'red' : 'blue'" size="small">
|
||||||
|
{{ log.type }}
|
||||||
|
</a-tag>
|
||||||
|
<span class="log-time">{{ log.time }}</span>
|
||||||
|
<span class="log-msg">{{ log.message }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="eventLogs.length === 0" class="empty-log">暂无日志</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed, onMounted } from 'vue';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { useUserStore } from '/@/store/modules/user';
|
||||||
|
import SzDistribute from '/@/components/semri/distributeComponent/SzDistribute.vue';
|
||||||
|
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const userInfo = computed(() => userStore.getUserInfo);
|
||||||
|
|
||||||
|
const distributeRef = ref<InstanceType<typeof SzDistribute> | null>(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
addLog('info', '页面初始化');
|
||||||
|
addLog('info', `当前用户信息: ${JSON.stringify(userInfo.value)}`);
|
||||||
|
|
||||||
|
if (!formData.departId && userInfo.value?.orgCode) {
|
||||||
|
formData.departId = userInfo.value.orgCode;
|
||||||
|
addLog('info', `自动获取当前用户部门ID: ${formData.departId}`);
|
||||||
|
} else if (!formData.departId) {
|
||||||
|
addLog('warn', '当前用户没有部门信息,请手动输入部门ID或选择一个测试部门ID');
|
||||||
|
// 提供一些测试用的部门ID
|
||||||
|
addLog('info', '测试提示:可以使用部门ID如 "a7d898d8964111efb0b2a4e2a1f0467d" 进行测试');
|
||||||
|
} else {
|
||||||
|
addLog('info', `使用手动输入的部门ID: ${formData.departId}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const eventLogs = ref<any[]>([]);
|
||||||
|
const saving = ref(false);
|
||||||
|
const isSpecialChecked = ref(false);
|
||||||
|
|
||||||
|
const inputColumns = [
|
||||||
|
{ title: '字段名', dataIndex: 'name', key: 'name', width: 200 },
|
||||||
|
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
|
||||||
|
{ title: '必填', dataIndex: 'required', key: 'required', width: 80 },
|
||||||
|
{ title: '说明', dataIndex: 'description', key: 'description' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const inputFields = [
|
||||||
|
{
|
||||||
|
name: 'secLevel',
|
||||||
|
type: 'Number',
|
||||||
|
required: '是',
|
||||||
|
description: '文件密级,1=非密、2=内部、3=秘密、4=机密。用于控制显示的人员密级范围,只显示人员密级 > 文件密级的用户',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
type: 'Number',
|
||||||
|
required: '是',
|
||||||
|
description: '当前用户角色,1=领导、2=部门负责人。不同的角色有不同的分发权限和界面展示',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'departId',
|
||||||
|
type: 'String',
|
||||||
|
required: '是',
|
||||||
|
description: '当前登录用户的部门ID,用于确定当前部门,以便在分发时排除当前部门',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'docId',
|
||||||
|
type: 'String',
|
||||||
|
required: '否',
|
||||||
|
description: '公文ID(可选),用于查询已分发的部门,在重新分发时排除已分发的部门',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'isSpecial',
|
||||||
|
type: 'Number',
|
||||||
|
required: '否',
|
||||||
|
description: '是否特殊分发,0=否、1=是。特殊分发可能有不同的权限控制或业务逻辑',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const outputColumns = [
|
||||||
|
{ title: '字段名', dataIndex: 'name', key: 'name', width: 200 },
|
||||||
|
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
|
||||||
|
{ title: '格式', dataIndex: 'format', key: 'format', width: 200 },
|
||||||
|
{ title: '说明', dataIndex: 'description', key: 'description' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const outputFields = [
|
||||||
|
{
|
||||||
|
name: 'checkedMainDepartIds',
|
||||||
|
type: 'String',
|
||||||
|
format: '逗号分隔的ID字符串',
|
||||||
|
description: '主办部门ID列表,多个部门用逗号分隔,如:"dept1,dept2,dept3"',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'checkedAssistDepartIds',
|
||||||
|
type: 'String',
|
||||||
|
format: '逗号分隔的ID字符串',
|
||||||
|
description: '协办部门ID列表,多个部门用逗号分隔,用于标识协助办理的部门',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'checkedReadDepartIds',
|
||||||
|
type: 'String',
|
||||||
|
format: '逗号分隔的ID字符串',
|
||||||
|
description: '阅文部门ID列表,多个部门用逗号分隔,用于标识仅需要阅读公文的部门',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'checkedUsernames',
|
||||||
|
type: 'String',
|
||||||
|
format: '逗号分隔的用户名字符串',
|
||||||
|
description: '办文人员用户名列表,多个用户用逗号分隔,用于标识需要办理公文的具体人员',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'checkedReadUsernames',
|
||||||
|
type: 'String',
|
||||||
|
format: '逗号分隔的用户名字符串',
|
||||||
|
description: '阅文人员用户名列表,多个用户用逗号分隔,用于标识仅需要阅读公文的具体人员',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reasons',
|
||||||
|
type: 'String',
|
||||||
|
format: '文本字符串',
|
||||||
|
description: '分发意见或备注,用于记录分发的说明或意见',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
secLevel: 3,
|
||||||
|
role: 1 as number,
|
||||||
|
departId: '',
|
||||||
|
docId: '',
|
||||||
|
isSpecial: 0,
|
||||||
|
checkedMainDepartIds: '',
|
||||||
|
checkedAssistDepartIds: '',
|
||||||
|
checkedReadDepartIds: '',
|
||||||
|
checkedUsernames: '',
|
||||||
|
checkedReadUsernames: '',
|
||||||
|
reasons: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
function addLog(type: string, message: string) {
|
||||||
|
const now = new Date();
|
||||||
|
const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
|
||||||
|
eventLogs.value.unshift({ type, message, time });
|
||||||
|
if (eventLogs.value.length > 50) eventLogs.value.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDistributeChange(data: any) {
|
||||||
|
addLog('info', `分发方案变更: ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckedDepartIdsChange(data: string) {
|
||||||
|
addLog('info', `已选部门IDs: ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
saving.value = true;
|
||||||
|
addLog('info', '开始模拟保存...');
|
||||||
|
try {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||||
|
addLog('success', `保存成功!主办部门: ${formData.checkedMainDepartIds || '无'},协办部门: ${formData.checkedAssistDepartIds || '无'},阅文部门: ${formData.checkedReadDepartIds || '无'},办文人员: ${formData.checkedUsernames || '无'},阅文人员: ${formData.checkedReadUsernames || '无'}`);
|
||||||
|
} catch (error: any) {
|
||||||
|
addLog('error', `保存失败: ${error.message || '未知错误'}`);
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset() {
|
||||||
|
formData.secLevel = 3;
|
||||||
|
formData.role = 1;
|
||||||
|
formData.departId = '';
|
||||||
|
formData.docId = '';
|
||||||
|
formData.isSpecial = 0;
|
||||||
|
isSpecialChecked.value = false;
|
||||||
|
formData.checkedMainDepartIds = '';
|
||||||
|
formData.checkedAssistDepartIds = '';
|
||||||
|
formData.checkedReadDepartIds = '';
|
||||||
|
formData.checkedUsernames = '';
|
||||||
|
formData.checkedReadUsernames = '';
|
||||||
|
formData.reasons = '';
|
||||||
|
eventLogs.value = [];
|
||||||
|
addLog('info', '表单已重置');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.sz-distribute-demo-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log-wrapper {
|
||||||
|
.event-log-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-msg {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-log {
|
||||||
|
text-align: center;
|
||||||
|
color: #8c8c8c;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user