263 lines
7.9 KiB
Vue
263 lines
7.9 KiB
Vue
<template>
|
|
<div class="container">
|
|
<a-row :gutter="16">
|
|
<a-col :span="24" :md="17">
|
|
<GiTable
|
|
v-model:selectedKeys="selectedKeys"
|
|
row-key="id"
|
|
:data="dataList"
|
|
:columns="listColumns"
|
|
:loading="loading"
|
|
:scroll="{ x: '100%', y: '100%', minWidth: 500 }"
|
|
style="max-height: 600px"
|
|
:pagination="pagination"
|
|
:disabled-tools="['size', 'fullscreen', 'setting', 'refresh']"
|
|
:row-selection="{ type: props.multiple ? 'checkbox' : 'radio', showCheckedAll: true }"
|
|
@select="onSelect"
|
|
@select-all="onSelectAll"
|
|
@refresh="search"
|
|
>
|
|
<template #top>
|
|
<a-space wrap :size="[8, 8]">
|
|
<a-input-search v-model="queryForm.description" placeholder="搜索用户名/昵称/描述" allow-clear @search="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>
|
|
<a-alert>
|
|
<template v-if="selectedKeys.length > 0">
|
|
已选中 {{ selectedKeys.length }} 条记录(可跨页)
|
|
</template>
|
|
<template v-else>未选中任何记录</template>
|
|
<template v-if="selectedKeys.length > 0" #action>
|
|
<a-link @click="onClearSelected">清空</a-link>
|
|
</template>
|
|
</a-alert>
|
|
</template>
|
|
|
|
<template #gender="{ record }">
|
|
<GiCellGender :gender="record.gender" />
|
|
</template>
|
|
<template #status="{ record }">
|
|
<GiCellStatus :status="record.status" />
|
|
</template>
|
|
</GiTable>
|
|
</a-col>
|
|
<a-col :span="24" :md="7" class="section">
|
|
<a-card>
|
|
<template #title>已选择: {{ selectedKeys.length }}</template>
|
|
<a-table :columns="selectedColumns" :data="[...selectedData.values()]" :pagination="paginationOptions">
|
|
<template #nickname="{ record }">
|
|
{{ record.nickname }}({{ record.username }})
|
|
</template>
|
|
<template #action="{ record }">
|
|
<a-button status="danger" size="mini" @click="handleDeleteSelectedUser(record)">
|
|
<icon-delete />
|
|
</a-button>
|
|
</template>
|
|
</a-table>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { TableInstance, TreeNodeData } from '@arco-design/web-vue'
|
|
|
|
import { type UserQuery, type UserResp, listAllUser, listUser } from '@/apis'
|
|
import { type Options, useTable } from '@/hooks'
|
|
import { useDept } from '@/hooks/app'
|
|
import { isMobile } from '@/utils'
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
multiple: true,
|
|
value: () => [],
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'select-user', keys: Array<any>): void
|
|
}>()
|
|
|
|
interface Props {
|
|
multiple?: boolean
|
|
value: string | string[]
|
|
roleId?: string
|
|
}
|
|
|
|
// 查询表单
|
|
const queryForm = reactive<UserQuery>({
|
|
sort: ['t1.createTime,desc', 't1.id,desc'],
|
|
roleId: props.roleId,
|
|
})
|
|
|
|
// 用户列表
|
|
const { tableData: dataList, loading, pagination, search } = useTable(
|
|
(page) => listUser({ ...queryForm, ...page }),
|
|
{ immediate: true, formatResult: (data) => data.map((i) => ({ ...i, id: `${i?.id}`, disabled: false })) },
|
|
)
|
|
|
|
// 表格列定义
|
|
const listColumns: TableInstance['columns'] = [
|
|
{
|
|
title: '序号',
|
|
width: 66,
|
|
align: 'center',
|
|
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
|
fixed: !isMobile() ? 'left' : undefined,
|
|
},
|
|
{
|
|
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', minWidth: 180, ellipsis: true, tooltip: true },
|
|
{ title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true },
|
|
]
|
|
|
|
// 右侧已选用户列定义
|
|
const selectedColumns = [
|
|
{ title: '用户', dataIndex: 'nickname', slotName: 'nickname', minWidth: 140, ellipsis: true, tooltip: true },
|
|
{ title: '操作', dataIndex: 'action', slotName: 'action', align: 'center', width: 90 },
|
|
]
|
|
const paginationOptions: Options = {
|
|
defaultPageSize: 10,
|
|
defaultSizeOptions: [10, 20, 30, 40, 50],
|
|
}
|
|
|
|
// 重置
|
|
const reset = () => {
|
|
queryForm.description = undefined
|
|
queryForm.deptId = undefined
|
|
search()
|
|
}
|
|
|
|
// 选中用户 ID
|
|
const selectedKeys = ref<string[]>([])
|
|
// 选中用户信息
|
|
const selectedData = ref<Map<string, UserResp>>(new Map())
|
|
|
|
const emitSelectUser = () => {
|
|
emit('select-user', selectedKeys.value)
|
|
}
|
|
|
|
// 单选
|
|
const onSelect = (rowKeys: string[], rowKey: string, record: UserResp) => {
|
|
if (props.multiple) {
|
|
if (rowKeys.includes(rowKey)) {
|
|
// 选中时,添加到 Map
|
|
selectedData.value.set(rowKey, record)
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
} else {
|
|
// 取消选中时,从 Map 移除
|
|
selectedData.value.delete(rowKey)
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
}
|
|
} else {
|
|
selectedData.value.clear()
|
|
selectedKeys.value = []
|
|
if (rowKeys.includes(rowKey)) {
|
|
selectedData.value.set(rowKey, record)
|
|
selectedKeys.value = [rowKey]
|
|
}
|
|
}
|
|
emitSelectUser()
|
|
}
|
|
|
|
// 全选
|
|
const onSelectAll = (checked: boolean) => {
|
|
if (checked) {
|
|
// 全选时,将所有数据添加到 Map
|
|
dataList.value.forEach((item) => {
|
|
selectedData.value.set(item.id, item)
|
|
})
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
} else {
|
|
// 取消全选时,清空 Map
|
|
dataList.value.forEach((item) => {
|
|
selectedData.value.delete(item.id)
|
|
})
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
}
|
|
emitSelectUser()
|
|
}
|
|
|
|
// 从选中列表中移除用户
|
|
const handleDeleteSelectedUser = (user: UserResp) => {
|
|
selectedData.value.delete(user.id)
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
emitSelectUser()
|
|
}
|
|
|
|
// 清空所有选中数据
|
|
const onClearSelected = () => {
|
|
selectedData.value.clear()
|
|
selectedKeys.value = []
|
|
emitSelectUser()
|
|
}
|
|
|
|
// 部门列表
|
|
const { deptList, getDeptList } = useDept()
|
|
// 过滤部门
|
|
const filterDeptOptions = (searchKey: string, nodeData: TreeNodeData) => {
|
|
if (nodeData.title) {
|
|
return nodeData.title.toLowerCase().includes(searchKey.toLowerCase())
|
|
}
|
|
return false
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await getDeptList()
|
|
// 过滤已选择的用户
|
|
if (props.value && props.value.length > 0) {
|
|
const { data } = await listAllUser({ userIds: props.value })
|
|
if (props.multiple) {
|
|
// 使用 Map 存储用户,避免重复
|
|
data.map((i) => ({ ...i, id: `${i?.id}`, disabled: false })).forEach((item) => {
|
|
if (props.value.includes(item.id)) {
|
|
selectedData.value.set(item.id, item)
|
|
}
|
|
})
|
|
selectedKeys.value = Array.from(selectedData.value.keys())
|
|
} else {
|
|
const user = data.find((item) => props.value[0] === item.id)
|
|
if (user) {
|
|
selectedData.value.set(user.id, user)
|
|
selectedKeys.value = [user.id]
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
defineExpose({ onClearSelected })
|
|
</script>
|
|
|
|
<style scoped>
|
|
:deep(.arco-row-align-start) {
|
|
align-items: normal;
|
|
}
|
|
|
|
.container {
|
|
padding: 0 20px;
|
|
}
|
|
</style>
|