fix: 不在销售台账处出库。在发货台账处导入excel,匹配客户进行出库。销售台账处的产品列表需要展示已发货/未发货数量。
已修改11个文件
9773 ■■■■■ 文件已修改
src/api/salesManagement/deliveryLedger.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/HomeTab.vue 948 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/index.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 2044 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 670 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionCosting/index.vue 692 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentLedger/index.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 5107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/deliveryLedger.js
@@ -27,3 +27,12 @@
    data: query,
  });
}
// å…¼å®¹æ—§é¡µé¢ï¼ˆé”€å”®å°è´¦ï¼‰å‘货接口
export function addShippingInfo(data) {
  return request({
    url: "/shippingInfo/add",
    method: "post",
    data,
  });
}
src/views/basicData/customerFile/index.vue
@@ -15,10 +15,10 @@
                   style="width: 240px"
                   clearable
                   @change="handleQuery">
          <el-option label="零售客户"
                     value="零售客户" />
          <el-option label="进销商客户"
                     value="进销商客户" />
          <el-option label="对公"
                     value="1" />
          <el-option label="对私"
                     value="2" />
        </el-select>
        <el-button type="primary"
                   @click="handleQuery"
@@ -125,10 +125,10 @@
              <el-select v-model="form.customerType"
                         placeholder="请选择"
                         clearable>
                <el-option label="零售客户"
                           value="零售客户" />
                <el-option label="进销商客户"
                           value="进销商客户" />
                <el-option label="对公"
                           :value="1" />
                <el-option label="对私"
                           :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
@@ -276,7 +276,8 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitReminderForm">确认</el-button>
          <el-button type="primary"
                     @click="submitReminderForm">确认</el-button>
          <el-button @click="closeReminderDialog">取消</el-button>
        </div>
      </template>
@@ -359,7 +360,8 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitNegotiationForm">确认</el-button>
          <el-button type="primary"
                     @click="submitNegotiationForm">确认</el-button>
          <el-button @click="closeNegotiationDialog">取消</el-button>
        </div>
      </template>
@@ -383,7 +385,7 @@
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">客户分类:</span>
                <span class="info-value">{{ detailForm.customerType }}</span>
                <span class="info-value">{{ detailForm.customerType==1?"对公":"对私" }}</span>
              </div>
            </el-col>
          </el-row>
@@ -713,6 +715,9 @@
      label: "客户分类",
      prop: "customerType",
      width: 120,
      formatData: params => {
        return params == 1 ? "对公" : "对私";
      },
    },
    {
      label: "客户名称",
@@ -786,13 +791,13 @@
            openForm("edit", row);
          },
        },
                {
                    name: "添加洽谈进度",
                    type: "text",
                    clickFun: row => {
                        openNegotiationDialog(row);
                    },
                },
        {
          name: "添加洽谈进度",
          type: "text",
          clickFun: row => {
            openNegotiationDialog(row);
          },
        },
        {
          name: "回访提醒",
          type: "text",
@@ -800,13 +805,13 @@
            openReminderDialog(row);
          },
        },
                {
                    name: "详情",
                    type: "text",
                    clickFun: row => {
                        openDetailDialog(row);
                    },
                },
        {
          name: "详情",
          type: "text",
          clickFun: row => {
            openDetailDialog(row);
          },
        },
      ],
    },
  ]);
@@ -854,20 +859,20 @@
    },
    rules: {
      customerName: [{ required: true, message: "请输入", trigger: "blur" }],
      taxpayerIdentificationNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
      companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      // taxpayerIdentificationNumber: [
      //   { required: true, message: "请输入", trigger: "blur" },
      // ],
      // companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
      // companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPerson: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      maintainer: [{ required: false, message: "请选择", trigger: "change" }],
      maintenanceTime: [
        { required: false, message: "请选择", trigger: "change" },
      ],
      basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankCode: [{ required: true, message: "请输入", trigger: "blur" }],
      // basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      // bankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      // bankCode: [{ required: true, message: "请输入", trigger: "blur" }],
      customerType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
src/views/basicData/supplierManage/components/HomeTab.vue
@@ -3,185 +3,172 @@
    <div class="search_form">
      <div>
        <span class="search_title">供应商档案:</span>
        <el-input
            v-model="searchForm.supplierName"
            style="width: 240px"
            placeholder="输入供应商名称搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
        <el-input v-model="searchForm.supplierName"
                  style="width: 240px"
                  placeholder="输入供应商名称搜索"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')"
        >新增供应商</el-button
        >
        <el-button type="primary"
                   @click="openForm('add')">新增供应商</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="info" plain icon="Upload" @click="handleImport"
        >导入</el-button
        >
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button type="info"
                   plain
                   icon="Upload"
                   @click="handleImport">导入</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
      ></PIMTable>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"></PIMTable>
    </div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增供应商信息' : '编辑供应商信息'"
        width="70%"
        @close="closeDia"
    >
      <el-form
          :model="form"
          label-width="140px"
          label-position="top"
          :rules="rules"
          ref="formRef"
      >
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增供应商信息' : '编辑供应商信息'"
               width="70%"
               @close="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierName">
              <el-input
                  v-model="form.supplierName"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="供应商名称:"
                          prop="supplierName">
              <el-input v-model="form.supplierName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
                label="纳税人识别号:"
                prop="taxpayerIdentificationNum"
            >
              <el-input
                  v-model="form.taxpayerIdentificationNum"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="纳税人识别号:"
                          prop="taxpayerIdentificationNum">
              <el-input v-model="form.taxpayerIdentificationNum"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公司地址:" prop="companyAddress">
              <el-input
                  v-model="form.companyAddress"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="公司地址:"
                          prop="companyAddress">
              <el-input v-model="form.companyAddress"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:" prop="companyPhone">
              <el-input
                  v-model="form.companyPhone"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="公司电话:"
                          prop="companyPhone">
              <el-input v-model="form.companyPhone"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行:" prop="bankAccountName">
              <el-input
                  v-model="form.bankAccountName"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="开户行:"
                          prop="bankAccountName">
              <el-input v-model="form.bankAccountName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="账号:" prop="bankAccountNum">
              <el-input
                  v-model="form.bankAccountNum"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="账号:"
                          prop="bankAccountNum">
              <el-input v-model="form.bankAccountNum"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="联系人:" prop="contactUserName">
              <el-input
                  v-model="form.contactUserName"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="联系人:"
                          prop="contactUserName">
              <el-input v-model="form.contactUserName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:" prop="contactUserPhone">
              <el-input
                  v-model="form.contactUserPhone"
                  placeholder="请输入"
                  clearable
              />
            <el-form-item label="联系电话:"
                          prop="contactUserPhone">
              <el-input v-model="form.contactUserPhone"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:" prop="maintainUserId">
              <el-select
                  v-model="form.maintainUserId"
                  placeholder="请选择"
                  clearable
                  disabled
              >
                <el-option
                    v-for="item in userList"
                    :key="item.nickName"
                    :label="item.nickName"
                    :value="item.userId"
                />
            <el-form-item label="维护人:"
                          prop="maintainUserId">
              <el-select v-model="form.maintainUserId"
                         placeholder="请选择"
                         clearable
                         disabled>
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="维护时间:" prop="maintainTime">
              <el-date-picker
                  style="width: 100%"
                  v-model="form.maintainTime"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  type="date"
                  placeholder="请选择"
                  clearable
              />
            <el-form-item label="维护时间:"
                          prop="maintainTime">
              <el-date-picker style="width: 100%"
                              v-model="form.maintainTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商类型:" prop="supplierType">
              <el-select v-model="form.supplierType" placeholder="请选择" clearable>
                <el-option label="甲" value="甲" />
                <el-option label="乙" value="乙" />
                <el-option label="丙" value="丙" />
                <el-option label="丁" value="丁" />
            <el-form-item label="供应商类型:"
                          prop="supplierType">
              <el-select v-model="form.supplierType"
                         placeholder="请选择"
                         clearable>
                <el-option label="对公"
                           :value="1" />
                <el-option label="对私"
                           :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="是否白名单:" prop="isWhite">
              <el-select v-model="form.isWhite" placeholder="请选择" clearable>
                <el-option label="是" :value="0" />
                <el-option label="否" :value="1" />
            <el-form-item label="是否白名单:"
                          prop="isWhite">
              <el-select v-model="form.isWhite"
                         placeholder="请选择"
                         clearable>
                <el-option label="是"
                           :value="0" />
                <el-option label="否"
                           :value="1" />
              </el-select>
            </el-form-item>
          </el-col>
@@ -189,50 +176,44 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- ä¾›åº”商导入对话框 -->
    <el-dialog
        :title="upload.title"
        v-model="upload.open"
        width="400px"
        append-to-body
    >
      <el-upload
          ref="uploadRef"
          :limit="1"
          accept=".xlsx, .xls"
          :headers="upload.headers"
          :action="upload.url + '?updateSupport=' + upload.updateSupport"
          :disabled="upload.isUploading"
          :on-progress="handleFileUploadProgress"
          :on-success="handleFileSuccess"
          :on-error="handleFileError"
          :auto-upload="false"
          drag
      >
    <el-dialog :title="upload.title"
               v-model="upload.open"
               width="400px"
               append-to-body>
      <el-upload ref="uploadRef"
                 :limit="1"
                 accept=".xlsx, .xls"
                 :headers="upload.headers"
                 :action="upload.url + '?updateSupport=' + upload.updateSupport"
                 :disabled="upload.isUploading"
                 :on-progress="handleFileUploadProgress"
                 :on-success="handleFileSuccess"
                 :on-error="handleFileError"
                 :auto-upload="false"
                 drag>
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip text-center">
            <span>仅允许导入xls、xlsx格式文件。</span>
            <el-link
                type="primary"
                :underline="false"
                style="font-size: 12px; vertical-align: baseline"
                @click="importTemplate"
            >下载模板</el-link
            >
            <el-link type="primary"
                     :underline="false"
                     style="font-size: 12px; vertical-align: baseline"
                     @click="importTemplate">下载模板</el-link>
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
          <el-button type="primary"
                     @click="submitFileForm">ç¡® å®š</el-button>
          <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
        </div>
      </template>
@@ -242,349 +223,364 @@
</template>
<script setup>
import { onMounted, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import { delSupplier } from "@/api/basicData/supplierManageFile.js";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import {
  addSupplier,
  getSupplier,
  listSupplier,
  updateSupplier,
} from "@/api/basicData/supplierManageFile.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
import FilesDia from "../filesDia.vue";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
  import { onMounted, ref } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import { delSupplier } from "@/api/basicData/supplierManageFile.js";
  import { ElMessageBox } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import {
    addSupplier,
    getSupplier,
    listSupplier,
    updateSupplier,
  } from "@/api/basicData/supplierManageFile.js";
  import useUserStore from "@/store/modules/user";
  import { getToken } from "@/utils/auth.js";
  import FilesDia from "../filesDia.vue";
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
const tableColumn = ref([
  {
    label: "供应商名称",
    prop: "supplierName",
    width: 250,
  },
  {
    label: "供应商类型",
    prop: "supplierType",
    width: 120,
  },
  {
    label: "纳税人识别号",
    prop: "taxpayerIdentificationNum",
    width: 230,
  },
  {
    label: "公司地址",
    prop: "companyAddress",
    width: 220,
  },
  {
    label: "联系方式",
    prop: "companyPhone",
    width:150
  },
  {
    label: "开户行",
    prop: "bankAccountName",
    width: 220,
  },
  {
    label: "账号",
    prop: "bankAccountNum",
    width: 220,
  },
  {
    label: "联系人",
    prop: "contactUserName",
  },
  {
    label: "联系电话",
    prop: "contactUserPhone",
    width: 150,
  },
  {
    label: "维护人",
    prop: "maintainUserName",
  },
  {
    label: "维护时间",
    prop: "maintainTime",
    width:100
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    width: 150,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
  const tableColumn = ref([
    {
      label: "供应商名称",
      prop: "supplierName",
      width: 250,
    },
    {
      label: "供应商类型",
      prop: "supplierType",
      width: 120,
      formatData: params => {
        return params == 1 ? "对公" : "对私";
      },
      {
        //资质附件
        name: "资质文件",
        type: "text",
        clickFun: (row) => {
          openFilesFormDia(row)
    },
    {
      label: "纳税人识别号",
      prop: "taxpayerIdentificationNum",
      width: 230,
    },
    {
      label: "公司地址",
      prop: "companyAddress",
      width: 220,
    },
    {
      label: "联系方式",
      prop: "companyPhone",
      width: 150,
    },
    {
      label: "开户行",
      prop: "bankAccountName",
      width: 220,
    },
    {
      label: "账号",
      prop: "bankAccountNum",
      width: 220,
    },
    {
      label: "联系人",
      prop: "contactUserName",
    },
    {
      label: "联系电话",
      prop: "contactUserPhone",
      width: 150,
    },
    {
      label: "维护人",
      prop: "maintainUserName",
    },
    {
      label: "维护时间",
      prop: "maintainTime",
      width: 100,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 150,
      operation: [
        {
          name: "编辑",
          type: "text",
          clickFun: row => {
            openForm("edit", row);
          },
        },
        {
          //资质附件
          name: "资质文件",
          type: "text",
          clickFun: row => {
            openFilesFormDia(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const selectedRows = ref([]);
  const userList = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const filesDia = ref();
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      supplierName: "",
    },
    form: {
      supplierName: "",
      taxpayerIdentificationNum: "",
      companyAddress: "",
      companyPhone: "",
      bankAccountName: "",
      bankAccountNum: "",
      contactUserName: "",
      contactUserPhone: "",
      maintainUserId: "",
      maintainTime: "",
      supplierType: "",
      isWhite: "",
    },
    rules: {
      supplierName: [{ required: true, message: "请输入", trigger: "blur" }],
      // taxpayerIdentificationNum: [
      //   { required: true, message: "请输入", trigger: "blur" },
      // ],
      // companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
      // companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      // bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }],
      // bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }],
      contactUserName: [{ required: false, message: "请输入", trigger: "blur" }],
      contactUserPhone: [{ required: false, message: "请输入", trigger: "blur" }],
      maintainUserId: [{ required: false, message: "请选择", trigger: "change" }],
      maintainTime: [{ required: false, message: "请选择", trigger: "change" }],
      supplierType: [
        { required: true, message: "请选择供应商类型", trigger: "change" },
      ],
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  /** æäº¤ä¸Šä¼ æ–‡ä»¶ */
  function submitFileForm() {
    upload.isUploading = true;
    proxy.$refs["uploadRef"].submit();
  }
  const getList = () => {
    tableLoading.value = true;
    listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then(res => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      page.total = res.data.total;
    });
  };
  const upload = reactive({
    // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(供应商导入)
    open: false,
    // å¼¹å‡ºå±‚标题(供应商导入)
    title: "",
    // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
    isUploading: false,
    // æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
    updateSupport: 1,
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
  });
  /** å¯¼å…¥æŒ‰é’®æ“ä½œ */
  function handleImport() {
    upload.title = "供应商导入";
    upload.open = true;
  }
  /** ä¸‹è½½æ¨¡æ¿ */
  function importTemplate() {
    proxy.download(
      "/system/supplier/downloadTemplate",
      {},
      "供应商导入模板.xlsx"
    );
  }
  /**文件上传中处理 */
  const handleFileUploadProgress = (event, file, fileList) => {
    upload.isUploading = true;
  };
  /** æ–‡ä»¶ä¸Šä¼ æˆåŠŸå¤„ç† */
  const handleFileSuccess = (response, file, fileList) => {
    upload.isUploading = false;
    if (response.code === 200) {
      proxy.$modal.msgSuccess("文件上传成功");
      upload.open = false;
      proxy.$refs["uploadRef"].clearFiles();
      getList();
    } else if (response.code === 500) {
      proxy.$modal.msgError(response.msg);
    } else {
      proxy.$modal.msgWarning(response.msg);
    }
  };
  /** æ–‡ä»¶ä¸Šä¼ å¤±è´¥å¤„理 */
  const handleFileError = (error, file, fileList) => {
    upload.isUploading = false;
    proxy.$modal.msgError("文件上传失败");
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  // æ‰“开弹框
  const openForm = (type, row) => {
    operationType.value = type;
    form.value = {};
    form.value.maintainUserId = userStore.id;
    form.value.maintainTime = getCurrentDate();
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    if (type === "edit") {
      getSupplier(row.id).then(res => {
        form.value = { ...res.data };
      });
    }
    dialogFormVisible.value = true;
  };
  // æäº¤è¡¨å•
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitEdit();
        } else {
          submitAdd();
        }
      }
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0,
});
const filesDia = ref()
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    supplierName: "",
  },
  form: {
    supplierName: "",
    taxpayerIdentificationNum: "",
    companyAddress: "",
    companyPhone: "",
    bankAccountName: "",
    bankAccountNum: "",
    contactUserName: "",
    contactUserPhone: "",
    maintainUserId: "",
    maintainTime: "",
    supplierType: "",
    isWhite: "",
  },
  rules: {
    supplierName: [{ required: true, message: "请输入", trigger: "blur" }],
    taxpayerIdentificationNum: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
    companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }],
    contactUserName: [{ required: false, message: "请输入", trigger: "blur" }],
    contactUserPhone: [{ required: false, message: "请输入", trigger: "blur" }],
    maintainUserId: [{ required: false, message: "请选择", trigger: "change" }],
    maintainTime: [{ required: false, message: "请选择", trigger: "change" }],
    supplierType: [{ required: true, message: "请选择供应商类型", trigger: "change" }],
  },
});
const { searchForm, form, rules } = toRefs(data);
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
function submitFileForm() {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
}
const getList = () => {
  tableLoading.value = true;
  listSupplier({ ...searchForm.value, ...page, isWhite: 0 }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.data.records;
    page.total = res.data.total;
  });
};
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(供应商导入)
  open: false,
  // å¼¹å‡ºå±‚标题(供应商导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
  updateSupport: 1,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
});
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
function handleImport() {
  upload.title = "供应商导入";
  upload.open = true;
}
/** ä¸‹è½½æ¨¡æ¿ */
function importTemplate() {
  proxy.download("/system/supplier/downloadTemplate", {}, "供应商导入模板.xlsx");
}
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
  upload.isUploading = true;
};
/** æ–‡ä»¶ä¸Šä¼ æˆåŠŸå¤„ç† */
const handleFileSuccess = (response, file, fileList) => {
  upload.isUploading = false;
  if(response.code === 200){
    proxy.$modal.msgSuccess("文件上传成功");
    upload.open = false;
    proxy.$refs["uploadRef"].clearFiles();
    getList();
  }else if(response.code === 500){
    proxy.$modal.msgError(response.msg);
  }else{
    proxy.$modal.msgWarning(response.msg);
  }
};
/** æ–‡ä»¶ä¸Šä¼ å¤±è´¥å¤„理 */
const handleFileError = (error, file, fileList) => {
  upload.isUploading = false;
  proxy.$modal.msgError("文件上传失败");
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开弹框
const openForm = (type, row) => {
  operationType.value = type;
  form.value = {};
  form.value.maintainUserId = userStore.id;
  form.value.maintainTime = getCurrentDate();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  if (type === "edit") {
    getSupplier(row.id).then((res) => {
      form.value = { ...res.data };
    });
  }
  dialogFormVisible.value = true;
};
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitEdit();
      } else {
        submitAdd();
      }
    }
  });
};
// æäº¤æ–°å¢ž
const submitAdd = () => {
  addSupplier(form.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// æäº¤ä¿®æ”¹
const submitEdit = () => {
  updateSupplier(form.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
  };
  // æäº¤æ–°å¢ž
  const submitAdd = () => {
    addSupplier(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    });
  };
  // æäº¤ä¿®æ”¹
  const submitEdit = () => {
    updateSupplier(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/system/supplier/export", { isWhite: 0 }, "供应商档案.xlsx");
        proxy.download(
          "/system/supplier/export",
          { isWhite: 0 },
          "供应商档案.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
    const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName);
    if (unauthorizedData.length > 0) {
      proxy.$modal.msgWarning("不可删除他人维护的数据");
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
      const unauthorizedData = selectedRows.value.filter(
        item => item.maintainUserName !== userStore.nickName
      );
      if (unauthorizedData.length > 0) {
        proxy.$modal.msgWarning("不可删除他人维护的数据");
        return;
      }
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        tableLoading.value = true;
        delSupplier(ids)
            .then((res) => {
              proxy.$modal.msgSuccess("删除成功");
              getList();
            })
            .finally(() => {
              tableLoading.value = false;
            });
          .then(res => {
            proxy.$modal.msgSuccess("删除成功");
            getList();
          })
          .finally(() => {
            tableLoading.value = false;
          });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
  };
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// æ‰“开附件弹框
const openFilesFormDia = (row) => {
  nextTick(() => {
    filesDia.value?.openDialog(row)
  })
};
  // èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
  function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }
  // æ‰“开附件弹框
  const openFilesFormDia = row => {
    nextTick(() => {
      filesDia.value?.openDialog(row);
    });
  };
onMounted(() => {
  getList();
});
  onMounted(() => {
    getList();
  });
defineExpose({
  getList,
});
  defineExpose({
    getList,
  });
</script>
src/views/basicData/supplierManage/index.vue
@@ -1,11 +1,14 @@
<!-- åœ¨ä½ çš„主页面中 -->
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane label="正常供应商" name="home">
    <el-tabs v-model="activeTab"
             @tab-change="handleTabChange">
      <el-tab-pane label="正常供应商"
                   name="home">
        <HomeTab ref="homeTab" />
      </el-tab-pane>
      <el-tab-pane label="黑名单" name="blacklist">
      <el-tab-pane label="黑名单"
                   name="blacklist">
        <BlacklistTab ref="blacklistTab" />
      </el-tab-pane>
    </el-tabs>
@@ -13,31 +16,35 @@
</template>
<script>
import HomeTab from './components/HomeTab.vue'
import BlacklistTab from './components/BlacklistTab.vue'
  import HomeTab from "./components/HomeTab.vue";
  import BlacklistTab from "./components/BlacklistTab.vue";
export default {
  name: 'MainPage',
  components: {
    HomeTab,
    BlacklistTab
  },
  data() {
    return {
      activeTab: 'home'
    }
  },
  methods: {
    handleTabChange(tabName) {
      this.activeTab = tabName
      this.$nextTick(() => {
        if (tabName === 'home') {
          this.$refs.homeTab && this.$refs.homeTab.getList && this.$refs.homeTab.getList()
        } else if (tabName === 'blacklist') {
          this.$refs.blacklistTab && this.$refs.blacklistTab.getList && this.$refs.blacklistTab.getList()
        }
      })
  export default {
    name: "MainPage",
    components: {
      HomeTab,
      BlacklistTab,
    },
  }
}
    data() {
      return {
        activeTab: "home",
      };
    },
    methods: {
      handleTabChange(tabName) {
        this.activeTab = tabName;
        this.$nextTick(() => {
          if (tabName === "home") {
            this.$refs.homeTab &&
              this.$refs.homeTab.getList &&
              this.$refs.homeTab.getList();
          } else if (tabName === "blacklist") {
            this.$refs.blacklistTab &&
              this.$refs.blacklistTab.getList &&
              this.$refs.blacklistTab.getList();
          }
        });
      },
    },
  };
</script>
src/views/index.vue
@@ -9,14 +9,15 @@
          <div class="welcome-banner">
            <div class="welcome-title">
              <span class="welcome-user">{{ userStore.roleName || '系统管理员' }}</span>
              <span> æ‚¨å¥½ï¼ç¥æ‚¨å¼€å¿ƒæ¯ä¸€å¤©</span>
              <span> æ‚¨å¥½ï¼ï¼ç¥æ‚¨å¼€å¿ƒæ¯ä¸€å¤©</span>
            </div>
            <div class="welcome-time">登录于: {{ userStore.currentLoginTime }}</div>
          </div>
          <!-- ç”¨æˆ·ä¿¡æ¯å¡ç‰‡ -->
          <div class="user-card">
            <img :src="userStore.avatar" class="avatar" alt="" />
            <img :src="userStore.avatar"
                 class="avatar"
                 alt="" />
            <div class="user-card-main">
              <div class="user-name">{{ userStore.name }}</div>
              <div class="user-role">{{ userStore.roleName }}</div>
@@ -44,7 +45,6 @@
              <div class="data-value">{{ businessInfo.monthSaleHaveMoney }}</div>
            </div>
          </div>
        </div>
        <div class="data-card purchase">
          <div class="data-title">采购数据</div>
@@ -76,8 +76,10 @@
      <!-- å³ï¼šå¾…办事项 -->
      <div class="todo-panel">
        <div class="section-title">待办事项</div>
        <ul class="todo-list" v-if="todoList.length > 0">
          <li v-for="item in todoList" :key="item.id">
        <ul class="todo-list"
            v-if="todoList.length > 0">
          <li v-for="item in todoList"
              :key="item.id">
            <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px">
              <div style="display: flex;justify-content: space-between;align-items: center;">
                <div class="todo-title">待办编号:{{ item.approveId }}</div>
@@ -88,7 +90,8 @@
            </div>
          </li>
        </ul>
        <div v-else style="text-align: center">
        <div v-else
             style="text-align: center">
          æš‚无数据
        </div>
      </div>
@@ -98,23 +101,36 @@
        <div class="process-panel__header">
          <div class="section-title">工序数据生产统计明细</div>
          <div style="display: flex; gap: 10px; align-items: center;">
            <el-button type="primary" size="small" plain icon="Filter" @click="openProcessDialog">选择工序</el-button>
            <el-button type="info" size="small" plain icon="Refresh" @click="resetProcessFilter">重置</el-button>
            <el-radio-group v-model="processRange" size="small" @change="refreshProcessStats">
            <el-button type="primary"
                       size="small"
                       plain
                       icon="Filter"
                       @click="openProcessDialog">选择工序</el-button>
            <el-button type="info"
                       size="small"
                       plain
                       icon="Refresh"
                       @click="resetProcessFilter">重置</el-button>
            <el-radio-group v-model="processRange"
                            size="small"
                            @change="refreshProcessStats">
              <el-radio-button :value="1">日</el-radio-button>
              <el-radio-button :value="2">周</el-radio-button>
              <el-radio-button :value="3">月</el-radio-button>
            </el-radio-group>
          </div>
        </div>
        <div class="process-panel__body">
          <div class="process-panel__chart">
            <Echarts :chartStyle="{ width: '100%', height: '100%' }" :grid="processGrid" :series="processSeries"
              :tooltip="processTooltip" :xAxis="processXAxis" :yAxis="processYAxis" style="height: 100%"
              @click="handleChartClick" />
            <Echarts :chartStyle="{ width: '100%', height: '100%' }"
                     :grid="processGrid"
                     :series="processSeries"
                     :tooltip="processTooltip"
                     :xAxis="processXAxis"
                     :yAxis="processYAxis"
                     style="height: 100%"
                     @click="handleChartClick" />
          </div>
          <div class="process-panel__aside">
            <div class="process-legend">
              <div class="process-legend__item">
@@ -127,9 +143,7 @@
                <span class="dot dot-teal"></span><span>产出量</span>
              </div>
            </div>
            <div class="process-card process-card--name">{{ processAside.processName }}</div>
            <div class="process-card">
              <div class="process-card__label">累计总投入</div>
              <div class="process-card__value">{{ formatAmount(processAside.totalInput) }}
@@ -149,13 +163,18 @@
        </div>
      </div>
    </div>
    <!-- å·¥åºé€‰æ‹©å¼¹çª— -->
    <el-dialog v-model="processDialogVisible" title="选择工序" width="500px" append-to-body>
    <el-dialog v-model="processDialogVisible"
               title="选择工序"
               width="500px"
               append-to-body>
      <div class="process-selection-wrapper">
        <el-checkbox-group v-model="tempProcessIds">
          <div class="process-grid">
            <el-checkbox v-for="item in processOptions" :key="item.id" :label="item.id" border>
            <el-checkbox v-for="item in processOptions"
                         :key="item.id"
                         :label="item.id"
                         border>
              {{ item.name }}
            </el-checkbox>
          </div>
@@ -164,7 +183,8 @@
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="processDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleProcessDialogConfirm">确认</el-button>
          <el-button type="primary"
                     @click="handleProcessDialogConfirm">确认</el-button>
        </span>
      </template>
    </el-dialog>
@@ -174,7 +194,9 @@
        <div class="section-title">客户合同金额分析</div>
        <div class="contract-summary">
          <div class="contract-info">
            <img src="../assets/images/khtitle.png" alt="" style="width: 42px" />
            <img src="../assets/images/khtitle.png"
                 alt=""
                 style="width: 42px" />
            <div class="contract-card">
              <div class="contract-name">总合同金额(元)</div>
              <div class="contract-meta">
@@ -184,16 +206,20 @@
            </div>
          </div>
        </div>
        <div
          style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px">
        <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px">
          <div>
            <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie" :series="materialPieSeries"
              :tooltip="pieTooltip"></Echarts>
            <Echarts ref="chart"
                     :legend="pieLegend"
                     :chartStyle="chartStylePie"
                     :series="materialPieSeries"
                     :tooltip="pieTooltip"></Echarts>
          </div>
          <ul class="contract-list">
            <li v-for="item in materialPieSeries[0].data" :key="item.name">
            <li v-for="item in materialPieSeries[0].data"
                :key="item.name">
              <div style="display: flex;align-items: center;justify-content: space-between;width: 100%">
                <div class="line" :style="{ color: item.itemStyle.color }">●{{ item.name }}</div>
                <div class="line"
                     :style="{ color: item.itemStyle.color }">●{{ item.name }}</div>
                <div style="width: 70px">{{ item.rate }}%</div>
                <div>ï¿¥{{ item.value }}</div>
              </div>
@@ -210,17 +236,26 @@
          <!--                        <el-radio-button label="按季度" :value="3" />-->
          <!--                    </el-radio-group>-->
        </div>
        <Echarts ref="chart" :color="barColors2" :chartStyle="chartStyle" :grid="grid" :series="barSeries"
          :tooltip="tooltip" :xAxis="xAxis" :yAxis="yAxis" style="height: 260px"></Echarts>
        <Echarts ref="chart"
                 :color="barColors2"
                 :chartStyle="chartStyle"
                 :grid="grid"
                 :series="barSeries"
                 :tooltip="tooltip"
                 :xAxis="xAxis"
                 :yAxis="yAxis"
                 style="height: 260px"></Echarts>
      </div>
    </div>
    <!-- åº•部横向两栏 -->
    <div class="dashboard-row">
      <div class="main-panel">
        <div style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 10px;">
          <div class="section-title" style="margin-bottom: 0;">质量统计</div>
          <el-radio-group v-model="qualityRange" size="small" @change="qualityStatisticsInfo">
          <div class="section-title"
               style="margin-bottom: 0;">质量统计</div>
          <el-radio-group v-model="qualityRange"
                          size="small"
                          @change="qualityStatisticsInfo">
            <el-radio-button :value="1">周</el-radio-button>
            <el-radio-button :value="2">月</el-radio-button>
            <el-radio-button :value="3">季度</el-radio-button>
@@ -231,1033 +266,1078 @@
          <div class="quality-card two">过程检验数量 <span>{{ qualityStatisticsObject.processNum }}ä»¶</span></div>
          <div class="quality-card three">出厂已检数量 <span>{{ qualityStatisticsObject.factoryNum }}ä»¶</span></div>
        </div>
        <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="barLegend" :series="barSeries1"
          :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" style="height: 260px"></Echarts>
        <Echarts ref="chart"
                 :chartStyle="chartStyle"
                 :grid="grid"
                 :legend="barLegend"
                 :series="barSeries1"
                 :tooltip="tooltip"
                 :xAxis="xAxis1"
                 :yAxis="yAxis1"
                 style="height: 260px"></Echarts>
      </div>
      <div class="main-panel">
        <div class="section-title">回款与开票分析</div>
        <Echarts ref="invoiceChart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"
          :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;" />
        <Echarts ref="invoiceChart"
                 :chartStyle="chartStyle"
                 :grid="grid"
                 :legend="lineLegend"
                 :series="lineSeries"
                 :tooltip="tooltipLine"
                 :xAxis="xAxis2"
                 :yAxis="yAxis2"
                 style="height: 270px;" />
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted, computed, reactive } from 'vue'
import Echarts from "@/components/Echarts/echarts.vue";
import * as echarts from 'echarts';
import useUserStore from "@/store/modules/user.js";
import {
  analysisCustomerContractAmounts, getAmountHalfYear,
  getBusiness,
  homeTodos,
  processDataProductionStatistics,
  statisticsReceivablePayable,
  qualityInspectionStatistics
} from "@/api/viewIndex.js";
import { list } from '@/api/productionManagement/productionProcess';
  import { ref, onMounted, computed, reactive } from "vue";
  import Echarts from "@/components/Echarts/echarts.vue";
  import * as echarts from "echarts";
  import useUserStore from "@/store/modules/user.js";
  import {
    analysisCustomerContractAmounts,
    getAmountHalfYear,
    getBusiness,
    homeTodos,
    processDataProductionStatistics,
    statisticsReceivablePayable,
    qualityInspectionStatistics,
  } from "@/api/viewIndex.js";
  import { list } from "@/api/productionManagement/productionProcess";
  const userStore = useUserStore();
const userStore = useUserStore()
  const processOptions = ref([]);
  const selectedProcessIds = ref([]);
  const tempProcessIds = ref([]);
  const processDialogVisible = ref(false);
  const activeProcessIndex = ref(0);
const processOptions = ref([])
const selectedProcessIds = ref([])
const tempProcessIds = ref([])
const processDialogVisible = ref(false)
const activeProcessIndex = ref(0)
  const businessInfo = ref({
    inventoryNum: 0,
    monthPurchaseHaveMoney: 0,
    monthPurchaseMoney: 0,
    monthSaleHaveMoney: 0,
    monthSaleMoney: 0,
    todayInventoryNum: 0,
  });
  const qualityStatisticsObject = ref({
    supplierNum: 0,
    processNum: 0,
    factoryNum: 0,
  });
  const sum = ref(0);
  const yny = ref(0);
  const chain = ref(0);
const businessInfo = ref({
  inventoryNum: 0,
  monthPurchaseHaveMoney: 0,
  monthPurchaseMoney: 0,
  monthSaleHaveMoney: 0,
  monthSaleMoney: 0,
  todayInventoryNum: 0,
})
const qualityStatisticsObject = ref({
  supplierNum: 0,
  processNum: 0,
  factoryNum: 0,
})
const sum = ref(0)
const yny = ref(0)
const chain = ref(0)
const pieLegend = reactive({
  show: false,
})
const barSeries = ref([
  {
    type: 'bar',
    data: [],
    label: {
      show: true,
    }
  },
])
const barSeries1 = ref([
  {
    name: '原材料不合格数',
    type: 'bar',
    barGap: 0,
    emphasis: {
      focus: 'series'
    },
    data: []
  },
  {
    name: '过程不合格数',
    type: 'bar',
    emphasis: {
      focus: 'series'
    },
    data: []
  },
  {
    name: '出厂不合格数',
    type: 'bar',
    emphasis: {
      focus: 'series'
    },
    data: []
  },
])
const chartStyle = {
  width: '100%',
  height: '100%' // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
}
const chartStylePie = {
  width: '140%',
  height: '140%' // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
}
const grid = {
  left: '3%',
  right: '4%',
  bottom: '3%',
  containLabel: true
}
const barLegend = {
  show: true,
  data: ['原材料不合格数', '过程不合格数', '出厂不合格数']
}
const barLegend1 = {
  show: true,
  data: ['预付账款', '应付账款', '预收账款', '应收账款']
}
const lineLegend = {
  show: true,
  data: ['开票', '回款']
}
const tooltip = {
  trigger: 'axis',
  axisPointer: {
    type: 'shadow'
  }
}
const xAxis = [{
  type: 'value',
}]
const xAxis1 = ref([{
  type: 'category',
  axisTick: { show: false },
  data: []
}])
const yAxis = [{
  type: 'category',
  data: ['应付账款', '应收账款',]
}]
const yAxis1 = [{
  type: 'value'
}]
const pieTooltip = reactive({
  trigger: 'item',
  formatter: function (params) {
    // åŠ¨æ€ç”Ÿæˆæç¤ºä¿¡æ¯ï¼ŒåŸºäºŽæ•°æ®é¡¹çš„ name å±žæ€§
    const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额';
    return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
  },
  position: 'right'
})
const materialPieSeries = ref([
  {
    type: 'pie',
    radius: ['66%', '90%'],
    avoidLabelOverlap: false,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: false
    },
    data: []
  }
])
const lineSeries = ref([
  {
    type: 'line',
    data: [],
    label: {
      show: true
    },
    showSymbol: true, // æ˜¾ç¤ºåœ†ç‚¹
  },
])
const tooltipLine = {
  trigger: 'axis',
}
const yAxis2 = ref([
  {
    type: 'value',
  }
])
const xAxis2 = ref([
  {
    type: 'category',
    data: [],
    axisLabel: {
      interval: 0,
      formatter: function (value) {
        return value.replace(/~/g, '\n');
      },
    }
  }
])
// å¾…办事项
const todoList = ref([])
const radio1 = ref(1)
const qualityRange = ref(1)
// å›¾è¡¨å¼•用
const barChart = ref(null)
const lineChart = ref(null)
const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8']
// éšæœºé¢œè‰²ç”Ÿæˆå‡½æ•°
const getRandomColor = () => {
  return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0');
}
onMounted(() => {
  getBusinessData()
  analysisCustomer()
  todoInfoS()
  statisticsReceivable()
  qualityStatisticsInfo()
  getAmountHalfYearNum()
  getProcessList()
})
// æ•°æ®ç»Ÿè®¡
const getBusinessData = () => {
  getBusiness().then((res) => {
    businessInfo.value = { ...res.data }
  })
}
// åˆåŒé‡‘额
const analysisCustomer = () => {
  analysisCustomerContractAmounts().then((res) => {
    sum.value = res.data.sum
    yny.value = res.data.yny
    chain.value = res.data.chain
    // ä¸ºæ¯ä¸ªæ•°æ®é¡¹åˆ†é…éšæœºé¢œè‰²
    materialPieSeries.value[0].data = res.data.item.map(item => ({
      ...item,
      itemStyle: { color: getRandomColor() }
    }))
  })
}
// å¾…办事项
const todoInfoS = () => {
  homeTodos().then((res) => {
    todoList.value = res.data
  })
}
// èŽ·å–å·¥åºåˆ—è¡¨
const getProcessList = () => {
  list().then(res => {
    processOptions.value = res.data
  })
}
const openProcessDialog = () => {
  tempProcessIds.value = [...selectedProcessIds.value]
  processDialogVisible.value = true
}
const handleProcessDialogConfirm = () => {
  selectedProcessIds.value = [...tempProcessIds.value]
  processDialogVisible.value = false
  refreshProcessStats()
}
const resetProcessFilter = () => {
  selectedProcessIds.value = []
  tempProcessIds.value = []
  refreshProcessStats()
}
const handleChartClick = (params) => {
  if (params && params.dataIndex !== undefined) {
    activeProcessIndex.value = params.dataIndex
  }
}
// åº”付应收统计
const statisticsReceivable = () => {
  statisticsReceivablePayable({ type: radio1.value }).then((res) => {
    barSeries.value[0].data = [
      // { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } },
      { value: res.data.payableMoney, itemStyle: { color: barColors2[0] } },
      // { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } },
      { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } }
    ]
  })
}
// è´¨æ£€ç»Ÿè®¡
const qualityStatisticsInfo = () => {
  qualityInspectionStatistics({ type: qualityRange.value }).then((res) => {
    xAxis1.value[0].data = []
    barSeries1.value[0].data = []
    barSeries1.value[1].data = []
    barSeries1.value[2].data = []
    res.data.item.forEach(item => {
      xAxis1.value[0].data.push(item.date)
      barSeries1.value[0].data.push(item.supplierNum)
      barSeries1.value[1].data.push(item.processNum)
      barSeries1.value[2].data.push(item.factoryNum)
    })
    qualityStatisticsObject.value.supplierNum = res.data.supplierNum
    qualityStatisticsObject.value.processNum = res.data.processNum
    qualityStatisticsObject.value.factoryNum = res.data.factoryNum
  })
}
const getAmountHalfYearNum = async () => {
  const res = await getAmountHalfYear()
  console.log(res)
  const monthName = []
  const receiptAmount = []
  const invoiceAmount = []
  res.data.forEach(item => {
    monthName.push(item.month)
    receiptAmount.push(item.receiptAmount)
    invoiceAmount.push(item.invoiceAmount)
  })
  // æ­£ç¡®å“åº”式赋值:创建新的 xAxis å’Œ series å¯¹è±¡
  xAxis2.value[0].data = monthName
  xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~'));
  lineSeries.value = [
  const pieLegend = reactive({
    show: false,
  });
  const barSeries = ref([
    {
      name: '开票',
      type: 'line',
      data: invoiceAmount,
      stack: 'Total',
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgba(131, 207, 255, 1)'
          },
          {
            offset: 1,
            color: 'rgba(186, 228, 255, 1)'
          }
        ])
      type: "bar",
      data: [],
      label: {
        show: true,
      },
      itemStyle: {
        color: '#2D99FF',
        borderColor: '#2D99FF'
      },
    },
  ]);
  const barSeries1 = ref([
    {
      name: "原材料不合格数",
      type: "bar",
      barGap: 0,
      emphasis: {
        focus: 'series'
        focus: "series",
      },
      lineStyle: {
        width: 0
      },
      showSymbol: true,
      data: [],
    },
    {
      name: '回款',
      type: 'line',
      data: receiptAmount,
      stack: 'Total',
      lineStyle: {
        width: 0
      },
      itemStyle: {
        color: '#83CFFF',
        borderColor: '#83CFFF'
      },
      showSymbol: true,
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgba(54, 153, 255, 1)'
          },
          {
            offset: 1,
            color: 'rgba(89, 169, 254, 1)'
          }
        ])
      },
      name: "过程不合格数",
      type: "bar",
      emphasis: {
        focus: 'series'
        focus: "series",
      },
      data: [],
    },
    {
      name: "出厂不合格数",
      type: "bar",
      emphasis: {
        focus: "series",
      },
      data: [],
    },
  ]);
  const chartStyle = {
    width: "100%",
    height: "100%", // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
  };
  const chartStylePie = {
    width: "140%",
    height: "140%", // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
  };
  const grid = {
    left: "3%",
    right: "4%",
    bottom: "3%",
    containLabel: true,
  };
  const barLegend = {
    show: true,
    data: ["原材料不合格数", "过程不合格数", "出厂不合格数"],
  };
  const barLegend1 = {
    show: true,
    data: ["预付账款", "应付账款", "预收账款", "应收账款"],
  };
  const lineLegend = {
    show: true,
    data: ["开票", "回款"],
  };
  const tooltip = {
    trigger: "axis",
    axisPointer: {
      type: "shadow",
    },
  };
  const xAxis = [
    {
      type: "value",
    },
  ];
  const xAxis1 = ref([
    {
      type: "category",
      axisTick: { show: false },
      data: [],
    },
  ]);
  const yAxis = [
    {
      type: "category",
      data: ["应付账款", "应收账款"],
    },
  ];
  const yAxis1 = [
    {
      type: "value",
    },
  ];
  const pieTooltip = reactive({
    trigger: "item",
    formatter: function (params) {
      // åŠ¨æ€ç”Ÿæˆæç¤ºä¿¡æ¯ï¼ŒåŸºäºŽæ•°æ®é¡¹çš„ name å±žæ€§
      const description =
        params.name === "本月回款金额" ? "本月回款金额" : "应收款金额";
      return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
    },
    position: "right",
  });
  const materialPieSeries = ref([
    {
      type: "pie",
      radius: ["66%", "90%"],
      avoidLabelOverlap: false,
      itemStyle: {
        borderColor: "#fff",
        borderWidth: 2,
      },
      label: {
        show: false,
      },
      data: [],
    },
  ]);
  const lineSeries = ref([
    {
      type: "line",
      data: [],
      label: {
        show: true,
      },
      showSymbol: true, // æ˜¾ç¤ºåœ†ç‚¹
    },
  ]);
  const tooltipLine = {
    trigger: "axis",
  };
  const yAxis2 = ref([
    {
      type: "value",
    },
  ]);
  const xAxis2 = ref([
    {
      type: "category",
      data: [],
      axisLabel: {
        interval: 0,
        formatter: function (value) {
          return value.replace(/~/g, "\n");
        },
      },
    },
  ]);
  // å¾…办事项
  const todoList = ref([]);
  const radio1 = ref(1);
  const qualityRange = ref(1);
  // å›¾è¡¨å¼•用
  const barChart = ref(null);
  const lineChart = ref(null);
  const barColors2 = ["#5181DB", "#D369E0", "#F2CA6D", "#60CCA8"];
  // éšæœºé¢œè‰²ç”Ÿæˆå‡½æ•°
  const getRandomColor = () => {
    return (
      "#" +
      Math.floor(Math.random() * 0xffffff)
        .toString(16)
        .padStart(6, "0")
    );
  };
  onMounted(() => {
    getBusinessData();
    analysisCustomer();
    todoInfoS();
    statisticsReceivable();
    qualityStatisticsInfo();
    getAmountHalfYearNum();
    getProcessList();
  });
  // æ•°æ®ç»Ÿè®¡
  const getBusinessData = () => {
    getBusiness().then(res => {
      businessInfo.value = { ...res.data };
    });
  };
  // åˆåŒé‡‘额
  const analysisCustomer = () => {
    analysisCustomerContractAmounts().then(res => {
      sum.value = res.data.sum;
      yny.value = res.data.yny;
      chain.value = res.data.chain;
      // ä¸ºæ¯ä¸ªæ•°æ®é¡¹åˆ†é…éšæœºé¢œè‰²
      materialPieSeries.value[0].data = res.data.item.map(item => ({
        ...item,
        itemStyle: { color: getRandomColor() },
      }));
    });
  };
  // å¾…办事项
  const todoInfoS = () => {
    homeTodos().then(res => {
      todoList.value = res.data;
    });
  };
  // èŽ·å–å·¥åºåˆ—è¡¨
  const getProcessList = () => {
    list().then(res => {
      processOptions.value = res.data;
    });
  };
  const openProcessDialog = () => {
    tempProcessIds.value = [...selectedProcessIds.value];
    processDialogVisible.value = true;
  };
  const handleProcessDialogConfirm = () => {
    selectedProcessIds.value = [...tempProcessIds.value];
    processDialogVisible.value = false;
    refreshProcessStats();
  };
  const resetProcessFilter = () => {
    selectedProcessIds.value = [];
    tempProcessIds.value = [];
    refreshProcessStats();
  };
  const handleChartClick = params => {
    if (params && params.dataIndex !== undefined) {
      activeProcessIndex.value = params.dataIndex;
    }
  ]
}
  };
  // åº”付应收统计
  const statisticsReceivable = () => {
    statisticsReceivablePayable({ type: radio1.value }).then(res => {
      barSeries.value[0].data = [
        // { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } },
        { value: res.data.payableMoney, itemStyle: { color: barColors2[0] } },
        // { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } },
        { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } },
      ];
    });
  };
  // è´¨æ£€ç»Ÿè®¡
  const qualityStatisticsInfo = () => {
    qualityInspectionStatistics({ type: qualityRange.value }).then(res => {
      xAxis1.value[0].data = [];
      barSeries1.value[0].data = [];
      barSeries1.value[1].data = [];
      barSeries1.value[2].data = [];
      res.data.item.forEach(item => {
        xAxis1.value[0].data.push(item.date);
        barSeries1.value[0].data.push(item.supplierNum);
        barSeries1.value[1].data.push(item.processNum);
        barSeries1.value[2].data.push(item.factoryNum);
      });
      qualityStatisticsObject.value.supplierNum = res.data.supplierNum;
      qualityStatisticsObject.value.processNum = res.data.processNum;
      qualityStatisticsObject.value.factoryNum = res.data.factoryNum;
    });
  };
  const getAmountHalfYearNum = async () => {
    const res = await getAmountHalfYear();
    console.log(res);
    const monthName = [];
    const receiptAmount = [];
    const invoiceAmount = [];
    res.data.forEach(item => {
      monthName.push(item.month);
      receiptAmount.push(item.receiptAmount);
      invoiceAmount.push(item.invoiceAmount);
    });
    // æ­£ç¡®å“åº”式赋值:创建新的 xAxis å’Œ series å¯¹è±¡
    xAxis2.value[0].data = monthName;
    xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, "\n~"));
    lineSeries.value = [
      {
        name: "开票",
        type: "line",
        data: invoiceAmount,
        stack: "Total",
        areaStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: "rgba(131, 207, 255, 1)",
            },
            {
              offset: 1,
              color: "rgba(186, 228, 255, 1)",
            },
          ]),
        },
        itemStyle: {
          color: "#2D99FF",
          borderColor: "#2D99FF",
        },
        emphasis: {
          focus: "series",
        },
        lineStyle: {
          width: 0,
        },
        showSymbol: true,
      },
      {
        name: "回款",
        type: "line",
        data: receiptAmount,
        stack: "Total",
        lineStyle: {
          width: 0,
        },
        itemStyle: {
          color: "#83CFFF",
          borderColor: "#83CFFF",
        },
        showSymbol: true,
        areaStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: "rgba(54, 153, 255, 1)",
            },
            {
              offset: 1,
              color: "rgba(89, 169, 254, 1)",
            },
          ]),
        },
        emphasis: {
          focus: "series",
        },
      },
    ];
  };
// å·¥åºæ•°æ®ç”Ÿäº§ç»Ÿè®¡æ˜Žç»†ï¼ˆå‡æ•°æ® + å›¾è¡¨ï¼‰
const processRange = ref(1)
const processChartData = ref([])
  // å·¥åºæ•°æ®ç”Ÿäº§ç»Ÿè®¡æ˜Žç»†ï¼ˆå‡æ•°æ® + å›¾è¡¨ï¼‰
  const processRange = ref(1);
  const processChartData = ref([]);
const processXAxis = ref([
  {
    nameTextStyle: { color: 'rgba(0,0,0,0.35)', fontSize: 12 },
    axisLabel: { color: 'rgba(0,0,0,0.35)' },
    splitLine: { lineStyle: { color: 'rgba(0,0,0,0.06)', type: 'dashed' } },
  },
])
const processYAxis = ref([
  {
    type: 'category',
    axisTick: { show: false },
    axisLine: { show: false },
    axisLabel: { color: 'rgba(0,0,0,0.45)' },
    data: [],
  },
])
const processGrid = reactive({ left: 0, right: 100, top: 30, bottom: 20, containLabel: true })
const processTooltip = reactive({
  trigger: 'axis',
  axisPointer: { type: 'shadow' },
  formatter: (params) => {
    const name = params?.[0]?.name ?? ''
    const list = Array.isArray(params) ? params : []
    const lines = list
      .map((p) => {
        const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`
        return `${colorBox}${p.seriesName} <b style="float:right;">${Number(p.value || 0).toFixed(2)}</b>`
      })
      .join('<br/>')
    return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>`
  },
})
const processSeries = computed(() => {
  const input = processChartData.value.map((i) => i.input)
  const scrap = processChartData.value.map((i) => i.scrap)
  const output = processChartData.value.map((i) => i.output)
  return [
  const processXAxis = ref([
    {
      name: '投入量',
      type: 'bar',
      stack: 'total',
      barWidth: 22,
      itemStyle: { color: '#1E5BFF', borderRadius: [6, 0, 0, 6] },
      data: input,
      nameTextStyle: { color: "rgba(0,0,0,0.35)", fontSize: 12 },
      axisLabel: { color: "rgba(0,0,0,0.35)" },
      splitLine: { lineStyle: { color: "rgba(0,0,0,0.06)", type: "dashed" } },
    },
  ]);
  const processYAxis = ref([
    {
      name: '报废量',
      type: 'bar',
      stack: 'total',
      barWidth: 22,
      itemStyle: { color: '#F7B500' },
      data: scrap,
      type: "category",
      axisTick: { show: false },
      axisLine: { show: false },
      axisLabel: { color: "rgba(0,0,0,0.45)" },
      data: [],
    },
    {
      name: '产出量',
      type: 'bar',
      stack: 'total',
      barWidth: 22,
      itemStyle: { color: '#19C6C6', borderRadius: [0, 6, 6, 0] },
      data: output,
  ]);
  const processGrid = reactive({
    left: 0,
    right: 100,
    top: 30,
    bottom: 20,
    containLabel: true,
  });
  const processTooltip = reactive({
    trigger: "axis",
    axisPointer: { type: "shadow" },
    formatter: params => {
      const name = params?.[0]?.name ?? "";
      const list = Array.isArray(params) ? params : [];
      const lines = list
        .map(p => {
          const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`;
          return `${colorBox}${p.seriesName} <b style="float:right;">${Number(
            p.value || 0
          ).toFixed(2)}</b>`;
        })
        .join("<br/>");
      return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>`;
    },
  ]
})
  });
const processAside = computed(() => {
  const list = processChartData.value
  const item = list[activeProcessIndex.value] || {}
  return {
    processName: item.name || '暂无数据',
    totalInput: item.input || 0,
    totalScrap: item.scrap || 0,
    totalOutput: item.output || 0,
  }
})
  const processSeries = computed(() => {
    const input = processChartData.value.map(i => i.input);
    const scrap = processChartData.value.map(i => i.scrap);
    const output = processChartData.value.map(i => i.output);
const formatAmount = (n) => {
  const num = Number(n || 0)
  return num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}
    return [
      {
        name: "投入量",
        type: "bar",
        stack: "total",
        barWidth: 22,
        itemStyle: { color: "#1E5BFF", borderRadius: [6, 0, 0, 6] },
        data: input,
      },
      {
        name: "报废量",
        type: "bar",
        stack: "total",
        barWidth: 22,
        itemStyle: { color: "#F7B500" },
        data: scrap,
      },
      {
        name: "产出量",
        type: "bar",
        stack: "total",
        barWidth: 22,
        itemStyle: { color: "#19C6C6", borderRadius: [0, 6, 6, 0] },
        data: output,
      },
    ];
  });
const refreshProcessStats = () => {
  processDataProductionStatistics({
    type: processRange.value,
    processIds: selectedProcessIds.value.length > 0 ? selectedProcessIds.value.join(',') : null
  }).then(res => {
    processChartData.value = res.data.map(item => ({
      name: item.processName,
      input: item.totalInput,
      scrap: item.totalScrap,
      output: item.totalOutput
    }))
    processYAxis.value[0].data = processChartData.value.map((i) => i.name)
    activeProcessIndex.value = 0
  })
}
  const processAside = computed(() => {
    const list = processChartData.value;
    const item = list[activeProcessIndex.value] || {};
    return {
      processName: item.name || "暂无数据",
      totalInput: item.input || 0,
      totalScrap: item.scrap || 0,
      totalOutput: item.output || 0,
    };
  });
onMounted(() => {
  getBusinessData()
  analysisCustomer()
  todoInfoS()
  statisticsReceivable()
  qualityStatisticsInfo()
  getAmountHalfYearNum()
  refreshProcessStats()
})
  const formatAmount = n => {
    const num = Number(n || 0);
    return num.toLocaleString(undefined, {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    });
  };
  const refreshProcessStats = () => {
    processDataProductionStatistics({
      type: processRange.value,
      processIds:
        selectedProcessIds.value.length > 0
          ? selectedProcessIds.value.join(",")
          : null,
    }).then(res => {
      processChartData.value = res.data.map(item => ({
        name: item.processName,
        input: item.totalInput,
        scrap: item.totalScrap,
        output: item.totalOutput,
      }));
      processYAxis.value[0].data = processChartData.value.map(i => i.name);
      activeProcessIndex.value = 0;
    });
  };
  onMounted(() => {
    getBusinessData();
    analysisCustomer();
    todoInfoS();
    statisticsReceivable();
    qualityStatisticsInfo();
    getAmountHalfYearNum();
    refreshProcessStats();
  });
</script>
<style scoped>
.dashboard {
  background: #f5f7fa;
  min-height: 100vh;
  padding: 20px;
  box-sizing: border-box;
}
  .dashboard {
    background: #f5f7fa;
    min-height: 100vh;
    padding: 20px;
    box-sizing: border-box;
  }
.dashboard-top {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  align-items: flex-start;
  justify-content: space-evenly;
}
  .dashboard-top {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
    align-items: flex-start;
    justify-content: space-evenly;
  }
.company-info {
  padding: 0;
  overflow: hidden;
  border-radius: 12px;
  background: #fff;
  height: 100%;
}
  .company-info {
    padding: 0;
    overflow: hidden;
    border-radius: 12px;
    background: #fff;
    height: 100%;
  }
.welcome-banner {
  padding: 10px 10px;
  background: linear-gradient(135deg, rgba(229, 240, 255, 0.9), rgba(214, 232, 255, 0.7), rgba(207, 236, 255, 0.9));
}
  .welcome-banner {
    padding: 10px 10px;
    background: linear-gradient(
      135deg,
      rgba(229, 240, 255, 0.9),
      rgba(214, 232, 255, 0.7),
      rgba(207, 236, 255, 0.9)
    );
  }
.welcome-title {
  font-size: 18px;
  font-weight: 700;
  color: #222;
  line-height: 1.3;
}
  .welcome-title {
    font-size: 18px;
    font-weight: 700;
    color: #222;
    line-height: 1.3;
  }
.welcome-user {
  margin-right: 6px;
}
  .welcome-user {
    margin-right: 6px;
  }
.welcome-time {
  margin-top: 10px;
  font-size: 16px;
  color: rgba(0, 0, 0, 0.55);
}
  .welcome-time {
    margin-top: 10px;
    font-size: 16px;
    color: rgba(0, 0, 0, 0.55);
  }
.user-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 22px;
}
  .user-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 18px 22px;
  }
.user-card-main {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
  .user-card-main {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
  }
.user-name {
  font-size: 16px;
  font-weight: bold;
  color: #111;
  letter-spacing: 1px;
}
  .user-name {
    font-size: 16px;
    font-weight: bold;
    color: #111;
    letter-spacing: 1px;
  }
.user-role {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  padding: 5px 10px;
  background: rgba(245, 246, 248, 1);
  color: #333;
  width: fit-content;
  font-weight: 600;
}
  .user-role {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 20px;
    padding: 5px 10px;
    background: rgba(245, 246, 248, 1);
    color: #333;
    width: fit-content;
    font-weight: 600;
  }
.user-meta {
  font-size: 12px;
  color: rgba(0, 0, 0, 0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
  .user-meta {
    font-size: 12px;
    color: rgba(0, 0, 0, 0.55);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
.user-meta .sep {
  margin: 0 10px;
  color: rgba(0, 0, 0, 0.25);
}
  .user-meta .sep {
    margin: 0 10px;
    color: rgba(0, 0, 0, 0.25);
  }
.avatar {
  width: 90px;
  height: 90px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
}
  .avatar {
    width: 90px;
    height: 90px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
  }
.data-cards {
  width: 50%;
  display: flex;
  gap: 16px;
  justify-content: flex-start;
  background: #ffffff;
  border-radius: 12px;
  padding: 20px;
}
  .data-cards {
    width: 50%;
    display: flex;
    gap: 16px;
    justify-content: flex-start;
    background: #ffffff;
    border-radius: 12px;
    padding: 20px;
  }
.data-title {
  font-weight: 700;
  font-size: 26px;
  color: #FFFFFF;
}
  .data-title {
    font-weight: 700;
    font-size: 26px;
    color: #ffffff;
  }
.data-num {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 20px;
}
  .data-num {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 20px;
  }
.data-card {
  background: #fff;
  border-radius: 12px;
  padding: 14px 10px 10px 10px;
  min-width: 160px;
  box-shadow: 0 2px 8px #eee;
  display: flex;
  flex-direction: column;
  width: 32%;
  height: 140px;
}
  .data-card {
    background: #fff;
    border-radius: 12px;
    padding: 14px 10px 10px 10px;
    min-width: 160px;
    box-shadow: 0 2px 8px #eee;
    display: flex;
    flex-direction: column;
    width: 32%;
    height: 140px;
  }
.data-card.sales {
  background-image: url("../assets/images/xioashoushuju.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
  .data-card.sales {
    background-image: url("../assets/images/xioashoushuju.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
.data-card.purchase {
  background-image: url("../assets/images/caigou.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
  .data-card.purchase {
    background-image: url("../assets/images/caigou.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
.data-card.inventory {
  background-image: url("../assets/images/kucun.png");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
  .data-card.inventory {
    background-image: url("../assets/images/kucun.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
.data-desc {
  font-weight: 500;
  font-size: 13px;
  color: #FFFFFF;
}
  .data-desc {
    font-weight: 500;
    font-size: 13px;
    color: #ffffff;
  }
.data-value {
  font-size: 18px;
  font-weight: 500;
  margin: 10px 0;
  color: #FFFFFF;
}
  .data-value {
    font-size: 18px;
    font-weight: 500;
    margin: 10px 0;
    color: #ffffff;
  }
.top-left {
  display: flex;
  flex-direction: column;
  gap: 20px;
  height: 180px;
  width: 20%;
}
  .top-left {
    display: flex;
    flex-direction: column;
    gap: 20px;
    height: 180px;
    width: 20%;
  }
.todo-panel {
  background: #fff;
  border-radius: 12px;
  padding: 20px;
  height: 180px;
  width: 30%;
}
  .todo-panel {
    background: #fff;
    border-radius: 12px;
    padding: 20px;
    height: 180px;
    width: 30%;
  }
.todo-list {
  height: 100px;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 15px;
  overflow-y: auto;
}
  .todo-list {
    height: 100px;
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 15px;
    overflow-y: auto;
  }
.todo-list li {
  border-radius: 8px;
  margin-bottom: 12px;
  padding: 8px 20px;
  height: 74px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: rgba(225, 227, 250, 0.62);
}
.todo-title {
  font-weight: 400;
  font-size: 12px;
  color: #000000;
  position: relative;
}
.todo-title::before {
  content: '';
  /* å¿…需,表示这里有一个内容 */
  position: absolute;
  left: -10px;
  /* å®šä½åˆ°å·¦ä¾§ */
  top: 50%;
  /* åž‚直居中 */
  transform: translateY(-50%);
  /* å¾®è°ƒåž‚直居中 */
  width: 6px;
  /* åœ†çš„直径 */
  height: 6px;
  /* åœ†çš„直径 */
  background: #498CEB;
  border-radius: 50%;
  /* è®©å…¶å˜æˆåœ†å½¢ */
}
.todo-division {
  font-weight: 400;
  font-size: 12px;
  color: #000000;
}
.todo-time {
  font-weight: 400;
  font-size: 12px;
  color: #000000;
}
.todo-meta {
  color: #888;
  font-size: 13px;
}
.dashboard-row {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
}
.main-panel {
  background: #fff;
  border-radius: 12px;
  padding: 20px;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.section-title {
  position: relative;
  font-size: 18px;
  color: #333;
  padding-left: 10px;
  margin-bottom: 10px;
  font-weight: 700;
}
.section-title::before {
  position: absolute;
  left: 0;
  top: 4px;
  content: '';
  width: 4px;
  height: 18px;
  background-color: #002FA7;
  border-radius: 2px;
}
.contract-info {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 90px;
  background: rgba(245, 245, 245, 0.59);
  width: 100%;
  border-radius: 10px;
  padding: 10px 30px;
}
.contract-summary {
  display: flex;
  align-items: center;
  gap: 30px;
}
.contract-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.contract-name {
  font-weight: 400;
  font-size: 14px;
  color: #050505;
}
.contract-meta {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 80px;
}
.main-amount {
  font-size: 24px;
  color: rgba(51, 50, 50, 0.85);
}
.up {
  color: #e57373;
}
.contract-list {
  margin-top: 16px;
  font-size: 14px;
  color: #666;
  list-style: none;
  padding: 0;
  height: 190px;
  overflow-y: auto;
  width: 460px;
}
.line {
  position: relative;
  width: 230px;
}
.line::after {
  content: '';
  position: absolute;
  right: 2px;
  top: 0;
  bottom: 0;
  width: 1px;
  background-color: #C9C5C5;
  border-radius: 2px;
}
.contract-list li {
  margin-top: 10px;
}
.quality-cards {
  display: flex;
  gap: 12px;
  margin-bottom: 12px;
}
.quality-card {
  border-radius: 8px;
  padding: 15px 10px 10px 50px;
  font-weight: 400;
  font-size: 12px;
  color: rgba(0, 0, 0, 0.67);
  width: 236px;
  height: 49px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.quality-card.one {
  background-image: url("../assets/images/yuancailiao.png");
}
.quality-card.two {
  background-image: url("../assets/images/guocheng.png");
}
.quality-card.three {
  background-image: url("../assets/images/chuchang.png");
}
.quality-card span {
  color: #4fc3f7;
  font-weight: bold;
  margin-left: 6px;
}
.chart {
  width: 100%;
  height: 220px;
  margin-top: 10px;
}
.process-panel {
  padding-bottom: 10px;
}
.process-panel__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.process-panel__body {
  display: flex;
  gap: 24px;
  align-items: stretch;
  margin-top: 10px;
}
.process-panel__chart {
  flex: 1;
  min-width: 0;
  padding: 6px 0;
}
.process-panel__aside {
  width: 260px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.process-legend {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
  padding: 8px 6px;
}
.process-legend__item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: rgba(0, 0, 0, 0.55);
}
.dot {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  display: inline-block;
}
.dot-blue {
  background: #1E5BFF;
}
.dot-yellow {
  background: #F7B500;
}
  .todo-list li {
    border-radius: 8px;
    margin-bottom: 12px;
    padding: 8px 20px;
    height: 74px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: rgba(225, 227, 250, 0.62);
  }
.dot-teal {
  background: #19C6C6;
}
  .todo-title {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
    position: relative;
  }
.process-card {
  background: rgba(245, 247, 250, 0.9);
  border-radius: 10px;
  padding: 16px 16px;
}
  .todo-title::before {
    content: "";
    /* å¿…需,表示这里有一个内容 */
    position: absolute;
    left: -10px;
    /* å®šä½åˆ°å·¦ä¾§ */
    top: 50%;
    /* åž‚直居中 */
    transform: translateY(-50%);
    /* å¾®è°ƒåž‚直居中 */
    width: 6px;
    /* åœ†çš„直径 */
    height: 6px;
    /* åœ†çš„直径 */
    background: #498ceb;
    border-radius: 50%;
    /* è®©å…¶å˜æˆåœ†å½¢ */
  }
.process-card--name {
  background: rgba(235, 242, 255, 1);
  color: #1E5BFF;
  font-weight: 800;
  font-size: 14px;
}
  .todo-division {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
  }
.process-card__label {
  font-size: 13px;
  color: rgba(0, 0, 0, 0.55);
  margin-bottom: 10px;
}
  .todo-time {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
  }
.process-card__value {
  font-size: 24px;
  font-weight: 800;
  color: rgba(0, 0, 0, 0.8);
}
  .todo-meta {
    color: #888;
    font-size: 13px;
  }
.process-card__value .unit {
  font-size: 12px;
  font-weight: 600;
  color: rgba(0, 0, 0, 0.45);
  margin-left: 6px;
}
  .dashboard-row {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
  }
@media (max-width: 1200px) {
  .process-panel__body {
  .main-panel {
    background: #fff;
    border-radius: 12px;
    padding: 20px;
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  .process-panel__aside {
  .section-title {
    position: relative;
    font-size: 18px;
    color: #333;
    padding-left: 10px;
    margin-bottom: 10px;
    font-weight: 700;
  }
  .section-title::before {
    position: absolute;
    left: 0;
    top: 4px;
    content: "";
    width: 4px;
    height: 18px;
    background-color: #002fa7;
    border-radius: 2px;
  }
  .contract-info {
    display: flex;
    align-items: center;
    gap: 20px;
    height: 90px;
    background: rgba(245, 245, 245, 0.59);
    width: 100%;
    flex-direction: row;
    flex-wrap: wrap;
    border-radius: 10px;
    padding: 10px 30px;
  }
  .contract-summary {
    display: flex;
    align-items: center;
    gap: 30px;
  }
  .contract-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .contract-name {
    font-weight: 400;
    font-size: 14px;
    color: #050505;
  }
  .contract-meta {
    display: flex;
    align-items: center;
    width: 100%;
    gap: 80px;
  }
  .main-amount {
    font-size: 24px;
    color: rgba(51, 50, 50, 0.85);
  }
  .up {
    color: #e57373;
  }
  .contract-list {
    margin-top: 16px;
    font-size: 14px;
    color: #666;
    list-style: none;
    padding: 0;
    height: 190px;
    overflow-y: auto;
    width: 460px;
  }
  .line {
    position: relative;
    width: 230px;
  }
  .line::after {
    content: "";
    position: absolute;
    right: 2px;
    top: 0;
    bottom: 0;
    width: 1px;
    background-color: #c9c5c5;
    border-radius: 2px;
  }
  .contract-list li {
    margin-top: 10px;
  }
  .quality-cards {
    display: flex;
    gap: 12px;
    margin-bottom: 12px;
  }
  .quality-card {
    border-radius: 8px;
    padding: 15px 10px 10px 50px;
    font-weight: 400;
    font-size: 12px;
    color: rgba(0, 0, 0, 0.67);
    width: 236px;
    height: 49px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  .quality-card.one {
    background-image: url("../assets/images/yuancailiao.png");
  }
  .quality-card.two {
    background-image: url("../assets/images/guocheng.png");
  }
  .quality-card.three {
    background-image: url("../assets/images/chuchang.png");
  }
  .quality-card span {
    color: #4fc3f7;
    font-weight: bold;
    margin-left: 6px;
  }
  .chart {
    width: 100%;
    height: 220px;
    margin-top: 10px;
  }
  .process-panel {
    padding-bottom: 10px;
  }
  .process-panel__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .process-panel__body {
    display: flex;
    gap: 24px;
    align-items: stretch;
    margin-top: 10px;
  }
  .process-panel__chart {
    flex: 1;
    min-width: 0;
    padding: 6px 0;
  }
  .process-panel__aside {
    width: 260px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  .process-legend {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: flex-start;
    padding: 8px 6px;
  }
  .process-legend__item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: rgba(0, 0, 0, 0.55);
  }
  .dot {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    display: inline-block;
  }
  .dot-blue {
    background: #1e5bff;
  }
  .dot-yellow {
    background: #f7b500;
  }
  .dot-teal {
    background: #19c6c6;
  }
  .process-card {
    flex: 1;
    min-width: 220px;
    background: rgba(245, 247, 250, 0.9);
    border-radius: 10px;
    padding: 16px 16px;
  }
}
.process-selection-wrapper {
  max-height: 400px;
  overflow-y: auto;
  padding: 10px;
}
  .process-card--name {
    background: rgba(235, 242, 255, 1);
    color: #1e5bff;
    font-weight: 800;
    font-size: 14px;
  }
.process-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 12px;
}
  .process-card__label {
    font-size: 13px;
    color: rgba(0, 0, 0, 0.55);
    margin-bottom: 10px;
  }
:deep(.el-checkbox.is-bordered) {
  margin-left: 0 !important;
  width: 100%;
}
  .process-card__value {
    font-size: 24px;
    font-weight: 800;
    color: rgba(0, 0, 0, 0.8);
  }
  .process-card__value .unit {
    font-size: 12px;
    font-weight: 600;
    color: rgba(0, 0, 0, 0.45);
    margin-left: 6px;
  }
  @media (max-width: 1200px) {
    .process-panel__body {
      flex-direction: column;
    }
    .process-panel__aside {
      width: 100%;
      flex-direction: row;
      flex-wrap: wrap;
    }
    .process-card {
      flex: 1;
      min-width: 220px;
    }
  }
  .process-selection-wrapper {
    max-height: 400px;
    overflow-y: auto;
    padding: 10px;
  }
  .process-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: 12px;
  }
  :deep(.el-checkbox.is-bordered) {
    margin-left: 0 !important;
    width: 100%;
  }
</style>
src/views/procurementManagement/procurementLedger/index.vue
@@ -33,6 +33,18 @@
                      prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="供应商类型:">
            <el-select v-model="searchForm.supplierType"
                       placeholder="请选择"
                       style="width: 220px"
                       clearable
                       @change="handleQuery">
              <el-option label="对公"
                         value="1" />
              <el-option label="对私"
                         value="2" />
            </el-select>
          </el-form-item>
          <el-form-item label="录入日期:">
            <el-date-picker v-model="searchForm.entryDate"
                            value-format="YYYY-MM-DD"
@@ -53,7 +65,9 @@
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="primary" plain @click="handleImport">导入</el-button>
        <el-button type="primary"
                   plain
                   @click="handleImport">导入</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
@@ -94,18 +108,29 @@
                               prop="availableQuality" />
              <el-table-column label="退货数量"
                               prop="returnQuality" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
                               prop="taxRate"
                               v-if="props.row.supplierType === 1" />
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               v-if="props.row.supplierType === 1" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               v-if="props.row.supplierType === 1" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
                               :formatter="formattedNumber"
                               v-if="props.row.supplierType === 1" />
              <el-table-column label="单价(对私)"
                               prop="unitPrice"
                               :formatter="formattedNumber"
                               v-if="props.row.supplierType === 2" />
              <el-table-column label="总价(对私)"
                               prop="totalPrice"
                               :formatter="formattedNumber"
                               v-if="props.row.supplierType === 2" />
            </el-table>
          </template>
        </el-table-column>
@@ -119,12 +144,20 @@
                         show-overflow-tooltip />
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                          width="160"
                         width="160"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
                         prop="supplierName"
                          width="160"
                         width="160"
                         show-overflow-tooltip />
        <el-table-column label="供应商类型"
                         prop="supplierType"
                         width="100"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ scope.row.supplierType === 1 ? '对公' : '对私' }}
          </template>
        </el-table-column>
        <el-table-column label="项目名称"
                         prop="projectName"
                         width="320"
@@ -134,9 +167,8 @@
                         width="100"
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag
              :type="getApprovalStatusType(scope.row.approvalStatus)"
              size="small">
            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)"
                    size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
@@ -189,12 +221,12 @@
                  @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
               :width="'70%'"
               :operation-type="operationType"
               @close="closeDia"
               @confirm="submitForm"
               @cancel="closeDia">
                :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
                :width="'70%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
@@ -232,11 +264,12 @@
              <el-select v-model="form.supplierId"
                         placeholder="请选择"
                         filterable
                         clearable>
                         clearable
                         @change="handleSupplierChange">
                <el-option v-for="item in supplierList"
                           :key="item.id"
                           :label="item.supplierName"
                                                     :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option>
                           :value="item.id">{{item.supplierName + '---' + (item.supplierType === 1 ? item.taxpayerIdentificationNum || '无' : '对私')}}</el-option>
              </el-select>
            </el-form-item>
          </el-col>
@@ -304,38 +337,33 @@
              <template #label>
                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                  <span>审批人选择:</span>
                  <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button>
                  <el-button type="primary"
                             size="small"
                             @click="addApproverNode"
                             icon="Plus">新增节点</el-button>
                </div>
              </template>
              <div class="approver-nodes-container">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  class="approver-node-item"
                >
                <div v-for="(node, index) in approverNodes"
                     :key="node.id"
                     class="approver-node-item">
                  <div class="approver-node-header">
                    <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                    <el-button
                      v-if="approverNodes.length > 1"
                      type="danger"
                      size="small"
                      text
                      @click="removeApproverNode(index)"
                      icon="Delete"
                    >删除</el-button>
                    <el-button v-if="approverNodes.length > 1"
                               type="danger"
                               size="small"
                               text
                               @click="removeApproverNode(index)"
                               icon="Delete">删除</el-button>
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="请选择审批人"
                    filterable
                    style="width: 100%;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  <el-select v-model="node.userId"
                             placeholder="请选择审批人"
                             filterable
                             style="width: 100%;">
                    <el-option v-for="user in userList"
                               :key="user.userId"
                               :label="user.nickName"
                               :value="user.userId" />
                  </el-select>
                </div>
              </div>
@@ -346,6 +374,7 @@
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button type="primary"
                       v-if="currentSupplierType"
                       @click="openProductForm('add')">添加</el-button>
            <el-button plain
                       type="danger"
@@ -373,11 +402,10 @@
                         :value="item.templateName">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                  <span>{{ item.templateName }}</span>
                  <el-icon
                    v-if="item.id"
                    class="delete-icon"
                    @click.stop="handleDeleteTemplate(item)"
                    style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
                  <el-icon v-if="item.id"
                           class="delete-icon"
                           @click.stop="handleDeleteTemplate(item)"
                           style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
                    <Delete />
                  </el-icon>
                </div>
@@ -420,19 +448,33 @@
                           show-overflow-tooltip />
          <el-table-column label="税率(%)"
                           prop="taxRate"
                           width="80" />
                           width="80"
                           v-if="currentSupplierType === 1" />
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="150"
                           v-if="currentSupplierType === 1" />
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="150"
                           v-if="currentSupplierType === 1" />
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
                           width="150"
                           v-if="currentSupplierType === 1" />
          <el-table-column label="单价(对私)"
                           prop="unitPrice"
                           :formatter="formattedNumber"
                           width="150"
                           v-if="currentSupplierType === 2" />
          <el-table-column label="总价(对私)"
                           prop="totalPrice"
                           :formatter="formattedNumber"
                           width="150"
                           v-if="currentSupplierType === 2" />
          <el-table-column label="是否质检"
                           prop="isChecked"
                           width="150">
@@ -493,28 +535,24 @@
      </el-form>
    </FormDialog>
    <!-- å¯¼å…¥å¼¹çª— -->
    <FormDialog
      v-model="importUpload.open"
      :title="importUpload.title"
      :width="'600px'"
      @close="importUpload.open = false"
      @confirm="submitImportFile"
      @cancel="importUpload.open = false"
    >
      <el-upload
        ref="importUploadRef"
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
        :headers="importUpload.headers"
        :before-upload="importUpload.beforeUpload"
        :on-success="importUpload.onSuccess"
        :on-error="importUpload.onError"
        :on-progress="importUpload.onProgress"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
    <FormDialog v-model="importUpload.open"
                :title="importUpload.title"
                :width="'600px'"
                @close="importUpload.open = false"
                @confirm="submitImportFile"
                @cancel="importUpload.open = false">
      <el-upload ref="importUploadRef"
                 :limit="1"
                 accept=".xlsx,.xls"
                 :action="importUpload.url"
                 :headers="importUpload.headers"
                 :before-upload="importUpload.beforeUpload"
                 :on-success="importUpload.onSuccess"
                 :on-error="importUpload.onError"
                 :on-progress="importUpload.onProgress"
                 :on-change="importUpload.onChange"
                 :auto-upload="false"
                 drag>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          å°†æ–‡ä»¶æ‹–到此处,或<em>点击上传</em>
@@ -522,22 +560,24 @@
        <template #tip>
          <div class="el-upload__tip">
            ä»…支持 xls/xlsx,大小不超过 10MB。
            <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
            <el-button link
                       type="primary"
                       @click="downloadTemplate">下载导入模板</el-button>
          </div>
        </template>
      </el-upload>
    </FormDialog>
    <FormDialog v-model="productFormVisible"
               :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
               :width="'40%'"
               :operation-type="productOperationType"
               @close="closeProductDia"
               @confirm="submitProduct"
               @cancel="closeProductDia">
                :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
                :width="'40%'"
                :operation-type="productOperationType"
                @close="closeProductDia"
                @confirm="submitProduct"
                @cancel="closeProductDia">
      <el-form :model="productForm"
               label-width="140px"
               label-position="top"
               :rules="productRules"
               :rules="getProductRules()"
               ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
@@ -581,7 +621,8 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率(%):"
                          prop="taxRate">
                          prop="taxRate"
                          v-if="currentSupplierType === 1">
              <el-select v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
@@ -598,18 +639,6 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice">
              <el-input-number v-model="productForm.taxInclusiveUnitPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="mathNum" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
@@ -619,14 +648,39 @@
                               style="width: 100%"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               @change="currentSupplierType === 1 ? mathNum() : mathNumPrivate()" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice"
                          v-if="currentSupplierType === 1">
              <el-input-number v-model="productForm.taxInclusiveUnitPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="mathNum" />
            </el-form-item>
            <el-form-item label="单价(对私):"
                          prop="unitPrice"
                          v-else>
              <el-input-number v-model="productForm.unitPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="mathNumPrivate" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):"
                          prop="taxInclusiveTotalPrice">
                          prop="taxInclusiveTotalPrice"
                          v-if="currentSupplierType === 1">
              <el-input-number v-model="productForm.taxInclusiveTotalPrice"
                               :precision="2"
                               :step="0.1"
@@ -635,10 +689,34 @@
                               style="width: 100%"
                               @change="reverseMathNum('taxInclusiveTotalPrice')" />
            </el-form-item>
            <el-form-item label="总价(对私):"
                          prop="totalPrice"
                          v-else>
              <el-input-number v-model="productForm.totalPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="reverseMathNumPrivate('totalPrice')" />
            </el-form-item>
          </el-col>
          <el-col v-if="currentSupplierType === 2"
                  :span="12">
            <el-form-item label="库存预警数量:"
                          prop="warnNum">
              <el-input-number v-model="productForm.warnNum"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):"
                          prop="taxExclusiveTotalPrice">
                          prop="taxExclusiveTotalPrice"
                          v-if="currentSupplierType === 1">
              <el-input-number v-model="productForm.taxExclusiveTotalPrice"
                               :precision="2"
                               :step="0.1"
@@ -652,7 +730,8 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:"
                          prop="invoiceType">
                          prop="invoiceType"
                          v-if="currentSupplierType === 1">
              <el-select v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable>
@@ -663,7 +742,8 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
          <el-col v-if="currentSupplierType === 1"
                  :span="12">
            <el-form-item label="库存预警数量:"
                          prop="warnNum">
              <el-input-number v-model="productForm.warnNum"
@@ -690,11 +770,9 @@
        </el-row>
      </el-form>
    </FormDialog>
    <FileListDialog
      ref="fileListRef"
      v-model="fileListDialogVisible"
      title="附件列表"
    />
    <FileListDialog ref="fileListRef"
                    v-model="fileListDialogVisible"
                    title="附件列表" />
  </div>
</template>
@@ -712,8 +790,8 @@
  import { Search, Delete } from "@element-plus/icons-vue";
  import { ElMessageBox, ElMessage } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import FormDialog from '@/components/Dialog/FormDialog.vue';
  import FileListDialog from '@/components/Dialog/FileListDialog.vue';
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
  import {
    getSalesLedgerWithProducts,
    addOrUpdateSalesLedgerProduct,
@@ -748,6 +826,7 @@
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const tableLoading = ref(false);
  const currentSupplierType = ref(null); // è·Ÿè¸ªå½“前选择的供应商类型
  const page = reactive({
    current: 1,
    size: 100,
@@ -766,11 +845,31 @@
  const addApproverNode = () => {
    approverNodes.value.push({ id: nextApproverId++, userId: null });
  };
  const removeApproverNode = (index) => {
  const removeApproverNode = index => {
    approverNodes.value.splice(index, 1);
  };
  // è®¢å•审批状态显示文本
  // å¤„理供应商选择变化
  const handleSupplierChange = async supplierId => {
    const selectedSupplier = supplierList.value.find(
      item => item.id === supplierId
    );
    if (selectedSupplier) {
      // åªæœ‰å½“当前供应商类型不为 null ä¸”发生变化时,才清空产品表
      // è¿™æ ·åœ¨ç¼–辑时加载数据后调用该函数不会清空产品数据
      if (
        currentSupplierType.value !== null &&
        currentSupplierType.value !== selectedSupplier.supplierType
      ) {
        productData.value = [];
      }
      currentSupplierType.value = selectedSupplier.supplierType;
    } else {
      currentSupplierType.value = null;
      productData.value = [];
    }
    await getTemplateList();
  };
  const approvalStatusText = {
    1: "待审核",
    2: "审批中",
@@ -779,12 +878,12 @@
  };
  // èŽ·å–å®¡æ‰¹çŠ¶æ€æ ‡ç­¾ç±»åž‹
  const getApprovalStatusType = (status) => {
  const getApprovalStatusType = status => {
    const typeMap = {
      1: "info",      // å¾…审核 - ç°è‰²
      2: "warning",   // å®¡æ‰¹ä¸­ - æ©™è‰²
      3: "success",   // å®¡æ‰¹é€šè¿‡ - ç»¿è‰²
      4: "danger",    // å®¡æ‰¹å¤±è´¥ - çº¢è‰²
      1: "info", // å¾…审核 - ç°è‰²
      2: "warning", // å®¡æ‰¹ä¸­ - æ©™è‰²
      3: "success", // å®¡æ‰¹é€šè¿‡ - ç»¿è‰²
      4: "danger", // å®¡æ‰¹å¤±è´¥ - çº¢è‰²
    };
    return typeMap[status] || "";
  };
@@ -866,7 +965,8 @@
        form.value.paymentMethod = matchedTemplate.paymentMethod;
      }
      // æ¨¡æ¿æ•°æ®ä¸­çš„产品字段是 productList,需要转换为 productData
      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
      productData.value =
        matchedTemplate.productList || matchedTemplate.productData || [];
    } else {
      // æœªåŒ¹é…åˆ°å·²æœ‰æ¨¡æ¿ï¼Œè§†ä¸ºæ–°æ¨¡æ¿
      currentTemplateId.value = null;
@@ -902,6 +1002,7 @@
      entryDate: null, // å½•入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
      supplierType: "", // ä¾›åº”商类型
    },
    form: {
      purchaseContractNumber: "",
@@ -985,6 +1086,43 @@
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  const getProductRules = () => {
    const baseRules = {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      warnNum: [{ required: true, message: "请选择", trigger: "change" }],
      isChecked: [{ required: true, message: "请选择", trigger: "change" }],
    };
    if (currentSupplierType.value === 1) {
      // å¯¹å…¬ä¾›åº”商需要验证的字段
      return {
        ...baseRules,
        taxInclusiveUnitPrice: [
          { required: true, message: "请输入", trigger: "blur" },
        ],
        taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        taxInclusiveTotalPrice: [
          { required: true, message: "请输入", trigger: "blur" },
        ],
        taxExclusiveTotalPrice: [
          { required: true, message: "请输入", trigger: "blur" },
        ],
        invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
      };
    } else if (currentSupplierType.value === 2) {
      // å¯¹ç§ä¾›åº”商需要验证的字段
      return {
        ...baseRules,
        unitPrice: [{ required: true, message: "请输入", trigger: "blur" }],
        totalPrice: [{ required: true, message: "请输入", trigger: "blur" }],
      };
    } else {
      return baseRules;
    }
  };
  const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
@@ -1000,7 +1138,7 @@
    url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
    beforeUpload: file => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
@@ -1049,7 +1187,11 @@
  // ä¸‹è½½å¯¼å…¥æ¨¡æ¿ï¼ˆå¦‚后端路径不同,可在此处调整)
  const downloadTemplate = () => {
    proxy.download("/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx");
    proxy.download(
      "/purchase/ledger/exportTemplate",
      {},
      "采购台账导入模板.xlsx"
    );
  };
  const submitImportFile = () => {
@@ -1114,8 +1256,8 @@
    // æ£€æŸ¥æ˜¯å¦æœ‰äº§å“æ•°æ®
    if (!productData.value || productData.value.length === 0) {
      ElMessage({
        message: '请先添加产品信息',
        type: 'warning',
        message: "请先添加产品信息",
        type: "warning",
      });
      return;
    }
@@ -1126,7 +1268,7 @@
        .filter(node => node.userId)
        .map(node => node.userId)
        .join(",");
      let params = {
        productData: proxy.HaveJson(productData.value),
        supplierId: form.value.supplierId,
@@ -1136,7 +1278,12 @@
        approveUserIds: approveUserIds,
        templateName: templateName.value.trim(),
      };
      console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
      console.log(
        "template params ===>",
        params,
        "currentTemplateId:",
        currentTemplateId.value
      );
      // å¦‚æžœ currentTemplateId æœ‰å€¼ï¼Œè¯´æ˜Žå½“前是“编辑已有模板” â†’ è°ƒç”¨æ›´æ–°æŽ¥å£
      // å¦åˆ™ä¸ºâ€œæ–°å»ºæ¨¡æ¿â€ â†’ è°ƒç”¨æ–°å¢žæŽ¥å£
@@ -1147,7 +1294,10 @@
          ...params,
        });
      } else {
        res = await addPurchaseTemplate(params);
        res = await addPurchaseTemplate({
          ...params,
          templateType: currentSupplierType.value,
        });
      }
      if (res && res.code === 200) {
@@ -1179,22 +1329,34 @@
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeChildrenTable = param => {
    return proxy.summarizeTable(
      param,
      [
    // æ£€æŸ¥æ˜¯å¦æœ‰æ•°æ®ï¼Œä»¥åŠæ•°æ®çš„供应商类型
    const hasData = param && param.data && param.data.length > 0;
    const supplierType = hasData ? param.data[0].supplierType : null;
    // æ ¹æ®ä¾›åº”商类型确定要合计的字段
    const fields = [
      "ticketsNum",
      "ticketsAmount",
      "futureTickets",
      "futureTicketsAmount",
    ];
    if (supplierType === 1) {
      // å¯¹å…¬ä¾›åº”商
      fields.unshift(
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
        "ticketsNum",
        "ticketsAmount",
        "futureTickets",
        "futureTicketsAmount",
      ],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
        "taxExclusiveTotalPrice"
      );
    } else if (supplierType === 2) {
      // å¯¹ç§ä¾›åº”商
      fields.unshift("unitPrice", "totalPrice");
    }
    return proxy.summarizeTable(param, fields, {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    });
  };
  const paginationChange = obj => {
    page.current = obj.page;
@@ -1238,7 +1400,12 @@
        const res = await productList({ salesLedgerId: row.id, type: 2 });
        const index = tableData.value.findIndex(item => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data || [];
          // ä¸ºå­è¡¨æ ¼æ•°æ®æ·»åŠ ä¾›åº”å•†ç±»åž‹ä¿¡æ¯
          const children = (res.data || []).map(item => ({
            ...item,
            supplierType: row.supplierType, // ä»Žçˆ¶è¡ŒèŽ·å–ä¾›åº”å•†ç±»åž‹
          }));
          tableData.value[index].children = children;
          expandedRowKeys.value.push(row.id);
        }
      } catch (error) {
@@ -1260,11 +1427,17 @@
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeProTable = param => {
    return proxy.summarizeTable(param, [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
    const fields = [];
    if (currentSupplierType.value === 1) {
      fields.push(
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice"
      );
    } else if (currentSupplierType.value === 2) {
      fields.push("unitPrice", "totalPrice");
    }
    return proxy.summarizeTable(param, fields);
  };
  // æ‰“开弹框
  const openForm = async (type, row) => {
@@ -1275,8 +1448,8 @@
        return;
      }
    }
    await getTemplateList();
    currentSupplierType.value = null;
    operationType.value = type;
    form.value = {};
    productData.value = [];
@@ -1307,6 +1480,7 @@
      form.value.entryDate = getCurrentDate();
      if (type === "add") {
        await getTemplateList();
        // æ–°å¢žæ—¶ç”Ÿæˆé‡‡è´­åˆåŒå·
        try {
          const purchaseNoRes = await createPurchaseNo();
@@ -1330,9 +1504,13 @@
            const approverIds = purchaseRes.approveUserIds.split(",");
            approverNodes.value = approverIds.map((id, index) => ({
              id: index + 1,
              userId: Number(id)
              userId: Number(id),
            }));
            nextApproverId = approverIds.length + 1;
          }
          // è®¾ç½®å½“前供应商类型
          if (form.value.supplierId) {
            handleSupplierChange(form.value.supplierId);
          }
        } catch (error) {
          console.error("加载采购台账数据失败:", error);
@@ -1408,8 +1586,10 @@
          proxy.$modal.msgError("请为所有审批节点选择审批人!");
          return;
        }
        const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
        const approveUserIds = approverNodes.value
          .map(node => node.userId)
          .join(",");
        if (productData.value.length > 0) {
          // æ–°å¢žæ—¶ï¼Œéœ€è¦ä»Žæ¯ä¸ªäº§å“å¯¹è±¡ä¸­åˆ é™¤ id å­—段
          let processedProductData = productData.value;
@@ -1438,7 +1618,10 @@
        }
        // æ–°å¢žæ—¶ä¸ä¼ é€’id
        const submitData = { ...form.value };
        const submitData = {
          ...form.value,
          purchaseType: currentSupplierType.value,
        };
        if (operationType.value === "add") {
          delete submitData.id;
        }
@@ -1463,20 +1646,39 @@
  const openProductForm = async (type, row, index) => {
    productOperationType.value = type;
    productOperationIndex.value = index;
    productForm.value = {};
    productForm.value = {
      productId: "",
      productCategory: "",
      productModelId: "",
      specificationModel: "",
      unit: "",
      quantity: "",
      // å¯¹å…¬å­—段
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
      // å¯¹ç§å­—段
      unitPrice: "",
      totalPrice: "",
      // å…¬å…±å­—段
      warnNum: "",
      isChecked: true,
    };
    proxy.resetForm("productFormRef");
    productFormVisible.value = true;
    // å…ˆèŽ·å–äº§å“é€‰é¡¹ï¼Œç¡®ä¿æ•°æ®åŠ è½½å®Œæˆ
    await getProductOptions();
    // ç­‰å¾… DOM æ›´æ–°
    await nextTick();
    if (type === "edit") {
      // å¤åˆ¶è¡Œæ•°æ®
      productForm.value = { ...row };
      // å¦‚果是从模板加载的数据,可能没有 productId å’Œ productModelId
      // éœ€è¦æ ¹æ® productCategory å’Œ specificationModel æ¥æŸ¥æ‰¾å¯¹åº”çš„ ID
      if (!productForm.value.productId && productForm.value.productCategory) {
@@ -1487,25 +1689,34 @@
              return nodes[i].value;
            }
            if (nodes[i].children && nodes[i].children.length > 0) {
              const found = findProductIdByCategory(nodes[i].children, categoryName);
              const found = findProductIdByCategory(
                nodes[i].children,
                categoryName
              );
              if (found) return found;
            }
          }
          return null;
        };
        const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
        const productId = findProductIdByCategory(
          productOptions.value,
          productForm.value.productCategory
        );
        if (productId) {
          productForm.value.productId = productId;
          // èŽ·å–åž‹å·åˆ—è¡¨å¹¶ç­‰å¾…å®Œæˆ
          const modelRes = await modelList({ id: productId });
          modelOptions.value = modelRes;
          // ç­‰å¾… DOM æ›´æ–°
          await nextTick();
          // æ ¹æ® specificationModel æŸ¥æ‰¾ productModelId
          if (productForm.value.specificationModel && modelOptions.value.length > 0) {
          if (
            productForm.value.specificationModel &&
            modelOptions.value.length > 0
          ) {
            const modelItem = modelOptions.value.find(
              item => item.model === productForm.value.specificationModel
            );
@@ -1519,15 +1730,15 @@
      } else if (productForm.value.productId) {
        // å¦‚果有 productId,正常加载型号列表
        await getModels(productForm.value.productId);
        // ç­‰å¾… DOM æ›´æ–°
        await nextTick();
        if (productForm.value.productModelId) {
          getProductModel(productForm.value.productModelId);
        }
      }
      // æœ€åŽå†ç­‰å¾…一次 DOM æ›´æ–°ï¼Œç¡®ä¿æ‰€æœ‰æ•°æ®éƒ½å·²è®¾ç½®
      await nextTick();
    }
@@ -1611,15 +1822,21 @@
    });
  };
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 2;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
        productData.value = res.productData;
      });
    });
    // ç›´æŽ¥ä¿®æ”¹æœ¬åœ°æ•°æ®ï¼Œä¸å†è°ƒç”¨æŽ¥å£
    if (productOperationType.value === "add") {
      // æ–°å¢žäº§å“
      productData.value.push({ ...productForm.value });
    } else if (productOperationType.value === "edit") {
      // ç¼–辑产品
      if (
        productOperationIndex.value !== "" &&
        productOperationIndex.value !== null
      ) {
        productData.value[productOperationIndex.value] = { ...productForm.value };
      }
    }
    proxy.$modal.msgSuccess("操作成功");
    closeProductDia();
  };
  // åˆ é™¤äº§å“
  const deleteProduct = () => {
@@ -1627,40 +1844,27 @@
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    if (operationType.value === "add") {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(
          product => product.id === selectedRow.id
        );
        if (index !== -1) {
          productData.value.splice(index, 1);
        }
      });
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getPurchaseById({ id: currentId.value, type: 2 }).then(
              res => {
                productData.value = res.productData;
              }
            );
          });
        })
        .catch(() => {
          proxy.$modal.msg("已取消");
    // ç›´æŽ¥ä»Žæœ¬åœ°æ•°æ®ä¸­åˆ é™¤ï¼Œä¸å†è°ƒç”¨æŽ¥å£
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        productSelectedRows.value.forEach(selectedRow => {
          const index = productData.value.findIndex(
            product => product.id === selectedRow.id
          );
          if (index !== -1) {
            productData.value.splice(index, 1);
          }
        });
    }
        proxy.$modal.msgSuccess("删除成功");
        closeProductDia();
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // å…³é—­äº§å“å¼¹æ¡†
  const closeProductDia = () => {
@@ -1826,6 +2030,27 @@
      }
    }
  };
  // å¯¹ç§ä¾›åº”商的价格计算
  const mathNumPrivate = () => {
    const { unitPrice, quantity } = productForm.value;
    if (unitPrice && quantity) {
      productForm.value.totalPrice = (unitPrice * quantity).toFixed(2);
    }
  };
  // å¯¹ç§ä¾›åº”商的反向价格计算
  const reverseMathNumPrivate = type => {
    const { unitPrice, quantity, totalPrice } = productForm.value;
    if (type === "totalPrice") {
      if (quantity) {
        productForm.value.unitPrice = (totalPrice / quantity).toFixed(2);
      } else if (unitPrice) {
        productForm.value.quantity = (totalPrice / unitPrice).toFixed(2);
      }
    }
  };
  // é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
  const salesLedgerChange = async row => {
    console.log("row", row);
@@ -1855,19 +2080,26 @@
  // èŽ·å–æ¨¡æ¿ä¿¡æ¯
  const getTemplateList = async () => {
    let res = await getPurchaseTemplateList();
    if (res && res.code === 200 && Array.isArray(res.data)) {
      templateList.value = res.data;
    console.log("currentSupplierType.value", currentSupplierType.value);
    if (currentSupplierType.value) {
      let res = await getPurchaseTemplateList({
        templateType: currentSupplierType.value,
      });
      if (res && res.code === 200 && Array.isArray(res.data)) {
        templateList.value = res.data;
      }
    } else {
      templateList.value = [];
    }
  };
  // åˆ é™¤æ¨¡æ¿
  const handleDeleteTemplate = async (item) => {
  const handleDeleteTemplate = async item => {
    if (!item.id) {
      proxy.$modal.msgWarning("无法删除该模板");
      return;
    }
    try {
      await ElMessageBox.confirm(
        `确定要删除模板"${item.templateName}"吗?`,
@@ -1878,7 +2110,7 @@
          type: "warning",
        }
      );
      const res = await delPurchaseTemplate([item.id]);
      if (res && res.code === 200) {
        ElMessage({
@@ -1931,7 +2163,7 @@
    display: flex;
    align-items: center;
  }
  // å®¡æ‰¹äººèŠ‚ç‚¹å®¹å™¨æ ·å¼
  .approver-nodes-container {
    display: flex;
@@ -1942,7 +2174,7 @@
    border-radius: 4px;
    border: 1px solid #e4e7ed;
  }
  .approver-node-item {
    flex: 0 0 calc(33.333% - 12px);
    min-width: 200px;
@@ -1951,38 +2183,38 @@
    border-radius: 4px;
    border: 1px solid #dcdfe6;
    transition: all 0.3s;
    &:hover {
      border-color: #409eff;
      box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
    }
  }
  .approver-node-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
  }
  .approver-node-label {
    font-size: 13px;
    font-weight: 500;
    color: #606266;
  }
  @media (max-width: 1200px) {
    .approver-node-item {
      flex: 0 0 calc(50% - 8px);
    }
  }
  @media (max-width: 768px) {
    .approver-node-item {
      flex: 0 0 100%;
    }
  }
  // åˆ é™¤å›¾æ ‡æ ·å¼
  .delete-icon {
    transition: all 0.3s;
src/views/productionManagement/productionCosting/index.vue
@@ -1,371 +1,387 @@
<template>
    <div class="app-container">
        <el-row :gutter="16" class="content-row">
            <!-- å·¦ä¾§å°è´¦ + é¡¶éƒ¨ç­›é€‰ -->
            <el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="left-col">
                <div class="left-panel">
                <div class="left-header">
          <el-form :model="searchForm" inline>
            <el-form-item prop="dateType">
              <el-radio-group v-model="searchForm.dateType" size="small" @change="handleDateTypeChange">
                <el-radio-button label="day">日</el-radio-button>
                <el-radio-button label="month">月</el-radio-button>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="日期:" prop="dateRange">
              <el-date-picker
                  v-model="searchForm.dateRange"
                  :type="searchForm.dateType === 'day' ? 'date' : 'daterange'"
                  range-separator="至"
                  start-placeholder="开始日期"
                  end-placeholder="结束日期"
                  format="YYYY-MM-DD"
                  value-format="YYYY-MM-DD"
                  style="width: 200px"
                  @change="handleDateRangeChange"
              />
  <div class="app-container">
    <el-row :gutter="16"
            class="content-row">
      <!-- å·¦ä¾§å°è´¦ + é¡¶éƒ¨ç­›é€‰ -->
      <el-col :xs="24"
              :sm="24"
              :md="24"
              :lg="8"
              :xl="8"
              class="left-col">
        <div class="left-panel">
          <div class="left-header">
            <el-form :model="searchForm"
                     inline>
              <el-form-item prop="dateType">
                <el-radio-group v-model="searchForm.dateType"
                                size="small"
                                @change="handleDateTypeChange">
                  <el-radio-button label="day">日</el-radio-button>
                  <el-radio-button label="month">月</el-radio-button>
                </el-radio-group>
              </el-form-item>
              <el-form-item label="日期:"
                            prop="dateRange">
                <el-date-picker v-model="searchForm.dateRange"
                                :type="searchForm.dateType === 'day' ? 'date' : 'daterange'"
                                range-separator="至"
                                start-placeholder="开始日期"
                                end-placeholder="结束日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 200px"
                                @change="handleDateRangeChange" />
              </el-form-item>
            </el-form>
          </div>
          <PIMTable rowKey="id"
                    :column="leftTableColumn"
                    :tableData="leftTableData"
                    :tableLoading="tableLoading"
                    :page="page"
                    @row-click="handleLeftRowClick"
                    @pagination="pagination"></PIMTable>
        </div>
      </el-col>
      <!-- å³ä¾§æ˜Žç»† -->
      <el-col :xs="24"
              :sm="24"
              :md="24"
              :lg="16"
              :xl="16"
              class="right-col">
        <div class="right-panel">
          <el-form inline>
            <el-form-item>
              <el-button type="primary"
                         @click="handleOut">导出</el-button>
            </el-form-item>
          </el-form>
                </div>
                <PIMTable
                    rowKey="id"
                    :column="leftTableColumn"
                    :tableData="leftTableData"
                    :tableLoading="tableLoading"
          :page="page"
          @row-click="handleLeftRowClick"
          @pagination="pagination"
        ></PIMTable>
                </div>
            </el-col>
            <!-- å³ä¾§æ˜Žç»† -->
            <el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="right-col">
                <div class="right-panel">
                    <el-form inline>
                        <el-form-item>
                            <el-button type="primary" @click="handleOut">导出</el-button>
                        </el-form-item>
                    </el-form>
                    <PIMTable
                        rowKey="id"
                        :column="tableColumn"
                        :tableData="tableData"
                        :page="page1"
                        :tableLoading="tableLoading1"
                        style="margin-right: 20px;"
                        @pagination="pagination1"
                    ></PIMTable>
                </div>
            </el-col>
        </el-row>
    </div>
          <PIMTable rowKey="id"
                    :column="tableColumn"
                    :tableData="tableData"
                    :page="page1"
                    :tableLoading="tableLoading1"
                    style="margin-right: 20px;"
                    @pagination="pagination1"></PIMTable>
        </div>
      </el-col>
    </el-row>
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import { ElMessageBox } from "element-plus";
import dayjs from "dayjs";
import {salesLedgerProductionAccountingListProductionDetails, salesLedgerProductionAccountingList} from "@/api/productionManagement/productionCosting.js";
const { proxy } = getCurrentInstance();
  import { onMounted, ref } from "vue";
  import { ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import {
    salesLedgerProductionAccountingListProductionDetails,
    salesLedgerProductionAccountingList,
  } from "@/api/productionManagement/productionCosting.js";
  const { proxy } = getCurrentInstance();
const tableColumn = ref([
    {
        label: "生产日期",
        prop: "schedulingDate",
    minWidth: 100,
    },
    {
        label: "生产人",
        prop: "schedulingUserName",
    minWidth: 100,
    },
    {
        label: "合同号",
        prop: "salesContractNo",
    minWidth: 100,
    },
    {
        label: "客户名称",
        prop: "customerName",
    minWidth: 100,
    },
    {
        label: "产品大类",
        prop: "productName",
    minWidth: 100,
    },
    {
        label: "规格型号",
        prop: "productModelName",
    minWidth: 100,
    },
    {
        label: "单位",
        prop: "unit",
    minWidth: 100,
    },
    {
        label: "工序",
        prop: "process",
    minWidth: 100,
    },
    {
        label: "生产数量",
        prop: "quantity",
    minWidth: 100,
    },
    {
        label: "工时定额",
        prop: "workHours",
    minWidth: 100,
    },
    {
        label: "工资",
        prop: "wages",
    minWidth: 100,
    },
]);
// å·¦ä¾§æ±‡æ€»å°è´¦åˆ—(生产人、产量、工资、合格率)
const leftTableColumn = ref([
    {
        label: "生产人",
        prop: "schedulingUserName",
    minWidth: 100,
    },
    {
        label: "产量",
        prop: "outputNum",
    minWidth: 100,
  },
    {
        label: "工资",
        prop: "wages",
    minWidth: 100,
    },
    {
        label: "合格率",
        prop: "outputRate",
    minWidth: 100,
    formatData: (val) => {
      if (val == null || val === '') return '-'
      return parseFloat(val).toFixed(2)
  const tableColumn = ref([
    {
      label: "生产日期",
      prop: "schedulingDate",
      minWidth: 100,
    },
    },
]);
    {
      label: "生产人",
      prop: "schedulingUserName",
      minWidth: 100,
    },
    // {
    //     label: "合同号",
    //     prop: "salesContractNo",
    //   minWidth: 100,
    // },
    // {
    //     label: "客户名称",
    //     prop: "customerName",
    //   minWidth: 100,
    // },
    {
      label: "产品大类",
      prop: "productName",
      minWidth: 100,
    },
    {
      label: "规格型号",
      prop: "productModelName",
      minWidth: 100,
    },
    {
      label: "单位",
      prop: "unit",
      minWidth: 100,
    },
    {
      label: "工序",
      prop: "process",
      minWidth: 100,
    },
    {
      label: "生产数量",
      prop: "quantity",
      minWidth: 100,
    },
    {
      label: "工时定额",
      prop: "workHours",
      minWidth: 100,
    },
    {
      label: "工资",
      prop: "wages",
      minWidth: 100,
    },
  ]);
const tableData = ref([]);
const tableLoading = ref(false);
const tableLoading1 = ref(false);
const leftTableData = ref([]);
// æ—¥ / æœˆ åˆ‡æ¢ï¼ˆé»˜è®¤æŒ‰æ—¥ï¼‰
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
  // å·¦ä¾§æ±‡æ€»å°è´¦åˆ—(生产人、产量、工资、合格率)
  const leftTableColumn = ref([
    {
      label: "生产人",
      prop: "schedulingUserName",
      minWidth: 100,
    },
    {
      label: "产量",
      prop: "outputNum",
      minWidth: 100,
    },
    {
      label: "工资",
      prop: "wages",
      minWidth: 100,
    },
    {
      label: "合格率",
      prop: "outputRate",
      minWidth: 100,
      formatData: val => {
        if (val == null || val === "") return "-";
        return parseFloat(val).toFixed(2);
      },
    },
  ]);
const page1 = reactive({
  current: 1,
  size: 100,
  total: 0,
});
  const tableData = ref([]);
  const tableLoading = ref(false);
  const tableLoading1 = ref(false);
  const leftTableData = ref([]);
  // æ—¥ / æœˆ åˆ‡æ¢ï¼ˆé»˜è®¤æŒ‰æ—¥ï¼‰
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
const data = reactive({
    searchForm: {
        schedulingUserName: "",
        salesContractNo: "",
    dateType: "day",
    dateRange: dayjs().format("YYYY-MM-DD"),
        entryDate: dayjs().format("YYYY-MM-DD"),
        entryDateStart: undefined,
        entryDateEnd: undefined,
    },
});
const { searchForm } = toRefs(data);
  const page1 = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
  const data = reactive({
    searchForm: {
      schedulingUserName: "",
      salesContractNo: "",
      dateType: "day",
      dateRange: dayjs().format("YYYY-MM-DD"),
      entryDate: dayjs().format("YYYY-MM-DD"),
      entryDateStart: undefined,
      entryDateEnd: undefined,
    },
  });
  const { searchForm } = toRefs(data);
const pagination1 = (obj) => {
  page1.current = obj.page;
  page1.size = obj.limit;
    getList1();
};
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
const handleDateRangeChange = (value) => {
    if (value) {
    if (searchForm.value.dateType === "day") {
      searchForm.value.entryDate = value;
  const pagination1 = obj => {
    page1.current = obj.page;
    page1.size = obj.limit;
    getList1();
  };
  const handleDateRangeChange = value => {
    if (value) {
      if (searchForm.value.dateType === "day") {
        searchForm.value.entryDate = value;
      } else {
        searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
        searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
      }
    } else {
      searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
      searchForm.value.entryDate = undefined;
      searchForm.value.entryDateStart = undefined;
      searchForm.value.entryDateEnd = undefined;
    }
    reloadData();
  };
  const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    salesLedgerProductionAccountingList(params)
      .then(res => {
        const records = res.data.records || [];
        leftTableData.value = records;
        page.total = res.data.total || 0;
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const getList1 = () => {
    tableLoading1.value = true;
    const params = { ...page1, ...searchForm.value };
    salesLedgerProductionAccountingListProductionDetails(params)
      .then(res => {
        tableData.value = res.data.records || [];
        page1.total = res.data.total || 0;
      })
      .finally(() => {
        tableLoading1.value = false;
      });
  };
  // æž„建左侧汇总台账(按生产人汇总产量、工资等)
  const buildLeftTableData = records => {
    const map = {};
    records.forEach(item => {
      const key = item.schedulingUserName || "未知";
      if (!map[key]) {
        map[key] = {
          id: key,
          schedulingUserName: key,
          finishedNum: 0,
          wages: 0,
          qualifiedRate: item.qualifiedRate ?? null,
        };
      }
      map[key].finishedNum += Number(item.finishedNum || 0);
      map[key].wages += Number(item.wages || 0);
      if (item.qualifiedRate != null) {
        map[key].qualifiedRate = item.qualifiedRate;
      }
    });
    leftTableData.value = Object.values(map);
  };
  // å·¦ä¾§æ—¥/月切换
  const handleDateTypeChange = value => {
    // è¿™é‡Œåªä½œä¸ºç­›é€‰æ¡ä»¶çš„一部分,直接重新查询列表
    if (value === "day") {
      searchForm.value.entryDate = dayjs().format("YYYY-MM-DD");
      searchForm.value.dateRange = searchForm.value.entryDate;
    } else {
      searchForm.value.entryDateStart = dayjs()
        .startOf("month")
        .format("YYYY-MM-DD");
      searchForm.value.entryDateEnd = dayjs().endOf("month").format("YYYY-MM-DD");
      searchForm.value.dateRange = [
        searchForm.value.entryDateStart,
        searchForm.value.entryDateEnd,
      ];
    }
    } else {
        searchForm.value.entryDate = undefined;
        searchForm.value.entryDateStart = undefined;
        searchForm.value.entryDateEnd = undefined;
    }
  reloadData()
};
    reloadData();
  };
const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
  const reloadData = () => {
    page.current = 1;
    page1.current = 1;
    getList();
    tableData.value = [];
  };
  salesLedgerProductionAccountingList(params).then((res) => {
        const records = res.data.records || [];
    leftTableData.value = records;
        page.total = res.data.total || 0;
    }).finally(() => {
    tableLoading.value = false;
  })
  // ç‚¹å‡»å·¦ä¾§è¡Œï¼Œåˆ·å³ä¾§æ˜Žç»†ï¼ˆæŒ‰ç”Ÿäº§äººè¿‡æ»¤ï¼‰
  const handleLeftRowClick = row => {
    searchForm.value.schedulingUserName = row.schedulingUserName || "";
    handleQuery();
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page1.current = 1;
    getList1();
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download(
          "/salesLedger/productionAccounting/export",
          {},
          "生产核算.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
};
const getList1 = () => {
  tableLoading1.value = true;
  const params = { ...page1, ...searchForm.value };
  salesLedgerProductionAccountingListProductionDetails(params).then((res) => {
    tableData.value = res.data.records || [];;
    page1.total = res.data.total || 0;
  }).finally(() => {
    tableLoading1.value = false;
  })
};
// æž„建左侧汇总台账(按生产人汇总产量、工资等)
const buildLeftTableData = (records) => {
    const map = {};
    records.forEach((item) => {
        const key = item.schedulingUserName || "未知";
        if (!map[key]) {
            map[key] = {
                id: key,
                schedulingUserName: key,
                finishedNum: 0,
                wages: 0,
                qualifiedRate: item.qualifiedRate ?? null,
            };
        }
        map[key].finishedNum += Number(item.finishedNum || 0);
        map[key].wages += Number(item.wages || 0);
        if (item.qualifiedRate != null) {
            map[key].qualifiedRate = item.qualifiedRate;
        }
    });
    leftTableData.value = Object.values(map);
};
// å·¦ä¾§æ—¥/月切换
const handleDateTypeChange = (value) => {
    // è¿™é‡Œåªä½œä¸ºç­›é€‰æ¡ä»¶çš„一部分,直接重新查询列表
  if (value === "day") {
    searchForm.value.entryDate = dayjs().format("YYYY-MM-DD");
    searchForm.value.dateRange = searchForm.value.entryDate
  } else {
    searchForm.value.entryDateStart = dayjs().startOf("month").format("YYYY-MM-DD");
    searchForm.value.entryDateEnd = dayjs().endOf("month").format("YYYY-MM-DD");
    searchForm.value.dateRange = [searchForm.value.entryDateStart, searchForm.value.entryDateEnd]
  }
  reloadData()
};
const reloadData = () => {
  page.current = 1;
  page1.current = 1;
  getList();
  tableData.value = []
}
// ç‚¹å‡»å·¦ä¾§è¡Œï¼Œåˆ·å³ä¾§æ˜Žç»†ï¼ˆæŒ‰ç”Ÿäº§äººè¿‡æ»¤ï¼‰
const handleLeftRowClick = (row) => {
    searchForm.value.schedulingUserName = row.schedulingUserName || "";
    handleQuery();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page1.current = 1;
  getList1();
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/salesLedger/productionAccounting/export", {}, "生产核算.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
onMounted(() => {
    getList();
});
  onMounted(() => {
    getList();
  });
</script>
<style scoped lang="scss">
.content-row {
  width: 100%;
}
  .content-row {
    width: 100%;
  }
.content-row .left-col,
.content-row .right-col {
  margin-bottom: 16px;
}
  .content-row .left-col,
  .content-row .right-col {
    margin-bottom: 16px;
  }
.left-panel,
.right-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
  .left-panel,
  .right-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
  }
.left-header {
  display: flex;
  align-items: center;
  gap: 12px;
}
  .left-header {
    display: flex;
    align-items: center;
    gap: 12px;
  }
.left-title {
  font-size: 16px;
  color: #ffffff;
}
  .left-title {
    font-size: 16px;
    color: #ffffff;
  }
.header-filters {
  display: flex;
  align-items: center;
  flex: 1;
  justify-content: flex-end;
  gap: 8px;
}
  .header-filters {
    display: flex;
    align-items: center;
    flex: 1;
    justify-content: flex-end;
    gap: 8px;
  }
.search_title {
  color: #ffffff;
}
  .search_title {
    color: #ffffff;
  }
.ml10 {
  margin-left: 10px;
}
  .ml10 {
    margin-left: 10px;
  }
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -11,14 +11,14 @@
                    style="width: 160px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="合同号:">
        <!-- <el-form-item label="合同号:">
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        </el-form-item>
        </el-form-item> -->
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productCategory"
                    placeholder="请输入"
@@ -41,8 +41,10 @@
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary" @click="isShowNewModal = true">新增</el-button>
        <el-button type="danger" @click="handleDelete">删除</el-button>
        <el-button type="primary"
                   @click="isShowNewModal = true">新增</el-button>
        <el-button type="danger"
                   @click="handleDelete">删除</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
@@ -57,11 +59,9 @@
                @selection-change="handleSelectionChange"
                @pagination="pagination">
        <template #completionStatus="{ row }">
          <el-progress
            :percentage="toProgressPercentage(row?.completionStatus)"
            :color="progressColor(toProgressPercentage(row?.completionStatus))"
            :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
          />
          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
      </PIMTable>
    </div>
@@ -90,10 +90,9 @@
        </span>
      </template>
    </el-dialog>
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         @completed="handleQuery" />
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery" />
  </div>
</template>
@@ -106,12 +105,15 @@
    productOrderListPage,
    listProcessRoute,
    bindingRoute,
    listProcessBom, delProductOrder,
    listProcessBom,
    delProductOrder,
  } from "@/api/productionManagement/productionOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
  import { fileDel } from "@/api/financialManagement/revenueManagement.js";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
  const NewProductOrder = defineAsyncComponent(() =>
    import("@/views/productionManagement/productionOrder/New.vue")
  );
  const { proxy } = getCurrentInstance();
@@ -122,32 +124,32 @@
    {
      label: "生产订单号",
      prop: "npsNo",
      width: '120px',
      width: "120px",
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: '150px',
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: '200px',
    },
    // {
    //   label: "销售合同号",
    //   prop: "salesContractNo",
    //   width: "150px",
    // },
    // {
    //   label: "客户名称",
    //   prop: "customerName",
    //   width: "200px",
    // },
    {
      label: "产品名称",
      prop: "productCategory",
      width: '120px',
      width: "120px",
    },
    {
      label: "规格",
      prop: "specificationModel",
      width: '120px',
      width: "120px",
    },
    {
      label: "工艺路线编号",
      prop: "processRouteCode",
      width: '200px',
      width: "200px",
    },
    {
      label: "需求数量",
@@ -176,12 +178,12 @@
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    {
      label: "交付日期",
      prop: "deliveryDate",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    // {
    //   label: "交付日期",
    //   prop: "deliveryDate",
    //   formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    //   width: 120,
    // },
    {
      dataType: "action",
      label: "操作",
@@ -253,18 +255,18 @@
  // æ·»åŠ è¡¨è¡Œç±»åæ–¹æ³•
  const tableRowClassName = ({ row }) => {
    if (!row.deliveryDate) return '';
    if (row.isFh) return '';
    if (!row.deliveryDate) return "";
    if (row.isFh) return "";
    const diff = row.deliveryDaysDiff;
    if (diff === 15) {
      return 'yellow';
      return "yellow";
    } else if (diff === 10) {
      return 'pink';
      return "pink";
    } else if (diff === 2) {
      return 'purple';
      return "purple";
    } else if (diff < 2) {
      return 'red';
      return "red";
    }
  };
@@ -402,14 +404,14 @@
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
@@ -418,14 +420,16 @@
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      delProductOrder(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
    })
      .then(() => {
        delProductOrder(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
    }).catch(() => {
      proxy.$modal.msg("已取消");
    });
  };
  // å¯¼å‡º
@@ -436,7 +440,11 @@
      type: "warning",
    })
      .then(() => {
        proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
        proxy.download(
          "/productOrder/export",
          { ...searchForm.value },
          "生产订单.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -451,23 +459,23 @@
</script>
<style scoped lang="scss">
.search_form{
  align-items: start;
}
  .search_form {
    align-items: start;
  }
::v-deep .yellow {
  background-color: #FAF0DE;
}
  ::v-deep .yellow {
    background-color: #faf0de;
  }
::v-deep .pink {
  background-color: #FAE1DE;
}
  ::v-deep .pink {
    background-color: #fae1de;
  }
::v-deep .red {
  background-color: #f80202;
}
  ::v-deep .red {
    background-color: #f80202;
  }
::v-deep .purple{
  background-color: #F4DEFA;
}
  ::v-deep .purple {
    background-color: #f4defa;
  }
</style>
src/views/productionManagement/productionReporting/index.vue
@@ -99,8 +99,7 @@
                                style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="操作"
                             >
            <el-table-column label="操作">
              <template #default="scope">
                <el-button link
                           type="primary"
@@ -172,11 +171,11 @@
      prop: "workOrderNo",
      width: 120,
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: 120,
    },
    // {
    //   label: "销售合同号",
    //   prop: "salesContractNo",
    //   width: 120,
    // },
    {
      label: "产品名称",
      prop: "productName",
@@ -202,7 +201,7 @@
      prop: "unit",
      width: 120,
    },
    {
      label: "创建时间",
      prop: "createTime",
src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -142,6 +142,8 @@
          :data="salesRecord"
          border
          :row-key="(row) => row.id"
          show-summary
          :summary-method="summarizeSalesTable"
          height="calc(100vh - 18.5em)"
        >
          <el-table-column align="center" label="序号" type="index" width="60" />
@@ -247,6 +249,18 @@
  return summarizeTable;
};
// é”€å”®å¾€æ¥è¡¨åˆè®¡ï¼ˆå·²å‘è´§/未发货)
const summarizeSalesTable = (param) => {
  return proxy.summarizeTable(
    param,
    ["shippedQuantity", "unshippedQuantity"],
    {
      shippedQuantity: { noDecimal: false },
      unshippedQuantity: { noDecimal: false },
    }
  );
};
const receiptPaymentList = (id) => {
  const param = {
    customerId: id,
src/views/salesManagement/salesLedger/index.vue
ÎļþÌ«´ó