refactor: 优化通知公告用户选择部分代码

This commit is contained in:
2024-11-07 22:22:41 +08:00
parent 4cd4e51ef1
commit 5ade6bcecf
5 changed files with 105 additions and 107 deletions

View File

@@ -18,6 +18,11 @@ export function listMenuTree(query: { description: string }) {
return http.get<TreeNodeData[]>(`${BASE_URL}/tree/menu`, query) return http.get<TreeNodeData[]>(`${BASE_URL}/tree/menu`, query)
} }
/** @desc 查询用户列表 */
export function listUserDict(query?: { status: number }) {
return http.get<LabelValueState[]>(`${BASE_URL}/dict/user`, query)
}
/** @desc 查询角色列表 */ /** @desc 查询角色列表 */
export function listRoleDict(query?: { name: string, status: number }) { export function listRoleDict(query?: { name: string, status: number }) {
return http.get<LabelValueState[]>(`${BASE_URL}/dict/role`, query) return http.get<LabelValueState[]>(`${BASE_URL}/dict/role`, query)

View File

@@ -9,6 +9,8 @@ const BASE_URL = '/system/user'
export function listUser(query: T.UserPageQuery) { export function listUser(query: T.UserPageQuery) {
return http.get<PageRes<T.UserResp[]>>(`${BASE_URL}`, query) return http.get<PageRes<T.UserResp[]>>(`${BASE_URL}`, query)
} }
/** @desc 查询所有用户列表 */
export function listAllUser(query: Partial<T.UserPageQuery>) { export function listAllUser(query: Partial<T.UserPageQuery>) {
return http.get<T.UserResp[]>(`${BASE_URL}/list`, query) return http.get<T.UserResp[]>(`${BASE_URL}/list`, query)
} }

View File

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

View File

@@ -3,11 +3,10 @@
<div style="display: flex;"> <div style="display: flex;">
<a-select <a-select
v-model="selectedUsers" v-model="selectedUsers"
:allow-clear="true" :options="userList"
:multiple="props.multiple"
:max-tag-count="4" :max-tag-count="4"
:field-names="{ value: 'id', label: 'nickname' }" :multiple="props.multiple"
:options="options" :allow-clear="true"
@change="handleSelectChange" @change="handleSelectChange"
/> />
<a-tooltip content="选择用户"> <a-tooltip content="选择用户">
@@ -39,7 +38,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import UserSelectContent from './component/UserSelectContent.vue' import UserSelectContent from './component/UserSelectContent.vue'
import { type UserResp, listAllUser } from '@/apis' import { listUserDict } from '@/apis'
import type { UserSelectPropType } from '@/components/UserSelect/type' import type { UserSelectPropType } from '@/components/UserSelect/type'
const props = withDefaults(defineProps<UserSelectPropType>(), { const props = withDefaults(defineProps<UserSelectPropType>(), {
@@ -49,11 +48,11 @@ const props = withDefaults(defineProps<UserSelectPropType>(), {
const emit = defineEmits(['update:value']) const emit = defineEmits(['update:value'])
const visible = ref<boolean>(false) // 控制弹窗显示的状态
const { width } = useWindowSize() // 获取窗口的宽度,用于设置弹窗宽度 const { width } = useWindowSize() // 获取窗口的宽度,用于设置弹窗宽度
const options = ref<UserResp[]>([]) // 保存用户选项列表 const visible = ref<boolean>(false) // 控制弹窗显示的状态
const userList = ref([]) // 保存用户选项列表
const userSelectContentRef = ref() // 引用 UserSelectContent 组件实例 const userSelectContentRef = ref() // 引用 UserSelectContent 组件实例
const selectedUsers = ref<string[]>([]) // 保存已选择的用户 const selectedUsers = ref([]) // 保存已选择的用户
// 打开用户选择弹窗 // 打开用户选择弹窗
const onOpen = () => { const onOpen = () => {
visible.value = true visible.value = true
@@ -85,15 +84,10 @@ const handleModalOk = () => {
// 组件挂载后初始化用户列表 // 组件挂载后初始化用户列表
onMounted(async () => { onMounted(async () => {
const { data } = await listAllUser({}) // 获取所有用户 const { data } = await listUserDict() // 获取所有用户
options.value = data.map((user) => { userList.value = data
user.id = String(user.id)
user.disabled = false // 初始化时设置用户未被禁用
return user
})
// 初始化选择的用户 // 初始化选择的用户
selectedUsers.value = Array.isArray(props.value) ? props.value : props.value.split(',') selectedUsers.value = Array.isArray(props.value) ? props.value : props.value?.split(',')
}) })
</script> </script>

View File

@@ -1,7 +1,8 @@
export interface UserSelectPropType { export interface UserSelectPropType {
multiple: boolean multiple: boolean
value: string | string[] value?: string[]
} }
export interface UserItem { export interface UserItem {
id: string id: string
nickname: string nickname: string