ether-admin/src/views/maintenance/TaskList.vue

402 lines
11 KiB
Vue

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import type { PaginationInfo } from '@/types'
import { Button, Select, Space, message, Badge, Modal, Input, Form } from 'ant-design-vue'
import type { ColumnsType } from 'ant-design-vue/es/table'
import {
SearchOutlined,
ReloadOutlined,
CheckCircleOutlined,
PlayCircleOutlined,
CloseCircleOutlined,
UserOutlined
} from '@ant-design/icons-vue'
import {
getMaintenanceTasks,
acceptMaintenanceTask,
startMaintenanceTask,
completeMaintenanceTask,
cancelMaintenanceTask,
type MaintenanceTask,
type TaskStatus
} from '@/api/maintenance'
import { getProjectSelectorList } from '@/api/project'
// 任务状态映射
const statusMap: Record<TaskStatus, { text: string; color: string; status: 'default' | 'processing' | 'success' | 'error' | 'warning' | 'default' }> = {
PENDING: { text: '待接受', color: 'default', status: 'default' },
ACCEPTED: { text: '已接受', color: 'blue', status: 'processing' },
IN_PROGRESS: { text: '进行中', color: 'processing', status: 'processing' },
COMPLETED: { text: '已完成', color: 'success', status: 'success' },
CANCELLED: { text: '已取消', color: 'error', status: 'error' }
}
// 表格列定义
const columns: ColumnsType = [
{ title: '任务标题', dataIndex: 'title', key: 'title', width: 180 },
{ title: '所属项目', dataIndex: 'projectName', key: 'projectName', width: 150 },
{ title: '关联设备', dataIndex: 'equipmentName', key: 'equipmentName', width: 150 },
{ title: '负责人', dataIndex: 'assigneeName', key: 'assigneeName', width: 100 },
{ title: '计划日期', dataIndex: 'scheduledDate', key: 'scheduledDate', width: 120 },
{ title: '开始时间', dataIndex: 'startTime', key: 'startTime', width: 160 },
{ title: '完成时间', dataIndex: 'completedTime', key: 'completedTime', width: 160 },
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
{ title: '操作', key: 'action', width: 140, fixed: 'right' as const }
]
// 项目选择选项
const projectOptions = ref<{ value: string; label: string }[]>([])
// 状态选项
const statusOptions = [
{ value: 'PENDING', label: '待接受' },
{ value: 'ACCEPTED', label: '已接受' },
{ value: 'IN_PROGRESS', label: '进行中' },
{ value: 'COMPLETED', label: '已完成' },
{ value: 'CANCELLED', label: '取消' }
]
// 查询参数
const queryParams = reactive({
projectId: '',
status: undefined as TaskStatus | undefined,
assigneeId: undefined as string | undefined
})
// 数据状态
const loading = ref(false)
const tableData = ref<MaintenanceTask[]>([])
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0
})
// 取消模态框
const cancelModalVisible = ref(false)
const cancelReason = ref('')
const cancellingTaskId = ref<string | null>(null)
// 完成模态框
const completeModalVisible = ref(false)
const completionNotes = ref('')
const completingTaskId = ref<string | null>(null)
// 获取项目列表
const fetchProjects = async () => {
try {
const res = await getProjectSelectorList()
projectOptions.value = (res.data.data || []).map((item: any) => ({
value: item.id,
label: item.name
}))
} catch {
message.error('获取项目列表失败')
}
}
// 获取维保任务列表
const fetchTaskList = async () => {
if (!queryParams.projectId) {
message.warning('请先选择项目')
return
}
loading.value = true
try {
const res = await getMaintenanceTasks({
projectId: queryParams.projectId,
status: queryParams.status,
assigneeId: queryParams.assigneeId
})
const data = res.data.data || []
tableData.value = data
pagination.total = data.length
} catch {
message.error('获取维保任务列表失败')
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
pagination.current = 1
fetchTaskList()
}
// 重置
const handleReset = () => {
queryParams.projectId = ''
queryParams.status = undefined
queryParams.assigneeId = undefined
pagination.current = 1
tableData.value = []
}
// 分页变化
const handlePageChange = (page: number, pageSize: number) => {
pagination.current = page
pagination.pageSize = pageSize
fetchTaskList()
}
// 接受任务
const handleAccept = async (id: string) => {
try {
await acceptMaintenanceTask(id, 'current-user-id')
message.success('任务已接受')
fetchTaskList()
} catch {
message.error('接受任务失败')
}
}
// 开始任务
const handleStart = async (id: string) => {
try {
await startMaintenanceTask(id)
message.success('任务已开始')
fetchTaskList()
} catch {
message.error('开始任务失败')
}
}
// 打开完成模态框
const handleOpenComplete = (id: string) => {
completingTaskId.value = id
completionNotes.value = ''
completeModalVisible.value = true
}
// 完成任务
const handleComplete = async () => {
if (!completingTaskId.value) return
try {
await completeMaintenanceTask(completingTaskId.value, { completionNotes: completionNotes.value })
message.success('任务已完成')
completeModalVisible.value = false
completingTaskId.value = null
completionNotes.value = ''
fetchTaskList()
} catch {
message.error('完成任务失败')
}
}
// 打开取消模态框
const handleOpenCancel = (id: string) => {
cancellingTaskId.value = id
cancelReason.value = ''
cancelModalVisible.value = true
}
// 取消任务
const handleCancel = async () => {
if (!cancellingTaskId.value) return
try {
await cancelMaintenanceTask(cancellingTaskId.value)
message.success('任务已取消')
cancelModalVisible.value = false
cancellingTaskId.value = null
cancelReason.value = ''
fetchTaskList()
} catch {
message.error('取消任务失败')
}
}
// 格式化日期时间
const formatDateTime = (date: string | Date | undefined) => {
if (!date) return '-'
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hour = String(d.getHours()).padStart(2, '0')
const minute = String(d.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
}
onMounted(() => {
fetchProjects()
})
</script>
<template>
<div class="page-container">
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">维保任务</h2>
</div>
<!-- 筛选区 -->
<div class="filter-bar">
<Space>
<Select
v-model:value="queryParams.projectId"
placeholder="请选择项目"
style="width: 240px"
allow-clear
:options="projectOptions"
/>
<Select
v-model:value="queryParams.status"
placeholder="任务状态"
style="width: 120px"
allow-clear
:options="statusOptions"
/>
<Button type="primary" @click="handleSearch">
<SearchOutlined /> 查询
</Button>
<Button @click="handleReset">
<ReloadOutlined /> 重置
</Button>
</Space>
</div>
<!-- 表格区 -->
<div class="table-card">
<a-table
:columns="columns"
:data-source="tableData"
:loading="loading"
:row-key="(record: MaintenanceTask) => record.id"
:pagination="{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`
}"
@change="(pag: PaginationInfo) => handlePageChange(pag.current, pag.pageSize)"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'assigneeName'">
<Space>
<UserOutlined />
{{ record.assigneeName || '-' }}
</Space>
</template>
<template v-else-if="column.key === 'scheduledDate'">
{{ record.scheduledDate || '-' }}
</template>
<template v-else-if="column.key === 'startTime'">
{{ formatDateTime(record.startTime) }}
</template>
<template v-else-if="column.key === 'completedTime'">
{{ formatDateTime(record.completedTime) }}
</template>
<template v-else-if="column.key === 'status'">
<Badge
:status="statusMap[record.status as TaskStatus]?.status"
:text="statusMap[record.status as TaskStatus]?.text"
/>
</template>
<template v-else-if="column.key === 'action'">
<Space>
<!-- 待接受状态显示接受按钮 -->
<Button
v-if="record.status === 'PENDING'"
type="link"
size="small"
@click="handleAccept(record.id)"
>
<CheckCircleOutlined /> 接受
</Button>
<!-- 已接受状态显示开始按钮 -->
<Button
v-if="record.status === 'ACCEPTED'"
type="link"
size="small"
@click="handleStart(record.id)"
>
<PlayCircleOutlined /> 开始
</Button>
<!-- 进行中状态显示完成按钮 -->
<Button
v-if="record.status === 'IN_PROGRESS'"
type="link"
size="small"
@click="handleOpenComplete(record.id)"
>
<CheckCircleOutlined /> 完成
</Button>
<!-- 待接受、已接受、进行中状态显示取消按钮 -->
<Button
v-if="['PENDING', 'ACCEPTED', 'IN_PROGRESS'].includes(record.status)"
type="link"
size="small"
danger
@click="handleOpenCancel(record.id)"
>
<CloseCircleOutlined /> 取消
</Button>
</Space>
</template>
</template>
</a-table>
<!-- 未选择项目提示 -->
<a-empty v-if="!queryParams.projectId && tableData.length === 0" description="请先选择项目" />
</div>
<!-- 取消任务模态框 -->
<Modal
v-model:open="cancelModalVisible"
title="取消任务"
@ok="handleCancel"
>
<p>确定要取消该维保任务吗?</p>
</Modal>
<!-- 完成任务模态框 -->
<Modal
v-model:open="completeModalVisible"
title="完成任务"
@ok="handleComplete"
>
<Form layout="vertical">
<Form.Item label="完成备注">
<Input.TextArea
v-model:value="completionNotes"
placeholder="请输入完成备注"
:rows="4"
/>
</Form.Item>
</Form>
</Modal>
</div>
</template>
<style scoped>
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title {
margin: 0;
font-size: 20px;
font-weight: 500;
color: #262626;
}
.filter-bar {
margin-bottom: 24px;
padding: 16px;
background: #fafafa;
border-radius: 8px;
}
.table-card {
background: #fff;
border-radius: 8px;
padding: 16px;
}
</style>