liyong
8 天以前 1839bc040282f8972a7b55f504d3e72f4269e48e
Merge remote-tracking branch 'origin/dev_NEW_pro' into dev_NEW_pro
已添加1个文件
已修改53个文件
2104 ■■■■ 文件已修改
src/api/productionManagement/productionOrder.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/workOrder.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFileOpenSea/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/HomeTab.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/knowledgeBase/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/afterSalesHandling/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/expiryAfterSales/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/calibration/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/fixedAssets.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/intangibleAssets.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/generalLedger/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/input-invoice.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/payment.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/paymentApply.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/purchaseIn.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/reconciliation.vue 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/invoiceApply.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/outputInvoice.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/receipt.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/reconciliation.vue 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/salesOut.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/salesReturn.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/voucher/index.vue 576 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Record.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/lavorissue/statistics/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/socialSecuritySet/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementReport/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processStatistics/index.vue 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionTraceability/index.vue 395 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderEdit/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderManagement/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/index.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/accidentReportingRecord/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/emergencyPlanReview/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/hazardSourceLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/hazardousMaterialsControl/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safeWorkApproval/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js
@@ -102,6 +102,15 @@
    data,
  });
}
// ç”Ÿäº§è®¢å•溯源详情
export function getOrderDetail(npsNo) {
  return request({
    url: "/productionOrder/ordeDetail",
    method: "get",
    params: { npsNo },
  });
}
// ç”Ÿäº§è®¢å•-领料详情列表
// export function listMaterialPickingDetail(query) {
//   return request({
src/api/productionManagement/workOrder.js
@@ -86,3 +86,12 @@
    data,
  });
}
// èŽ·å–å·¥åºç»Ÿè®¡æ•°æ®
export function getOperationStatistics(query) {
  return request({
    url: "/productionOperationTask/getOperation",
    method: "get",
    params: query,
  });
}
src/assets/styles/index.scss
@@ -148,7 +148,6 @@
  }
}
.table_list {
  margin-top: 20px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-md);
src/views/basicData/customerFile/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form" style="margin-bottom: 20px;">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input v-model="searchForm.customerName"
src/views/basicData/customerFileOpenSea/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form" style="margin-bottom: 20px;">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input v-model="searchForm.customerName"
src/views/basicData/supplierManage/components/HomeTab.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
  <div>
    <div class="search_form">
      <div>
      <div style="margin-bottom: 10px;">
        <span class="search_title">供应商档案:</span>
        <el-input
            v-model="searchForm.supplierName"
@@ -15,7 +15,7 @@
        >搜索</el-button
        >
      </div>
      <div>
      <div style="margin-bottom: 10px;">
        <el-button type="primary" @click="openForm('add')"
        >新增供应商</el-button
        >
src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form" style="margin-bottom: 20px;">
      <div>
        <span class="search_title">知识标题:</span>
        <el-input
src/views/customerService/afterSalesHandling/index.vue
@@ -1,6 +1,6 @@
<template>
    <div class="app-container">
        <div class="search-wrapper">
        <div class="search-wrapper mb20">
      <el-form
          :model="searchForm"
          class="demo-form-inline"
src/views/customerService/expiryAfterSales/index.vue
@@ -1,6 +1,6 @@
<template>
    <div class="app-container">
        <div class="search_form">
        <div class="search_form mb20">
            <div>
                <span class="search_title">临期日期:</span>
                <el-date-picker
src/views/equipmentManagement/calibration/index.vue
@@ -1,6 +1,6 @@
<template>
    <div class="app-container">
        <div class="search_form">
        <div class="search_form mb20">
            <div>
                <span class="search_title">检定日期:</span>
                <el-date-picker
src/views/equipmentManagement/measurementEquipment/index.vue
@@ -1,6 +1,6 @@
<template>
    <div class="app-container">
        <div class="search_form">
        <div class="search_form mb20">
            <div>
                <span class="search_title">录入日期:</span>
                <el-date-picker
src/views/financialManagement/assets/fixedAssets.vue
@@ -75,7 +75,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -178,16 +178,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "固定资产",
src/views/financialManagement/assets/intangibleAssets.vue
@@ -76,7 +76,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -171,16 +171,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "无形资产",
src/views/financialManagement/generalLedger/index.vue
@@ -60,7 +60,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="600px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-form-item label="科目编码" prop="subjectCode">
          <el-input v-model="form.subjectCode" placeholder="请输入科目编码" />
@@ -94,16 +94,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "总帐科目",
src/views/financialManagement/payable/input-invoice.vue
@@ -67,7 +67,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -142,16 +142,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "进项发票",
src/views/financialManagement/payable/payment.vue
@@ -70,7 +70,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -134,16 +134,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "付款单",
src/views/financialManagement/payable/paymentApply.vue
@@ -60,7 +60,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -118,16 +118,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "付款申请",
src/views/financialManagement/payable/purchaseIn.vue
@@ -21,7 +21,6 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增入库</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
@@ -50,7 +49,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -118,16 +117,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "采购入库",
src/views/financialManagement/payable/reconciliation.vue
@@ -55,7 +55,7 @@
      </PIMTable>
    </div>
    <el-dialog title="对账明细" v-model="detailDialogVisible" width="900px" append-to-body>
    <FormDialog title="对账明细" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
      <div class="statement-header">
        <h3>{{ currentSupplier }} åº”付对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
@@ -88,16 +88,72 @@
        <el-table-column prop="remark" label="备注" show-overflow-tooltip />
      </el-table>
      <template #footer>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
        <el-button type="primary" @click="printDetail">打印</el-button>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>
    </FormDialog>
    <FormDialog title="生成对账单" v-model="generateDialogVisible" width="1000px" @confirm="confirmGenerate" @cancel="generateDialogVisible = false">
      <el-form :model="generateForm" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="选择供应商" prop="supplierId">
              <el-select v-model="generateForm.supplierId" placeholder="请选择供应商" style="width: 100%;" @change="onSupplierChange">
                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="对账月份" prop="period">
              <el-date-picker v-model="generateForm.period" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div v-if="purchaseData.length > 0" class="purchase-section">
        <div class="section-title">本月采购数据</div>
        <el-table :data="purchaseData" border style="width: 100%; margin-bottom: 15px;" v-loading="purchaseLoading" @selection-change="handlePurchaseSelectionChange">
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column prop="date" label="日期" width="120" />
          <el-table-column prop="code" label="单据编号" width="150" />
          <el-table-column prop="type" label="类型" width="100">
            <template #default="{ row }">
              <el-tag :type="row.type === '入库' ? 'success' : 'danger'">{{ row.type }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额" width="120">
            <template #default="{ row }">
              <span :class="row.type === '入库' ? 'text-danger' : 'text-success'">Â¥{{ formatMoney(row.amount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="remark" label="备注" />
        </el-table>
        <div class="summary-row">
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.beginBalance) }}</strong></span>
          <span>本期应付: <strong class="text-danger">Â¥{{ formatMoney(generateForm.currentPayable) }}</strong></span>
          <span>本期付款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentPayment) }}</strong></span>
          <span>期末余额: <strong class="text-primary">Â¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment)) }}</strong></span>
        </div>
      </div>
      <div v-else-if="generateForm.supplierId && !purchaseLoading" class="empty-tip">
        <el-empty description="该供应商本月暂无采购数据" />
      </div>
      <template #footer>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
        <el-button @click="generateDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "应付对账",
@@ -132,6 +188,24 @@
const currentPeriod = ref("");
const detailData = ref([]);
const generateDialogVisible = ref(false);
const purchaseLoading = ref(false);
const purchaseData = ref([]);
const selectedPurchases = ref([]);
const generateForm = reactive({
  supplierId: "",
  supplierName: "",
  period: "",
  beginBalance: 0,
  currentPayable: 0,
  currentPayment: 0,
});
const canGenerate = computed(() => {
  return generateForm.supplierId && generateForm.period && selectedPurchases.value.length > 0;
});
const supplierList = [
  { id: 1, name: "北京原材料供应商" },
  { id: 2, name: "上海电子元器件公司" },
@@ -144,6 +218,10 @@
  { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "上海电子元器件公司", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 },
  { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 },
];
const calculateEndBalance = (beginBalance, currentPayable, currentPayment) => {
  return beginBalance + currentPayable - currentPayment;
};
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -177,34 +255,133 @@
};
const generateStatement = () => {
  ElMessage.success("对账单生成成功");
  generateForm.supplierId = "";
  generateForm.supplierName = "";
  generateForm.period = "";
  generateForm.beginBalance = 0;
  generateForm.currentPayable = 0;
  generateForm.currentPayment = 0;
  purchaseData.value = [];
  selectedPurchases.value = [];
  generateDialogVisible.value = true;
};
const onSupplierChange = (supplierId) => {
  const supplier = supplierList.find(item => item.id === supplierId);
  if (supplier) {
    generateForm.supplierName = supplier.name;
  }
  loadPurchaseData();
};
const onPeriodChange = () => {
  loadPurchaseData();
};
const loadPurchaseData = () => {
  if (!generateForm.supplierId || !generateForm.period) {
    purchaseData.value = [];
    return;
  }
  purchaseLoading.value = true;
  setTimeout(() => {
    const mockPurchaseData = [
      { id: 1, date: generateForm.period + "-05", code: "RK2024001", type: "入库", amount: 8000, remark: "原材料采购" },
      { id: 2, date: generateForm.period + "-10", code: "FK2024001", type: "付款", amount: 5000, remark: "支付货款" },
      { id: 3, date: generateForm.period + "-15", code: "RK2024002", type: "入库", amount: 12000, remark: "电子元器件" },
      { id: 4, date: generateForm.period + "-18", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" },
      { id: 5, date: generateForm.period + "-22", code: "RK2024003", type: "入库", amount: 6000, remark: "包装材料" },
      { id: 6, date: generateForm.period + "-25", code: "FK2024002", type: "付款", amount: 8000, remark: "支付货款" },
    ];
    purchaseData.value = mockPurchaseData;
    const lastPeriod = getLastPeriod(generateForm.period);
    const lastStatement = mockData.find(item =>
      item.supplierId === generateForm.supplierId && item.period === lastPeriod
    );
    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
    calculateSummary();
    purchaseLoading.value = false;
  }, 500);
};
const getLastPeriod = (period) => {
  const [year, month] = period.split("-").map(Number);
  if (month === 1) {
    return `${year - 1}-12`;
  }
  return `${year}-${String(month - 1).padStart(2, "0")}`;
};
const calculateSummary = () => {
  let payable = 0;
  let payment = 0;
  selectedPurchases.value.forEach(item => {
    if (item.type === "入库") {
      payable += item.amount;
    } else if (item.type === "退货") {
      payable -= item.amount;
    } else if (item.type === "付款") {
      payment += item.amount;
    }
  });
  generateForm.currentPayable = payable;
  generateForm.currentPayment = payment;
};
const handlePurchaseSelectionChange = (selection) => {
  selectedPurchases.value = selection;
  calculateSummary();
};
const confirmGenerate = () => {
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const supplier = supplierList[Math.floor(Math.random() * supplierList.length)];
  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment);
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    supplierId: supplier.id,
    supplierName: supplier.name,
    period: "2024-03",
    beginBalance: Math.floor(Math.random() * 20000),
    currentPayable: Math.floor(Math.random() * 25000),
    currentPayment: Math.floor(Math.random() * 20000),
    endBalance: Math.floor(Math.random() * 25000),
    supplierId: generateForm.supplierId,
    supplierName: generateForm.supplierName,
    period: generateForm.period,
    beginBalance: generateForm.beginBalance,
    currentPayable: generateForm.currentPayable,
    currentPayment: generateForm.currentPayment,
    endBalance,
  });
  generateDialogVisible.value = false;
  ElMessage.success("对账单生成成功");
  getTableData();
};
const viewDetail = (row) => {
  currentSupplier.value = row.supplierName;
  currentPeriod.value = row.period;
  const purchaseInAmount = Math.floor(row.currentPayable * 0.7);
  const returnAmount = Math.floor(row.currentPayable * 0.1);
  const firstPayment = Math.floor(row.currentPayment * 0.5);
  const secondPayment = row.currentPayment - firstPayment;
  let runningBalance = row.beginBalance;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "入库", code: "RK2024001", debit: 0, credit: 8000, balance: row.beginBalance + 8000, remark: "" },
    { date: row.period + "-10", type: "付款", code: "FK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 3000, remark: "" },
    { date: row.period + "-15", type: "入库", code: "RK2024002", credit: 12000, balance: row.beginBalance + 15000, remark: "" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 2000, credit: 0, balance: row.beginBalance + 13000, remark: "" },
    { date: row.period + "-25", type: "付款", code: "FK2024002", debit: row.currentPayment - 5000, balance: row.endBalance, remark: "" },
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "入库", code: "RK2024001", debit: 0, credit: purchaseInAmount, balance: runningBalance += purchaseInAmount, remark: "采购入库" },
    { date: row.period + "-10", type: "付款", code: "FK2024001", debit: firstPayment, credit: 0, balance: runningBalance -= firstPayment, remark: "支付货款" },
    { date: row.period + "-15", type: "入库", code: "RK2024002", debit: 0, credit: row.currentPayable - purchaseInAmount - returnAmount, balance: runningBalance += (row.currentPayable - purchaseInAmount - returnAmount), remark: "采购入库" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: -returnAmount, balance: runningBalance -= returnAmount, remark: "采购退货" },
    { date: row.period + "-25", type: "付款", code: "FK2024002", debit: secondPayment, credit: 0, balance: runningBalance -= secondPayment, remark: "支付货款" },
  ];
  detailDialogVisible.value = true;
};
@@ -251,4 +428,42 @@
    margin: 0;
  }
}
.purchase-section {
  margin-top: 20px;
  .section-title {
    font-size: 16px;
    font-weight: bold;
    margin-bottom: 15px;
    padding-left: 10px;
    border-left: 4px solid #409eff;
  }
}
.summary-row {
  display: flex;
  justify-content: space-around;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 4px;
  margin-top: 15px;
  span {
    font-size: 14px;
    strong {
      font-size: 16px;
      margin-left: 5px;
    }
  }
}
.empty-tip {
  margin-top: 30px;
}
.text-primary {
  color: #409eff;
}
</style>
src/views/financialManagement/receivable/invoiceApply.vue
@@ -61,7 +61,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -119,16 +119,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "开票申请",
src/views/financialManagement/receivable/outputInvoice.vue
@@ -57,7 +57,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -132,16 +132,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "销项发票",
src/views/financialManagement/receivable/receipt.vue
@@ -63,7 +63,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -120,16 +120,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "收款单",
src/views/financialManagement/receivable/reconciliation.vue
@@ -55,7 +55,7 @@
      </PIMTable>
    </div>
    <el-dialog title="对账明细" v-model="detailDialogVisible" width="900px" append-to-body>
    <FormDialog title="对账明细" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
      <div class="statement-header">
        <h3>{{ currentCustomer }} åº”收对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
@@ -88,16 +88,72 @@
        <el-table-column prop="remark" label="备注" show-overflow-tooltip />
      </el-table>
      <template #footer>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
        <el-button type="primary" @click="printDetail">打印</el-button>
        <el-button @click="detailDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>
    </FormDialog>
    <FormDialog title="生成对账单" v-model="generateDialogVisible" width="1000px" @confirm="confirmGenerate" @cancel="generateDialogVisible = false">
      <el-form :model="generateForm" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="选择客户" prop="customerId">
              <el-select v-model="generateForm.customerId" placeholder="请选择客户" style="width: 100%;" @change="onCustomerChange">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="对账月份" prop="period">
              <el-date-picker v-model="generateForm.period" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div v-if="salesData.length > 0" class="sales-section">
        <div class="section-title">本月销售数据</div>
        <el-table :data="salesData" border style="width: 100%; margin-bottom: 15px;" v-loading="salesLoading" @selection-change="handleSalesSelectionChange">
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column prop="date" label="日期" width="120" />
          <el-table-column prop="code" label="单据编号" width="150" />
          <el-table-column prop="type" label="类型" width="100">
            <template #default="{ row }">
              <el-tag :type="row.type === '出库' ? 'success' : row.type === '收款' ? 'primary' : 'danger'">{{ row.type }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额" width="120">
            <template #default="{ row }">
              <span :class="row.type === '出库' ? 'text-primary' : row.type === '收款' ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(row.amount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="remark" label="备注" />
        </el-table>
        <div class="summary-row">
          <span>期初余额: <strong class="text-primary">Â¥{{ formatMoney(generateForm.beginBalance) }}</strong></span>
          <span>本期应收: <strong class="text-primary">Â¥{{ formatMoney(generateForm.currentReceivable) }}</strong></span>
          <span>本期收款: <strong class="text-success">Â¥{{ formatMoney(generateForm.currentReceipt) }}</strong></span>
          <span>期末余额: <strong :class="calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt) >= 0 ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt)) }}</strong></span>
        </div>
      </div>
      <div v-else-if="generateForm.customerId && !salesLoading" class="empty-tip">
        <el-empty description="该客户本月暂无销售数据" />
      </div>
      <template #footer>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
        <el-button @click="generateDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "应收对账",
@@ -132,6 +188,24 @@
const currentPeriod = ref("");
const detailData = ref([]);
const generateDialogVisible = ref(false);
const salesLoading = ref(false);
const salesData = ref([]);
const selectedSales = ref([]);
const generateForm = reactive({
  customerId: "",
  customerName: "",
  period: "",
  beginBalance: 0,
  currentReceivable: 0,
  currentReceipt: 0,
});
const canGenerate = computed(() => {
  return generateForm.customerId && generateForm.period && selectedSales.value.length > 0;
});
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
@@ -144,6 +218,10 @@
  { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "上海贸易公司", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 },
  { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "北京科技有限公司", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 },
];
const calculateEndBalance = (beginBalance, currentReceivable, currentReceipt) => {
  return beginBalance + currentReceivable - currentReceipt;
};
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -177,34 +255,133 @@
};
const generateStatement = () => {
  ElMessage.success("对账单生成成功");
  generateForm.customerId = "";
  generateForm.customerName = "";
  generateForm.period = "";
  generateForm.beginBalance = 0;
  generateForm.currentReceivable = 0;
  generateForm.currentReceipt = 0;
  salesData.value = [];
  selectedSales.value = [];
  generateDialogVisible.value = true;
};
const onCustomerChange = (customerId) => {
  const customer = customerList.find(item => item.id === customerId);
  if (customer) {
    generateForm.customerName = customer.name;
  }
  loadSalesData();
};
const onPeriodChange = () => {
  loadSalesData();
};
const loadSalesData = () => {
  if (!generateForm.customerId || !generateForm.period) {
    salesData.value = [];
    return;
  }
  salesLoading.value = true;
  setTimeout(() => {
    const mockSalesData = [
      { id: 1, date: generateForm.period + "-03", code: "CK2024001", type: "出库", amount: 8000, remark: "产品A销售" },
      { id: 2, date: generateForm.period + "-08", code: "SK2024001", type: "收款", amount: 5000, remark: "客户回款" },
      { id: 3, date: generateForm.period + "-12", code: "CK2024002", type: "出库", amount: 12000, remark: "产品B销售" },
      { id: 4, date: generateForm.period + "-15", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" },
      { id: 5, date: generateForm.period + "-20", code: "CK2024003", type: "出库", amount: 5000, remark: "产品C销售" },
      { id: 6, date: generateForm.period + "-25", code: "SK2024002", type: "收款", amount: 8000, remark: "客户回款" },
    ];
    salesData.value = mockSalesData;
    const lastPeriod = getLastPeriod(generateForm.period);
    const lastStatement = mockData.find(item =>
      item.customerId === generateForm.customerId && item.period === lastPeriod
    );
    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
    calculateSummary();
    salesLoading.value = false;
  }, 500);
};
const getLastPeriod = (period) => {
  const [year, month] = period.split("-").map(Number);
  if (month === 1) {
    return `${year - 1}-12`;
  }
  return `${year}-${String(month - 1).padStart(2, "0")}`;
};
const calculateSummary = () => {
  let receivable = 0;
  let receipt = 0;
  selectedSales.value.forEach(item => {
    if (item.type === "出库") {
      receivable += item.amount;
    } else if (item.type === "退货") {
      receivable -= item.amount;
    } else if (item.type === "收款") {
      receipt += item.amount;
    }
  });
  generateForm.currentReceivable = receivable;
  generateForm.currentReceipt = receipt;
};
const handleSalesSelectionChange = (selection) => {
  selectedSales.value = selection;
  calculateSummary();
};
const confirmGenerate = () => {
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const customer = customerList[Math.floor(Math.random() * customerList.length)];
  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt);
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    customerId: customer.id,
    customerName: customer.name,
    period: "2024-03",
    beginBalance: Math.floor(Math.random() * 10000),
    currentReceivable: Math.floor(Math.random() * 20000),
    currentReceipt: Math.floor(Math.random() * 15000),
    endBalance: Math.floor(Math.random() * 20000),
    customerId: generateForm.customerId,
    customerName: generateForm.customerName,
    period: generateForm.period,
    beginBalance: generateForm.beginBalance,
    currentReceivable: generateForm.currentReceivable,
    currentReceipt: generateForm.currentReceipt,
    endBalance,
  });
  generateDialogVisible.value = false;
  ElMessage.success("对账单生成成功");
  getTableData();
};
const viewDetail = (row) => {
  currentCustomer.value = row.customerName;
  currentPeriod.value = row.period;
  const saleOutAmount = Math.floor(row.currentReceivable * 0.6);
  const returnAmount = Math.floor(row.currentReceivable * 0.1);
  const firstReceipt = Math.floor(row.currentReceipt * 0.4);
  const secondReceipt = row.currentReceipt - firstReceipt;
  let runningBalance = row.beginBalance;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: row.beginBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "出库", code: "CK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 5000, remark: "" },
    { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: 3000, balance: row.beginBalance + 2000, remark: "" },
    { date: row.period + "-15", type: "出库", code: "CK2024002", debit: 8000, credit: 0, balance: row.beginBalance + 10000, remark: "" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: 2000, balance: row.beginBalance + 8000, remark: "" },
    { date: row.period + "-25", type: "收款", code: "SK2024002", credit: row.currentReceipt - 3000, balance: row.endBalance, remark: "" },
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "出库", code: "CK2024001", debit: saleOutAmount, credit: 0, balance: runningBalance += saleOutAmount, remark: "销售出库" },
    { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: firstReceipt, balance: runningBalance -= firstReceipt, remark: "客户回款" },
    { date: row.period + "-15", type: "出库", code: "CK2024002", debit: row.currentReceivable - saleOutAmount - returnAmount, credit: 0, balance: runningBalance += (row.currentReceivable - saleOutAmount - returnAmount), remark: "销售出库" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: returnAmount, balance: runningBalance -= returnAmount, remark: "销售退货" },
    { date: row.period + "-25", type: "收款", code: "SK2024002", debit: 0, credit: secondReceipt, balance: runningBalance -= secondReceipt, remark: "客户回款" },
  ];
  detailDialogVisible.value = true;
};
@@ -255,4 +432,38 @@
    margin: 0;
  }
}
.sales-section {
  margin-top: 20px;
  .section-title {
    font-size: 16px;
    font-weight: bold;
    margin-bottom: 15px;
    padding-left: 10px;
    border-left: 4px solid #409eff;
  }
}
.summary-row {
  display: flex;
  justify-content: space-around;
  padding: 15px;
  background-color: #f5f7fa;
  border-radius: 4px;
  margin-top: 15px;
  span {
    font-size: 14px;
    strong {
      font-size: 16px;
      margin-left: 5px;
    }
  }
}
.empty-tip {
  margin-top: 30px;
}
</style>
src/views/financialManagement/receivable/salesOut.vue
@@ -21,7 +21,6 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增出库</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
@@ -47,7 +46,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -80,16 +79,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "销售出库",
src/views/financialManagement/receivable/salesReturn.vue
@@ -21,7 +21,6 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary" @click="add" icon="Plus">新增退货</el-button>
          <el-button @click="handleOut" icon="Download">导出</el-button>
        </div>
      </div>
@@ -47,7 +46,7 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" append-to-body>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
@@ -94,16 +93,17 @@
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm">确定</el-button>
        <el-button @click="dialogVisible = false">取消</el-button>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "销售退货",
src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog v-model="visible" title="收款/退款" width="90%" append-to-body>
  <FormDialog v-model="visible" title="收款/退款" width="90%" @confirm="submit" @cancel="visible=false">
    <div class="section">
      <div class="section-title descriptions">基础资料</div>
      <el-form :model="form" label-width="100px">
@@ -140,12 +140,13 @@
      <el-button type="primary" @click="submit">确认</el-button>
      <el-button @click="visible=false">取消</el-button>
    </template>
  </el-dialog>
  </FormDialog>
</template>
<script setup>
import { ref } from 'vue';
import { getToken } from '@/utils/auth';
import FormDialog from "@/components/Dialog/FormDialog.vue";
const visible = ref(false);
const form = ref({
src/views/financialManagement/voucher/index.vue
@@ -69,87 +69,142 @@
      </PIMTable>
    </div>
    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="900px" append-to-body>
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="凭证字号" prop="voucherNo">
              <el-input v-model="form.voucherNo" placeholder="系统自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="凭证日期" prop="voucherDate">
              <el-date-picker v-model="form.voucherDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="附件张数" prop="attachmentCount">
              <el-input-number v-model="form.attachmentCount" :min="0" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="凭证分录" prop="entries">
          <el-table :data="form.entries" border style="width: 100%">
            <el-table-column type="index" label="序号" width="60" />
            <el-table-column prop="subjectCode" label="科目编码" width="120">
              <template #default="{ $index }">
                <el-select v-model="form.entries[$index].subjectCode" placeholder="选择科目" filterable style="width: 100%;" @change="(val) => handleSubjectChange(val, $index)">
                  <el-option v-for="item in subjectList" :key="item.code" :label="item.code" :value="item.code" />
                </el-select>
              </template>
            </el-table-column>
            <el-table-column prop="subjectName" label="科目名称" width="150">
              <template #default="{ $index }">
                <el-input v-model="form.entries[$index].subjectName" disabled />
              </template>
            </el-table-column>
            <el-table-column prop="summary" label="摘要">
              <template #default="{ $index }">
                <el-input v-model="form.entries[$index].summary" placeholder="请输入摘要" />
              </template>
            </el-table-column>
            <el-table-column prop="debit" label="借方金额" width="130">
              <template #default="{ $index }">
                <el-input-number v-model="form.entries[$index].debit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" />
              </template>
            </el-table-column>
            <el-table-column prop="credit" label="贷方金额" width="130">
              <template #default="{ $index }">
                <el-input-number v-model="form.entries[$index].credit" :min="0" :precision="2" style="width: 100%;" @change="calculateTotal" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80">
              <template #default="{ $index }">
                <el-button type="danger" link @click="removeEntry($index)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div style="display: flex; justify-content: space-between; margin-top: 10px;">
            <el-button type="primary" link @click="addEntry">+ æ·»åŠ åˆ†å½•</el-button>
            <div>
              <span style="margin-right: 20px;">合计: å€Ÿæ–¹ <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalDebitEntry) }}</span></span>
              <span>贷方 <span :class="totalDebitEntry === totalCreditEntry ? 'text-success' : 'text-danger'">Â¥{{ formatMoney(totalCreditEntry) }}</span></span>
    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="1200px" @confirm="submitForm" @cancel="dialogVisible = false">
      <div class="voucher-container">
        <div class="voucher-header">
          <h2 class="voucher-title">记账凭证</h2>
          <div class="voucher-period">{{ form.voucherDate ? form.voucherDate.substring(0, 7) + '期' : '' }}</div>
        </div>
        <el-form :model="form" :rules="rules" ref="formRef" label-width="0">
          <div class="voucher-info">
            <div class="voucher-no-section">
              <span class="label">凭证字:</span>
              <el-select v-model="form.voucherPrefix" style="width: 70px;">
                <el-option label="è®°" value="è®°" />
              </el-select>
              <el-input v-model="form.voucherNum" style="width: 60px;" />
              <span class="label" style="margin-left: 5px;">号</span>
            </div>
            <div class="voucher-date-section">
              <span class="label">日期:</span>
              <el-date-picker v-model="form.voucherDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 140px;" />
            </div>
            <div class="voucher-attachment-section">
              <span class="label">附件:</span>
              <el-input-number v-model="form.attachmentCount" :min="0" :controls="false" style="width: 60px;" />
              <span class="label" style="margin-left: 5px;">å¼ </span>
              <el-button type="primary" link style="margin-left: 10px;">上传文件</el-button>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="制单人" prop="creator">
          <el-input v-model="form.creator" disabled />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
          <div class="voucher-table">
            <table class="accounting-voucher">
              <thead>
                <tr>
                  <th class="col-summary" rowspan="2">摘要</th>
                  <th class="col-subject" rowspan="2">会计科目</th>
                  <th class="col-debit-header" colspan="11">借方</th>
                  <th class="col-credit-header" colspan="11">贷方</th>
                  <th class="col-action" rowspan="2">操作</th>
                </tr>
                <tr class="amount-header">
                  <th>亿</th>
                  <th>千</th>
                  <th>百</th>
                  <th>十</th>
                  <th>万</th>
                  <th>千</th>
                  <th>百</th>
                  <th>十</th>
                  <th>元</th>
                  <th>角</th>
                  <th>分</th>
                  <th>亿</th>
                  <th>千</th>
                  <th>百</th>
                  <th>十</th>
                  <th>万</th>
                  <th>千</th>
                  <th>百</th>
                  <th>十</th>
                  <th>元</th>
                  <th>角</th>
                  <th>分</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="(entry, rowIndex) in form.entries" :key="rowIndex" @click="selectRow(rowIndex)" :class="{ 'selected-row': selectedRowIndex === rowIndex }">
                  <td class="col-summary">
                    <el-input v-model="entry.summary" placeholder="请输入摘要" @focus="selectRow(rowIndex)" />
                  </td>
                  <td class="col-subject">
                    <el-select v-model="entry.subjectCode" placeholder="选择科目" filterable @change="(val) => handleSubjectChange(val, rowIndex)" @focus="selectRow(rowIndex)">
                      <el-option v-for="item in subjectList" :key="item.code" :label="item.code + item.name" :value="item.code" />
                    </el-select>
                    <div class="subject-name">{{ entry.subjectName }}</div>
                  </td>
                  <!-- å€Ÿæ–¹11列 -->
                  <template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'">
                    <td colspan="11" class="debit-input-cell">
                      <el-input-number ref="amountInputRef" v-model="entry.debit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
                    </td>
                  </template>
                  <template v-else>
                    <td v-for="(digit, dIndex) in getAmountDigits(entry.debit, 11)" :key="'debit-'+dIndex" class="amount-cell debit-cell" @click="openAmountInput(rowIndex, 'debit')">
                      <span :class="{ 'text-primary': digit !== '', 'zero': digit === '' }">{{ digit || '' }}</span>
                    </td>
                  </template>
                  <!-- è´·æ–¹11列 -->
                  <template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'">
                    <td colspan="11" class="credit-input-cell">
                      <el-input-number ref="amountInputRef" v-model="entry.credit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
                    </td>
                  </template>
                  <template v-else>
                    <td v-for="(digit, dIndex) in getAmountDigits(entry.credit, 11)" :key="'credit-'+dIndex" class="amount-cell credit-cell" @click="openAmountInput(rowIndex, 'credit')">
                      <span :class="{ 'text-danger': digit !== '', 'zero': digit === '' }">{{ digit || '' }}</span>
                    </td>
                  </template>
                  <td class="col-action">
                    <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="form.entries.length <= 2">删除</el-button>
                  </td>
                </tr>
                <tr class="total-row">
                  <td class="col-summary" colspan="2" style="text-align: center; font-weight: bold;">合计:</td>
                  <td v-for="(digit, index) in getAmountDigits(totalDebitEntry, 11)" :key="'total-debit-'+index" class="amount-cell total-debit-cell">
                    <span :class="{ 'text-primary': digit !== '' }">{{ digit }}</span>
                  </td>
                  <td v-for="(digit, index) in getAmountDigits(totalCreditEntry, 11)" :key="'total-credit-'+index" class="amount-cell total-credit-cell">
                    <span :class="{ 'text-danger': digit !== '' }">{{ digit }}</span>
                  </td>
                  <td class="col-action"></td>
                </tr>
              </tbody>
            </table>
          </div>
          <div class="voucher-toolbar">
            <el-button type="primary" link @click="addEntry" icon="Plus">新增行</el-button>
          </div>
          <div class="voucher-footer">
            <div class="creator-section">
              <span class="label">制单人:{{ form.creator }}</span>
            </div>
          </div>
        </el-form>
      </div>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="submitForm" :disabled="totalDebitEntry !== totalCreditEntry">确定</el-button>
        <div>
          <el-button type="primary" @click="submitForm" :disabled="!isBalanced">保存</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
        </div>
      </template>
    </el-dialog>
    </FormDialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ref, reactive, onMounted, computed, nextTick } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
defineOptions({
  name: "凭证管理",
@@ -198,11 +253,24 @@
const form = reactive({
  voucherNo: "",
  voucherPrefix: "è®°",
  voucherNum: "",
  voucherDate: "",
  attachmentCount: 0,
  entries: [],
  creator: "张三",
  remark: "",
});
const selectedRowIndex = ref(-1);
const editingCell = reactive({
  row: -1,
  type: "",
});
const amountInputRef = ref(null);
const isBalanced = computed(() => {
  return totalDebitEntry.value === totalCreditEntry.value && totalDebitEntry.value > 0;
});
const rules = {
@@ -283,6 +351,56 @@
  form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
};
const selectRow = (index) => {
  selectedRowIndex.value = index;
};
const openAmountInput = (index, type) => {
  editingCell.row = index;
  editingCell.type = type;
  nextTick(() => {
    if (amountInputRef.value) {
      amountInputRef.value.focus();
    }
  });
};
const finishEdit = () => {
  editingCell.row = -1;
  editingCell.type = "";
};
const getAmountDigits = (amount, length) => {
  if (!amount || amount === 0) {
    return new Array(length).fill('');
  }
  const amountStr = Number(amount).toFixed(2);
  const [intPart, decPart] = amountStr.split('.');
  const fullAmount = intPart + decPart;
  // å·¦å¡«å……0到指定长度
  const paddedAmount = fullAmount.padStart(length, '0');
  const digits = paddedAmount.split('');
  // æ‰¾åˆ°ç¬¬ä¸€ä¸ªéžé›¶æ•°å­—的位置
  let firstNonZeroIndex = 0;
  for (let i = 0; i < digits.length; i++) {
    if (digits[i] !== '0') {
      firstNonZeroIndex = i;
      break;
    }
  }
  // åªéšè—å‰å¯¼é›¶ï¼ˆç¬¬ä¸€ä¸ªéžé›¶æ•°å­—之前的零)
  return digits.map((d, index) => {
    if (index < firstNonZeroIndex) {
      return ''; // å‰å¯¼é›¶æ˜¾ç¤ºä¸ºç©º
    }
    return d; // ä¿ç•™é‡‘额中的零
  });
};
const removeEntry = (index) => {
  form.entries.splice(index, 1);
  calculateTotal();
@@ -302,14 +420,23 @@
const add = () => {
  isEdit.value = false;
  dialogTitle.value = "新增凭证";
  const nextNum = String(mockData.length + 1).padStart(2, "0");
  Object.assign(form, {
    voucherNo: "è®°-" + String(mockData.length + 1).padStart(4, "0"),
    voucherNo: "è®°-" + nextNum,
    voucherPrefix: "è®°",
    voucherNum: nextNum,
    voucherDate: new Date().toISOString().split('T')[0],
    attachmentCount: 0,
    entries: [{ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 }],
    entries: [
      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
    ],
    creator: "张三",
    remark: "",
  });
  selectedRowIndex.value = 0;
  dialogVisible.value = true;
};
@@ -317,7 +444,18 @@
  isEdit.value = true;
  currentId.value = row.id;
  dialogTitle.value = "编辑凭证";
  Object.assign(form, row);
  const parts = row.voucherNo.split('-');
  Object.assign(form, {
    ...row,
    voucherPrefix: parts[0] || 'è®°',
    voucherNum: parts[1] || '',
  });
  if (form.entries.length < 4) {
    while (form.entries.length < 4) {
      form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
    }
  }
  selectedRowIndex.value = 0;
  dialogVisible.value = true;
};
@@ -366,20 +504,33 @@
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      if (totalDebitEntry.value !== totalCreditEntry.value) {
      if (!isBalanced.value) {
        ElMessage.error("借贷不平衡,请检查分录");
        return;
      }
      const summary = form.entries.find(e => e.debit > 0)?.summary || "";
      const validEntries = form.entries.filter(e => e.subjectCode && (e.debit > 0 || e.credit > 0));
      const summary = validEntries.find(e => e.debit > 0)?.summary || "";
      const voucherNo = `${form.voucherPrefix}-${form.voucherNum}`;
      const dataToSave = {
        ...form,
        voucherNo,
        summary,
        debit: totalDebitEntry.value,
        credit: totalCreditEntry.value,
        entries: validEntries,
      };
      if (isEdit.value) {
        const index = mockData.findIndex(item => item.id === currentId.value);
        if (index !== -1) {
          mockData[index] = { ...mockData[index], ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value };
          mockData[index] = { ...mockData[index], ...dataToSave };
        }
        ElMessage.success("编辑成功");
      } else {
        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
        mockData.push({ id: newId, ...form, summary, debit: totalDebitEntry.value, credit: totalCreditEntry.value, status: "unposted" });
        mockData.push({ id: newId, ...dataToSave, status: "unposted" });
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
@@ -415,4 +566,271 @@
  color: #f56c6c;
  font-weight: bold;
}
.text-primary {
  color: #409eff;
}
.voucher-container {
  background: #fff;
  padding: 20px;
}
.voucher-header {
  text-align: center;
  margin-bottom: 15px;
  .voucher-title {
    font-size: 22px;
    font-weight: bold;
    margin: 0 0 5px 0;
    color: #303133;
  }
  .voucher-period {
    font-size: 14px;
    color: #909399;
  }
}
.voucher-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  padding: 0 10px;
  .label {
    font-size: 14px;
    color: #606266;
  }
  .voucher-no-section,
  .voucher-date-section,
  .voucher-attachment-section {
    display: flex;
    align-items: center;
  }
}
.voucher-table {
  border: 1px solid #dcdfe6;
  border-right: none;
  margin-bottom: 15px;
}
.accounting-voucher {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  th,
  td {
    border: 1px solid #dcdfe6;
    text-align: center;
    padding: 0;
    height: 36px;
  }
  & th:last-child,
  & td:last-child {
    border-right: none !important;
  }
  thead {
    background-color: #f5f7fa;
    th {
      font-weight: normal;
      color: #606266;
      font-size: 12px;
    }
    .col-summary,
    .col-subject {
      font-weight: bold;
      font-size: 13px;
    }
    .col-debit-header,
    .col-credit-header {
      background-color: #ecf5ff;
      color: #409eff;
      font-weight: bold;
    }
  }
  .amount-header {
    th {
      font-size: 11px;
      padding: 2px 0;
      background-color: #f5f7fa;
    }
  }
  .col-summary {
    width: 160px;
    min-width: 160px;
  }
  .col-subject {
    width: 180px;
    min-width: 180px;
  }
  .col-action {
    width: 60px;
    min-width: 60px;
    text-align: center;
  }
  .amount-cell {
    width: 24px;
    min-width: 24px;
    max-width: 24px;
    padding: 0;
    font-size: 13px;
    font-family: 'Courier New', monospace;
    cursor: pointer;
    text-align: center;
    &:hover {
      background-color: #f5f7fa;
    }
    span {
      display: block;
      width: 100%;
      height: 100%;
      line-height: 36px;
      &.zero {
        color: #c0c4cc;
      }
    }
  }
  .debit-input-cell,
  .credit-input-cell {
    padding: 0;
    background-color: #ecf5ff;
    .full-width-input {
      width: 100%;
      :deep(.el-input__wrapper) {
        padding: 0 10px;
        box-shadow: none;
        background-color: transparent;
      }
      input {
        text-align: right;
        font-size: 14px;
        height: 34px;
      }
    }
  }
  tbody {
    tr {
      &:hover {
        background-color: #f5f7fa;
      }
      &.selected-row {
        background-color: #ecf5ff;
      }
    }
    td {
      .el-input {
        .el-input__wrapper {
          box-shadow: none;
          padding: 0 5px;
        }
        input {
          text-align: center;
          height: 34px;
        }
      }
      .el-select {
        width: 100%;
        .el-input__wrapper {
          box-shadow: none;
        }
        input {
          text-align: center;
          height: 34px;
        }
      }
    }
    .col-summary {
      .el-input input {
        text-align: left;
        padding-left: 10px;
      }
    }
    .col-subject {
      position: relative;
      .el-select {
        .el-input input {
          font-size: 12px;
        }
      }
      .subject-name {
        font-size: 11px;
        color: #909399;
        margin-top: 2px;
        line-height: 1.2;
      }
    }
  }
  .total-row {
    background-color: #fdf6ec;
    td {
      font-weight: bold;
    }
    .total-cell {
      background-color: #fdf6ec;
      font-weight: bold;
    }
  }
}
.voucher-toolbar {
  display: flex;
  justify-content: flex-start;
  padding: 10px 0;
  margin-top: 5px;
}
.voucher-footer {
  display: flex;
  justify-content: flex-end;
  padding: 0 10px;
  margin-top: 10px;
  .creator-section {
    .label {
      font-size: 14px;
      color: #606266;
    }
  }
}
:deep(.el-dialog__body) {
  padding: 10px 20px;
}
</style>
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -1,6 +1,6 @@
<template>
    <div class="app-container">
        <div class="search_form">
    <div>
        <div class="search_form" style="margin-bottom: 10px;">
            <div>
                <span class="search_title ml10">出库日期:</span>
                <el-date-picker
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
  <div>
    <div class="search_form" style="margin-bottom: 10px;">
      <div>
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker v-model="searchForm.timeStr"
src/views/inventoryManagement/stockManagement/Record.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
  <div>
    <div class="search_form mb10">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
src/views/lavorissue/statistics/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">发放季度:</span>
        <el-select
src/views/personnelManagement/contractManagement/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">姓名:</span>
        <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="请输入姓名搜索" @change="handleQuery"
src/views/personnelManagement/dimission/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">姓名:</span>
        <el-input
src/views/personnelManagement/employeeRecord/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">姓名:</span>
        <el-input
src/views/personnelManagement/socialSecuritySet/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">主题:</span>
        <el-input
src/views/procurementManagement/paymentLedger/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form" style="margin-bottom: 20px;">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input
src/views/procurementManagement/procurementLedger/index.vue
@@ -635,7 +635,6 @@
</template>
<script setup>
import {Search, Delete} from "@element-plus/icons-vue";
import FormDialog from '@/components/Dialog/FormDialog.vue';
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
  import { getToken } from "@/utils/auth";
src/views/procurementManagement/procurementReport/index.vue
@@ -334,12 +334,6 @@
</script>
<style scoped>
.app-container {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
}
.page-header {
  text-align: center;
  margin-bottom: 20px;
src/views/productionManagement/processStatistics/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,268 @@
<template>
  <div class="app-container">
    <div class="search-bar">
      <el-form :model="searchForm"
               inline>
        <el-form-item label="日期区间:">
          <el-date-picker v-model="searchForm.dateRange"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          end-placeholder="结束日期"
                          value-format="YYYY-MM-DD"
                          style="width: 240px" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     icon="Search"
                     @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="stats-grid"
         v-loading="loading">
      <el-row :gutter="16"
              v-if="statsData.length > 0">
        <el-col v-for="(item, index) in statsData"
                :key="index"
                :xs="24"
                :sm="12"
                :md="8"
                :lg="4.8"
                :xl="4.8"
                class="mb-16">
          <div class="stats-card">
            <div class="card-header">
              <span class="process-name">{{ item.name }}</span>
              <div class="header-stats">
                <div class="stat-row">
                  <span class="label">计划数</span>
                  <span class="value">{{ item.planned }}</span>
                </div>
                <div class="stat-row">
                  <span class="label">良品数</span>
                  <span class="value">{{ item.good }}</span>
                </div>
                <div class="stat-row">
                  <span class="label">不良品数</span>
                  <span class="value">{{ item.bad }}</span>
                </div>
              </div>
            </div>
            <div class="card-body">
              <div class="main-stat">
                <div class="big-number">{{ item.total }}</div>
                <div class="sub-label">生产任务数</div>
              </div>
            </div>
            <div class="card-footer">
              <div class="progress-info">
                <span class="progress-label">进度:</span>
                <el-progress :percentage="Math.min(item.percentage, 100)"
                             :color="getProgressColor(item.percentage)"
                             :stroke-width="10"
                             :show-text="false"
                             class="flex-1" />
                <span class="percentage-text">{{ item.percentage }}%</span>
              </div>
            </div>
          </div>
        </el-col>
      </el-row>
      <el-empty v-else
                description="暂无数据" />
    </div>
  </div>
</template>
<script setup>
  import { reactive, ref, onMounted } from "vue";
  import dayjs from "dayjs";
  import { getOperationStatistics } from "@/api/productionManagement/workOrder.js";
  const loading = ref(false);
  const searchForm = reactive({
    dateRange: [],
  });
  const statsData = ref([]);
  const getProgressColor = percentage => {
    if (percentage >= 100) return "#67c23a";
    if (percentage >= 50) return "#409eff";
    if (percentage >= 25) return "#e6a23c";
    return "red";
  };
  const getList = () => {
    loading.value = true;
    const params = {
      startDate: searchForm.dateRange?.[0] || "",
      endDate: searchForm.dateRange?.[1] || "",
    };
    getOperationStatistics(params)
      .then(res => {
        // æ ¹æ®å®žé™…接口返回的字段进行映射
        statsData.value = (res.data || []).map(item => ({
          name: item.operationName || "-",
          total: item.productionTaskCount || 0,
          planned: item.planQuantity || 0,
          good: item.goodQuantity || 0,
          bad: item.scrapQty || 0,
          percentage: Number(item.completionStatus || 0),
        }));
      })
      .finally(() => {
        loading.value = false;
      });
  };
  const handleQuery = () => {
    getList();
  };
  onMounted(() => {
    getList();
  });
</script>
<style scoped lang="scss">
  .app-container {
    padding: 20px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 84px);
  }
  .search-bar {
    background: #fff;
    padding: 15px 20px 0;
    border-radius: 4px;
    margin-bottom: 20px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  }
  .mb-16 {
    margin-bottom: 16px;
  }
  // æ¨¡æ‹Ÿ lg="4.8" å› ä¸º element ä¸æ”¯æŒ 24/5
  @media only screen and (min-width: 1200px) {
    .el-col-lg-4-8 {
      width: 20%;
      max-width: 20%;
      flex: 0 0 20%;
    }
  }
  .stats-card {
    background: #fff;
    border-radius: 8px;
    padding: 16px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: transform 0.3s;
    &:hover {
      transform: translateY(-2px);
    }
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      margin-bottom: 12px;
      .process-name {
        background-color: #e6f7ff;
        color: #1890ff;
        padding: 2px 8px;
        border-radius: 4px;
        font-size: 14px;
        font-weight: 500;
      }
      .header-stats {
        text-align: right;
        .stat-row {
          display: flex;
          justify-content: flex-end;
          align-items: center;
          gap: 8px;
          margin-bottom: 2px;
          .label {
            font-size: 12px;
            color: #909399;
          }
          .value {
            font-size: 13px;
            color: #303133;
            font-weight: bold;
            min-width: 24px;
          }
        }
      }
    }
    .card-body {
      padding: 10px 0;
      .main-stat {
        .big-number {
          font-size: 28px;
          font-weight: bold;
          color: #303133;
          line-height: 1;
        }
        .sub-label {
          font-size: 14px;
          color: #606266;
          margin-top: 8px;
          font-weight: 500;
        }
      }
    }
    .card-footer {
      margin-top: 16px;
      .progress-info {
        display: flex;
        align-items: center;
        gap: 8px;
        .progress-label {
          font-size: 12px;
          color: #909399;
          white-space: nowrap;
        }
        .flex-1 {
          flex: 1;
        }
        .percentage-text {
          font-size: 12px;
          color: #606266;
          min-width: 45px;
          text-align: right;
        }
      }
    }
  }
  // ä¿®æ­£ el-col å¸ƒå±€é€‚配 5 åˆ—
  :deep(.el-row) {
    display: flex;
    flex-wrap: wrap;
  }
  @media only screen and (min-width: 1200px) {
    .el-col-lg-4\.8 {
      flex: 0 0 20%;
      max-width: 20%;
    }
  }
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -75,6 +75,26 @@
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
        <template #processRouteStatus="{ row }">
          <div v-if="row.processRouteStatus && row.processRouteStatus.length"
               class="process-progress-container">
            <div v-for="(item, index) in row.processRouteStatus"
                 :key="index"
                 class="process-step">
              <div class="step-content">
                <div class="step-circle"
                     :class="{ 'is-completed': item.percentage >= 100 }">
                  <span class="step-percentage"
                        :style="{ color: item.percentage >= 70 ? item.percentage >= 100 ? '#67c23a' : '#f56c6c' : '#000' }">{{ item.percentage }}%</span>
                </div>
                <div class="step-name">{{ item.name }}</div>
              </div>
              <div v-if="index < row.processRouteStatus.length - 1"
                   class="step-line"></div>
            </div>
          </div>
          <span v-else>-</span>
        </template>
      </PIMTable>
    </div>
    <el-dialog v-model="bindRouteDialogVisible"
@@ -215,6 +235,7 @@
    getProductOrderSource,
    updateProductOrder,
  } from "@/api/productionManagement/productionOrder.js";
  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue";
  import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue";
@@ -241,7 +262,17 @@
    total: 0,
  });
  const tableColumn = ref([
  const processColumnWidth = computed(() => {
    if (!tableData.value || tableData.value.length === 0) return "200px";
    const maxProcesses = Math.max(
      ...tableData.value.map(row => row.processRouteStatus?.length || 0)
    );
    if (maxProcesses === 0) return "100px";
    // æ¯ä¸ªå·¥åºåœ†åœˆ 36px + çº¿æ¡ 30px = 66px,额外加 60px è¾¹è·å’Œæ–‡å­—空间
    return `${maxProcesses * 66 + 60}px`;
  });
  const tableColumn = computed(() => [
    {
      label: "生产订单号",
      prop: "npsNo",
@@ -296,6 +327,13 @@
    {
      label: "完成数量",
      prop: "completeQuantity",
    },
    {
      label: "工序生产进度",
      prop: "processRouteStatus",
      dataType: "slot",
      slot: "processRouteStatus",
      width: processColumnWidth.value,
    },
    {
      dataType: "slot",
@@ -630,10 +668,35 @@
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
      .then(async res => {
        const records = res.data.records || [];
        // ä¸ºæ¯ä¸ªè®¢å•查询对应的工序进度数据
        const processPromises = records.map(async item => {
          if (item.npsNo) {
            try {
              const workOrderRes = await productWorkOrderPage({
                npsNo: item.npsNo,
                size: 100,
              });
              const workOrders = workOrderRes.data.records || [];
              // æŒ‰ç…§å·¥åºé¡ºåºæŽ’序(如果有顺序字段,假设为 orderNum æˆ–按返回顺序)
              // è½¬æ¢ä¸º processRouteStatus æ ¼å¼
              const processRouteStatus = workOrders.map(wo => ({
                name: wo.operationName || "未知工序",
                percentage: wo.completionStatus > 100 ? 100 : wo.completionStatus,
              }));
              return { ...item, processRouteStatus };
            } catch (error) {
              console.error(`获取工单 ${item.npsNo} è¿›åº¦å¤±è´¥:`, error);
              return { ...item, processRouteStatus: [] };
            }
          }
          return { ...item, processRouteStatus: [] };
        });
        tableData.value = await Promise.all(processPromises);
        page.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(() => {
        tableLoading.value = false;
@@ -813,6 +876,64 @@
  .table_list {
    margin-top: unset;
  }
  .process-progress-container {
    display: inline-flex;
    align-items: center;
    padding: 10px 0;
    white-space: nowrap;
    .process-step {
      display: flex;
      align-items: center;
      position: relative;
      .step-content {
        display: flex;
        flex-direction: column;
        align-items: center;
        z-index: 1;
        .step-circle {
          width: 36px;
          height: 36px;
          border-radius: 50%;
          border: 2px solid #409eff;
          display: flex;
          align-items: center;
          justify-content: center;
          background-color: #fff;
          margin-bottom: 4px;
          .step-percentage {
            font-size: 11px;
            font-weight: bold;
          }
          &.is-completed {
            border-color: #67c23a;
            .step-percentage {
              color: #67c23a;
            }
          }
        }
        .step-name {
          font-size: 12px;
          color: #606266;
          white-space: nowrap;
        }
      }
      .step-line {
        width: 30px;
        height: 1px;
        background-color: #dcdfe6;
        margin: 0 -2px;
        margin-top: -20px; // å‘上偏移以对齐圆心
      }
    }
  }
</style>
<style lang="scss">
  .status-cell {
src/views/productionManagement/productionTraceability/index.vue
@@ -22,9 +22,6 @@
                           :value="option.id" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-button @click="handleBack">返回</el-button>
            </el-form-item>
          </el-form>
        </div>
      </template>
@@ -37,7 +34,7 @@
          <el-descriptions-item label="生产订单号">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品名称">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品规格">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item>
          <el-descriptions-item label="物料编码">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item>
          <!-- <el-descriptions-item label="物料编码">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item> -->
          <el-descriptions-item label="计划数量">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item>
          <el-descriptions-item label="当前状态">
            <el-tag :type="getStatusType(rowData.productionOrderDto?.status)">
@@ -45,12 +42,12 @@
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="客户名称">{{ rowData.productionOrderDto?.customerName || '-' }}</el-descriptions-item>
          <el-descriptions-item label="开始日期">{{ rowData.productionOrderDto?.startTime || '-' }}</el-descriptions-item>
          <el-descriptions-item label="开始日期">{{ parseTime(rowData.productionOrderDto?.startTime) || '-' }}</el-descriptions-item>
          <el-descriptions-item label="完成进度">
            <el-progress :percentage="rowData.productionOrderDto?.completionStatus"
            <el-progress :percentage="rowData.productionOrderDto?.completionStatus>=100?100:rowData.productionOrderDto?.completionStatus"
                         :color="customColors(rowData.productionOrderDto?.completionStatus)"
                         :status="rowData.productionOrderDto?.completionStatus === 100 ? 'success' : ''"
                         style="width: 120px;" />
                         :status="rowData.productionOrderDto?.completionStatus >= 100 ? 'success' : ''"
                         style="width: 80%;" />
          </el-descriptions-item>
        </el-descriptions>
      </div>
@@ -65,25 +62,35 @@
            <el-table :data="rowData.productionRecords"
                      border
                      style="width: 100%">
              <el-table-column prop="productNo"
              <el-table-column prop="workOrder.workOrderNo"
                               label="工单编号"
                               align="center">
              </el-table-column>
              <el-table-column prop="productName"
                               label="产品名称"
                               align="center" />
              <el-table-column prop="model"
                               label="规格"
                               align="center" />
              <el-table-column prop="processName"
                               label="工序名称"
                               align="center" />
              <el-table-column prop="requiredQuantity"
              <el-table-column label="产品名称"
                               align="center">
                <template #default="{ row }">
                  {{ row.workOrder.productName || '-' }}
                </template>
              </el-table-column>
              <el-table-column label="规格"
                               align="center">
                <template #default="{ row }">
                  {{ row.workOrder.model || '-' }}
                </template>
              </el-table-column>
              <el-table-column prop="workOrder.planQuantity"
                               label="需求数量"
                               align="center" />
              <el-table-column prop="completedQuantity"
              <el-table-column prop="workOrder.completeQuantity"
                               label="完成数量"
                               align="center" />
              <el-table-column prop="workOrder.completionStatus"
                               label="完成进度"
                               align="center">
                <template #default="{ row }">
                  <span :style="{ color: customColors(row.workOrder.completionStatus) }">{{ row.workOrder.completionStatus || 0 }}%</span>
                </template>
              </el-table-column>
              <el-table-column label="详情"
                               align="center"
                               width="200">
@@ -111,39 +118,40 @@
      <div class="detail-container">
        <!-- åŸºç¡€ä¿¡æ¯ -->
        <div class="detail-section">
          <h3 class="section-title">基础信息</h3>
          <h3 class="section-title">工单基础信息</h3>
          <el-descriptions :column="3"
                           border>
            <el-descriptions-item label="生产工单号">{{ detailData.npsNo || '-' }}</el-descriptions-item>
            <el-descriptions-item label="班组">
              <el-tag :type="detailData.schedule === '白班' ? 'primary' : 'warning'">{{ detailData.schedule || '-' }}</el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="产品编码">{{ detailData.materialCode || '-' }}</el-descriptions-item>
            <el-descriptions-item label="产品名称">{{ detailData.productName || '-' }}</el-descriptions-item>
            <el-descriptions-item label="规格">{{ detailData.model || '-' }}</el-descriptions-item>
            <el-descriptions-item label="合格数量"><span class="num2">{{ detailData.qualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
            <el-descriptions-item label="不合格数量"><span class="num3">{{ detailData.unqualifiedQuantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
            <el-descriptions-item label="总数量"><span class="num1">{{ detailData.quantity || 0 }}</span> <span class="unit">{{ detailData.unit || '-' }}</span></el-descriptions-item>
            <el-descriptions-item label="开始时间">{{ detailData.reportingTime || '-' }}</el-descriptions-item>
            <el-descriptions-item label="生产工单号">{{ detailData.workOrder.workOrderNo || '-' }}</el-descriptions-item>
            <el-descriptions-item label="计划数量">{{ detailData.workOrder.planQuantity || 0 }}</el-descriptions-item>
            <el-descriptions-item label="完成数量">{{ detailData.workOrder.completeQuantity || 0 }}</el-descriptions-item>
            <el-descriptions-item label="实际开始时间">{{ parseTime(detailData.workOrder.actualStartTime) || '-' }}</el-descriptions-item>
            <el-descriptions-item label="实际结束时间">{{ parseTime(detailData.workOrder.actualEndTime) || '-' }}</el-descriptions-item>
            <!-- <el-descriptions-item label="完成进度">
              <el-progress :percentage="detailData.workOrder.completionStatus >= 100 ? 100 : (detailData.workOrder.completionStatus || 0)"
                           :color="customColors(detailData.workOrder.completionStatus)"
                           :status="detailData.workOrder.completionStatus >= 100 ? 'success' : ''"
                           style="width:500px;" />
            </el-descriptions-item> -->
            <el-descriptions-item label="完成进度"><span :style="{ color: customColors(detailData.workOrder.completionStatus) }">{{ detailData.workOrder.completionStatus || 0 }}%</span></el-descriptions-item>
          </el-descriptions>
        </div>
        <div class="detail-section">
          <h3 class="section-title">报工明细</h3>
          <el-table :data="[detailData]"
          <el-table :data="detailData.reports"
                    border
                    style="width: 100%">
            <el-table-column label="报工单号"
                             prop="productNo"
                             align="center" />
            <el-table-column label="产出数量"
                             prop="qualifiedQuantity"
                             align="center" />
            <el-table-column label="报废数量"
                             prop="unqualifiedQuantity"
            <el-table-column label="创建人"
                             prop="userName"
                             align="center" />
            <el-table-column label="创建时间"
                             prop="reportingTime"
                             align="center" />
                             align="center">
              <template #default="{ row }">
                {{ parseTime(row.createTime) }}
              </template>
            </el-table-column>
            <el-table-column label="操作"
                             align="center"
                             width="200">
@@ -205,25 +213,24 @@
             :key="record.id"
             class="quality-record-block">
          <div class="detail-section">
            <h3 class="section-title">检测记录 {{ index + 1 }} - {{ record.checkTime }}</h3>
            <h3 class="section-title">检测记录 {{ index + 1 }} - {{ parseTime(record.createTime) }}</h3>
            <el-descriptions :column="3"
                             border>
              <el-descriptions-item label="检测日期">{{ record.checkTime || '-' }}</el-descriptions-item>
              <el-descriptions-item label="生产工单号">{{ record.workOrderNo || '-' }}</el-descriptions-item>
              <el-descriptions-item label="工序">{{ record.process || '-' }}</el-descriptions-item>
              <el-descriptions-item label="检验员">{{ record.checkName || '-' }}</el-descriptions-item>
              <el-descriptions-item label="检测日期">{{ parseTime(record.createTime) || '-' }}</el-descriptions-item>
              <el-descriptions-item label="报工单号">{{ record.reportNo || '-' }}</el-descriptions-item>
              <el-descriptions-item label="检验员">{{ record.userName || '-' }}</el-descriptions-item>
              <el-descriptions-item label="产品名称">{{ record.productName || '-' }}</el-descriptions-item>
              <el-descriptions-item label="规格型号">{{ record.model || '-' }}</el-descriptions-item>
              <el-descriptions-item label="数量">{{ record.quantity || 0 }} {{ record.unit || '-' }}</el-descriptions-item>
              <el-descriptions-item label="检测单位">{{ record.checkCompany || '-' }}</el-descriptions-item>
              <el-descriptions-item label="检测结果">
                <el-tag :type="record.checkResult === '合格' ? 'success' : 'danger'">
                  {{ record.checkResult || '-' }}
                  {{ record.checkResult || '待检测' }}
                </el-tag>
              </el-descriptions-item>
            </el-descriptions>
            <h4 class="sub-section-title">检验指标列表</h4>
            <el-table :data="record.inspectItems"
            <el-table :data="record.inspectParamList"
                      border
                      style="width: 100%">
              <el-table-column label="序号"
@@ -231,7 +238,7 @@
                               width="60"
                               align="center" />
              <el-table-column label="指标"
                               prop="itemName"
                               prop="parameterItem"
                               align="center" />
              <el-table-column label="单位"
                               prop="unit"
@@ -243,13 +250,10 @@
                               prop="controlValue"
                               align="center" />
              <el-table-column label="实际值"
                               prop="actualValue"
                               prop="testValue"
                               align="center" />
            </el-table>
          </div>
          <!-- <div class="detail-section">
          </div> -->
          <el-divider v-if="index < qualityRecords.length - 1" />
        </div>
      </div>
@@ -266,7 +270,12 @@
  import { ref, reactive, onMounted } from "vue";
  import { useRoute, useRouter } from "vue-router";
  import { ElMessage } from "element-plus";
  import { parseTime } from "@/utils/ruoyi";
  import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
  import {
    getOrderDetail,
    productOrderListPage,
  } from "@/api/productionManagement/productionOrder";
  const route = useRoute();
  const router = useRouter();
@@ -277,47 +286,7 @@
  });
  const selectedNpsNo = ref(null);
  const npsNoLoading = ref(false);
  const npsNoOptions = ref([
    {
      id: 1,
      npsNo: "PO20240301001",
      productName: "精密液压缸",
      model: "HG-100/50-500",
      materialCode: "MAT-2024-001",
      quantity: 500,
      unit: "ä»¶",
      status: 1,
      customerName: "重工机械有限公司",
      startTime: "2024-03-01",
      completionStatus: 65,
    },
    {
      id: 2,
      npsNo: "PO20240301002",
      productName: "工业伺服电机",
      model: "SV-400W-3000",
      materialCode: "MAT-2024-002",
      quantity: 200,
      unit: "台",
      status: 2,
      customerName: "自动化设备科技公司",
      startTime: "2024-03-02",
      completionStatus: 100,
    },
    {
      id: 3,
      npsNo: "PO20240301003",
      productName: "高压密封圈",
      model: "SR-80-5",
      materialCode: "MAT-2024-003",
      quantity: 5000,
      unit: "个",
      status: 0,
      customerName: "密封系统配件厂",
      startTime: "2024-03-05",
      completionStatus: 0,
    },
  ]);
  const npsNoOptions = ref([]);
  // è¯¦æƒ…数据
  const rowData = reactive({
@@ -327,7 +296,10 @@
  // æŠ¥å·¥è¯¦æƒ…弹窗
  const detailDialogVisible = ref(false);
  const detailData = ref({});
  const detailData = ref({
    workOrder: {},
    reports: [],
  });
  // æŠ•入模态框
  const isShowInput = ref(false);
@@ -351,12 +323,12 @@
  // çŠ¶æ€å¤„ç†
  const getStatusType = status => {
    const typeMap = { 0: "info", 1: "primary", 2: "success" };
    const typeMap = { 1: "primary", 2: "warning", 3: "success", 5: "danger" };
    return typeMap[status] || "info";
  };
  const getStatusText = status => {
    const statusMap = { 0: "未开始", 1: "生产中", 2: "已完成" };
    return statusMap[status] || "未知";
    const statusMap = { 1: "待开始", 2: "进行中", 3: "已完成", 5: "已结束" };
    return statusMap[status] || "已取消";
  };
  const customColors = percentage => {
    if (percentage < 30) return "#f56c6c";
@@ -365,51 +337,41 @@
  };
  // æ¨¡æ‹Ÿæœç´¢æ–¹æ³•
  const handleNpsNoSearch = query => {
    if (query) {
      npsNoLoading.value = true;
      setTimeout(() => {
        npsNoLoading.value = false;
      }, 300);
  const handleNpsNoSearch = async query => {
    npsNoLoading.value = true;
    try {
      const res = await productOrderListPage({
        npsNo: query || "",
        pageNum: 1,
        pageSize: 50,
      });
      // å‚ç…§ productionOrder/index.vue çš„æ•°æ®ç»“æž„ res.data.records
      npsNoOptions.value = res.data?.records || res.rows || [];
    } catch (error) {
      console.error(error);
    } finally {
      npsNoLoading.value = false;
    }
  };
  const handleSearch = id => {
  const handleSearch = async id => {
    const selected = npsNoOptions.value.find(item => item.id === id);
    if (selected) {
      rowData.productionOrderDto = selected;
      rowData.productionRecords = [
        {
          id: 1001,
          productNo: "MO-2024-001-01",
          productName: selected.productName,
          model: selected.model,
          processName: "毛坯加工",
          requiredQuantity: selected.quantity,
          completedQuantity: Math.floor(selected.quantity * 0.4),
          qualifiedQuantity: Math.floor(selected.quantity * 0.4) - 2,
          unqualifiedQuantity: 2,
          reportingTime: "2024-03-01 10:00:00",
          schedule: "白班",
          postName: "张三",
          unit: selected.unit,
        },
        {
          id: 1002,
          productNo: "MO-2024-001-02",
          productName: selected.productName,
          model: selected.model,
          processName: "精加工",
          requiredQuantity: Math.floor(selected.quantity * 0.4),
          completedQuantity: Math.floor(selected.quantity * 0.25),
          qualifiedQuantity: Math.floor(selected.quantity * 0.25),
          unqualifiedQuantity: 0,
          reportingTime: "2024-03-01 16:00:00",
          schedule: "白班",
          postName: "李四",
          unit: selected.unit,
        },
      ];
      try {
        const res = await getOrderDetail(selected.npsNo);
        if (res.code === 200) {
          const { productionOrder, workOrderList } = res.data;
          rowData.productionOrderDto = productionOrder || selected;
          rowData.productionRecords = workOrderList || [];
        } else {
          rowData.productionOrderDto = selected;
          rowData.productionRecords = [];
        }
      } catch (error) {
        console.error(error);
        ElMessage.error("获取订单详情失败");
        rowData.productionOrderDto = selected;
      }
    }
  };
@@ -418,121 +380,34 @@
  };
  const handleClickStep = row => {
    // row æ˜¯ workOrderList ä¸­çš„一项,包含 workOrder, reportList, inspectList
    detailData.value = {
      id: row.id || Math.floor(Math.random() * 1000),
      productNo: row.productNo,
      npsNo: rowData.productionOrderDto.npsNo,
      schedule: row.schedule,
      postName: row.postName,
      materialCode: rowData.productionOrderDto.materialCode,
      productName: row.productName,
      model: row.model,
      qualifiedQuantity: row.qualifiedQuantity,
      unqualifiedQuantity: row.unqualifiedQuantity || 0,
      quantity: row.completedQuantity,
      unit: row.unit,
      reportingTime: row.reportingTime,
      productionOperationParamList: [
        { id: 1, paramName: "主轴转速", inputValue: "2400", unit: "rpm" },
        { id: 2, paramName: "进给速度", inputValue: "120", unit: "mm/min" },
        { id: 3, paramName: "切削深度", inputValue: "0.5", unit: "mm" },
        { id: 4, paramName: "冷却液压力", inputValue: "0.6", unit: "Mpa" },
      ],
      workOrder: row.workOrder || {},
      reports: (row.reportList || []).map(r => ({
        ...r.reportMain,
        productionOperationParamList: r.reportParamList || [],
      })),
    };
    detailDialogVisible.value = true;
  };
  const handleClickQuality = row => {
    qualityRecords.value = [
      {
        id: 2001,
        checkTime: "2024-03-01 11:30:00",
        workOrderNo: row.productNo,
        process: row.processName,
        checkName: "质量部-王建国",
        productName: row.productName,
        model: row.model,
        unit: row.unit,
        quantity: row.completedQuantity,
        checkCompany: "内部实验室",
        checkResult: "合格",
        inspectItems: [
          {
            id: 1,
            itemName: "外径尺寸",
            unit: "mm",
            standardValue: "100.00±0.05",
            controlValue: "100.00±0.03",
            actualValue: "100.01",
            result: "合格",
          },
          {
            id: 2,
            itemName: "内径尺寸",
            unit: "mm",
            standardValue: "50.00+0.02/-0",
            controlValue: "50.00+0.01/-0",
            actualValue: "50.01",
            result: "合格",
          },
          {
            id: 3,
            itemName: "表面粗糙度",
            unit: "Ra",
            standardValue: "≤1.6",
            controlValue: "≤1.2",
            actualValue: "0.8",
            result: "合格",
          },
        ],
      },
      {
        id: 2001,
        checkTime: "2024-03-01 11:30:00",
        workOrderNo: row.productNo,
        process: row.processName,
        checkName: "质量部-王建国",
        productName: row.productName,
        model: row.model,
        unit: row.unit,
        quantity: row.completedQuantity,
        checkCompany: "内部实验室",
        checkResult: "合格",
        inspectItems: [
          {
            id: 1,
            itemName: "外径尺寸",
            unit: "mm",
            standardValue: "100.00±0.05",
            controlValue: "100.00±0.03",
            actualValue: "100.01",
            result: "合格",
          },
          {
            id: 2,
            itemName: "内径尺寸",
            unit: "mm",
            standardValue: "50.00+0.02/-0",
            controlValue: "50.00+0.01/-0",
            actualValue: "50.01",
            result: "合格",
          },
          {
            id: 3,
            itemName: "表面粗糙度",
            unit: "Ra",
            standardValue: "≤1.6",
            controlValue: "≤1.2",
            actualValue: "0.8",
            result: "合格",
          },
        ],
      },
    ];
    // row æ˜¯ workOrderList ä¸­çš„一项
    const inspects = row.inspectList || [];
    qualityRecords.value = inspects.map(i => ({
      ...i.inspect,
      reportNo: i.reportNo,
      userName: i.reportMain?.userName || "-",
      inspectParamList: i.inspectParamList || [],
      inspectFileList: i.inspectFileList || [],
    }));
    qualityDialogVisible.value = true;
  };
  onMounted(() => {
  onMounted(async () => {
    // åˆå§‹åŠ è½½åˆ—è¡¨
    await handleNpsNoSearch();
    if (route.query.npsNo) {
      const npsNo = route.query.npsNo;
      const found = npsNoOptions.value.find(item => item.npsNo === npsNo);
@@ -540,23 +415,23 @@
        selectedNpsNo.value = found.id;
        handleSearch(found.id);
      } else {
        // å¦‚果没找到,创建一个临时的
        const mockItem = {
          id: Date.now(),
          npsNo: npsNo,
          productName: route.query.productName || "精密液压缸",
          model: route.query.model || "HG-100/50-500",
          materialCode: "MAT-2024-MOCK",
          quantity: 100,
          unit: "ä»¶",
          status: 1,
          customerName: "模拟客户",
          startTime: "2024-03-01",
          completionStatus: 50,
        };
        npsNoOptions.value.push(mockItem);
        selectedNpsNo.value = mockItem.id;
        handleSearch(mockItem.id);
        // å¦‚果列表中没有(可能是分页原因),则根据 npsNo å†æ¬¡ç²¾å‡†æœç´¢
        try {
          const res = await productOrderListPage({
            npsNo,
            pageNum: -1,
            pageSize: -1,
          });
          const records = res.data?.records || res.rows || [];
          if (records.length > 0) {
            const item = records[0];
            npsNoOptions.value.unshift(item);
            selectedNpsNo.value = item.id;
            handleSearch(item.id);
          }
        } catch (error) {
          console.error("获取路由参数对应的订单失败", error);
        }
      }
    }
  });
src/views/productionManagement/workOrderEdit/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div class="search-row">
        <div class="search-item">
          <span class="search_title">工单编号:</span>
@@ -13,7 +13,7 @@
        </div>
        <div class="search-item">
          <span class="search_title">生产订单号:</span>
          <el-input v-model="searchForm.productOrderNpsNo"
          <el-input v-model="searchForm.npsNo"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
@@ -259,7 +259,7 @@
  const data = reactive({
    searchForm: {
      workOrderNo: "",
      productOrderNpsNo: "",
      npsNo: "",
    },
  });
  const { searchForm } = toRefs(data);
src/views/productionManagement/workOrderManagement/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div class="search-row">
        <div class="search-item">
          <span class="search_title">工单编号:</span>
@@ -13,7 +13,7 @@
        </div>
        <div class="search-item">
          <span class="search_title">生产订单号:</span>
          <el-input v-model="searchForm.productOrderNpsNo"
          <el-input v-model="searchForm.npsNo"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
@@ -265,7 +265,9 @@
  import QRCode from "qrcode";
  import { getCurrentInstance, reactive, toRefs } from "vue";
  import MaterialDialog from "./components/MaterialDialog.vue";
  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
  const FileList = defineAsyncComponent(() =>
    import("@/components/Dialog/FileList.vue")
  );
  import useUserStore from "@/store/modules/user";
  const { proxy } = getCurrentInstance();
@@ -525,7 +527,7 @@
  const data = reactive({
    searchForm: {
      workOrderNo: "",
      productOrderNpsNo: "",
      npsNo: "",
    },
  });
  const { searchForm } = toRefs(data);
src/views/qualityManagement/finalInspection/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">产品名称:</span>
        <el-input
src/views/qualityManagement/nonconformingManagement/index.vue
@@ -1,24 +1,21 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div style="display: flex;flex-direction: row;align-items: center;">
        <div>
          <span class="search_title">类型:</span>
      <el-form :model="searchForm" inline style="margin-bottom: 0;">
        <el-form-item label="类型:">
          <el-select v-model="searchForm.inspectType" clearable style="width: 200px" @change="handleQuery">
            <el-option label="原材料检验" :value="0" />
            <el-option label="过程检验" :value="1" />
            <el-option label="出厂检验" :value="2" />
          </el-select>
        </div>
        <div style="margin-left: 10px">
          <span class="search_title">状态:</span>
        </el-form-item>
        <el-form-item label="状态:">
          <el-select v-model="searchForm.inspectState" clearable style="width: 200px" @change="handleQuery">
            <el-option label="待处理" :value="0" />
            <el-option label="已处理" :value="1" />
          </el-select>
        </div>
        <div style="margin-left: 10px">
          <span class="search_title">产品名称:</span>
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input
              v-model="searchForm.productName"
              style="width: 200px"
@@ -27,19 +24,22 @@
              clearable
              :prefix-icon="Search"
          />
        </div>
        <span  style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                 style="width: 300px"
                         placeholder="请选择" clearable @change="changeDaterange" />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        </el-form-item>
        <el-form-item label="检测日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                          style="width: 300px"
                          placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="mb20" style="text-align: right;">
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
src/views/qualityManagement/processInspection/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">工序:</span>
        <el-input
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">供应商:</span>
        <el-input
src/views/safeProduction/accidentReportingRecord/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">事故名称:</span>
        <el-input v-model="searchForm.accidentName"
src/views/safeProduction/emergencyPlanReview/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">应急预案名称:</span>
        <el-input v-model="searchForm.planName"
src/views/safeProduction/hazardSourceLedger/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">危险源名称:</span>
        <el-input v-model="searchForm.name"
src/views/safeProduction/hazardousMaterialsControl/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">危险源名称:</span>
        <el-input v-model="searchForm.name"
src/views/safeProduction/safeWorkApproval/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <!-- æ ‡ç­¾é¡µåˆ‡æ¢ä¸åŒçš„审批类型 -->
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">流程编号:</span>
        <el-input v-model="searchForm.approveId"
src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form" style="margin-bottom: 20px;">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input
vite.config.js
@@ -8,7 +8,7 @@
  const { VITE_APP_ENV } = env;
  const baseUrl =
      env.VITE_APP_ENV === "development"
          ? "http://localhost:7005"
          ? "http://1.15.17.182:9003"
          : env.VITE_BASE_API;
  const javaUrl =
      env.VITE_APP_ENV === "development"