mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 22:57:15 +08:00 
			
		
		
		
	refactor: 重构部门管理、菜单管理树列表过滤(前端过滤)
This commit is contained in:
		| @@ -114,7 +114,6 @@ export interface MenuResp { | ||||
| export interface MenuQuery { | ||||
|   title?: string | ||||
|   status?: number | ||||
|   sort: Array<string> | ||||
| } | ||||
|  | ||||
| /** 系统部门类型 */ | ||||
| @@ -136,7 +135,6 @@ export interface DeptResp { | ||||
| export interface DeptQuery { | ||||
|   description?: string | ||||
|   status?: number | ||||
|   sort: Array<string> | ||||
| } | ||||
|  | ||||
| /** 系统字典类型 */ | ||||
|   | ||||
| @@ -18,17 +18,9 @@ | ||||
|         <IconRight v-else /> | ||||
|       </template> | ||||
|       <template #custom-left> | ||||
|         <a-input v-model="queryForm.description" placeholder="请输入名称/描述" allow-clear @change="search"> | ||||
|         <a-input v-model="name" placeholder="请输入名称" allow-clear @change="search"> | ||||
|           <template #prefix><icon-search /></template> | ||||
|         </a-input> | ||||
|         <a-select | ||||
|           v-model="queryForm.status" | ||||
|           :options="DisEnableStatusList" | ||||
|           placeholder="请选择状态" | ||||
|           allow-clear | ||||
|           style="width: 150px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button @click="reset">重置</a-button> | ||||
|       </template> | ||||
|       <template #custom-right> | ||||
| @@ -80,10 +72,53 @@ import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { useDownload, useTable } from '@/hooks' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
| import { DisEnableStatusList } from '@/constant/common' | ||||
|  | ||||
| defineOptions({ name: 'SystemDept' }) | ||||
|  | ||||
| const tableRef = ref<InstanceType<typeof GiTable>>() | ||||
| const queryForm = reactive<DeptQuery>({}) | ||||
| const { | ||||
|   tableData, | ||||
|   loading, | ||||
|   search, | ||||
|   handleDelete | ||||
| } = useTable(() => listDept(queryForm), { | ||||
|   immediate: true, | ||||
|   onSuccess: () => { | ||||
|     nextTick(() => { | ||||
|       tableRef.value?.tableRef?.expandAll(true) | ||||
|     }) | ||||
|   } | ||||
| }) | ||||
|  | ||||
| // 过滤树 | ||||
| const name = ref('') | ||||
| const searchData = (name: string) => { | ||||
|   const loop = (data: DeptResp[]) => { | ||||
|     const result = [] as DeptResp[] | ||||
|     data.forEach((item: DeptResp) => { | ||||
|       if (item.name?.toLowerCase().includes(name.toLowerCase())) { | ||||
|         result.push({ ...item }) | ||||
|       } else if (item.children) { | ||||
|         const filterData = loop(item.children) | ||||
|         if (filterData.length) { | ||||
|           result.push({ | ||||
|             ...item, | ||||
|             children: filterData | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|     return result | ||||
|   } | ||||
|   return loop(tableData.value) | ||||
| } | ||||
|  | ||||
| const dataList = computed(() => { | ||||
|   if (!name.value) return tableData.value | ||||
|   return searchData(name.value) | ||||
| }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { title: '名称', dataIndex: 'name', width: 170, ellipsis: true, tooltip: true }, | ||||
|   { title: '状态', slotName: 'status', align: 'center' }, | ||||
| @@ -104,30 +139,9 @@ const columns: TableInstanceColumns[] = [ | ||||
|   } | ||||
| ] | ||||
|  | ||||
| const queryForm = reactive<DeptQuery>({ | ||||
|   sort: ['parentId,asc', 'sort,asc', 'createTime,desc'] | ||||
| }) | ||||
|  | ||||
| const tableRef = ref<InstanceType<typeof GiTable>>() | ||||
| const { | ||||
|   tableData: dataList, | ||||
|   loading, | ||||
|   search, | ||||
|   handleDelete | ||||
| } = useTable(() => listDept(queryForm), { | ||||
|   immediate: true, | ||||
|   onSuccess: () => { | ||||
|     nextTick(() => { | ||||
|       tableRef.value?.tableRef?.expandAll(true) | ||||
|     }) | ||||
|   } | ||||
| }) | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   queryForm.description = undefined | ||||
|   queryForm.status = undefined | ||||
|   search() | ||||
|   name.value = '' | ||||
| } | ||||
|  | ||||
| // 删除 | ||||
|   | ||||
| @@ -145,7 +145,7 @@ const menuSelectTree = computed(() => { | ||||
|   })) | ||||
| }) | ||||
|  | ||||
| // 过滤菜单树 | ||||
| // 过滤树 | ||||
| const filterOptions = (searchKey: string, nodeData: TreeNodeData) => { | ||||
|   if (nodeData.title) { | ||||
|     return nodeData.title.toLowerCase().includes(searchKey.toLowerCase()) | ||||
|   | ||||
| @@ -17,17 +17,9 @@ | ||||
|         <IconRight v-else /> | ||||
|       </template> | ||||
|       <template #custom-left> | ||||
|         <a-input v-model="queryForm.title" placeholder="请输入菜单标题" allow-clear @change="search"> | ||||
|         <a-input v-model="title" placeholder="请输入菜单标题" allow-clear @change="search"> | ||||
|           <template #prefix><icon-search /></template> | ||||
|         </a-input> | ||||
|         <a-select | ||||
|           v-model="queryForm.status" | ||||
|           :options="DisEnableStatusList" | ||||
|           placeholder="请选择状态" | ||||
|           allow-clear | ||||
|           style="width: 150px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button @click="reset">重置</a-button> | ||||
|       </template> | ||||
|       <template #custom-right> | ||||
| @@ -88,24 +80,48 @@ import MenuAddModal from './MenuAddModal.vue' | ||||
| import { type MenuQuery, type MenuResp, deleteMenu, listMenu } from '@/apis/system' | ||||
| import type GiTable from '@/components/GiTable/index.vue' | ||||
| import type { TableInstanceColumns } from '@/components/GiTable/type' | ||||
| import { DisEnableStatusList } from '@/constant/common' | ||||
| import { isMobile } from '@/utils' | ||||
| import has from '@/utils/has' | ||||
| import { useTable } from '@/hooks' | ||||
|  | ||||
| defineOptions({ name: 'SystemMenu' }) | ||||
|  | ||||
| const queryForm = reactive<MenuQuery>({ | ||||
|   sort: ['parentId,asc', 'sort,asc', 'createTime,desc'] | ||||
| }) | ||||
|  | ||||
| const queryForm = reactive<MenuQuery>({}) | ||||
| const { | ||||
|   tableData: dataList, | ||||
|   tableData, | ||||
|   loading, | ||||
|   search, | ||||
|   handleDelete | ||||
| } = useTable(() => listMenu(queryForm), { immediate: true }) | ||||
|  | ||||
| // 过滤树 | ||||
| const title = ref('') | ||||
| const searchData = (title: string) => { | ||||
|   const loop = (data: MenuResp[]) => { | ||||
|     const result = [] as MenuResp[] | ||||
|     data.forEach((item: MenuResp) => { | ||||
|       if (item.title?.toLowerCase().includes(title.toLowerCase())) { | ||||
|         result.push({ ...item }) | ||||
|       } else if (item.children) { | ||||
|         const filterData = loop(item.children) | ||||
|         if (filterData.length) { | ||||
|           result.push({ | ||||
|             ...item, | ||||
|             children: filterData | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|     return result | ||||
|   } | ||||
|   return loop(tableData.value) | ||||
| } | ||||
|  | ||||
| const dataList = computed(() => { | ||||
|   if (!title.value) return tableData.value | ||||
|   return searchData(title.value) | ||||
| }) | ||||
|  | ||||
| const columns: TableInstanceColumns[] = [ | ||||
|   { title: '菜单标题', dataIndex: 'title', slotName: 'title', width: 170, fixed: !isMobile() ? 'left' : undefined }, | ||||
|   { title: '类型', slotName: 'type', align: 'center' }, | ||||
| @@ -134,9 +150,7 @@ const columns: TableInstanceColumns[] = [ | ||||
|  | ||||
| // 重置 | ||||
| const reset = () => { | ||||
|   queryForm.title = undefined | ||||
|   queryForm.status = undefined | ||||
|   search() | ||||
|   title.value = '' | ||||
| } | ||||
|  | ||||
| // 删除 | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <div class="left-tree"> | ||||
|     <div class="left-tree__search"> | ||||
|       <a-input v-model="inputValue" :placeholder="props.placeholder" allow-clear> | ||||
|       <a-input v-model="searchKey" :placeholder="props.placeholder" allow-clear> | ||||
|         <template #prefix><icon-search /></template> | ||||
|       </a-input> | ||||
|     </div> | ||||
| @@ -9,7 +9,7 @@ | ||||
|       <div class="left-tree__tree"> | ||||
|         <a-tree | ||||
|           ref="treeRef" | ||||
|           :data="deptList" | ||||
|           :data="treeData" | ||||
|           show-line | ||||
|           block-node | ||||
|           default-expand-all | ||||
| @@ -20,6 +20,13 @@ | ||||
|             <IconCaretDown v-if="!isLeaf" /> | ||||
|             <IconIdcard v-else /> | ||||
|           </template> | ||||
|           <template #title="nodeData"> | ||||
|             <template v-if="index = getMatchIndex(nodeData?.title), index < 0">{{ nodeData?.title }}</template> | ||||
|             <span v-else>{{ nodeData?.title?.substr(0, index) }} | ||||
|               <span style="color: rgb(var(--arcoblue-6));">{{ nodeData?.title?.substr(index, searchKey.length) }}</span> | ||||
|               {{ nodeData?.title?.substr(index + searchKey.length) }} | ||||
|             </span> | ||||
|           </template> | ||||
|         </a-tree> | ||||
|       </div> | ||||
|     </div> | ||||
| @@ -27,7 +34,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="tsx"> | ||||
| import type { TreeInstance } from '@arco-design/web-vue' | ||||
| import type { TreeInstance, TreeNodeData } from '@arco-design/web-vue' | ||||
| import { ref } from 'vue' | ||||
| import { useDept } from '@/hooks/app' | ||||
|  | ||||
| @@ -58,12 +65,43 @@ const { deptList, getDeptList } = useDept({ | ||||
|   } | ||||
| }) | ||||
|  | ||||
| // 树查询 | ||||
| const inputValue = ref('') | ||||
| watch(inputValue, (val) => { | ||||
|   getDeptList(val) | ||||
| // 过滤树 | ||||
| const searchKey = ref('') | ||||
| const search = (keyword: string) => { | ||||
|   const loop = (data: TreeNodeData[]) => { | ||||
|     const result = [] as TreeNodeData[] | ||||
|     data.forEach((item: TreeNodeData) => { | ||||
|       if (item.title?.toLowerCase().includes(keyword.toLowerCase())) { | ||||
|         result.push({ ...item }) | ||||
|       } else if (item.children) { | ||||
|         const filterData = loop(item.children) | ||||
|         if (filterData.length) { | ||||
|           result.push({ | ||||
|             ...item, | ||||
|             children: filterData | ||||
|           }) | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
|     return result | ||||
|   } | ||||
|   return loop(deptList.value) | ||||
| } | ||||
|  | ||||
| const treeData = computed(() => { | ||||
|   if (!searchKey.value) return deptList.value | ||||
|   return search(searchKey.value) | ||||
| }) | ||||
|  | ||||
| /** | ||||
|  * 获取匹配索引 | ||||
|  * @param name 名称 | ||||
|  */ | ||||
| const getMatchIndex = (name: string) => { | ||||
|   if (!searchKey.value) return -1 | ||||
|   return name.toLowerCase().indexOf(searchKey.value.toLowerCase()) | ||||
| } | ||||
|  | ||||
| onMounted(() => { | ||||
|   getDeptList() | ||||
| }) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|     </a-row> | ||||
|     <a-row align="stretch" :gutter="14" class="h-full page_content"> | ||||
|       <a-col :xs="0" :sm="8" :md="7" :lg="6" :xl="5" :xxl="4" flex="260px" class="h-full ov-hidden"> | ||||
|         <DeptTree placeholder="请输入名称/描述" @node-click="handleSelectDept" /> | ||||
|         <DeptTree placeholder="请输入名称" @node-click="handleSelectDept" /> | ||||
|       </a-col> | ||||
|       <a-col :xs="24" :sm="16" :md="17" :lg="18" :xl="19" :xxl="20" flex="1" class="h-full ov-hidden"> | ||||
|         <GiTable row-key="id" :data="dataList" :columns="columns" :loading="loading" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user