| 
							
							
							
						 |  |  | @@ -1,29 +1,14 @@ | 
		
	
		
			
				|  |  |  |  | <template> | 
		
	
		
			
				|  |  |  |  |   <div class="container"> | 
		
	
		
			
				|  |  |  |  |     <a-row :gutter="16"> | 
		
	
		
			
				|  |  |  |  |       <a-col :span="24" :md="5" class="section"> | 
		
	
		
			
				|  |  |  |  |         <a-input v-model="searchKey" placeholder="请输入部门名称" allow-clear> | 
		
	
		
			
				|  |  |  |  |           <template #prefix> | 
		
	
		
			
				|  |  |  |  |             <icon-search /> | 
		
	
		
			
				|  |  |  |  |           </template> | 
		
	
		
			
				|  |  |  |  |         </a-input> | 
		
	
		
			
				|  |  |  |  |         <a-tree | 
		
	
		
			
				|  |  |  |  |           ref="treeRef" | 
		
	
		
			
				|  |  |  |  |           :data="treeData" | 
		
	
		
			
				|  |  |  |  |           block-node | 
		
	
		
			
				|  |  |  |  |           @select="handleDeptSelect" | 
		
	
		
			
				|  |  |  |  |         /> | 
		
	
		
			
				|  |  |  |  |       </a-col> | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       <a-col :span="24" :md="14" class="section"> | 
		
	
		
			
				|  |  |  |  |       <a-col :span="24" :md="18" class="section"> | 
		
	
		
			
				|  |  |  |  |         <GiTable | 
		
	
		
			
				|  |  |  |  |           v-model:selectedKeys="selectedKeys" | 
		
	
		
			
				|  |  |  |  |           style="min-height: 600px;" | 
		
	
		
			
				|  |  |  |  |           row-key="id" | 
		
	
		
			
				|  |  |  |  |           :data="dataList" | 
		
	
		
			
				|  |  |  |  |           :columns="tableColumns" | 
		
	
		
			
				|  |  |  |  |           :loading="loading" | 
		
	
		
			
				|  |  |  |  |           :scroll="{ x: '100%', y: '100%' }" | 
		
	
		
			
				|  |  |  |  |           :scroll="{ x: '100%', y: '100%', minWidth: 1000 }" | 
		
	
		
			
				|  |  |  |  |           :pagination="pagination" | 
		
	
		
			
				|  |  |  |  |           :disabled-tools="['size', 'fullscreen', 'setting', 'refresh']" | 
		
	
		
			
				|  |  |  |  |           :row-selection="{ type: props.multiple ? 'checkbox' : 'radio', showCheckedAll: true }" | 
		
	
	
		
			
				
					
					|  |  |  | @@ -34,16 +19,19 @@ | 
		
	
		
			
				|  |  |  |  |           <template #top> | 
		
	
		
			
				|  |  |  |  |             <div> | 
		
	
		
			
				|  |  |  |  |               <a-space class="mt-5"> | 
		
	
		
			
				|  |  |  |  |                 <a-input v-model="queryForm.description" placeholder="用户名/昵称/描述" /> | 
		
	
		
			
				|  |  |  |  |                 <a-button @click="search"> | 
		
	
		
			
				|  |  |  |  |                   <template #icon> | 
		
	
		
			
				|  |  |  |  |                     <icon-search /> | 
		
	
		
			
				|  |  |  |  |                   </template> | 
		
	
		
			
				|  |  |  |  |                 </a-button> | 
		
	
		
			
				|  |  |  |  |                 <a-button @click="onRefresh"> | 
		
	
		
			
				|  |  |  |  |                   <template #icon> | 
		
	
		
			
				|  |  |  |  |                     <icon-refresh /> | 
		
	
		
			
				|  |  |  |  |                   </template> | 
		
	
		
			
				|  |  |  |  |                 <a-input v-model="queryForm.description" placeholder="用户名/昵称/描述" allow-clear @change="search" /> | 
		
	
		
			
				|  |  |  |  |                 <a-tree-select | 
		
	
		
			
				|  |  |  |  |                   v-model="queryForm.deptId" | 
		
	
		
			
				|  |  |  |  |                   :data="deptList" | 
		
	
		
			
				|  |  |  |  |                   placeholder="请选择所属部门" | 
		
	
		
			
				|  |  |  |  |                   allow-clear | 
		
	
		
			
				|  |  |  |  |                   allow-search | 
		
	
		
			
				|  |  |  |  |                   :filter-tree-node="filterDeptOptions" | 
		
	
		
			
				|  |  |  |  |                   @change="search" | 
		
	
		
			
				|  |  |  |  |                 /> | 
		
	
		
			
				|  |  |  |  |                 <a-button @click="reset"> | 
		
	
		
			
				|  |  |  |  |                   <template #icon><icon-refresh /></template> | 
		
	
		
			
				|  |  |  |  |                   <template #default>重置</template> | 
		
	
		
			
				|  |  |  |  |                 </a-button> | 
		
	
		
			
				|  |  |  |  |               </a-space> | 
		
	
		
			
				|  |  |  |  |             </div> | 
		
	
	
		
			
				
					
					|  |  |  | @@ -52,7 +40,7 @@ | 
		
	
		
			
				|  |  |  |  |                 已选中{{ selectedKeys.length }}条记录(可跨页) | 
		
	
		
			
				|  |  |  |  |               </template> | 
		
	
		
			
				|  |  |  |  |               <template v-else> | 
		
	
		
			
				|  |  |  |  |                 未选中任何项目 | 
		
	
		
			
				|  |  |  |  |                 未选中任何用户 | 
		
	
		
			
				|  |  |  |  |               </template> | 
		
	
		
			
				|  |  |  |  |               <template v-if="selectedKeys.length > 0" #action> | 
		
	
		
			
				|  |  |  |  |                 <a-link @click="onClearSelected">清空</a-link> | 
		
	
	
		
			
				
					
					|  |  |  | @@ -63,12 +51,18 @@ | 
		
	
		
			
				|  |  |  |  |           <template #status="{ record }"> | 
		
	
		
			
				|  |  |  |  |             <GiCellStatus :status="record.status" /> | 
		
	
		
			
				|  |  |  |  |           </template> | 
		
	
		
			
				|  |  |  |  |           <template #gender="{ record }"> | 
		
	
		
			
				|  |  |  |  |             <GiCellGender :gender="record.gender" /> | 
		
	
		
			
				|  |  |  |  |           </template> | 
		
	
		
			
				|  |  |  |  |         </GiTable> | 
		
	
		
			
				|  |  |  |  |       </a-col> | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |       <a-col :span="24" :md="5" class="section"> | 
		
	
		
			
				|  |  |  |  |       <a-col :span="24" :md="6" class="section"> | 
		
	
		
			
				|  |  |  |  |         <a-card title="已选用户"> | 
		
	
		
			
				|  |  |  |  |           <a-table :columns="rightColumn" :data="selectedData"> | 
		
	
		
			
				|  |  |  |  |             <template #nickname="{ record }"> | 
		
	
		
			
				|  |  |  |  |               {{ record.nickname }}({{ record.username }}) | 
		
	
		
			
				|  |  |  |  |             </template> | 
		
	
		
			
				|  |  |  |  |             <template #action="{ record }"> | 
		
	
		
			
				|  |  |  |  |               <a-button @click="handleDeleteSelectUser(record)"> | 
		
	
		
			
				|  |  |  |  |                 <icon-delete /> | 
		
	
	
		
			
				
					
					|  |  |  | @@ -85,8 +79,10 @@ | 
		
	
		
			
				|  |  |  |  | import type { TreeNodeData } from '@arco-design/web-vue' | 
		
	
		
			
				|  |  |  |  | import { useDept } from '@/hooks/app' | 
		
	
		
			
				|  |  |  |  | import { useTable } from '@/hooks' | 
		
	
		
			
				|  |  |  |  | import { listAllUser, listUser } from '@/apis' | 
		
	
		
			
				|  |  |  |  | import { type UserQuery, listAllUser, listUser } from '@/apis' | 
		
	
		
			
				|  |  |  |  | import type { UserItem, UserSelectPropType } from '@/components/UserSelect/type' | 
		
	
		
			
				|  |  |  |  | import type { TableInstanceColumns } from '@/components/GiTable/type' | 
		
	
		
			
				|  |  |  |  | import { isMobile } from '@/utils' | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | const props = withDefaults(defineProps<UserSelectPropType & { selectedUsers: string | string[] }>(), { | 
		
	
		
			
				|  |  |  |  |   multiple: false, | 
		
	
	
		
			
				
					
					|  |  |  | @@ -95,84 +91,77 @@ const props = withDefaults(defineProps<UserSelectPropType & { selectedUsers: str | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | const emit = defineEmits(['update:selectedUsers']) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 查询表单引用 | 
		
	
		
			
				|  |  |  |  | const queryForm = ref({ description: '' }) | 
		
	
		
			
				|  |  |  |  | // 表格列定义 | 
		
	
		
			
				|  |  |  |  | const tableColumns: TableInstanceColumns[] = [ | 
		
	
		
			
				|  |  |  |  |   { | 
		
	
		
			
				|  |  |  |  |     title: '昵称', | 
		
	
		
			
				|  |  |  |  |     dataIndex: 'nickname', | 
		
	
		
			
				|  |  |  |  |     slotName: 'nickname', | 
		
	
		
			
				|  |  |  |  |     minWidth: 140, | 
		
	
		
			
				|  |  |  |  |     ellipsis: true, | 
		
	
		
			
				|  |  |  |  |     tooltip: true, | 
		
	
		
			
				|  |  |  |  |     fixed: !isMobile() ? 'left' : undefined, | 
		
	
		
			
				|  |  |  |  |   }, | 
		
	
		
			
				|  |  |  |  |   { title: '用户名', dataIndex: 'username', slotName: 'username', minWidth: 140, ellipsis: true, tooltip: true }, | 
		
	
		
			
				|  |  |  |  |   { title: '状态', slotName: 'status', align: 'center' }, | 
		
	
		
			
				|  |  |  |  |   { title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center' }, | 
		
	
		
			
				|  |  |  |  |   { title: '所属部门', dataIndex: 'deptName', ellipsis: true, tooltip: true, minWidth: 180 }, | 
		
	
		
			
				|  |  |  |  |   { title: '角色', dataIndex: 'roleNames', minWidth: 160, slotName: 'roleNames' }, | 
		
	
		
			
				|  |  |  |  |   { title: '手机号', dataIndex: 'phone', minWidth: 170, ellipsis: true, tooltip: true }, | 
		
	
		
			
				|  |  |  |  |   { title: '邮箱', dataIndex: 'email', minWidth: 170, ellipsis: true, tooltip: true }, | 
		
	
		
			
				|  |  |  |  |   { title: '系统内置', slotName: 'isSystem', width: 100, align: 'center', show: false }, | 
		
	
		
			
				|  |  |  |  |   { title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true }, | 
		
	
		
			
				|  |  |  |  |   { title: '创建人', dataIndex: 'createUserString', width: 140, ellipsis: true, tooltip: true, show: false }, | 
		
	
		
			
				|  |  |  |  |   { title: '创建时间', dataIndex: 'createTime', width: 180 }, | 
		
	
		
			
				|  |  |  |  |   { title: '修改人', dataIndex: 'updateUserString', width: 140, ellipsis: true, tooltip: true, show: false }, | 
		
	
		
			
				|  |  |  |  |   { title: '修改时间', dataIndex: 'updateTime', width: 180, show: false }, | 
		
	
		
			
				|  |  |  |  | ] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 部门树引用 | 
		
	
		
			
				|  |  |  |  | const treeRef = ref() | 
		
	
		
			
				|  |  |  |  | const selectedKeys = ref<string[]>([]) | 
		
	
		
			
				|  |  |  |  | const selectedDeptId = ref<string>('') | 
		
	
		
			
				|  |  |  |  | const selectedData = ref<any[]>([]) | 
		
	
		
			
				|  |  |  |  | // 右侧已选用户列定义 | 
		
	
		
			
				|  |  |  |  | const rightColumn = [ | 
		
	
		
			
				|  |  |  |  |   { title: '用户', dataIndex: 'nickname', slotName: 'nickname', minWidth: 140, ellipsis: true, tooltip: true }, | 
		
	
		
			
				|  |  |  |  |   { title: '操作', dataIndex: 'action', slotName: 'action' }, | 
		
	
		
			
				|  |  |  |  | ] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 查询表单 | 
		
	
		
			
				|  |  |  |  | const queryForm = reactive<UserQuery>({ | 
		
	
		
			
				|  |  |  |  |   sort: ['t1.createTime,desc'], | 
		
	
		
			
				|  |  |  |  | }) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 用户列表 | 
		
	
		
			
				|  |  |  |  | const { tableData: dataList, loading, pagination, search } = useTable( | 
		
	
		
			
				|  |  |  |  |   (page) => listUser({ ...queryForm.value, deptId: selectedDeptId.value, sort: [], ...page }), | 
		
	
		
			
				|  |  |  |  |   (page) => listUser({ ...queryForm, ...page }), | 
		
	
		
			
				|  |  |  |  |   { immediate: false, formatResult: (data) => data.map((i) => ({ ...i, disabled: false })) }, | 
		
	
		
			
				|  |  |  |  | ) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 刷新表单 | 
		
	
		
			
				|  |  |  |  | const onRefresh = () => { | 
		
	
		
			
				|  |  |  |  |   queryForm.value.description = '' | 
		
	
		
			
				|  |  |  |  | // 重置 | 
		
	
		
			
				|  |  |  |  | const reset = () => { | 
		
	
		
			
				|  |  |  |  |   queryForm.description = undefined | 
		
	
		
			
				|  |  |  |  |   queryForm.deptId = undefined | 
		
	
		
			
				|  |  |  |  |   search() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 使用 useDept 钩子获取部门列表数据 | 
		
	
		
			
				|  |  |  |  | const treeRef = ref() | 
		
	
		
			
				|  |  |  |  | // 部门列表 | 
		
	
		
			
				|  |  |  |  | const { deptList, getDeptList } = useDept({ | 
		
	
		
			
				|  |  |  |  |   onSuccess: () => { | 
		
	
		
			
				|  |  |  |  |     nextTick(() => treeRef.value?.expandAll(true)) | 
		
	
		
			
				|  |  |  |  |   }, | 
		
	
		
			
				|  |  |  |  | }) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 部门树过滤函数 | 
		
	
		
			
				|  |  |  |  | const deptTreeSearch = (keyword: string, data: TreeNodeData[]): TreeNodeData[] => { | 
		
	
		
			
				|  |  |  |  |   return data | 
		
	
		
			
				|  |  |  |  |     .map((item) => ({ | 
		
	
		
			
				|  |  |  |  |       ...item, | 
		
	
		
			
				|  |  |  |  |       children: item.children ? deptTreeSearch(keyword, item.children) : [], | 
		
	
		
			
				|  |  |  |  |     })) | 
		
	
		
			
				|  |  |  |  |     .filter( | 
		
	
		
			
				|  |  |  |  |       (item) => | 
		
	
		
			
				|  |  |  |  |         item.title?.toLowerCase().includes(keyword.toLowerCase()) || item.children?.length, | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 过滤树数据 | 
		
	
		
			
				|  |  |  |  | const searchKey = ref('') | 
		
	
		
			
				|  |  |  |  | const treeData = computed(() => { | 
		
	
		
			
				|  |  |  |  |   return searchKey.value ? deptTreeSearch(searchKey.value, deptList.value) : deptList.value | 
		
	
		
			
				|  |  |  |  | }) | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 表格列定义 | 
		
	
		
			
				|  |  |  |  | const tableColumns = [ | 
		
	
		
			
				|  |  |  |  |   { title: '昵称', dataIndex: 'nickname' }, | 
		
	
		
			
				|  |  |  |  |   { title: '部门', dataIndex: 'deptName' }, | 
		
	
		
			
				|  |  |  |  |   { title: '角色', dataIndex: 'roleNames' }, | 
		
	
		
			
				|  |  |  |  |   { title: '手机号', dataIndex: 'phone' }, | 
		
	
		
			
				|  |  |  |  |   { title: '邮箱', dataIndex: 'email' }, | 
		
	
		
			
				|  |  |  |  |   { title: '状态', dataIndex: 'status', slotName: 'status' }, | 
		
	
		
			
				|  |  |  |  | ] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 右侧已选用户列定义 | 
		
	
		
			
				|  |  |  |  | const rightColumn = [ | 
		
	
		
			
				|  |  |  |  |   { title: '昵称', dataIndex: 'nickname' }, | 
		
	
		
			
				|  |  |  |  |   { title: '操作', dataIndex: 'action', slotName: 'action' }, | 
		
	
		
			
				|  |  |  |  | ] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 处理部门选择 | 
		
	
		
			
				|  |  |  |  | const handleDeptSelect = (keys: Array<any>) => { | 
		
	
		
			
				|  |  |  |  |   selectedDeptId.value = keys[0] || '' | 
		
	
		
			
				|  |  |  |  |   search() | 
		
	
		
			
				|  |  |  |  | // 过滤部门 | 
		
	
		
			
				|  |  |  |  | const filterDeptOptions = (searchKey: string, nodeData: TreeNodeData) => { | 
		
	
		
			
				|  |  |  |  |   if (nodeData.title) { | 
		
	
		
			
				|  |  |  |  |     return nodeData.title.toLowerCase().includes(searchKey.toLowerCase()) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return false | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | const selectedKeys = ref<string[]>([]) | 
		
	
		
			
				|  |  |  |  | const selectedData = ref<any[]>([]) | 
		
	
		
			
				|  |  |  |  | const emitSelectedUsers = () => { | 
		
	
		
			
				|  |  |  |  |   emit('update:selectedUsers', selectedKeys.value) | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | // 从选中列表中移除用户 | 
		
	
		
			
				|  |  |  |  | const handleDeleteSelectUser = (user: UserItem) => { | 
		
	
		
			
				|  |  |  |  |   selectedData.value = selectedData.value.filter((item) => item.id !== user.id) | 
		
	
		
			
				|  |  |  |  |   selectedKeys.value = selectedData.value.map((item) => item.id) | 
		
	
		
			
				|  |  |  |  |   emitSelectedUsers() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 行选择事件 | 
		
	
		
			
				|  |  |  |  | const onRowSelect = (rowKeys: string[], rowKey: string, record: UserItem) => { | 
		
	
		
			
				|  |  |  |  |   selectedData.value = props.multiple | 
		
	
	
		
			
				
					
					|  |  |  | @@ -180,10 +169,10 @@ const onRowSelect = (rowKeys: string[], rowKey: string, record: UserItem) => { | 
		
	
		
			
				|  |  |  |  |       ? [...selectedData.value, record] | 
		
	
		
			
				|  |  |  |  |       : selectedData.value.filter((item) => item.id !== rowKey) | 
		
	
		
			
				|  |  |  |  |     : [record] | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  |   selectedKeys.value = selectedData.value.map((item) => item.id) | 
		
	
		
			
				|  |  |  |  |   emitSelectedUsers() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 全选事件 | 
		
	
		
			
				|  |  |  |  | const onTableSelectAll = (checked: boolean) => { | 
		
	
		
			
				|  |  |  |  |   selectedData.value = checked | 
		
	
	
		
			
				
					
					|  |  |  | @@ -193,6 +182,13 @@ const onTableSelectAll = (checked: boolean) => { | 
		
	
		
			
				|  |  |  |  |   emitSelectedUsers() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 从选中列表中移除用户 | 
		
	
		
			
				|  |  |  |  | const handleDeleteSelectUser = (user: UserItem) => { | 
		
	
		
			
				|  |  |  |  |   selectedData.value = selectedData.value.filter((item) => item.id !== user.id) | 
		
	
		
			
				|  |  |  |  |   selectedKeys.value = selectedData.value.map((item) => item.id) | 
		
	
		
			
				|  |  |  |  |   emitSelectedUsers() | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  |  | 
		
	
		
			
				|  |  |  |  | // 清空所有选中数据 | 
		
	
		
			
				|  |  |  |  | const onClearSelected = () => { | 
		
	
		
			
				|  |  |  |  |   selectedData.value = [] | 
		
	
	
		
			
				
					
					|  |  |  | @@ -208,7 +204,7 @@ const init = (selectUsers: string[]) => { | 
		
	
		
			
				|  |  |  |  |     // admin的id是number 不是string 类型 所以处理一下 | 
		
	
		
			
				|  |  |  |  |     listAllUser({ userIds: selectUsers }).then((dataList) => { | 
		
	
		
			
				|  |  |  |  |       selectedData.value = dataList.data.map((data) => { | 
		
	
		
			
				|  |  |  |  |         return { ...data, id: `${data.id}` } | 
		
	
		
			
				|  |  |  |  |         return { ...data, id: data.id } | 
		
	
		
			
				|  |  |  |  |       }) | 
		
	
		
			
				|  |  |  |  |     }) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					
					|  |  |  |   |