Industrial-image-management.../src/views/system/post/index.vue

378 lines
10 KiB
Vue

<template>
<GiPageLayout>
<GiTable
row-key="postId"
:data="dataList"
:columns="tableColumns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1200 }"
:pagination="pagination"
:disabled-tools="['size']"
@refresh="search"
>
<template #top>
<GiForm v-model="queryForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset"></GiForm>
</template>
<template #toolbar-left>
<a-button v-permission="['system:post:create']" type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<template #default>新增</template>
</a-button>
</template>
<template #status="{ record }">
<a-tag :color="Number(record.status) === 1 ? 'green' : 'red'" size="small">
{{ Number(record.status) === 1 ? '正常' : '停用' }}
</a-tag>
</template>
<template #postName="{ record }">
<a-dropdown trigger="hover" @visible-change="(visible) => onDropdownVisibleChange(visible, record)">
<a-link type="primary" :title="`点击查询 ${record.postName} 岗位下的用户信息`" @click="onPostClick(record)">
{{ record.postName }}
<template #icon>
<icon-user style="margin-right: 4px; font-size: 12px; color: #666;" />
<icon-down />
</template>
</a-link>
<template #content>
<a-doption
:value="record.postId"
:disabled="userLoadingMap[record.postId]"
>
<div v-if="userLoadingMap[record.postId]" class="loading-container">
<a-spin size="mini" />
<span style="margin-left: 8px;">加载中...</span>
</div>
<div v-else-if="postUsersMap[record.postId]?.length">
<div class="user-list">
<div class="user-list-header">
<span class="user-count">该岗位下有 {{ postUsersMap[record.postId].length }} 个用户</span>
</div>
<div class="user-item" v-for="user in postUsersMap[record.postId]" :key="user.userId">
<a-avatar :size="24" :src="user.avatar">
{{ user.name?.charAt(0) || user.account?.charAt(0) || 'U' }}
</a-avatar>
<div class="user-info">
<div class="user-name clickable" @click="onUserClick(user)" :title="`点击查看 ${user.name || user.account} 的详细信息`">{{ user.name || user.account }}</div>
<div class="user-detail">
<span v-if="user.mobile">手机: {{ user.mobile }}</span>
<span v-if="user.email">邮箱: {{ user.email }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-else class="no-users">
该岗位暂无用户
</div>
</a-doption>
</template>
</a-dropdown>
</template>
<template #action="{ record }">
<a-space>
<a-link v-permission="['system:post:query']" title="详情" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['system:post:update']" title="修改" @click="onUpdate(record)">修改</a-link>
<a-link
v-permission="['system:post:delete']"
status="danger"
title="删除"
@click="onDelete(record)"
>
删除
</a-link>
</a-space>
</template>
</GiTable>
<PostAddModal ref="PostAddModalRef" @save-success="search" />
<PostDetailDrawer ref="PostDetailDrawerRef" />
<UserDetailDrawer ref="UserDetailDrawerRef" />
</GiPageLayout>
</template>
<script setup lang="ts">
import type { TableColumnData } from '@arco-design/web-vue'
import PostAddModal from './PostAddModal.vue'
import PostDetailDrawer from './PostDetailDrawer.vue'
import UserDetailDrawer from '../user/UserDetailDrawer.vue'
import { deletePost, listPost, getPostUsers } from '@/apis/system/post'
import type { PostVO } from '@/apis/system/type'
import type { UserNewResp } from '@/apis/system/type'
import { useResetReactive, useTable } from '@/hooks'
import { isMobile } from '@/utils'
import has from '@/utils/has'
import type { ColumnItem } from '@/components/GiForm'
defineOptions({ name: 'SystemPost' })
const [queryForm, resetForm] = useResetReactive({
postName: undefined,
status: undefined,
})
const queryFormColumns: ColumnItem[] = reactive([
{
type: 'input',
label: '岗位名称',
field: 'postName',
span: { xs: 24, sm: 12, xxl: 8 },
props: {
placeholder: '请输入岗位名称',
},
},
{
type: 'select',
label: '状态',
field: 'status',
span: { xs: 24, sm: 12, xxl: 8 },
props: {
options: [
{ label: '正常', value: 1 },
{ label: '停用', value: 0 },
],
placeholder: '请选择状态',
},
},
])
const {
tableData: dataList,
loading,
pagination,
search,
handleDelete,
} = useTable((params) => listPost({
...queryForm,
}), { immediate: true })
// 岗位用户数据
const postUsersMap = ref<Record<string, UserNewResp[]>>({})
const userLoadingMap = ref<Record<string, boolean>>({})
const tableColumns = ref<TableColumnData[]>([
{
title: '序号',
width: 66,
align: 'center',
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
fixed: !isMobile() ? 'left' : undefined,
},
{
title: '岗位名称',
dataIndex: 'postName',
slotName: 'postName',
minWidth: 140,
ellipsis: true,
tooltip: true,
fixed: !isMobile() ? 'left' : undefined,
},
{
title: '岗位排序',
dataIndex: 'postSort',
minWidth: 100,
ellipsis: true,
tooltip: true,
},
{
title: '状态',
dataIndex: 'status',
slotName: 'status',
align: 'center',
width: 100,
},
{
title: '说明',
dataIndex: 'remark',
minWidth: 180,
ellipsis: true,
tooltip: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
minWidth: 180,
ellipsis: true,
tooltip: true,
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 200,
fixed: !isMobile() ? 'right' : undefined,
show: has.hasPermOr(['system:post:update', 'system:post:delete']),
},
])
// 重置
const reset = () => {
resetForm()
search()
}
// 删除
const onDelete = (record: PostVO) => {
return handleDelete(() => deletePost(record.postId), {
content: `确定删除岗位「${record.postName}」吗?`,
showModal: true,
})
}
// 下拉框显示状态变化时自动加载数据
const onDropdownVisibleChange = async (visible: boolean, record: PostVO) => {
if (visible && !postUsersMap.value[record.postId] && !userLoadingMap.value[record.postId]) {
// 设置加载状态
userLoadingMap.value[record.postId] = true
try {
const response = await getPostUsers(record.postId)
if (response.success && response.data) {
postUsersMap.value[record.postId] = response.data
} else {
postUsersMap.value[record.postId] = []
}
} catch (error) {
console.error('获取岗位用户信息失败:', error)
postUsersMap.value[record.postId] = []
} finally {
userLoadingMap.value[record.postId] = false
}
}
}
// 点击岗位名称触发用户查询
const onPostClick = async (record: PostVO) => {
// 如果已经加载过该岗位的用户数据,直接返回
if (postUsersMap.value[record.postId]) {
return
}
// 设置加载状态
userLoadingMap.value[record.postId] = true
try {
const response = await getPostUsers(record.postId)
if (response.success && response.data) {
postUsersMap.value[record.postId] = response.data
} else {
postUsersMap.value[record.postId] = []
}
} catch (error) {
console.error('获取岗位用户信息失败:', error)
postUsersMap.value[record.postId] = []
} finally {
userLoadingMap.value[record.postId] = false
}
}
// 点击用户详情
const onUserClick = (user: UserNewResp) => {
UserDetailDrawerRef.value?.onOpen(user.userId)
}
const PostAddModalRef = ref<InstanceType<typeof PostAddModal>>()
const PostDetailDrawerRef = ref<InstanceType<typeof PostDetailDrawer>>()
const UserDetailDrawerRef = ref<InstanceType<typeof UserDetailDrawer>>()
// 新增
const onAdd = () => {
PostAddModalRef.value?.onAdd()
}
// 修改
const onUpdate = (record: PostVO) => {
PostAddModalRef.value?.onUpdate(record.postId)
}
// 详情
const onDetail = (record: PostVO) => {
PostDetailDrawerRef.value?.onDetail(record.postId)
}
</script>
<style scoped lang="scss">
.loading-container {
display: flex;
align-items: center;
padding: 8px;
}
.user-list {
min-width: 300px;
max-width: 400px;
.user-list-header {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
margin-bottom: 8px;
.user-count {
font-size: 12px;
color: #666;
}
}
.user-item {
display: flex;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.user-info {
margin-left: 8px;
flex: 1;
.user-name {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 2px;
&.clickable {
cursor: pointer;
color: #165dff;
transition: color 0.2s ease;
&:hover {
color: #0e42d2;
text-decoration: underline;
}
}
}
.user-detail {
font-size: 12px;
color: #666;
span {
margin-right: 8px;
&:last-child {
margin-right: 0;
}
}
}
}
}
}
.no-users {
padding: 16px;
text-align: center;
color: #999;
font-size: 14px;
}
</style>