feat(projects): 工作台小组件设计
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { computed, ref } from 'vue';
|
||||
import { fetchGetUserManagementRelationTree } from '@/service/api';
|
||||
import type { TreeCheckState } from './use-dept-source';
|
||||
|
||||
type ChainNode = Api.SystemManage.UserManagementRelationTreeRespVO;
|
||||
|
||||
export function useChainSource(selectedIds: () => Set<string>, disabledUserIdSet: () => Set<string>) {
|
||||
const tree = ref<ChainNode[]>([]);
|
||||
const loading = ref(false);
|
||||
let loaded = false;
|
||||
|
||||
async function ensureLoaded() {
|
||||
if (loaded) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await fetchGetUserManagementRelationTree({ fromUserIndex: false });
|
||||
tree.value = data ?? [];
|
||||
loaded = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function nodeKey(node: ChainNode): string {
|
||||
return node.id ?? `chain_${node.userId}`;
|
||||
}
|
||||
|
||||
function getNodeUserIds(node: ChainNode): string[] {
|
||||
const ids = new Set<string>([String(node.userId)]);
|
||||
if (node.children) {
|
||||
for (const c of node.children) {
|
||||
for (const id of getNodeUserIds(c)) ids.add(id);
|
||||
}
|
||||
}
|
||||
return [...ids];
|
||||
}
|
||||
|
||||
function getNodeCheckState(node: ChainNode): TreeCheckState {
|
||||
const ids = getNodeUserIds(node).filter(id => !disabledUserIdSet().has(id));
|
||||
if (!ids.length) return 'none';
|
||||
const sel = ids.filter(id => selectedIds().has(id)).length;
|
||||
if (sel === 0) return 'none';
|
||||
if (sel === ids.length) return 'all';
|
||||
return 'partial';
|
||||
}
|
||||
|
||||
function findNode(list: ChainNode[], key: string): ChainNode | null {
|
||||
for (const n of list) {
|
||||
if (nodeKey(n) === key) return n;
|
||||
if (n.children) {
|
||||
const r = findNode(n.children, key);
|
||||
if (r) return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function matchKeyword(node: ChainNode, kw: string): boolean {
|
||||
if (!kw) return true;
|
||||
if (node.userNickname.toLowerCase().includes(kw)) return true;
|
||||
if (node.children) return node.children.some(c => matchKeyword(c, kw));
|
||||
return false;
|
||||
}
|
||||
|
||||
function filterByKeyword(kw: string) {
|
||||
const lower = kw.trim().toLowerCase();
|
||||
if (!lower) return tree.value;
|
||||
return tree.value.filter(n => matchKeyword(n, lower));
|
||||
}
|
||||
|
||||
function getMetaText(node: ChainNode): string {
|
||||
const total = getNodeUserIds(node).length;
|
||||
return total > 1 ? `${total} 人` : '';
|
||||
}
|
||||
|
||||
const treeProps = computed(() => ({ children: 'children', label: 'userNickname' }) as const);
|
||||
|
||||
return {
|
||||
tree,
|
||||
loading,
|
||||
treeProps,
|
||||
ensureLoaded,
|
||||
getNodeUserIds,
|
||||
getNodeCheckState,
|
||||
findNode,
|
||||
filterByKeyword,
|
||||
getMetaText,
|
||||
nodeKey
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { computed, ref } from 'vue';
|
||||
import { fetchGetDeptSimpleList } from '@/service/api';
|
||||
import { buildMenuTree } from '@/views/system/shared/menu-tree';
|
||||
|
||||
export type TreeCheckState = 'none' | 'partial' | 'all';
|
||||
|
||||
export function useDeptSource(
|
||||
userOptions: () => Api.SystemManage.UserSimple[],
|
||||
selectedIds: () => Set<string>,
|
||||
disabledUserIdSet: () => Set<string>
|
||||
) {
|
||||
const tree = ref<Api.SystemManage.DeptSimple[]>([]);
|
||||
const loading = ref(false);
|
||||
let loaded = false;
|
||||
|
||||
async function ensureLoaded() {
|
||||
if (loaded) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await fetchGetDeptSimpleList();
|
||||
tree.value = data ? buildMenuTree(data) : [];
|
||||
loaded = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function collectDeptIds(node: Api.SystemManage.DeptSimple): string[] {
|
||||
const ids: string[] = [String(node.id)];
|
||||
if (node.children) {
|
||||
for (const c of node.children) ids.push(...collectDeptIds(c));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
function getNodeUserIds(node: Api.SystemManage.DeptSimple): string[] {
|
||||
const deptIds = new Set(collectDeptIds(node));
|
||||
return userOptions()
|
||||
.filter(u => u.deptId !== null && u.deptId !== undefined && deptIds.has(String(u.deptId)))
|
||||
.map(u => String(u.id));
|
||||
}
|
||||
|
||||
function getNodeCheckState(node: Api.SystemManage.DeptSimple): TreeCheckState {
|
||||
const ids = getNodeUserIds(node).filter(id => !disabledUserIdSet().has(id));
|
||||
if (!ids.length) return 'none';
|
||||
const sel = ids.filter(id => selectedIds().has(id)).length;
|
||||
if (sel === 0) return 'none';
|
||||
if (sel === ids.length) return 'all';
|
||||
return 'partial';
|
||||
}
|
||||
|
||||
function findNode(list: Api.SystemManage.DeptSimple[], key: string): Api.SystemManage.DeptSimple | null {
|
||||
for (const n of list) {
|
||||
if (String(n.id) === key) return n;
|
||||
if (n.children) {
|
||||
const r = findNode(n.children, key);
|
||||
if (r) return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function matchKeyword(node: Api.SystemManage.DeptSimple, kw: string): boolean {
|
||||
if (!kw) return true;
|
||||
if (node.name.toLowerCase().includes(kw)) return true;
|
||||
if (node.children) return node.children.some(c => matchKeyword(c, kw));
|
||||
return false;
|
||||
}
|
||||
|
||||
function filterByKeyword(kw: string) {
|
||||
const lower = kw.trim().toLowerCase();
|
||||
if (!lower) return tree.value;
|
||||
return tree.value.filter(n => matchKeyword(n, lower));
|
||||
}
|
||||
|
||||
function getMetaText(node: Api.SystemManage.DeptSimple): string {
|
||||
const total = getNodeUserIds(node).length;
|
||||
return total > 0 ? `${total} 人` : '';
|
||||
}
|
||||
|
||||
function nodeKey(node: Api.SystemManage.DeptSimple): string {
|
||||
return String(node.id);
|
||||
}
|
||||
|
||||
const treeProps = computed(() => ({ children: 'children', label: 'name' }) as const);
|
||||
|
||||
return {
|
||||
tree,
|
||||
loading,
|
||||
treeProps,
|
||||
ensureLoaded,
|
||||
getNodeUserIds,
|
||||
getNodeCheckState,
|
||||
findNode,
|
||||
filterByKeyword,
|
||||
getMetaText,
|
||||
nodeKey
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
export interface PickerSelectionOptions {
|
||||
multiple: boolean;
|
||||
}
|
||||
|
||||
export function usePickerSelection(options: () => PickerSelectionOptions) {
|
||||
const multiSet = ref<Set<string>>(new Set());
|
||||
const singleId = ref<string | null>(null);
|
||||
|
||||
const multiple = computed(() => options().multiple);
|
||||
|
||||
function has(userId: string): boolean {
|
||||
if (multiple.value) return multiSet.value.has(userId);
|
||||
return singleId.value === userId;
|
||||
}
|
||||
|
||||
function toggle(userId: string) {
|
||||
if (multiple.value) {
|
||||
if (multiSet.value.has(userId)) multiSet.value.delete(userId);
|
||||
else multiSet.value.add(userId);
|
||||
multiSet.value = new Set(multiSet.value);
|
||||
} else {
|
||||
singleId.value = singleId.value === userId ? null : userId;
|
||||
}
|
||||
}
|
||||
|
||||
function addMany(userIds: readonly string[]) {
|
||||
if (!multiple.value) {
|
||||
singleId.value = userIds[0] ?? singleId.value;
|
||||
return;
|
||||
}
|
||||
for (const id of userIds) multiSet.value.add(id);
|
||||
multiSet.value = new Set(multiSet.value);
|
||||
}
|
||||
|
||||
function removeMany(userIds: readonly string[]) {
|
||||
if (!multiple.value) {
|
||||
if (singleId.value && userIds.includes(singleId.value)) singleId.value = null;
|
||||
return;
|
||||
}
|
||||
for (const id of userIds) multiSet.value.delete(id);
|
||||
multiSet.value = new Set(multiSet.value);
|
||||
}
|
||||
|
||||
function clear(preserveIds?: readonly string[]) {
|
||||
const keep = new Set((preserveIds ?? []).map(String));
|
||||
if (multiple.value) {
|
||||
const next = new Set<string>();
|
||||
for (const id of multiSet.value) {
|
||||
if (keep.has(id)) next.add(id);
|
||||
}
|
||||
multiSet.value = next;
|
||||
} else if (singleId.value && !keep.has(singleId.value)) singleId.value = null;
|
||||
}
|
||||
|
||||
function reset(initial: string | string[] | null | undefined) {
|
||||
if (multiple.value) {
|
||||
const ids = Array.isArray(initial) ? initial.map(String) : [];
|
||||
multiSet.value = new Set(ids);
|
||||
} else {
|
||||
singleId.value = typeof initial === 'string' ? initial : null;
|
||||
}
|
||||
}
|
||||
|
||||
const selectedIds = computed<string[]>(() => {
|
||||
if (multiple.value) return [...multiSet.value];
|
||||
return singleId.value ? [singleId.value] : [];
|
||||
});
|
||||
|
||||
const size = computed(() => selectedIds.value.length);
|
||||
|
||||
function commit(): string | string[] | null {
|
||||
if (multiple.value) return [...multiSet.value];
|
||||
return singleId.value;
|
||||
}
|
||||
|
||||
return {
|
||||
selectedIds,
|
||||
size,
|
||||
has,
|
||||
toggle,
|
||||
addMany,
|
||||
removeMany,
|
||||
clear,
|
||||
reset,
|
||||
commit
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user