gaoluyang
15 小时以前 4f0517d5c30ec2962c16883f0552cfa6db2034dc
feat: 设备台账启用状态、审批签核展示与导入交互修复

- 设备台账:存放位置改为启用/停用单选项
- 设备台账导入:增加上传失败提示与状态复位,未选择文件时给出提示,失败后可直接重试
- 协同审批:审批任务状态/意见改用后端 taskStatus/comment 字段,修复审批人状态统一显示为“待审批”
- 销售台账:创建框架订单入口文案改为创建发货订单
- 采购付款:可付款余额按剩余应付金额校验

Co-Authored-By: Claude Code <noreply@anthropic.com>
已修改6个文件
127 ■■■■■ 文件已修改
src/api/salesManagement/salesLedger.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Form.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/index.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/salesLedger.js
@@ -118,7 +118,7 @@
    });
}
// 创建框架订单
// 创建发货订单
export function createFrameworkOrder(query) {
  return request({
    url: "/sales/ledger/createFrameworkOrder",
src/views/equipmentManagement/ledger/Form.vue
@@ -57,8 +57,11 @@
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="存放位置" prop="storageLocation">
          <el-input v-model="form.storageLocation" placeholder="请输入存放位置" />
        <el-form-item label="启用状态" prop="isEnabled">
          <el-radio-group v-model="form.isEnabled">
            <el-radio :value="1">启用</el-radio>
            <el-radio :value="0">停用</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-col>
      <el-col :span="12">
@@ -246,7 +249,7 @@
  type: undefined, // 设备类型
  areaId: undefined, // 所属区域
  supplierName: undefined, // 供应商
  storageLocation: undefined, // 存放位置
  isEnabled: 1, // 启用状态 1-启用 0-停用,停用设备不参与巡检
  isDepr: 2, // 是否启用折旧 1-是 2-否
  annualDepreciationAmount: undefined, // 每年折旧金额
  unit: undefined, // 单位
@@ -292,7 +295,7 @@
    form.type = data.type;
    form.supplierName = data.supplierName;
    form.areaId = data.areaId;
    form.storageLocation = data.storageLocation;
    form.isEnabled = data.isEnabled ?? 1;
    form.isDepr = data.isDepr;
    form.annualDepreciationAmount = data.annualDepreciationAmount;
    form.unit = data.unit;
src/views/equipmentManagement/ledger/index.vue
@@ -140,6 +140,11 @@
            @selection-change="handleSelectionChange"
            @pagination="changePage"
          >
            <template #isEnabled="{ row }">
              <el-tag :type="row.isEnabled === 0 ? 'info' : 'success'" size="small">
                {{ row.isEnabled === 0 ? '停用' : '启用' }}
              </el-tag>
            </template>
          </PIMTable>
        </div>
      </div>
@@ -166,6 +171,9 @@
        :disabled="upload.isUploading"
        :on-progress="handleFileUploadProgress"
        :on-success="handleFileSuccess"
        :on-error="handleFileError"
        :on-change="handleFileChange"
        :on-exceed="handleFileExceed"
        :auto-upload="false"
        drag
      >
@@ -202,7 +210,11 @@
        <el-descriptions-item label="设备类型">{{ detailData.type }}</el-descriptions-item>
        <el-descriptions-item label="供应商">{{ detailData.supplierName }}</el-descriptions-item>
        <el-descriptions-item label="所属区域">{{ detailData.areaName }}</el-descriptions-item>
        <el-descriptions-item label="存放位置">{{ detailData.storageLocation }}</el-descriptions-item>
        <el-descriptions-item label="启用状态">
          <el-tag :type="detailData.isEnabled === 0 ? 'info' : 'success'" size="small">
            {{ detailData.isEnabled === 0 ? '停用' : '启用' }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="单位">{{ detailData.unit }}</el-descriptions-item>
        <el-descriptions-item label="数量">{{ detailData.number }}</el-descriptions-item>
        <el-descriptions-item label="启用折旧">{{ detailData.isDepr === 1 ? '是' : '否' }}</el-descriptions-item>
@@ -287,7 +299,7 @@
  editDeviceArea,
  delDeviceArea,
} from "@/api/equipmentManagement/area";
import { onMounted, getCurrentInstance, ref, reactive, computed } from "vue";
import { onMounted, getCurrentInstance, ref, reactive, computed, nextTick } from "vue";
import Modal from "./Modal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
import { UploadFilled } from "@element-plus/icons-vue";
@@ -339,6 +351,7 @@
  open: false,
  title: "",
  isUploading: false,
  file: null,
  headers: { Authorization: "Bearer " + getToken() },
  url: import.meta.env.VITE_APP_BASE_API + "/device/ledger/import",
});
@@ -387,8 +400,11 @@
      prop: "supplierName",
    },
    {
      label: "存放位置",
      prop: "storageLocation",
      label: "启用状态",
      prop: "isEnabled",
      dataType: "slot",
      slot: "isEnabled",
      width: 100,
    },
    {
      label: "数量",
@@ -670,7 +686,13 @@
const handleImport = () => {
  upload.title = "设备台账导入";
  upload.isUploading = false;
  upload.file = null;
  upload.open = true;
  // 清理上一次失败/成功遗留的文件,避免出现已选文件却无法再次选择的情况
  nextTick(() => {
    proxy.$refs["uploadRef"] && proxy.$refs["uploadRef"].clearFiles();
  });
};
const importTemplate = () => {
@@ -685,21 +707,71 @@
  upload.isUploading = true;
};
const handleFileSuccess = (response, file, fileList) => {
  upload.open = false;
  upload.isUploading = false;
  proxy.$refs["uploadRef"].handleRemove(file);
const handleFileChange = (file) => {
  // 只记录待上传的文件;删除/成功/失败时 Element Plus 也会触发 change,不能覆盖成旧文件
  upload.file = file && file.status === "ready" ? file.raw : null;
};
const handleFileExceed = (files) => {
  // 只允许选择一个文件,后选的文件覆盖先选的文件
  proxy.$refs["uploadRef"].clearFiles();
  proxy.$refs["uploadRef"].handleStart(files[0]);
};
const showImportResult = (msg) => {
  proxy.$alert(
    "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
      response.msg +
      msg +
      "</div>",
    "导入结果",
    { dangerouslyUseHTMLString: true }
  );
  getTableData();
};
const handleFileSuccess = (response, file, fileList) => {
  upload.open = false;
  upload.isUploading = false;
  upload.file = null;
  proxy.$refs["uploadRef"].handleRemove(file);
  const success = response && response.code === 200;
  showImportResult((response && response.msg) || (success ? "导入成功" : "导入失败"));
  if (success) {
    getTableData();
  }
};
// 网络异常/后端返回非 2xx 时触发,必须复位上传状态并给出提示,否则再次点击“确定”不会有任何反应
const handleFileError = (error, file, fileList) => {
  upload.isUploading = false;
  upload.file = null;
  // 移除失败文件,否则文件状态为 fail,再次点“确定”不会重新上传
  try {
    proxy.$refs["uploadRef"].handleRemove(file);
  } catch (e) {
    // ignore
  }
  let msg = "导入失败,请检查网络或稍后重试";
  try {
    const data = error && error.message ? JSON.parse(error.message) : null;
    if (data && data.msg) {
      msg = data.msg;
    }
  } catch (e) {
    // 非 JSON 响应,使用默认提示
  }
  showImportResult(msg);
};
const submitFileForm = () => {
  if (upload.isUploading) {
    ElMessage.warning("文件上传中,请稍候");
    return;
  }
  if (!upload.file) {
    ElMessage.warning("请先选择要导入的 Excel 文件");
    return;
  }
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
};
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -186,8 +186,9 @@
      templateId: t.templateId,
      approverId: t.approverId,
      approverName: t.approverName || "",
      status: t.status,
      approveComment: t.approveComment,
      // 后端字段为 taskStatus / comment,兼容旧的 status / approveComment
      status: t.status ?? t.taskStatus,
      approveComment: t.approveComment ?? t.comment,
      approveTime: t.approveTime,
    });
    node.tasks.push(t);
src/views/procurementManagement/paymentLedger/index.vue
@@ -127,12 +127,12 @@
            v-model="paymentForm.paymentAmount"
            :precision="2"
            :min="0.01"
            :max="Math.max(0, (Number(currentPaymentRow.payableAmount) || 0) - (Number(currentPaymentRow.paymentAmount) || 0))"
            :max="Math.max(0, Number(currentPaymentRow.payableAmount) || 0)"
            placeholder="请输入付款金额"
            controls-position="right"
          />
          <div style="color: #e6a23c; font-size: 12px; margin-top: 4px">
            当前可付款余额:{{ (Number(currentPaymentRow.payableAmount) || 0) - (Number(currentPaymentRow.paymentAmount) || 0) > 0 ? ((Number(currentPaymentRow.payableAmount) || 0) - (Number(currentPaymentRow.paymentAmount) || 0)).toFixed(2) : '0.00' }} 元
            当前可付款余额:{{ Number(currentPaymentRow.payableAmount) > 0 ? Number(currentPaymentRow.payableAmount).toFixed(2) : '0.00' }} 元
          </div>
        </el-form-item>
        <el-form-item label="付款方式:" prop="paymentMethod">
@@ -277,9 +277,8 @@
    if (!value) {
      return callback(new Error("请输入付款金额"));
    }
    const remaining =
      (Number(currentPaymentRow.value.payableAmount) || 0) -
      (Number(currentPaymentRow.value.paymentAmount) || 0);
    // 应付金额已扣减过累计付款,此处直接使用剩余应付金额
    const remaining = Number(currentPaymentRow.value.payableAmount) || 0;
    if (value > remaining) {
      return callback(new Error(`付款金额不能超过可付款余额 ${remaining.toFixed(2)} 元`));
    }
src/views/salesManagement/salesLedger/index.vue
@@ -282,7 +282,7 @@
              link
              type="success"
              @click="openFrameworkOrderDialog(scope.row)"
              >创建框架订单
              >创建发货订单
            </el-button>
            <el-button
              v-if="scope.row.contractType === 2"
@@ -1264,10 +1264,10 @@
        </div>
      </template>
    </el-dialog>
    <!-- 创建框架订单弹窗 -->
    <!-- 创建发货订单弹窗 -->
    <el-dialog
      v-model="frameworkOrderDialogVisible"
      title="创建框架订单"
      title="创建发货订单"
      width="70%"
      :close-on-click-modal="false"
    >
@@ -3504,7 +3504,7 @@
        deliveryDate: p.deliveryDate,
      })),
    });
    proxy.$modal.msgSuccess("框架订单创建成功");
    proxy.$modal.msgSuccess("发货订单创建成功");
    frameworkOrderDialogVisible.value = false;
  } catch (e) {
    // 错误已由请求拦截器统一提示