| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- <template>
- <div class="auth-select-wrapper">
- <a-tree-select
- :model-value="modelValue"
- @update:model-value="onUpdate"
- :data="authOptions"
- tree-checked-strategy="child"
- :tree-checkable="multiple"
- :max-tag-count="2"
- :fieldNames="{ title: 'name', key: 'id' }"
- allow-search
- allow-clear
- :filter-tree-node="filterTreeNode"
- :treeProps="{
- defaultExpandedKeys: [],
- expandedKeys: expandedKeys,
- onExpand: expandTreeNode,
- }"
- placeholder="负责人"
- >
- <template #header v-if="multiple && showFooter">
- <div class="tree-select-header">
- <a-space>
- <a-button type="primary" size="mini" @click="handleSelectAll">
- 全选
- </a-button>
- <a-button type="outline" size="mini" @click="handleInvertSelect">
- 反选
- </a-button>
- <a-button type="dashed" size="mini" @click="handleClear">
- 清空
- </a-button>
- </a-space>
- </div>
- </template>
- </a-tree-select>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, defineProps, defineEmits } from "vue";
- import advertCommonApi from "@/views/v1/api/advert/common";
- const props = defineProps({
- modelValue: {
- type: [String, Number, Array],
- default: "",
- },
- multiple: {
- type: Boolean,
- default: false,
- },
- showFooter: {
- type: Boolean,
- default: true,
- },
- });
- const expandedKeys = ref([]);
- const searchText = ref(""); // 存储当前搜索文本
- const emit = defineEmits(["update:modelValue", "change"]);
- const onUpdate = (val) => {
- emit("update:modelValue", val);
- emit("change", val);
- };
- const authOptions = ref([]);
- // 获取节点的所有祖先节点ID
- const getAncestorIds = (nodeId) => {
- const ancestorIds = [];
- const findNode = (nodes, targetId) => {
- for (const node of nodes) {
- if (node.id === targetId) {
- return node;
- }
- if (node.children && node.children.length > 0) {
- const found = findNode(node.children, targetId);
- if (found) return found;
- }
- }
- return null;
- };
- let currentId = nodeId;
- while (currentId) {
- const node = findNode(authOptions.value, currentId);
- if (node && node.parent_id) {
- ancestorIds.push(node.parent_id);
- currentId = node.parent_id;
- } else {
- break;
- }
- }
- return ancestorIds;
- };
- // 搜索
- const filterTreeNode = (inputText, node) => {
- // 更新搜索文本
- searchText.value = inputText || "";
- if (inputText) {
- const filterData =
- node.name.toLowerCase().indexOf(inputText.toLowerCase()) > -1;
- if (filterData) {
- // 获取所有祖先节点并展开
- const ancestorIds = getAncestorIds(node.id);
- ancestorIds.forEach((ancestorId) => {
- if (!expandedKeys.value.includes(ancestorId)) {
- expandedKeys.value.push(ancestorId);
- }
- });
- }
- return filterData;
- }
- };
- // 展开/收起节点
- const expandTreeNode = (expandKeys) => {
- expandedKeys.value = expandKeys;
- };
- // 获取所有子节点的ID(支持搜索过滤)
- const getAllChildIds = (tree, filterText = "") => {
- const ids = [];
- // 收集指定节点下的所有叶子节点ID
- const collectLeafIds = (nodes) => {
- nodes.forEach((node) => {
- if (node.children && node.children.length > 0) {
- collectLeafIds(node.children);
- } else if (!String(node.id).includes("dept_id_")) {
- ids.push(node.id);
- }
- });
- };
- const traverse = (nodes) => {
- nodes.forEach((node) => {
- // 检查节点是否匹配搜索条件
- const matchFilter =
- !filterText ||
- node.name.toLowerCase().indexOf(filterText.toLowerCase()) > -1;
- if (matchFilter) {
- // 如果节点匹配,收集该节点下所有叶子节点
- if (node.children && node.children.length > 0) {
- collectLeafIds(node.children);
- } else if (!String(node.id).includes("dept_id_")) {
- ids.push(node.id);
- }
- } else if (node.children && node.children.length > 0) {
- // 如果不匹配但有子节点,继续在子节点中搜索
- traverse(node.children);
- }
- });
- };
- traverse(tree);
- return [...new Set(ids)]; // 去重
- };
- // 全选
- const handleSelectAll = () => {
- if (!props.multiple) return;
- const currentSelected = Array.isArray(props.modelValue)
- ? props.modelValue
- : [];
- const filterIds = getAllChildIds(authOptions.value, searchText.value);
- if (searchText.value) {
- // 有搜索条件:保留原有选中项,添加搜索结果
- const newIds = [...new Set([...currentSelected, ...filterIds])];
- onUpdate(newIds);
- } else {
- // 无搜索条件:全选所有
- onUpdate(filterIds);
- }
- };
- // 反选
- const handleInvertSelect = () => {
- if (!props.multiple) return;
- const currentSelected = Array.isArray(props.modelValue)
- ? props.modelValue
- : [];
- const filterIds = getAllChildIds(authOptions.value, searchText.value);
- if (searchText.value) {
- // 有搜索条件:只在搜索结果范围内反选,保留其他已选项
- const otherSelected = currentSelected.filter(
- (id) => !filterIds.includes(id)
- );
- const invertedInFilter = filterIds.filter(
- (id) => !currentSelected.includes(id)
- );
- onUpdate([...otherSelected, ...invertedInFilter]);
- } else {
- // 无搜索条件:全局反选
- const invertedIds = filterIds.filter((id) => !currentSelected.includes(id));
- onUpdate(invertedIds);
- }
- };
- // 清空
- const handleClear = () => {
- if (!props.multiple) return;
- onUpdate([]);
- };
- // 获取后台归属人列表
- const getAuthList = async () => {
- const res = await advertCommonApi.getAuthOptionsApi();
- if (res.code == 200) {
- // 添加 parent_id 字段
- const processedData = addParentId(res.data);
- if (props.multiple) {
- authOptions.value = processedData;
- } else {
- const data = await handleData(processedData);
- authOptions.value = data;
- }
- }
- };
- // 添加 parent_id 字段到树形数据
- const addParentId = (data, parentId = null) => {
- return data.map((item) => {
- const newItem = {
- ...item,
- parent_id: parentId,
- };
- if (item.children && item.children.length > 0) {
- newItem.children = addParentId(item.children, item.id);
- }
- return newItem;
- });
- };
- // 处理数据
- const handleData = (data) => {
- return new Promise((resolve) => {
- for (const item of data) {
- if (String(item.id).includes("dept_id_")) {
- item.disabled = true;
- }
- if (item.children) {
- handleData(item.children);
- }
- }
- resolve(data);
- });
- };
- onMounted(() => {
- getAuthList();
- });
- </script>
- <style scoped>
- .auth-select-wrapper {
- width: 100%;
- }
- .tree-select-header {
- padding: 8px 12px;
- border-bottom: 1px solid var(--color-border-2);
- }
- .tree-select-header :deep(.arco-btn-text) {
- color: var(--color-text-2);
- }
- .tree-select-header :deep(.arco-btn-text:hover) {
- color: rgb(var(--primary-6));
- background-color: transparent;
- }
- </style>
|