学习进度条
学习时间:1小时
代码量:100行
博客产出:1篇
继续推进团队项目
主要是完成前端页面的代码
MaintenancePlan.vue
<script setup>
import {
Edit,
Delete,
Plus
} from '@element-plus/icons-vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
// 保养计划分类数据模型(示例分类)
const categories = ref([
{ id: 1, categoryName: '设备保养', categoryAlias: 'sbby' },
{ id: 2, categoryName: '车辆保养', categoryAlias: 'clby' },
{ id: 3, categoryName: '设施保养', categoryAlias: 'ssby' }
])
// 搜索条件
const planType = ref('') // 保养类型
const planStatus = ref('') // 保养状态
const searchKey = ref('') // 搜索关键词
// 保养计划列表数据模型
const plans = ref([
{
id: 1,
planName: '服务器年度保养计划',
type: 1,
status: '待执行',
createTime: '2023-11-01 09:00:00',
description: '检查服务器硬件状态并清洁',
nextDate: '2023-11-15', // 下次保养时间
cycle: '年度' // 保养周期
},
{
id: 2,
planName: '公司车辆季度保养',
type: 2,
status: '进行中',
createTime: '2023-11-05 14:20:00',
description: '更换机油、滤芯及轮胎检查',
nextDate: '2024-02-05',
cycle: '季度'
}
])
// 分页参数
const currentPage = ref(1)
const pageSize = ref(10)
const totalCount = ref(0)
// 模拟接口调用(实际需替换为真实API)
const fetchCategories = () => {
categories.value = [
{ id: 1, categoryName: '设备保养' },
{ id: 2, categoryName: '车辆保养' },
{ id: 3, categoryName: '设施保养' }
]
}
const fetchPlans = async () => {
const mockData = {
total: 30,
items: [
{ id: 1, planName: '交换机保养', type: 1, status: '待执行', createTime: '2023-11-01', nextDate: '2023-11-15', cycle: '年度' },
{ id: 2, planName: '货车轮胎保养', type: 2, status: '进行中', createTime: '2023-11-05', nextDate: '2024-02-05', cycle: '季度' }
]
}
totalCount.value = mockData.total
plans.value = mockData.items.map(item => ({
...item,
categoryName: categories.value.find(c => c.id === item.type)?.categoryName || '未知类型'
}))
}
// 生命周期钩子
fetchCategories()
fetchPlans()
// 表单相关
const drawerVisible = ref(false)
const planForm = ref({
id: null,
planName: '',
type: '',
status: '待执行', // 初始状态
description: '',
nextDate: '', // 下次保养时间
cycle: '月度' // 保养周期(默认月度)
})
const handleSubmit = async (isPublish) => {
planForm.value.status = isPublish ? '已发布' : '草稿'
try {
// 模拟保存逻辑(实际需调用API)
if (planForm.value.id) {
ElMessage.success('更新保养计划成功')
} else {
ElMessage.success('新增保养计划成功')
}
drawerVisible.value = false
fetchPlans()
resetForm()
} catch (error) {
ElMessage.error('操作失败,请重试')
}
}
const resetForm = () => {
planForm.value = {
id: null,
planName: '',
type: '',
status: '待执行',
description: '',
nextDate: '',
cycle: '月度'
}
}
const handleDelete = async (id) => {
try {
await ElMessageBox.confirm('确定要删除该保养计划吗?', '提示', {
type: 'warning'
})
ElMessage.success('删除成功')
fetchPlans()
} catch (error) {
if (error !== 'cancel') ElMessage.error('删除失败')
}
}
const handleEdit = (item) => {
planForm.value = {
...item,
status: item.status || '待执行'
}
drawerVisible.value = true
}
</script>
<template>
<el-card class="page-container">
<template #header>
<div class="header">
<span>保养计划管理</span>
<div class="extra">
<el-button type="primary" @click="drawerVisible = true">
<Plus /> 新建保养计划
</el-button>
</div>
</div>
</template>
<!-- 搜索过滤 -->
<el-form inline class="mb-4">
<el-form-item label="保养类型:">
<el-select
v-model="planType"
placeholder="请选择保养类型"
clearable
style="width: 200px"
>
<el-option
v-for="c in categories"
:key="c.id"
:label="c.categoryName"
:value="c.id"
/>
</el-select>
</el-form-item>
<el-form-item label="保养状态:">
<el-select
v-model="planStatus"
placeholder="请选择状态"
clearable
style="width: 150px"
>
<el-option label="待执行" value="待执行" />
<el-option label="进行中" value="进行中" />
<el-option label="已完成" value="已完成" />
<el-option label="已过期" value="已过期" />
</el-select>
</el-form-item>
<el-form-item label="关键词搜索:">
<el-input
v-model="searchKey"
placeholder="请输入计划名称/设备名称"
style="width: 300px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchPlans">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<!-- 计划列表 -->
<el-table :data="plans" style="width: 100%">
<el-table-column label="计划名称" prop="planName" width="200" />
<el-table-column label="保养类型" prop="categoryName" width="120" />
<el-table-column label="状态" prop="status" width="100">
<template #default="{ row }">
<span v-if="row.status === '已过期'" style="color: var(--el-color-danger)">过期</span>
<span v-else>{{ row.status }}</span>
</template>
</el-table-column>
<el-table-column label="下次保养时间" prop="nextDate" width="180" />
<el-table-column label="保养周期" prop="cycle" width="100" />
<el-table-column label="创建时间" prop="createTime" width="180" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button
:icon="Edit"
circle plain
type="primary"
@click="handleEdit(row)"
:disabled="row.status === '已完成'"
/>
<el-button
:icon="Delete"
circle plain
type="danger"
@click="handleDelete(row.id)"
:disabled="row.status === '已完成'"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 10, 20, 50]"
:total="totalCount"
layout="prev, pager, next, jumper, sizes, total"
@size-change="fetchPlans"
@current-change="fetchPlans"
class="mt-4"
/>
<!-- 抽屉表单 -->
<el-drawer
v-model="drawerVisible"
:title="planForm.id ? '编辑保养计划' : '新建保养计划'"
direction="rtl"
size="50%"
@close="resetForm"
>
<el-form :model="planForm" label-width="120px">
<el-form-item label="计划名称">
<el-input
v-model="planForm.planName"
placeholder="请输入保养计划名称"
required
/>
</el-form-item>
<el-form-item label="保养类型">
<el-select
v-model="planForm.type"
placeholder="请选择保养类型"
required
>
<el-option
v-for="c in categories"
:key="c.id"
:label="c.categoryName"
:value="c.id"
/>
</el-select>
</el-form-item>
<el-form-item label="保养状态">
<el-radio-group v-model="planForm.status">
<el-radio label="待执行"></el-radio>
<el-radio label="进行中"></el-radio>
<el-radio label="已完成"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="下次保养时间">
<el-date-picker
v-model="planForm.nextDate"
type="date"
placeholder="选择下次保养日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
required
/>
</el-form-item>
<el-form-item label="保养周期">
<el-select v-model="planForm.cycle" placeholder="请选择保养周期" required>
<el-option label="每日" value="每日" />
<el-option label="每周" value="每周" />
<el-option label="每月" value="每月" />
<el-option label="每季度" value="每季度" />
<el-option label="每年" value="每年" />
</el-select>
</el-form-item>
<el-form-item label="计划描述">
<el-input
type="textarea"
v-model="planForm.description"
placeholder="请输入保养详细内容"
rows="5"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit(true)">发布计划</el-button>
<el-button type="secondary" @click="handleSubmit(false)">保存草稿</el-button>
</el-form-item>
</el-form>
</el-drawer>
</el-card>
</template>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.el-table {
margin-top: 15px;
.is-expired {
color: var(--el-color-danger);
text-decoration: line-through;
}
}
/* 状态颜色样式 */
.el-table .cell {
&[data-status="已过期"] {
color: var(--el-color-danger);
}
&[data-status="待执行"] {
color: var(--el-color-warning);
}
&[data-status="已完成"] {
color: var(--el-color-success);
}
}
}
</style>
InspectionPlan.vue
<script setup>
import {
Edit,
Delete,
Plus,
Search
} from '@element-plus/icons-vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
// 巡检类型数据模型
const inspectionTypes = ref([
{ id: 1, typeName: '日常巡检', typeCode: 'daily' },
{ id: 2, typeName: '月度巡检', typeCode: 'monthly' },
{ id: 3, typeName: '年度巡检', typeCode: 'yearly' },
{ id: 4, typeName: '专项巡检', typeCode: 'special' }
])
// 设备类型数据模型
const deviceTypes = ref([
{ id: 1, typeName: '服务器', typeCode: 'server' },
{ id: 2, typeName: '网络设备', typeCode: 'network' },
{ id: 3, typeName: '存储设备', typeCode: 'storage' },
{ id: 4, typeName: '安全设备', typeCode: 'security' }
])
// 搜索条件
const searchForm = ref({
inspectionType: '', // 巡检类型
deviceType: '', // 设备类型
status: '', // 巡检状态
keyword: '' // 搜索关键词
})
// 巡检计划列表数据模型
const inspectionPlans = ref([
{
id: 1,
planName: '核心服务器日常巡检',
inspectionType: 1,
deviceType: 1,
status: '进行中',
startTime: '2023-10-01 08:00:00',
endTime: '2023-10-31 18:00:00',
frequency: '每日',
responsiblePerson: '张三',
description: '核心业务服务器日常健康检查'
},
{
id: 2,
planName: '网络设备月度巡检',
inspectionType: 2,
deviceType: 2,
status: '未开始',
startTime: '2023-11-01 09:00:00',
endTime: '2023-11-05 18:00:00',
frequency: '每月1次',
responsiblePerson: '李四',
description: '全网网络设备性能及配置检查'
}
])
// 分页参数
const pagination = ref({
currentPage: 1,
pageSize: 10,
total: 0
})
// 获取巡检计划列表
const fetchInspectionPlans = async () => {
// 模拟API调用
const mockData = {
total: 35,
items: [
{
id: 1,
planName: '核心服务器日常巡检',
inspectionType: 1,
deviceType: 1,
status: '进行中',
startTime: '2023-10-01',
endTime: '2023-10-31',
frequency: '每日',
responsiblePerson: '张三'
},
{
id: 2,
planName: '网络设备月度巡检',
inspectionType: 2,
deviceType: 2,
status: '未开始',
startTime: '2023-11-01',
endTime: '2023-11-05',
frequency: '每月1次',
responsiblePerson: '李四'
}
]
}
pagination.value.total = mockData.total
inspectionPlans.value = mockData.items.map(item => ({
...item,
inspectionTypeName: inspectionTypes.value.find(t => t.id === item.inspectionType)?.typeName || '未知类型',
deviceTypeName: deviceTypes.value.find(t => t.id === item.deviceType)?.typeName || '未知设备'
}))
}
// 初始化数据
fetchInspectionPlans()
// 表单相关
const drawerVisible = ref(false)
const planForm = ref({
id: null,
planName: '',
inspectionType: '',
deviceType: '',
status: '未开始',
startTime: '',
endTime: '',
frequency: '',
responsiblePerson: '',
description: ''
})
// 提交表单
const handleSubmit = async () => {
try {
// 验证必填字段
if (!planForm.value.planName) {
ElMessage.warning('请填写计划名称');
return;
}
if (!planForm.value.deviceType) {
ElMessage.warning('请选择设备类型');
return;
}
if (!planForm.value.startTime) {
ElMessage.warning('请设置开始时间');
return;
}
if (!planForm.value.endTime) {
ElMessage.warning('请设置结束时间');
return;
}
if (!planForm.value.responsiblePerson) {
ElMessage.warning('请填写负责人');
return;
}
// 准备提交数据
const submitData = {
planName: planForm.value.planName,
deviceType: planForm.value.deviceType,
startTime: planForm.value.startTime,
endTime: planForm.value.endTime,
frequency: planForm.value.frequency,
responsiblePerson: planForm.value.responsiblePerson,
description: planForm.value.description,
status: planForm.value.status
};
console.log('提交的检测计划数据:', submitData);
if (planForm.value.id) {
// 更新逻辑
const response = await updatePlanService(submitData);
if (response.success) {
ElMessage.success('更新检测计划成功');
} else {
throw new Error(response.message || '更新失败');
}
} else {
// 新增逻辑
const response = await addPlanService(submitData);
if (response.success) {
ElMessage.success('新增检测计划成功');
} else {
throw new Error(response.message || '添加失败');
}
}
drawerVisible.value = false;
fetchInspectionPlans();
resetForm();
} catch (error) {
console.error('操作失败:', error);
ElMessage.error('操作失败: ' + (error.response?.data?.message || error.message));
}
};
// 重置表单
const resetForm = () => {
planForm.value = {
id: null,
planName: '',
inspectionType: '',
deviceType: '',
status: '未开始',
startTime: '',
endTime: '',
frequency: '',
responsiblePerson: '',
description: ''
}
};
// 删除计划
const handleDelete = async (id) => {
try {
await ElMessageBox.confirm('确定要删除该巡检计划吗?', '提示', {
type: 'warning'
})
// await deleteInspectionPlan(id)
ElMessage.success('删除成功')
fetchInspectionPlans()
} catch (error) {
if (error !== 'cancel') ElMessage.error('删除失败')
}
}
// 编辑计划
const handleEdit = (item) => {
planForm.value = { ...item }
drawerVisible.value = true
}
// 重置搜索
const resetSearch = () => {
searchForm.value = {
inspectionType: '',
deviceType: '',
status: '',
keyword: ''
}
fetchInspectionPlans()
}
</script>
<template>
<el-card class="page-container">
<template #header>
<div class="header">
<span>设备巡检计划管理</span>
<div class="extra">
<el-button type="primary" @click="drawerVisible = true">
<Plus /> 新建巡检计划
</el-button>
</div>
</div>
</template>
<!-- 搜索过滤 -->
<el-form :model="searchForm" inline class="mb-4">
<el-form-item label="巡检类型:">
<el-select
v-model="searchForm.inspectionType"
placeholder="请选择巡检类型"
clearable
style="width: 150px"
>
<el-option
v-for="type in inspectionTypes"
:key="type.id"
:label="type.typeName"
:value="type.id"
/>
</el-select>
</el-form-item>
<el-form-item label="设备类型:">
<el-select
v-model="searchForm.deviceType"
placeholder="请选择设备类型"
clearable
style="width: 150px"
>
<el-option
v-for="type in deviceTypes"
:key="type.id"
:label="type.typeName"
:value="type.id"
/>
</el-select>
</el-form-item>
<el-form-item label="状态:">
<el-select
v-model="searchForm.status"
placeholder="请选择状态"
clearable
style="width: 120px"
>
<el-option label="未开始" value="未开始" />
<el-option label="进行中" value="进行中" />
<el-option label="已完成" value="已完成" />
<el-option label="已取消" value="已取消" />
</el-select>
</el-form-item>
<el-form-item label="关键词:">
<el-input
v-model="searchForm.keyword"
placeholder="计划名称/负责人"
style="width: 200px"
:prefix-icon="Search"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchInspectionPlans">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<!-- 计划列表 -->
<el-table :data="inspectionPlans" style="width: 100%" border>
<el-table-column label="计划名称" prop="planName" width="180" />
<el-table-column label="巡检类型" prop="inspectionTypeName" width="100" />
<el-table-column label="设备类型" prop="deviceTypeName" width="100" />
<el-table-column label="状态" prop="status" width="80">
<template #default="{ row }">
<el-tag :type="row.status === '已完成' ? 'success' : row.status === '进行中' ? 'warning' : 'info'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="时间范围" width="220">
<template #default="{ row }">
<div>{{ row.startTime }} 至</div>
<div>{{ row.endTime }}</div>
</template>
</el-table-column>
<el-table-column label="巡检频率" prop="frequency" width="100" />
<el-table-column label="负责人" prop="responsiblePerson" width="100" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
:icon="Edit"
circle plain
type="primary"
@click="handleEdit(row)"
/>
<el-button
:icon="Delete"
circle plain
type="danger"
@click="handleDelete(row.id)"
:disabled="row.status === '进行中' || row.status === '已完成'"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="fetchInspectionPlans"
@current-change="fetchInspectionPlans"
class="mt-4"
/>
<!-- 抽屉表单 -->
<el-drawer
v-model="drawerVisible"
:title="planForm.id ? '编辑巡检计划' : '新建巡检计划'"
direction="rtl"
size="50%"
@close="resetForm"
>
<el-form :model="planForm" label-width="120px">
<el-form-item label="计划名称" required>
<el-input
v-model="planForm.planName"
placeholder="请输入巡检计划名称"
/>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="巡检类型" required>
<el-select
v-model="planForm.inspectionType"
placeholder="请选择巡检类型"
style="width: 100%"
>
<el-option
v-for="type in inspectionTypes"
:key="type.id"
:label="type.typeName"
:value="type.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备类型" required>
<el-select
v-model="planForm.deviceType"
placeholder="请选择设备类型"
style="width: 100%"
>
<el-option
v-for="type in deviceTypes"
:key="type.id"
:label="type.typeName"
:value="type.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开始时间" required>
<el-date-picker
v-model="planForm.startTime"
type="datetime"
placeholder="选择开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间" required>
<el-date-picker
v-model="planForm.endTime"
type="datetime"
placeholder="选择结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="巡检频率" required>
<el-input
v-model="planForm.frequency"
placeholder="如:每日、每周一、每月1次等"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人" required>
<el-input
v-model="planForm.responsiblePerson"
placeholder="请输入负责人姓名"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="计划状态">
<el-radio-group v-model="planForm.status">
<el-radio label="未开始"></el-radio>
<el-radio label="进行中"></el-radio>
<el-radio label="已完成"></el-radio>
<el-radio label="已取消"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="计划描述">
<el-input
type="textarea"
v-model="planForm.description"
placeholder="请输入巡检计划详细描述"
rows="5"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">保存</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-drawer>
</el-card>
</template>
<style lang="scss" scoped>
.page-container {
min-height: 100%;
padding: 20px;
box-sizing: border-box;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.el-table {
margin-top: 15px;
}
:deep(.el-drawer__body) {
padding: 20px;
}
}
</style>
EngineerRepairOrder.vue
<script setup>
import { Picture, Check } from '@element-plus/icons-vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getRepairListService, uploadRepairImageService, updateRepairService } from '@/api/repair.js'
import { useTokenStore } from '@/stores/token'
import request from '@/utils/request'
// 获取token store
const tokenStore = useTokenStore()
// 工单状态选项
const workOrderStatusOptions = ref('')
//设备名称
const deviceName = ref('')
// 设备类型
const deviceType = ref('')
// 故障工单列表数据模型
const workOrders = ref([])
const pageNum = ref(1)
const pageSize = ref(5)
const total = ref(20)
// 分页大小变化
const onSizeChange = (size) => {
pageSize.value = size
getWorkOrders();
}
// 当前页变化
const onCurrentChange = (num) => {
pageNum.value = num
getWorkOrders();
}
// 获取工单列表
const getWorkOrders = async () => {
try {
let params = {
pageNum: pageNum.value,
pageSize: pageSize.value,
deviceType: deviceType.value ? deviceType.value : null,
deviceName: deviceName.value ? deviceName.value : null,
status: workOrderStatusOptions.value ? workOrderStatusOptions.value : null
}
console.log('请求参数:', params);
const result = await getRepairListService(params);
console.log('API响应:', result);
if (result && result.data) {
total.value = result.data.total;
workOrders.value = result.data.items.map(item => ({
id: item.planId,
name: item.name,
code: item.code,
type: item.type,
status: item.status,
description: item.description,
location: item.location,
reporter: item.reporterName,
contactPhone: item.contactPhone,
repairTime: item.repairTime,
faultCode: item.faultCode,
faultType: item.faultType,
reportType: item.reportType,
static2: item.static2,
image: item.image
}));
} else {
throw new Error('获取数据失败');
}
} catch (error) {
console.error('获取工单列表失败:', error);
ElMessage.error('获取工单列表失败: ' + (error.response?.data?.message || error.message));
}
};
getWorkOrders();
// 控制照片上传对话框显示
const photoDialogVisible = ref(false)
const currentOrder = ref(null)
// 上传的照片列表
const uploadPhotos = ref([])
// 表单数据模型
const formData = ref({
faultCode: '',
faultType: '',
reportType: '',
description: '',
image: ''
})
// 故障类型选项
const faultTypeOptions = [
{ label: '机械故障', value: '机械故障' },
{ label: '电气故障', value: '电气故障' },
{ label: '软件故障', value: '软件故障' },
{ label: '其他故障', value: '其他故障' }
]
// 报修方式选项
const reportTypeOptions = [
{ label: '自主报修', value: 1 },
{ label: '电话报修', value: 2 }
]
// 打开上传照片对话框
const openUploadDialog = (order) => {
currentOrder.value = order
uploadPhotos.value = []
// 初始化表单数据
formData.value = {
faultCode: order.faultCode || '',
faultType: order.faultType || '',
reportType: order.reportType || '',
description: order.description || '',
image: order.image || ''
}
photoDialogVisible.value = true
}
// 自定义上传方法
const customUpload = async (options) => {
const { file, onProgress, onSuccess, onError } = options
try {
console.log('开始上传文件:', file.name);
const uploadFormData = new FormData();
uploadFormData.append('file', file);
// 使用统一的request工具上传
const response = await uploadRepairImageService(uploadFormData);
console.log('上传响应:', response);
// 根据您的API响应格式调整
if (response && response.code === 1) {
const url = response.data;
if (!url) {
throw new Error('上传成功但未返回有效的URL');
}
// 将URL添加到uploadPhotos数组
if (!uploadPhotos.value.includes(url)) {
uploadPhotos.value.push(url);
}
// 更新表单中的图片字段
if (!formData.value) {
formData.value = { image: url };
} else if (!formData.value.image) {
formData.value.image = url;
} else {
formData.value.image += ',' + url;
}
// 调用成功回调
onSuccess({
data: url,
url: url,
status: 'success'
});
ElMessage.success('照片上传成功');
} else {
throw new Error(response?.message || '上传失败');
}
} catch (error) {
console.error('上传失败:', error);
ElMessage.error(`上传失败: ${error.message}`);
onError(error);
}
}
// 上传照片成功
const handleUploadSuccess = (response, file, fileList) => {
console.log('上传成功回调:', response);
// 这个函数现在只用于更新UI,实际的数据处理已经在customUpload中完成
}
// 删除照片
const handleRemove = (file) => {
const url = file.response?.data || file.url
const index = uploadPhotos.value.indexOf(url)
if (index !== -1) {
uploadPhotos.value.splice(index, 1)
formData.value.image = uploadPhotos.value.join(',')
}
}
// 预览照片
const handlePictureCardPreview = (file) => {
const url = file.response?.data || file.url
window.open(url, '_blank')
}
// 上传前检查
const beforeUpload = (file) => {
console.log('上传前检查文件:', file)
const isImage = file.type.startsWith('image/')
const isLt5M = file.size / 1024 / 1024 < 5
if (!isImage) {
ElMessage.error('只能上传图片文件!')
return false
}
if (!isLt5M) {
ElMessage.error('图片大小不能超过 5MB!')
return false
}
return true
}
// 提交维修信息
const submitRepairInfo = async () => {
try {
if (!formData.value.faultType) {
ElMessage.warning('请选择故障类型')
return
}
if (!formData.value.description) {
ElMessage.warning('请输入故障描述')
return
}
if (uploadPhotos.value.length === 0) {
ElMessage.warning('请至少上传一张照片')
return
}
const params = {
deviceName: currentOrder.value.name,
deviceCode: currentOrder.value.code,
faultCode: formData.value.faultCode,
faultType: formData.value.faultType,
faultDesc: formData.value.description,
reportType: formData.value.reportType,
image: formData.value.image,
location: currentOrder.value.location,
reporterName: currentOrder.value.reporter,
contactPhone: currentOrder.value.contactPhone
}
console.log('提交参数:', params)
// 使用submit接口
const response = await request.post('/api/repair/submit', params)
console.log('提交响应:', response)
if (!response) {
throw new Error('服务器未返回响应')
}
if (response.success) {
ElMessage.success(response.message || '维修信息提交成功')
photoDialogVisible.value = false
getWorkOrders()
} else {
throw new Error(response.message || '提交失败')
}
} catch (error) {
console.error('提交维修信息失败:', error)
ElMessage.error('提交维修信息失败: ' + (error.message || '未知错误'))
}
}
// 标记为已修好
const markAsRepaired = async (order) => {
try {
await ElMessageBox.confirm('确定该设备已修好吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const params = {
planId: order.id,
status: 2 // 标记为已完成
}
const result = await updateRepairService(params)
if (result.code === 200) {
ElMessage.success('状态更新成功')
getWorkOrders()
} else {
throw new Error(result.message || '更新失败')
}
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('操作失败: ' + error.message)
}
}
}
// 格式化日期时间
const formatDateTime = (dateTime) => {
if (!dateTime) return '-'
const date = new Date(dateTime)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(/\//g, '-')
}
</script>
<template>
<el-card class="page-container">
<template #header>
<div class="header">
<span>维修管理</span>
<div class="extra">
<el-button type="primary" @click="getWorkOrders">刷新</el-button>
</div>
</div>
</template>
<!-- 搜索表单 -->
<el-form inline>
<!-- <el-form-item label="设备类型:">
<el-select v-model="deviceType" placeholder="请选择设备类型" clearable>
<el-option label="客服" value="客服"></el-option>
<el-option label="机电" value="机电"></el-option>
<el-option label="电梯" value="电梯"></el-option>
<el-option label="消防" value="消防"></el-option>
</el-select>
</el-form-item> -->
<el-form-item label="设备名称:">
<el-input v-model="deviceName" placeholder="请输入设备名称" clearable />
</el-form-item>
<el-form-item label="工单状态:">
<el-select v-model="workOrderStatusOptions" placeholder="请选择状态" clearable>
<el-option label="待处理" value="0"></el-option>
<el-option label="处理中" value="1"></el-option>
<el-option label="已完成" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getWorkOrders">搜索</el-button>
</el-form-item>
</el-form>
<!-- 工单列表 -->
<el-table :data="workOrders" style="width: 100%" border>
<el-table-column prop="name" label="设备名称" width="150" />
<el-table-column prop="code" label="设备编号" width="150" />
<el-table-column prop="description" label="故障描述" width="200" />
<el-table-column prop="location" label="位置" width="120" />
<el-table-column prop="reporter" label="报修人" width="100" />
<el-table-column prop="repairTime" label="报修时间" width="180">
<template #default="{ row }">
{{ formatDateTime(row.repairTime) }}
</template>
</el-table-column>
<el-table-column prop="contactPhone" label="联系电话" width="120" />
<el-table-column label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'warning' : row.status === 1 ? 'primary' : 'success'">
{{ row.status === 0 ? '待处理' : row.status === 1 ? '处理中' : '已完成' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button :icon="Picture" type="primary" size="small" @click="openUploadDialog(row)"
:disabled="row.status === 2">
维修处理
</el-button>
<!-- <el-button
:icon="Check"
type="success"
size="small"
@click="markAsRepaired(row)"
:disabled="row.status === 2"
>
已修好
</el-button> -->
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无工单数据" />
</template>
</el-table>
<!-- 分页条 -->
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5, 10, 15]"
layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"
@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" />
<!-- 维修信息填写对话框 -->
<el-dialog v-model="photoDialogVisible" title="维修处理" width="60%">
<el-form :model="formData" label-width="100px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="设备名称">
<el-input v-model="currentOrder.name" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备编号">
<el-input v-model="currentOrder.code" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="故障编号" prop="faultCode">
<el-input v-model="formData.faultCode" placeholder="请输入故障编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="故障类型" prop="faultType" required>
<el-select v-model="formData.faultType" placeholder="请选择故障类型" style="width: 100%">
<el-option v-for="item in faultTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="报修方式" prop="reportType">
<el-select v-model="formData.reportType" placeholder="请选择报修方式" style="width: 100%">
<el-option v-for="item in reportTypeOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="位置">
<el-input v-model="currentOrder.location" disabled />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="故障描述" prop="description" required>
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入详细的故障描述" />
</el-form-item>
<el-form-item label="维修照片" required>
<el-upload :http-request="customUpload" list-type="picture-card" :on-success="handleUploadSuccess"
:on-remove="handleRemove" :on-preview="handlePictureCardPreview" :before-upload="beforeUpload"
:file-list="uploadPhotos" :limit="9" :auto-upload="true" :show-file-list="true" multiple accept="image/*">
<el-icon>
<Plus />
</el-icon>
<template #tip>
<div class="el-upload__tip">
请上传维修过程中的照片,支持jpg、png格式,单张图片不超过5MB,最多9张
</div>
</template>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="photoDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitRepairInfo">提交维修信息</el-button>
</template>
</el-dialog>
</el-card>
</template>
<style lang="scss" scoped>
.page-container {
min-height: 100%;
box-sizing: border-box;
.header {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.el-table {
margin-top: 20px;
}
.el-pagination {
margin-top: 20px;
justify-content: flex-end;
}
:deep(.el-upload-list__item-thumbnail) {
width: 100%;
height: 100%;
object-fit: cover;
}
.el-form-item {
margin-bottom: 20px;
}
.el-dialog {
.el-form {
padding: 0 20px;
}
}
.el-upload__tip {
color: #909399;
font-size: 12px;
margin-top: 7px;
line-height: 1.5;
}
</style>
EngineerCheck.vue
<script setup>
import {
Edit,
Delete,
Plus
} from '@element-plus/icons-vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getPlanListService, deletePlanService,addPlanService } from '@/api/Device.js'
import { useTokenStore } from '@/stores/token'
// 检测计划分类数据模型
const categories = ref([
{ id: 1, categoryName: '安全检测', categoryAlias: 'aqjc' },
{ id: 2, categoryName: '性能检测', categoryAlias: 'xnjc' },
{ id: 3, categoryName: '兼容性检测', categoryAlias: 'jyrjc' }
])
// 搜索条件
const deviceType = ref('') // 设备类型
const planStatus = ref('') // 检测状态
const searchKey = ref('') // 搜索关键词
// 检测计划列表数据模型
const plans = ref([
{
planCode: 1,
planName: 'APP性能检测计划',
deviceType: 2,
status: '进行中',
createTime: '2023-10-08 14:30:00',
description: '针对APP v2.3版本的性能测试'
},
{
planCode: 2,
planName: 'Web安全检测计划',
deviceType: 1,
status: '未开始',
createTime: '2023-10-09 09:15:00',
description: '年度Web安全漏洞扫描计划'
}
])
// 分页参数
const pageNum = ref(1)//当前页
const total = ref(20)//总条数
const pageSize = ref(5)//每页条数
//分页大小变化
const onSizeChange = (size) => {
pageSize.value = size
getDeviceLCheckList();
}
// 当前页变化
const onCurrentChange = (num) => {
pageNum.value = num
getDeviceLCheckList();
}
// 获取检测计划数据
const getDeviceLCheckList = async () => {
let params = {
pageNum: pageNum.value,
pageSize: pageSize.value,
planName: searchKey.value ? searchKey.value : null,
deviceType: deviceType.value ? deviceType.value : null,
status: planStatus.value ? planStatus.value : null,
searchKey: searchKey.value ? searchKey.value : null
}
let result = await getPlanListService(params)
// 渲染视图
total.value = result.data.total;
plans.value = result.data.items;
}
getDeviceLCheckList();
//删除计划
const deleteCheckPlan = async (planCode) => {
try {
await ElMessageBox.confirm('确定要删除该检测计划吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
// 调用删除接口
await deletePlanService(planCode)
ElMessage.success('删除成功')
getDeviceLCheckList();
} catch (error) {
if (error !== 'cancel') ElMessage.error('删除失败')
}
}
//控制抽屉是否显示
const visibleDrawer = ref(false)
//添加表单数据模型
const planForm = ref({
planCode: `PLAN-${Date.now().toString().slice(-6)}`, // 自动生成计划编号
planName: '',
deviceType: '', // 设备类型
startTime: null, // 开始时间
endTime: null, // 结束时间
frequency: 'month', // 默认检测频度为月
duration: 30, // 默认检测时长30分钟
minStayTime: 2, // 默认最短停留2分钟
status: 1, // 默认状态为启用(1)
description: '' // 可选描述
})
//添加计划
const addPlan = async () => {
try {
// 验证必填字段
if (!planForm.value.planName) {
ElMessage.warning('请填写计划名称');
return;
}
if (!planForm.value.deviceType) {
ElMessage.warning('请选择设备类型');
return;
}
if (!planForm.value.startTime) {
ElMessage.warning('请设置开始时间');
return;
}
if (!planForm.value.endTime) {
ElMessage.warning('请设置结束时间');
return;
}
// 准备提交数据
const submitData = {
planName: planForm.value.planName,
deviceType: planForm.value.deviceType,
startTime: planForm.value.startTime,
endTime: planForm.value.endTime,
frequency: planForm.value.frequency,
duration: planForm.value.duration,
minStayTime: planForm.value.minStayTime,
status: planForm.value.status,
description: planForm.value.description
};
console.log('提交的检测计划数据:', submitData);
// 调用接口,添加检测计划
const response = await addPlanService(submitData);
if (response && response.success) {
ElMessage.success('添加检测计划成功');
// 关闭抽屉
visibleDrawer.value = false;
// 刷新当前列表
getDeviceLCheckList();
// 重置表单
resetForm();
} else {
const errorMessage = response?.message || '添加失败';
ElMessage.error(errorMessage);
}
} catch (error) {
console.error('添加检测计划失败:', error);
// 处理不同类型的错误
let errorMessage = '添加检测计划失败';
if (error instanceof Error) {
errorMessage = error.message;
} else if (typeof error === 'string') {
errorMessage = error;
} else if (error?.response?.data?.message) {
errorMessage = error.response.data.message;
}
ElMessage.error(errorMessage);
}
};
//编辑计划
const editPlan = async (planCode) => {
try {
const result = await getPlanDetailService(planCode)
planModel.value = result.data
visibleDrawer.value = true
} catch (error) {
ElMessage.error('获取计划详情失败')
}
}
//更新计划
const updatePlan = async (planCode) => {
try {
planModel.value.status = clickState
await updatePlanService(planModel.value)
const result = await updatePlanService(planCode, planForm.value)
ElMessage.success(result.msg ? result.msg : '更新成功')
visibleDrawer.value = false
getDeviceLCheckList()
} catch (error) {
ElMessage.error('更新计划失败')
}
}
//提交表单-根据是否有id判断是新增还是编辑
const submitForm = (clickState) => {
if (planForm.value.id) {
// 编辑操作
updatePlan(planForm.value.id)
} else {
// 新增操作
addPlan(clickState)
}
}
const handleSubmit = async (isPublish) => {
planForm.value.status = isPublish ? '已发布' : '草稿'
try {
// 模拟保存逻辑(实际需调用API)
if (planForm.value.id) {
// await planService.update(planForm.value)
ElMessage.success('更新检测计划成功')
} else {
// await planService.create(planForm.value)
ElMessage.success('新增检测计划成功')
}
visibleDrawer.value = false
getDeviceLCheckList()
resetForm()
} catch (error) {
ElMessage.error('操作失败,请重试')
}
}
const resetForm = () => {
planForm.value = {
planCode: `PLAN-${Date.now().toString().slice(-6)}`, // 重新生成新的计划编号
planName: '',
deviceType: '',
startTime: null,
endTime: null,
frequency: 'month',
duration: 30,
minStayTime: 2,
status: 1,
description: ''
}
};
// 表单验证规则
const rules = {
planName: [
{ required: true, message: '请输入计划名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
deviceType: [
{ required: true, message: '请选择设备类型', trigger: 'change' }
],
startTime: [
{ required: true, message: '请选择开始时间', trigger: 'change' }
],
endTime: [
{ required: true, message: '请选择结束时间', trigger: 'change' }
],
frequency: [
{ required: true, message: '请选择检测频度', trigger: 'change' }
],
duration: [
{ required: true, message: '请输入检测时长', trigger: 'blur' }
]
};
// 监听抽屉关闭事件
const handleDrawerClose = () => {
resetForm()
}
</script>
<template>
<el-card class="page-container">
<template #header>
<div class="header">
<span>检测计划管理</span>
<div class="extra">
<el-button type="primary" @click="visibleDrawer = true">新建检测计划</el-button>
</div>
</div>
</template>
<!-- 搜索过滤 -->
<el-form inline class="mb-4">
<el-form-item label="设备类型:">
<el-select v-model="deviceType" placeholder="请选择检测类型" clearable style="width: 200px">
<el-option label="客服" value="客服"></el-option>
<el-option label="机电" value="机电"></el-option>
<el-option label="电梯" value="电梯"></el-option>
<el-option label="消防" value="消防"></el-option>
</el-select>
</el-form-item>
<el-form-item label="检测状态:">
<el-select v-model="planStatus" placeholder="请选择状态" clearable style="width: 150px">
<el-option label="未开始" value="未开始" />
<el-option label="进行中" value="进行中" />
<el-option label="已完成" value="已完成" />
</el-select>
</el-form-item>
<el-form-item label="关键词搜索:">
<el-input v-model="searchKey" placeholder="请输入计划名称" style="width: 300px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getDeviceLCheckList">搜索</el-button>
<!-- <el-button @click="resetSearch">重置</el-button> -->
</el-form-item>
</el-form>
<!-- 计划列表 -->
<el-table :data="plans" style="width: 100%">
<el-table-column label="计划编号" prop="planCode" width="200" />
<el-table-column label="计划名称" prop="planName" width="200" />
<el-table-column label="设备类型" prop="deviceType" width="120" />
<el-table-column label="状态" prop="status" width="100" />
<el-table-column label="创建时间" prop="createTime" width="180" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button :icon="Edit" circle plain type="primary" @click="editPlan(row)" />
<el-button :icon="Delete" circle plain type="danger" @click="deleteCheckPlan(row.planCode)"
:disabled="row.status === '已完成'" />
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无计划数据" />
</template>
</el-table>
<!-- 分页条 -->
<el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5, 10, 15]"
layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"
@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" />
<!-- 抽屉表单 -->
<!-- 抽屉表单 -->
<el-drawer v-model="visibleDrawer" :title="planForm.id ? '编辑检测计划' : '新建检测计划'" direction="rtl" size="50%"
@close="resetForm">
<el-form :model="planForm" label-width="120px" :rules="rules" ref="formRef">
<!-- 计划编号(只读) -->
<el-form-item label="计划编号">
<el-input v-model="planForm.planCode" disabled />
</el-form-item>
<el-form-item label="计划名称" prop="planName">
<el-input v-model="planForm.planName" placeholder="请输入检测计划名称" />
</el-form-item>
<!-- 修改为设备类型选择 -->
<el-form-item label="设备类型" prop="deviceType">
<el-select v-model="planForm.deviceType" placeholder="请选择设备类型">
<el-option label="消防" value="fire" />
<el-option label="电梯" value="elevator" />
<el-option label="机电" value="mechanical" />
<el-option label="客服" value="service" />
</el-select>
</el-form-item>
<!-- 时间范围选择 -->
<el-form-item label="时间范围" required>
<el-col :span="11">
<el-form-item prop="startTime">
<el-date-picker v-model="planForm.startTime" type="datetime" placeholder="开始时间"
value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">至</el-col>
<el-col :span="11">
<el-form-item prop="endTime">
<el-date-picker v-model="planForm.endTime" type="datetime" placeholder="结束时间"
value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
</el-form-item>
</el-col>
</el-form-item>
<!-- 检测频度 -->
<el-form-item label="检测频度" prop="frequency">
<el-select v-model="planForm.frequency" placeholder="请选择检测频度">
<el-option label="每日" value="day" />
<el-option label="每月" value="month" />
<el-option label="每季度" value="quarter" />
<el-option label="每半年" value="half-year" />
<el-option label="每年" value="year" />
</el-select>
</el-form-item>
<!-- 检测时长 -->
<el-form-item label="检测时长(分钟)" prop="duration">
<el-input-number v-model="planForm.duration" :min="1" :max="480" :step="5" />
</el-form-item>
<!-- 最短停留时间 -->
<el-form-item label="最短停留(分钟)" prop="minStayTime">
<el-input-number v-model="planForm.minStayTime" :min="1" :max="60" />
</el-form-item>
<!-- 修改为启用/停用状态 -->
<el-form-item label="计划状态">
<el-radio-group v-model="planForm.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="计划描述">
<el-input type="textarea" v-model="planForm.description" placeholder="请输入检测计划详细描述" rows="5" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">保存</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-drawer>
</el-card>
</template>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
padding: 20px;
box-sizing: border-box;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.el-table {
margin-top: 15px;
.is-done {
color: var(--el-color-success);
text-decoration: line-through;
}
}
}
</style>

浙公网安备 33010602011771号