add 巡检定时任务
parent
441c6c59e0
commit
834bb4f53b
|
|
@ -24,6 +24,7 @@
|
||||||
"benz-amr-recorder": "^1.1.5",
|
"benz-amr-recorder": "^1.1.5",
|
||||||
"bpmn-js-token-simulation": "^0.36.0",
|
"bpmn-js-token-simulation": "^0.36.0",
|
||||||
"camunda-bpmn-moddle": "^7.0.1",
|
"camunda-bpmn-moddle": "^7.0.1",
|
||||||
|
"cronstrue": "^3.9.0",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
|
@ -8440,6 +8441,15 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/cronstrue": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-T3S35zmD0Ai2B4ko6+mEM+k9C6tipe2nB9RLiGT6QL2Wn0Vsn2cCZAC8Oeuf4CaE00GZWVdpYitbpWCNlIWqdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"cronstrue": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cropperjs": {
|
"node_modules/cropperjs": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://mirrors.huaweicloud.com/repository/npm/cropperjs/-/cropperjs-1.6.2.tgz",
|
"resolved": "https://mirrors.huaweicloud.com/repository/npm/cropperjs/-/cropperjs-1.6.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
"benz-amr-recorder": "^1.1.5",
|
"benz-amr-recorder": "^1.1.5",
|
||||||
"bpmn-js-token-simulation": "^0.36.0",
|
"bpmn-js-token-simulation": "^0.36.0",
|
||||||
"camunda-bpmn-moddle": "^7.0.1",
|
"camunda-bpmn-moddle": "^7.0.1",
|
||||||
|
"cronstrue": "^3.9.0",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import request from '@/config/axios'
|
||||||
export interface ProjectInspectionRecordVO {
|
export interface ProjectInspectionRecordVO {
|
||||||
id: number // 主键ID
|
id: number // 主键ID
|
||||||
projectId: number // 项目ID
|
projectId: number // 项目ID
|
||||||
inspectionDate: Date // 巡检日期
|
inspectionDate: Date // 巡检日期时间
|
||||||
inspector: number // 巡检人
|
inspector: number // 巡检人
|
||||||
overallStatus: number // 总体状态
|
overallStatus: number // 总体状态
|
||||||
summary: string // 巡检总结
|
summary: string // 巡检总结
|
||||||
remark: string // 备注
|
remark: string // 备注
|
||||||
status: number // 状态
|
status: number // 状态
|
||||||
|
configType: number[] // 配置类型列表
|
||||||
|
updateTime: Date // 更新时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询巡检记录分页
|
// 查询巡检记录分页
|
||||||
|
|
@ -17,6 +19,11 @@ export const getProjectInspectionRecordPage = async (params: any) => {
|
||||||
return await request.get({ url: `/crm/project-inspection-record/page`, params })
|
return await request.get({ url: `/crm/project-inspection-record/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据更新时间查询巡检记录分页
|
||||||
|
export const getProjectInspectionRecordPageByUpdateTime = async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/project-inspection-record/page-by-update-time`, params })
|
||||||
|
}
|
||||||
|
|
||||||
// 查询巡检记录详情
|
// 查询巡检记录详情
|
||||||
export const getProjectInspectionRecord = async (id: number) => {
|
export const getProjectInspectionRecord = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/project-inspection-record/get?id=` + id })
|
return await request.get({ url: `/crm/project-inspection-record/get?id=` + id })
|
||||||
|
|
|
||||||
|
|
@ -512,12 +512,7 @@ const inputChange = () => {
|
||||||
<el-input v-model="defaultValue" class="input-with-select" v-bind="$attrs" @input="inputChange">
|
<el-input v-model="defaultValue" class="input-with-select" v-bind="$attrs" @input="inputChange">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-select v-model="select" placeholder="生成器" style="width: 115px">
|
<el-select v-model="select" placeholder="生成器" style="width: 115px">
|
||||||
<el-option label="每分钟" value="0 * * * * ?" />
|
<el-option label="每天" value="0 0 0 * * ?" />
|
||||||
<el-option label="每小时" value="0 0 * * * ?" />
|
|
||||||
<el-option label="每天零点" value="0 0 0 * * ?" />
|
|
||||||
<el-option label="每月一号零点" value="0 0 0 1 * ?" />
|
|
||||||
<el-option label="每月最后一天零点" value="0 0 0 L * ?" />
|
|
||||||
<el-option label="每周星期日零点" value="0 0 0 ? * 1" />
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(item, index) in shortcuts"
|
v-for="(item, index) in shortcuts"
|
||||||
:key="index"
|
:key="index"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Cron 表达式翻译成中文描述
|
||||||
|
* 使用 cronstrue 库进行转换
|
||||||
|
*/
|
||||||
|
import cronstrue from 'cronstrue/i18n'
|
||||||
|
|
||||||
|
export function cronToChinese(cron: string): string {
|
||||||
|
if (!cron || typeof cron !== "string") {
|
||||||
|
return "无效的 Cron 表达式";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 cronstrue 库转换为中文
|
||||||
|
return cronstrue.toString(cron.trim(), {
|
||||||
|
locale: 'zh_CN', // 设置语言为简体中文
|
||||||
|
use24HourTimeFormat: true // 使用24小时制
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return "Cron 表达式格式错误";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
v-model="formData.frequency"
|
v-model="formData.frequency"
|
||||||
placeholder="请输入或选择Cron表达式"
|
placeholder="请输入或选择Cron表达式"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
:shortcuts="cronShortcuts"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
@ -178,6 +179,14 @@ const formRules = reactive({
|
||||||
})
|
})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** Cron 表达式快捷选项 */
|
||||||
|
const cronShortcuts = [
|
||||||
|
{ text: '每周一', value: '0 0 0 ? * 2' },
|
||||||
|
{ text: '每周六', value: '0 0 0 ? * 7' },
|
||||||
|
{ text: '每月', value: '0 0 0 1 * ?' },
|
||||||
|
{ text: '每季度', value: '0 0 0 1 1/3 ?' }
|
||||||
|
]
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (type: string, id?: number) => {
|
const open = async (type: string, id?: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,22 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="配置类型" prop="configType">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.configType"
|
||||||
|
placeholder="请选择配置类型"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROJECT_INSPECTION_CHECKPOINT)"
|
||||||
|
:key="String(dict.value)"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="负责人" prop="responsiblePersonId">
|
<el-form-item label="负责人" prop="responsiblePersonId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.responsiblePersonId"
|
v-model="queryParams.responsiblePersonId"
|
||||||
|
|
@ -94,9 +110,9 @@
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_CHECKPOINT" :value="scope.row.configType" />
|
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_CHECKPOINT" :value="scope.row.configType" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="巡检频率" align="center" prop="frequency" min-width="150">
|
<el-table-column label="巡检频率" align="center" prop="frequency" min-width="350">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ row.frequency || '-' }}
|
{{ row.frequency ? cronToChinese(row.frequency) : '-' }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="管理方" align="center" prop="managementParty" min-width="120">
|
<el-table-column label="管理方" align="center" prop="managementParty" min-width="120">
|
||||||
|
|
@ -161,6 +177,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
|
import { cronToChinese } from '@/utils/cron'
|
||||||
import * as ProjectInspectionConfigApi from '@/api/crm/project/inspection'
|
import * as ProjectInspectionConfigApi from '@/api/crm/project/inspection'
|
||||||
import * as ProjectApi from '@/api/crm/project'
|
import * as ProjectApi from '@/api/crm/project'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
|
|
@ -180,6 +197,7 @@ const queryParams = reactive({
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
projectId: undefined,
|
projectId: undefined,
|
||||||
responsiblePersonId: undefined,
|
responsiblePersonId: undefined,
|
||||||
|
configType: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROJECT_INSPECTION_DATA_TYPE)"
|
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROJECT_INSPECTION_CHECKPOINT)"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@
|
||||||
<el-form-item label="巡检日期" prop="inspectionDate">
|
<el-form-item label="巡检日期" prop="inspectionDate">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="formData.inspectionDate"
|
v-model="formData.inspectionDate"
|
||||||
type="date"
|
type="datetime"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="x"
|
||||||
placeholder="选择巡检日期"
|
placeholder="选择巡检日期时间"
|
||||||
class="!w-full"
|
class="!w-full"
|
||||||
|
editable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
@ -71,7 +72,7 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20">
|
<!-- <el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="结论" prop="overallStatus">
|
<el-form-item label="结论" prop="overallStatus">
|
||||||
<el-radio-group v-model="formData.overallStatus">
|
<el-radio-group v-model="formData.overallStatus">
|
||||||
|
|
@ -85,20 +86,20 @@
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row> -->
|
||||||
<el-row :gutter="20">
|
<!-- <el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="巡检总结" prop="summary">
|
<el-form-item label="巡检内容" prop="summary">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.summary"
|
v-model="formData.summary"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:rows="3"
|
:rows="3"
|
||||||
placeholder="请输入巡检总结"
|
placeholder="请输入巡检内容"
|
||||||
resize="both"
|
resize="both"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row> -->
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
|
|
@ -121,6 +122,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import * as ProjectInspectionRecordApi from '@/api/crm/project/inspection/record'
|
import * as ProjectInspectionRecordApi from '@/api/crm/project/inspection/record'
|
||||||
import * as ProjectApi from '@/api/crm/project'
|
import * as ProjectApi from '@/api/crm/project'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
|
|
@ -168,7 +170,8 @@ const open = async (type: string, id?: number) => {
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
formData.value = await ProjectInspectionRecordApi.getProjectInspectionRecord(id)
|
const data = await ProjectInspectionRecordApi.getProjectInspectionRecord(id)
|
||||||
|
formData.value = data
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +187,13 @@ const submitForm = async () => {
|
||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = formData.value as unknown as ProjectInspectionRecordApi.ProjectInspectionRecordVO
|
// 确保 status 和 overallStatus 字段正确传递
|
||||||
|
const data = {
|
||||||
|
...formData.value,
|
||||||
|
status: formData.value.status,
|
||||||
|
overallStatus: formData.value.overallStatus
|
||||||
|
} as unknown as ProjectInspectionRecordApi.ProjectInspectionRecordVO
|
||||||
|
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
await ProjectInspectionRecordApi.createProjectInspectionRecord(data)
|
await ProjectInspectionRecordApi.createProjectInspectionRecord(data)
|
||||||
message.success(t('common.createSuccess'))
|
message.success(t('common.createSuccess'))
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,22 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 巡检结论 -->
|
||||||
|
<el-divider>
|
||||||
|
<span style="font-weight: bold; font-size: 16px;">巡检结论</span>
|
||||||
|
</el-divider>
|
||||||
|
<el-form-item label="巡检结论" prop="overallStatus" :rules="[{ required: true, message: '请选择巡检结论', trigger: 'change' }]">
|
||||||
|
<el-radio-group v-model="formData.overallStatus">
|
||||||
|
<el-radio
|
||||||
|
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_OVERALL_STATUS)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||||
|
|
@ -120,6 +136,7 @@ import * as UserApi from '@/api/system/user'
|
||||||
import UploadImg from '@/components/UploadFile/src/UploadImg.vue'
|
import UploadImg from '@/components/UploadFile/src/UploadImg.vue'
|
||||||
import UploadFile from '@/components/UploadFile/src/UploadFile.vue'
|
import UploadFile from '@/components/UploadFile/src/UploadFile.vue'
|
||||||
import { getDictLabel, getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
import { getDictLabel, getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
||||||
/** 巡检记录填报 表单 */
|
/** 巡检记录填报 表单 */
|
||||||
|
|
@ -241,14 +258,18 @@ const getFieldRules = (item: ProjectInspectionDataApi.ProjectInspectionDataVO) =
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (
|
const open = async (record: ProjectInspectionRecordApi.ProjectInspectionRecordVO) => {
|
||||||
record: ProjectInspectionRecordApi.ProjectInspectionRecordVO,
|
|
||||||
dataList: ProjectInspectionDataApi.ProjectInspectionDataVO[]
|
|
||||||
) => {
|
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 检查记录中是否有 configType
|
||||||
|
if (!record.configType || !Array.isArray(record.configType) || record.configType.length === 0) {
|
||||||
|
message.warning('该巡检记录没有配置巡检类型')
|
||||||
|
dialogVisible.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 加载项目列表和用户列表
|
// 加载项目列表和用户列表
|
||||||
projectList.value = await ProjectApi.getProjectSimpleList()
|
projectList.value = await ProjectApi.getProjectSimpleList()
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
userList.value = await UserApi.getSimpleUserList()
|
||||||
|
|
@ -262,14 +283,23 @@ const open = async (
|
||||||
// 巡检人显示当前登录用户名称
|
// 巡检人显示当前登录用户名称
|
||||||
inspectorName.value = userStore.user.nickname || '当前用户'
|
inspectorName.value = userStore.user.nickname || '当前用户'
|
||||||
|
|
||||||
// 格式化巡检日期
|
// 格式化巡检日期时间
|
||||||
if (record.inspectionDate) {
|
if (record.inspectionDate) {
|
||||||
const date = new Date(record.inspectionDate)
|
const date = new Date(record.inspectionDate)
|
||||||
inspectionDate.value = date.toISOString().split('T')[0]
|
inspectionDate.value = formatDate(date, 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 record.configType 查询巡检内容
|
||||||
|
const inspectionDataListResult = await ProjectInspectionDataApi.getProjectInspectionDataList(record.configType)
|
||||||
|
|
||||||
|
if (!inspectionDataListResult || inspectionDataListResult.length === 0) {
|
||||||
|
message.warning('没有找到对应的巡检内容')
|
||||||
|
dialogVisible.value = false
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置巡检内容列表(不需要在这里排序,分组时会排序)
|
// 设置巡检内容列表(不需要在这里排序,分组时会排序)
|
||||||
inspectionDataList.value = dataList || []
|
inspectionDataList.value = inspectionDataListResult
|
||||||
|
|
||||||
// 初始化表单数据和验证规则
|
// 初始化表单数据和验证规则
|
||||||
formData.value = {}
|
formData.value = {}
|
||||||
|
|
@ -287,6 +317,10 @@ const open = async (
|
||||||
// 设置验证规则
|
// 设置验证规则
|
||||||
formRules.value[fieldKey] = getFieldRules(item)
|
formRules.value[fieldKey] = getFieldRules(item)
|
||||||
})
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('打开填报表单失败:', error)
|
||||||
|
message.error('获取巡检内容失败,请稍后重试')
|
||||||
|
dialogVisible.value = false
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -308,12 +342,17 @@ const submitForm = async () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建分组数据
|
// 构建分组数据(用于后端处理,使用ID作为键)
|
||||||
const summaryData: Record<number, Record<number, any>> = {}
|
const summaryData: Record<number, Record<number, any>> = {}
|
||||||
|
|
||||||
|
// 构建易读的总结数据(用于人类阅读,使用名称作为键,包含字段类型信息)
|
||||||
|
const readableSummaryData: Record<string, Array<{ name: string; value: any; type: number }>> = {}
|
||||||
|
|
||||||
// 按配置类型分组收集表单数据
|
// 按配置类型分组收集表单数据
|
||||||
groupedInspectionData.value.forEach((group) => {
|
groupedInspectionData.value.forEach((group) => {
|
||||||
const groupData: Record<number, any> = {}
|
const groupData: Record<number, any> = {}
|
||||||
|
const readableGroupData: Array<{ name: string; value: any; type: number }> = []
|
||||||
|
const configTypeName = getConfigTypeName(group.configType)
|
||||||
|
|
||||||
group.items.forEach((item) => {
|
group.items.forEach((item) => {
|
||||||
const fieldKey = `data_${item.id}`
|
const fieldKey = `data_${item.id}`
|
||||||
|
|
@ -324,30 +363,61 @@ const submitForm = async () => {
|
||||||
// 多选类型:只有当数组不为空时才提交
|
// 多选类型:只有当数组不为空时才提交
|
||||||
if (value.length > 0) {
|
if (value.length > 0) {
|
||||||
groupData[item.id] = value.join(',')
|
groupData[item.id] = value.join(',')
|
||||||
|
readableGroupData.push({
|
||||||
|
name: item.name,
|
||||||
|
value: value.join('、'), // 使用中文顿号分隔,更易读
|
||||||
|
type: item.type
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if (value !== undefined && value !== null && value !== '') {
|
} else if (value !== undefined && value !== null && value !== '') {
|
||||||
// 其他类型:有值才提交
|
// 其他类型:有值才提交
|
||||||
groupData[item.id] = value
|
groupData[item.id] = value
|
||||||
|
readableGroupData.push({
|
||||||
|
name: item.name,
|
||||||
|
value: value,
|
||||||
|
type: item.type
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 只有当分组有数据时才添加到 summaryData
|
// 只有当分组有数据时才添加
|
||||||
if (Object.keys(groupData).length > 0) {
|
if (Object.keys(groupData).length > 0) {
|
||||||
summaryData[group.configType] = groupData
|
summaryData[group.configType] = groupData
|
||||||
|
readableSummaryData[configTypeName] = readableGroupData
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 构建JSON格式的总结数据(结构化数据,便于前端渲染)
|
||||||
|
const summaryJson = JSON.stringify(readableSummaryData)
|
||||||
|
|
||||||
|
// 判断填报日期是否大于巡检日期
|
||||||
|
const currentDate = new Date()
|
||||||
|
currentDate.setHours(0, 0, 0, 0) // 只比较日期,忽略时间
|
||||||
|
|
||||||
|
let inspectionDateObj: Date | null = null
|
||||||
|
if (originalRecord.value.inspectionDate) {
|
||||||
|
inspectionDateObj = new Date(originalRecord.value.inspectionDate)
|
||||||
|
inspectionDateObj.setHours(0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果填报日期大于巡检日期,状态改为3(逾期填报),否则为2(已填报)
|
||||||
|
let recordStatus = 2
|
||||||
|
if (inspectionDateObj && currentDate > inspectionDateObj) {
|
||||||
|
recordStatus = 3
|
||||||
|
}
|
||||||
|
|
||||||
// 构建更新数据
|
// 构建更新数据
|
||||||
// 注意:status 在 API 定义中是 boolean,但实际后端使用数字类型(通过字典可以确认)
|
// 注意:status 在 API 定义中是 boolean,但实际后端使用数字类型(通过字典可以确认)
|
||||||
|
// summary 字段存储易读的文本格式,同时保留原始数据用于后端处理(如果需要的话)
|
||||||
const updateData: any = {
|
const updateData: any = {
|
||||||
id: recordId.value,
|
id: recordId.value,
|
||||||
projectId: originalRecord.value.projectId,
|
projectId: originalRecord.value.projectId,
|
||||||
inspectionDate: originalRecord.value.inspectionDate,
|
inspectionDate: originalRecord.value.inspectionDate,
|
||||||
inspector: originalRecord.value.inspector,
|
inspector: originalRecord.value.inspector,
|
||||||
overallStatus: originalRecord.value.overallStatus,
|
overallStatus: formData.value.overallStatus, // 使用表单中选择的巡检结论
|
||||||
summary: JSON.stringify(summaryData), // 将分组数据转换为 JSON 字符串存入 summary 字段
|
summary: summaryJson || JSON.stringify(summaryData), // 使用JSON格式存储结构化数据
|
||||||
remark: originalRecord.value.remark || '',
|
remark: originalRecord.value.remark || '',
|
||||||
status: 2 // 设置状态为 2(已填报)
|
status: recordStatus // 根据填报日期和巡检日期比较结果设置状态(recordStatus的值通过status字段传递)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用更新 API
|
// 调用更新 API
|
||||||
|
|
|
||||||
|
|
@ -68,21 +68,6 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
|
||||||
<el-select
|
|
||||||
v-model="queryParams.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
clearable
|
|
||||||
class="!w-240px"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_STATUS)"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||||
|
|
@ -107,8 +92,145 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 标签页 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
|
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||||
|
<!-- 时间轴页面 -->
|
||||||
|
<el-tab-pane label="时间轴" name="timeline">
|
||||||
|
<div v-loading="loading" class="timeline-container">
|
||||||
|
<el-timeline v-if="list.length > 0">
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
:timestamp="formatDateForTimeline(item.updateTime)"
|
||||||
|
placement="top"
|
||||||
|
:color="getTimelineColor(item.status)"
|
||||||
|
>
|
||||||
|
<el-card
|
||||||
|
:class="['timeline-card', getCardStatusClass(item.overallStatus)]"
|
||||||
|
shadow="hover"
|
||||||
|
>
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title">
|
||||||
|
<span class="project-name">{{ getProjectName(item.projectId) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-info">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">巡检人:</span>
|
||||||
|
<span class="info-value">{{ getUserName(item.inspector) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.inspectionDate" class="info-item">
|
||||||
|
<span class="info-label">计划完成时间:</span>
|
||||||
|
<span class="info-value">{{ formatDateForDisplay(item.inspectionDate) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<!-- <el-button
|
||||||
|
v-if="canShowUploadButton(item)"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
size="small"
|
||||||
|
@click="handleUpload(item)"
|
||||||
|
>
|
||||||
|
填报
|
||||||
|
</el-button> -->
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="openForm('update', item.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
@click="handleDelete(item.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:delete']"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.summary" class="card-summary">
|
||||||
|
<span class="summary-label">巡检内容:</span>
|
||||||
|
<div class="summary-content-wrapper">
|
||||||
|
<template v-if="getParsedSummary(item.summary)">
|
||||||
|
<div
|
||||||
|
v-for="(groupData, configTypeName) in getParsedSummary(item.summary)"
|
||||||
|
:key="configTypeName"
|
||||||
|
class="summary-group"
|
||||||
|
>
|
||||||
|
<div class="summary-group-title">{{ configTypeName }}</div>
|
||||||
|
<div
|
||||||
|
v-for="field in groupData"
|
||||||
|
:key="`${configTypeName}-${field.name}`"
|
||||||
|
class="summary-field"
|
||||||
|
>
|
||||||
|
<span class="field-name">{{ field.name }}:</span>
|
||||||
|
<span v-if="field.type === 6" class="field-value-image">
|
||||||
|
<img
|
||||||
|
:src="getFileUrl(field.value)"
|
||||||
|
@click="previewImage(field.value)"
|
||||||
|
class="inspection-thumbnail"
|
||||||
|
alt="巡检图片"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<a
|
||||||
|
v-else-if="field.type === 7"
|
||||||
|
:href="getFileUrl(field.value)"
|
||||||
|
target="_blank"
|
||||||
|
class="field-value-file"
|
||||||
|
>
|
||||||
|
📎 {{ field.value.split('/').pop() }}
|
||||||
|
</a>
|
||||||
|
<span v-else class="field-value-text">{{ field.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-else class="summary-text">{{ item.summary }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.status || item.overallStatus" class="card-status">
|
||||||
|
<span class="status-label">状态:</span>
|
||||||
|
<dict-tag
|
||||||
|
v-if="item.status"
|
||||||
|
:type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_STATUS"
|
||||||
|
:value="item.status"
|
||||||
|
/>
|
||||||
|
<span v-if="item.overallStatus" class="overall-status-label" style="margin-left: 16px;">巡检结论:</span>
|
||||||
|
<dict-tag
|
||||||
|
v-if="item.overallStatus"
|
||||||
|
:type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_OVERALL_STATUS"
|
||||||
|
:value="item.overallStatus"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.remark" class="card-remark">
|
||||||
|
<span class="remark-label">备注:</span>
|
||||||
|
<span class="remark-content">{{ item.remark }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 已巡检表格页面 -->
|
||||||
|
<el-tab-pane label="已巡检" name="table">
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
<el-table-column label="项目名称" align="center" prop="projectId">
|
<el-table-column label="项目名称" align="center" prop="projectId">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|
@ -119,8 +241,143 @@
|
||||||
label="巡检日期"
|
label="巡检日期"
|
||||||
align="center"
|
align="center"
|
||||||
prop="inspectionDate"
|
prop="inspectionDate"
|
||||||
:formatter="dateFormatter2"
|
:formatter="dateFormatter"
|
||||||
width="120"
|
width="180"
|
||||||
|
/>
|
||||||
|
<el-table-column label="巡检人" align="center" prop="inspector">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getUserName(scope.row.inspector) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_STATUS" :value="scope.row.status" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="结论" align="center" prop="overallStatus">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_OVERALL_STATUS" :value="scope.row.overallStatus" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" min-width="120px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="canShowUploadButton(scope.row)"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleUpload(scope.row)"
|
||||||
|
>
|
||||||
|
填报
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:delete']"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 逾期巡检表格页面 -->
|
||||||
|
<el-tab-pane label="逾期巡检" name="tableOverdue">
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="项目名称" align="center" prop="projectId">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getProjectName(scope.row.projectId) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="巡检日期"
|
||||||
|
align="center"
|
||||||
|
prop="inspectionDate"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180"
|
||||||
|
/>
|
||||||
|
<el-table-column label="巡检人" align="center" prop="inspector">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getUserName(scope.row.inspector) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_STATUS" :value="scope.row.status" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="结论" align="center" prop="overallStatus">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_PROJECT_INSPECTION_RECORD_OVERALL_STATUS" :value="scope.row.overallStatus" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" min-width="120px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="canShowUploadButton(scope.row)"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleUpload(scope.row)"
|
||||||
|
>
|
||||||
|
填报
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['crm:project-inspection-record:delete']"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 未巡检表格页面 -->
|
||||||
|
<el-tab-pane label="未巡检" name="tableUninspected">
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="项目名称" align="center" prop="projectId">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getProjectName(scope.row.projectId) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="巡检日期"
|
||||||
|
align="center"
|
||||||
|
prop="inspectionDate"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="巡检人" align="center" prop="inspector">
|
<el-table-column label="巡检人" align="center" prop="inspector">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|
@ -174,6 +431,8 @@
|
||||||
v-model:limit="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
|
@ -184,11 +443,9 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
import { dateFormatter2 } from '@/utils/formatTime'
|
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
import * as ProjectInspectionRecordApi from '@/api/crm/project/inspection/record'
|
import * as ProjectInspectionRecordApi from '@/api/crm/project/inspection/record'
|
||||||
import * as ProjectInspectionConfigApi from '@/api/crm/project/inspection'
|
|
||||||
import * as ProjectInspectionDataApi from '@/api/crm/project/inspection/data'
|
|
||||||
import * as ProjectApi from '@/api/crm/project'
|
import * as ProjectApi from '@/api/crm/project'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
|
@ -211,7 +468,6 @@ const queryParams = reactive({
|
||||||
inspectionDate: [],
|
inspectionDate: [],
|
||||||
inspector: undefined,
|
inspector: undefined,
|
||||||
overallStatus: undefined,
|
overallStatus: undefined,
|
||||||
status: undefined,
|
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
|
@ -219,14 +475,99 @@ const projectList = ref<ProjectApi.ProjectVO[]>([]) // 项目列表
|
||||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||||
const userStore = useUserStore() // 用户store
|
const userStore = useUserStore() // 用户store
|
||||||
const uploadFormRef = ref() // 填报表单Ref
|
const uploadFormRef = ref() // 填报表单Ref
|
||||||
|
const activeTab = ref('timeline') // 当前激活的标签页
|
||||||
|
|
||||||
|
|
||||||
|
/** 格式化日期显示 */
|
||||||
|
const formatDateForTimeline = (date: string | Date | undefined) => {
|
||||||
|
if (!date) return ''
|
||||||
|
return formatDate(new Date(date), 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 格式化日期显示(用于预计巡检日期等) */
|
||||||
|
const formatDateForDisplay = (date: string | Date | undefined) => {
|
||||||
|
if (!date) return ''
|
||||||
|
return formatDate(new Date(date), 'YYYY-MM-DD HH:mm:ss')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据状态获取时间线颜色 */
|
||||||
|
const getTimelineColor = (status: number | undefined) => {
|
||||||
|
if (status === undefined) return '#409EFF'
|
||||||
|
// 根据状态返回不同颜色
|
||||||
|
// 可以根据实际的状态值调整颜色
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return '#909399' // 灰色 - 未开始
|
||||||
|
case 1:
|
||||||
|
return '#E6A23C' // 橙色 - 进行中
|
||||||
|
case 2:
|
||||||
|
return '#67C23A' // 绿色 - 已完成
|
||||||
|
default:
|
||||||
|
return '#409EFF' // 蓝色 - 默认
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 标签页切换处理 */
|
||||||
|
const handleTabChange = () => {
|
||||||
|
// 切换标签页时,重置分页并重新加载数据
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await ProjectInspectionRecordApi.getProjectInspectionRecordPage(queryParams)
|
// 根据当前标签页设置查询条件
|
||||||
|
const params: any = { ...queryParams }
|
||||||
|
|
||||||
|
if (activeTab.value === 'timeline'){
|
||||||
|
// 时间轴标签页使用按更新时间分页的接口
|
||||||
|
params.status = [2, 3]
|
||||||
|
const data = await ProjectInspectionRecordApi.getProjectInspectionRecordPageByUpdateTime(params)
|
||||||
list.value = data.list
|
list.value = data.list
|
||||||
total.value = data.total
|
total.value = data.total
|
||||||
|
return
|
||||||
|
} else if (activeTab.value === 'table') {
|
||||||
|
// 已巡检:status = 2(已填报)
|
||||||
|
params.status = 2
|
||||||
|
} else if (activeTab.value === 'tableOverdue') {
|
||||||
|
// 逾期巡检:status = 3(逾期填报)
|
||||||
|
params.status = 3
|
||||||
|
} else if (activeTab.value === 'tableUninspected') {
|
||||||
|
// 未巡检:status != 2 且 status != 3(未填报)
|
||||||
|
// 尝试传递 statusNotIn 参数给后端,让后端过滤
|
||||||
|
// 如果后端不支持,可以尝试传递可能的未巡检状态值 [0, 1],或者移除该参数让后端返回所有数据
|
||||||
|
// 注意:如果后端不支持排除查询,需要后端添加支持,否则分页总数会不准确
|
||||||
|
params.statusNotIn = [2, 3]
|
||||||
|
// 或者可以尝试:params.status = [0, 1] (如果知道未巡检的状态值)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await ProjectInspectionRecordApi.getProjectInspectionRecordPage(params)
|
||||||
|
|
||||||
|
// 如果后端支持 statusNotIn 或 status 数组,直接使用后端返回的数据
|
||||||
|
if (activeTab.value === 'tableUninspected') {
|
||||||
|
// 前端二次过滤确保数据正确(防止后端不支持 statusNotIn)
|
||||||
|
const filteredList = data.list.filter(item => item.status !== 2 && item.status !== 3)
|
||||||
|
list.value = filteredList
|
||||||
|
|
||||||
|
// 检查后端是否已经过滤了数据
|
||||||
|
// 如果过滤后列表长度等于原列表长度,说明后端已经过滤,使用后端返回的 total
|
||||||
|
// 否则说明后端未过滤,需要重新计算 total(这种情况需要后端支持,否则无法准确计算)
|
||||||
|
if (filteredList.length === data.list.length) {
|
||||||
|
// 后端已经过滤,使用后端返回的 total
|
||||||
|
total.value = data.total
|
||||||
|
} else {
|
||||||
|
// 后端未过滤,说明后端不支持 statusNotIn
|
||||||
|
// 这种情况下无法准确计算 total,需要后端支持或者获取所有数据
|
||||||
|
// 临时方案:使用原 total 减去当前页过滤掉的数量,但这不准确
|
||||||
|
// 更好的方案是让后端支持 statusNotIn 参数
|
||||||
|
total.value = Math.max(0, data.total - (data.list.length - filteredList.length))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -319,7 +660,7 @@ const isAdmin = () => {
|
||||||
/** 判断是否可以看到填报按钮 */
|
/** 判断是否可以看到填报按钮 */
|
||||||
const canShowUploadButton = (row: ProjectInspectionRecordApi.ProjectInspectionRecordVO) => {
|
const canShowUploadButton = (row: ProjectInspectionRecordApi.ProjectInspectionRecordVO) => {
|
||||||
// 1. 只有未填报(status != 2)才显示
|
// 1. 只有未填报(status != 2)才显示
|
||||||
if (row.status === 2) {
|
if (row.status === 2 || row.status === 3) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,51 +683,82 @@ const canShowUploadButton = (row: ProjectInspectionRecordApi.ProjectInspectionRe
|
||||||
|
|
||||||
/** 填报按钮操作 */
|
/** 填报按钮操作 */
|
||||||
const handleUpload = async (row: ProjectInspectionRecordApi.ProjectInspectionRecordVO) => {
|
const handleUpload = async (row: ProjectInspectionRecordApi.ProjectInspectionRecordVO) => {
|
||||||
|
// 直接打开填报表单,组件内部会根据 row.configType 查询表单内容
|
||||||
|
uploadFormRef.value.open(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 解析巡检内容数据 */
|
||||||
|
const parseSummaryData = (summary: string) => {
|
||||||
|
if (!summary) return null
|
||||||
try {
|
try {
|
||||||
// 根据项目ID和巡检人ID查询配置,获取configType列表
|
// 先尝试直接解析
|
||||||
const inspectorIdNum = typeof row.inspector === 'string' ? Number(row.inspector) : row.inspector
|
let data: any = summary
|
||||||
const configParams = {
|
if (typeof summary === 'string') {
|
||||||
projectId: row.projectId,
|
// 尝试解析JSON(可能已经是JSON字符串)
|
||||||
responsiblePersonId: inspectorIdNum,
|
try {
|
||||||
status: 0, // 只查询启用状态的配置
|
data = JSON.parse(summary)
|
||||||
pageNo: 1,
|
// 如果解析后还是字符串,说明是双重编码,再解析一次
|
||||||
pageSize: 99, // 获取所有配置
|
if (typeof data === 'string') {
|
||||||
|
data = JSON.parse(data)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 如果解析失败,说明不是JSON格式,返回null
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
const configData = await ProjectInspectionConfigApi.getProjectInspectionConfigPage(configParams)
|
|
||||||
|
|
||||||
if (!configData.list || configData.list.length === 0) {
|
|
||||||
message.warning('该巡检人没有配置巡检项目')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取所有configType(去重)
|
// 检查是否是新的JSON格式(包含type字段的数组)
|
||||||
const configTypes = Array.from(
|
if (typeof data === 'object' && !Array.isArray(data) && data !== null) {
|
||||||
new Set(
|
const result: Record<string, Array<{ name: string; value: any; type: number }>> = {}
|
||||||
configData.list
|
Object.keys(data).forEach((key) => {
|
||||||
.map((config: ProjectInspectionConfigApi.ProjectInspectionConfigVO) => config.configType)
|
const value = data[key]
|
||||||
.filter((type: number | undefined) => type !== undefined)
|
// 检查是否是数组,且数组元素包含type字段
|
||||||
)
|
if (Array.isArray(value) && value.length > 0) {
|
||||||
) as number[]
|
// 检查第一个元素是否是对象且包含type字段
|
||||||
|
const firstItem = value[0]
|
||||||
if (configTypes.length === 0) {
|
if (typeof firstItem === 'object' && firstItem !== null && 'type' in firstItem && 'name' in firstItem) {
|
||||||
message.warning('该巡检人没有配置巡检类型')
|
result[key] = value
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据configType查询巡检内容
|
|
||||||
const inspectionDataList = await ProjectInspectionDataApi.getProjectInspectionDataList(configTypes)
|
|
||||||
|
|
||||||
if (!inspectionDataList || inspectionDataList.length === 0) {
|
|
||||||
message.warning('没有找到对应的巡检内容')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
// 打开填报表单
|
return Object.keys(result).length > 0 ? result : null
|
||||||
uploadFormRef.value.open(row, inspectionDataList)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取巡检配置失败:', error)
|
|
||||||
message.error('获取巡检配置失败,请稍后重试')
|
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析巡检内容失败:', e, summary)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取解析后的巡检内容(用于模板) */
|
||||||
|
const getParsedSummary = (summary: string) => {
|
||||||
|
return parseSummaryData(summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取文件完整URL */
|
||||||
|
const getFileUrl = (url: string): string => {
|
||||||
|
if (!url) return ''
|
||||||
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
const baseURL = import.meta.env.VITE_BASE_URL || ''
|
||||||
|
return baseURL ? `${baseURL}${url.startsWith('/') ? '' : '/'}${url}` : url
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预览图片 */
|
||||||
|
const previewImage = (imageUrl: string) => {
|
||||||
|
window.open(getFileUrl(imageUrl), '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据巡检结论获取卡片状态class */
|
||||||
|
const getCardStatusClass = (overallStatus: number | undefined) => {
|
||||||
|
if (overallStatus === 5) {
|
||||||
|
return 'card-status-danger' // 红色背景
|
||||||
|
} else if (overallStatus === 4) {
|
||||||
|
return 'card-status-warning' // 黄色背景
|
||||||
|
}
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
|
|
@ -398,3 +770,225 @@ onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.timeline-container {
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
.timeline-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 巡检结论为5时,红色背景提示
|
||||||
|
&.card-status-danger {
|
||||||
|
background-color: #fef0f0;
|
||||||
|
border-left: 4px solid #f56c6c;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #fde2e2;
|
||||||
|
box-shadow: 0 4px 12px rgba(245, 108, 108, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 巡检结论为4时,黄色背景提示
|
||||||
|
&.card-status-warning {
|
||||||
|
background-color: #fdf6ec;
|
||||||
|
border-left: 4px solid #e6a23c;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #faecd8;
|
||||||
|
box-shadow: 0 4px 12px rgba(230, 162, 60, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
.card-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
.info-label {
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-summary,
|
||||||
|
.card-status,
|
||||||
|
.card-overall-status,
|
||||||
|
.card-remark {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.summary-label,
|
||||||
|
.status-label,
|
||||||
|
.overall-status-label,
|
||||||
|
.remark-label {
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-content-wrapper {
|
||||||
|
margin-top: 8px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
.summary-group {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-group-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0f70d1;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-name {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
margin-right: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-value-text {
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-word;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-value-image {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
.inspection-thumbnail {
|
||||||
|
max-width: 120px;
|
||||||
|
max-height: 120px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
padding: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-value-file {
|
||||||
|
color: #409EFF;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border: 1px solid #409EFF;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #409EFF;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-text {
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.6;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-content {
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-timeline) {
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
.el-timeline-item {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
|
||||||
|
.el-timeline-item__timestamp {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue