ith5 3 месяцев назад
Родитель
Сommit
67bf04c63c
1 измененных файлов с 136 добавлено и 19 удалено
  1. 136 19
      src/components/game-select/index.vue

+ 136 - 19
src/components/game-select/index.vue

@@ -1,22 +1,40 @@
 <template>
 <template>
-  <a-tree-select
-    :model-value="modelValue"
-    @update:model-value="onUpdate"
-    :data="gameListTree"
-    tree-checked-strategy="child"
-    :tree-checkable="multiple"
-    :max-tag-count="1"
-    :fieldNames="{ title: 'name', key: 'id' }"
-    allow-search
-    allow-clear
-    :filter-tree-node="filterTreeNode"
-    :treeProps="{
-      defaultExpandedKeys: [],
-      expandedKeys: expandedKeys,
-      onExpand: expandTreeNode,
-    }"
-    placeholder="游戏"
-  />
+  <div class="game-select-wrapper">
+    <a-tree-select
+      :model-value="modelValue"
+      @update:model-value="onUpdate"
+      :data="gameListTree"
+      tree-checked-strategy="child"
+      :tree-checkable="multiple"
+      :max-tag-count="1"
+      :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>
 </template>
 
 
 <script setup>
 <script setup>
@@ -32,9 +50,14 @@ const props = defineProps({
     type: Boolean,
     type: Boolean,
     default: false,
     default: false,
   },
   },
+  showFooter: {
+    type: Boolean,
+    default: true,
+  },
 });
 });
 
 
 const expandedKeys = ref([]);
 const expandedKeys = ref([]);
+const searchText = ref(""); // 存储当前搜索文本
 const emit = defineEmits(["update:modelValue", "change"]);
 const emit = defineEmits(["update:modelValue", "change"]);
 const onUpdate = (val) => {
 const onUpdate = (val) => {
   emit("update:modelValue", val);
   emit("update:modelValue", val);
@@ -60,6 +83,9 @@ const fetchGameList = async () => {
   }
   }
 };
 };
 const filterTreeNode = (inputText, node) => {
 const filterTreeNode = (inputText, node) => {
+  // 更新搜索文本
+  searchText.value = inputText || "";
+
   if (inputText) {
   if (inputText) {
     const filterData =
     const filterData =
       node.name.toLowerCase().indexOf(inputText.toLowerCase()) > -1;
       node.name.toLowerCase().indexOf(inputText.toLowerCase()) > -1;
@@ -67,7 +93,6 @@ const filterTreeNode = (inputText, node) => {
       if (!expandedKeys.value.includes(node.parent_id)) {
       if (!expandedKeys.value.includes(node.parent_id)) {
         expandedKeys.value.push(node.parent_id);
         expandedKeys.value.push(node.parent_id);
       }
       }
-      console.log(node);
     }
     }
     return filterData;
     return filterData;
   }
   }
@@ -76,5 +101,97 @@ const filterTreeNode = (inputText, node) => {
 const expandTreeNode = (expandKeys) => {
 const expandTreeNode = (expandKeys) => {
   expandedKeys.value = expandKeys;
   expandedKeys.value = expandKeys;
 };
 };
+
+// 获取所有子节点的ID(支持搜索过滤)
+const getAllChildIds = (tree, filterText = "") => {
+  const ids = [];
+  const traverse = (nodes) => {
+    nodes.forEach((node) => {
+      // 检查节点是否匹配搜索条件
+      const matchFilter =
+        !filterText ||
+        node.name.toLowerCase().indexOf(filterText.toLowerCase()) > -1;
+
+      if (node.children && node.children.length > 0) {
+        traverse(node.children);
+      } else if (matchFilter) {
+        // 只有叶子节点且匹配搜索条件时才添加
+        ids.push(node.id);
+      }
+    });
+  };
+  traverse(tree);
+  return ids;
+};
+
+// 全选
+const handleSelectAll = () => {
+  if (!props.multiple) return;
+  const currentSelected = Array.isArray(props.modelValue)
+    ? props.modelValue
+    : [];
+  const filterIds = getAllChildIds(gameListTree.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(gameListTree.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([]);
+};
+
 onMounted(fetchGameList);
 onMounted(fetchGameList);
 </script>
 </script>
+
+<style scoped>
+.game-select-wrapper {
+  width: 100%;
+}
+
+.tree-select-header {
+  padding: 8px 12px;
+  border-bottom: 1px solid var(--color-border-2);
+  text-align: center;
+}
+
+.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>