yyb
4 天以前 5cc12cd3646f5f4dee4b1eab7652b106c4f5c29b
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-management into dev_HXSJ
已修改3个文件
828 ■■■■ 文件已修改
src/views/basicData/customerFile/index.vue 507 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/advancedPriceManagement/index.vue 189 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue
@@ -3,212 +3,211 @@
    <div class="search_form">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input
          v-model="searchForm.customerName"
        <el-input v-model="searchForm.customerName"
          style="width: 240px;margin-right: 10px"
          placeholder="请输入"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
                  :prefix-icon="Search" />
         <span class="search_title">客户分类:</span>
         <el-select
          v-model="searchForm.customerType"
        <el-select v-model="searchForm.customerType"
          placeholder="请选择"
          style="width: 240px"
          clearable
           @change="handleQuery"
        >
           <el-option  label="零售客户" value="零售客户" />
           <el-option  label="进销商客户" value="进销商客户" />
                   @change="handleQuery">
          <el-option label="零售客户"
                     value="零售客户" />
          <el-option label="经销商客户"
                     value="经销商客户" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
          >搜索</el-button
        >
        <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"
      <PIMTable rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
      ></PIMTable>
                @pagination="pagination"></PIMTable>
    </div>
    <el-dialog
      v-model="dialogFormVisible"
    <el-dialog v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增客户信息' : '编辑客户信息'"
      width="70%"
      @close="closeDia"
    >
      <el-form
        :model="form"
               @close="closeDia">
      <el-form :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerName">
              <el-input
                v-model="form.customerName"
            <el-form-item label="客户名称:"
                          prop="customerName">
              <el-input v-model="form.customerName"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
              label="纳税人识别号:"
              prop="taxpayerIdentificationNumber"
            >
              <el-input
                v-model="form.taxpayerIdentificationNumber"
            <el-form-item label="纳税人识别号:"
                          prop="taxpayerIdentificationNumber">
              <el-input v-model="form.taxpayerIdentificationNumber"
                placeholder="请输入"
                clearable
              />
                        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"
            <el-form-item label="公司地址:"
                          prop="companyAddress">
              <el-input v-model="form.companyAddress"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:" prop="companyPhone">
              <el-input
                v-model="form.companyPhone"
            <el-form-item label="公司电话:"
                          prop="companyPhone">
              <el-input v-model="form.companyPhone"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="银行基本户:" prop="basicBankAccount">
              <el-input
                v-model="form.basicBankAccount"
            <el-form-item label="银行基本户:"
                          prop="basicBankAccount">
              <el-input v-model="form.basicBankAccount"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号:" prop="bankAccount">
              <el-input
                v-model="form.bankAccount"
            <el-form-item label="银行账号:"
                          prop="bankAccount">
              <el-input v-model="form.bankAccount"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行号:" prop="bankCode">
              <el-input
                v-model="form.bankCode"
            <el-form-item label="开户行号:"
                          prop="bankCode">
              <el-input v-model="form.bankCode"
                placeholder="请输入"
                clearable
              />
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户分类:" prop="customerType">
               <el-select  v-model="form.customerType" placeholder="请选择" clearable>
                 <el-option  label="零售客户" value="零售客户" />
                 <el-option  label="进销商客户" value="进销商客户" />
            <el-form-item label="客户分类:"
                          prop="customerType">
              <el-select v-model="form.customerType"
                         placeholder="请选择"
                         clearable>
                <el-option label="零售客户"
                           value="零售客户" />
                <el-option label="经销商客户"
                           value="经销商客户" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30" v-for="(contact, index) in formYYs.contactList" :key="index">
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
                    <el-col :span="12">
                        <el-form-item label="联系人:" prop="contactPerson">
                            <el-input v-model="contact.contactPerson" placeholder="请输入" clearable  />
            <el-form-item label="联系人:"
                          prop="contactPerson">
              <el-input v-model="contact.contactPerson"
                        placeholder="请输入"
                        clearable />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="联系电话:" prop="contactPhone">
            <el-form-item label="联系电话:"
                          prop="contactPhone">
                            <div style="display: flex; align-items: center;width: 100%;">
                                <el-input v-model="contact.contactPhone" placeholder="请输入" clearable />
                                <el-button   @click="removeContact(index)" type="danger" circle style="margin-left: 5px;">
                                    <el-icon><Close /></el-icon>
                <el-input v-model="contact.contactPhone"
                          placeholder="请输入"
                          clearable />
                <el-button @click="removeContact(index)"
                           type="danger"
                           circle
                           style="margin-left: 5px;">
                  <el-icon>
                    <Close />
                  </el-icon>
                                </el-button>
                            </div>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-button @click="addNewContact" style="margin-bottom: 10px;">+ 新增联系人</el-button>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ 新增联系人</el-button>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:" prop="maintainer">
              <el-select
                v-model="form.maintainer"
            <el-form-item label="维护人:"
                          prop="maintainer">
              <el-select v-model="form.maintainer"
                placeholder="请选择"
                clearable
                disabled
              >
                <el-option
                  v-for="item in userList"
                         disabled>
                <el-option v-for="item in userList"
                  :key="item.nickName"
                  :label="item.nickName"
                  :value="item.nickName"
                />
                           :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="维护时间:" prop="maintenanceTime">
              <el-date-picker
                style="width: 100%"
            <el-form-item label="维护时间:"
                          prop="maintenanceTime">
              <el-date-picker style="width: 100%"
                v-model="form.maintenanceTime"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
              />
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 用户导入对话框 -->
    <el-dialog
      :title="upload.title"
    <el-dialog :title="upload.title"
      v-model="upload.open"
      width="400px"
      append-to-body
    >
      <el-upload
        ref="uploadRef"
               append-to-body>
      <el-upload ref="uploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
@@ -220,27 +219,75 @@
        :on-error="upload.onError"
        :on-change="upload.onChange"
        :auto-upload="false"
        drag
      >
                 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"
            <el-link type="primary"
              :underline="false"
              style="font-size: 12px; vertical-align: baseline"
              @click="importTemplate"
              >下载模板</el-link
            >
                     @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>
    </el-dialog>
    <!-- 客户画像弹窗 -->
    <el-dialog v-model="profileDialogVisible"
               title="客户画像"
               width="70%"
               @close="closeProfileDialog">
      <div class="profile-content">
        <div class="profile-info">
          <h3 class="profile-title">购买习惯</h3>
          <el-descriptions :column="2"
                           border>
            <el-descriptions-item label="客户名称">{{ currentCustomer.customerName || '-' }}</el-descriptions-item>
            <el-descriptions-item label="购买次数">{{ purchaseHabits.purchaseCount || 0 }}</el-descriptions-item>
            <el-descriptions-item label="平均金额"><span style="color:#ff6f00">{{ purchaseHabits.averageAmount || 0 }}</span> 元</el-descriptions-item>
            <el-descriptions-item label="最近购买时间">{{ purchaseHabits.latestPurchaseTime || '-' }}</el-descriptions-item>
            <el-descriptions-item label="常购品">
              <el-tag v-for="item in purchaseHabits.topProducts"
                      style="margin-right: 10px;"
                      :key="item">{{ item }}</el-tag>
            </el-descriptions-item>
          </el-descriptions>
        </div>
        <div class="profile-form">
          <h3 class="profile-title">客户标签设置</h3>
          <el-form :model="profileForm"
                   label-width="120px">
            <el-form-item label="客户等级">
              <el-radio-group v-model="profileForm.isVip">
                <el-radio :label="true">VIP客户</el-radio>
                <el-radio :label="false">普通客户</el-radio>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="价格敏感">
              <el-checkbox v-model="profileForm.isPriceSensitive">价格敏感</el-checkbox>
            </el-form-item>
            <el-form-item label="客户偏好">
              <el-input v-model="profileForm.preferences"
                        type="textarea"
                        :rows="4"
                        placeholder="请输入客户偏好,如:喜欢高品质产品、注重售后服务等" />
            </el-form-item>
          </el-form>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="saveProfile">保存</el-button>
          <el-button @click="closeProfileDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
@@ -275,6 +322,7 @@
    prop: "customerName",
    width: 220,
  },
  {
    label: "纳税人识别码",
    prop: "taxpayerIdentificationNumber",
@@ -292,7 +340,7 @@
  {
    label: "联系电话",
    prop: "contactPhone",
    width:150
      width: 150,
  },
  {
    label: "银行基本户",
@@ -307,8 +355,9 @@
  {
    label: "开户行号",
    prop: "bankCode",
    width:220
      width: 220,
  },
  {
    label: "维护人",
    prop: "maintainer",
@@ -322,13 +371,25 @@
    dataType: "action",
    label: "操作",
    align: "center",
        fixed: 'right',
      width: 160,
      fixed: "right",
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("edit", row);
          },
        },
        {
          name: "客户画像",
          type: "text",
          color: "rgb(229 187 21)",
          showHide: row => {
            return row.customerType == "经销商客户";
          },
          clickFun: row => {
            openCustomerProfile(row);
        },
      },
    ],
@@ -348,13 +409,14 @@
// 用户信息表单弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const formYYs = ref({    // 其他字段...
  const formYYs = ref({
    // 其他字段...
  contactList: [
    {
      contactPerson: "",
      contactPhone: ""
    }
  ]
        contactPhone: "",
      },
    ],
});
const data = reactive({
  searchForm: {
@@ -406,10 +468,14 @@
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
  // 文件上传前的回调
  beforeUpload: (file) => {
    console.log('文件即将上传', file);
    beforeUpload: file => {
      console.log("文件即将上传", file);
    // 可以在此处做文件类型或大小校验
    const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
      const isValid =
        file.type ===
          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
        file.name.endsWith(".xlsx") ||
        file.name.endsWith(".xls");
    if (!isValid) {
      proxy.$modal.msgError("只能上传 Excel 文件");
    }
@@ -417,11 +483,11 @@
  },
  // 文件状态改变时的回调
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
      console.log("文件状态改变", file, fileList);
  },
  // 文件上传成功时的回调
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', response, file, fileList);
      console.log("上传成功", response, file, fileList);
    upload.isUploading = false;
    if(response.code === 200){
      proxy.$modal.msgSuccess("文件上传成功");
@@ -436,24 +502,24 @@
  },
  // 文件上传失败时的回调
  onError: (error, file, fileList) => {
    console.error('上传失败', error, file, fileList);
      console.error("上传失败", error, file, fileList);
    upload.isUploading = false;
    proxy.$modal.msgError("文件上传失败");
  },
  // 文件上传进度回调
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  }
      console.log("上传中...", event.percent);
    },
});
const { searchForm, form, rules } = toRefs(data);
const addNewContact = () => {
  formYYs.value.contactList.push({
    contactPerson: "",
    contactPhone: ""
      contactPhone: "",
  });
};
const removeContact = (index) => {
  const removeContact = index => {
  if (formYYs.value.contactList.length > 1) {
    formYYs.value.contactList.splice(index, 1);
  }
@@ -464,21 +530,21 @@
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  listCustomer({ ...searchForm.value, ...page }).then((res) => {
    listCustomer({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false;
    tableData.value = res.records;
    page.total = res.total;
  });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
/** 提交上传文件 */
@@ -503,30 +569,31 @@
  formYYs.value.contactList = [
    {
      contactPerson: "",
      contactPhone: ""
    }
        contactPhone: "",
      },
  ];
  form.value.maintenanceTime = getCurrentDate();
  userListNoPage().then((res) => {
    userListNoPage().then(res => {
    userList.value = res.data;
  });
  if (type === "edit") {
    getCustomer(row.id).then((res) => {
      getCustomer(row.id).then(res => {
      form.value = { ...res.data };
      formYYs.value.contactList = res.data.contactPerson.split(",").map((item, index) => {
        formYYs.value.contactList = res.data.contactPerson
          .split(",")
          .map((item, index) => {
        return {
          contactPerson: item,
          contactPhone: res.data.contactPhone.split(",")[index]
        }
              contactPhone: res.data.contactPhone.split(",")[index],
            };
      });
    });
  }
  dialogFormVisible.value = true;
};
// 提交表单
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      if (operationType.value === "edit") {
        submitEdit();
@@ -541,9 +608,13 @@
  if(formYYs.value.contactList.length < 1){
    return proxy.$modal.msgWarning("请至少添加一个联系人");
  }
  form.value.contactPerson = formYYs.value.contactList.map(item => item.contactPerson).join(",");
  form.value.contactPhone = formYYs.value.contactList.map(item => item.contactPhone).join(",");
  addCustomer(form.value).then((res) => {
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    addCustomer(form.value).then(res => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
@@ -551,9 +622,13 @@
};
// 提交修改
const submitEdit = () => {
    form.value.contactPerson = formYYs.value.contactList.map(item => item.contactPerson).join(",");
    form.value.contactPhone = formYYs.value.contactList.map(item => item.contactPhone).join(",");
  updateCustomer(form.value).then((res) => {
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    updateCustomer(form.value).then(res => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
@@ -583,12 +658,14 @@
  let ids = [];
  if (selectedRows.value.length > 0) {
    // 检查是否有他人维护的数据
    const unauthorizedData = selectedRows.value.filter(item => item.maintainer !== userStore.nickName);
      const unauthorizedData = selectedRows.value.filter(
        item => item.maintainer !== userStore.nickName
      );
    if (unauthorizedData.length > 0) {
      proxy.$modal.msgWarning("不可删除他人维护的数据");
      return;
    }
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -601,7 +678,7 @@
    .then(() => {
      tableLoading.value = true;
      delCustomer(ids)
        .then((res) => {
          .then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        })
@@ -623,9 +700,141 @@
  return `${year}-${month}-${day}`;
}
  // 客户画像相关
  const profileDialogVisible = ref(false);
  const currentCustomer = ref({});
  const purchaseHabits = ref({
    purchaseCount: 0,
    averageAmount: 1240,
    latestPurchaseTime: "",
    topProducts: [],
  });
  const profileForm = ref({
    isVip: false,
    isPriceSensitive: false,
    preferences: "",
  });
  // 打开客户画像弹窗
  const openCustomerProfile = row => {
    currentCustomer.value = { ...row };
    purchaseHabits.value = {
      purchaseCount: row.purchaseCount || 0,
      averageAmount: row.averageAmount || 0,
      latestPurchaseTime: row.latestPurchaseTime || "-",
      topProducts: row.topProducts || [],
    };
    profileForm.value = {
      isVip: row.isVip,
      isPriceSensitive: row.isPriceSensitive,
      preferences: row.preferences || "",
    };
    profileDialogVisible.value = true;
  };
  // 关闭客户画像弹窗
  const closeProfileDialog = () => {
    profileDialogVisible.value = false;
    currentCustomer.value = {};
  };
  // 保存客户画像
  const saveProfile = () => {
    const updateData = {
      id: currentCustomer.value.id,
      preferences: profileForm.value.preferences,
      isPriceSensitive: profileForm.value.isPriceSensitive,
      isVip: profileForm.value.isVip,
    };
    updateCustomer(updateData).then(res => {
      proxy.$modal.msgSuccess("客户画像保存成功");
      closeProfileDialog();
      getList();
    });
  };
onMounted(() => {
    getList();
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
  .profile-content {
    .profile-title {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
      margin: 20px 0 15px 0;
      padding-bottom: 10px;
      border-bottom: 2px solid #409eff;
      position: relative;
      &::before {
        content: "";
        position: absolute;
        left: 0;
        bottom: -2px;
        width: 60px;
        height: 2px;
        background-color: #409eff;
      }
    }
    .profile-info {
      margin-bottom: 30px;
      :deep(.el-descriptions) {
        border-radius: 8px;
        overflow: hidden;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
      }
      :deep(.el-descriptions__label) {
        background-color: #f5f7fa;
        font-weight: 500;
        color: #606266;
      }
      :deep(.el-descriptions__content) {
        color: #303133;
        font-weight: 500;
      }
    }
    .profile-form {
      background-color: #f9f9f9;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
      :deep(.el-form-item__label) {
        font-weight: 500;
        color: #606266;
      }
      :deep(.el-radio-group) {
        display: flex;
        gap: 20px;
      }
      :deep(.el-radio) {
        margin-right: 0;
      }
      :deep(.el-checkbox) {
        font-weight: normal;
      }
      :deep(.el-textarea__inner) {
        border-radius: 6px;
        border: 1px solid #dcdfe6;
        transition: all 0.3s ease;
        &:focus {
          border-color: #409eff;
          box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
        }
      }
    }
  }
</style>
src/views/procurementManagement/advancedPriceManagement/index.vue
@@ -8,7 +8,7 @@
        </el-form-item>
        <el-form-item label="供应商:">
          <el-select v-model="searchForm.supplierId" placeholder="请选择供应商" clearable style="width: 200px">
            <el-option v-for="supplier in supplierList" :key="supplier.id" :label="supplier.name" :value="supplier.id" />
            <el-option v-for="supplier in supplierList" :key="supplier.id" :label="supplier.supplierName" :value="supplier.id" />
          </el-select>
        </el-form-item>
        <el-form-item>
@@ -62,11 +62,11 @@
            <div class="product-info">
              <div class="product-name">{{ row.productName }}</div>
              <div class="product-spec">{{ row.specification }}</div>
              <div class="product-code">编码: {{ row.productCode }}</div>
              <!-- <div class="product-code">编码: {{ row.productCode }}</div> -->
            </div>
          </template>
        </el-table-column>
        <el-table-column label="供应商" prop="supplierName" width="150" />
        <el-table-column label="供应商" prop="supplierName" width="200" />
        <el-table-column label="基础价格" width="120" align="right">
          <template #default="{ row }">
            <span class="price-text">¥{{ row.basePrice }}</span>
@@ -103,9 +103,9 @@
        <el-table-column label="状态" width="100" align="center">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
            <div v-if="isPriceWarning(row)" class="warning-indicator">
            <!-- <div v-if="isPriceWarning(row)" class="warning-indicator">
              <el-icon color="#F56C6C"><Warning /></el-icon>
            </div>
            </div> -->
          </template>
        </el-table-column>
        <el-table-column label="生效时间" prop="effectiveTime" width="180" />
@@ -143,10 +143,20 @@
      <el-form :model="formData" :rules="formRules" ref="formRef" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="商品名称" prop="productName">
              <el-select v-model="formData.productName" placeholder="请选择商品" style="width: 100%" filterable>
                <el-option v-for="product in productList" :key="product.id" :label="product.name" :value="product.name" />
              </el-select>
            <el-form-item label="商品名称" prop="productId">
              <el-tree-select
                v-model="formData.productId"
                placeholder="请选择商品"
                clearable
                filterable
                check-strictly
                @change="getModels"
                :data="productOptions"
                :props="{ label: 'productName', value: 'id', children: 'children' }"
                node-key="id"
                :render-after-expand="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -158,13 +168,35 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="规格型号" prop="specification">
              <el-input v-model="formData.specification" placeholder="请输入规格型号" />
              <el-select
                v-model="formData.specification"
                placeholder="请选择"
                clearable
                @change="getProductModel"
              >
                <el-option
                  v-for="item in modelOptions"
                  :key="item.id"
                  :label="item.model"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商" prop="supplierName">
              <el-select v-model="formData.supplierName" placeholder="请选择供应商" style="width: 100%">
                <el-option v-for="supplier in supplierList" :key="supplier.id" :label="supplier.name" :value="supplier.name" />
            <el-form-item label="供应商" prop="supplierId">
              <el-select
                v-model="formData.supplierId"
                placeholder="请选择供应商"
                clearable
                @change="handleSupplierChange"
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
@@ -185,7 +217,7 @@
        <!-- 折扣设置 -->
        <el-divider content-position="left">折扣设置</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
          <el-col :span="12">
            <el-form-item label="折扣类型">
              <el-select v-model="formData.discountType" placeholder="请选择折扣类型" style="width: 100%">
                <el-option label="无折扣" value="" />
@@ -194,10 +226,11 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="折扣值" v-if="formData.discountType && formData.discountType !== 'tiered'">
          <el-col :span="12">
            <el-form-item label="折扣值">
              <el-input-number 
                v-model="formData.discountValue" 
                :disabled="!(formData.discountType && formData.discountType !== 'tiered')"
                :min="0" 
                :max="formData.discountType === 'percentage' ? 100 : undefined"
                :precision="2" 
@@ -206,10 +239,11 @@
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
          <el-col :span="12">
            <el-form-item label="折扣有效期">
              <el-date-picker 
                v-model="formData.discountEndTime" 
                :disabled="!(formData.discountType && formData.discountType !== 'tiered')"
                type="datetime" 
                placeholder="选择结束时间" 
                style="width: 100%" 
@@ -221,19 +255,14 @@
        <!-- 价格控制 -->
        <el-divider content-position="left">价格控制</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
          <el-col :span="12">
            <el-form-item label="最低价格">
              <el-input-number v-model="formData.minPrice" :min="0" :precision="2" placeholder="最低价格" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
          <el-col :span="12">
            <el-form-item label="最高价格">
              <el-input-number v-model="formData.maxPrice" :min="0" :precision="2" placeholder="最高价格" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="预警阈值(%)">
              <el-input-number v-model="formData.warningThreshold" :min="0" :max="100" :precision="1" placeholder="预警阈值" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -334,12 +363,14 @@
<script setup>
import {ref, reactive, computed, onMounted, getCurrentInstance} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { productTreeList, modelList } from "@/api/basicData/product.js";
import {
  Search, Refresh, Plus, Discount, Setting, Download, Delete, Edit, 
  Warning
} from '@element-plus/icons-vue'
import { listPage, update, del, add } from '@/api/procurementManagement/advancedPriceManagement'
import {
  getOptions,
} from "@/api/procurementManagement/procurementLedger.js";
// 响应式数据
const loading = ref(false)
const submitLoading = ref(false)
@@ -347,6 +378,8 @@
const batchDiscountVisible = ref(false)
const priceControlVisible = ref(false)
const dialogType = ref('add')
const productOptions = ref([]);
const modelOptions = ref([]);
const selectedRows = ref([])
const formRef = ref()
@@ -367,10 +400,13 @@
// 表单数据
const formData = reactive({
  productId: null,
  productName: '',
  productCode: '',
  specification: '',
  supplierId: null,
  supplierName: '',
  specificationId: null,
  basePrice: 0,
  unit: '',
  discountType: '',
@@ -405,24 +441,19 @@
// 表单验证规则
const formRules = {
  productName: [{ required: true, message: '请选择商品名称', trigger: 'change' }],
  productId: [{ required: true, message: '请选择商品名称', trigger: 'change' }],
  productCode: [{ required: true, message: '请输入商品编码', trigger: 'blur' }],
  supplierName: [{ required: true, message: '请选择供应商', trigger: 'change' }],
  supplierId: [{ required: true, message: '请选择供应商', trigger: 'change' }],
  basePrice: [{ required: true, message: '请输入基础价格', trigger: 'blur' }],
  effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }],
  reason: [{ required: true, message: '请选择调价原因', trigger: 'change' }]
}
const supplierList = ref([
  { id: 1, name: '优质五金供应商' },
  { id: 2, name: '钢材贸易公司' },
  { id: 3, name: '建材批发商' }
])
getOptions().then((res) => {
    supplierList.value = res.data;
  });
const productList = ref([
  { id: 1, name: '高强度螺栓' },
  { id: 2, name: '不锈钢管' },
  { id: 3, name: '铝合金型材' }
const supplierList = ref([
])
@@ -434,7 +465,8 @@
  } else if (row.discountType === 'fixed') {
    finalPrice = row.basePrice - row.discountValue
  }
  return Math.max(finalPrice, 0)
  let man = Math.max(finalPrice, 0)
  return man.toFixed(2)
}
const getDiscountTagType = (discountType) => {
@@ -454,6 +486,40 @@
  return textMap[discountType] || '未知'
}
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = res;
  });
};
const findNodeById = (data, id) => {
  for (const item of data) {
    if (item.id === id) return item;
    if (item.children) {
      const found = findNodeById(item.children, id);
      if (found) return found;
    }
  }
  return null;
};
const getModels = (value) => {
  if (value) {
    const selectedProduct = findNodeById(productOptions.value, value);
    if (selectedProduct) {
      formData.productName = selectedProduct.productName;
      formData.productCode = selectedProduct.productCode;
    }
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  } else {
    formData.productName = "";
    formData.productCode = "";
    modelOptions.value = [];
  }
};
const getStatusType = (status) => {
  const statusMap = {
    active: 'success',
@@ -461,6 +527,27 @@
    expired: 'info'
  }
  return statusMap[status] || 'info'
}
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    formData.specification = modelOptions.value[index].model;
    formData.specificationId = modelOptions.value[index].id;
    formData.unit = modelOptions.value[index].unit;
  } else {
    formData.specification = null;
    formData.specificationId = null;
    formData.unit = null;
  }
};
const handleSupplierChange = (value) => {
  const supplier = supplierList.value.find(supplier => supplier.id === value)
  if (supplier) {
      formData.supplierId = supplier.id
      formData.supplierName = supplier.supplierName
  }
}
const getStatusText = (status) => {
@@ -471,13 +558,6 @@
  }
  return statusMap[status] || '未知'
}
const isPriceWarning = (row) => {
  if (!row.priceControl) return false
  const finalPrice = calculateFinalPrice(row)
  return finalPrice < row.priceControl.minPrice || finalPrice > row.priceControl.maxPrice
}
const handleSearch = () => {
  loading.value = true
@@ -501,10 +581,14 @@
const openDialog = (type, row = {}) => {
  dialogType.value = type
  if (type === 'edit' && row.id) {
    // 复制行数据到表单
    Object.assign(formData, {
      ...row,
      minPrice: row.priceControl?.minPrice,
      maxPrice: row.priceControl?.maxPrice,
      // 兼容两种数据结构:平铺的字段或嵌套在 priceControl 中的字段
      minPrice: row.minPrice ?? row.priceControl?.minPrice,
      maxPrice: row.maxPrice ?? row.priceControl?.maxPrice,
      // 确保折扣有效期也被赋值 (如果 row.discountEndTime 存在的话)
      discountEndTime: row.discountEndTime || row.discountEndTime,
      tieredDiscount: row.tieredDiscount || []
    })
  } else {
@@ -515,9 +599,11 @@
const resetFormData = () => {
  Object.assign(formData, {
    productId: null,
    productName: '',
    productCode: '',
    specification: '',
    supplierId: null,
    supplierName: '',
    basePrice: 0,
    unit: '',
@@ -543,12 +629,12 @@
  })
}
const removeTieredRow = (index) => {
  formData.tieredDiscount.splice(index, 1)
}
const handleSubmit = async () => {
  if (!formRef.value) return
    formData.actualPrice = calculateFinalPrice(formData)
    if( formData.discountType === ''){
      formData.discountEndTime = '2099-12-31 23:59:59'
    }
  
  try {
    await formRef.value.validate()
@@ -677,6 +763,7 @@
// 生命周期
onMounted(() => {
  handleSearch()
  getProductOptions()
})
</script>
src/views/procurementManagement/procurementLedger/index.vue
@@ -552,6 +552,11 @@
                            </el-select>
                        </el-form-item>
                    </el-col>
          <el-col :span="24">
            <el-form-item label=" ">
              <el-button type="warning" :disabled="!(productForm.productId && productForm.productModelId && productForm.taxRate)" @click="showPriceReference" icon="Search">查看历史采购价格参考</el-button>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -636,6 +641,62 @@
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 历史采购价格参考弹窗 -->
    <el-dialog
      v-model="priceReferenceVisible"
      title="历史采购价格参考"
      width="1000px"
      append-to-body
    >
      <el-table
        v-loading="priceReferenceLoading"
        :data="priceReferenceData"
        border
        style="width: 100%"
      >
        <el-table-column label="商品名称" prop="productName" min-width="150" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specification" width="150" show-overflow-tooltip />
        <el-table-column label="供应商" prop="supplierName" width="200" show-overflow-tooltip />
        <el-table-column label="基础价格" width="120" align="right">
          <template #default="{ row }">
            <span style="color: #f56c6c; font-weight: bold;">¥{{ row.basePrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="折扣信息" width="120" align="center">
          <template #default="{ row }">
            <el-tag v-if="row.discountType === 'percentage'" type="success">
              {{ row.discountValue }}%
            </el-tag>
            <el-tag v-else-if="row.discountType === 'fixed'" type="warning">
              -¥{{ row.discountValue }}
            </el-tag>
            <span v-else>无折扣</span>
          </template>
        </el-table-column>
        <el-table-column label="生效时间" prop="effectiveTime" width="180" align="center" />
        <el-table-column label="操作" width="100" align="center" fixed="right">
          <template #default="{ row }">
            <el-button type="primary" link @click="selectPriceReference(row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-container" style="margin-top: 20px; display: flex; justify-content: flex-end;">
        <el-pagination
          v-model:current-page="priceReferencePagination.current"
          v-model:page-size="priceReferencePagination.size"
          :page-sizes="[10, 20, 50]"
          :total="priceReferenceTotal"
          layout="total, sizes, prev, pager, next"
          @size-change="handlePriceReferenceSizeChange"
          @current-change="handlePriceReferenceCurrentChange"
        />
      </div>
      <template #footer>
        <el-button @click="priceReferenceVisible = false">关闭</el-button>
      </template>
    </el-dialog>
@@ -903,6 +964,7 @@
let nextApproverId = 2;
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import { listPage as listAdvancedPrice } from "@/api/procurementManagement/advancedPriceManagement.js";
import dayjs from "dayjs";
const userStore = useUserStore();
@@ -996,6 +1058,72 @@
  },
});
const { productForm, productRules } = toRefs(productFormData);
// 采购价格管理参考弹窗
const priceReferenceVisible = ref(false);
const priceReferenceLoading = ref(false);
const priceReferenceData = ref([]);
const priceReferenceTotal = ref(0);
const priceReferencePagination = reactive({
  current: 1,
  size: 10
});
const showPriceReference = () => {
  priceReferenceVisible.value = true;
  handlePriceReferenceSearch();
};
const handlePriceReferenceSearch = () => {
  priceReferenceLoading.value = true;
  // 模拟搜索参数:productId 映射为 productName 或 ID,这里根据 advancedPriceManagement 的 API 确定参数
  // 假设高级价格管理的 listPage 接收 productId
  const query = {
    productId: productForm.value.productId,
    specificationId: productForm.value.productModelId,
    current: priceReferencePagination.current,
    size: priceReferencePagination.size
  };
  listAdvancedPrice(query).then(res => {
    priceReferenceData.value = res.data.records;
    priceReferenceTotal.value = res.data.total;
    priceReferenceLoading.value = false;
  }).catch(() => {
    priceReferenceLoading.value = false;
  });
};
const handlePriceReferenceSizeChange = (size) => {
  priceReferencePagination.size = size;
  handlePriceReferenceSearch();
};
const handlePriceReferenceCurrentChange = (page) => {
  priceReferencePagination.current = page;
  handlePriceReferenceSearch();
};
const selectPriceReference = (row) => {
   // 计算实际价格:基础价格 - 折扣
   let actualPrice = row.basePrice;
   if (row.discountType === 'percentage') {
     actualPrice = row.basePrice * (1 - row.discountValue / 100);
   } else if (row.discountType === 'fixed') {
     actualPrice = row.basePrice - row.discountValue;
   }
   // 填充含税单价,保留两位小数
   productForm.value.taxInclusiveUnitPrice = Number(Math.max(actualPrice, 0)).toFixed(2);
   // 如果已经输入了数量,则自动计算总价
   if (productForm.value.quantity) {
     mathNum();
   }
   priceReferenceVisible.value = false;
   proxy.$modal.msgSuccess("已引用历史价格");
 };
const upload = reactive({
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
@@ -1230,6 +1358,10 @@
};
// 打开产品弹框
const openProductForm = (type, row, index) => {
  if(!form.value.supplierId){
    proxy.$modal.msgWarning("请选择供应商");
    return;
  }
  productOperationType.value = type;
  productOperationIndex.value = index;
  productForm.value = {};