| | |
| | | {{ formatters(scope.row[item.prop], item.formatData) }} |
| | | </el-tag> |
| | | </div> |
| | | <!-- 时间类型 --> |
| | | <div v-else-if="item.dataType === 'date'"> |
| | | {{ scope.row[item.prop] ? dayjs(scope.row[item.prop]).format('YYYY-MM-DD') : '' }} |
| | | </div> |
| | | <!-- 按钮 --> |
| | | <div v-else-if="item.dataType == 'action'" |
| | | @click.stop> |
| | |
| | | import pagination from "./Pagination.vue"; |
| | | import { computed, ref, inject, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // 获取全局的 uploadHeader |
| | | const { proxy } = getCurrentInstance(); |
| | |
| | | { |
| | | label: "创建时间", |
| | | prop: "createTime", |
| | | dataType:"date" |
| | | }, |
| | | { |
| | | label: "操作", |
| | |
| | | const formCreateTimeDate = computed({ |
| | | get: () => (formData.createTime ? String(formData.createTime).split(" ")[0] : ""), |
| | | set: (value) => { |
| | | formData.createTime = value ? `${value} ${dayjs().format("HH:mm:ss")}` : ""; |
| | | formData.createTime = value ? dayjs(value).format("YYYY-MM-DD") : ""; |
| | | }, |
| | | }); |
| | | // const productTypes = ref([]); |
| | |
| | | label: "创建时间", |
| | | prop: "createTime", |
| | | width: 180, |
| | | dataType:"date" |
| | | }, |
| | | { |
| | | dataType: "action", |
| | |
| | | }, |
| | | { label: "版本", prop: "version", width: 120 }, |
| | | { label: "发布人", prop: "createUserName", width: 120 }, |
| | | { label: "发布时间", prop: "createTime", width: 180 }, |
| | | { label: "发布时间", prop: "createTime", width: 180,dataType: "date" }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | |
| | | formatData: (v) => getSealTypeText(v), |
| | | formatType: () => 'info' |
| | | }, |
| | | { label: '申请时间', prop: 'createTime', width: 180 }, |
| | | { label: '申请时间', prop: 'createTime', width: 180,dataType: "date" }, |
| | | { |
| | | label: '状态', |
| | | prop: 'status', |
| | |
| | | <template> |
| | | <div class="spare-part-category"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
| | | <el-tab-pane label="备件列表" name="list"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="queryParams" class="search-form"> |
| | | <el-form-item label="备件名称"> |
| | | <el-input |
| | | v-model="queryParams.name" |
| | | placeholder="请输入备件名称" |
| | | clearable |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">查询</el-button> |
| | | <el-button @click="resetQuery">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="addCategory">新增</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="spare-part-category"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
| | | <el-tab-pane label="备件列表" name="list"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="queryParams" class="search-form"> |
| | | <el-form-item label="备件名称"> |
| | | <el-input |
| | | v-model="queryParams.name" |
| | | placeholder="请输入备件名称" |
| | | clearable |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">查询</el-button> |
| | | <el-button @click="resetQuery">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="addCategory">新增</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog title="分类管理" v-model="dialogVisible" width="60%"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-form-item label="设备" prop="deviceLedgerIds"> |
| | | <el-select |
| | | v-model="form.deviceLedgerIds" |
| | | placeholder="请选择设备" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" |
| | | multiple |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="(item, index) in deviceOptions" |
| | | :key="index" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备件名称" prop="name"> |
| | | <el-input v-model="form.name"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="备件编号" prop="sparePartsNo"> |
| | | <el-input v-model="form.sparePartsNo"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="数量" prop="quantity"> |
| | | <el-input type="number" v-model="form.quantity"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态"> |
| | | <el-option label="正常" value="正常"></el-option> |
| | | <el-option label="禁用" value="禁用"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-input v-model="form.description"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="价格" prop="price"> |
| | | <el-input-number |
| | | v-model="form.price" |
| | | placeholder="请输入价格" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | ></el-input-number> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | |
| | | <el-dialog title="分类管理" v-model="dialogVisible" width="60%"> |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="100px"> |
| | | <el-form-item label="设备" prop="deviceLedgerIds"> |
| | | <el-select |
| | | v-model="form.deviceLedgerIds" |
| | | placeholder="请选择设备" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" |
| | | multiple |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="(item, index) in deviceOptions" |
| | | :key="index" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备件名称" prop="name"> |
| | | <el-input v-model="form.name"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="备件编号" prop="sparePartsNo"> |
| | | <el-input v-model="form.sparePartsNo"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="数量" prop="quantity"> |
| | | <el-input type="number" v-model="form.quantity"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-select v-model="form.status" placeholder="请选择状态"> |
| | | <el-option label="正常" value="正常"></el-option> |
| | | <el-option label="禁用" value="禁用"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="描述" prop="description"> |
| | | <el-input v-model="form.description"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="价格" prop="price"> |
| | | <el-input-number |
| | | v-model="form.price" |
| | | placeholder="请输入价格" |
| | | :min="0" |
| | | :step="0.01" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | ></el-input-number> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button> |
| | | <el-button @click="dialogVisible = false" :disabled="formLoading">取消</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="备件领用记录" name="usage"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="usageQuery" class="search-form"> |
| | | <el-form-item label="备件名称"> |
| | | <el-input v-model="usageQuery.sparePartsName" placeholder="请输入备件名称" clearable style="width: 240px" /> |
| | | </el-form-item> |
| | | <el-form-item label="来源"> |
| | | <el-select v-model="usageQuery.sourceType" placeholder="请选择" clearable style="width: 200px"> |
| | | <el-option label="维修" :value="0" /> |
| | | <el-option label="保养" :value="1" /> |
| | | <el-option label="直接出库" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleUsageQuery">查询</el-button> |
| | | <el-button @click="resetUsageQuery">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" @click="openRequisitionDialog">直接出库</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="备件领用记录" name="usage"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="usageQuery" class="search-form"> |
| | | <el-form-item label="备件名称"> |
| | | <el-input v-model="usageQuery.sparePartsName" placeholder="请输入备件名称" clearable style="width: 240px" /> |
| | | </el-form-item> |
| | | <el-form-item label="来源"> |
| | | <el-select v-model="usageQuery.sourceType" placeholder="请选择" clearable style="width: 200px"> |
| | | <el-option label="维修" :value="0" /> |
| | | <el-option label="保养" :value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleUsageQuery">查询</el-button> |
| | | <el-button @click="resetUsageQuery">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="rowKey" |
| | |
| | | @pagination="handleUsagePageChange" |
| | | /> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | |
| | | <!-- 直接出库弹窗 --> |
| | | <el-dialog title="直接出库" v-model="requisitionDialogVisible" width="500px"> |
| | | <el-form :model="requisitionForm" :rules="requisitionRules" ref="requisitionFormRef" label-width="100px"> |
| | | <el-form-item label="设备选择" prop="deviceLedgerId"> |
| | | <el-select |
| | | v-model="requisitionForm.deviceLedgerId" |
| | | placeholder="请选择设备" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in requisitionDeviceOptions" |
| | | :key="item.id" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="备件选择" prop="sparePartsId"> |
| | | <el-select |
| | | v-model="requisitionForm.sparePartsId" |
| | | placeholder="请选择备件" |
| | | filterable |
| | | clearable |
| | | style="width: 100%" |
| | | @change="onRequisitionSparePartChange" |
| | | > |
| | | <el-option |
| | | v-for="item in requisitionSparePartsOptions" |
| | | :key="item.id" |
| | | :label="`${item.name}(库存: ${item.quantity})`" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="领用数量" prop="quantity"> |
| | | <el-input-number |
| | | v-model="requisitionForm.quantity" |
| | | :min="1" |
| | | :max="selectedSparePartMaxQty" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input v-model="requisitionForm.remark" placeholder="请输入备注" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button type="primary" @click="submitRequisition" :loading="requisitionLoading">确定</el-button> |
| | | <el-button @click="requisitionDialogVisible = false">取消</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import { getSparePartsUsagePage, outboundSpareParts } from "@/api/equipmentManagement/sparePartsUsage"; |
| | | import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage"; |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false); |
| | |
| | | const formRef = ref(null); |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | name: '' |
| | | name: '' |
| | | }); |
| | | // 分页参数 |
| | | const pagination = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | }); |
| | | |
| | | // 备件领用记录 |
| | | const usageLoading = ref(false); |
| | | const usageQuery = reactive({ |
| | | sparePartsName: "", |
| | | sourceType: "", |
| | | sparePartsName: "", |
| | | sourceType: "", |
| | | }); |
| | | const usagePagination = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | const usageTableData = ref([]); |
| | | const usageColumns = ref([ |
| | | { label: "来源", prop: "sourceText" }, |
| | | { label: "单据/记录ID", prop: "sourceId" }, |
| | | { label: "设备名称", prop: "deviceName" }, |
| | | { label: "备件名称", prop: "sparePartsName" }, |
| | | { label: "领用数量", prop: "quantity" }, |
| | | { label: "操作人", prop: "operator" }, |
| | | { label: "时间", prop: "createTime" }, |
| | | { label: "来源", prop: "sourceText" }, |
| | | { label: "单据/记录ID", prop: "sourceId" }, |
| | | { label: "设备名称", prop: "deviceName" }, |
| | | { label: "备件名称", prop: "sparePartsName" }, |
| | | { label: "领用数量", prop: "quantity" }, |
| | | { label: "操作人", prop: "operator" }, |
| | | { label: "时间", prop: "createTime",dataType: "date"}, |
| | | ]); |
| | | |
| | | const handleTabChange = async (name) => { |
| | | if (name === "usage") { |
| | | usagePagination.current = 1; |
| | | await fetchUsageData(); |
| | | } |
| | | if (name === "usage") { |
| | | usagePagination.current = 1; |
| | | await fetchUsageData(); |
| | | } |
| | | }; |
| | | const columns = ref([ |
| | | { |
| | | label: "设备名称", |
| | | prop: "deviceNameStr", |
| | | }, |
| | | { |
| | | label: "备件名称", |
| | | prop: "name", |
| | | }, |
| | | { |
| | | label: "备件编号", |
| | | prop: "sparePartsNo", |
| | | }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | | slot: "status", |
| | | dataType: "slot", |
| | | }, |
| | | { |
| | | label: "价格", |
| | | prop: "price", |
| | | }, |
| | | { |
| | | label: "数量", |
| | | prop: "quantity", |
| | | }, |
| | | { |
| | | label: "描述", |
| | | prop: "description", |
| | | }, |
| | | { |
| | | label: "操作", |
| | | prop: "operation", |
| | | width: 150, |
| | | fixed: 'right', |
| | | align: "center", |
| | | dataType: "action", |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | clickFun: (row) => { |
| | | editCategory(row) |
| | | }, |
| | | }, |
| | | { |
| | | name: "删除", |
| | | clickFun: (row) => { |
| | | deleteCategory(row.id) |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | label: "设备名称", |
| | | prop: "deviceNameStr", |
| | | }, |
| | | { |
| | | label: "备件名称", |
| | | prop: "name", |
| | | }, |
| | | { |
| | | label: "备件编号", |
| | | prop: "sparePartsNo", |
| | | }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | | slot: "status", |
| | | dataType: "slot", |
| | | }, |
| | | { |
| | | label: "价格", |
| | | prop: "price", |
| | | }, |
| | | { |
| | | label: "数量", |
| | | prop: "quantity", |
| | | }, |
| | | { |
| | | label: "描述", |
| | | prop: "description", |
| | | }, |
| | | { |
| | | label: "操作", |
| | | prop: "operation", |
| | | width: 150, |
| | | fixed: 'right', |
| | | align: "center", |
| | | dataType: "action", |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | clickFun: (row) => { |
| | | editCategory(row) |
| | | }, |
| | | }, |
| | | { |
| | | name: "删除", |
| | | clickFun: (row) => { |
| | | deleteCategory(row.id) |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | // 表单数据 |
| | | const form = reactive({ |
| | | id:'', |
| | | name: '', |
| | | sparePartsNo: '', |
| | | status: '', |
| | | description: '', |
| | | deviceLedgerIds: [], |
| | | price: null |
| | | id:'', |
| | | name: '', |
| | | sparePartsNo: '', |
| | | status: '', |
| | | description: '', |
| | | deviceLedgerIds: [], |
| | | price: null |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = reactive({ |
| | | name: [ |
| | | { required: true, message: '请输入备件名称', trigger: 'blur' } |
| | | ], |
| | | sparePartsNo: [ |
| | | { required: true, message: '请输入备件编号', trigger: 'blur' } |
| | | ], |
| | | quantity:[ |
| | | { required: true, message: '请输入数量', trigger: 'blur' } |
| | | ], |
| | | status: [ |
| | | { required: true, message: '请选择状态', trigger: 'change' } |
| | | ], |
| | | deviceLedgerIds: [ |
| | | { |
| | | required: true, |
| | | message: '请选择设备', |
| | | trigger: 'change', |
| | | validator: (rule, value, callback) => { |
| | | if (operationType.value === 'add' && (!value || value.length === 0)) { |
| | | callback(new Error('请选择设备')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | name: [ |
| | | { required: true, message: '请输入备件名称', trigger: 'blur' } |
| | | ], |
| | | sparePartsNo: [ |
| | | { required: true, message: '请输入备件编号', trigger: 'blur' } |
| | | ], |
| | | quantity:[ |
| | | { required: true, message: '请输入数量', trigger: 'blur' } |
| | | ], |
| | | status: [ |
| | | { required: true, message: '请选择状态', trigger: 'change' } |
| | | ], |
| | | deviceLedgerIds: [ |
| | | { |
| | | required: true, |
| | | message: '请选择设备', |
| | | trigger: 'change', |
| | | validator: (rule, value, callback) => { |
| | | if (operationType.value === 'add' && (!value || value.length === 0)) { |
| | | callback(new Error('请选择设备')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | }); |
| | | // 获取缩进量 |
| | | const getIndentation = (row) => { |
| | | // 这里简单返回 20,可根据实际需求实现层级缩进逻辑 |
| | | return 20; |
| | | // 这里简单返回 20,可根据实际需求实现层级缩进逻辑 |
| | | return 20; |
| | | }; |
| | | // 定义 buildTree 函数 |
| | | const buildTree = (flatData) => { |
| | | const map = {}; |
| | | const result = []; |
| | | if(flatData){ |
| | | return result; |
| | | } |
| | | flatData.forEach(item => { |
| | | map[item.id] = { ...item, children: [] }; |
| | | }); |
| | | flatData.forEach(item => { |
| | | if (item.parentId === null || !map[item.parentId]) { |
| | | result.push(map[item.id]); |
| | | } else { |
| | | map[item.parentId].children.push(map[item.id]); |
| | | } |
| | | }); |
| | | return result; |
| | | const map = {}; |
| | | const result = []; |
| | | if(flatData){ |
| | | return result; |
| | | } |
| | | flatData.forEach(item => { |
| | | map[item.id] = { ...item, children: [] }; |
| | | }); |
| | | flatData.forEach(item => { |
| | | if (item.parentId === null || !map[item.parentId]) { |
| | | result.push(map[item.id]); |
| | | } else { |
| | | map[item.parentId].children.push(map[item.id]); |
| | | } |
| | | }); |
| | | return result; |
| | | }; |
| | | // 获取列表数据 |
| | | const fetchListData = async () => { |
| | | loading.value = true; |
| | | try { |
| | | const params = { |
| | | current: pagination.current, |
| | | size: pagination.size |
| | | }; |
| | | if (queryParams.name) { |
| | | params.name = queryParams.name; |
| | | } |
| | | const res = await getSparePartsList(params); |
| | | if (res.code === 200) { |
| | | renderTableData.value = res.data.records || []; |
| | | categories.value = res.data.records || []; |
| | | pagination.total = res.data.total || 0; |
| | | } |
| | | } catch (error) { |
| | | loading.value = true; |
| | | try { |
| | | const params = { |
| | | current: pagination.current, |
| | | size: pagination.size |
| | | }; |
| | | if (queryParams.name) { |
| | | params.name = queryParams.name; |
| | | } |
| | | const res = await getSparePartsList(params); |
| | | if (res.code === 200) { |
| | | renderTableData.value = res.data.records || []; |
| | | categories.value = res.data.records || []; |
| | | pagination.total = res.data.total || 0; |
| | | } |
| | | } catch (error) { |
| | | loading.value = false; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | const fetchUsageData = async () => { |
| | | usageLoading.value = true; |
| | | try { |
| | | const res = await getSparePartsUsagePage({ |
| | | current: usagePagination.current, |
| | | size: usagePagination.size, |
| | | sparePartsName: usageQuery.sparePartsName || undefined, |
| | | sourceType: usageQuery.sourceType || undefined, |
| | | }); |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | usagePagination.total = res?.data?.total || 0; |
| | | usageTableData.value = records.map((r, idx) => ({ |
| | | rowKey: r.id ?? `${usagePagination.current}-${idx}`, |
| | | ...r, |
| | | sourceText: r.sourceText === "" ? "-" : r.sourceText, |
| | | })); |
| | | } else { |
| | | usagePagination.total = 0; |
| | | usageTableData.value = []; |
| | | } |
| | | } finally { |
| | | usageLoading.value = false; |
| | | } |
| | | usageLoading.value = true; |
| | | try { |
| | | const res = await getSparePartsUsagePage({ |
| | | current: usagePagination.current, |
| | | size: usagePagination.size, |
| | | sparePartsName: usageQuery.sparePartsName || undefined, |
| | | sourceType: usageQuery.sourceType || undefined, |
| | | }); |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | usagePagination.total = res?.data?.total || 0; |
| | | usageTableData.value = records.map((r, idx) => ({ |
| | | rowKey: r.id ?? `${usagePagination.current}-${idx}`, |
| | | ...r, |
| | | sourceText: r.sourceText === "" ? "-" : r.sourceText, |
| | | })); |
| | | } else { |
| | | usagePagination.total = 0; |
| | | usageTableData.value = []; |
| | | } |
| | | } finally { |
| | | usageLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleUsageQuery = () => { |
| | | usagePagination.current = 1; |
| | | fetchUsageData(); |
| | | usagePagination.current = 1; |
| | | fetchUsageData(); |
| | | }; |
| | | const resetUsageQuery = () => { |
| | | usageQuery.sparePartsName = ""; |
| | | usageQuery.sourceType = ""; |
| | | usagePagination.current = 1; |
| | | fetchUsageData(); |
| | | usageQuery.sparePartsName = ""; |
| | | usageQuery.sourceType = ""; |
| | | usagePagination.current = 1; |
| | | fetchUsageData(); |
| | | }; |
| | | const handleUsagePageChange = (obj) => { |
| | | usagePagination.current = obj.page; |
| | | usagePagination.size = obj.limit; |
| | | fetchUsageData(); |
| | | usagePagination.current = obj.page; |
| | | usagePagination.size = obj.limit; |
| | | fetchUsageData(); |
| | | }; |
| | | |
| | | // 查询 |
| | | const handleQuery = () => { |
| | | pagination.current = 1; |
| | | fetchListData(); |
| | | pagination.current = 1; |
| | | fetchListData(); |
| | | } |
| | | |
| | | // 重置查询 |
| | | const resetQuery = () => { |
| | | queryParams.name = ''; |
| | | pagination.current = 1; |
| | | fetchListData(); |
| | | queryParams.name = ''; |
| | | pagination.current = 1; |
| | | pagination.size = 10; |
| | | fetchListData(); |
| | | } |
| | | |
| | | // 分页变化 |
| | | // 分页大小改变 |
| | | const handleSizeChange = ({ page, limit }) => { |
| | | pagination.current = page; |
| | | pagination.size = limit; |
| | | fetchListData(); |
| | | pagination.current = page; |
| | | pagination.size = limit; |
| | | fetchListData(); |
| | | } |
| | | |
| | | // 当前页改变 |
| | | const handleCurrentChange = (current) => { |
| | | pagination.current = current; |
| | | fetchListData(); |
| | | } |
| | | |
| | | // 加载设备列表(在打开弹框时调用) |
| | | const loadDeviceName = async () => { |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (error) { |
| | | ElMessage.error('获取设备列表失败'); |
| | | } |
| | | try { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data || []; |
| | | } catch (error) { |
| | | ElMessage.error('获取设备列表失败'); |
| | | } |
| | | }; |
| | | |
| | | // 新增分类 |
| | | const addCategory = async () => { |
| | | await loadDeviceName(); |
| | | form.id = ''; |
| | | form.name = ''; |
| | | form.sparePartsNo = ''; |
| | | form.status = ''; |
| | | form.description = ''; |
| | | form.deviceLedgerIds = []; |
| | | form.quantity = undefined; |
| | | form.price = null; |
| | | operationType.value = 'add' |
| | | dialogVisible.value = true; |
| | | await loadDeviceName(); |
| | | form.id = ''; |
| | | form.name = ''; |
| | | form.sparePartsNo = ''; |
| | | form.status = ''; |
| | | form.description = ''; |
| | | form.deviceLedgerIds = []; |
| | | form.quantity = undefined; |
| | | form.price = null; |
| | | operationType.value = 'add' |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // 编辑分类 |
| | | const editCategory = async (row) => { |
| | | await loadDeviceName(); |
| | | Object.assign(form, row); |
| | | // 如果后端返回的是 deviceIds 字符串,需要转换为数组 |
| | | if (row.deviceIds && typeof row.deviceIds === 'string') { |
| | | // 确保ID类型与设备选项中的ID类型一致 |
| | | const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id); |
| | | // 如果设备选项中的ID是数字类型,则转换为数字 |
| | | if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') { |
| | | form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id)); |
| | | } else { |
| | | form.deviceLedgerIds = deviceIdsArray; |
| | | } |
| | | } else if (row.deviceIds && Array.isArray(row.deviceIds)) { |
| | | form.deviceLedgerIds = row.deviceIds; |
| | | } else { |
| | | form.deviceLedgerIds = []; |
| | | } |
| | | operationType.value = 'edit' |
| | | dialogVisible.value = true; |
| | | await loadDeviceName(); |
| | | Object.assign(form, row); |
| | | // 如果后端返回的是 deviceIds 字符串,需要转换为数组 |
| | | if (row.deviceIds && typeof row.deviceIds === 'string') { |
| | | // 确保ID类型与设备选项中的ID类型一致 |
| | | const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id); |
| | | // 如果设备选项中的ID是数字类型,则转换为数字 |
| | | if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') { |
| | | form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id)); |
| | | } else { |
| | | form.deviceLedgerIds = deviceIdsArray; |
| | | } |
| | | } else if (row.deviceIds && Array.isArray(row.deviceIds)) { |
| | | form.deviceLedgerIds = row.deviceIds; |
| | | } else { |
| | | form.deviceLedgerIds = []; |
| | | } |
| | | operationType.value = 'edit' |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // 删除分类 |
| | | const deleteCategory = async (id) => { |
| | | try { |
| | | await ElMessageBox.confirm('此操作将永久删除该分类,是否继续?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }); |
| | | loading.value = true; |
| | | const res = await delSparePart(id); |
| | | if (res.code === 200) { |
| | | ElMessage.success('删除成功'); |
| | | fetchListData(); |
| | | } else { |
| | | ElMessage.error(res.message || '删除失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error('删除失败'); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm('此操作将永久删除该分类,是否继续?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }); |
| | | loading.value = true; |
| | | const res = await delSparePart(id); |
| | | if (res.code === 200) { |
| | | ElMessage.success('删除成功'); |
| | | fetchListData(); |
| | | } else { |
| | | ElMessage.error(res.message || '删除失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error('删除失败'); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = async () => { |
| | | if (!formRef.value) return; |
| | | try { |
| | | await formRef.value.validate(); |
| | | formLoading.value = true; |
| | | |
| | | // 构建提交数据 |
| | | const submitData = { |
| | | ...form, |
| | | deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0 |
| | | ? form.deviceLedgerIds.join(',') |
| | | : '' |
| | | }; |
| | | |
| | | // 删除不需要的字段 |
| | | delete submitData.deviceLedgerIds; |
| | | |
| | | if (operationType.value === 'edit') { |
| | | let res = await editSparePart(submitData); |
| | | if (res.code === 200) { |
| | | ElMessage.success('编辑成功'); |
| | | dialogVisible.value = false; |
| | | fetchListData(); |
| | | } |
| | | } else { |
| | | let res = await addSparePart(submitData); |
| | | if (res.code === 200) { |
| | | ElMessage.success('新增成功'); |
| | | dialogVisible.value = false; |
| | | fetchListData(); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('请填写完整表单信息'); |
| | | } finally { |
| | | formLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 直接出库 |
| | | const requisitionDialogVisible = ref(false); |
| | | const requisitionLoading = ref(false); |
| | | const requisitionFormRef = ref(null); |
| | | const requisitionDeviceOptions = ref([]); |
| | | const requisitionSparePartsOptions = ref([]); |
| | | const selectedSparePartMaxQty = ref(1); |
| | | const requisitionForm = reactive({ |
| | | deviceLedgerId: null, |
| | | sparePartsId: null, |
| | | quantity: 1, |
| | | remark: '', |
| | | }); |
| | | const requisitionRules = { |
| | | sparePartsId: [{ required: true, message: '请选择备件', trigger: 'change' }], |
| | | quantity: [{ required: true, message: '请输入领用数量', trigger: 'blur' }], |
| | | }; |
| | | |
| | | const loadRequisitionOptions = async () => { |
| | | try { |
| | | const [deviceRes, spareRes] = await Promise.all([ |
| | | getDeviceLedger(), |
| | | getSparePartsList({ current: 1, size: 9999 }), |
| | | ]); |
| | | requisitionDeviceOptions.value = deviceRes.data || []; |
| | | requisitionSparePartsOptions.value = spareRes.data?.records || []; |
| | | } catch (e) { |
| | | ElMessage.error('加载选项失败'); |
| | | } |
| | | }; |
| | | |
| | | const openRequisitionDialog = async () => { |
| | | requisitionForm.deviceLedgerId = null; |
| | | requisitionForm.sparePartsId = null; |
| | | requisitionForm.quantity = 1; |
| | | requisitionForm.remark = ''; |
| | | selectedSparePartMaxQty.value = 1; |
| | | await loadRequisitionOptions(); |
| | | requisitionDialogVisible.value = true; |
| | | }; |
| | | |
| | | const onRequisitionSparePartChange = (val) => { |
| | | const selected = requisitionSparePartsOptions.value.find(item => item.id === val); |
| | | selectedSparePartMaxQty.value = selected ? selected.quantity : 1; |
| | | if (requisitionForm.quantity > selectedSparePartMaxQty.value) { |
| | | requisitionForm.quantity = selectedSparePartMaxQty.value; |
| | | } |
| | | }; |
| | | |
| | | const submitRequisition = async () => { |
| | | if (!requisitionFormRef.value) return; |
| | | try { |
| | | await requisitionFormRef.value.validate(); |
| | | } catch { |
| | | return; |
| | | } |
| | | if (requisitionForm.quantity <= 0) { |
| | | ElMessage.warning('领用数量必须大于0'); |
| | | return; |
| | | } |
| | | const selected = requisitionSparePartsOptions.value.find(item => item.id === requisitionForm.sparePartsId); |
| | | if (selected && requisitionForm.quantity > selected.quantity) { |
| | | ElMessage.warning(`库存不足,当前库存为 ${selected.quantity}`); |
| | | return; |
| | | } |
| | | requisitionLoading.value = true; |
| | | try { |
| | | const res = await outboundSpareParts({ |
| | | deviceLedgerId: requisitionForm.deviceLedgerId || undefined, |
| | | sparePartsId: requisitionForm.sparePartsId, |
| | | quantity: requisitionForm.quantity, |
| | | remark: requisitionForm.remark || undefined, |
| | | }); |
| | | if (res.code === 200) { |
| | | ElMessage.success('领用成功'); |
| | | requisitionDialogVisible.value = false; |
| | | fetchUsageData(); |
| | | } else { |
| | | ElMessage.error(res.msg || '领用失败'); |
| | | } |
| | | } catch { |
| | | ElMessage.error('领用失败'); |
| | | } finally { |
| | | requisitionLoading.value = false; |
| | | } |
| | | if (!formRef.value) return; |
| | | try { |
| | | await formRef.value.validate(); |
| | | formLoading.value = true; |
| | | |
| | | // 构建提交数据 |
| | | const submitData = { |
| | | ...form, |
| | | deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0 |
| | | ? form.deviceLedgerIds.join(',') |
| | | : '' |
| | | }; |
| | | |
| | | // 删除不需要的字段 |
| | | delete submitData.deviceLedgerIds; |
| | | |
| | | if (operationType.value === 'edit') { |
| | | let res = await editSparePart(submitData); |
| | | if (res.code === 200) { |
| | | ElMessage.success('编辑成功'); |
| | | dialogVisible.value = false; |
| | | fetchListData(); |
| | | } |
| | | } else { |
| | | let res = await addSparePart(submitData); |
| | | if (res.code === 200) { |
| | | ElMessage.success('新增成功'); |
| | | dialogVisible.value = false; |
| | | fetchListData(); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('请填写完整表单信息'); |
| | | } finally { |
| | | formLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 组件挂载时获取列表数据 |
| | | onMounted(() => { |
| | | fetchListData(); |
| | | fetchListData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .spare-part-category { |
| | | padding: 20px; |
| | | padding: 20px; |
| | | } |
| | | .search_form { |
| | | display: flex; |
| | |
| | | justify-content: space-between; |
| | | } |
| | | .table_list { |
| | | margin-top: unset; |
| | | margin-top: unset; |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .el-table__header-wrapper th { |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .el-table__row:hover > td { |
| | | background-color: #fafafa; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | /* 按钮组样式 */ |
| | | .actions > div { |
| | | display: flex; |
| | | gap: 10px; |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* 确保表格中的操作按钮不会被截断 */ |
| | | .el-table-column--fixed-right .el-button { |
| | | margin: 0 2px; |
| | | margin: 0 2px; |
| | | } |
| | | |
| | | /* 树形节点内容样式 */ |
| | | .nested-tree .el-tree-node__expand-icon { |
| | | font-size: 12px; |
| | | margin-right: 4px; |
| | | font-size: 12px; |
| | | margin-right: 4px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-tabs v-model="activeTab" |
| | | @tab-change="handleTabChange"> |
| | | <!-- 保养任务tab --> |
| | | <el-tab-pane label="保养任务" |
| | | name="scheduled"> |
| | | <div class="search_form"> |
| | | <el-form :model="scheduledFilters" |
| | | :inline="true"> |
| | | <el-form-item label="任务名称"> |
| | | <el-input v-model="scheduledFilters.taskName" |
| | | style="width: 240px" |
| | | placeholder="请输入任务名称" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getScheduledTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="任务状态"> |
| | | <el-select v-model="scheduledFilters.status" |
| | | placeholder="请选择任务状态" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option label="启用" |
| | | value="1" /> |
| | | <el-option label="停用" |
| | | value="0" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getScheduledTableData">搜索</el-button> |
| | | <el-button @click="resetScheduledFilters">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" |
| | | size="large">保养任务</el-text> |
| | | <div> |
| | | <el-button type="primary" |
| | | icon="Plus" |
| | | @click="addScheduledTask"> |
| | | 新增任务 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="scheduledMultipleList.length <= 0" |
| | | @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))"> |
| | | 批量删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | isSelection |
| | | :column="scheduledColumns" |
| | | :tableData="scheduledDataList" |
| | | :page="{ |
| | | <div class="app-container"> |
| | | <el-tabs v-model="activeTab" |
| | | @tab-change="handleTabChange"> |
| | | <!-- 保养任务tab --> |
| | | <el-tab-pane label="保养任务" |
| | | name="scheduled"> |
| | | <div class="search_form"> |
| | | <el-form :model="scheduledFilters" |
| | | :inline="true"> |
| | | <el-form-item label="任务名称"> |
| | | <el-input v-model="scheduledFilters.taskName" |
| | | style="width: 240px" |
| | | placeholder="请输入任务名称" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getScheduledTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="任务状态"> |
| | | <el-select v-model="scheduledFilters.status" |
| | | placeholder="请选择任务状态" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option label="启用" |
| | | value="1" /> |
| | | <el-option label="停用" |
| | | value="0" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getScheduledTableData">搜索</el-button> |
| | | <el-button @click="resetScheduledFilters">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" |
| | | size="large">保养任务</el-text> |
| | | <div> |
| | | <el-button type="primary" |
| | | icon="Plus" |
| | | @click="addScheduledTask"> |
| | | 新增任务 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="scheduledMultipleList.length <= 0" |
| | | @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))"> |
| | | 批量删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | isSelection |
| | | :column="scheduledColumns" |
| | | :tableData="scheduledDataList" |
| | | :page="{ |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | total: scheduledPagination.total, |
| | | }" |
| | | @selection-change="handleScheduledSelectionChange" |
| | | @pagination="changeScheduledPage"> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">启用</el-tag> |
| | | <el-tag v-if="row.status === 0" |
| | | type="danger">停用</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="editScheduledTask(row)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="delScheduledTaskByIds(row.id)"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-tab-pane> |
| | | <!-- 保养记录tab(原设备保养页面) --> |
| | | <el-tab-pane label="保养记录" |
| | | name="record"> |
| | | <div class="search_form"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="设备名称"> |
| | | <el-input v-model="filters.deviceName" |
| | | style="width: 240px" |
| | | placeholder="请输入设备名称" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划保养日期"> |
| | | <el-date-picker v-model="filters.maintenancePlanTime" |
| | | type="date" |
| | | placeholder="请选择计划保养日期" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,2)" /> |
| | | </el-form-item> |
| | | <el-form-item label="实际保养日期"> |
| | | <el-date-picker v-model="filters.maintenanceActuallyTime" |
| | | type="date" |
| | | placeholder="请选择实际保养日期" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,1)" /> |
| | | </el-form-item> |
| | | <el-form-item label="实际保养人"> |
| | | <el-input v-model="filters.maintenanceActuallyName" |
| | | style="width: 240px" |
| | | placeholder="请输入实际保养人" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getTableData">搜索</el-button> |
| | | <el-button @click="resetFilters">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" |
| | | size="large">保养记录</el-text> |
| | | <div> |
| | | <el-button type="success" |
| | | icon="Van" |
| | | @click="addPlan"> |
| | | 新增计划 |
| | | </el-button> |
| | | <el-button @click="handleOut"> |
| | | 导出 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0" |
| | | @click="delRepairByIds(multipleList.map((item) => item.id))"> |
| | | 批量删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-table |
| | | :data="groupedDataList" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.date" |
| | | @expand-change="onExpandChange" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table :data="props.row.children" border row-key="id"> |
| | | <el-table-column label="设备名称" prop="deviceName" align="center" /> |
| | | <el-table-column label="规格型号" prop="deviceModel" align="center" /> |
| | | <el-table-column label="录入人" prop="createUserName" align="center" /> |
| | | <el-table-column label="保养项目" prop="machineryCategory" align="center"> |
| | | <template #default="{ row }"> |
| | | {{ row.machineryCategory || '--' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="实际保养人" prop="maintenanceActuallyName" align="center" /> |
| | | <el-table-column label="实际保养日期" align="center"> |
| | | <template #default="{ row }"> |
| | | {{ row.maintenanceActuallyTime ? dayjs(row.maintenanceActuallyTime).format('YYYY-MM-DD HH:mm:ss') : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="保养结果" align="center"> |
| | | <template #default="{ row }"> |
| | | {{ row.maintenanceResult || '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="状态" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag v-if="row.status === 2" type="danger">失败</el-tag> |
| | | <el-tag v-if="row.status === 1" type="success">完结</el-tag> |
| | | <el-tag v-if="row.status === 0" type="warning">待保养</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="300"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" link :disabled="row.status === 1" @click="editPlan(row.id)">编辑</el-button> |
| | | <el-button type="success" link :disabled="row.status !== 0" @click="handleSingleComplete(row.id)">保养完成</el-button> |
| | | <el-button type="success" link :disabled="row.status === 1" @click="addMaintain(row)">保养</el-button> |
| | | <el-button type="danger" link :disabled="row.status === 1" @click="delRepairByIds(row.id)">删除</el-button> |
| | | <el-button type="primary" link @click="openFileDialog(row)">附件</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计划保养日期" prop="date" align="center" /> |
| | | <el-table-column label="保养记录数" prop="count" align="center" /> |
| | | <el-table-column label="待保养数" align="center" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ row.children.filter(c => c.status === 0).length }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200"> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | type="primary" |
| | | size="small" |
| | | :disabled="!row.children.some(c => c.status === 0)" |
| | | @click="handleBatchComplete(row.date)" |
| | | > |
| | | 一键保养完成 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <Pagination |
| | | v-show="pagination.total > 0" |
| | | :total="pagination.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="pagination.currentPage" |
| | | :limit="pagination.pageSize" |
| | | @pagination="changePage" |
| | | /> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <PlanModal ref="planModalRef" |
| | | @ok="getTableData" /> |
| | | <MaintenanceModal ref="maintainModalRef" |
| | | @ok="getTableData" /> |
| | | <FormDia ref="formDiaRef" |
| | | @closeDia="getScheduledTableData" /> |
| | | <FileList v-if="fileDialogVisible" |
| | | v-model:visible="fileDialogVisible" |
| | | :record-type="'device_maintenance'" |
| | | :record-id="currentMaintenanceTaskId" /> |
| | | |
| | | </div> |
| | | @selection-change="handleScheduledSelectionChange" |
| | | @pagination="changeScheduledPage"> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">启用</el-tag> |
| | | <el-tag v-if="row.status === 0" |
| | | type="danger">停用</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="editScheduledTask(row)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | @click="delScheduledTaskByIds(row.id)"> |
| | | 删除 |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-tab-pane> |
| | | <!-- 保养记录tab(原设备保养页面) --> |
| | | <el-tab-pane label="保养记录" |
| | | name="record"> |
| | | <div class="search_form"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="设备名称"> |
| | | <el-input v-model="filters.deviceName" |
| | | style="width: 240px" |
| | | placeholder="请输入设备名称" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item label="计划保养日期"> |
| | | <el-date-picker v-model="filters.maintenancePlanTime" |
| | | type="date" |
| | | placeholder="请选择计划保养日期" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,2)" /> |
| | | </el-form-item> |
| | | <el-form-item label="实际保养日期"> |
| | | <el-date-picker v-model="filters.maintenanceActuallyTime" |
| | | type="date" |
| | | placeholder="请选择实际保养日期" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,1)" /> |
| | | </el-form-item> |
| | | <el-form-item label="实际保养人"> |
| | | <el-input v-model="filters.maintenanceActuallyName" |
| | | style="width: 240px" |
| | | placeholder="请输入实际保养人" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="getTableData">搜索</el-button> |
| | | <el-button @click="resetFilters">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" |
| | | size="large">保养记录</el-text> |
| | | <div> |
| | | <el-button type="success" |
| | | icon="Van" |
| | | @click="addPlan"> |
| | | 新增计划 |
| | | </el-button> |
| | | <el-button @click="handleOut"> |
| | | 导出 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0 || hasFinishedStatus" |
| | | @click="delRepairByIds(multipleList.map((item) => item.id))"> |
| | | 批量删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage"> |
| | | <template #maintenanceResultRef="{ row }"> |
| | | <div>{{ row.maintenanceResult || '-' }}</div> |
| | | </template> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 2" |
| | | type="danger">失败</el-tag> |
| | | <el-tag v-if="row.status === 1" |
| | | type="success">完结</el-tag> |
| | | <el-tag v-if="row.status === 0" |
| | | type="warning">待保养</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <!-- 这个功能跟新增保养功能一模一样,有啥意义? --> |
| | | <!-- <el-button |
| | | type="primary" |
| | | text |
| | | @click="addMaintain(row)" |
| | | > |
| | | 新增保养 |
| | | </el-button> --> |
| | | <el-button type="primary" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="editPlan(row.id)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button type="success" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="addMaintain(row)"> |
| | | 保养 |
| | | </el-button> |
| | | <el-button type="danger" |
| | | link |
| | | :disabled="row.status === 1" |
| | | @click="delRepairByIds(row.id)"> |
| | | 删除 |
| | | </el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openFileDialog(row)"> |
| | | 附件 |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <PlanModal ref="planModalRef" |
| | | @ok="getTableData" /> |
| | | <MaintenanceModal ref="maintainModalRef" |
| | | @ok="getTableData" /> |
| | | <FormDia ref="formDiaRef" |
| | | @closeDia="getScheduledTableData" /> |
| | | <FileList v-if="fileDialogVisible" |
| | | v-model:visible="fileDialogVisible" |
| | | :record-type="'device_maintenance'" |
| | | :record-id="currentMaintenanceTaskId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | reactive, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | computed, |
| | | defineAsyncComponent, |
| | | } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PlanModal from "./Form/PlanModal.vue"; |
| | | import MaintenanceModal from "./Form/MaintenanceModal.vue"; |
| | | import FormDia from "./Form/formDia.vue"; |
| | | import { |
| | | getUpkeepPage, |
| | | delUpkeep, |
| | | deviceMaintenanceTaskList, |
| | | deviceMaintenanceTaskDel, |
| | | batchCompleteMaintenance, |
| | | completeMaintenance, |
| | | } from "@/api/equipmentManagement/upkeep"; |
| | | import dayjs from "dayjs"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { |
| | | ref, |
| | | onMounted, |
| | | reactive, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | computed, |
| | | defineAsyncComponent, |
| | | } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import PlanModal from "./Form/PlanModal.vue"; |
| | | import MaintenanceModal from "./Form/MaintenanceModal.vue"; |
| | | import FormDia from "./Form/formDia.vue"; |
| | | import { |
| | | getUpkeepPage, |
| | | delUpkeep, |
| | | deviceMaintenanceTaskList, |
| | | deviceMaintenanceTaskDel, |
| | | } from "@/api/equipmentManagement/upkeep"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | const { proxy } = getCurrentInstance(); |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | |
| | | // Tab相关 |
| | | const activeTab = ref("scheduled"); |
| | | // Tab相关 |
| | | const activeTab = ref("scheduled"); |
| | | |
| | | // 计划弹窗控制器 |
| | | const planModalRef = ref(); |
| | | // 保养弹窗控制器 |
| | | const maintainModalRef = ref(); |
| | | // 定时任务弹窗控制器 |
| | | const formDiaRef = ref(); |
| | | // 附件弹窗 |
| | | const fileListDialogRef = ref(null); |
| | | const fileDialogVisible = ref(false); |
| | | const currentMaintenanceTaskId = ref(null); |
| | | // 计划弹窗控制器 |
| | | const planModalRef = ref(); |
| | | // 保养弹窗控制器 |
| | | const maintainModalRef = ref(); |
| | | // 定时任务弹窗控制器 |
| | | const formDiaRef = ref(); |
| | | // 附件弹窗 |
| | | const fileListDialogRef = ref(null); |
| | | const fileDialogVisible = ref(false); |
| | | const currentMaintenanceTaskId = ref(null); |
| | | |
| | | // 保养记录tab(原设备保养页面)相关变量 |
| | | const filters = reactive({ |
| | | deviceName: "", |
| | | maintenancePlanTime: "", |
| | | maintenanceActuallyTime: "", |
| | | maintenanceActuallyName: "", |
| | | }); |
| | | // 保养记录tab(原设备保养页面)相关变量 |
| | | const filters = reactive({ |
| | | deviceName: "", |
| | | maintenancePlanTime: "", |
| | | maintenanceActuallyTime: "", |
| | | maintenanceActuallyName: "", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 100, |
| | | total: 0, |
| | | }); |
| | | const multipleList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const expandedRowKeys = ref([]); |
| | | const dataList = ref([]); |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | const multipleList = ref([]); |
| | | |
| | | const groupByDate = (list) => { |
| | | const map = {}; |
| | | list.forEach(item => { |
| | | const date = item.dateStr || (item.maintenancePlanTime ? dayjs(item.maintenancePlanTime).format('YYYY-MM-DD') : (item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD') : '未知日期')); |
| | | if (!map[date]) map[date] = []; |
| | | map[date].push(item); |
| | | }); |
| | | return Object.entries(map).map(([date, children]) => ({ date, children, count: children.length })); |
| | | }; |
| | | // 保养任务tab相关变量 |
| | | const scheduledFilters = reactive({ |
| | | taskName: "", |
| | | status: "", |
| | | }); |
| | | |
| | | const groupedDataList = computed(() => groupByDate(dataList.value)); |
| | | const scheduledDataList = ref([]); |
| | | const scheduledPagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | const scheduledMultipleList = ref([]); |
| | | |
| | | // 单条保养完成 |
| | | const handleSingleComplete = async (id) => { |
| | | try { |
| | | await ElMessageBox.confirm('确认将该保养记录标记为完成?', '提示', { type: 'warning' }); |
| | | const res = await completeMaintenance(id); |
| | | if (res.code === 200) { |
| | | ElMessage.success('保养完成'); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || '操作失败'); |
| | | } |
| | | } catch { /* 取消 */ } |
| | | }; |
| | | // 保养任务表格列配置 |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设备名称" }, |
| | | { |
| | | label: "规格型号", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "保养项目", |
| | | prop: "machineryCategory", |
| | | minWidth: 120, |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | | label: "频次", |
| | | minWidth: 150, |
| | | // PIMTable 使用的是 formatData,而不是 Element-Plus 的 formatter |
| | | formatData: cell => |
| | | ({ |
| | | DAILY: "每日", |
| | | WEEKLY: "每周", |
| | | MONTHLY: "每月", |
| | | QUARTERLY: "季度", |
| | | }[cell] || ""), |
| | | }, |
| | | { |
| | | prop: "frequencyDetail", |
| | | label: "开始日期与时间", |
| | | minWidth: 150, |
| | | // 同样改用 formatData,PIMTable 内部会把单元格值传进来 |
| | | formatData: cell => { |
| | | if (typeof cell !== "string") return ""; |
| | | let val = cell; |
| | | const replacements = { |
| | | MON: "周一", |
| | | TUE: "周二", |
| | | WED: "周三", |
| | | THU: "周四", |
| | | FRI: "周五", |
| | | SAT: "周六", |
| | | SUN: "周日", |
| | | }; |
| | | // 使用正则一次性替换所有匹配项 |
| | | return val.replace( |
| | | /MON|TUE|WED|THU|FRI|SAT|SUN/g, |
| | | match => replacements[match] |
| | | ); |
| | | }, |
| | | }, |
| | | { prop: "maintenancePerson", label: "保养人", minWidth: 100 }, |
| | | { prop: "registrant", label: "登记人", minWidth: 100 }, |
| | | { |
| | | prop: "registrationDate", |
| | | label: "登记日期", |
| | | minWidth: 100, |
| | | formatData: cell => |
| | | // cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | cell ? dayjs(cell).format("YYYY-MM-DD") : "-", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "操作", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | ]); |
| | | |
| | | // 一键保养完成 |
| | | const handleBatchComplete = async (dateStr) => { |
| | | try { |
| | | await ElMessageBox.confirm(`确认将 ${dateStr} 的所有待保养记录标记为完成?`, '提示', { type: 'warning' }); |
| | | const res = await batchCompleteMaintenance(dateStr); |
| | | if (res.code === 200) { |
| | | ElMessage.success(res.msg || '批量保养完成成功'); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || '批量保养完成失败'); |
| | | } |
| | | } catch { /* 取消 */ } |
| | | }; |
| | | // 保养记录表格列配置(原设备保养表格列) |
| | | const columns = ref([ |
| | | { |
| | | label: "设备名称", |
| | | align: "center", |
| | | prop: "deviceName", |
| | | }, |
| | | { |
| | | label: "规格型号", |
| | | align: "center", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "计划保养日期", |
| | | align: "center", |
| | | prop: "maintenancePlanTime", |
| | | formatData: cell => { |
| | | return cell == null ? "-" : dayjs(cell).format("YYYY-MM-DD"); |
| | | }, |
| | | }, |
| | | { |
| | | label: "录入人", |
| | | align: "center", |
| | | prop: "createUserName", |
| | | }, |
| | | { |
| | | label: "保养项目", |
| | | align: "center", |
| | | prop: "machineryCategory", |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | // { |
| | | // label: "录入日期", |
| | | // align: "center", |
| | | // prop: "createTime", |
| | | // formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"), |
| | | // width: 200, |
| | | // }, |
| | | { |
| | | label: "实际保养人", |
| | | align: "center", |
| | | prop: "maintenanceActuallyName", |
| | | }, |
| | | { |
| | | label: "实际保养日期", |
| | | align: "center", |
| | | prop: "maintenanceActuallyTime", |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | }, |
| | | { |
| | | label: "保养结果", |
| | | align: "center", |
| | | prop: "maintenanceResult", |
| | | dataType: "slot", |
| | | slot: "maintenanceResultRef", |
| | | }, |
| | | { |
| | | label: "状态", |
| | | align: "center", |
| | | prop: "status", |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "操作", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "350px", |
| | | }, |
| | | ]); |
| | | |
| | | const onExpandChange = (row, expandedRows) => { |
| | | expandedRowKeys.value = expandedRows.map(r => r.date); |
| | | }; |
| | | // Tab切换处理 |
| | | const handleTabChange = tabName => { |
| | | if (tabName === "record") { |
| | | getTableData(); |
| | | } else if (tabName === "scheduled") { |
| | | getScheduledTableData(); |
| | | } |
| | | }; |
| | | |
| | | // 保养任务tab相关变量 |
| | | const scheduledFilters = reactive({ |
| | | taskName: "", |
| | | status: "", |
| | | }); |
| | | // 保养任务相关方法 |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | taskName: scheduledFilters.taskName || undefined, |
| | | status: scheduledFilters.status || undefined, |
| | | }; |
| | | const { code, data } = await deviceMaintenanceTaskList(params); |
| | | if (code === 200) { |
| | | scheduledDataList.value = data?.records || []; |
| | | scheduledPagination.total = data?.total || 0; |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error("获取定时任务列表失败"); |
| | | } |
| | | }; |
| | | |
| | | const scheduledDataList = ref([]); |
| | | const scheduledPagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | const scheduledMultipleList = ref([]); |
| | | const resetScheduledFilters = () => { |
| | | scheduledFilters.taskName = ""; |
| | | scheduledFilters.status = ""; |
| | | getScheduledTableData(); |
| | | }; |
| | | |
| | | // 保养任务表格列配置 |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设备名称" }, |
| | | { |
| | | label: "规格型号", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "保养项目", |
| | | prop: "machineryCategory", |
| | | minWidth: 120, |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | { |
| | | prop: "frequencyType", |
| | | label: "频次", |
| | | minWidth: 150, |
| | | // PIMTable 使用的是 formatData,而不是 Element-Plus 的 formatter |
| | | formatData: cell => |
| | | ({ |
| | | DAILY: "每日", |
| | | WEEKLY: "每周", |
| | | MONTHLY: "每月", |
| | | QUARTERLY: "季度", |
| | | }[cell] || ""), |
| | | }, |
| | | { |
| | | prop: "frequencyDetail", |
| | | label: "开始日期与时间", |
| | | minWidth: 150, |
| | | // 同样改用 formatData,PIMTable 内部会把单元格值传进来 |
| | | formatData: cell => { |
| | | if (typeof cell !== "string") return ""; |
| | | let val = cell; |
| | | const replacements = { |
| | | MON: "周一", |
| | | TUE: "周二", |
| | | WED: "周三", |
| | | THU: "周四", |
| | | FRI: "周五", |
| | | SAT: "周六", |
| | | SUN: "周日", |
| | | }; |
| | | // 使用正则一次性替换所有匹配项 |
| | | return val.replace( |
| | | /MON|TUE|WED|THU|FRI|SAT|SUN/g, |
| | | match => replacements[match] |
| | | ); |
| | | }, |
| | | }, |
| | | { prop: "maintenancePerson", label: "保养人", minWidth: 100 }, |
| | | { prop: "registrant", label: "登记人", minWidth: 100 }, |
| | | { |
| | | prop: "registrationDate", |
| | | label: "登记日期", |
| | | minWidth: 100, |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "操作", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | ]); |
| | | const handleScheduledSelectionChange = selection => { |
| | | scheduledMultipleList.value = selection; |
| | | }; |
| | | |
| | | // 保养记录表格列配置(原设备保养表格列) |
| | | const columns = ref([ |
| | | { |
| | | label: "设备名称", |
| | | align: "center", |
| | | prop: "deviceName", |
| | | }, |
| | | { |
| | | label: "规格型号", |
| | | align: "center", |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "计划保养日期", |
| | | align: "center", |
| | | prop: "maintenancePlanTime", |
| | | formatData: cell => { |
| | | return cell == null ? "-" : dayjs(cell).format("YYYY-MM-DD"); |
| | | }, |
| | | }, |
| | | { |
| | | label: "录入人", |
| | | align: "center", |
| | | prop: "createUserName", |
| | | }, |
| | | { |
| | | label: "保养项目", |
| | | align: "center", |
| | | prop: "machineryCategory", |
| | | formatData: cell => cell || "--", |
| | | }, |
| | | // { |
| | | // label: "录入日期", |
| | | // align: "center", |
| | | // prop: "createTime", |
| | | // formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"), |
| | | // width: 200, |
| | | // }, |
| | | { |
| | | label: "实际保养人", |
| | | align: "center", |
| | | prop: "maintenanceActuallyName", |
| | | }, |
| | | { |
| | | label: "实际保养日期", |
| | | align: "center", |
| | | prop: "maintenanceActuallyTime", |
| | | formatData: cell => |
| | | cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-", |
| | | }, |
| | | { |
| | | label: "保养结果", |
| | | align: "center", |
| | | prop: "maintenanceResult", |
| | | dataType: "slot", |
| | | slot: "maintenanceResultRef", |
| | | }, |
| | | { |
| | | label: "状态", |
| | | align: "center", |
| | | prop: "status", |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "操作", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "350px", |
| | | }, |
| | | ]); |
| | | const changeScheduledPage = page => { |
| | | scheduledPagination.currentPage = page.page; |
| | | scheduledPagination.pageSize = page.limit; |
| | | getScheduledTableData(); |
| | | }; |
| | | |
| | | // Tab切换处理 |
| | | const handleTabChange = tabName => { |
| | | if (tabName === "record") { |
| | | getTableData(); |
| | | } else if (tabName === "scheduled") { |
| | | getScheduledTableData(); |
| | | } |
| | | }; |
| | | const addScheduledTask = () => { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("add"); |
| | | }); |
| | | }; |
| | | |
| | | // 保养任务相关方法 |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | taskName: scheduledFilters.taskName || undefined, |
| | | status: scheduledFilters.status || undefined, |
| | | }; |
| | | const { code, data } = await deviceMaintenanceTaskList(params); |
| | | if (code === 200) { |
| | | scheduledDataList.value = data?.records || []; |
| | | scheduledPagination.total = data?.total || 0; |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error("获取定时任务列表失败"); |
| | | } |
| | | }; |
| | | const editScheduledTask = row => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("edit", row); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const resetScheduledFilters = () => { |
| | | scheduledFilters.taskName = ""; |
| | | scheduledFilters.status = ""; |
| | | getScheduledTableData(); |
| | | }; |
| | | const delScheduledTaskByIds = async ids => { |
| | | try { |
| | | await ElMessageBox.confirm("确定删除选中的定时任务吗?", "提示", { |
| | | type: "warning", |
| | | }); |
| | | const payload = Array.isArray(ids) ? ids : [ids]; |
| | | await deviceMaintenanceTaskDel(payload); |
| | | ElMessage.success("删除定时任务成功"); |
| | | getScheduledTableData(); |
| | | } catch (error) { |
| | | // 用户取消删除 |
| | | } |
| | | }; |
| | | |
| | | const handleScheduledSelectionChange = selection => { |
| | | scheduledMultipleList.value = selection; |
| | | }; |
| | | const handleScheduledOut = () => { |
| | | ElMessage.info("导出定时任务功能待实现"); |
| | | }; |
| | | |
| | | const changeScheduledPage = page => { |
| | | scheduledPagination.currentPage = page.page; |
| | | scheduledPagination.pageSize = page.limit; |
| | | getScheduledTableData(); |
| | | }; |
| | | // 保养记录相关方法(原设备保养页面方法) |
| | | const getTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | deviceName: filters.deviceName || undefined, |
| | | maintenancePlanTime: filters.maintenancePlanTime |
| | | ? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyTime: filters.maintenanceActuallyTime |
| | | ? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyName: filters.maintenanceActuallyName || undefined, |
| | | }; |
| | | |
| | | const addScheduledTask = () => { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("add"); |
| | | }); |
| | | }; |
| | | const { code, data } = await getUpkeepPage(params); |
| | | if (code === 200) { |
| | | dataList.value = data.records; |
| | | pagination.value.total = data.total; |
| | | } |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | }; |
| | | |
| | | const editScheduledTask = row => { |
| | | if (row) { |
| | | nextTick(() => { |
| | | formDiaRef.value?.openDialog("edit", row); |
| | | }); |
| | | } |
| | | }; |
| | | const resetFilters = () => { |
| | | filters.deviceName = ""; |
| | | filters.maintenancePlanTime = ""; |
| | | filters.maintenanceActuallyTime = ""; |
| | | filters.maintenanceActuallyName = ""; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const delScheduledTaskByIds = async ids => { |
| | | try { |
| | | await ElMessageBox.confirm("确定删除选中的定时任务吗?", "提示", { |
| | | type: "warning", |
| | | }); |
| | | const payload = Array.isArray(ids) ? ids : [ids]; |
| | | await deviceMaintenanceTaskDel(payload); |
| | | ElMessage.success("删除定时任务成功"); |
| | | getScheduledTableData(); |
| | | } catch (error) { |
| | | // 用户取消删除 |
| | | } |
| | | }; |
| | | const handleSelectionChange = selection => { |
| | | multipleList.value = selection; |
| | | }; |
| | | |
| | | const handleScheduledOut = () => { |
| | | ElMessage.info("导出定时任务功能待实现"); |
| | | }; |
| | | // 检查选中的记录中是否有完结状态的 |
| | | const hasFinishedStatus = computed(() => { |
| | | return multipleList.value.some(item => item.status === 1); |
| | | }); |
| | | |
| | | // 保养记录相关方法(原设备保养页面方法) |
| | | const getTableData = async () => { |
| | | tableLoading.value = true; |
| | | try { |
| | | const params = { |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | deviceName: filters.deviceName || undefined, |
| | | maintenancePlanTime: filters.maintenancePlanTime |
| | | ? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyTime: filters.maintenanceActuallyTime |
| | | ? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD") |
| | | : undefined, |
| | | maintenanceActuallyName: filters.maintenanceActuallyName || undefined, |
| | | }; |
| | | |
| | | const { code, data } = await getUpkeepPage(params); |
| | | if (code === 200) { |
| | | dataList.value = data.records; |
| | | pagination.value.total = data.total; |
| | | } |
| | | } catch (error) { |
| | | console.log(error); |
| | | } finally { |
| | | tableLoading.value = false; |
| | | } |
| | | }; |
| | | const changePage = page => { |
| | | pagination.value.currentPage = page.page; |
| | | pagination.value.pageSize = page.limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.deviceName = ""; |
| | | filters.maintenancePlanTime = ""; |
| | | filters.maintenanceActuallyTime = ""; |
| | | filters.maintenanceActuallyName = ""; |
| | | getTableData(); |
| | | }; |
| | | const addMaintain = row => { |
| | | maintainModalRef.value.open(row.id, row); |
| | | }; |
| | | |
| | | const handleSelectionChange = selection => { |
| | | // 从选中的分组行中收集所有子记录ID |
| | | const ids = []; |
| | | selection.forEach(group => { |
| | | if (group.children) { |
| | | group.children.forEach(child => ids.push(child.id)); |
| | | } |
| | | }); |
| | | multipleList.value = ids; |
| | | }; |
| | | const addPlan = () => { |
| | | planModalRef.value.openModal(); |
| | | }; |
| | | |
| | | const changePage = page => { |
| | | pagination.value.currentPage = page.page; |
| | | pagination.value.pageSize = page.limit; |
| | | getTableData(); |
| | | }; |
| | | const editPlan = id => { |
| | | planModalRef.value.openEdit(id); |
| | | }; |
| | | |
| | | const addMaintain = row => { |
| | | maintainModalRef.value.open(row.id, row); |
| | | }; |
| | | const delRepairByIds = async ids => { |
| | | // 检查是否有完结状态的记录 |
| | | const hasFinished = multipleList.value.some(item => item.status === 1); |
| | | if (hasFinished) { |
| | | ElMessage.warning("不能删除状态为完结的记录"); |
| | | return; |
| | | } |
| | | |
| | | const addPlan = () => { |
| | | planModalRef.value.openModal(); |
| | | }; |
| | | try { |
| | | await ElMessageBox.confirm("确认删除保养数据, 此操作不可逆?", "警告", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | |
| | | const editPlan = id => { |
| | | planModalRef.value.openEdit(id); |
| | | }; |
| | | const { code } = await delUpkeep(ids); |
| | | if (code === 200) { |
| | | ElMessage.success("删除成功"); |
| | | getTableData(); |
| | | } |
| | | } catch (error) { |
| | | // 用户取消删除 |
| | | } |
| | | }; |
| | | |
| | | const delRepairByIds = async ids => { |
| | | const idList = Array.isArray(ids) ? ids : [ids]; |
| | | if (idList.length === 0) return; |
| | | |
| | | try { |
| | | await ElMessageBox.confirm("确认删除保养数据, 此操作不可逆?", "警告", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }); |
| | | |
| | | const { code } = await delUpkeep(idList); |
| | | if (code === 200) { |
| | | ElMessage.success("删除成功"); |
| | | getTableData(); |
| | | } |
| | | } catch (error) { |
| | | // 用户取消删除 |
| | | } |
| | | }; |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/device/maintenance/export", {}, "设备保养.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info("已取消"); |
| | | }); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/device/maintenance/export", {}, "设备保养.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info("已取消"); |
| | | }); |
| | | }; |
| | | const handleDateChange = (date, type) => { |
| | | if (type === 1) { |
| | | filters.maintenanceActuallyTime = date |
| | | ? dayjs(date).format("YYYY-MM-DD") |
| | | : ""; |
| | | } else { |
| | | filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : ""; |
| | | } |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleDateChange = (date, type) => { |
| | | if (type === 1) { |
| | | filters.maintenanceActuallyTime = date |
| | | ? dayjs(date).format("YYYY-MM-DD") |
| | | : ""; |
| | | } else { |
| | | filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : ""; |
| | | } |
| | | getTableData(); |
| | | }; |
| | | // 打开附件弹窗 |
| | | const openFileDialog = async row => { |
| | | currentMaintenanceTaskId.value = row.id; |
| | | fileDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 打开附件弹窗 |
| | | const openFileDialog = async row => { |
| | | currentMaintenanceTaskId.value = row.id; |
| | | fileDialogVisible.value = true; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 根据默认激活的 Tab 调用对应的查询接口 |
| | | if (activeTab.value === "scheduled") { |
| | | getScheduledTableData(); |
| | | } else { |
| | | getTableData(); |
| | | } |
| | | }); |
| | | onMounted(() => { |
| | | // 根据默认激活的 Tab 调用对应的查询接口 |
| | | if (activeTab.value === "scheduled") { |
| | | getScheduledTableData(); |
| | | } else { |
| | | getTableData(); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| | | |
| | | |
| | |
| | | { label: "退货单号", prop: "returnNo", minWidth: "150" }, |
| | | { label: "供应商", prop: "supplierName", minWidth: "180" }, |
| | | { label: "关联入库单号", prop: "inboundBatches", minWidth: "150" }, |
| | | { label: "退货日期", prop: "preparedAt", minWidth: "170" }, |
| | | { label: "退货日期", prop: "preparedAt", minWidth: "170",dataType: "date"}, |
| | | { |
| | | label: "退款总额", |
| | | prop: "totalAmount", |
| | |
| | | { label: "退货单号", prop: "returnNo", minWidth: "150" }, |
| | | { label: "客户名称", prop: "customerName", minWidth: "180" }, |
| | | { label: "关联发货单号", prop: "shippingNo", minWidth: "150" }, |
| | | { label: "退货日期", prop: "makeTime", minWidth: "170" }, |
| | | { label: "退货日期", prop: "makeTime", minWidth: "170",dataType: "date" }, |
| | | { |
| | | label: "退款总额", |
| | | prop: "refundAmount", |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="出库日期" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.createTime ? scope.row.createTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产品大类" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | |
| | | prop="inboundBatches" |
| | | width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="入库时间" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="入库时间" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.createTime ? scope.row.createTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="产品大类" |
| | | prop="productName" |
| | | show-overflow-tooltip /> |
| | |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column |
| | | label="最近更新时间" |
| | | prop="updateTime" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="最近更新时间" |
| | | prop="updateTime" |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.updateTime ? scope.row.updateTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" min-width="180" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | |
| | | <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip /> |
| | | <el-table-column label="库存预警数量" prop="warnNum" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" |
| | | prop="updateTime" |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.updateTime ? scope.row.updateTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" min-width="90" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button> |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" |
| | | prop="updateTime" |
| | | show-overflow-tooltip /> |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.updateTime ? scope.row.updateTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="80" |
| | |
| | | <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip /> |
| | | <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" |
| | | prop="updateTime" |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.updateTime ? scope.row.updateTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" min-width="90" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button> |
| | |
| | | return { createdTime, updatedTime, createTime: createdTime, updateTime: updatedTime }; |
| | | } |
| | | |
| | | function normalizeTimeValue(val) { |
| | | if (val == null || val === "") return ""; |
| | | if (Array.isArray(val) && val.length >= 3) { |
| | | const [y, m, d, h = 0, min = 0, s = 0] = val; |
| | | return dayjs(new Date(y, m - 1, d, h, min, s)).format("YYYY-MM-DD HH:mm:ss"); |
| | | } |
| | | if (typeof val === "number") { |
| | | const d = val > 1e12 ? dayjs(val) : dayjs.unix(val); |
| | | return d.isValid() ? d.format("YYYY-MM-DD HH:mm:ss") : ""; |
| | | } |
| | | const s = String(val).trim(); |
| | | if (!s) return ""; |
| | | const parsed = dayjs(s.includes("T") ? s : s.replace(/-/g, "/")); |
| | | return parsed.isValid() ? parsed.format("YYYY-MM-DD HH:mm:ss") : s; |
| | | |
| | | /** |
| | | * @param {any} val 时间值 |
| | | * @param {boolean} [onlyDate=true] true:只年月日,false:年月日时分秒 |
| | | */ |
| | | function normalizeTimeValue(val, onlyDate = true) { |
| | | if (val == null || val === "") return ""; |
| | | const fmt = onlyDate ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss"; |
| | | |
| | | if (Array.isArray(val) && val.length >= 3) { |
| | | const [y, m, d, h = 0, min = 0, s = 0] = val; |
| | | return dayjs(new Date(y, m - 1, d, h, min, s)).format(fmt); |
| | | } |
| | | if (typeof val === "number") { |
| | | const d = val > 1e12 ? dayjs(val) : dayjs.unix(val); |
| | | return d.isValid() ? d.format(fmt) : ""; |
| | | } |
| | | const s = String(val).trim(); |
| | | if (!s) return ""; |
| | | const parsed = dayjs(s.includes("T") ? s : s.replace(/-/g, "/")); |
| | | return parsed.isValid() ? parsed.format(fmt) : s; |
| | | } |
| | | |
| | | export function formatDisplayTime(val) { |
| | | const t = normalizeTimeValue(val); |
| | | export function formatDisplayTime(val,onlyDate) { |
| | | const t = normalizeTimeValue(val,onlyDate); |
| | | return t || "—"; |
| | | } |
| | | |
| | |
| | | { label: "保险类型", prop: "insuranceTypes", width: 120 }, |
| | | { label: "使用范围", prop: "deptNames", width: 120 }, |
| | | { label: "备注", prop: "remark", minWidth: 120 }, |
| | | { label: "创建时间", prop: "createTime", width: 160 }, |
| | | { label: "创建时间", prop: "createTime", width: 160,dataType: "date" }, |
| | | { label: "创建人", prop: "createUserName", width: 100 }, |
| | | { |
| | | dataType: "action", |
| | |
| | | label: "创建时间", |
| | | prop: "createTime", |
| | | width: 120, |
| | | dataType: "date" |
| | | }, |
| | | { |
| | | dataType: "action", |
| | |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="上报时间" |
| | | prop="createTime" |
| | | show-overflow-tooltip /> |
| | | show-overflow-tooltip > |
| | | <template #default="scope"> |
| | | {{ scope.row.createTime ? scope.row.createTime.split(' ')[0] : '-' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="整改完成期限" |
| | | prop="rectifyTime" |
| | | width="120" |