| | |
| | | <script setup lang="tsx"> |
| | | import { ref, h, computed } from 'vue' |
| | | import { useBoolean } from '@/hooks' |
| | | import { computed, h, ref } from 'vue' |
| | | import { useBoolean, usePermission } from '@/hooks' |
| | | import { useRouter } from 'vue-router' |
| | | import type { DataTableColumns } from 'naive-ui' |
| | | import { NButton, NTag, NDropdown, NTooltip, NSwitch } from 'naive-ui' |
| | | import { usePermission } from '@/hooks' |
| | | import { getOperationsUserList, exportOperationsUserTemplate, exportOperationsUser, importOperationsUser, |
| | | addOperationsUser, editOperationsUser, updateOperationsUserStatus, deleteOperationsUser } from '@/service/api/attendance' |
| | | import { NButton, NDropdown, NSwitch, NTag, NTooltip } from 'naive-ui' |
| | | import { addOperationsUser, deleteOperationsUser, editOperationsUser, exportOperationsUser, exportOperationsUserTemplate, getOperationsUserList, importOperationsUser, updateOperationsUserStatus } from '@/service/api/attendance' |
| | | import { fetchRoleList } from '@/service/api/fleet' |
| | | import { downloadFile } from '@/utils' |
| | | import SearchPanel from '@/components/custom/SearchPanel.vue' |
| | |
| | | nickName: '', |
| | | phoneNumber: '', |
| | | roleId: null as string | null, |
| | | status: null as string | null |
| | | status: null as string | null, |
| | | }) |
| | | |
| | | const showFormModal = ref(false) |
| | |
| | | |
| | | const statusOptions = [ |
| | | { label: '启用', value: true }, |
| | | { label: '禁用', value: false } |
| | | { label: '禁用', value: false }, |
| | | ] |
| | | |
| | | const personnelList = ref<any[]>([]) |
| | |
| | | pageParams.value.pageNum = 1 |
| | | getList() |
| | | } |
| | | const handleReset = () => { |
| | | function handleReset() { |
| | | searchModel.value = { |
| | | nickName: '', |
| | | phoneNumber: '', |
| | | roleId: null, |
| | | status: null |
| | | status: null, |
| | | } |
| | | handleSearch() |
| | | } |
| | | |
| | | const handleExport = () => { |
| | | function handleExport() { |
| | | const params = { |
| | | ...pageParams.value, |
| | | nickName: searchModel.value.nickName, |
| | |
| | | roleId: searchModel.value.roleId, |
| | | status: searchModel.value.status, |
| | | } |
| | | exportOperationsUser(params).then(res => { |
| | | exportOperationsUser(params).then((res) => { |
| | | if (res instanceof Blob) { |
| | | downloadFile(res, `运维人员列表_${new Date().getTime()}.xlsx`) |
| | | window.$message.success('导出成功') |
| | |
| | | showResetPasswordModal.value = true |
| | | } |
| | | |
| | | const handleAdd = () => { |
| | | function handleAdd() { |
| | | formMode.value = 'add' |
| | | showFormModal.value = true |
| | | } |
| | | |
| | | const handleEdit = (row: any) => { |
| | | function handleEdit(row: any) { |
| | | formMode.value = 'edit' |
| | | showFormModal.value = true |
| | | modalRef.value?.openModal(row) |
| | | } |
| | | |
| | | const handleDelete = (row: any) => { |
| | | function handleDelete(row: any) { |
| | | dialog.create({ |
| | | title: '是否确认删除数据?', |
| | | content: `确认删除该人员?此操作不可恢复。`, |
| | | positiveText: '确认', |
| | | negativeText: '取消', |
| | | maskClosable: false, |
| | | autoFocus: false, |
| | | autoFocus: false, |
| | | onPositiveClick: () => { |
| | | deleteOperationsUser(row.id).then(res => { |
| | | deleteOperationsUser(row.id).then((res) => { |
| | | if (res.code === 200) { |
| | | window.$message.success('删除成功') |
| | | getList() |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | }) |
| | | } |
| | | |
| | |
| | | positiveText: '确认', |
| | | negativeText: '取消', |
| | | maskClosable: false, |
| | | autoFocus: false, |
| | | autoFocus: false, |
| | | onPositiveClick: async () => { |
| | | const res = await updateOperationsUserStatus({ |
| | | id: row.id, |
| | | status: newStatus |
| | | status: newStatus, |
| | | }) |
| | | if (res.code === 200) { |
| | | window.$message.success('操作成功') |
| | |
| | | }) |
| | | } |
| | | |
| | | const handleConfirmForm = (data: any) => { |
| | | function handleConfirmForm(data: any) { |
| | | if (data.id) { |
| | | editOperationsUser(data).then(res => { |
| | | editOperationsUser(data).then((res) => { |
| | | if (res.code === 200) { |
| | | showFormModal.value = false |
| | | window.$message.success('编辑成功') |
| | | getList() |
| | | } |
| | | }) |
| | | } else { |
| | | addOperationsUser(data).then(res => { |
| | | } |
| | | else { |
| | | addOperationsUser(data).then((res) => { |
| | | if (res.code === 200) { |
| | | showFormModal.value = false |
| | | window.$message.success('新增成功') |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | const filterItems = computed(() => [ |
| | | { |
| | | label: '员工姓名', |
| | | path: 'nickName', |
| | | render: () => <n-input v-model:value={searchModel.value.nickName} placeholder="请输入" clearable /> |
| | | render: () => <n-input v-model:value={searchModel.value.nickName} placeholder="请输入" clearable />, |
| | | }, |
| | | { |
| | | label: '联系电话', |
| | | path: 'phoneNumber', |
| | | render: () => <n-input v-model:value={searchModel.value.phoneNumber} placeholder="请输入" clearable /> |
| | | render: () => <n-input v-model:value={searchModel.value.phoneNumber} placeholder="请输入" clearable />, |
| | | }, |
| | | { |
| | | label: '角色', |
| | |
| | | ), |
| | | }} |
| | | </n-select> |
| | | ) |
| | | ), |
| | | }, |
| | | { |
| | | label: '账号状态', |
| | |
| | | ), |
| | | }} |
| | | </n-select> |
| | | ) |
| | | ), |
| | | }, |
| | | ]) |
| | | |
| | |
| | | resizable: true, |
| | | align: 'left', |
| | | render: (row) => { |
| | | const avatarUrl = row.avatar ? JSON.parse(row.avatar).url : null; |
| | | const avatarUrl = row.avatar ? JSON.parse(row.avatar).url : null |
| | | return ( |
| | | <div class="flex items-center gap-2"> |
| | | {avatarUrl ? ( |
| | | <img src={avatarUrl} alt="头像" class="w-24px h-24px rounded-full object-cover" onError={(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/ab6c7ff4-403a-4a2c-9ebf-310c023dc20f.png' }} /> |
| | | ) : row.sex === 1 ? ( |
| | | <img src="/src/assets/element-img/avatar.png" class="w-24px h-24px rounded-full" alt="默认男头像" /> |
| | | ) : ( |
| | | <img src="/src/assets/element-img/acatar-woman.png" class="w-24px h-24px rounded-full" alt="默认女头像" /> |
| | | )} |
| | | {avatarUrl |
| | | ? ( |
| | | <img src={avatarUrl} alt="头像" class="w-24px h-24px rounded-full object-cover" onError={(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/ab6c7ff4-403a-4a2c-9ebf-310c023dc20f.png' }} /> |
| | | ) |
| | | : row.sex === 1 |
| | | ? ( |
| | | <img src="/src/assets/element-img/avatar.png" class="w-24px h-24px rounded-full" alt="默认男头像" /> |
| | | ) |
| | | : ( |
| | | <img src="/src/assets/element-img/acatar-woman.png" class="w-24px h-24px rounded-full" alt="默认女头像" /> |
| | | )} |
| | | <NTooltip trigger="hover" placement="top" max-width="500px"> |
| | | {{ |
| | | trigger: () => <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>{row.nickName}</div>, |
| | |
| | | ) |
| | | }, |
| | | }, |
| | | { title: '编号', key: 'uniqueNumber', width: 100, resizable: true, align: 'left' }, |
| | | { title: '联系电话', key: 'phoneNumber', width: 120, resizable: true, align: 'left' }, |
| | | { title: '编号', key: 'uniqueNumber', width: 100, resizable: true, align: 'left', ellipsis: { tooltip: true } }, |
| | | { title: '联系电话', key: 'phoneNumber', width: 120, resizable: true, align: 'left' }, |
| | | { |
| | | title: '角色', |
| | | key: 'sysRoles', |
| | | width: 360, |
| | | resizable: true, |
| | | align: 'left', |
| | | ellipsis: true , |
| | | render: (row) => ( |
| | | ellipsis: true, |
| | | render: row => ( |
| | | <div class="flex items-center gap-1 flex-wrap"> |
| | | { |
| | | row.sysRoles?.map((role: any) => ( |
| | | <NTag type="primary" size="small" style={{ borderRadius: '4px' }} >{role.roleName}</NTag> |
| | | <NTag type="primary" bordered={false} size="small" style={{ borderRadius: '4px' }}>{role.roleName}</NTag> |
| | | )) |
| | | } |
| | | </div> |
| | | ) |
| | | ), |
| | | }, |
| | | { |
| | | title: '状态', |
| | |
| | | width: 80, |
| | | resizable: true, |
| | | align: 'left', |
| | | render: (row) => ( |
| | | row.status ? ( |
| | | <NTag |
| | | bordered={false} |
| | | style={{ |
| | | borderRadius: '4px', |
| | | color: '#0BB52D', |
| | | backgroundColor: '#DCF5DD', |
| | | }} |
| | | size="small" |
| | | > |
| | | 启用 |
| | | </NTag> |
| | | ) : ( |
| | | <NTag |
| | | bordered={false} |
| | | style={{ |
| | | borderRadius: '4px', |
| | | color: '#666666', |
| | | backgroundColor: 'rgba(23, 31, 38, 0.05)', |
| | | }} |
| | | size="small" |
| | | > |
| | | 禁用 |
| | | </NTag> |
| | | ) |
| | | ) |
| | | render: row => ( |
| | | row.status |
| | | ? ( |
| | | <NTag |
| | | bordered={false} |
| | | style={{ |
| | | borderRadius: '4px', |
| | | color: '#0BB52D', |
| | | backgroundColor: '#DCF5DD', |
| | | }} |
| | | size="small" |
| | | > |
| | | 启用 |
| | | </NTag> |
| | | ) |
| | | : ( |
| | | <NTag |
| | | bordered={false} |
| | | style={{ |
| | | borderRadius: '4px', |
| | | color: '#666666', |
| | | backgroundColor: 'rgba(23, 31, 38, 0.05)', |
| | | }} |
| | | size="small" |
| | | > |
| | | 禁用 |
| | | </NTag> |
| | | ) |
| | | ), |
| | | }, |
| | | { title: '创建时间', key: 'createTime', width: 160 , resizable: true, align: 'left', render: (row) => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' }, |
| | | { title: '备注', key: 'remarks', width: 150, resizable: true, align: 'left', ellipsis: { tooltip: true }, render: (row) => row.remarks || '-' }, |
| | | { title: '创建时间', key: 'createTime', width: 160, resizable: true, align: 'left', render: row => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' }, |
| | | { title: '备注', key: 'remarks', width: 150, resizable: true, align: 'left', ellipsis: { tooltip: true }, render: row => row.remarks || '-' }, |
| | | { |
| | | title: '操作', |
| | | key: 'actions', |
| | | width: 220, |
| | | width: 250, |
| | | fixed: 'right', |
| | | align: 'left', |
| | | render: (row) => ( |
| | | <div class="flex items-center gap-3"> |
| | | |
| | | render: row => ( |
| | | <div class="flex items-center gap-16px"> |
| | | |
| | | {/* <a v-permission="70115" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleEdit(row)}>编辑</a> */} |
| | | <a v-permission="70117" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleResetPassword(row)}>重置密码</a> |
| | | <a v-permission="70111" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>详情</a> |
| | | <a v-permission="70111" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>详情</a> |
| | | <a v-permission="70118" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleDelete(row)}>删除</a> |
| | | <NSwitch |
| | | v-permission="70116" |
| | |
| | | }} |
| | | </NSwitch> |
| | | </div> |
| | | ) |
| | | } |
| | | ), |
| | | }, |
| | | ]) |
| | | |
| | | async function getList() { |
| | |
| | | } |
| | | getOperationsUserList(params).then((res) => { |
| | | personnelList.value = res.data.records || [] |
| | | personnelList.value.forEach((item: any, index: number) => { |
| | | personnelList.value.forEach((item: any, index: number) => { |
| | | item.serialNumber = (pageParams.value.pageNum - 1) * pageParams.value.pageSize + index + 1 |
| | | }) |
| | | total.value = res.data.total || res.data.records?.length |
| | |
| | | getList() |
| | | } |
| | | |
| | | const handleDownloadTemplate = async () => { |
| | | async function handleDownloadTemplate() { |
| | | const response = await exportOperationsUserTemplate() |
| | | if (response instanceof Blob) { |
| | | downloadFile(response, `运维人员导入模板.xlsx`) |
| | |
| | | </n-form-item> |
| | | </n-gi> |
| | | <template #action> |
| | | <n-button style="width: 84px; height: 32px" @click="handleReset">重置</n-button> |
| | | <n-button type="primary" style="width: 84px; height: 32px" @click="handleSearch">查询</n-button> |
| | | <NButton style="width: 84px; height: 32px" @click="handleReset"> |
| | | 重置 |
| | | </NButton> |
| | | <NButton type="primary" style="width: 84px; height: 32px" @click="handleSearch"> |
| | | 查询 |
| | | </NButton> |
| | | </template> |
| | | </SearchPanel> |
| | | </n-form> |
| | |
| | | <n-card class="flex-1 robotaxi-table-card"> |
| | | <template #header> |
| | | <n-flex justify="flex-end"> |
| | | <n-button v-permission="70112" @click="handleExport" style="width: 84px; height: 32px">导出</n-button> |
| | | <n-button v-permission="70113" @click="handleImport" style="width: 84px; height: 32px">导入</n-button> |
| | | <n-button v-permission="70112" @click="handleDownloadTemplate" style="width: 84px; height: 32px">下载</n-button> |
| | | <n-button v-permission="70114" style="width: 84px;height: 32px;background-color: #E6F4FF;color: #0068FF;border: 1px solid #7ABDFF;" |
| | | @click="handleAdd">新增</n-button> |
| | | <NButton v-permission="70112" style="width: 84px; height: 32px" @click="handleExport"> |
| | | 导出 |
| | | </NButton> |
| | | <NButton v-permission="70113" style="width: 84px; height: 32px" @click="handleImport"> |
| | | 导入 |
| | | </NButton> |
| | | <NButton v-permission="70112" style="width: 84px; height: 32px" @click="handleDownloadTemplate"> |
| | | 下载 |
| | | </NButton> |
| | | <NButton |
| | | v-permission="70114" |
| | | secondary style="width: 84px;height: 32px;background-color: #E6F4FF;color: #0068FF;" |
| | | @click="handleAdd" |
| | | > |
| | | 新增 |
| | | </NButton> |
| | | </n-flex> |
| | | </template> |
| | | <div class="flex-1 flex flex-col h-full"> |
| | |
| | | </div> |
| | | </n-card> |
| | | |
| | | <PersonnelFormModal v-model:show="showFormModal" ref="modalRef" :mode="formMode" :roleOptions="roleOptions" |
| | | @confirm="handleConfirmForm" /> |
| | | <!-- 重置密码弹窗 --> |
| | | <PersonnelFormModal |
| | | ref="modalRef" v-model:show="showFormModal" :mode="formMode" :role-options="roleOptions" |
| | | @confirm="handleConfirmForm" |
| | | /> |
| | | <!-- 重置密码弹窗 --> |
| | | <ResetPasswordModal |
| | | v-model:visible="showResetPasswordModal" |
| | | :row-data="resetPasswordRow" |
| | |
| | | style="display: none" |
| | | accept=".xlsx, .xls" |
| | | @change="handleFileChange" |
| | | /> |
| | | > |
| | | </div> |
| | | </template> |
| | | |