已添加1个文件
已修改26个文件
4248 ■■■■■ 文件已修改
src/api/equipmentManagement/calibration.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/ledger.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/upkeep.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementLedger.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/index.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/calibration/index.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/formDia.vue 382 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/index.vue 509 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Modal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/index.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/formDia.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/MaintainForm.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/RepairForm.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/MaintainModal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/RepairModal.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/spareParts/index.vue 353 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanForm.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/formDia.vue 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 744 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 1623 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/calibration.js
@@ -24,4 +24,12 @@
    method: "post",
    data: query,
  });
}
// åˆ é™¤è®°å½•
export function ledgerRecordDelete(ids) {
  return request({
    url: "/measuringInstrumentLedgerRecord/delete",
    method: "delete",
    data: ids,
  });
}
src/api/equipmentManagement/ledger.js
@@ -42,11 +42,3 @@
    method: "get",
  });
};
// è®¾å¤‡å°è´¦å›¾è¡¨
export const getAssetInfo = (params) => {
    return request({
        url: "/device/ledger/report/forms",
        method: "get",
        params
    });
}
src/api/equipmentManagement/upkeep.js
@@ -70,3 +70,35 @@
    method: "delete",
  });
};
// æ·»åŠ è®¾å¤‡ä¿å…»å®šæ—¶ä»»åŠ¡
export const deviceMaintenanceTaskAdd = (params) => {
  return request({
    url: '/deviceMaintenanceTask/add',
    method: "post",
    data: params,
  });
};
// ä¿®æ”¹è®¾å¤‡ä¿å…»å®šæ—¶ä»»åŠ¡
export const deviceMaintenanceTaskEdit = (params) => {
  return request({
    url: '/deviceMaintenanceTask/update',
    method: "post",
    data: params,
  });
};
// è®¾å¤‡ä¿å…»å®šæ—¶ä»»åŠ¡åˆ—è¡¨
export const deviceMaintenanceTaskList = (params) => {
  return request({
    url: '/deviceMaintenanceTask/listPage',
    method: "get",
    params: params,
  });
};
// è®¾å¤‡ä¿å…»å®šæ—¶ä»»åŠ¡åˆ—è¡¨
export const deviceMaintenanceTaskDel = (params) => {
  return request({
    url: '/deviceMaintenanceTask/delete',
    method: "delete",
    data: params,
  });
};
src/api/procurementManagement/procurementLedger.js
@@ -78,4 +78,24 @@
        method: "post",
        data: query,
    });
}
}
// ä¿å­˜é‡‡è´­æ¨¡æ¿
// /purchase/ledger/addPurchaseTemplate
export function addPurchaseTemplate(data) {
    return request({
        url: "/purchase/ledger/addPurchaseTemplate",
        method: "post",
        data: data,
    });
}
// æŸ¥è¯¢é‡‡è´­æ¨¡æ¿
// /purchase/ledger/getPurchaseTemplateList
export function getPurchaseTemplateList(query) {
    return request({
        url: "/purchase/ledger/getPurchaseTemplateList",
        method: "get",
        params: query,
    });
}
src/utils/index.js
@@ -396,3 +396,16 @@
export function isEqual(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}
/**
 * èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
 * @returns {string} æ ¼å¼åŒ–的日期字符串
 */
export function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0'); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
src/views/equipmentManagement/calibration/index.vue
@@ -57,10 +57,10 @@
<script setup>
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
import {ElMessageBox, ElMessage} from "element-plus";
import useUserStore from "@/store/modules/user.js";
import CalibrationDia from "@/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue";
import {ledgerRecordListPage} from "@/api/equipmentManagement/calibration.js";
import {ledgerRecordListPage, ledgerRecordDelete} from "@/api/equipmentManagement/calibration.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
@@ -134,6 +134,7 @@
    {
        dataType: "action",
        label: "操作",
        width: 140,
        align: "center",
        fixed: 'right',
        operation: [
@@ -142,7 +143,17 @@
                type: "text",
                clickFun: (row) => {
                    openCalibrationDia("edit", row);
                }
                },
            },
            {
                name: "删除",
                type: "text",
                style: {
                    color: "#F56C6C"
                },
                clickFun: (row) => {
                    handleDelete(row);
                },
            },
        ],
    },
@@ -191,6 +202,26 @@
    })
}
// åˆ é™¤è®°å½•
const handleDelete = (row) => {
    ElMessageBox.confirm(`确认删除计量器具编号为"${row.code}"的检定记录吗?`, "删除确认", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            ledgerRecordDelete([row.id]).then(() => {
                ElMessage.success("删除成功");
                getList();
            }).catch(() => {
                ElMessage.error("删除失败");
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消删除");
        });
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -1,102 +1,116 @@
<template>
  <div>
    <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
               v-model="dialogVisitable" width="800px" @close="cancel">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="设备名称" prop="taskId">
              <el-select v-model="form.taskId" @change="setDeviceModel">
                <el-option
                  v-for="(item, index) in deviceOptions"
                  :key="index"
                  :label="item.deviceName"
                  :value="item.id"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="巡检人" prop="inspector">
              <el-select v-model="form.inspector" placeholder="请选择" multiple clearable>
                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="备注" prop="remarks">
              <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务频率" prop="frequencyType">
              <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                <el-option label="每日" value="DAILY"/>
                <el-option label="每周" value="WEEKLY"/>
                <el-option label="每月" value="MONTHLY"/>
                <!-- <el-option label="季度" value="QUARTERLY"/> -->
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm" />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                <el-option label="周一" value="MON"/>
                <el-option label="周二" value="TUE"/>
                <el-option label="周三" value="WED"/>
                <el-option label="周四" value="THU"/>
                <el-option label="周五" value="FRI"/>
                <el-option label="周六" value="SAT"/>
                <el-option label="周日" value="SUN"/>
              </el-select>
              <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm"  style="width: 50%"/>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="DD,HH:mm"
                  value-format="DD,HH:mm"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="MM,DD,HH:mm"
                  value-format="MM,DD,HH:mm"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancel">取消</el-button>
          <el-button type="primary" @click="submitForm">保存</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
    <div>
        <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
                             v-model="dialogVisitable" width="800px" @close="cancel">
            <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="taskId">
                            <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                                <el-option
                                    v-for="(item, index) in deviceOptions"
                                    :key="index"
                                    :label="item.deviceName"
                                    :value="item.id"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="巡检人" prop="inspector">
                            <el-select v-model="form.inspector"                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" multiple clearable>
                                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="备注" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="登记时间" prop="dateStr">
                            <el-date-picker
                                v-model="form.dateStr"
                                type="date"
                                placeholder="选择登记日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="任务频率" prop="frequencyType">
                            <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                                <el-option label="每日" value="DAILY"/>
                                <el-option label="每周" value="WEEKLY"/>
                                <el-option label="每月" value="MONTHLY"/>
                                <!-- <el-option label="季度" value="QUARTERLY"/> -->
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                                <el-option label="周一" value="MON"/>
                                <el-option label="周二" value="TUE"/>
                                <el-option label="周三" value="WED"/>
                                <el-option label="周四" value="THU"/>
                                <el-option label="周五" value="FRI"/>
                                <el-option label="周六" value="SAT"/>
                                <el-option label="周日" value="SUN"/>
                            </el-select>
                            <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm"  style="width: 50%"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="DD,HH:mm"
                                value-format="DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="MM,DD,HH:mm"
                                value-format="MM,DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="cancel">取消</el-button>
                    <el-button type="primary" @click="submitForm">保存</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
@@ -113,115 +127,117 @@
const operationType = ref('add');
const deviceOptions = ref([]);
const data = reactive({
  form: {
    taskId: undefined,
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  },
  rules: {
    taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
    inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
  }
    form: {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        dateStr: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
        dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
})
const { form, rules } = toRefs(data)
const userList = ref([])
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
};
const setDeviceModel = (id) => {
  const option = deviceOptions.value.find((item) => item.id === id);
  if (option) {
    form.value.taskName = option.deviceName;
  }
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskName = option.deviceName;
    }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
  dialogVisitable.value = true
  operationType.value = type
  // é‡ç½®è¡¨å•
  resetForm();
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  // åŠ è½½è®¾å¤‡åˆ—è¡¨
  await loadDeviceName();
  if (type === 'edit' && row) {
    form.value = {...row}
    form.value.inspector = form.value.inspectorIds.split(',').map(Number)
    // å¦‚果有设备ID,自动设置设备信息
    if (form.value.taskId) {
      setDeviceModel(form.value.taskId);
    }
  }
    dialogVisitable.value = true
    operationType.value = type
    // é‡ç½®è¡¨å•
    resetForm();
    // åŠ è½½ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // åŠ è½½è®¾å¤‡åˆ—è¡¨
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = {...row}
        form.value.inspector = form.value.inspectorIds.split(',').map(Number)
        // å¦‚果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
    }
}
// å…³é—­å¯¹è¯æ¡†
const cancel = () => {
  resetForm()
  dialogVisitable.value = false
  emit('closeDia')
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
}
// é‡ç½®è¡¨å•函数
const resetForm = () => {
  if (proxy.$refs.formRef) {
    proxy.$refs.formRef.resetFields()
  }
  // é‡ç½®è¡¨å•数据确保设备信息正确重置
  form.value = {
    taskId: undefined,
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  }
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: ''
    }
}
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate(async valid => {
    if (valid) {
      try {
        form.value.inspectorIds = form.value.inspector.join(',')
        delete form.value.inspector
        if (form.value.frequencyType === 'WEEKLY') {
          let frequencyDetail = ''
          frequencyDetail = form.value.week + ',' + form.value.time
          form.value.frequencyDetail = frequencyDetail
        }
        let res = await userStore.getInfo()
        form.value.registrantId = res.user.userId
        await addOrEditTimingTask(form.value)
        cancel()
        proxy.$modal.msgSuccess('提交成功')
      } catch (error) {
        proxy.$modal.msgError('提交失败,请重试')
      }
    }
  })
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                form.value.inspectorIds = form.value.inspector.join(',')
                delete form.value.inspector
                if (form.value.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = form.value.week + ',' + form.value.time
                    form.value.frequencyDetail = frequencyDetail
                }
                let res = await userStore.getInfo()
                form.value.registrantId = res.user.userId
                await addOrEditTimingTask(form.value)
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
}
defineExpose({ openDialog })
</script>
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -32,7 +32,7 @@
        
        <!-- ç”Ÿäº§åŽ -->
        <div class="form-container">
          <div class="title">生产后</div>
          <div class="title">生产中</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
@@ -59,7 +59,7 @@
        
        <!-- ç”Ÿäº§é—®é¢˜ -->
        <div class="form-container">
          <div class="title">生产问题</div>
          <div class="title">生产后</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,80 +1,78 @@
<template>
  <div class="app-container">
    <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item label="搜索">
        <el-input
            v-model="queryParams.searchAll"
            placeholder="请输入关键字"
            clearable
            :style="{ width: '100%' }"
        />
      </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>
    <el-card>
      <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
        <el-radio-group v-model="activeRadio" @change="radioChange">
          <el-radio-button v-for="tab in radios"
                           :key="tab.name"
                           :label="tab.label"
                           :value="tab.name"/>
        </el-radio-group>
        <!-- æ“ä½œæŒ‰é’®åŒº -->
        <el-space v-if="activeRadio !== 'task'">
          <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
          <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
        <el-space v-else>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
      </div>
      <div>
        <div>
          <PIMTable :table-loading="tableLoading"
                  :table-data="tableData"
                  :column="tableColumns"
                  @selection-change="handleSelectionChange"
                  :is-selection="true"
                  :border="true"
                  :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
          >
          <template #inspector="{ row }">
            <div class="person-tags">
              <!-- è°ƒè¯•信息,上线时删除 -->
              <!-- {{ console.log('inspector data:', row.inspector) }} -->
              <template v-if="row.inspector && row.inspector.length > 0">
                <el-tag
                  v-for="(person, index) in row.inspector"
                  :key="index"
                  size="small"
                  type="primary"
                  class="person-tag"
                >
                  {{ person }}
                </el-tag>
              </template>
              <span v-else class="no-data">--</span>
            </div>
          </template>
            </PIMTable>
        </div>
        <pagination
            v-if="total>0"
            :page="pageNum"
            :limit="pageSize"
            :total="total"
            @pagination="handlePagination"
            :layout="'total, prev, pager, next, jumper'"
        />
      </div>
    </el-card>
    <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
    <view-files ref="viewFiles"></view-files>
  </div>
    <div class="app-container">
        <el-form :inline="true" :model="queryParams" class="search-form">
            <el-form-item label="搜索">
                <el-input
                    v-model="queryParams.searchAll"
                    placeholder="请输入关键字"
                    clearable
                    :style="{ width: '100%' }"
                />
            </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>
        <el-card>
            <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
                <el-radio-group v-model="activeRadio" @change="radioChange">
                    <el-radio-button v-for="tab in radios"
                                                     :key="tab.name"
                                                     :label="tab.label"
                                                     :value="tab.name"/>
                </el-radio-group>
                <!-- æ“ä½œæŒ‰é’®åŒº -->
                <el-space v-if="activeRadio !== 'task'">
                    <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
                    <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
                <el-space v-else>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
            </div>
            <div>
                <div>
                    <PIMTable :table-loading="tableLoading"
                                        :table-data="tableData"
                                        :column="tableColumns"
                                        @selection-change="handleSelectionChange"
                                        :is-selection="true"
                                        :border="true"
                                        :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
                                        :page="{
          current: pageNum,
          size: pageSize,
          total: total,
        }"
                                        @pagination="pagination"
                    >
                        <template #inspector="{ row }">
                            <div class="person-tags">
                                <!-- è°ƒè¯•信息,上线时删除 -->
                                <!-- {{ console.log('inspector data:', row.inspector) }} -->
                                <template v-if="row.inspector && row.inspector.length > 0">
                                    <el-tag
                                        v-for="(person, index) in row.inspector"
                                        :key="index"
                                        size="small"
                                        type="primary"
                                        class="person-tag"
                                    >
                                        {{ person }}
                                    </el-tag>
                                </template>
                                <span v-else class="no-data">--</span>
                            </div>
                        </template>
                    </PIMTable>
                </div>
            </div>
        </el-card>
        <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
        <view-files ref="viewFiles"></view-files>
    </div>
</template>
<script setup>
@@ -90,9 +88,9 @@
// æŽ¥å£å¼•å…¥
import {
  delTimingTask,
  inspectionTaskList,
  timingTaskList
    delTimingTask,
    inspectionTaskList,
    timingTaskList
} from "@/api/inspectionManagement/index.js";
// å…¨å±€å˜é‡
@@ -102,14 +100,14 @@
// æŸ¥è¯¢å‚æ•°
const queryParams = reactive({
  searchAll: "",
    searchAll: "",
});
// å•选框配置
const activeRadio = ref("taskManage");
const radios = reactive([
  { name: "taskManage", label: "定时任务管理" },
  { name: "task", label: "定时任务记录" },
    { name: "taskManage", label: "定时任务管理" },
    { name: "task", label: "定时任务记录" },
]);
// è¡¨æ ¼æ•°æ®
@@ -124,234 +122,233 @@
// åˆ—配置
const columns = ref([
  { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
  { prop: "remarks", label: "备注", minWidth: 150 },
  { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
  {
    prop: "frequencyType",
    label: "频次",
    minWidth: 150,
    formatter: (_, __, val) => ({
      DAILY: "每日",
      WEEKLY: "每周",
      MONTHLY: "每月",
      QUARTERLY: "季度"
    }[val] || "")
  },
  {
    prop: "frequencyDetail",
    label: "开始日期与时间",
    minWidth: 150,
    formatter: (row, column, cellValue) => {
      // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
      if (typeof cellValue !== 'string') return '';
      let val = cellValue;
      const replacements = {
        MON: '周一',
        TUE: '周二',
        WED: '周三',
        THU: '周四',
        FRI: '周五',
        SAT: '周六',
        SUN: '周日'
      };
      // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
      return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
    }
  },
  { prop: "registrant", label: "登记人", minWidth: 100 },
  { prop: "createTime", label: "登记日期", minWidth: 100 },
    { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
    { prop: "remarks", label: "备注", minWidth: 150 },
    { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
    {
        prop: "frequencyType",
        label: "频次",
        minWidth: 150,
        formatData: (cell) => ({
            DAILY: "每日",
            WEEKLY: "每周",
            MONTHLY: "每月",
            QUARTERLY: "季度"
        }[cell] || "")
    },
    {
        prop: "frequencyDetail",
        label: "开始日期与时间",
        minWidth: 150,
        formatter: (row, column, cellValue) => {
            // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
            if (typeof cellValue !== 'string') return '';
            let val = cellValue;
            const replacements = {
                MON: '周一',
                TUE: '周二',
                WED: '周三',
                THU: '周四',
                FRI: '周五',
                SAT: '周六',
                SUN: '周日'
            };
            // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
            return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
        }
    },
    { prop: "registrant", label: "登记人", minWidth: 100 },
    { prop: "dateStr", label: "登记日期", minWidth: 100 },
]);
// æ“ä½œåˆ—配置
const getOperationColumn = (operations) => {
  if (!operations || operations.length === 0) return null;
  const operationConfig = {
    label: "操作",
    width: 130,
    fixed: "right",
    dataType: "action",
    operation: operations.map(op => {
      switch (op) {
        case 'edit':
          return {
            name: "编辑",
            clickFun: handleAdd,
            color: "#409EFF"
          };
        case 'viewFile':
          return {
            name: "查看附件",
            clickFun: viewFile,
            color: "#67C23A"
          };
        default:
          return null;
      }
    }).filter(Boolean)
  };
  return operationConfig;
    if (!operations || operations.length === 0) return null;
    const operationConfig = {
        label: "操作",
        width: 130,
        fixed: "right",
        dataType: "action",
        operation: operations.map(op => {
            switch (op) {
                case 'edit':
                    return {
                        name: "编辑",
                        clickFun: handleAdd,
                        color: "#409EFF"
                    };
                case 'viewFile':
                    return {
                        name: "查看附件",
                        clickFun: viewFile,
                        color: "#67C23A"
                    };
                default:
                    return null;
            }
        }).filter(Boolean)
    };
    return operationConfig;
};
onMounted(() => {
  radioChange('taskManage');
    radioChange('taskManage');
});
// å•选变化
const radioChange = (value) => {
  if (value === "taskManage") {
    const operationColumn = getOperationColumn(['edit']);
    tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['edit'];
  } else if (value === "task") {
    const operationColumn = getOperationColumn(['viewFile']);
    tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['viewFile'];
  }
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
    if (value === "taskManage") {
        const operationColumn = getOperationColumn(['edit']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['edit'];
    } else if (value === "task") {
        const operationColumn = getOperationColumn(['viewFile']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['viewFile'];
    }
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
};
// æŸ¥è¯¢æ“ä½œ
const handleQuery = () => {
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
};
// åˆ†é¡µå¤„理
const handlePagination = (val) => {
    pageNum.value = val.page;
    pageSize.value = val.size;
const pagination = (obj) => {
    pageNum.value = obj.page;
    pageSize.value = obj.limit;
    getList();
};
// èŽ·å–åˆ—è¡¨æ•°æ®
const getList = () => {
  tableLoading.value = true;
  const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
  let apiCall;
  if (activeRadio.value === "task") {
    apiCall = inspectionTaskList(params);
  } else {
    apiCall = timingTaskList(params);
  }
  apiCall.then(res => {
    const rawData = res.data.records || [];
    // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
    tableData.value = rawData.map(item => {
      const processedItem = { ...item };
      // å¤„理 inspector å­—段
      if (processedItem.inspector) {
        if (typeof processedItem.inspector === 'string') {
          // å­—符串按逗号分割
          processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
        } else if (!Array.isArray(processedItem.inspector)) {
          // éžæ•°ç»„转为数组
          processedItem.inspector = [processedItem.inspector];
        }
      } else {
        // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
        processedItem.inspector = [];
      }
      return processedItem;
    });
    total.value = res.data.total || 0;
  }).finally(() => {
    tableLoading.value = false;
  });
    tableLoading.value = true;
    const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
    let apiCall;
    if (activeRadio.value === "task") {
        apiCall = inspectionTaskList(params);
    } else {
        apiCall = timingTaskList(params);
    }
    apiCall.then(res => {
        const rawData = res.data.records || [];
        // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
        tableData.value = rawData.map(item => {
            const processedItem = { ...item };
            // å¤„理 inspector å­—段
            if (processedItem.inspector) {
                if (typeof processedItem.inspector === 'string') {
                    // å­—符串按逗号分割
                    processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
                } else if (!Array.isArray(processedItem.inspector)) {
                    // éžæ•°ç»„转为数组
                    processedItem.inspector = [processedItem.inspector];
                }
            } else {
                // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
                processedItem.inspector = [];
            }
            return processedItem;
        });
        total.value = res.data.total || 0;
    }).finally(() => {
        tableLoading.value = false;
    });
};
// é‡ç½®æŸ¥è¯¢
const resetQuery = () => {
  for (const key in queryParams) {
    if (!["pageNum", "pageSize"].includes(key)) {
      queryParams[key] = "";
    }
  }
  handleQuery();
    for (const key in queryParams) {
        if (!["pageNum", "pageSize"].includes(key)) {
            queryParams[key] = "";
        }
    }
    handleQuery();
};
// æ–°å¢ž / ç¼–辑
const handleAdd = (row) => {
  const type = row ? 'edit' : 'add';
  nextTick(() => {
    formDia.value?.openDialog(type, row);
  });
    const type = row ? 'edit' : 'add';
    nextTick(() => {
        formDia.value?.openDialog(type, row);
    });
};
// æŸ¥çœ‹é™„ä»¶
const viewFile = (row) => {
  nextTick(() => {
    viewFiles.value?.openDialog(row);
  });
    nextTick(() => {
        viewFiles.value?.openDialog(row);
    });
};
// åˆ é™¤æ“ä½œ
const handleDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning("请选择要删除的数据");
    return;
  }
  const deleteIds = selectedRows.value.map(item => item.id);
  proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
    return delTimingTask(deleteIds);
  }).then(() => {
    proxy.$modal.msgSuccess("删除成功");
    handleQuery();
  }).catch(() => {});
    if (!selectedRows.value.length) {
        proxy.$modal.msgWarning("请选择要删除的数据");
        return;
    }
    const deleteIds = selectedRows.value.map(item => item.id);
    proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
        return delTimingTask(deleteIds);
    }).then(() => {
        proxy.$modal.msgSuccess("删除成功");
        handleQuery();
    }).catch(() => {});
};
// å¤šé€‰å˜æ›´
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
    selectedRows.value = selection;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      // æ ¹æ®å½“前选中的标签页调用不同的导出接口
      if (activeRadio.value === "taskManage") {
        // å®šæ—¶ä»»åŠ¡ç®¡ç†
        proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
      } else if (activeRadio.value === "task") {
        // å®šæ—¶ä»»åŠ¡è®°å½•
        proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
      }
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            // æ ¹æ®å½“前选中的标签页调用不同的导出接口
            if (activeRadio.value === "taskManage") {
                // å®šæ—¶ä»»åŠ¡ç®¡ç†
                proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
            } else if (activeRadio.value === "task") {
                // å®šæ—¶ä»»åŠ¡è®°å½•
                proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
            }
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
</script>
<style scoped>
.person-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.person-tag {
  margin-right: 4px;
  margin-bottom: 2px;
    margin-right: 4px;
    margin-bottom: 2px;
}
.no-data {
  color: #909399;
  font-size: 14px;
    color: #909399;
    font-size: 14px;
}
</style>
src/views/equipmentManagement/ledger/Modal.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog :title="modalOptions.title" v-model="visible" @close="close">
  <el-dialog :title="modalOptions.title" v-model="visible" @close="close" draggable>
    <Form ref="formRef"></Form>
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
src/views/equipmentManagement/ledger/index.vue
@@ -82,15 +82,14 @@
      </PIMTable>
    </div>
    <Modal ref="modalRef" @success="getTableData"></Modal>
        <el-dialog v-model="qrDialogVisible" title="二维码" width="300px">
            <div style="text-align:center;">
                <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
                <div style="margin-top:6px;font-size:14px;color:#333;">{{ qrRowData?.deviceName }}</div>
                <div style="margin:10px 0;">
                    <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
                </div>
            </div>
        </el-dialog>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px" draggable>
      <div style="text-align:center;">
        <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
        <div style="margin:10px 0;">
          <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
@@ -300,44 +299,10 @@
};
const downloadQRCode = () => {
    const name = qrRowData.value?.deviceName || "二维码";
    const img = new Image();
    img.src = qrCodeUrl.value;
    img.onload = () => {
        const padding = 10;
        const qrSize = 200;
        const textHeight = 24; // space for text
        const width = qrSize + padding * 2;
        const height = qrSize + padding * 2 + textHeight;
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext("2d");
        // background
        ctx.fillStyle = "#ffffff";
        ctx.fillRect(0, 0, width, height);
        // draw QR centered
        ctx.drawImage(img, padding, padding, qrSize, qrSize);
        // draw name centered below
        ctx.fillStyle = "#333";
        ctx.font = "14px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        const maxTextWidth = width - padding * 2;
        let displayName = name;
        // ellipsis if too long
        while (ctx.measureText(displayName).width > maxTextWidth && displayName.length > 0) {
            displayName = displayName.slice(0, -1);
        }
        if (displayName !== name) displayName = displayName + "…";
        ctx.fillText(displayName, width / 2, qrSize + padding + textHeight / 2);
        const dataUrl = canvas.toDataURL("image/png");
        const a = document.createElement("a");
        a.href = dataUrl;
        a.download = `${name}.png`;
        a.click();
    };
  const a = document.createElement("a");
  a.href = qrCodeUrl.value;
  a.download = `${qrRowData.value.deviceName || "二维码"}.png`;
  a.click();
};
onMounted(() => {
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -4,6 +4,7 @@
            v-model="dialogFormVisible"
            title="计量器具"
            width="50%"
            draggable
            @close="closeDia"
        >
            <el-form
@@ -67,7 +68,9 @@
                            <el-select
                                v-model="form.userId"
                                placeholder="请选择"
                                disabled
                filterable
                default-first-option
                :reserve-keyword="false"
                                clearable
                            >
                                <el-option
@@ -129,6 +132,7 @@
import {getToken} from "@/utils/auth.js";
import {ledgerRecordUpdate, ledgerRecordVerifying} from "@/api/equipmentManagement/calibration.js";
import {delLedgerFile} from "@/api/salesManagement/salesLedger.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -248,14 +252,6 @@
    dialogFormVisible.value = false;
    emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
    openDialog,
});
src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -4,6 +4,7 @@
        v-model="dialogFormVisible"
        title="计量器具"
        width="50%"
                draggable
        @close="closeDia"
    >
            <el-form
@@ -64,7 +65,9 @@
                                v-model="form.userId"
                                placeholder="请选择"
                                clearable
                                disabled
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="item in userList"
@@ -124,6 +127,7 @@
import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
import {getToken} from "@/utils/auth.js";
import {measuringInstrumentAdd, measuringInstrumentUpdate} from "@/api/equipmentManagement/measurementEquipment.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -234,14 +238,6 @@
  dialogFormVisible.value = false;
  emit('close')
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
}
defineExpose({
  openDialog,
});
src/views/equipmentManagement/measurementEquipment/index.vue
@@ -148,13 +148,13 @@
                    openCalibrationDia("verifying", row);
                },
            },
            {
                name: "附件",
                type: "text",
                clickFun: (row) => {
          openFilesFormDia(row);
                },
            },
            // {
            //     name: "附件",
            //     type: "text",
            //     clickFun: (row) => {
      //     openFilesFormDia(row);
            //     },
            // },
        ],
    },
]);
@@ -221,12 +221,6 @@
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.userId !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
src/views/equipmentManagement/repair/Form/MaintainForm.vue
@@ -6,6 +6,13 @@
    <el-form-item label="维修结果">
      <el-input v-model="form.maintenanceResult" placeholder="请输入维修结果" />
    </el-form-item>
    <el-form-item label="报修状态">
      <el-select v-model="form.status">
        <el-option label="待报修" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="维修日期">
      <el-date-picker
        v-model="form.maintenanceTime"
@@ -34,6 +41,7 @@
  maintenanceName: undefined, // ç»´ä¿®åç§°
  maintenanceResult: undefined, // ç»´ä¿®ç»“æžœ
  maintenanceTime: undefined, // ç»´ä¿®æ—¥æœŸ
  status: 0,
});
const setForm = (data) => {
src/views/equipmentManagement/repair/Form/RepairForm.vue
@@ -3,7 +3,7 @@
    <el-row>
      <el-col :span="12">
        <el-form-item label="设备名称">
          <el-select v-model="form.deviceLedgerId" @change="setDeviceModel">
          <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable>
            <el-option
              v-for="(item, index) in deviceOptions"
              :key="index"
@@ -40,6 +40,19 @@
          <el-input v-model="form.repairName" placeholder="请输入报修人" />
        </el-form-item>
      </el-col>
    </el-row>
    <el-row v-if="id">
      <el-col :span="12">
        <el-form-item label="报修状态">
          <el-select v-model="form.status">
            <el-option label="待维修" :value="0"></el-option>
            <el-option label="完结" :value="1"></el-option>
            <el-option label="失败" :value="2"></el-option>
          </el-select>
        </el-form-item>
      </el-col>
    </el-row>
    <el-row>
      <el-col :span="24">
        <el-form-item label="故障现象">
          <el-input
@@ -55,9 +68,12 @@
</template>
<script setup>
import dayjs from "dayjs";
import useFormData from "@/hooks/useFormData";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import useUserStore from "@/store/modules/user";
const { id } = defineProps(["id"])
defineOptions({
  name: "设备报修表单",
@@ -75,9 +91,10 @@
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  repairTime: undefined, // æŠ¥ä¿®æ—¥æœŸ
  repairTime: dayjs().format("YYYY-MM-DD"), // æŠ¥ä¿®æ—¥æœŸï¼Œé»˜è®¤å½“天
  repairName: userStore.nickName, // æŠ¥ä¿®äºº
  remark: undefined, // æ•…障现象
  status: 0, // æŠ¥ä¿®çŠ¶æ€
});
const setDeviceModel = (id) => {
@@ -96,6 +113,7 @@
  form.repairTime = data.repairTime;
  form.repairName = data.repairName;
  form.remark = data.remark;
  form.status = data.status;
};
// onMounted(() => {
src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr" draggable>
    <MaintainForm ref="maintainFormRef" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -1,6 +1,6 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" @close="close">
    <RepairForm ref="repairFormRef" />
  <el-dialog v-model="visible" :title="modalOptions.title" @close="close" draggable>
    <RepairForm ref="repairFormRef" :id="id" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
src/views/equipmentManagement/repair/index.vue
@@ -106,8 +106,9 @@
        @pagination="changePage"
      >
        <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="danger">待维修</el-tag>
          <el-tag v-if="row.status === 0" type="warning">待维修</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
src/views/equipmentManagement/spareParts/index.vue
@@ -1,48 +1,55 @@
<template>
  <div class="spare-part-category">
        <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">
      <div class="actions">
        <el-text class="mx-1" size="large">设备分类</el-text>
        <div>
          <el-button @click="fetchTreeData" :loading="loading">刷新</el-button>
          <el-button type="primary" @click="addCategory" >新增</el-button>
        </div>
      </div>
      <el-table
        v-loading="loading"
        :data="renderTableData"
        style="width: 100%; margin-top: 10px;"
        border
        row-key="id"
        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      >
        <el-table-column prop="name" label="分类名称" width="450">
          <template #default="{ row }">
            <span :style="{ paddingLeft: getIndentation(row) + 'px' }">
              {{ row.name }}
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="sparePartsNo" label="分类编号" width="200"></el-table-column>
      <el-table-column prop="deviceNameStr" label="设备名称"  width="300"></el-table-column>
        <el-table-column prop="name" label="备件名称" width="200"></el-table-column>
        <el-table-column prop="sparePartsNo" label="备件编号" width="200"></el-table-column>
        <el-table-column prop="status" label="状态" width="100">
          <template #default="{ row }">
            <el-tag type="success" size="small">{{ row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="description" label="描述" win-width="330"></el-table-column>
        <el-table-column label="操作" width="180" fixed="right">
        <el-table-column prop="price" label="ä»·æ ¼" width="140"></el-table-column>
        <el-table-column prop="description" label="描述" width="150"></el-table-column>
        <el-table-column label="操作" width="150" fixed="right" align="center">
          <template #default="{ row }">
            <el-button
              type="text"
              size="small"
              link
                            type="primary"
              @click="() => editCategory(row)"
              :disabled="loading"
            >
              ç¼–辑
            </el-button>
            <el-button
              type="text"
              size="small"
                            link
              @click="() => deleteCategory(row.id)"
              style="color: #f56c6c;"
              :disabled="loading"
@@ -55,10 +62,28 @@
    </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="name">
        <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-form-item label="备件编号" prop="sparePartsNo">
          <el-input v-model="form.sparePartsNo"></el-input>
        </el-form-item>
        <el-form-item label="状态" prop="status">
@@ -70,17 +95,15 @@
        <el-form-item label="描述" prop="description">
          <el-input v-model="form.description"></el-input>
        </el-form-item>
        <el-form-item label="上级分类" prop="parentId">
          <el-select v-model="form.parentId" placeholder="请选择上级分类">
            <el-option label="无上级分类" :value="null"></el-option>
            <el-option
              v-for="(item, index) in categories"
              :key="index"
              :label="item.name"
              :value="item.id"
            ></el-option>
          </el-select>
        <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>
@@ -96,7 +119,8 @@
<script setup>
import { ref, computed, onMounted, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart,getSparePartsTree } from "@/api/equipmentManagement/spareParts";
import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
// åŠ è½½çŠ¶æ€
const loading = ref(false);
@@ -111,8 +135,14 @@
// const renderTableData = computed(() => buildTree(categories.value));
const renderTableData = ref([]);
const operationType = ref('add')
// è®¾å¤‡é€‰é¡¹
const deviceOptions = ref([]);
// è¡¨å•引用
const formRef = ref(null);
// æŸ¥è¯¢å‚æ•°
const queryParams = reactive({
  name: ''
});
// è¡¨å•数据
const form = reactive({
  id:'',
@@ -120,19 +150,34 @@
  sparePartsNo: '',
  status: '',
  description: '',
  parentId: null
  deviceLedgerIds: [],
  price: null
});
// è¡¨å•验证规则
const rules = reactive({
  name: [
    { required: true, message: '请输入分类名称', trigger: 'blur' }
    { required: true, message: '请输入备件名称', trigger: 'blur' }
  ],
  sparePartsNo: [
    { required: true, message: '请输入分类编号', trigger: 'blur' }
    { 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();
        }
      }
    }
  ]
});
// èŽ·å–ç¼©è¿›é‡
@@ -159,54 +204,80 @@
  });
  return result;
};
//获取树形结构
const fetchTreeData = async () => {
  fetchCategories();
// èŽ·å–åˆ—è¡¨æ•°æ®
const fetchListData = async () => {
  loading.value = true;
  try {
    const res = await getSparePartsTree();
    if (res.code === 200) {
      renderTableData.value = res.data;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
    const params = {};
    if (queryParams.name) {
      params.name = queryParams.name;
    }
  }catch (error) {
    ElMessage.error('获取分类列表失败');
    const res = await getSparePartsList(params);
    if (res.code === 200) {
      renderTableData.value = res.data.records || [];
      categories.value = res.data.records || [];
    }
  } catch (error) {
        loading.value = false;
  } finally {
    loading.value = false;
  }
}
// èŽ·å–åˆ†ç±»åˆ—è¡¨
const fetchCategories = async () => {
  loading.value = true;
// æŸ¥è¯¢
const handleQuery = () => {
  fetchListData();
}
// é‡ç½®æŸ¥è¯¢
const resetQuery = () => {
  queryParams.name = '';
  fetchListData();
}
// åŠ è½½è®¾å¤‡åˆ—è¡¨ï¼ˆåœ¨æ‰“å¼€å¼¹æ¡†æ—¶è°ƒç”¨ï¼‰
const loadDeviceName = async () => {
  try {
    const res = await getSparePartsList();
    if (res.code === 200) {
      categories.value = res.data.records;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
    }
    const { data } = await getDeviceLedger();
    deviceOptions.value = data || [];
  } catch (error) {
    ElMessage.error('获取分类列表失败');
  } finally {
    loading.value = false;
    ElMessage.error('获取设备列表失败');
  }
};
// æ–°å¢žåˆ†ç±»
const addCategory = () => {
const addCategory = async () => {
  await loadDeviceName();
  form.id = '';
  form.name = '';
  form.sparePartsNo = '';
  form.status = '';
  form.description = '';
  form.parentId = null;
  form.deviceLedgerIds = [];
  form.price = null;
  operationType.value = 'add'
  dialogVisible.value = true;
  console.log('dialogVisible æ›´æ–°ä¸º', dialogVisible.value);
};
// ç¼–辑分类
const editCategory = (row) => {
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;
};
@@ -223,7 +294,7 @@
    const res = await delSparePart(id);
    if (res.code === 200) {
      ElMessage.success('删除成功');
      fetchTreeData();
      fetchListData();
    } else {
      ElMessage.error(res.message || '删除失败');
    }
@@ -242,19 +313,31 @@
  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(form);
      let res = await editSparePart(submitData);
      if (res.code === 200) {
      ElMessage.success('编辑成功');
      dialogVisible.value = false;
      fetchTreeData();
    }
    } else {
      let res = await addSparePart(form);
        if (res.code === 200) {
        ElMessage.success('编辑成功');
        dialogVisible.value = false;
        fetchTreeData();
        fetchListData();
      }
    } else {
      let res = await addSparePart(submitData);
      if (res.code === 200) {
        ElMessage.success('新增成功');
        dialogVisible.value = false;
        fetchListData();
      }
    }
  } catch (error) {
@@ -264,10 +347,9 @@
  }
};
// ç»„件挂载时获取分类列表
// ç»„件挂载时获取列表数据
onMounted(() => {
  fetchCategories();
  fetchTreeData();
  fetchListData();
});
</script>
@@ -275,43 +357,13 @@
.spare-part-category {
  padding: 20px;
}
.search_form {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
}
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  align-items: center;
}
/* åµŒå¥—树形结构样式 */
.nested-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0 4px;
  height: 30px;
  line-height: 30px;
}
.category-code {
  color: #606266;
  font-size: 12px;
  margin-left: 8px;
}
.tree-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
/* è¡¨æ ¼æ ·å¼è°ƒæ•´ */
.el-table {
  font-size: 14px;
}
.el-table__header-wrapper th {
@@ -321,22 +373,6 @@
.el-table__row:hover > td {
  background-color: #fafafa;
}
/* åµŒå¥—树形结构特定样式 */
.nested-tree {
  background-color: transparent;
}
.nested-tree .el-tree-node__content {
  height: auto;
  background-color: transparent;
}
/* æœç´¢æ¡†æ ·å¼è°ƒæ•´ */
.actions .el-input {
  margin-right: 10px;
  width: 200px;
}
/* æŒ‰é’®ç»„样式 */
@@ -354,64 +390,5 @@
.nested-tree .el-tree-node__expand-icon {
  font-size: 12px;
  margin-right: 4px;
}
/* ç©ºçŠ¶æ€æ ·å¼ */
.el-table .cell:empty::before {
  content: '-';
  color: #c0c4cc;
}
/* å±•å¼€/折叠功能样式 */
.expand-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  text-align: center;
  line-height: 20px;
  cursor: pointer;
  font-size: 12px;
  color: #909399;
}
.expand-icon.expanded {
  color: #409eff;
}
/* å±•开内容样式 */
.expand-content {
  padding: 10px 20px;
}
/* å­çº§å†…容样式 */
.child-content {
  padding-left: 40px;
}
/* æ— å­åˆ†ç±»æç¤ºæ ·å¼ */
.no-children {
  padding: 10px 20px;
  color: #909399;
  font-size: 14px;
}
/* ç¡®ä¿å±•开的表格样式正确 */
.el-table .el-table__expanded-cell {
  background-color: #fafafa;
}
/* å±•开的子表格样式 */
.el-table .el-table {
  border-top: none;
  border-bottom: none;
}
/* å±•开的子表格单元格样式 */
.expand-content .el-table__body-wrapper .el-table__row {
  background-color: #ffffff;
}
.expand-content .el-table__body-wrapper .el-table__row:hover > td {
  background-color: #fafafa;
}
</style>
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue
@@ -17,11 +17,22 @@
        style="width: 100%"
      />
    </el-form-item>
    <el-form-item label="保养状态">
      <el-select v-model="form.status">
        <el-option label="待保养" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="保养结果">
      <el-select v-model="form.maintenanceResult" placeholder="请选择保养结果">
      <!-- <el-select v-model="form.maintenanceResult" placeholder="请选择保养结果">
        <el-option label="完好" :value="1"></el-option>
        <el-option label="ç»´ä¿®" :value="0"></el-option>
      </el-select>
      </el-select> -->
      <el-input
        v-model="form.maintenanceResult"
        placeholder="请输入保养结果"
        type="text" />
    </el-form-item>
  </el-form>
</template>
@@ -40,6 +51,7 @@
  maintenanceActuallyName: undefined, // å®žé™…保养人
  maintenanceActuallyTime: undefined, // å®žé™…保养日期
  maintenanceResult: undefined, // ä¿å…»ç»“æžœ
  status: 0, // ä¿å…»çŠ¶æ€
});
const setForm = (data) => {
src/views/equipmentManagement/upkeep/Form/PlanForm.vue
@@ -5,6 +5,9 @@
        v-model="form.deviceLedgerId"
        @change="setDeviceModel"
        placeholder="请选择设备"
        filterable
        default-first-option
        :reserve-keyword="false"
      >
        <el-option
          v-for="(item, index) in deviceOptions"
@@ -20,6 +23,30 @@
        placeholder="请输入规格型号"
        disabled
      />
    </el-form-item>
    <el-form-item label="录入人">
      <el-select
        v-model="form.createUser"
        placeholder="请选择"
        filterable
        default-first-option
        :reserve-keyword="false"
        clearable
      >
        <el-option
          v-for="item in userList"
          :key="item.userId"
          :label="item.nickName"
          :value="item.userId"
        />
      </el-select>
    </el-form-item>
    <el-form-item v-if="id" label="保修状态">
      <el-select v-model="form.status">
        <el-option label="待保修" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="计划保养日期">
      <el-date-picker
@@ -40,6 +67,7 @@
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { onMounted } from "vue";
import dayjs from "dayjs";
import { userListNoPage } from "@/api/system/user.js";
defineOptions({
  name: "计划表单",
@@ -51,11 +79,15 @@
  deviceOptions.value = data;
};
const { id } = defineProps(['id']);
const { form, resetForm } = useFormData({
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  maintenancePlanTime: undefined, // è®¡åˆ’保养日期
  createUser: undefined, // å½•入人
  status: 0, //保修状态
});
const setDeviceModel = (id) => {
@@ -75,15 +107,23 @@
  form.deviceLedgerId = data.deviceLedgerId;
  form.deviceName = data.deviceName;
  form.deviceModel = data.deviceModel;
  form.createUser = Number(data.createUser);
  form.status = data.status;
  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
    "YYYY-MM-DD HH:mm:ss"
  );
};
// ç”¨æˆ·åˆ—表
const userList = ref([]);
const loadForm = () => {};
onMounted(() => {
  loadDeviceName();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
});
defineExpose({
src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
  <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr" draggable>
    <MaintenanceForm ref="maintenanceFormRef" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
@@ -3,9 +3,10 @@
    v-model="visible"
    :title="modalOptions.title"
    width="30%"
        draggable
    @close="close"
  >
    <PlanForm ref="planFormRef"></PlanForm>
    <PlanForm ref="planFormRef" :id="id"></PlanForm>
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
src/views/equipmentManagement/upkeep/Modal/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,304 @@
<template>
    <div>
        <el-dialog :title="operationType === 'add' ? '新增保养任务' : '编辑保养任务'"
                             v-model="dialogVisitable" width="800px" @close="cancel">
            <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="taskId">
                            <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                                <el-option
                                    v-for="(item, index) in deviceOptions"
                                    :key="index"
                                    :label="item.deviceName"
                                    :value="item.id"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="规格型号">
                            <el-input
                                v-model="form.deviceModel"
                                placeholder="请输入规格型号"
                                disabled
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="录入人" prop="inspector">
                            <el-select
                                v-model="form.inspector"
                                filterable
                                default-first-option
                                :reserve-keyword="false"
                                placeholder="请选择"
                                clearable
                            >
                                <el-option
                                    v-for="item in userList"
                                    :label="item.nickName"
                                    :value="item.userId"
                                    :key="item.userId"
                                />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="登记时间" prop="registrationDate">
                            <el-date-picker
                                v-model="form.registrationDate"
                                type="date"
                                placeholder="选择登记日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="任务频率" prop="frequencyType">
                            <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                                <el-option label="每日" value="DAILY"/>
                                <el-option label="每周" value="WEEKLY"/>
                                <el-option label="每月" value="MONTHLY"/>
                                <el-option label="季度" value="QUARTERLY"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                                <el-option label="周一" value="MON"/>
                                <el-option label="周二" value="TUE"/>
                                <el-option label="周三" value="WED"/>
                                <el-option label="周四" value="THU"/>
                                <el-option label="周五" value="FRI"/>
                                <el-option label="周六" value="SAT"/>
                                <el-option label="周日" value="SUN"/>
                            </el-select>
                            <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm"  style="width: 50%"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="DD,HH:mm"
                                value-format="DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="MM,DD,HH:mm"
                                value-format="MM,DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="备注" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">保存</el-button>
                    <el-button @click="cancel">取消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
import { reactive, ref, getCurrentInstance, toRefs } from "vue";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { deviceMaintenanceTaskAdd, deviceMaintenanceTaskEdit } from "@/api/equipmentManagement/upkeep";
import { getCurrentDate } from "@/utils/index.js";
import useUserStore from "@/store/modules/user.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits()
const dialogVisitable = ref(false);
const operationType = ref('add');
const deviceOptions = ref([]);
const userStore = useUserStore();
const data = reactive({
    form: {
        taskId: undefined,
        taskName: undefined,
        // å½•入人:单选一个用户 id
        inspector: undefined,
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        deviceModel: undefined, // è§„格型号
        registrationDate: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请选择录入人", trigger: "blur" },],
        registrationDate: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
})
const { form, rules } = toRefs(data)
const userList = ref([])
const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
};
// é€‰æ‹©è®¾å¤‡æ—¶ï¼Œå›žå¡«è®¾å¤‡åç§°(taskName)和规格型号(deviceModel)
const setDeviceModel = (id) => {
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskId = option.id;
        form.value.taskName = option.deviceName;
        form.value.deviceModel = option.deviceModel;
    }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
    dialogVisitable.value = true
    operationType.value = type
    // é‡ç½®è¡¨å•
    resetForm();
    // åŠ è½½ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // åŠ è½½è®¾å¤‡åˆ—è¡¨
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = { ...row }
        // ç¼–辑时用接口返回的 registrantId å›žæ˜¾å½•入人
        if (row.registrantId) {
            form.value.inspector = row.registrantId
        }
        // å¦‚果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
    } else if (type === 'add') {
        // æ–°å¢žæ—¶è®¾ç½®ç™»è®°æ—¥æœŸä¸ºå½“天
        form.value.registrationDate = getCurrentDate();
        // æ–°å¢žæ—¶è®¾ç½®å½•入人为当前登录账户
        form.value.inspector = userStore.id;
    }
}
// å…³é—­å¯¹è¯æ¡†
const cancel = () => {
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
}
// é‡ç½®è¡¨å•函数
const resetForm = () => {
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: undefined,
        inspector: undefined,
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        deviceModel: undefined,
        registrationDate: ''
    }
}
// æäº¤è¡¨å•
const submitForm = () => {
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                const payload = { ...form.value }
                // ä¸å†å‘后端传保养人字段,仅使用接口要求的 registrant / registrantId
                // æ ¹æ®é€‰æ‹©çš„“录入人”设置 registrant / registrantId
                if (payload.inspector) {
                    const selectedUser = userList.value.find(
                        (u) => String(u.userId) === String(payload.inspector)
                    )
                    if (selectedUser) {
                        payload.registrantId = selectedUser.userId
                        payload.registrant = selectedUser.nickName
                    }
                }
                delete payload.inspector
                delete payload.inspectorIds
                if (payload.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = payload.week + ',' + payload.time
                    payload.frequencyDetail = frequencyDetail
                }
                // å½•入日期:直接使用表单里的 registrationDate å­—段
                // ä¸€äº›é»˜è®¤çŠ¶æ€å­—æ®µ
                if (payload.status === undefined || payload.status === null || payload.status === '') {
                    payload.status = '0' // é»˜è®¤çŠ¶æ€ï¼Œå¯æŒ‰å®žé™…æžšä¸¾è°ƒæ•´
                }
                payload.active = true
                payload.deleted = 0
                if (operationType.value === 'edit') {
                    await deviceMaintenanceTaskEdit(payload)
                } else {
                    await deviceMaintenanceTaskAdd(payload)
                }
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
}
defineExpose({ openDialog })
</script>
<style scoped>
</style>
src/views/equipmentManagement/upkeep/index.vue
@@ -1,78 +1,165 @@
<template>
  <div class="app-container">
    <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 class="table_list">
      <div class="actions">
        <el-text class="mx-1" size="large">设备保养</el-text>
        <div>
          <el-button
            type="primary"
            icon="Plus"
            :disabled="multipleList.length !== 1"
            @click="addMaintain"
          >
            æ–°å¢žä¿å…»
          </el-button>
          <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>
    <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>
      <PIMTable
        <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"
                text
                icon="editPen"
                @click="editScheduledTask(row)"
              >
                ç¼–辑
              </el-button>
              <el-button
                type="danger"
                text
                icon="delete"
                @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="primary"
                icon="Plus"
                :disabled="multipleList.length !== 1"
                @click="addMaintain"
              >
                æ–°å¢žä¿å…»
              </el-button>
              <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>
         <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
@@ -86,16 +173,12 @@
        @pagination="changePage"
      >
        <template #maintenanceResultRef="{ row }">
          <el-tag v-if="row.maintenanceResult === 1" type="success">
            å®Œå¥½
          </el-tag>
          <el-tag v-if="row.maintenanceResult === 0" type="danger">
            ç»´ä¿®
          </el-tag>
          <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="danger">待保养</el-tag>
          <el-tag v-if="row.status === 0" type="warning">待保养</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
@@ -116,189 +199,369 @@
          </el-button>
        </template>
      </PIMTable>
    </div>
        </div>
      </el-tab-pane>
    </el-tabs>
    <PlanModal ref="planModalRef" @ok="getTableData" />
    <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
        <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
        <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
  </div>
</template>
<script setup>
import { usePaginationApi } from "@/hooks/usePaginationApi";
import { getUpkeepPage, delUpkeep } from "@/api/equipmentManagement/upkeep";
import { onMounted, getCurrentInstance } from "vue";
import PlanModal from "./Modal/PlanModal.vue";
import MaintenanceModal from "./Modal/MaintenanceModal.vue";
import dayjs from "dayjs";
import { ElMessageBox, ElMessage } from "element-plus";
import { ref, onMounted, reactive, getCurrentInstance, nextTick } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import PlanModal from './Modal/PlanModal.vue'
import MaintenanceModal from './Modal/MaintenanceModal.vue'
import FormDia from './Modal/formDia.vue'
import {
  getUpkeepPage,
  delUpkeep,
  deviceMaintenanceTaskList,
  deviceMaintenanceTaskDel,
} from '@/api/equipmentManagement/upkeep'
import dayjs from 'dayjs'
defineOptions({
  name: "设备保养",
});
const { proxy } = getCurrentInstance()
const { proxy } = getCurrentInstance();
// Tab相关
const activeTab = ref('scheduled')
// è®¡åˆ’弹窗控制器
const planModalRef = ref();
const planModalRef = ref()
// ä¿å…»å¼¹çª—控制器
const maintainModalRef = ref();
const maintainModalRef = ref()
// å®šæ—¶ä»»åŠ¡å¼¹çª—æŽ§åˆ¶å™¨
const formDiaRef = ref()
// è¡¨æ ¼å¤šé€‰æ¡†é€‰ä¸­é¡¹
const multipleList = ref([]);
// ä»»åŠ¡è®°å½•tab(原设备保养页面)相关变量
const filters = reactive({
  deviceName: '',
  maintenancePlanTime: '',
  maintenanceActuallyTime: '',
  maintenanceActuallyName: '',
})
// å¤šé€‰åŽåšä»€ä¹ˆ
const handleSelectionChange = (selectionList) => {
  multipleList.value = selectionList;
};
const dataList = ref([])
const pagination = ref({
  currentPage: 1,
  pageSize: 10,
  total: 0,
})
const multipleList = ref([])
// è¡¨æ ¼é’©å­
const {
  filters,
  columns,
  dataList,
  pagination,
  getTableData,
  resetFilters,
  onCurrentChange,
} = usePaginationApi(getUpkeepPage, {
  deviceName: undefined,
  maintenancePlanTime: undefined,
  maintenanceActuallyTime: undefined,
  maintenanceActuallyName: undefined,
}, [
  {
    label: "设备名称",
    align: "center",
    prop: "deviceName",
  },
  {
    label: "规格型号",
    align: "center",
    prop: "deviceModel",
  },
  {
    label: "计划保养日期",
    align: "center",
    prop: "maintenancePlanTime",
    formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
  },
  {
    label: "录入人",
    align: "center",
    prop: "createUserName",
  },
  {
    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: "200px",
  },
]);
// type == 1实际保养时间 2计划保养时间
const handleDateChange = (value,type) => {
  filters.maintenanceActuallyTimeReq = null
  filters.maintenancePlanTimeReq = null
  if(type === 1){
    if (value) {
      filters.maintenanceActuallyTimeReq = dayjs(value).format("YYYY-MM-DD");
    }
  }else{
    if (value) {
      filters.maintenancePlanTimeReq = dayjs(value).format("YYYY-MM-DD");
    }
// å®šæ—¶ä»»åŠ¡ç®¡ç†tab相关变量
const scheduledFilters = reactive({
  taskName: '',
  status: '',
})
const scheduledDataList = ref([])
const scheduledPagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
})
const scheduledMultipleList = ref([])
// å®šæ—¶ä»»åŠ¡ç®¡ç†è¡¨æ ¼åˆ—é…ç½®
const scheduledColumns = ref([
    { prop: "taskName", label: "设备名称"},
    {
        label: "规格型号",
        prop: "deviceModel",
    },
    {
        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: "registrant", label: "登记人", minWidth: 100 },
    { prop: "registrationDate", label: "登记日期", minWidth: 100 },
    {
        fixed: "right",
        label: "操作",
        dataType: "slot",
        slot: "operation",
        align: "center",
        width: "200px",
    },
])
// ä»»åŠ¡è®°å½•è¡¨æ ¼åˆ—é…ç½®ï¼ˆåŽŸè®¾å¤‡ä¿å…»è¡¨æ ¼åˆ—ï¼‰
const columns = ref([
    {
        label: "设备名称",
        align: "center",
        prop: "deviceName",
    },
    {
        label: "规格型号",
        align: "center",
        prop: "deviceModel",
    },
    {
        label: "计划保养日期",
        align: "center",
        prop: "maintenancePlanTime",
        formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
    },
    {
        label: "录入人",
        align: "center",
        prop: "createUserName",
    },
    // {
    //   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: "200px",
    },
])
// Tab切换处理
const handleTabChange = (tabName) => {
  if (tabName === 'record') {
    getTableData()
  } else if (tabName === 'scheduled') {
    getScheduledTableData()
  }
  getTableData();
};
}
// æ–°å¢žä¿å…»
const addMaintain = () => {
  const row = multipleList.value[0];
  maintainModalRef.value.open(row.id, row);
};
// æ–°å¢žè®¡åˆ’
const addPlan = () => {
  planModalRef.value.openModal();
};
// ç¼–辑计划
const editPlan = (id) => {
  planModalRef.value.openEdit(id);
};
const changePage = ({ page, limit }) => {
    pagination.currentPage = page;
    pagination.pageSize = limit;
    onCurrentChange(page);
};
// å•行删除
const delRepairByIds = async (ids) => {
  ElMessageBox.confirm("确认删除报修数据, æ­¤æ“ä½œä¸å¯é€†?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    const { code } = await delUpkeep(ids);
    if (code === 200) {
      ElMessage.success("删除成功");
      getTableData();
// å®šæ—¶ä»»åŠ¡ç®¡ç†ç›¸å…³æ–¹æ³•
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 resetScheduledFilters = () => {
  scheduledFilters.taskName = ''
  scheduledFilters.status = ''
  getScheduledTableData()
}
const handleScheduledSelectionChange = (selection) => {
  scheduledMultipleList.value = selection
}
const changeScheduledPage = (page) => {
  scheduledPagination.currentPage = page.page
  scheduledPagination.pageSize = page.limit
  getScheduledTableData()
}
const addScheduledTask = () => {
  nextTick(() => {
        formDiaRef.value?.openDialog('add');
    });
}
const editScheduledTask = (row) => {
  if (row) {
        nextTick(() => {
            formDiaRef.value?.openDialog('edit', row);
        });
  }
}
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 handleScheduledOut = () => {
  ElMessage.info('导出定时任务功能待实现')
}
// ä»»åŠ¡è®°å½•ç›¸å…³æ–¹æ³•ï¼ˆåŽŸè®¾å¤‡ä¿å…»é¡µé¢æ–¹æ³•ï¼‰
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 { code, data } = await getUpkeepPage(params)
    if (code === 200) {
      dataList.value = data.records
      pagination.value.total = data.total
    }
  } catch (error) {
    console.log(error);
  }
}
const resetFilters = () => {
  filters.deviceName = ''
  filters.maintenancePlanTime = ''
  filters.maintenanceActuallyTime = ''
  filters.maintenanceActuallyName = ''
  getTableData()
}
const handleSelectionChange = (selection) => {
  multipleList.value = selection
}
const changePage = (page) => {
  pagination.value.currentPage = page.page
  pagination.value.pageSize = page.limit
  getTableData()
}
const addMaintain = () => {
  const row = multipleList.value[0]
  maintainModalRef.value.open(row.id, row)
}
const addPlan = () => {
  planModalRef.value.openModal()
}
const editPlan = (id) => {
  planModalRef.value.openEdit(id)
}
const delRepairByIds = async (ids) => {
  try {
    await ElMessageBox.confirm('确认删除保养数据, æ­¤æ“ä½œä¸å¯é€†?', '警告', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
    const { code } = await delUpkeep(ids)
    if (code === 200) {
      ElMessage.success('删除成功')
      getTableData()
    }
  } catch (error) {
    // ç”¨æˆ·å–消删除
  }
}
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  ElMessageBox.confirm('选中的内容将被导出,是否确认导出?', '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  })
    .then(() => {
      proxy.download("/device/maintenance/export", {}, "设备保养.xlsx");
      proxy.download('/device/maintenance/export', {}, '设备保养.xlsx')
    })
    .catch(() => {
      ElMessage.info("已取消");
    });
};
      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()
}
onMounted(() => {
  getTableData();
});
  // æ ¹æ®é»˜è®¤æ¿€æ´»çš„ Tab è°ƒç”¨å¯¹åº”的查询接口
  if (activeTab.value === 'scheduled') {
    getScheduledTableData()
  } else {
    getTableData()
  }
})
</script>
<style lang="scss" scoped>
@@ -311,3 +574,8 @@
  margin-bottom: 10px;
}
</style>
src/views/procurementManagement/procurementLedger/index.vue
@@ -44,32 +44,32 @@
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        show-summary
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        height="calc(100vh - 18.5em)"
        :row-class-name="tableRowClassName"
          :data="tableData"
          border
          v-loading="tableLoading"
          @selection-change="handleSelectionChange"
          :expand-row-keys="expandedRowKeys"
          :row-key="(row) => row.id"
          show-summary
          :summary-method="summarizeMainTable"
          @expand-change="expandChange"
          height="calc(100vh - 18.5em)"
          :row-class-name="tableRowClassName"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table
              :data="props.row.children"
              border
              show-summary
              :summary-method="summarizeChildrenTable"
                :data="props.row.children"
                border
                show-summary
                :summary-method="summarizeChildrenTable"
            >
              <el-table-column
                align="center"
                label="序号"
                type="index"
                width="60"
                  align="center"
                  label="序号"
                  type="index"
                  width="60"
              />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
@@ -77,41 +77,41 @@
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column
                label="含税单价(元)"
                prop="taxInclusiveUnitPrice"
                :formatter="formattedNumber"
                  label="含税单价(元)"
                  prop="taxInclusiveUnitPrice"
                  :formatter="formattedNumber"
              />
              <el-table-column
                label="含税总价(元)"
                prop="taxInclusiveTotalPrice"
                :formatter="formattedNumber"
                  label="含税总价(元)"
                  prop="taxInclusiveTotalPrice"
                  :formatter="formattedNumber"
              />
              <el-table-column
                label="不含税总价(元)"
                prop="taxExclusiveTotalPrice"
                :formatter="formattedNumber"
                  label="不含税总价(元)"
                  prop="taxExclusiveTotalPrice"
                  :formatter="formattedNumber"
              />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="采购合同号"
          prop="purchaseContractNumber"
          width="200"
          show-overflow-tooltip
            label="采购合同号"
            prop="purchaseContractNumber"
            width="200"
            show-overflow-tooltip
        />
        <el-table-column
          label="销售合同号"
          prop="salesContractNo"
          width="200"
          show-overflow-tooltip
            label="销售合同号"
            prop="salesContractNo"
            width="200"
            show-overflow-tooltip
        />
        <el-table-column
          label="供应商名称"
          width="240"
          prop="supplierName"
          show-overflow-tooltip
            label="供应商名称"
            width="240"
            prop="supplierName"
            show-overflow-tooltip
        />
        <el-table-column label="订单状态" width="100" align="center">
          <template #default="scope">
@@ -120,10 +120,10 @@
          </template>
        </el-table-column>
        <el-table-column
          label="项目名称"
          prop="projectName"
          width="420"
          show-overflow-tooltip
            label="项目名称"
            prop="projectName"
            width="420"
            show-overflow-tooltip
        />
        <el-table-column
            label="审批状态"
@@ -140,100 +140,100 @@
          </template>
        </el-table-column>
        <el-table-column
          label="付款方式"
          width="100"
          prop="paymentMethod"
          show-overflow-tooltip
            label="付款方式"
            width="100"
            prop="paymentMethod"
            show-overflow-tooltip
        />
        <el-table-column
          label="合同金额(元)"
          prop="contractAmount"
           width="200"
          show-overflow-tooltip
          :formatter="formattedNumber"
            label="合同金额(元)"
            prop="contractAmount"
            width="200"
            show-overflow-tooltip
            :formatter="formattedNumber"
        />
        <el-table-column
          label="录入人"
          prop="recorderName"
           width="100"
          show-overflow-tooltip
            label="录入人"
            prop="recorderName"
            width="100"
            show-overflow-tooltip
        />
        <el-table-column
          label="录入日期"
          prop="entryDate"
           width="100"
          show-overflow-tooltip
            label="录入日期"
            prop="entryDate"
            width="100"
            show-overflow-tooltip
        />
        <el-table-column
          fixed="right"
          label="操作"
          min-width="150"
          align="center"
            fixed="right"
            label="操作"
            min-width="150"
            align="center"
        >
          <template #default="scope">
            <el-button
              link
              type="primary"
              size="small"
              @click="openForm('edit', scope.row)"
              >编辑</el-button
                link
                type="primary"
                size="small"
                @click="openForm('edit', scope.row)"
            >编辑</el-button
            >
            <el-button
              link
              type="success"
              size="small"
              @click="showQRCode(scope.row)"
              >生成二维码</el-button
                link
                type="success"
                size="small"
                @click="showQRCode(scope.row)"
            >生成二维码</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationChange"
      />
    </div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
      width="70%"
      @close="closeDia"
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
        width="70%"
        @close="closeDia"
    >
      <el-form
        :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
          :model="form"
          label-width="140px"
          label-position="top"
          :rules="rules"
          ref="formRef"
      >
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="采购合同号:" prop="purchaseContractNumber">
              <el-input
                v-model="form.purchaseContractNumber"
                placeholder="请输入"
                clearable
                  v-model="form.purchaseContractNumber"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesLedgerId">
              <el-select
                v-model="form.salesLedgerId"
                placeholder="请选择"
                clearable
                @change="salesLedgerChange"
                  v-model="form.salesLedgerId"
                  placeholder="请选择"
                  clearable
                  @change="salesLedgerChange"
              >
                <el-option
                  v-for="item in salesContractList"
                  :key="item.id"
                  :label="item.salesContractNo"
                  :value="item.id"
                    v-for="item in salesContractList"
                    :key="item.id"
                    :label="item.salesContractNo"
                    :value="item.id"
                />
              </el-select>
            </el-form-item>
@@ -243,17 +243,17 @@
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierId">
              <el-select
                v-model="form.supplierId"
                placeholder="请选择"
                clearable
                filterable
                allow-create
                  v-model="form.supplierId"
                  placeholder="请选择"
                  clearable
                  filterable
                  allow-create
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                    v-for="item in supplierList"
                    :key="item.id"
                    :label="item.supplierName"
                    :value="item.id"
                />
              </el-select>
            </el-form-item>
@@ -261,51 +261,65 @@
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="form.projectName"
                placeholder="请输入"
                clearable
                  v-model="form.projectName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="付款方式">
                            <el-input
                                v-model="form.paymentMethod"
                                placeholder="请输入"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker
                                style="width: 100%"
                                v-model="form.executionDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:" prop="recorderId">
            <el-form-item label="付款方式">
              <el-input
                  v-model="form.paymentMethod"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="签订日期:" prop="executionDate">
              <el-date-picker
                  style="width: 100%"
                  v-model="form.executionDate"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  type="date"
                  placeholder="请选择"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="审批人:" prop="approverId">
              <el-select
                v-model="form.recorderId"
                placeholder="请选择"
                clearable
                disabled
                  v-model="form.approverId"
                  placeholder="请选择审批人"
                  clearable
              >
                <el-option
                  v-for="item in userList"
                  :key="item.userId"
                  :label="item.nickName"
                  :value="item.userId"
                    v-for="item in userList"
                    :key="item.userId"
                    :label="item.nickName"
                    :value="item.userId"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="录入人:" prop="recorderId" v-show="false">
              <el-select
                  v-model="form.recorderId"
                  placeholder="请选择"
                  clearable
                  disabled
              >
                <el-option
                    v-for="item in userList"
                    :key="item.userId"
                    :label="item.nickName"
                    :value="item.userId"
                />
              </el-select>
            </el-form-item>
@@ -313,13 +327,13 @@
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.entryDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
                  style="width: 100%"
                  v-model="form.entryDate"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  type="date"
                  placeholder="请选择"
                  clearable
              />
            </el-form-item>
          </el-col>
@@ -327,64 +341,95 @@
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button type="primary" @click="openProductForm('add')"
              >添加</el-button
            >添加</el-button
            >
            <el-button plain type="danger" @click="deleteProduct"
              >删除</el-button
            >删除</el-button
            >
          </el-form-item>
          <div class="select-button-group" style="width: 220px; margin: 20px 0;" v-if="operationType === 'add'">
            <el-select
                filterable
                allow-create
                :reserve-keyword="true"
                :default-first-option="false"
                v-model="templateName"
                :input-value="filterInputValue"
                @filter-change="onTemplateFilterChange"
                @change="onTemplateChange"
                style="width: 180px; border-right: none; border-radius: 4px 0 0 4px;"
                placeholder="请选择"
                class="no-arrow-select"
            >
              <el-option
                  v-for="item in templateList"
                  :key="item.value"
                  :label="item.templateName"
                  :value="item.templateName"
              ></el-option>
            </el-select>
            <!-- æŒ‰é’®ï¼šä¸Ž Select é«˜åº¦åŒ¹é…ï¼ŒåŽ»æŽ‰å·¦ä¾§è¾¹æ¡†ï¼Œæ— ç¼è¡”æŽ¥ -->
            <el-button
                size="small"
                style="height: 32px; border-radius: 0 4px 4px 0; margin-left: -1px;"
                @click="handleButtonClick"
                :disabled="!templateName || templateName.trim() === '' || isTemplateNameDuplicate"
            >
              ä¿å­˜
            </el-button>
          </div>
        </el-row>
        <el-table
          :data="productData"
          border
          @selection-change="productSelected"
          show-summary
          :summary-method="summarizeProTable"
            :data="productData"
            border
            @selection-change="productSelected"
            show-summary
            :summary-method="summarizeProTable"
        >
          <el-table-column align="center" type="selection" width="55" />
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
              align="center"
              label="序号"
              type="index"
              width="60"
          />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" width="70" />
          <el-table-column label="数量" prop="quantity" width="70" />
                    <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip />
          <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip />
          <el-table-column label="税率(%)" prop="taxRate" width="80" />
          <el-table-column
            label="含税单价(元)"
            prop="taxInclusiveUnitPrice"
            :formatter="formattedNumber"
            width="150"
              label="含税单价(元)"
              prop="taxInclusiveUnitPrice"
              :formatter="formattedNumber"
              width="150"
          />
          <el-table-column
            label="含税总价(元)"
            prop="taxInclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
              label="含税总价(元)"
              prop="taxInclusiveTotalPrice"
              :formatter="formattedNumber"
              width="150"
          />
          <el-table-column
            label="不含税总价(元)"
            prop="taxExclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
              label="不含税总价(元)"
              prop="taxExclusiveTotalPrice"
              :formatter="formattedNumber"
              width="150"
          />
          <el-table-column
            fixed="right"
            label="操作"
            min-width="60"
            align="center"
              fixed="right"
              label="操作"
              min-width="60"
              align="center"
          >
            <template #default="scope">
              <el-button
                link
                type="primary"
                size="small"
                @click="openProductForm('edit', scope.row, scope.$index)"
                >编辑</el-button
                  link
                  type="primary"
                  size="small"
                  @click="openProductForm('edit', scope.row, scope.$index)"
              >编辑</el-button
              >
            </template>
          </el-table-column>
@@ -393,11 +438,11 @@
          <el-col :span="24">
            <el-form-item label="备注·:" prop="remark">
              <el-input
                v-model="form.remark"
                placeholder="请输入"
                clearable
                type="textarea"
                :rows="2"
                  v-model="form.remark"
                  placeholder="请输入"
                  clearable
                  type="textarea"
                  :rows="2"
              />
            </el-form-item>
          </el-col>
@@ -406,16 +451,16 @@
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload
                v-model:file-list="fileList"
                :action="upload.url"
                multiple
                ref="fileUpload"
                auto-upload
                :headers="upload.headers"
                :before-upload="handleBeforeUpload"
                :on-error="handleUploadError"
                :on-success="handleUploadSuccess"
                :on-remove="handleRemove"
                  v-model:file-list="fileList"
                  :action="upload.url"
                  multiple
                  ref="fileUpload"
                  auto-upload
                  :headers="upload.headers"
                  :before-upload="handleBeforeUpload"
                  :on-error="handleUploadError"
                  :on-success="handleUploadSuccess"
                  :on-remove="handleRemove"
              >
                <el-button type="primary">上传</el-button>
                <template #tip>
@@ -437,30 +482,30 @@
      </template>
    </el-dialog>
    <el-dialog
      v-model="productFormVisible"
      :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
      width="40%"
      @close="closeProductDia"
        v-model="productFormVisible"
        :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
        width="40%"
        @close="closeProductDia"
    >
      <el-form
        :model="productForm"
        label-width="140px"
        label-position="top"
        :rules="productRules"
        ref="productFormRef"
          :model="productForm"
          label-width="140px"
          label-position="top"
          :rules="productRules"
          ref="productFormRef"
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:" prop="productId">
              <el-tree-select
                v-model="productForm.productId"
                placeholder="请选择"
                clearable
                check-strictly
                @change="getModels"
                :data="productOptions"
                :render-after-expand="false"
                style="width: 100%"
                  v-model="productForm.productId"
                  placeholder="请选择"
                  clearable
                  check-strictly
                  @change="getModels"
                  :data="productOptions"
                  :render-after-expand="false"
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
@@ -469,16 +514,16 @@
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                v-model="productForm.productModelId"
                placeholder="请选择"
                clearable
                @change="getProductModel"
                  v-model="productForm.productModelId"
                  placeholder="请选择"
                  clearable
                  @change="getProductModel"
              >
                <el-option
                  v-for="item in modelOptions"
                  :key="item.id"
                  :label="item.model"
                  :value="item.id"
                    v-for="item in modelOptions"
                    :key="item.id"
                    :label="item.model"
                    :value="item.id"
                />
              </el-select>
            </el-form-item>
@@ -488,75 +533,75 @@
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="productForm.unit"
                placeholder="请输入"
                clearable
                  v-model="productForm.unit"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select
                                v-model="productForm.taxRate"
                                placeholder="请选择"
                                clearable
                                @change="mathNum"
                            >
                                <el-option label="1" value="1" />
                                <el-option label="6" value="6" />
                                <el-option label="13" value="13" />
                            </el-select>
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="税率(%):" prop="taxRate">
              <el-select
                  v-model="productForm.taxRate"
                  placeholder="请选择"
                  clearable
                  @change="mathNum"
              >
                <el-option label="1" value="1" />
                <el-option label="6" value="6" />
                <el-option label="13" value="13" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number
                v-model="productForm.taxInclusiveUnitPrice"
                :precision="2"
                :step="0.1"
                clearable
                style="width: 100%"
                @change="mathNum"
                  v-model="productForm.taxInclusiveUnitPrice"
                  :precision="2"
                  :step="0.1"
                  clearable
                  style="width: 100%"
                  @change="mathNum"
              />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number
                                :step="0.1"
                                clearable
                                :precision="2"
                                style="width: 100%"
                                v-model="productForm.quantity"
                                placeholder="请输入"
                                @change="mathNum"
                            />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input-number
                  :step="0.1"
                  clearable
                  :precision="2"
                  style="width: 100%"
                  v-model="productForm.quantity"
                  placeholder="请输入"
                  @change="mathNum"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input-number
                v-model="productForm.taxInclusiveTotalPrice"
                :precision="2"
                :step="0.1"
                clearable
                style="width: 100%"
                @change="reverseMathNum('taxInclusiveTotalPrice')"
                  v-model="productForm.taxInclusiveTotalPrice"
                  :precision="2"
                  :step="0.1"
                  clearable
                  style="width: 100%"
                  @change="reverseMathNum('taxInclusiveTotalPrice')"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
              label="不含税总价(元):"
              prop="taxExclusiveTotalPrice"
                label="不含税总价(元):"
                prop="taxExclusiveTotalPrice"
            >
              <el-input
                v-model="productForm.taxExclusiveTotalPrice"
                @change="reverseMathNum('taxExclusiveTotalPrice')"
              <el-input
                  v-model="productForm.taxExclusiveTotalPrice"
                  @change="reverseMathNum('taxExclusiveTotalPrice')"
              />
            </el-form-item>
          </el-col>
@@ -565,26 +610,26 @@
          <el-col :span="12">
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select
                v-model="productForm.invoiceType"
                placeholder="请选择"
                clearable
                  v-model="productForm.invoiceType"
                  placeholder="请选择"
                  clearable
              >
                <el-option label="增普票" value="增普票" />
                <el-option label="增专票" value="增专票" />
              </el-select>
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="库存预警数量:" prop="warnNum">
                            <el-input-number
                                v-model="productForm.warnNum"
                                :precision="2"
                                :step="0.1"
                                clearable
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="库存预警数量:" prop="warnNum">
              <el-input-number
                  v-model="productForm.warnNum"
                  :precision="2"
                  :step="0.1"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
@@ -594,13 +639,13 @@
        </div>
      </template>
    </el-dialog>
    <!-- äºŒç»´ç æ˜¾ç¤ºå¯¹è¯æ¡† -->
    <el-dialog
      v-model="qrCodeDialogVisible"
      title="采购合同号二维码"
      width="400px"
      center
        v-model="qrCodeDialogVisible"
        title="采购合同号二维码"
        width="400px"
        center
    >
      <div style="text-align: center;">
        <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
@@ -612,27 +657,27 @@
    <!-- æ‰«ç æ–°å¢žå¯¹è¯æ¡† -->
    <el-dialog
      v-model="scanAddDialogVisible"
      title="扫码新增采购台账"
      width="70%"
      @close="closeScanAddDialog"
        v-model="scanAddDialogVisible"
        title="扫码新增采购台账"
        width="70%"
        @close="closeScanAddDialog"
    >
      <el-form
        :model="scanAddForm"
        label-width="140px"
        label-position="top"
        :rules="scanAddRules"
        ref="scanAddFormRef"
          :model="scanAddForm"
          label-width="140px"
          label-position="top"
          :rules="scanAddRules"
          ref="scanAddFormRef"
      >
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码内容:">
              <el-input
                v-model="scanAddForm.scanContent"
                type="textarea"
                :rows="3"
                placeholder="请扫描二维码或手动输入采购合同信息"
                @input="parseScanContent"
                  v-model="scanAddForm.scanContent"
                  type="textarea"
                  :rows="3"
                  placeholder="请扫描二维码或手动输入采购合同信息"
                  @input="parseScanContent"
              />
            </el-form-item>
          </el-col>
@@ -641,18 +686,18 @@
          <el-col :span="12">
            <el-form-item label="采购合同号:" prop="purchaseContractNumber">
              <el-input
                v-model="scanAddForm.purchaseContractNumber"
                placeholder="请输入"
                clearable
                  v-model="scanAddForm.purchaseContractNumber"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierName">
              <el-input
                v-model="scanAddForm.supplierName"
                placeholder="请输入"
                clearable
                  v-model="scanAddForm.supplierName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
@@ -661,21 +706,21 @@
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="scanAddForm.projectName"
                placeholder="请输入"
                clearable
                  v-model="scanAddForm.projectName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同金额(元):" prop="contractAmount">
              <el-input-number
                v-model="scanAddForm.contractAmount"
                :precision="2"
                :step="0.1"
                clearable
                style="width: 100%"
                placeholder="请输入"
                  v-model="scanAddForm.contractAmount"
                  :precision="2"
                  :step="0.1"
                  clearable
                  style="width: 100%"
                  placeholder="请输入"
              />
            </el-form-item>
          </el-col>
@@ -684,9 +729,9 @@
          <el-col :span="12">
            <el-form-item label="付款方式:">
              <el-input
                v-model="scanAddForm.paymentMethod"
                placeholder="请输入"
                clearable
                  v-model="scanAddForm.paymentMethod"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
@@ -700,11 +745,11 @@
          <el-col :span="24">
            <el-form-item label="备注:">
              <el-input
                v-model="scanAddForm.remark"
                type="textarea"
                :rows="2"
                placeholder="请输入备注信息"
                clearable
                  v-model="scanAddForm.remark"
                  type="textarea"
                  :rows="2"
                  placeholder="请输入备注信息"
                  clearable
              />
            </el-form-item>
          </el-col>
@@ -720,17 +765,17 @@
    <!-- æ‰«ç ç™»è®°å¯¹è¯æ¡† -->
    <el-dialog
      v-model="scanDialogVisible"
      title="扫码登记"
      width="60%"
      @close="closeScanDialog"
        v-model="scanDialogVisible"
        title="扫码登记"
        width="60%"
        @close="closeScanDialog"
    >
      <el-form
        :model="scanForm"
        label-width="120px"
        label-position="left"
        :rules="scanRules"
        ref="scanFormRef"
          :model="scanForm"
          label-width="120px"
          label-position="left"
          :rules="scanRules"
          ref="scanFormRef"
      >
        <el-row :gutter="20">
          <el-col :span="12">
@@ -774,10 +819,10 @@
          <el-col :span="24">
            <el-form-item label="扫码备注:">
              <el-input
                v-model="scanForm.scanRemark"
                type="textarea"
                :rows="3"
                placeholder="请输入扫码备注信息"
                  v-model="scanForm.scanRemark"
                  type="textarea"
                  :rows="3"
                  placeholder="请输入扫码备注信息"
              />
            </el-form-item>
          </el-col>
@@ -809,35 +854,42 @@
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { getToken } from "@/utils/auth";
import {getToken} from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import {getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs} from "vue";
import {Search} from "@element-plus/icons-vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {userListNoPage} from "@/api/system/user.js";
import {
  getSalesLedgerWithProducts,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile,
  delProduct,
  getProductInfoByContractNo,
  getSalesLedgerWithProducts,
} from "@/api/salesManagement/salesLedger.js";
import {
  addOrEditPurchase,
  delPurchase,
  getSalesNo,
  purchaseListPage,
  productList,
  getPurchaseById,
  getOptions,
  addPurchaseTemplate,
  createPurchaseNo,
  delPurchase,
  getOptions,
  getPurchaseById,
  getPurchaseTemplateList,
  getSalesNo,
  productList,
  purchaseListPage
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import QRCode from "qrcode";
import useUserStore from "@/store/modules/user";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import dayjs from "dayjs";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
@@ -855,9 +907,6 @@
});
const total = ref(0);
const fileList = ref([]);
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import dayjs from "dayjs";
const userStore = useUserStore();
@@ -870,6 +919,96 @@
  0: '审批中',
  1: '审批通过',
  2: '审批失败'
};
const templateName = ref('');
const filterInputValue = ref('');
const templateList = ref([]);
const isTemplateNameDuplicate = ref(false); // æ ‡è®°æ¨¡æ¿åç§°æ˜¯å¦é‡å¤
// æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦é‡å¤
const checkTemplateNameDuplicate = (name) => {
  if (!name || name.trim() === '') {
    isTemplateNameDuplicate.value = false;
    return false;
  }
  const isDuplicate = templateList.value.some(item => item.templateName === name.trim());
  isTemplateNameDuplicate.value = isDuplicate;
  return isDuplicate;
};
// é˜²æŠ–定时器
let duplicateCheckTimer = null;
const onTemplateFilterChange = (val) => {
  filterInputValue.value = val ?? '';
  // æ¸…除之前的定时器
  if (duplicateCheckTimer) {
    clearTimeout(duplicateCheckTimer);
  }
  // å®žæ—¶æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦é‡å¤ï¼ˆé˜²æŠ–处理,避免频繁提示)
  if (val && val.trim()) {
    duplicateCheckTimer = setTimeout(() => {
      const isDuplicate = checkTemplateNameDuplicate(val);
      if (isDuplicate) {
        ElMessage({
          message: '模板名称已存在,请更换模板名称',
          type: 'warning',
          duration: 2000
        });
      }
    }, 300); // 300ms é˜²æŠ–
  } else {
    isTemplateNameDuplicate.value = false;
  }
};
// allow-create æ—¶ï¼Œè¾“入不存在的内容会作为 string å€¼è¿”回;这里同步回输入框以确保文字不丢
const onTemplateChange = async (val) => {
  if (typeof val === 'string') {
    filterInputValue.value = val;
    // é€‰æ‹©æˆ–输入时检查重复
    checkTemplateNameDuplicate(val);
  }
  // è¿‡æ»¤æ•°æ®ï¼ŒæŸ¥æ‰¾åŒ¹é…çš„æ¨¡æ¿
  const matchedTemplate = templateList.value.find(item => item.templateName === val);
  if (matchedTemplate?.id) {
    // å¦‚果找到模板,加载模板数据
    form.value = {
      ...form.value,
      ...matchedTemplate,
    };
    productData.value = matchedTemplate.productData || [];
    // ç”Ÿæˆæ–°çš„采购合同号
    try {
      const res = await createPurchaseNo();
      if (res?.data) {
        form.value.purchaseContractNumber = res.data;
      }
    } catch (error) {
      console.error('生成采购合同号失败:', error);
    }
  } else {
    // å¦‚果没有找到模板,重置表单(保持当前表单状态)
    const currentFormData = { ...form.value };
    const currentProductData = [...productData.value];
    // å¦‚果对话框未打开,先打开
    if (!dialogFormVisible.value) {
      operationType.value = 'add';
      dialogFormVisible.value = true;
    }
    // ç­‰å¾…下一个 tick åŽæ¢å¤æ•°æ®
    await nextTick();
    form.value = {
      ...form.value,
      ...currentFormData,
    };
    productData.value = currentProductData;
  }
};
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
@@ -895,7 +1034,7 @@
    supplierName: "",
    supplierId: "",
    paymentMethod: "",
        executionDate: "",
    executionDate: "",
  },
  rules: {
    purchaseContractNumber: [
@@ -903,8 +1042,9 @@
    ],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    approverId:[{ required: true, message: "请选择审批人", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const {  form, rules } = toRefs(data);
@@ -928,7 +1068,7 @@
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
        warnNum: "",
    warnNum: "",
  },
  productRules: {
    productId: [{ required: true, message: "请选择", trigger: "change" }],
@@ -939,7 +1079,7 @@
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        warnNum: [{ required: false, message: "请选择", trigger: "change" }],
    warnNum: [{ required: false, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
@@ -969,7 +1109,14 @@
};
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
  if (cellValue === null || cellValue === undefined || cellValue === '') {
    return '0.00';
  }
  const num = parseFloat(cellValue);
  if (isNaN(num)) {
    return '0.00';
  }
  return num.toFixed(2);
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
@@ -977,23 +1124,100 @@
  page.current = 1;
  getList();
};
// ä¿å­˜æ¨¡æ¿
const handleButtonClick = async () => {
  // æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦ä¸ºç©º
  if (!templateName.value || templateName.value.trim() === '') {
    ElMessage({
      message: '请输入模板名称',
      type: 'warning',
    });
    return;
  }
  // æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦é‡å¤
  const isDuplicate = checkTemplateNameDuplicate(templateName.value);
  if (isDuplicate) {
    ElMessage({
      message: '模板名称已存在,请更换模板名称',
      type: 'warning',
    });
    return;
  }
  // æ£€æŸ¥ä¾›åº”商是否选择
  if (!form.value.supplierId) {
    ElMessage({
      message: '请先选择供应商',
      type: 'warning',
    });
    return;
  }
  // æ£€æŸ¥æ˜¯å¦æœ‰äº§å“æ•°æ®
  // if (!productData.value || productData.value.length === 0) {
  //   ElMessage({
  //     message: '请先添加产品信息',
  //     type: 'warning',
  //   });
  //   return;
  // }
  try {
    let params = {
      productData: proxy.HaveJson(productData.value),
      supplierId: form.value.supplierId,
      paymentMethod: form.value.paymentMethod,
      recorderId: form.value.recorderId,
      approverId: form.value.approverId,
      templateName: templateName.value.trim()
    };
    console.log(params);
    let res = await addPurchaseTemplate(params);
    if (res && res.code === 200) {
      ElMessage({
        message: '模板保存成功',
        type: 'success',
      });
      // ä¿å­˜æˆåŠŸåŽé‡æ–°èŽ·å–æ¨¡æ¿åˆ—è¡¨
      await getTemplateList();
      // æ¸…空模板名称输入
      templateName.value = '';
      filterInputValue.value = '';
      isTemplateNameDuplicate.value = false;
    } else {
      ElMessage({
        message: res?.msg || '模板保存失败',
        type: 'error',
      });
    }
  } catch (error) {
    console.error('保存模板失败:', error);
    ElMessage({
      message: '模板保存失败,请稍后重试',
      type: 'error',
    });
  }
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(
    param,
    [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
      "ticketsNum",
      "ticketsAmount",
      "futureTickets",
      "futureTicketsAmount",
    ],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
      param,
      [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
        "ticketsNum",
        "ticketsAmount",
        "futureTickets",
        "futureTicketsAmount",
      ],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
  );
};
const paginationChange = (obj) => {
@@ -1005,23 +1229,24 @@
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  purchaseListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      // tableData.value = res.data.records;
      // å¤„理数据,添加失效状态标记
      tableData.value = res.data.records.map(record => ({
        ...record,
        isInvalid: record.isWhite === 1
      }));
      tableData.value.map((item) => {
        item.children = [];
      .then((res) => {
        tableLoading.value = false;
        // tableData.value = res.data.records;
        // å¤„理数据,添加失效状态标记
        tableData.value = res.data.records.map(record => ({
          ...record,
          isInvalid: record.isWhite === 1
        }));
        // åˆå§‹åŒ–子数据数组
        tableData.value.forEach((item) => {
          item.children = [];
        });
        total.value = res.data.total;
        expandedRowKeys.value = [];
      })
      .catch(() => {
        tableLoading.value = false;
      });
      total.value = res.data.total;
      expandedRowKeys.value = [];
    })
    .catch(() => {
      tableLoading.value = false;
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
@@ -1032,19 +1257,24 @@
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
const expandChange = async (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
      const res = await productList({ salesLedgerId: row.id, type: 2 });
      const index = tableData.value.findIndex((item) => item.id === row.id);
      if (index > -1) {
        tableData.value[index].children = res.data || [];
        expandedRowKeys.value.push(row.id);
      });
      }
    } catch (error) {
      console.log(error);
      console.error('加载产品列表失败:', error);
      proxy.$modal.msgError('加载产品列表失败');
      // å±•开失败时,移除展开状态
      const index = expandedRows.findIndex(item => item.id === row.id);
      if (index > -1) {
        expandedRows.splice(index, 1);
      }
    }
  } else {
    expandedRowKeys.value = [];
@@ -1063,41 +1293,63 @@
  ]);
};
// æ‰“开弹框
const openForm = (type, row) => {
const openForm = async (type, row) => {
  await getTemplateList()
  operationType.value = type;
  form.value = {};
  productData.value = [];
  fileList.value = [];
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      form.value.purchaseContractNumber = res.data;
    });
  }
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  getSalesNo().then((res) => {
    salesContractList.value = res;
  });
  getOptions().then((res) => {
  templateName.value = '';
  filterInputValue.value = '';
  isTemplateNameDuplicate.value = false;
  try {
    // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
    const [userRes, salesRes, supplierRes] = await Promise.all([
      userListNoPage(),
      getSalesNo(),
      getOptions()
    ]);
    userList.value = userRes.data || [];
    salesContractList.value = salesRes || [];
    // ä¾›åº”商过滤出isWhite=0 çš„æ•°æ®
    supplierList.value = res.data.filter((item) => item.isWhite == 0);
  });
  form.value.recorderId = userStore.id;
  form.value.entryDate = getCurrentDate();
  if (type === "edit") {
    currentId.value = row.id;
    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
      form.value = { ...res };
      productData.value = form.value.productData;
      if (form.value.salesLedgerFiles) {
        fileList.value = form.value.salesLedgerFiles;
      } else {
        fileList.value = [];
    supplierList.value = (supplierRes.data || []).filter((item) => item.isWhite === 0);
    // è®¾ç½®é»˜è®¤å€¼
    form.value.recorderId = userStore.id;
    form.value.entryDate = getCurrentDate();
    if (type === "add") {
      // æ–°å¢žæ—¶ç”Ÿæˆé‡‡è´­åˆåŒå·
      try {
        const purchaseNoRes = await createPurchaseNo();
        if (purchaseNoRes?.data) {
          form.value.purchaseContractNumber = purchaseNoRes.data;
        }
      } catch (error) {
        console.error('生成采购合同号失败:', error);
        proxy.$modal.msgWarning('生成采购合同号失败');
      }
    });
    } else if (type === "edit" && row?.id) {
      // ç¼–辑时加载数据
      currentId.value = row.id;
      try {
        const purchaseRes = await getPurchaseById({ id: row.id, type: 2 });
        form.value = { ...purchaseRes };
        productData.value = purchaseRes.productData || [];
        fileList.value = purchaseRes.salesLedgerFiles || [];
      } catch (error) {
        console.error('加载采购台账数据失败:', error);
        proxy.$modal.msgError('加载数据失败');
        return;
      }
    }
    dialogFormVisible.value = true;
  } catch (error) {
    console.error('打开表单失败:', error);
    proxy.$modal.msgError('加载基础数据失败');
  }
  dialogFormVisible.value = true;
};
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
@@ -1126,43 +1378,57 @@
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  console.log("handleRemove", file.id);
  if (file.size > 1024 * 1024 * 10) {
    // ä»…前端清理,不调用删除接口和提示
    return;
async function handleRemove(file) {
  if (!file?.id) {
    return;
  }
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
  if (file.size > 1024 * 1024 * 10) {
    // ä»…前端清理,不调用删除接口和提示
    return;
  }
  if (operationType.value === "edit" && file.id) {
    try {
      await delLedgerFile([file.id]);
      proxy.$modal.msgSuccess("删除成功");
    });
    } catch (error) {
      console.error('删除文件失败:', error);
      proxy.$modal.msgError("删除文件失败");
    }
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
      let tempFileIds = [];
      if (fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 2;
      addOrEditPurchase(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
const submitForm = async () => {
  try {
    const valid = await proxy.$refs["formRef"].validate().catch(() => false);
    if (!valid) {
      return;
    }
  });
    if (!productData.value || productData.value.length === 0) {
      proxy.$modal.msgWarning("请添加产品信息");
      return;
    }
    form.value.productData = proxy.HaveJson(productData.value);
    form.value.tempFileIds = fileList.value
      .filter(item => item.tempId)
      .map((item) => item.tempId);
    form.value.type = 2;
    try {
      await addOrEditPurchase(form.value);
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    } catch (error) {
      console.error('提交表单失败:', error);
      proxy.$modal.msgError("提交失败,请稍后重试");
    }
  } catch (error) {
    console.error('表单验证失败:', error);
  }
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
@@ -1181,17 +1447,26 @@
  productFormVisible.value = true;
  getProductOptions();
};
const getProductOptions = () => {
  productTreeList().then((res) => {
const getProductOptions = async () => {
  try {
    const res = await productTreeList();
    productOptions.value = convertIdToValue(res);
  });
  } catch (error) {
    console.error('加载产品选项失败:', error);
    proxy.$modal.msgError('加载产品选项失败');
  }
};
const getModels = (value) => {
const getModels = async (value) => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
    try {
      const res = await modelList({ id: value });
      modelOptions.value = res || [];
    } catch (error) {
      console.error('加载规格型号失败:', error);
      proxy.$modal.msgError('加载规格型号失败');
      modelOptions.value = [];
    }
  } else {
    productForm.value.productCategory = "";
    modelOptions.value = [];
@@ -1236,75 +1511,103 @@
  });
}
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if (productOperationType.value === "add") {
          productData.value.push({ ...productForm.value });
          console.log("productData.value---", productData.value);
        } else {
          productData.value[productOperationIndex.value] = {
            ...productForm.value,
          };
        }
        closeProductDia();
      }
const submitProduct = async () => {
  try {
    const valid = await proxy.$refs["productFormRef"].validate().catch(() => false);
    if (!valid) {
      return;
    }
  });
    if (operationType.value === "edit") {
      await submitProductEdit();
    } else {
      if (productOperationType.value === "add") {
        productData.value.push({ ...productForm.value });
      } else {
        productData.value[productOperationIndex.value] = {
          ...productForm.value,
        };
      }
      closeProductDia();
    }
  } catch (error) {
    console.error('提交产品表单失败:', error);
  }
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 2;
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
const submitProductEdit = async () => {
  try {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 2;
    await addOrUpdateSalesLedgerProduct(productForm.value);
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
      productData.value = res.productData;
    });
  });
    // é‡æ–°åŠ è½½äº§å“æ•°æ®
    try {
      const res = await getPurchaseById({ id: currentId.value, type: 2 });
      productData.value = res.productData || [];
    } catch (error) {
      console.error('重新加载产品数据失败:', error);
    }
  } catch (error) {
    console.error('提交产品编辑失败:', error);
    proxy.$modal.msgError("提交失败,请稍后重试");
  }
};
// åˆ é™¤äº§å“
const deleteProduct = () => {
const deleteProduct = async () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    // æ–°å¢žæ¨¡å¼ä¸‹ï¼Œç›´æŽ¥ä»Žå‰ç«¯æ•°æ®ä¸­åˆ é™¤
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
          (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
      }
    });
    proxy.$modal.msgSuccess("删除成功");
  } else {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
    // ç¼–辑模式下,需要调用接口删除
    const ids = productSelectedRows.value
      .filter(item => item.id)
      .map((item) => item.id);
    if (ids.length === 0) {
      proxy.$modal.msgWarning("请选择有效的数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
    try {
      await ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除确认", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      });
      await delProduct(ids);
      proxy.$modal.msgSuccess("删除成功");
      closeProductDia();
      // é‡æ–°åŠ è½½äº§å“æ•°æ®
      try {
        const res = await getSalesLedgerWithProducts({ id: currentId.value, type: 2 });
        productData.value = res.productData || [];
      } catch (error) {
        console.error('重新加载产品数据失败:', error);
      }
    } catch (error) {
      if (error !== 'cancel') {
        console.error('删除产品失败:', error);
        proxy.$modal.msgError("删除失败,请稍后重试");
      }
    }
  }
};
// å…³é—­äº§å“å¼¹æ¡†
@@ -1313,62 +1616,71 @@
  productFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
const handleOut = async () => {
  try {
    await ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出确认", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    });
    proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
  } catch (error) {
    if (error !== 'cancel') {
      console.error('导出失败:', error);
      proxy.$modal.msgError("导出失败,请稍后重试");
    }
  }
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map((item) => item.id);
  } else {
const handleDelete = async () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delPurchase(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
  // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
  const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
  if (unauthorizedData.length > 0) {
    proxy.$modal.msgWarning("不可删除他人维护的数据");
    return;
  }
  const ids = selectedRows.value
    .filter(item => item.id)
    .map((item) => item.id);
  if (ids.length === 0) {
    proxy.$modal.msgWarning("请选择有效的数据");
    return;
  }
  try {
    await ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除确认", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    });
    await delPurchase(ids);
    proxy.$modal.msgSuccess("删除成功");
    getList();
  } catch (error) {
    if (error !== 'cancel') {
      console.error('删除失败:', error);
      proxy.$modal.msgError("删除失败,请稍后重试");
    }
  }
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
  return dayjs().format("YYYY-MM-DD");
}
const mathNum = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
@@ -1377,61 +1689,59 @@
  }
  // å«ç¨Žæ€»ä»·è®¡ç®—
  productForm.value.taxInclusiveTotalPrice =
    proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice,
      productForm.value.quantity
    );
      proxy.calculateTaxIncludeTotalPrice(
          productForm.value.taxInclusiveUnitPrice,
          productForm.value.quantity
      );
  if (productForm.value.taxRate) {
    // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        productForm.value.taxRate
      );
        proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
        );
  }
};
const reverseMathNum = (field) => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (!productForm.value.taxRate) {
    proxy.$modal.msgWarning("请先选择税率");
    return;
  }
  const taxRate = Number(productForm.value.taxRate);
  if (!taxRate) return;
  if (field === 'taxInclusiveTotalPrice') {
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
      productForm.value.taxInclusiveUnitPrice =
          (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œå«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
      productForm.value.quantity =
          (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
    // åç®—不含税总价
    productForm.value.taxExclusiveTotalPrice =
      (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
    productForm.value.taxExclusiveTotalPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
  } else if (field === 'taxExclusiveTotalPrice') {
    // åç®—含税总价
    productForm.value.taxInclusiveTotalPrice =
      (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
    productForm.value.taxInclusiveTotalPrice =
        (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
    // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
      productForm.value.taxInclusiveUnitPrice =
          (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
      productForm.value.quantity =
          (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
  }
};
// é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
const salesLedgerChange = async (row) => {
  console.log("row", row);
  var index = salesContractList.value.findIndex((item) => item.id == row);
  console.log("index", index);
  const index = salesContractList.value.findIndex((item) => item.id === row);
  if (index > -1) {
    form.value.projectName = salesContractList.value[index].projectName;
    await querygProductInfoByContractNo();
@@ -1478,14 +1788,19 @@
    proxy.$modal.msgWarning("二维码未生成");
    return;
  }
  const a = document.createElement('a');
  a.href = qrCodeUrl.value;
  a.download = `采购合同号二维码_${new Date().getTime()}.png`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  proxy.$modal.msgSuccess("下载成功");
  try {
    const a = document.createElement('a');
    a.href = qrCodeUrl.value;
    a.download = `采购合同号二维码_${dayjs().format('YYYYMMDDHHmmss')}.png`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    proxy.$modal.msgSuccess("下载成功");
  } catch (error) {
    console.error('下载二维码失败:', error);
    proxy.$modal.msgError("下载失败,请稍后重试");
  }
};
// æ‰«ç æ–°å¢žå¯¹è¯æ¡†ç›¸å…³å˜é‡
@@ -1538,7 +1853,7 @@
// è§£æžæ‰«ç å†…容(模拟解析二维码数据)
const parseScanContent = (content) => {
  if (!content) return;
  // æ¨¡æ‹Ÿè§£æžäºŒç»´ç å†…容,这里可以根据实际需求调整解析逻辑
  // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|项目|金额|付款方式
  const parts = content.split('|');
@@ -1558,30 +1873,35 @@
};
// æäº¤æ‰«ç æ–°å¢ž
const submitScanAdd = () => {
  proxy.$refs["scanAddFormRef"].validate((valid) => {
    if (valid) {
      // æž„建新增数据
      const newData = {
        purchaseContractNumber: scanAddForm.purchaseContractNumber,
        supplierName: scanAddForm.supplierName,
        projectName: scanAddForm.projectName,
        contractAmount: scanAddForm.contractAmount,
        paymentMethod: scanAddForm.paymentMethod,
        recorderName: scanAddForm.recorderName,
        entryDate: getCurrentDate(),
        remark: scanAddForm.scanRemark,
        type: 2
      };
      // æ¨¡æ‹Ÿæ–°å¢žæˆåŠŸ
      proxy.$modal.msgSuccess("扫码新增成功!");
      closeScanAddDialog();
      // å¯ä»¥é€‰æ‹©æ˜¯å¦åˆ·æ–°åˆ—表
      // getList();
const submitScanAdd = async () => {
  try {
    const valid = await proxy.$refs["scanAddFormRef"].validate().catch(() => false);
    if (!valid) {
      return;
    }
  });
    // æž„建新增数据
    const newData = {
      purchaseContractNumber: scanAddForm.purchaseContractNumber,
      supplierName: scanAddForm.supplierName,
      projectName: scanAddForm.projectName,
      contractAmount: scanAddForm.contractAmount,
      paymentMethod: scanAddForm.paymentMethod,
      recorderName: scanAddForm.recorderName,
      entryDate: getCurrentDate(),
      remark: scanAddForm.scanRemark,
      type: 2
    };
    // await addOrEditPurchase(newData);
    proxy.$modal.msgSuccess("扫码新增成功!");
    closeScanAddDialog();
    getList(); // åˆ·æ–°åˆ—表
  } catch (error) {
    console.error('提交扫码新增失败:', error);
    proxy.$modal.msgError("提交失败,请稍后重试");
  }
};
// æ‰“开扫码登记对话框
@@ -1604,33 +1924,32 @@
};
// æäº¤æ‰«ç ç™»è®°
const submitScan = () => {
  proxy.$refs["scanFormRef"].validate((valid) => {
    if (valid) {
      // æ·»åŠ æ‰«ç è®°å½•
      scanRecords.value.push({
        ...scanForm,
        id: Date.now(), // æ¨¡æ‹ŸID
        scanTime: getCurrentDateTime(),
      });
      scanForm.scanStatus = "已扫码";
      scanForm.scanRemark = scanForm.scanRemark || "无";
      proxy.$modal.msgSuccess("扫码登记成功!");
      closeScanDialog();
const submitScan = async () => {
  try {
    const valid = await proxy.$refs["scanFormRef"].validate().catch(() => false);
    if (!valid) {
      return;
    }
  });
    // æ·»åŠ æ‰«ç è®°å½•
    scanRecords.value.push({
      ...scanForm,
      id: Date.now(), // æ¨¡æ‹ŸID
      scanTime: getCurrentDateTime(),
    });
    scanForm.scanStatus = "已扫码";
    scanForm.scanRemark = scanForm.scanRemark || "无";
    proxy.$modal.msgSuccess("扫码登记成功!");
    closeScanDialog();
  } catch (error) {
    console.error('提交扫码登记失败:', error);
    proxy.$modal.msgError("提交失败,请稍后重试");
  }
};
// èŽ·å–å½“å‰æ—¥æœŸæ—¶é—´
function getCurrentDateTime() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  const hours = String(now.getHours()).padStart(2, "0");
  const minutes = String(now.getMinutes()).padStart(2, "0");
  const seconds = String(now.getSeconds()).padStart(2, "0");
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  return dayjs().format("YYYY-MM-DD HH:mm:ss");
}
// æ·»åŠ è¡Œç±»åæ–¹æ³•
@@ -1638,8 +1957,18 @@
  return row.isInvalid ? 'invalid-row' : '';
};
// èŽ·å–æ¨¡æ¿ä¿¡æ¯
const getTemplateList =async ()=>{
  let res = await getPurchaseTemplateList()
  if(res && res.code===200 && Array.isArray(res.data)){
    templateList.value = res.data
  }
}
onMounted(() => {
  getList();
  getTemplateList();
});
</script>
@@ -1648,4 +1977,16 @@
  opacity: 0.6;
  background-color: #f5f7fa;
}
.el-row{
  justify-content: space-between;
  align-items: center
}
.no-arrow-select {
  --el-select-suffix-icon-color: transparent; /* éšè—é»˜è®¤ä¸‹æ‹‰ç®­å¤´ */
}
.select-button-group {
  display: flex;
  align-items: center;
}
</style>