Merge remote-tracking branch 'origin/master'

master
夜笙歌 7 months ago
commit f148a367e7

@ -8,7 +8,7 @@ import { ProdBaseMachineInfoVO, ProdBaseMachineInfoForm, ProdBaseMachineInfoQuer
* @returns {*} * @returns {*}
*/ */
export const listDmsBaseMachineInfoIn = (query?: ProdBaseMachineInfoQuery): AxiosPromise<ProdBaseMachineInfoVO[]> => { export const listDmsBaseMachineInfo = (query?: ProdBaseMachineInfoQuery): AxiosPromise<ProdBaseMachineInfoVO[]> => {
return request({ return request({
url: '/dms/dmsBaseMachineInfo/list', url: '/dms/dmsBaseMachineInfo/list',
method: 'get', method: 'get',

@ -64,6 +64,11 @@ export interface DmsRecordShutDownVO {
*/ */
shutTypeName: string; shutTypeName: string;
/**
* ()
*/
shutReasonName: string;
} }
export interface DmsRecordShutDownForm extends BaseEntity { export interface DmsRecordShutDownForm extends BaseEntity {
@ -132,6 +137,11 @@ export interface DmsRecordShutDownForm extends BaseEntity {
*/ */
shutTypeName?: string; shutTypeName?: string;
/**
* ()
*/
shutReasonName?: string;
} }
export interface DmsRecordShutDownQuery extends PageQuery { export interface DmsRecordShutDownQuery extends PageQuery {
@ -200,6 +210,12 @@ export interface DmsRecordShutDownQuery extends PageQuery {
* *
*/ */
shutTypeName?: string; shutTypeName?: string;
/**
* ()
*/
shutReasonName?: string;
} }

@ -129,8 +129,8 @@
<script setup name='AlarmType' lang='ts'> <script setup name='AlarmType' lang='ts'>
import { listAlarmType, getAlarmType, delAlarmType, addAlarmType, updateAlarmType } from '@/api/dms/alarmType'; import { listAlarmType, getAlarmType, delAlarmType, addAlarmType, updateAlarmType } from '@/api/dms/alarmType';
import { AlarmTypeVO, AlarmTypeQuery, AlarmTypeForm } from '@/api/dms/alarmType/types'; import { AlarmTypeVO, AlarmTypeQuery, AlarmTypeForm } from '@/api/dms/alarmType/types';
import { getBaseDeviceCategoryListInDMS } from '@/api/dms/baseDeviceCategory'; import { getBaseDeviceCategoryList } from '@/api/dms/baseDeviceCategory';
import { getBaseDeviceTypeListInDMS } from '@/api/dms/baseDeviceType'; import { getBaseDeviceTypeList } from '@/api/dms/baseDeviceType';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { dms_level_status } = toRefs<any>(proxy?.useDict('dms_level_status')); const { dms_level_status } = toRefs<any>(proxy?.useDict('dms_level_status'));
@ -155,14 +155,14 @@ const dialog = reactive<DialogOption>({
/** 获取设备大类下拉框 */ /** 获取设备大类下拉框 */
const deviceCategoryList = ref([]); const deviceCategoryList = ref([]);
const getBaseDeviceCategoryListSelect = async () => { const getBaseDeviceCategoryListSelect = async () => {
let res = await getBaseDeviceCategoryListInDMS(null); let res = await getBaseDeviceCategoryList(null);
deviceCategoryList.value = res.data; deviceCategoryList.value = res.data;
}; };
/** 获取设备类型下拉框 */ /** 获取设备类型下拉框 */
const deviceTypeList = ref([]); const deviceTypeList = ref([]);
const getBaseDeviceTypeListSelect = async () => { const getBaseDeviceTypeListSelect = async () => {
let res = await getBaseDeviceTypeListInDMS(null); let res = await getBaseDeviceTypeList(null);
deviceTypeList.value = res.data; deviceTypeList.value = res.data;
}; };

@ -190,14 +190,12 @@ const data = reactive<PageData<DmsBaseDeviceSuppliesForm, DmsBaseDeviceSuppliesQ
}, },
rules: { rules: {
machineId: [ machineId: [
{ required: true, message: "设备不能为空", trigger: "blur" } { required: true, message: "设备不能为空", trigger: "change" },
{ required: true, message: "设备不能为空", trigger: "change" }
], ],
suppliesName: [ suppliesName: [
{ required: true, message: "配套信息名称不能为空", trigger: "blur" } { required: true, message: "配套信息名称不能为空", trigger: "blur" }
], ]
machineId: [
{ required: true, message: "设备不能为空", trigger: "change" }
],
} }
}); });

@ -134,7 +134,7 @@
<script setup name="DmsBaseInspectRoute" lang="ts"> <script setup name="DmsBaseInspectRoute" lang="ts">
import { listDmsBaseInspectRoute, getDmsBaseInspectRoute, delDmsBaseInspectRoute, addDmsBaseInspectRoute, updateDmsBaseInspectRoute } from '@/api/dms/dmsBaseInspectRoute'; import { listDmsBaseInspectRoute, getDmsBaseInspectRoute, delDmsBaseInspectRoute, addDmsBaseInspectRoute, updateDmsBaseInspectRoute } from '@/api/dms/dmsBaseInspectRoute';
import { DmsBaseInspectRouteVO, DmsBaseInspectRouteQuery, DmsBaseInspectRouteForm } from '@/api/dms/dmsBaseInspectRoute/types'; import { DmsBaseInspectRouteVO, DmsBaseInspectRouteQuery, DmsBaseInspectRouteForm } from '@/api/dms/dmsBaseInspectRoute/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { inspect_type } = toRefs<any>(proxy?.useDict('inspect_type')); const { inspect_type } = toRefs<any>(proxy?.useDict('inspect_type'));
@ -304,7 +304,7 @@ const handleExport = () => {
let deviceTypeOptions = ref([]) let deviceTypeOptions = ref([])
const getDeviceTypeOptions = async () => { const getDeviceTypeOptions = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -126,7 +126,7 @@
<script setup name="DmsBaseLubeStandard" lang="ts"> <script setup name="DmsBaseLubeStandard" lang="ts">
import { listDmsBaseLubeStandard, getDmsBaseLubeStandard, delDmsBaseLubeStandard, addDmsBaseLubeStandard, updateDmsBaseLubeStandard } from '@/api/dms/dmsBaseLubeStandard'; import { listDmsBaseLubeStandard, getDmsBaseLubeStandard, delDmsBaseLubeStandard, addDmsBaseLubeStandard, updateDmsBaseLubeStandard } from '@/api/dms/dmsBaseLubeStandard';
import { DmsBaseLubeStandardVO, DmsBaseLubeStandardQuery, DmsBaseLubeStandardForm } from '@/api/dms/dmsBaseLubeStandard/types'; import { DmsBaseLubeStandardVO, DmsBaseLubeStandardQuery, DmsBaseLubeStandardForm } from '@/api/dms/dmsBaseLubeStandard/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); const { active_flag } = toRefs<any>(proxy?.useDict('active_flag'));
@ -301,7 +301,7 @@ const handleExport = () => {
// //
let deviceTypeOptions = ref([]); let deviceTypeOptions = ref([]);
const getBaseDeviceTypeOption = async () => { const getBaseDeviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -136,7 +136,7 @@
import { listDmsBaseLubeStation, getDmsBaseLubeStation, delDmsBaseLubeStation, addDmsBaseLubeStation, updateDmsBaseLubeStation } from '@/api/dms/dmsBaseLubeStation'; import { listDmsBaseLubeStation, getDmsBaseLubeStation, delDmsBaseLubeStation, addDmsBaseLubeStation, updateDmsBaseLubeStation } from '@/api/dms/dmsBaseLubeStation';
import { DmsBaseLubeStationVO, DmsBaseLubeStationQuery, DmsBaseLubeStationForm } from '@/api/dms/dmsBaseLubeStation/types'; import { DmsBaseLubeStationVO, DmsBaseLubeStationQuery, DmsBaseLubeStationForm } from '@/api/dms/dmsBaseLubeStation/types';
import {getDmsBaseLubeStandardList} from "@/api/dms/dmsBaseLubeStandard"; import {getDmsBaseLubeStandardList} from "@/api/dms/dmsBaseLubeStandard";
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); const { active_flag } = toRefs<any>(proxy?.useDict('active_flag'));
@ -310,7 +310,7 @@ const handleExport = () => {
// //
let deviceTypeOptions = ref([]); let deviceTypeOptions = ref([]);
const getBaseDeviceTypeOption = async () => { const getBaseDeviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -129,7 +129,7 @@
<script setup name="DmsBaseMaintStandard" lang="ts"> <script setup name="DmsBaseMaintStandard" lang="ts">
import { listDmsBaseMaintStandard, getDmsBaseMaintStandard, delDmsBaseMaintStandard, addDmsBaseMaintStandard, updateDmsBaseMaintStandard } from '@/api/dms/dmsBaseMaintStandard'; import { listDmsBaseMaintStandard, getDmsBaseMaintStandard, delDmsBaseMaintStandard, addDmsBaseMaintStandard, updateDmsBaseMaintStandard } from '@/api/dms/dmsBaseMaintStandard';
import { DmsBaseMaintStandardVO, DmsBaseMaintStandardQuery, DmsBaseMaintStandardForm } from '@/api/dms/dmsBaseMaintStandard/types'; import { DmsBaseMaintStandardVO, DmsBaseMaintStandardQuery, DmsBaseMaintStandardForm } from '@/api/dms/dmsBaseMaintStandard/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); const { active_flag } = toRefs<any>(proxy?.useDict('active_flag'));
@ -298,7 +298,7 @@ const handleExport = () => {
// //
let dviceTypeOptions = ref([]); let dviceTypeOptions = ref([]);
const getDviceTypeOption = async () => { const getDviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
dviceTypeOptions.value = res.data; dviceTypeOptions.value = res.data;
} }

@ -149,7 +149,7 @@
import { listDmsBaseMaintStation, getDmsBaseMaintStation, delDmsBaseMaintStation, addDmsBaseMaintStation, updateDmsBaseMaintStation } from '@/api/dms/dmsBaseMaintStation'; import { listDmsBaseMaintStation, getDmsBaseMaintStation, delDmsBaseMaintStation, addDmsBaseMaintStation, updateDmsBaseMaintStation } from '@/api/dms/dmsBaseMaintStation';
import { DmsBaseMaintStationVO, DmsBaseMaintStationQuery, DmsBaseMaintStationForm } from '@/api/dms/dmsBaseMaintStation/types'; import { DmsBaseMaintStationVO, DmsBaseMaintStationQuery, DmsBaseMaintStationForm } from '@/api/dms/dmsBaseMaintStation/types';
import {getDmsBaseMaintStandardList} from "@/api/dms/dmsBaseMaintStandard"; import {getDmsBaseMaintStandardList} from "@/api/dms/dmsBaseMaintStandard";
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
import { getDmsBaseMaintProjectList } from '@/api/dms/dmsBaseMaintProject'; import { getDmsBaseMaintProjectList } from '@/api/dms/dmsBaseMaintProject';
@ -332,7 +332,7 @@ const getMaintStandardOtions = async () => {
// //
let dviceTypeOptions = ref([]); let dviceTypeOptions = ref([]);
const getDviceTypeOption = async () => { const getDviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
dviceTypeOptions.value = res.data; dviceTypeOptions.value = res.data;
} }

@ -204,7 +204,8 @@ const data = reactive<PageData<DmsBaseSpecialdeviceParamForm, DmsBaseSpecialdevi
}, },
rules: { rules: {
machineId: [ machineId: [
{ required: true, message: "设备不能为空", trigger: "blur" } { required: true, message: "设备不能为空", trigger: "blur" },
{ required: true, message: "设备不能为空", trigger: "change" }
], ],
paramCode: [ paramCode: [
{ required: true, message: "参数编号不能为空", trigger: "blur" } { required: true, message: "参数编号不能为空", trigger: "blur" }
@ -214,9 +215,6 @@ const data = reactive<PageData<DmsBaseSpecialdeviceParamForm, DmsBaseSpecialdevi
], ],
activeFlag: [ activeFlag: [
{ required: true, message: "激活标识不能为空", trigger: "change" } { required: true, message: "激活标识不能为空", trigger: "change" }
],
machineId: [
{ required: true, message: "设备不能为空", trigger: "change" }
] ]
} }
}); });

@ -4,20 +4,7 @@
<el-card shadow="never"> <el-card shadow="never">
<div style="display: flex; justify-content: space-between"> <div style="display: flex; justify-content: space-between">
<div> <div>
<el-button <!-- 移除提交按钮Web端不再创建工单 -->
v-if="submitButtonShow"
:loading="buttonLoading"
type="info"
@click="submitForm('draft')">
暂存
</el-button>
<el-button
v-if="submitButtonShow"
:loading="buttonLoading"
type="primary"
@click="submitForm('submit')">
提交
</el-button>
<el-button <el-button
v-if="approvalButtonShow" v-if="approvalButtonShow"
:loading="buttonLoading" :loading="buttonLoading"
@ -86,150 +73,84 @@
</el-form> </el-form>
</el-card> </el-card>
<!-- 当前步骤处理区域 --> <!-- PDA端创建的工单详情只读显示 -->
<el-card shadow="never"> <el-card shadow="never">
<h4 class="form-header">{{ currentStepTitle }}</h4> <h4 class="form-header">PDA端创建的工单详情只读</h4>
<el-form <el-form
ref="processFormRef" ref="processFormRef"
v-loading="loading" v-loading="loading"
:disabled="routeParams.type === 'view'"
:model="form" :model="form"
:rules="rules" :rules="rules"
label-width="120px"> label-width="120px">
<el-row> <el-row>
<!-- 第一步故障报修信息 --> <!-- 故障报修信息只读显示 -->
<template v-if="currentStepOrder === 1"> <el-col :span="8">
<el-col :span="8"> <el-form-item label="故障类型">
<el-form-item label="故障类型" prop="faultType"> <el-input v-model="form.faultType" disabled />
<el-select v-model="form.faultType" placeholder="请选择故障类型"> </el-form-item>
<el-option </el-col>
v-for="dict in activity_fault_type" <el-col :span="8">
:key="dict.value" <el-form-item label="维修类型">
:label="dict.label" <el-input v-model="form.repairType" disabled />
:value="dict.value"> </el-form-item>
</el-option> </el-col>
</el-select> <el-col :span="8">
</el-form-item> <el-form-item label="外协单位">
</el-col> <el-input v-model="outsourcingName" disabled />
<el-col :span="8"> </el-form-item>
<el-form-item label="维修类型" prop="repairType"> </el-col>
<el-select <el-col :span="24">
v-model="form.repairType" <el-form-item label="故障描述">
placeholder="请选择维修类型" <el-input
@change="updateOutsourcingValidation"> v-model="form.faultDescription"
<el-option type="textarea"
v-for="dict in fault_repair_type" :rows="3"
:key="dict.value" disabled />
:label="dict.label" </el-form-item>
:value="dict.value"> </el-col>
</el-option> <el-col :span="24">
</el-select> <el-form-item label="涉及操作">
</el-form-item> <el-input
</el-col> v-model="form.designOperations"
<el-col :span="8"> type="textarea"
<el-form-item label="外协单位" prop="outsourcingId"> :rows="2"
<el-input v-model="form.outsourcingId" placeholder="请输入外协单位" /> disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24">
<el-form-item label="故障描述" prop="faultDescription">
<el-input
v-model="form.faultDescription"
type="textarea"
:rows="3"
placeholder="请输入故障描述" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="涉及操作" prop="designOperations">
<el-input
v-model="form.designOperations"
type="textarea"
:rows="2"
placeholder="请输入涉及操作" />
</el-form-item>
</el-col>
</template>
<!-- 第二步维修处理 --> <!-- 维修处理信息只读显示 -->
<template v-if="currentStepOrder === 2"> <template v-if="form.repairer">
<el-col :span="8">
<el-form-item label="故障类型">
<el-input v-model="form.faultType" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="维修类型">
<el-input v-model="form.repairType" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="维修人" prop="repairer">
<el-input v-model="form.repairer" placeholder="请输入维修人" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="检查后的故障判断" prop="checkedFault">
<el-input
v-model="form.checkedFault"
type="textarea"
:rows="3"
placeholder="请输入检查后的故障判断" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="维修措施" prop="protectedMethod">
<el-input
v-model="form.protectedMethod"
type="textarea"
:rows="3"
placeholder="请输入维修措施" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="维修内容" prop="repairContent">
<el-input
v-model="form.repairContent"
type="textarea"
:rows="4"
placeholder="请输入维修内容" />
</el-form-item>
</el-col>
</template>
<!-- 第三步维修确认 -->
<template v-if="currentStepOrder === 3">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="维修人"> <el-form-item label="维修人">
<el-input v-model="form.repairer" disabled /> <el-input v-model="form.repairer" disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<el-form-item label="维修结果" prop="repairConfirm">
<el-select v-model="form.repairConfirm" placeholder="请选择维修结果">
<el-option
v-for="dict in fault_repair_confirm"
:key="dict.value"
:label="dict.label"
:value="dict.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="关联更换零部件" prop="componentsPartsId">
<el-input v-model="form.componentsPartsId" placeholder="请输入关联更换零部件内容" />
</el-form-item>
</el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="维修内容"> <el-form-item label="检查后的故障判断">
<el-input v-model="form.repairContent" type="textarea" :rows="3" disabled /> <el-input
v-model="form.checkedFault"
type="textarea"
:rows="3"
disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="维修措施"> <el-form-item label="维修措施">
<el-input v-model="form.protectedMethod" type="textarea" :rows="3" disabled /> <el-input
v-model="form.protectedMethod"
type="textarea"
:rows="3"
disabled />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="维修内容">
<el-input
v-model="form.repairContent"
type="textarea"
:rows="4"
disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
</template> </template>
@ -249,7 +170,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 处理意见除第一步外都需要 --> <!-- 处理意见审批时需要 -->
<el-col :span="24" v-if="needProcessResolution"> <el-col :span="24" v-if="needProcessResolution">
<el-form-item label="处理意见" prop="processHandleResolution"> <el-form-item label="处理意见" prop="processHandleResolution">
<el-input <el-input
@ -277,12 +198,11 @@
<script setup name="FaultWorkOrderProcess" lang="ts"> <script setup name="FaultWorkOrderProcess" lang="ts">
// //
import { ref, reactive, computed, onMounted, getCurrentInstance, toRefs, watch } from 'vue'; import { ref, reactive, computed, onMounted, getCurrentInstance, toRefs } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { ElMessageBox, type FormInstance } from 'element-plus';
import type { ComponentInternalInstance } from 'vue';
import { getDmsBillsFaultInstance, approveWorkOrder, confirmRepairResult } from '@/api/dms/dmsBillsFaultInstance'; import { getDmsBillsFaultInstance, approveWorkOrder, confirmRepairResult } from '@/api/dms/dmsBillsFaultInstance';
import { addDmsFaultInstanceActivity } from '@/api/dms/dmsFaultInstanceActivity';
import { pageByTaskWait } from '@/api/workflow/task';
import { getCurrentDateTime } from '@/utils/dateUtils';
import { getDmsBaseOutsourcingInfoList } from '@/api/dms/dmsBaseOutsourcingInfo'; import { getDmsBaseOutsourcingInfoList } from '@/api/dms/dmsBaseOutsourcingInfo';
import SubmitVerify from '@/views/dms/dmsFaultInstanceActivity/submitVerify.vue'; import SubmitVerify from '@/views/dms/dmsFaultInstanceActivity/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue'; import ApprovalRecord from '@/components/Process/approvalRecord.vue';
@ -299,15 +219,14 @@ const routeParams = ref<Record<string, any>>({});
const workOrder = ref<any>({}); const workOrder = ref<any>({});
const workOrderStatusText = ref(''); const workOrderStatusText = ref('');
const outsourceList = ref<any[]>([]); const outsourceList = ref<any[]>([]);
const outsourcingName = ref('');
// //
const processFormRef = ref<ElFormInstance>(); const processFormRef = ref<FormInstance>();
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>(); const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>(); const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
// //
const currentTask = ref<any>(null);
const currentStepOrder = ref(1);
const taskVariables = ref<any>({}); const taskVariables = ref<any>({});
// //
@ -325,7 +244,6 @@ const initFormData = {
repairConfirm: undefined, repairConfirm: undefined,
componentsPartsId: undefined, componentsPartsId: undefined,
processHandleResolution: undefined, processHandleResolution: undefined,
processStepOrder: undefined,
approveStatus: undefined approveStatus: undefined
}; };
@ -333,68 +251,60 @@ const form = reactive({ ...initFormData });
// //
const rules = reactive({ const rules = reactive({
faultType: [{ required: false, message: '故障类型不能为空', trigger: 'change' }], approveStatus: [{ required: false, message: '审批状态不能为空', trigger: 'change' }],
faultDescription: [{ required: false, message: '故障描述不能为空', trigger: 'blur' }], processHandleResolution: [{ required: false, message: '处理意见不能为空', trigger: 'blur' }]
repairType: [{ required: false, message: '维修类型不能为空', trigger: 'change' }],
outsourcingId: [{ required: false, message: '外协单位不能为空', trigger: 'change' }],
repairer: [{ required: false, message: '维修人不能为空', trigger: 'blur' }],
checkedFault: [{ required: false, message: '检查后的故障判断不能为空', trigger: 'blur' }],
protectedMethod: [{ required: false, message: '维修措施不能为空', trigger: 'blur' }],
repairContent: [{ required: false, message: '维修内容不能为空', trigger: 'blur' }],
repairConfirm: [{ required: false, message: '维修结果不能为空', trigger: 'change' }],
processHandleResolution: [{ required: false, message: '处理意见不能为空', trigger: 'blur' }],
approveStatus: [{ required: false, message: '审批状态不能为空', trigger: 'change' }]
}); });
// //
const needProcessResolution = computed(() => currentStepOrder.value > 1); const needProcessResolution = computed(() => {
return isApprovalStage.value || confirmButtonShow.value;
const currentStepName = computed(() => {
const stepNames = {
1: '故障报修',
2: '维修处理',
3: '维修确认'
};
return stepNames[currentStepOrder.value] || '处理工单';
}); });
const currentStepTitle = computed(() => { const currentStepName = computed(() => {
const stepTitles = { if (workOrder.value?.approveStatus === '1') {
1: '步骤1故障报修要求参数', return '审批工单';
2: '步骤2维修处理实际参数', }
3: '步骤3维修确认' return '处理工单';
};
return stepTitles[currentStepOrder.value] || '工单处理';
}); });
const approvalButtonShow = computed(() => { const approvalButtonShow = computed(() => {
return routeParams.value.type === 'approval' && currentTask.value; // (1)
return workOrder.value?.approveStatus === '1';
}); });
const confirmButtonShow = computed(() => { const confirmButtonShow = computed(() => {
// //
// 1. confirm console.log('主管确认按钮显示条件检查:', {
// 2. (2)(0) billsStatus: workOrder.value?.billsStatus,
// 3. (2) - billsStatusType: typeof workOrder.value?.billsStatus,
return routeParams.value.type === 'confirm' || approveStatus: workOrder.value?.approveStatus,
(workOrder.value?.billsStatus === '2' && approveStatusType: typeof workOrder.value?.approveStatus,
workOrder.value?.repairConfirm === '0' && repairConfirm: workOrder.value?.repairConfirm,
workOrder.value?.approveStatus === '2'); repairConfirmType: typeof workOrder.value?.repairConfirm
});
//
// (2) && (2) && (0)
// 使
const billsStatusMatches = String(workOrder.value?.billsStatus) === '2';
const approveStatusMatches = String(workOrder.value?.approveStatus) === '2';
const repairConfirmMatches = String(workOrder.value?.repairConfirm) === '0';
const shouldShow = billsStatusMatches && approveStatusMatches && repairConfirmMatches;
console.log('主管确认按钮显示结果:', {
billsStatusMatches,
approveStatusMatches,
repairConfirmMatches,
shouldShow
});
return shouldShow;
}); });
// //
const isApprovalStage = computed(() => { const isApprovalStage = computed(() => {
return routeParams.value.type === 'approval' && workOrder.value?.approveStatus === '1'; return workOrder.value?.approveStatus === '1';
});
//
const submitButtonShow = computed(() => {
return (
routeParams.value.type === 'add' ||
(routeParams.value.type === 'update' &&
workOrder.value?.billsStatus &&
(workOrder.value.billsStatus === 'draft' || workOrder.value.billsStatus === 'cancel' || workOrder.value.billsStatus === 'back'))
);
}); });
// //
@ -409,11 +319,6 @@ onMounted(async () => {
// //
await getOutsourceList(); await getOutsourceList();
//
if (routeParams.value.type === 'approval') {
await loadCurrentTask();
}
// //
initializeFormData(); initializeFormData();
@ -422,28 +327,19 @@ onMounted(async () => {
} catch (error) { } catch (error) {
console.error('初始化失败:', error); console.error('初始化失败:', error);
proxy?.$modal.msgError('加载数据失败'); (proxy as any)?.$modal.msgError('加载数据失败');
} finally { } finally {
loading.value = false; loading.value = false;
} }
}); });
//
watch(() => form.repairType, (newValue) => {
updateOutsourcingValidation();
//
if (newValue !== '2') {
form.outsourcingId = undefined;
}
});
// //
const loadWorkOrderInfo = async () => { const loadWorkOrderInfo = async () => {
// querybusinessId // querybusinessId
const businessId = routeParams.value.id; const businessId = routeParams.value.id;
if (!businessId) { if (!businessId) {
proxy?.$modal.msgError('缺少工单ID参数'); (proxy as any)?.$modal.msgError('缺少工单ID参数');
return; return;
} }
@ -454,50 +350,10 @@ const loadWorkOrderInfo = async () => {
workOrderStatusText.value = getStatusText(workOrder.value.billsStatus || ''); workOrderStatusText.value = getStatusText(workOrder.value.billsStatus || '');
}; };
//
const loadCurrentTask = async () => {
// querybusinessIdtaskId
const businessId = routeParams.value.id;
const taskId = routeParams.value.taskId;
// taskIdbusinessId
const taskRes = await pageByTaskWait({
pageNum: 1,
pageSize: 10,
flowCode: 'Fault01' //
});
const tasks = taskRes.rows.filter(task =>
task.businessId === businessId || task.id === taskId
);
if (tasks.length > 0) {
currentTask.value = tasks[0];
//
const nodeName = currentTask.value.nodeName;
if (nodeName === '故障报修') {
currentStepOrder.value = 1;
} else if (nodeName === '维修处理') {
currentStepOrder.value = 2;
} else if (nodeName === '维修确认') {
currentStepOrder.value = 3;
}
//
if (needProcessResolution.value) {
rules.processHandleResolution = [
{ required: true, message: '处理意见不能为空', trigger: 'blur' }
];
}
}
};
// //
const initializeFormData = () => { const initializeFormData = () => {
// //
form.repairInstanceId = workOrder.value?.repairInstanceId; form.repairInstanceId = workOrder.value?.repairInstanceId;
form.processStepOrder = currentStepOrder.value;
// undefined // undefined
form.faultType = workOrder.value?.faultType || ''; form.faultType = workOrder.value?.faultType || '';
@ -511,84 +367,19 @@ const initializeFormData = () => {
form.repairer = workOrder.value?.repairer || ''; form.repairer = workOrder.value?.repairer || '';
form.repairConfirm = workOrder.value?.repairConfirm || ''; form.repairConfirm = workOrder.value?.repairConfirm || '';
form.componentsPartsId = workOrder.value?.componentsPartsId || ''; form.componentsPartsId = workOrder.value?.componentsPartsId || '';
//
if (form.outsourcingId && outsourceList.value.length > 0) {
const outsourcing = outsourceList.value.find(item => item.outsourcingId === form.outsourcingId);
outsourcingName.value = outsourcing?.outsourcingName || '';
}
// //
if (routeParams.value.type === 'approval' && !form.approveStatus) { if (routeParams.value.type === 'approval' && !form.approveStatus) {
form.approveStatus = undefined; // form.approveStatus = undefined; //
} }
}; };
//
const submitForm = async (status: string) => {
if (!processFormRef.value) return;
try {
const valid = await processFormRef.value.validate();
if (valid) {
buttonLoading.value = true;
if (status === 'draft') {
//
await addDmsFaultInstanceActivity({
repairInstanceId: form.repairInstanceId,
faultType: form.faultType,
faultDescription: form.faultDescription,
designOperations: form.designOperations,
repairType: form.repairType,
outsourcingId: form.outsourcingId,
checkedFault: form.checkedFault || '',
repairContent: form.repairContent || '',
protectedMethod: form.protectedMethod || '',
repairer: form.repairer || '',
repairConfirm: form.repairConfirm,
componentsPartsId: form.componentsPartsId,
processHandleResolution: form.processHandleResolution || '',
processStepOrder: form.processStepOrder,
handleTime: getCurrentDateTime()
});
proxy?.$modal.msgSuccess('暂存成功');
goBack();
} else {
//
//
taskVariables.value = {
entity: {
repairInstanceId: form.repairInstanceId,
faultType: form.faultType,
faultDescription: form.faultDescription,
designOperations: form.designOperations,
repairType: form.repairType,
outsourcingId: form.outsourcingId,
checkedFault: form.checkedFault || '',
repairContent: form.repairContent || '',
protectedMethod: form.protectedMethod || '',
repairer: form.repairer || '',
repairConfirm: form.repairConfirm,
componentsPartsId: form.componentsPartsId,
processHandleResolution: form.processHandleResolution || '',
processStepOrder: form.processStepOrder
}
};
//
if (routeParams.value.taskId) {
submitVerifyRef.value?.openDialog(routeParams.value.taskId);
} else {
//
proxy?.$modal.msgSuccess('提交成功');
goBack();
}
}
}
} catch (error) {
console.error('提交失败:', error);
proxy?.$modal.msgError('提交失败');
} finally {
buttonLoading.value = false;
}
};
// //
const approvalVerifyOpen = async () => { const approvalVerifyOpen = async () => {
if (!processFormRef.value) return; if (!processFormRef.value) return;
@ -596,26 +387,26 @@ const approvalVerifyOpen = async () => {
try { try {
const valid = await processFormRef.value.validate(); const valid = await processFormRef.value.validate();
if (valid) { if (valid) {
await proxy?.$modal.confirm('是否确认提交审批?'); await (proxy as any)?.$modal.confirm('是否确认提交审批?');
buttonLoading.value = true; buttonLoading.value = true;
// //
const approveStatus = form.approveStatus; const approveStatus = form.approveStatus;
const message = form.processHandleResolution || ''; const message = form.processHandleResolution || '';
await approveWorkOrder(form.repairInstanceId, approveStatus, message); await approveWorkOrder(form.repairInstanceId, approveStatus, message);
const statusText = approveStatus === '2' ? '通过' : '拒绝'; const statusText = approveStatus === '2' ? '通过' : '拒绝';
proxy?.$modal.msgSuccess(`审批${statusText}成功`); (proxy as any)?.$modal.msgSuccess(`审批${statusText}成功`);
// //
goBack(); goBack();
} }
} catch (error: any) { } catch (error: any) {
if (error !== 'cancel' && error !== 'close') { if (error !== 'cancel' && error !== 'close') {
console.error('审批失败:', error); console.error('审批失败:', error);
proxy?.$modal.msgError('审批失败:' + (error.message || '未知错误')); (proxy as any)?.$modal.msgError('审批失败:' + (error.message || '未知错误'));
} }
} finally { } finally {
buttonLoading.value = false; buttonLoading.value = false;
@ -629,50 +420,67 @@ const submitCallback = async (approvalResult?: any) => {
// //
const confirmResult = form.repairConfirm || '1'; // const confirmResult = form.repairConfirm || '1'; //
await confirmRepairResult(form.repairInstanceId, confirmResult); await confirmRepairResult(form.repairInstanceId, confirmResult);
proxy?.$modal.msgSuccess('确认完成'); (proxy as any)?.$modal.msgSuccess('确认完成');
} else {
//
await addDmsFaultInstanceActivity({
repairInstanceId: form.repairInstanceId,
faultType: form.faultType,
faultDescription: form.faultDescription,
designOperations: form.designOperations,
repairType: form.repairType,
outsourcingId: form.outsourcingId,
checkedFault: form.checkedFault || '',
repairContent: form.repairContent || '',
protectedMethod: form.protectedMethod || '',
repairer: form.repairer || '',
repairConfirm: form.repairConfirm,
componentsPartsId: form.componentsPartsId,
processHandleResolution: form.processHandleResolution || '',
processStepOrder: form.processStepOrder,
handleTime: getCurrentDateTime()
});
proxy?.$modal.msgSuccess(`${currentStepName.value}完成`);
} }
// //
goBack(); goBack();
} catch (error: any) { } catch (error: any) {
console.error('操作失败:', error); console.error('操作失败:', error);
proxy?.$modal.msgError('操作失败:' + (error.message || '未知错误')); (proxy as any)?.$modal.msgError('操作失败:' + (error.message || '未知错误'));
} }
}; };
// //
const handleConfirm = async () => { const handleConfirm = async () => {
try { try {
// // 使
const result = await ElMessageBox.confirm(
'请选择维修结果确认方式:\n• 通过:维修通过,工单完成\n• 退回:维修不通过,返回维修环节重新维修',
'主管确认',
{
confirmButtonText: '通过',
cancelButtonText: '退回',
type: 'warning',
distinguishCancelAndClose: true
}
);
buttonLoading.value = true;
//
form.repairConfirm = '1'; // 1 form.repairConfirm = '1'; // 1
// //
await approvalVerifyOpen(); await confirmRepairResult(form.repairInstanceId, form.repairConfirm);
} catch (error: any) { (proxy as any)?.$modal.msgSuccess('确认通过完成');
if (error !== 'cancel' && error !== 'close') { goBack();
console.error('确认失败:', error);
proxy?.$modal.msgError('确认失败:' + (error.message || '未知错误')); } catch (action: any) {
if (action === 'cancel') {
// 退
try {
buttonLoading.value = true;
form.repairConfirm = '2'; // 2
await confirmRepairResult(form.repairInstanceId, form.repairConfirm);
(proxy as any)?.$modal.msgSuccess('退回维修环节完成');
goBack();
} catch (confirmError: any) {
console.error('退回操作失败:', confirmError);
(proxy as any)?.$modal.msgError('退回操作失败:' + (confirmError.message || '未知错误'));
} finally {
buttonLoading.value = false;
}
} else if (action === 'close') {
// ESC
console.log('用户取消了确认操作');
} else {
console.error('确认失败:', action);
(proxy as any)?.$modal.msgError('确认失败:' + (action.message || '未知错误'));
}
} finally {
if (form.repairConfirm === '1') {
buttonLoading.value = false;
} }
} }
}; };
@ -684,7 +492,7 @@ const handleApprovalRecord = () => {
// //
const goBack = () => { const goBack = () => {
proxy?.$tab.closePage(proxy?.$route); (proxy as any)?.$tab.closePage((proxy as any)?.$route);
router.go(-1); router.go(-1);
}; };
@ -698,16 +506,6 @@ const getOutsourceList = async () => {
} }
}; };
//
const updateOutsourcingValidation = () => {
//
if (routeParams.value.type !== 'approval' && form.repairType === '2') {
rules.outsourcingId = [{ required: true, message: '外协单位不能为空', trigger: 'change' }];
} else {
rules.outsourcingId = [{ required: false, message: '外协单位不能为空', trigger: 'change' }];
}
};
// //
const setDynamicValidationRules = () => { const setDynamicValidationRules = () => {
// //
@ -715,44 +513,17 @@ const setDynamicValidationRules = () => {
rules[key] = [{ required: false, message: rules[key][0].message, trigger: rules[key][0].trigger }]; rules[key] = [{ required: false, message: rules[key][0].message, trigger: rules[key][0].trigger }];
}); });
// //
if (routeParams.value.type === 'approval') { if (workOrder.value?.approveStatus === '1') {
// //
rules.approveStatus = [{ required: true, message: '审批状态不能为空', trigger: 'change' }]; rules.approveStatus = [{ required: true, message: '审批状态不能为空', trigger: 'change' }];
if (needProcessResolution.value) {
rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }];
}
} else if (routeParams.value.type === 'confirm') {
//
rules.repairConfirm = [{ required: true, message: '维修结果不能为空', trigger: 'change' }];
rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }]; rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }];
} else { } else if (workOrder.value?.billsStatus === '2' &&
// workOrder.value?.approveStatus === '2' &&
if (currentStepOrder.value === 1) { workOrder.value?.repairConfirm === '0') {
// //
rules.faultType = [{ required: true, message: '故障类型不能为空', trigger: 'change' }]; rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }];
rules.faultDescription = [{ required: true, message: '故障描述不能为空', trigger: 'blur' }];
rules.repairType = [{ required: true, message: '维修类型不能为空', trigger: 'change' }];
} else if (currentStepOrder.value === 2) {
//
rules.repairer = [{ required: true, message: '维修人不能为空', trigger: 'blur' }];
rules.checkedFault = [{ required: true, message: '检查后的故障判断不能为空', trigger: 'blur' }];
rules.protectedMethod = [{ required: true, message: '维修措施不能为空', trigger: 'blur' }];
rules.repairContent = [{ required: true, message: '维修内容不能为空', trigger: 'blur' }];
if (needProcessResolution.value) {
rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }];
}
} else if (currentStepOrder.value === 3) {
//
rules.repairConfirm = [{ required: true, message: '维修结果不能为空', trigger: 'change' }];
if (needProcessResolution.value) {
rules.processHandleResolution = [{ required: true, message: '处理意见不能为空', trigger: 'blur' }];
}
}
} }
//
updateOutsourcingValidation();
}; };
// //

@ -0,0 +1,561 @@
# 维修工单业务流程优化文档
## 目录
1. [概述](#概述)
2. [修改内容总结](#修改内容总结)
3. [业务流程梳理](#业务流程梳理)
4. [工作流集成](#工作流集成)
5. [状态流转逻辑](#状态流转逻辑)
6. [外协ID字段映射问题解决](#外协id字段映射问题解决)
7. [Web端功能简化](#web端功能简化)
8. [接口文档](#接口文档)
9. [数据表结构](#数据表结构)
---
## 概述
本次优化基于原有故障维修工单系统完善了审批流程、PDA端状态检查、主管确认等关键业务功能并与Warm-flow工作流框架深度集成实现了完整的维修工单生命周期管理。
### 核心改进
- **完善审批机制**:增加工单审批环节,确保维修申请的合规性
- **状态流转优化**:明确各阶段状态转换规则,防止非法操作
- **工作流集成**与Warm-flow框架深度整合实现标准化流程管控
- **PDA端适配**保持PDA代码不变的前提下完善后端状态检查逻辑
- **外协ID映射修复**解决PDA端outsrcId与后端outsourcingId的字段映射问题
- **Web端功能简化**Web端专注于审批功能避免重复信息录入
- **查询逻辑优化**:修复多条记录查询异常问题
---
## 修改内容总结
### ✅ 已完成功能
1. **完整的审批流程** - Web端审批PDA端状态检查
2. **状态流转控制** - 严格的状态检查和转换逻辑
3. **工作流集成** - 与Warm-flow框架深度整合
4. **主管确认机制** - 维修质量的最终把关
5. **循环维修机制** - 确认不通过自动返回维修环节
6. **质量控制闭环** - 从申请到确认的完整质量管控体系
7. **PDA端适配** - 保持原有PDA代码不变
8. **代码质量保障** - 通过全面审核确保与PDA标准完全一致
9. **外协ID映射修复** - 解决PDA端outsrcId与后端outsourcingId的字段映射问题
10. **Web端功能简化** - Web端专注审批PDA端负责工单创建和维修避免重复录入
11. **查询逻辑优化** - 使用instance_activity_id确保获取最新活动记录
### 🔄 业务流程
PDA创建工单 → Web审批 → PDA维修 → Web确认 → 工单完成
### 📊 技术特点
- **代码复用性高** - 模块化设计,便于扩展
- **状态管理严格** - 防止非法操作和数据不一致
- **工作流标准化** - 符合企业流程管控要求
- **接口设计清晰** - 前后端分离,易于维护
- **PDA标准兼容** - 完全符合原有PDA客户端要求
- **字段映射完善** - 解决不同端字段类型差异问题
- **职责分离明确** - 各端专注自身核心功能,提高效率
---
## 业务流程梳理
### 整体流程图
```
PDA端创建工单 → 工作流启动 → Web端审批 → PDA端维修 → Web端主管确认 → 工单完成
↓ ↓ ↓ ↓ ↓
申请阶段 待审批状态 审批通过 维修完成 确认完成
```
### 各端操作详情
#### PDA端操作
1. **创建工单** (`insertFaultInstsanceActivity`)
- 选择设备
- 填写故障信息(故障类型、描述、涉及操作)
- 选择维修类型(内部维修/外协维修)
- 如果选择外协选择外协单位传递outsrcId
- 上传故障图片
- 提交后自动启动工作流并生成DmsFaultInstanceActivity记录
2. **获取工单** (`getBillsFaultInstance4Repair`)
- 查询待维修工单列表
- 检查审批状态
3. **开始维修** (`startRepair`)
- 检查审批状态(必须为"2"通过)
- 更新工单状态为"1"维修中
- 记录维修开始时间
4. **完成维修** (`completeRepair`)
- 填写维修内容、措施、维修人员
- 上传维修后图片
- 更新工单状态为"2"维修完成
- 设置确认状态为"0"待确认
#### Web端操作
1. **审批处理** (`/dmsFaultInstanceActivity?type=approval`)
- 通过我的待办跳转到审批页面
- 查看PDA端创建的工单详细信息只读
- 使用submitVerify组件完成审批
- **注意**Web端不创建工单不编辑工单信息专注于审批功能
2. **主管确认** (`/confirm`)
- 查看PDA端完成的维修结果
- 确认维修质量
- 选择确认结果:
- **确认通过**:工单完成,流程结束
- **确认不通过**:工单返回维修环节,形成循环
### 业务流程简化原则
1. **职责分离**
- **PDA端**:工单创建、维修执行、信息录入
- **Web端**:审批决策、质量确认
2. **避免重复录入**
- 工单信息只在PDA端录入一次
- Web端只读显示不允许编辑
3. **按创建时间倒序显示**
- 工单实例信息按创建时间倒序排列
- 确保最新的维修记录优先显示
---
## 外协ID字段映射问题解决
### 问题描述
1. **字段类型不匹配**PDA端使用 `outsrcId`int类型后端期望 `outsourcingId`Long类型
2. **数据表结构差异**`dms_bills_fault_instance` 表没有 `outsourcingId` 字段,该字段在 `dms_fault_instance_activity` 表中
3. **查询逻辑问题**:查询最新活动记录时使用了错误的字段排序,导致返回多条记录
### 解决方案
#### 1. 字段转换处理
```java
// 在DmsBillsFaultInstanceServiceImpl.insertByBo()中添加转换逻辑
if (bo.getOutsrcId() != null && bo.getOutsrcId() > 0) {
bo.setOutsourcingId(Long.valueOf(bo.getOutsrcId()));
}
```
#### 2. Bo类字段扩展
```java
// 在DmsBillsFaultInstanceBo中添加字段
/**
* 外协IDPDA端传递的int类型需要转换为outsourcingId
*/
private Integer outsrcId;
```
#### 3. 查询逻辑修复
```sql
-- 修复前:可能返回多条相同 process_step_order 的记录
and dfia.process_step_order = (select max(process_step_order) ...)
-- 修复后:确保返回唯一的最新记录
and dfia.instance_activity_id = (select max(instance_activity_id) ...)
```
#### 4. PDA端兼容性处理
```java
// 在DmsMobileController中处理字段转换
if (voInstance.getOutsourcingId() != null) {
try {
mobileInstance.setOutsrcId(Math.toIntExact(voInstance.getOutsourcingId()));
} catch (ArithmeticException e) {
log.warn("外协单位ID转换失败值过大: {}", voInstance.getOutsourcingId());
mobileInstance.setOutsrcId(0); // 设置为0作为默认值
}
} else {
mobileInstance.setOutsrcId(0); // 当outsourcingId为null时明确设置为0
}
```
---
## Web端功能简化
### 简化原则
1. **移除创建功能**Web端不再提供工单创建功能
2. **移除编辑功能**Web端不再提供工单信息编辑功能
3. **专注审批**Web端只负责审批和主管确认
4. **只读显示**:所有工单信息以只读方式显示
### 具体修改
#### 1. 页面结构简化
- 移除"提交"和"暂存"按钮
- 移除分步骤的表单编辑界面
- 改为统一的只读信息展示
#### 2. 表单验证简化
```javascript
// 只保留审批相关的验证规则
const rules = reactive({
approveStatus: [{ required: false, message: '审批状态不能为空', trigger: 'change' }],
processHandleResolution: [{ required: false, message: '处理意见不能为空', trigger: 'blur' }]
});
```
#### 3. 业务逻辑简化
- 移除工单创建逻辑
- 移除工单编辑逻辑
- 保留审批和确认逻辑
#### 4. 界面展示优化
```vue
<!-- PDA端创建的工单详情只读显示 -->
<el-card shadow="never">
<h4 class="form-header">PDA端创建的工单详情只读</h4>
<!-- 所有字段都设置为disabled -->
</el-card>
```
---
## 工作流集成
### 工作流定义
- **流程代码**`Fault01`
- **节点配置**
1. 故障申请PDA端
2. 工单审批Web端
3. 维修处理PDA端
4. 主管确认Web端
### 状态映射关系
| 工作流状态 | 工单状态 | 审批状态 | 确认状态 | 说明 |
|-----------|---------|---------|---------|------|
| 故障申请 | 0(待维修) | 1(待审批) | null | PDA端创建工单后 |
| 工单审批 | 0(待维修) | 2(审批通过)/3(审批拒绝) | null | Web端审批后 |
| 维修处理 | 1(维修中)/2(维修完成) | 2(审批通过) | 0(待确认) | PDA端维修过程 |
| 主管确认 | 2(维修完成) | 2(审批通过) | 1(确认完成)/2(确认不通过) | Web端确认 |
---
## 状态流转逻辑
### 状态检查规则
1. **开始维修前检查**
- 审批状态必须为"2"(审批通过)
- 工单状态必须为"0"(待维修)
2. **完成维修后状态**
- 工单状态更新为"2"(维修完成)
- 确认状态设置为"0"(待确认)
3. **主管确认规则**
- 确认通过:工单完成,流程结束
- 确认不通过:返回维修环节,形成循环
### 循环维修机制
- 主管确认不通过时,工单状态回到"0"(待维修)
- PDA端可以重新开始维修
- 系统创建新的活动记录,保持历史记录完整性
---
## 接口文档
### PDA端接口
#### 1. 创建故障工单
- **接口**`POST /dms/mobile/insertFaultInstsanceActivity`
- **参数**
```json
{
"deviceId": 20033,
"outsrcId": 1, // PDA端传递的外协ID
"faultType": "1",
"faultDescription": "设备故障描述",
"repairType": "2"
}
```
#### 2. 获取工单详情
- **接口**`GET /dms/mobile/getBillsFaultInstance4Repair/{repairInstanceId}`
- **返回**包含转换后的outsrcId字段
#### 3. 开始维修
- **接口**`POST /dms/mobile/startRepair`
- **状态检查**:审批状态必须为"2"
#### 4. 完成维修
- **接口**`POST /dms/mobile/completeRepair`
- **状态更新**:工单状态→"2",确认状态→"0"
### Web端接口
#### 1. 审批工单
- **接口**`POST /dms/dmsBillsFaultInstance/approve`
- **参数**
```json
{
"repairInstanceId": "工单ID",
"approveStatus": "2", // 2-通过3-拒绝
"message": "审批意见"
}
```
#### 2. 主管确认
- **接口**`POST /dms/dmsBillsFaultInstance/confirm`
- **参数**
```json
{
"repairInstanceId": "工单ID",
"confirmResult": "1", // 1-确认通过2-确认不通过
"confirmUser": "确认人"
}
```
---
## 数据表结构
### 主要表结构
#### 1. dms_bills_fault_instance故障工单主表
- `repair_instance_id`:工单主键
- `bills_status`工单状态0待维修1维修中2维修完成
- `approve_status`审批状态1待审批2审批通过3审批拒绝
- `repair_confirm`确认状态0待确认1确认完成2确认不通过
#### 2. dms_fault_instance_activity故障工单活动表
- `instance_activity_id`活动主键自增ID
- `repair_instance_id`:关联工单主键
- `outsourcing_id`外协单位IDLong类型
- `process_step_order`:流程步骤顺序
### 字段映射关系
| PDA端字段 | 后端字段 | 数据类型转换 | 说明 |
|----------|---------|-------------|------|
| outsrcId | outsourcingId | int → Long | 外协单位ID |
| deviceId | machineId | int → Long | 设备ID |
| repairInstanceId | repairInstanceId | String → Long | 工单ID |
---
## 🚀 后续优化建议
1. 添加工单超时预警机制
2. 完善维修知识库集成
3. 增加维修效率统计分析
4. 支持批量操作功能
5. 前端常量统一管理(避免硬编码)
6. 添加外协单位管理功能
7. 完善维修历史记录查询和统计分析
8. 优化查询性能,添加适当索引
9. 完善异常处理和日志记录
10. 添加数据备份和恢复机制
## 问题背景
在维修工单系统的使用过程中,发现了以下问题:
1. PDA端显示的工单数量与数据库实际记录不符
2. 点击工单时出现SQL查询异常`Expected one result (or null) to be returned by selectOne(), but found: 2`
3. 外协ID字段映射问题PDA端传递`outsrcId`(int),后端期望`outsourcingId`(Long)
4. Web端和PDA端功能重复用户需要在两个端重复录入信息
## 根本原因分析
1. **查询逻辑问题**:使用`MAX(process_step_order)`查询最新记录时,在循环维修场景下会返回多条相同最大值的记录
2. **字段映射不匹配**PDA端使用`outsrcId`(int),后端使用`outsourcingId`(Long)
3. **业务流程设计问题**Web端和PDA端都在创建和编辑工单信息
4. **数据库异常处理**:在某些边界情况下,查询可能返回多条记录导致系统异常
## 解决方案
### 1. 数据库查询优化
**问题**`selectFaultInstanceJoinFirstAndDeviceById`查询可能返回多条记录
**解决方案**
- 修改SQL查询逻辑使用更精确的条件确保返回唯一记录
- 添加备用查询方案,当主查询失败时自动切换
```xml
<!-- 优化后的查询SQL -->
<select id="selectFaultInstanceJoinFirstAndDeviceById" parameterType="Long" resultMap="DmsBillsFaultInstanceResult">
select dbfi.repair_instance_id, dbfi.fault_source_type, dbfi.fault_source_id,
-- 其他字段...
from dms_bills_fault_instance dbfi
left join prod_base_machine_info dbdl on dbfi.machine_id=dbdl.machine_id
left join dms_fault_instance_activity dfia on (dbfi.repair_instance_id=dfia.repair_instance_id
and dfia.instance_activity_id = (
select max(instance_activity_id)
from dms_fault_instance_activity dfia2
where dfia2.repair_instance_id = dbfi.repair_instance_id
and dfia2.process_step_order = (
select max(process_step_order)
from dms_fault_instance_activity dfia3
where dfia3.repair_instance_id = dbfi.repair_instance_id
)
))
where dbfi.repair_instance_id = #{repairInstanceId}
</select>
```
### 2. 防重复机制
**问题**:在网络不稳定或重试场景下,可能重复插入维修记录
**解决方案**
- 在开始维修前检查是否已存在相同步骤的记录
- 添加幂等性保护,避免重复操作
```java
// 防重复检查
MPJLambdaWrapper<DmsFaultInstanceActivity> checkWrapper = JoinWrappers.lambda(DmsFaultInstanceActivity.class)
.eq(DmsFaultInstanceActivity::getRepairInstanceId, repairInstanceId)
.eq(DmsFaultInstanceActivity::getProcessStepOrder, 2L);
Long existingCount = dmsFaultInstanceActivityMapper.selectCount(checkWrapper);
if (existingCount > 0) {
log.warn("工单已经开始维修无需重复操作repairInstanceId: {}", repairInstanceId);
return 1; // 返回成功,避免重复操作
}
```
### 3. 异常处理机制
**问题**:当查询返回多条记录时,系统直接抛出异常
**解决方案**
- 添加异常捕获和备用查询方案
- 提供降级处理机制
```java
try {
// 主查询方案
DmsBillsFaultInstanceVo dmsBillsFaultInstance = baseMapper.selectFaultInstanceJoinFirstAndDeviceById(repairInstanceId);
return dmsBillsFaultInstance;
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().contains("Expected one result")) {
log.warn("检测到查询返回多条记录异常尝试备用查询方案repairInstanceId: {}", repairInstanceId);
// 备用方案:分别查询工单基本信息和最新活动记录
DmsBillsFaultInstanceVo workOrder = baseMapper.selectVoById(repairInstanceId);
// 查询最新的活动记录并合并数据
// ...
return workOrder;
}
throw new ServiceException("查询工单详情失败:" + e.getMessage());
}
```
### 4. 数据清理建议
如果数据库中已经存在重复或异常数据,建议执行以下清理操作:
```sql
-- 1. 查找重复的活动记录
SELECT repair_instance_id, process_step_order, COUNT(*) as count
FROM dms_fault_instance_activity
GROUP BY repair_instance_id, process_step_order
HAVING COUNT(*) > 1;
-- 2. 保留最新的记录,删除重复记录(谨慎操作,建议先备份)
-- 这里需要根据具体情况制定清理策略
```
### 5. 外协ID字段映射修复
**问题**PDA端传递`outsrcId`(int),后端期望`outsourcingId`(Long)
**解决方案**
1. 在`DmsBillsFaultInstanceBo`中添加`outsrcId`字段用于接收PDA端数据
2. 在`insertByBo`方法中实现字段转换
3. 在返回数据时进行反向转换确保PDA端兼容性
```java
// 在DmsBillsFaultInstanceBo中添加字段
private Integer outsrcId; // PDA端传递的外协ID
// 在insertByBo方法中转换
if (bo.getOutsrcId() != null && bo.getOutsrcId() > 0) {
bo.setOutsourcingId(Long.valueOf(bo.getOutsrcId()));
}
```
### 6. Web端功能简化
**问题**Web端和PDA端功能重复造成用户困惑
**解决方案**
1. **PDA端职责**:工单创建、维修执行、信息录入
2. **Web端职责**:审批决策、质量确认
3. **简化流程**PDA创建工单 → Web审批 → PDA维修 → Web确认 → 工单完成
#### Web端修改内容
- 移除工单创建和编辑功能
- 所有工单信息设为只读显示
- 专注于审批和确认功能
### 7. 业务流程重新设计
#### 优化后的流程:
```
1. PDA端创建工单
├── 录入故障信息
├── 上传故障图片
└── 提交审批
2. Web端审批
├── 查看工单详情(只读)
├── 审批决策(通过/拒绝)
└── 推送通知
3. PDA端维修执行
├── 接收审批通知
├── 开始维修
├── 记录维修过程
├── 更换零部件记录
├── 上传维修图片
└── 完成维修
4. Web端质量确认
├── 查看维修结果(只读)
├── 确认维修质量
└── 工单完成
```
## 技术实现细节
### 字段映射关系
| PDA端字段 | 后端字段 | 数据类型转换 | 说明 |
|----------|---------|-------------|------|
| outsrcId | outsourcingId | int → Long | 外协单位ID |
| deviceId | machineId | int → Long | 设备ID |
### 关键代码修改点
1. **DmsBillsFaultInstanceServiceImpl.java**
- 添加outsrcId到outsourcingId转换逻辑
- 增加防重复检查机制
- 添加异常处理和备用查询方案
2. **DmsBillsFaultInstanceMapper.xml**
- 优化查询逻辑,确保返回唯一记录
3. **Web端Vue页面**
- 简化功能,移除创建编辑逻辑
- 改为只读显示模式
### 监控和日志
- 添加详细的调试日志跟踪outsourcingId的传递过程
- 记录查询异常和备用方案的使用情况
- 监控重复操作的发生频率
## 预期效果
1. ✅ 解决SQL查询返回多条记录的异常
2. ✅ 修复外协ID字段映射问题
3. ✅ 简化Web端功能避免重复录入
4. ✅ 明确各端职责分工
5. ✅ 保持PDA代码完全不变
6. ✅ 提高系统稳定性和用户体验
7. ✅ 增强异常处理能力
8. ✅ 提供数据清理和恢复机制
## 风险控制
1. **数据安全**:所有数据库操作都在事务中执行
2. **向下兼容**保持PDA端接口不变
3. **降级处理**:提供备用查询方案
4. **监控告警**:记录异常情况便于排查
5. **回滚机制**:保留原有逻辑作为备用方案
## 后续优化建议
1. 考虑添加数据库索引优化查询性能
2. 实现自动数据清理机制
3. 添加更完善的监控和告警系统
4. 考虑引入分布式锁防止并发问题
5. 优化工作流程,减少不必要的步骤

@ -142,7 +142,7 @@
<script setup name="DmsKnowledgeLube" lang="ts"> <script setup name="DmsKnowledgeLube" lang="ts">
import { listDmsKnowledgeLube, getDmsKnowledgeLube, delDmsKnowledgeLube, addDmsKnowledgeLube, updateDmsKnowledgeLube } from '@/api/dms/dmsKnowledgeLube'; import { listDmsKnowledgeLube, getDmsKnowledgeLube, delDmsKnowledgeLube, addDmsKnowledgeLube, updateDmsKnowledgeLube } from '@/api/dms/dmsKnowledgeLube';
import { DmsKnowledgeLubeVO, DmsKnowledgeLubeQuery, DmsKnowledgeLubeForm } from '@/api/dms/dmsKnowledgeLube/types'; import { DmsKnowledgeLubeVO, DmsKnowledgeLubeQuery, DmsKnowledgeLubeForm } from '@/api/dms/dmsKnowledgeLube/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
import {getDmsBaseLubeStationList} from "@/api/dms/dmsBaseLubeStation"; import {getDmsBaseLubeStationList} from "@/api/dms/dmsBaseLubeStation";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -320,7 +320,7 @@ const handleExport = () => {
// //
let deviceTypeOptions = ref([]); let deviceTypeOptions = ref([]);
const getBaseDeviceTypeOption = async () => { const getBaseDeviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -142,7 +142,7 @@
<script setup name="DmsKnowledgeMaint" lang="ts"> <script setup name="DmsKnowledgeMaint" lang="ts">
import { listDmsKnowledgeMaint, getDmsKnowledgeMaint, delDmsKnowledgeMaint, addDmsKnowledgeMaint, updateDmsKnowledgeMaint } from '@/api/dms/dmsKnowledgeMaint'; import { listDmsKnowledgeMaint, getDmsKnowledgeMaint, delDmsKnowledgeMaint, addDmsKnowledgeMaint, updateDmsKnowledgeMaint } from '@/api/dms/dmsKnowledgeMaint';
import { DmsKnowledgeMaintVO, DmsKnowledgeMaintQuery, DmsKnowledgeMaintForm } from '@/api/dms/dmsKnowledgeMaint/types'; import { DmsKnowledgeMaintVO, DmsKnowledgeMaintQuery, DmsKnowledgeMaintForm } from '@/api/dms/dmsKnowledgeMaint/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
import {getDmsBaseMaintStationList} from "@/api/dms/dmsBaseMaintStation"; import {getDmsBaseMaintStationList} from "@/api/dms/dmsBaseMaintStation";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -320,7 +320,7 @@ const handleExport = () => {
// //
let deviceTypeOptions = ref([]); let deviceTypeOptions = ref([]);
const getBaseDeviceTypeOption = async () => { const getBaseDeviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -133,7 +133,7 @@
<script setup name="DmsKnowledgeRepair" lang="ts"> <script setup name="DmsKnowledgeRepair" lang="ts">
import { listDmsKnowledgeRepair, getDmsKnowledgeRepair, delDmsKnowledgeRepair, addDmsKnowledgeRepair, updateDmsKnowledgeRepair } from '@/api/dms/dmsKnowledgeRepair'; import { listDmsKnowledgeRepair, getDmsKnowledgeRepair, delDmsKnowledgeRepair, addDmsKnowledgeRepair, updateDmsKnowledgeRepair } from '@/api/dms/dmsKnowledgeRepair';
import { DmsKnowledgeRepairVO, DmsKnowledgeRepairQuery, DmsKnowledgeRepairForm } from '@/api/dms/dmsKnowledgeRepair/types'; import { DmsKnowledgeRepairVO, DmsKnowledgeRepairQuery, DmsKnowledgeRepairForm } from '@/api/dms/dmsKnowledgeRepair/types';
import {getBaseDeviceTypeListInDMS} from "@/api/dms/baseDeviceType"; import {getBaseDeviceTypeList} from "@/api/dms/baseDeviceType";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); const { active_flag } = toRefs<any>(proxy?.useDict('active_flag'));
@ -306,7 +306,7 @@ const handleExport = () => {
// //
let deviceTypeOptions = ref([]); let deviceTypeOptions = ref([]);
const getBaseDeviceTypeOption = async () => { const getBaseDeviceTypeOption = async () => {
const res = await getBaseDeviceTypeListInDMS(null); const res = await getBaseDeviceTypeList(null);
deviceTypeOptions.value = res.data; deviceTypeOptions.value = res.data;
} }

@ -58,12 +58,12 @@
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <!-- <el-col :span="1.5">-->
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['dms:dmsRecordShutDown:add']"></el-button> <!-- <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['dms:dmsRecordShutDown:add']"></el-button>-->
</el-col> <!-- </el-col>-->
<el-col :span="1.5"> <!-- <el-col :span="1.5">-->
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['dms:dmsRecordShutDown:edit']"></el-button> <!-- <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['dms:dmsRecordShutDown:edit']"></el-button>-->
</el-col> <!-- </el-col>-->
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['dms:dmsRecordShutDown:remove']"></el-button> <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['dms:dmsRecordShutDown:remove']"></el-button>
</el-col> </el-col>
@ -78,8 +78,8 @@
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键标识" align="center" prop="recordShutDownId" v-if="columns[0].visible"/> <el-table-column label="主键标识" align="center" prop="recordShutDownId" v-if="columns[0].visible"/>
<el-table-column label="设备" align="center" prop="machineName" v-if="columns[2].visible"/> <el-table-column label="设备" align="center" prop="machineName" v-if="columns[2].visible"/>
<el-table-column label="停机类型" align="center" prop="shutTypeId" v-if="columns[3].visible"/> <el-table-column label="停机类型" align="center" prop="shutTypeName" v-if="columns[3].visible"/>
<el-table-column label="停机原因" align="center" prop="shutReasonId" v-if="columns[4].visible"/> <el-table-column label="停机原因" align="center" prop="shutReasonName" v-if="columns[4].visible"/>
<el-table-column label="停机开始时间" align="center" prop="shutBeginTime" width="180" v-if="columns[5].visible"> <el-table-column label="停机开始时间" align="center" prop="shutBeginTime" width="180" v-if="columns[5].visible">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.shutBeginTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span> <span>{{ parseTime(scope.row.shutBeginTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
@ -90,8 +90,17 @@
<span>{{ parseTime(scope.row.shutEndTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span> <span>{{ parseTime(scope.row.shutEndTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="停机时长(毫秒)" align="center" prop="shutTime" v-if="columns[7].visible"/> <!-- <el-table-column label="停机时长(毫秒)" align="center" prop="shutTime" v-if="columns[7].visible"/>-->
<el-table-column label="停机标识" align="center" prop="downtimeFlag" v-if="columns[8].visible"/> <el-table-column label="停机时长" align="center" prop="shutTime" v-if="columns[7].visible">
<template #default="scope">
<span>{{ formatDuration(scope.row.shutTime) }}</span>
</template>
</el-table-column>
<el-table-column label="停机标识" align="center" prop="downtimeFlag" v-if="columns[8].visible">
<template #default="scope">
<dict-tag :options="downtime_flag" :value="scope.row.downtimeFlag"/>
</template>
</el-table-column>
<el-table-column label="停机原因" align="center" prop="shutReason" v-if="columns[9].visible"/> <el-table-column label="停机原因" align="center" prop="shutReason" v-if="columns[9].visible"/>
<el-table-column label="激活标识" align="center" prop="activeFlag" v-if="columns[10].visible"> <el-table-column label="激活标识" align="center" prop="activeFlag" v-if="columns[10].visible">
<template #default="scope"> <template #default="scope">
@ -173,7 +182,14 @@
<el-input v-model="form.shutTime" placeholder="请输入停机时长(毫秒)" /> <el-input v-model="form.shutTime" placeholder="请输入停机时长(毫秒)" />
</el-form-item> </el-form-item>
<el-form-item label="停机标识" prop="downtimeFlag"> <el-form-item label="停机标识" prop="downtimeFlag">
<el-input v-model="form.downtimeFlag" placeholder="请输入停机标识" /> <!-- <el-input v-model="form.downtimeFlag" placeholder="请输入停机标识" />-->
<el-radio-group v-model="form.downtimeFlag">
<el-radio
v-for="dict in downtime_flag"
:key="dict.value"
:value="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="激活标识" prop="activeFlag"> <el-form-item label="激活标识" prop="activeFlag">
<el-radio-group v-model="form.activeFlag"> <el-radio-group v-model="form.activeFlag">
@ -206,7 +222,7 @@ import {getDmsBaseShutTypeList} from "@/api/dms/dmsBaseShutType";
import {getDmsBaseShutReasonList} from "@/api/dms/dmsBaseShutReason"; import {getDmsBaseShutReasonList} from "@/api/dms/dmsBaseShutReason";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); const { active_flag, downtime_flag } = toRefs<any>(proxy?.useDict('active_flag', 'downtime_flag'));
const dmsRecordShutDownList = ref<DmsRecordShutDownVO[]>([]); const dmsRecordShutDownList = ref<DmsRecordShutDownVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
@ -231,7 +247,7 @@ const columns = ref<FieldOption[]>([
{ key: 1, label: `租户编号`, visible: false }, { key: 1, label: `租户编号`, visible: false },
{ key: 2, label: `设备`, visible: true }, { key: 2, label: `设备`, visible: true },
{ key: 3, label: `停机类型`, visible: true }, { key: 3, label: `停机类型`, visible: true },
{ key: 4, label: `停机原因id`, visible: false }, { key: 4, label: `停机原因`, visible: true },
{ key: 5, label: `停机开始时间`, visible: true }, { key: 5, label: `停机开始时间`, visible: true },
{ key: 6, label: `停机结束时间`, visible: true }, { key: 6, label: `停机结束时间`, visible: true },
{ key: 7, label: `停机时长(毫秒)`, visible: true }, { key: 7, label: `停机时长(毫秒)`, visible: true },
@ -260,6 +276,7 @@ const initFormData: DmsRecordShutDownForm = {
remark: undefined, remark: undefined,
machineName: undefined, machineName: undefined,
shutTypeName: undefined, shutTypeName: undefined,
shutReasonNameName: undefined,
} }
const data = reactive<PageData<DmsRecordShutDownForm, DmsRecordShutDownQuery>>({ const data = reactive<PageData<DmsRecordShutDownForm, DmsRecordShutDownQuery>>({
@ -279,6 +296,7 @@ const data = reactive<PageData<DmsRecordShutDownForm, DmsRecordShutDownQuery>>({
activeFlag: undefined, activeFlag: undefined,
machineName: undefined, machineName: undefined,
shutTypeName: undefined, shutTypeName: undefined,
shutReasonNameName: undefined,
params: { params: {
} }
}, },
@ -413,6 +431,14 @@ const handleShutReasonChange = (shutReasonId: number) => {
form.value.shutTypeId = selectedReason?.shutTypeId || ''; // form.value.shutTypeId = selectedReason?.shutTypeId || ''; //
}; };
const formatDuration = (millis: number): string => {
if (!millis || millis <= 0) return '0秒'
const hours = Math.floor(millis / 3600000)
const minutes = Math.floor((millis % 3600000) / 60000)
const seconds = Math.floor((millis % 60000) / 1000)
return `${hours ? hours + '小时' : ''}${minutes ? minutes + '分' : ''}${seconds}`
}
onMounted(() => { onMounted(() => {
getMachineOtions(); getMachineOtions();
getShutTypeOtions(); getShutTypeOtions();

@ -91,12 +91,12 @@
</el-button> </el-button>
</el-button-group> </el-button-group>
</div> </div>
<div class="toolbar-right"> <!-- <div class="toolbar-right">-->
<el-button type="primary" @click="exportData"> <!-- <el-button type="primary" @click="exportData">-->
<el-icon><Download /></el-icon> <!-- <el-icon><Download /></el-icon>-->
导出数据 <!-- 导出数据-->
</el-button> <!-- </el-button>-->
</div> <!-- </div>-->
</div> </div>
<!-- 查询过滤器 --> <!-- 查询过滤器 -->
@ -385,15 +385,15 @@
<el-col :span="6"> <el-col :span="6">
<el-statistic title="计划保养次数" :value="maintenanceStats.planned" /> <el-statistic title="计划保养次数" :value="maintenanceStats.planned" />
</el-col> </el-col>
<el-col :span="6"> <!-- <el-col :span="6">-->
<el-statistic title="临时保养次数" :value="maintenanceStats.temporary" /> <!-- <el-statistic title="临时保养次数" :value="maintenanceStats.temporary" />-->
</el-col> <!-- </el-col>-->
<el-col :span="6"> <!-- <el-col :span="6">-->
<el-statistic title="平均保养间隔" :value="maintenanceStats.avgInterval" suffix="天" /> <!-- <el-statistic title="平均保养间隔" :value="maintenanceStats.avgInterval" suffix="天" />-->
</el-col> <!-- </el-col>-->
<el-col :span="6"> <!-- <el-col :span="6">-->
<el-statistic title="下次保养时间" :value="maintenanceStats.nextDate || '未计划'" /> <!-- <el-statistic title="下次保养时间" :value="maintenanceStats.nextDate || '未计划'" />-->
</el-col> <!-- </el-col>-->
</el-row> </el-row>
</el-card> </el-card>
</el-col> </el-col>
@ -408,13 +408,13 @@
<el-table-column prop="maintDate" label="保养日期" /> <el-table-column prop="maintDate" label="保养日期" />
<el-table-column prop="maintPerson" label="保养人员" /> <el-table-column prop="maintPerson" label="保养人员" />
<el-table-column prop="maintContent" label="保养内容" show-overflow-tooltip /> <el-table-column prop="maintContent" label="保养内容" show-overflow-tooltip />
<el-table-column prop="maintResult" label="保养结果"> <!-- <el-table-column prop="maintResult" label="保养结果">-->
<template #default="scope"> <!-- <template #default="scope">-->
<el-tag :type="scope.row.maintResult === '合格' ? 'success' : 'warning'"> <!-- <el-tag :type="scope.row.maintResult === '合格' ? 'success' : 'warning'">-->
{{ scope.row.maintResult }} <!-- {{ scope.row.maintResult }}-->
</el-tag> <!-- </el-tag>-->
</template> <!-- </template>-->
</el-table-column> <!-- </el-table-column>-->
</el-table> </el-table>
</el-card> </el-card>
</el-col> </el-col>
@ -426,10 +426,10 @@
</div> </div>
</template> </template>
<script setup name="ProdBaseMachineInfo"> <script setup name="ProdBaseMachineInfo" lang="ts">
import { ref, reactive, toRefs, getCurrentInstance, onMounted, onUnmounted, shallowRef, markRaw, watch } from 'vue'; import { ref, reactive, toRefs, getCurrentInstance, onMounted, onUnmounted, shallowRef, markRaw, watch } from 'vue';
import { listDmsBaseMachineInfoIn, getDmsBaseMachineInfoList, getDmsBaseMachineInfo } from '@/api/dms/dmsBaseMachineInfo'; import { listDmsBaseMachineInfo, getDmsBaseMachineInfoList, getDmsBaseMachineInfo } from '@/api/dms/dmsBaseMachineInfo';
import { listBaseDeviceTypeInDMS, getBaseDeviceTypeListInDMS } from '@/api/dms/baseDeviceType'; import { listBaseDeviceType, getBaseDeviceTypeList } from '@/api/dms/baseDeviceType';
import { listDmsBaseDevicePurchase, getDmsBaseDevicePurchaseList } from '@/api/dms/dmsBaseDevicePurchase'; import { listDmsBaseDevicePurchase, getDmsBaseDevicePurchaseList } from '@/api/dms/dmsBaseDevicePurchase';
import { listDmsBaseDeviceInstall, getDmsBaseDeviceInstallList } from '@/api/dms/dmsBaseDeviceInstall'; import { listDmsBaseDeviceInstall, getDmsBaseDeviceInstallList } from '@/api/dms/dmsBaseDeviceInstall';
import { listDmsBaseDeviceDebugging, getDmsBaseDeviceDebuggingList } from '@/api/dms/dmsBaseDeviceDebugging'; import { listDmsBaseDeviceDebugging, getDmsBaseDeviceDebuggingList } from '@/api/dms/dmsBaseDeviceDebugging';
@ -459,8 +459,8 @@ import {
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
// TODO: // TODO:
const { machine_status, debug_status, fault_status, maint_level, maint_status } = toRefs(proxy.useDict( const { machine_status, debug_status, fault_status, maint_level, maint_status, handle_status, bills_status, active_flag } = toRefs(proxy.useDict(
'machine_status', 'debug_status', 'fault_status', 'maint_level', 'maint_status' 'machine_status', 'debug_status', 'fault_status', 'maint_level', 'maint_status', 'handle_status', 'bills_status', 'active_flag'
)); ));
// TODO: // TODO:
// const { active_flag } = toRefs<any>(proxy?.useDict('active_flag')); // const { active_flag } = toRefs<any>(proxy?.useDict('active_flag'));
@ -489,7 +489,7 @@ const deviceTypes = ref([]);
// //
const queryParams = ref({ const queryParams = ref({
pageNum: 1, pageNum: 1,
pageSize: 12, pageSize: 10,
machineCode: undefined, machineCode: undefined,
machineName: undefined, machineName: undefined,
machineLocation: undefined, machineLocation: undefined,
@ -618,7 +618,7 @@ const updateCurrentTime = () => {
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try { try {
const res = await listDmsBaseMachineInfoIn(queryParams.value); const res = await listDmsBaseMachineInfo(queryParams.value);
machineList.value = res.rows || []; machineList.value = res.rows || [];
total.value = res.total || 0; total.value = res.total || 0;
@ -635,7 +635,7 @@ const getList = async () => {
// //
const getDeviceTypes = async () => { const getDeviceTypes = async () => {
try { try {
const res = await getBaseDeviceTypeListInDMS({ pageNum: 1, pageSize: 100 }); const res = await getBaseDeviceTypeList({ pageNum: 1, pageSize: 100 });
deviceTypes.value = res.data; deviceTypes.value = res.data;
} catch (error) { } catch (error) {
console.error('获取设备类型失败:', error); console.error('获取设备类型失败:', error);
@ -1109,10 +1109,10 @@ const viewDetails = async (machine) => {
]); ]);
}; };
// // //
const exportData = () => { // const exportData = () => {
proxy.$modal.msgInfo('导出功能开发中...'); // proxy.$modal.msgInfo('...');
}; // };
// //
let timer = null; let timer = null;
@ -1227,14 +1227,14 @@ const loadMaintenanceStats = async (machineId) => {
// //
const loadLifecycleTypes = async () => { const loadLifecycleTypes = async () => {
if (!selectedMachine.value) return; if (!selectedMachine.value) return;
const machineId = selectedMachine.value.machineId; const machineId = selectedMachine.value.machineId;
try { try {
// 使API // 使API
const [ const [
purchaseRes, purchaseRes,
installRes, installRes,
debugRes, debugRes,
maintRes, maintRes,
faultRes, faultRes,
@ -1249,7 +1249,7 @@ const loadLifecycleTypes = async () => {
getDmsBillsInspectInstanceList({ machineId }).catch(() => ({ data: [] })), getDmsBillsInspectInstanceList({ machineId }).catch(() => ({ data: [] })),
getBaseAlarmInfoList({ machineId }).catch(() => ({ data: [] })) getBaseAlarmInfoList({ machineId }).catch(() => ({ data: [] }))
]); ]);
// //
lifecycleTypes.value.forEach(type => { lifecycleTypes.value.forEach(type => {
switch (type.value) { switch (type.value) {
@ -1273,7 +1273,7 @@ const loadLifecycleTypes = async () => {
break; break;
} }
}); });
// //
lifecycleStats.value = { lifecycleStats.value = {
faultCount: faultRes.data?.length || 0, faultCount: faultRes.data?.length || 0,
@ -1302,7 +1302,7 @@ const loadLifecycleTypes = async () => {
width: 100%; width: 100%;
height: 60px; height: 60px;
z-index: 0; z-index: 0;
svg { svg {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -1320,7 +1320,7 @@ const loadLifecycleTypes = async () => {
border-radius: 6px; border-radius: 6px;
margin-bottom: 8px; margin-bottom: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.panel-left { .panel-left {
.page-title { .page-title {
font-size: 18px; font-size: 18px;
@ -1331,14 +1331,14 @@ const loadLifecycleTypes = async () => {
align-items: center; align-items: center;
gap: 6px; gap: 6px;
} }
.page-subtitle { .page-subtitle {
font-size: 11px; font-size: 11px;
color: #7f8c8d; color: #7f8c8d;
margin: 1px 0 0 0; margin: 1px 0 0 0;
} }
} }
.panel-right { .panel-right {
.current-time { .current-time {
display: flex; display: flex;
@ -1356,7 +1356,7 @@ const loadLifecycleTypes = async () => {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 8px; gap: 8px;
margin-bottom: 8px; margin-bottom: 8px;
.stat-item { .stat-item {
position: relative; position: relative;
background: #fff; background: #fff;
@ -1365,12 +1365,12 @@ const loadLifecycleTypes = async () => {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease; transition: all 0.3s ease;
overflow: hidden; overflow: hidden;
&:hover { &:hover {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15);
} }
.stat-bg-effect { .stat-bg-effect {
position: absolute; position: absolute;
top: 0; top: 0;
@ -1381,7 +1381,7 @@ const loadLifecycleTypes = async () => {
opacity: 0.1; opacity: 0.1;
transform: translate(25px, -25px); transform: translate(25px, -25px);
} }
.stat-inner { .stat-inner {
position: relative; position: relative;
z-index: 2; z-index: 2;
@ -1389,7 +1389,7 @@ const loadLifecycleTypes = async () => {
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
.stat-icon-wrapper { .stat-icon-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
@ -1399,35 +1399,35 @@ const loadLifecycleTypes = async () => {
border-radius: 6px; border-radius: 6px;
color: #fff; color: #fff;
} }
.stat-info { .stat-info {
flex: 1; flex: 1;
.stat-number { .stat-number {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
gap: 3px; gap: 3px;
.value-animate { .value-animate {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
color: #2c3e50; color: #2c3e50;
} }
.unit { .unit {
font-size: 11px; font-size: 11px;
color: #7f8c8d; color: #7f8c8d;
font-weight: 500; font-weight: 500;
} }
} }
.stat-title { .stat-title {
font-size: 12px; font-size: 12px;
color: #7f8c8d; color: #7f8c8d;
margin-top: 1px; margin-top: 1px;
} }
} }
.stat-chart { .stat-chart {
position: absolute; position: absolute;
bottom: 6px; bottom: 6px;
@ -1435,28 +1435,28 @@ const loadLifecycleTypes = async () => {
width: 50px; width: 50px;
height: 20px; height: 20px;
opacity: 0.3; opacity: 0.3;
svg { svg {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
&.stat-total { &.stat-total {
.stat-icon-wrapper { background: linear-gradient(45deg, #667eea, #764ba2); } .stat-icon-wrapper { background: linear-gradient(45deg, #667eea, #764ba2); }
.stat-bg-effect { background: #667eea; } .stat-bg-effect { background: #667eea; }
} }
&.stat-running { &.stat-running {
.stat-icon-wrapper { background: linear-gradient(45deg, #56ab2f, #a8e6cf); } .stat-icon-wrapper { background: linear-gradient(45deg, #56ab2f, #a8e6cf); }
.stat-bg-effect { background: #56ab2f; } .stat-bg-effect { background: #56ab2f; }
} }
&.stat-maintenance { &.stat-maintenance {
.stat-icon-wrapper { background: linear-gradient(45deg, #f093fb, #f5576c); } .stat-icon-wrapper { background: linear-gradient(45deg, #f093fb, #f5576c); }
.stat-bg-effect { background: #f093fb; } .stat-bg-effect { background: #f093fb; }
} }
&.stat-alarm { &.stat-alarm {
.stat-icon-wrapper { background: linear-gradient(45deg, #fa709a, #fee140); } .stat-icon-wrapper { background: linear-gradient(45deg, #fa709a, #fee140); }
.stat-bg-effect { background: #fa709a; } .stat-bg-effect { background: #fa709a; }
@ -1469,7 +1469,7 @@ const loadLifecycleTypes = async () => {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: 8px;
.toolbar-left, .toolbar-right { .toolbar-left, .toolbar-right {
display: flex; display: flex;
gap: 6px; gap: 6px;
@ -1478,12 +1478,12 @@ const loadLifecycleTypes = async () => {
.search-filter-card { .search-filter-card {
margin-bottom: 8px; margin-bottom: 8px;
.filter-header { .filter-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.filter-title { .filter-title {
display: flex; display: flex;
align-items: center; align-items: center;
@ -1491,11 +1491,11 @@ const loadLifecycleTypes = async () => {
font-weight: 600; font-weight: 600;
} }
} }
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 12px; padding: 12px;
} }
.el-form { .el-form {
.el-form-item { .el-form-item {
margin-bottom: 8px; margin-bottom: 8px;
@ -1510,10 +1510,10 @@ const loadLifecycleTypes = async () => {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 8px; gap: 8px;
} }
.machine-card-wrapper { .machine-card-wrapper {
cursor: pointer; cursor: pointer;
.machine-card { .machine-card {
background: #fff; background: #fff;
border-radius: 4px; border-radius: 4px;
@ -1521,22 +1521,22 @@ const loadLifecycleTypes = async () => {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative; position: relative;
&:hover { &:hover {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
} }
&.selected { &.selected {
border: 2px solid #409EFF; border: 2px solid #409EFF;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3); box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
} }
.status-indicator { .status-indicator {
position: absolute; position: absolute;
top: 6px; top: 6px;
right: 6px; right: 6px;
.status-dot { .status-dot {
display: inline-block; display: inline-block;
width: 5px; width: 5px;
@ -1544,17 +1544,17 @@ const loadLifecycleTypes = async () => {
border-radius: 50%; border-radius: 50%;
animation: pulse 2s infinite; animation: pulse 2s infinite;
} }
&.status-0 .status-dot { background: #f56c6c; } &.status-0 .status-dot { background: #f56c6c; }
&.status-1 .status-dot { background: #67c23a; } &.status-1 .status-dot { background: #67c23a; }
&.status-2 .status-dot { background: #e6a23c; } &.status-2 .status-dot { background: #e6a23c; }
&.status-3 .status-dot { background: #909399; } &.status-3 .status-dot { background: #909399; }
} }
.machine-visual { .machine-visual {
text-align: center; text-align: center;
margin-bottom: 6px; margin-bottom: 6px;
.machine-icon-bg { .machine-icon-bg {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -1566,17 +1566,17 @@ const loadLifecycleTypes = async () => {
color: #fff; color: #fff;
margin-bottom: 4px; margin-bottom: 4px;
} }
.machine-number { .machine-number {
font-size: 11px; font-size: 11px;
font-weight: 600; font-weight: 600;
color: #2c3e50; color: #2c3e50;
} }
} }
.machine-details { .machine-details {
margin-bottom: 6px; margin-bottom: 6px;
.machine-name { .machine-name {
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
@ -1588,7 +1588,7 @@ const loadLifecycleTypes = async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.machine-meta { .machine-meta {
.meta-item { .meta-item {
display: flex; display: flex;
@ -1600,19 +1600,19 @@ const loadLifecycleTypes = async () => {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
} }
} }
.quick-actions { .quick-actions {
display: flex; display: flex;
gap: 4px; gap: 4px;
justify-content: center; justify-content: center;
.el-button { .el-button {
padding: 2px 6px; padding: 2px 6px;
font-size: 10px; font-size: 10px;
@ -1622,35 +1622,35 @@ const loadLifecycleTypes = async () => {
} }
} }
} }
.list-view { .list-view {
.status-cell { .status-cell {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
.status-badge { .status-badge {
display: inline-block; display: inline-block;
width: 5px; width: 5px;
height: 5px; height: 5px;
border-radius: 50%; border-radius: 50%;
&.status-0 { background: #f56c6c; } &.status-0 { background: #f56c6c; }
&.status-1 { background: #67c23a; } &.status-1 { background: #67c23a; }
&.status-2 { background: #e6a23c; } &.status-2 { background: #e6a23c; }
&.status-3 { background: #909399; } &.status-3 { background: #909399; }
} }
} }
.date-text { .date-text {
font-size: 12px; font-size: 12px;
color: #606266; color: #606266;
} }
:deep(.danger-row) { :deep(.danger-row) {
background-color: rgba(245, 108, 108, 0.05); background-color: rgba(245, 108, 108, 0.05);
} }
:deep(.warning-row) { :deep(.warning-row) {
background-color: rgba(230, 162, 60, 0.05); background-color: rgba(230, 162, 60, 0.05);
} }
@ -1670,12 +1670,12 @@ const loadLifecycleTypes = async () => {
padding: 12px; padding: 12px;
margin-bottom: 12px; margin-bottom: 12px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
.overview-header { .overview-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
.device-avatar { .device-avatar {
display: flex; display: flex;
align-items: center; align-items: center;
@ -1686,14 +1686,14 @@ const loadLifecycleTypes = async () => {
border-radius: 10px; border-radius: 10px;
color: #fff; color: #fff;
} }
.device-basic { .device-basic {
h2 { h2 {
margin: 0 0 3px 0; margin: 0 0 3px 0;
font-size: 16px; font-size: 16px;
color: #2c3e50; color: #2c3e50;
} }
p { p {
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
@ -1701,22 +1701,22 @@ const loadLifecycleTypes = async () => {
} }
} }
} }
.overview-stats { .overview-stats {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
gap: 10px; gap: 10px;
.stat { .stat {
text-align: center; text-align: center;
.stat-label { .stat-label {
display: block; display: block;
font-size: 11px; font-size: 11px;
color: #7f8c8d; color: #7f8c8d;
margin-bottom: 3px; margin-bottom: 3px;
} }
.stat-value { .stat-value {
display: block; display: block;
font-size: 18px; font-size: 18px;
@ -1726,21 +1726,21 @@ const loadLifecycleTypes = async () => {
} }
} }
} }
.lifecycle-type-selector { .lifecycle-type-selector {
margin-bottom: 12px; margin-bottom: 12px;
h3 { h3 {
margin: 0 0 10px 0; margin: 0 0 10px 0;
font-size: 15px; font-size: 15px;
color: #2c3e50; color: #2c3e50;
} }
.type-cards { .type-cards {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 6px; gap: 6px;
.type-card { .type-card {
background: #fff; background: #fff;
border: 2px solid #e1e6f0; border: 2px solid #e1e6f0;
@ -1750,16 +1750,16 @@ const loadLifecycleTypes = async () => {
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative; position: relative;
&:hover { &:hover {
border-color: #409EFF; border-color: #409EFF;
} }
&.active { &.active {
border-color: #409EFF; border-color: #409EFF;
background: #f0f9ff; background: #f0f9ff;
} }
.type-icon { .type-icon {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -1770,14 +1770,14 @@ const loadLifecycleTypes = async () => {
margin-bottom: 5px; margin-bottom: 5px;
color: #fff; color: #fff;
} }
.type-name { .type-name {
display: block; display: block;
font-size: 11px; font-size: 11px;
color: #2c3e50; color: #2c3e50;
font-weight: 500; font-weight: 500;
} }
.type-count { .type-count {
position: absolute; position: absolute;
top: -3px; top: -3px;
@ -1793,28 +1793,28 @@ const loadLifecycleTypes = async () => {
} }
} }
} }
.lifecycle-timeline-container { .lifecycle-timeline-container {
h3 { h3 {
margin: 0 0 10px 0; margin: 0 0 10px 0;
font-size: 15px; font-size: 15px;
color: #2c3e50; color: #2c3e50;
} }
.timeline-wrapper { .timeline-wrapper {
position: relative; position: relative;
.timeline-item { .timeline-item {
position: relative; position: relative;
padding-left: 35px; padding-left: 35px;
padding-bottom: 16px; padding-bottom: 16px;
&:last-child { &:last-child {
.timeline-line { .timeline-line {
display: none; display: none;
} }
} }
.timeline-marker { .timeline-marker {
position: absolute; position: absolute;
left: 0; left: 0;
@ -1828,47 +1828,47 @@ const loadLifecycleTypes = async () => {
color: #fff; color: #fff;
z-index: 2; z-index: 2;
} }
.timeline-content { .timeline-content {
background: #fff; background: #fff;
border-radius: 6px; border-radius: 6px;
padding: 10px; padding: 10px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
.event-header { .event-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 5px; margin-bottom: 5px;
h4 { h4 {
margin: 0; margin: 0;
font-size: 13px; font-size: 13px;
color: #2c3e50; color: #2c3e50;
} }
.event-time { .event-time {
font-size: 11px; font-size: 11px;
color: #7f8c8d; color: #7f8c8d;
} }
} }
.event-description { .event-description {
font-size: 12px; font-size: 12px;
color: #606266; color: #606266;
margin: 0 0 6px 0; margin: 0 0 6px 0;
} }
.event-tags { .event-tags {
margin-bottom: 6px; margin-bottom: 6px;
.el-tag { .el-tag {
margin-right: 3px; margin-right: 3px;
margin-bottom: 3px; margin-bottom: 3px;
} }
} }
} }
.timeline-line { .timeline-line {
position: absolute; position: absolute;
left: 13px; left: 13px;
@ -1888,7 +1888,7 @@ const loadLifecycleTypes = async () => {
font-weight: 600; font-weight: 600;
color: #2c3e50; color: #2c3e50;
} }
.maintenance-section { .maintenance-section {
.maintenance-summary { .maintenance-summary {
margin-bottom: 12px; margin-bottom: 12px;
@ -1899,7 +1899,7 @@ const loadLifecycleTypes = async () => {
.filter-btn-primary { .filter-btn-primary {
background: linear-gradient(45deg, #667eea, #764ba2); background: linear-gradient(45deg, #667eea, #764ba2);
border: none; border: none;
&:hover { &:hover {
background: linear-gradient(45deg, #5a6fd8, #6a4190); background: linear-gradient(45deg, #5a6fd8, #6a4190);
} }
@ -1910,12 +1910,12 @@ const loadLifecycleTypes = async () => {
transform: scale(0.95); transform: scale(0.95);
box-shadow: 0 0 0 0 currentColor; box-shadow: 0 0 0 0 currentColor;
} }
70% { 70% {
transform: scale(1); transform: scale(1);
box-shadow: 0 0 0 10px transparent; box-shadow: 0 0 0 10px transparent;
} }
100% { 100% {
transform: scale(0.95); transform: scale(0.95);
box-shadow: 0 0 0 0 transparent; box-shadow: 0 0 0 0 transparent;

Loading…
Cancel
Save