b982701bd16a5d6e10138a1e2d0f0be779503a83..461485f8fb5764d16f650fcdbf41d1c3e259d4a2
2026-03-28 ZN
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-m...
461485 对比 | 目录
2026-03-28 ZN
feat(采购管理): 优化高级价格管理与采购台账功能
c0d6fe 对比 | 目录
2026-03-28 yuan
Merge remote-tracking branch 'origin/dev_HXSJ' into dev_HXSJ
f7f5d6 对比 | 目录
2026-03-28 yuan
fix(enterpriseInfo): 优化图片路径显示逻辑
e508f0 对比 | 目录
2026-03-28 yuan
feat(enterpriseInfo): 新增企业信息管理页面
eca47c 对比 | 目录
2026-03-28 gongchunyi
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-m...
f3867f 对比 | 目录
2026-03-28 gongchunyi
feat: 入库管理新增"缺货字段"和"缺货情况"
60fb54 对比 | 目录
2026-03-28 yuan
feat(supplierManage): 添加文件预览功能
df8de8 对比 | 目录
2026-03-28 zhangwencui
固定资产
7c8f7b 对比 | 目录
2026-03-28 chenhj
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-m...
4db452 对比 | 目录
2026-03-28 chenhj
refactor(salesLedger): 移除错误消息显示
7159de 对比 | 目录
2026-03-28 yyb
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-m...
5cc12c 对比 | 目录
2026-03-28 yyb
预览
f25d98 对比 | 目录
2026-03-28 ZN
feat(procurementLedger): 添加历史采购价格参考功能
448645 对比 | 目录
2026-03-28 ZN
fix(采购台账): 打开产品弹窗前增加供应商校验
193580 对比 | 目录
2026-03-28 ZN
Merge branch 'dev_HXSJ' of http://114.132.189.42:9002/r/product-inventory-m...
d021ff 对比 | 目录
2026-03-28 ZN
feat(高级价格管理): 优化商品和供应商选择逻辑并调整表单布局
9de19b 对比 | 目录
2026-03-28 zhangwencui
客户画像分析、及修改下拉参数
76bad3 对比 | 目录
2026-03-28 yyb
供应商档案资质文件模块上传附件多个分页问题修复
bf75c7 对比 | 目录
已添加4个文件
已修改6个文件
2886 ■■■■ 文件已修改
src/api/basicData/enterpriseInfo.js 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/financialManagement/fixedAssets.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 1277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/enterpriseInfo/index.vue 664 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/filesDia.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/fixedAssets/index.vue 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/advancedPriceManagement/index.vue 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/enterpriseInfo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
// ä¼ä¸šé—¨æˆ·é¡µé¢æŽ¥å£
import request from '@/utils/request'
// èŽ·å–ä¼ä¸šä¿¡æ¯
export function getEnterpriseInfo() {
    return request({
        url: '/system/enterpriseInfo/getInfo',
        method: 'get'
    })
}
// ä¿å­˜ä¼ä¸šä¿¡æ¯
export function saveEnterpriseInfo(data) {
    return request({
        url: '/system/enterpriseInfo/save',
        method: 'post',
        data: data
    })
}
// ä¸Šä¼ Logo
export function uploadLogo(file) {
    const formData = new FormData()
    formData.append('file', file)
    return request({
        url: '/system/enterpriseInfo/uploadLogo',
        method: 'post',
        data: formData,
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    })
}
// ä¸Šä¼ äºŒç»´ç 
export function uploadQrCode(file) {
    const formData = new FormData()
    formData.append('file', file)
    return request({
        url: '/system/enterpriseInfo/uploadQrCode',
        method: 'post',
        data: formData,
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    })
}
src/api/financialManagement/fixedAssets.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢å›ºå®šèµ„产列表
export const listPage = (params) => {
  return request({
    url: "/enterpriseFixedAssets/listPage",
    method: "get",
    params,
  });
};
// æ–°å¢žå›ºå®šèµ„产
export function add(data) {
  return request({
    url: "/enterpriseFixedAssets/add",
    method: "post",
    data: data,
  });
}
// æ›´æ–°å›ºå®šèµ„产
export function update(data) {
  return request({
    url: "/enterpriseFixedAssets/update",
    method: "post",
    data: data,
  });
}
// åˆ é™¤å›ºå®šèµ„产
export const delFixedAssets = (query) => {
  return request({
    url: `/enterpriseFixedAssets/delete`,
    method: "delete",
    data: query,
  });
};
// å¯¼å‡ºå›ºå®šèµ„产
export const exportFixedAssets = (query) => {
  return request({
    url: "/financial/fixedAssets/export",
    method: "post",
    data: query,
    responseType: "blob",
  });
};
src/views/basicData/customerFile/index.vue
@@ -3,244 +3,291 @@
    <div class="search_form">
      <div>
        <span class="search_title">客户名称:</span>
        <el-input
          v-model="searchForm.customerName"
          style="width: 240px;margin-right: 10px"
          placeholder="请输入"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
         <span class="search_title">客户分类:</span>
         <el-select
          v-model="searchForm.customerType"
          placeholder="请选择"
          style="width: 240px"
          clearable
           @change="handleQuery"
        >
           <el-option  label="零售客户" value="零售客户" />
           <el-option  label="进销商客户" value="进销商客户" />
        <el-input v-model="searchForm.customerName"
                  style="width: 240px;margin-right: 10px"
                  placeholder="请输入"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <span class="search_title">客户分类:</span>
        <el-select v-model="searchForm.customerType"
                   placeholder="请选择"
                   style="width: 240px"
                   clearable
                   @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"
        :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="customerName">
              <el-input
                v-model="form.customerName"
                placeholder="请输入"
                clearable
              />
            <el-form-item label="客户名称:"
                          prop="customerName">
              <el-input v-model="form.customerName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
              label="纳税人识别号:"
              prop="taxpayerIdentificationNumber"
            >
              <el-input
                v-model="form.taxpayerIdentificationNumber"
                placeholder="请输入"
                clearable
              />
            <el-form-item label="纳税人识别号:"
                          prop="taxpayerIdentificationNumber">
              <el-input v-model="form.taxpayerIdentificationNumber"
                        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="basicBankAccount">
              <el-input
                v-model="form.basicBankAccount"
                placeholder="请输入"
                clearable
              />
            <el-form-item label="银行基本户:"
                          prop="basicBankAccount">
              <el-input v-model="form.basicBankAccount"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号:" prop="bankAccount">
              <el-input
                v-model="form.bankAccount"
                placeholder="请输入"
                clearable
              />
            <el-form-item label="银行账号:"
                          prop="bankAccount">
              <el-input v-model="form.bankAccount"
                        placeholder="请输入"
                        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"
                placeholder="请输入"
                clearable
              />
            <el-form-item label="开户行号:"
                          prop="bankCode">
              <el-input v-model="form.bankCode"
                        placeholder="请输入"
                        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-col :span="12">
                        <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">
                            <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-button>
                            </div>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-button @click="addNewContact" style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
        <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>
          </el-col>
          <el-col :span="12">
            <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-button>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <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"
                placeholder="请选择"
                clearable
                disabled
              >
                <el-option
                  v-for="item in userList"
                  :key="item.nickName"
                  :label="item.nickName"
                  :value="item.nickName"
                />
            <el-form-item label="维护人:"
                          prop="maintainer">
              <el-select v-model="form.maintainer"
                         placeholder="请选择"
                         clearable
                         disabled>
                <el-option v-for="item in userList"
                           :key="item.nickName"
                           :label="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%"
                v-model="form.maintenanceTime"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
              />
            <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 />
            </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"
      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"
        :before-upload="upload.beforeUpload"
        :on-progress="upload.onProgress"
        :on-success="upload.onSuccess"
        :on-error="upload.onError"
        :on-change="upload.onChange"
        :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"
                 :before-upload="upload.beforeUpload"
                 :on-progress="upload.onProgress"
                 :on-success="upload.onSuccess"
                 :on-error="upload.onError"
                 :on-change="upload.onChange"
                 :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>
    </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>
@@ -248,384 +295,546 @@
</template>
<script setup>
import {onMounted, ref} from "vue";
import { Search } from "@element-plus/icons-vue";
import {
  addCustomer,
  delCustomer,
  getCustomer,
  listCustomer,
  updateCustomer,
} from "@/api/basicData/customerFile.js";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
  import { onMounted, ref } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import {
    addCustomer,
    delCustomer,
    getCustomer,
    listCustomer,
    updateCustomer,
  } from "@/api/basicData/customerFile.js";
  import { ElMessageBox } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import useUserStore from "@/store/modules/user";
  import { getToken } from "@/utils/auth.js";
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
const tableColumn = ref([
  {
    label: "客户分类",
    prop: "customerType",
    width: 120,
  },
  {
    label: "客户名称",
    prop: "customerName",
    width: 220,
  },
  {
    label: "纳税人识别码",
    prop: "taxpayerIdentificationNumber",
    width: 220,
  },
  {
    label: "地址及联系方式",
    prop: "addressPhone",
    width: 250,
  },
  {
    label: "联系人",
    prop: "contactPerson",
  },
  {
    label: "联系电话",
    prop: "contactPhone",
    width:150
  },
  {
    label: "银行基本户",
    prop: "basicBankAccount",
    width: 220,
  },
  {
    label: "银行账号",
    prop: "bankAccount",
    width: 220,
  },
  {
    label: "开户行号",
    prop: "bankCode",
    width:220
  },
  {
    label: "维护人",
    prop: "maintainer",
  },
  {
    label: "维护时间",
    prop: "maintenanceTime",
    width: 100,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
        fixed: 'right',
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
  const tableColumn = ref([
    {
      label: "客户分类",
      prop: "customerType",
      width: 120,
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: 220,
    },
    {
      label: "纳税人识别码",
      prop: "taxpayerIdentificationNumber",
      width: 220,
    },
    {
      label: "地址及联系方式",
      prop: "addressPhone",
      width: 250,
    },
    {
      label: "联系人",
      prop: "contactPerson",
    },
    {
      label: "联系电话",
      prop: "contactPhone",
      width: 150,
    },
    {
      label: "银行基本户",
      prop: "basicBankAccount",
      width: 220,
    },
    {
      label: "银行账号",
      prop: "bankAccount",
      width: 220,
    },
    {
      label: "开户行号",
      prop: "bankCode",
      width: 220,
    },
    {
      label: "维护人",
      prop: "maintainer",
    },
    {
      label: "维护时间",
      prop: "maintenanceTime",
      width: 100,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      width: 160,
      fixed: "right",
      operation: [
        {
          name: "编辑",
          type: "text",
          clickFun: row => {
            openForm("edit", row);
          },
        },
        {
          name: "客户画像",
          type: "text",
          color: "rgb(229 187 21)",
          showHide: row => {
            return row.customerType == "经销商客户";
          },
          clickFun: row => {
            openCustomerProfile(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const selectedRows = ref([]);
  const userList = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const total = ref(0);
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const formYYs = ref({
    // å…¶ä»–字段...
    contactList: [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0,
});
const total = ref(0);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const formYYs = ref({    // å…¶ä»–字段...
  contactList: [
    {
  });
  const data = reactive({
    searchForm: {
      customerName: "",
      customerType: "",
    },
    form: {
      customerName: "",
      taxpayerIdentificationNumber: "",
      companyAddress: "",
      companyPhone: "",
      contactPerson: "",
      contactPhone: ""
    }
  ]
});
const data = reactive({
  searchForm: {
    customerName: "",
    customerType: "",
  },
  form: {
    customerName: "",
    taxpayerIdentificationNumber: "",
    companyAddress: "",
    companyPhone: "",
    contactPerson: "",
    contactPhone: "",
    maintainer: "",
    maintenanceTime: "",
    basicBankAccount: "",
    bankAccount: "",
    bankCode: "",
    customerType: "",
  },
  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" }],
    // 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" }],
    customerType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(客户导入)
  open: false,
  // å¼¹å‡ºå±‚标题(客户导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
  // æ–‡ä»¶ä¸Šä¼ å‰çš„回调
  beforeUpload: (file) => {
    console.log('文件即将上传', file);
    // å¯ä»¥åœ¨æ­¤å¤„做文件类型或大小校验
    const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    if (!isValid) {
      proxy.$modal.msgError("只能上传 Excel æ–‡ä»¶");
    }
    return isValid;
  },
  // æ–‡ä»¶çŠ¶æ€æ”¹å˜æ—¶çš„å›žè°ƒ
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
  },
  // æ–‡ä»¶ä¸Šä¼ æˆåŠŸæ—¶çš„å›žè°ƒ
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', 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);
    }
  },
  // æ–‡ä»¶ä¸Šä¼ å¤±è´¥æ—¶çš„回调
  onError: (error, file, fileList) => {
    console.error('上传失败', error, file, fileList);
    upload.isUploading = false;
    proxy.$modal.msgError("文件上传失败");
  },
  // æ–‡ä»¶ä¸Šä¼ è¿›åº¦å›žè°ƒ
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  }
});
const { searchForm, form, rules } = toRefs(data);
const addNewContact = () => {
  formYYs.value.contactList.push({
    contactPerson: "",
    contactPhone: ""
      contactPhone: "",
      maintainer: "",
      maintenanceTime: "",
      basicBankAccount: "",
      bankAccount: "",
      bankCode: "",
      customerType: "",
    },
    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" }],
      // 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" }],
      customerType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
};
const removeContact = (index) => {
  if (formYYs.value.contactList.length > 1) {
    formYYs.value.contactList.splice(index, 1);
  }
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  listCustomer({ ...searchForm.value, ...page }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.records;
    page.total = res.total;
  });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
function submitFileForm() {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
}
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
function handleImport() {
  upload.title = "客户导入";
  upload.open = true;
}
/** ä¸‹è½½æ¨¡æ¿ */
function importTemplate() {
  proxy.download("/basic/customer/downloadTemplate", {}, "客户导入模板.xlsx");
}
// æ‰“开弹框
const openForm = (type, row) => {
  operationType.value = type;
  form.value = {};
  form.value.maintainer = userStore.nickName;
  formYYs.value.contactList = [
    {
      contactPerson: "",
      contactPhone: ""
    }
  ];
  form.value.maintenanceTime = getCurrentDate();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  if (type === "edit") {
    getCustomer(row.id).then((res) => {
      form.value = { ...res.data };
      formYYs.value.contactList = res.data.contactPerson.split(",").map((item, index) => {
        return {
          contactPerson: item,
          contactPhone: res.data.contactPhone.split(",")[index]
        }
      });
    });
  }
  dialogFormVisible.value = true;
};
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitEdit();
      } else {
        submitAdd();
  const upload = reactive({
    // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(客户导入)
    open: false,
    // å¼¹å‡ºå±‚标题(客户导入)
    title: "",
    // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
    isUploading: false,
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
    // æ–‡ä»¶ä¸Šä¼ å‰çš„回调
    beforeUpload: file => {
      console.log("文件即将上传", file);
      // å¯ä»¥åœ¨æ­¤å¤„做文件类型或大小校验
      const isValid =
        file.type ===
          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
        file.name.endsWith(".xlsx") ||
        file.name.endsWith(".xls");
      if (!isValid) {
        proxy.$modal.msgError("只能上传 Excel æ–‡ä»¶");
      }
    }
      return isValid;
    },
    // æ–‡ä»¶çŠ¶æ€æ”¹å˜æ—¶çš„å›žè°ƒ
    onChange: (file, fileList) => {
      console.log("文件状态改变", file, fileList);
    },
    // æ–‡ä»¶ä¸Šä¼ æˆåŠŸæ—¶çš„å›žè°ƒ
    onSuccess: (response, file, fileList) => {
      console.log("上传成功", 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);
      }
    },
    // æ–‡ä»¶ä¸Šä¼ å¤±è´¥æ—¶çš„回调
    onError: (error, file, fileList) => {
      console.error("上传失败", error, file, fileList);
      upload.isUploading = false;
      proxy.$modal.msgError("文件上传失败");
    },
    // æ–‡ä»¶ä¸Šä¼ è¿›åº¦å›žè°ƒ
    onProgress: (event, file, fileList) => {
      console.log("上传中...", event.percent);
    },
  });
};
// æäº¤æ–°å¢ž
const submitAdd = () => {
  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) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// æäº¤ä¿®æ”¹
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) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/basic/customer/export", {}, "客户档案.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
  const { searchForm, form, rules } = toRefs(data);
  const addNewContact = () => {
    formYYs.value.contactList.push({
      contactPerson: "",
      contactPhone: "",
    });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
    const unauthorizedData = selectedRows.value.filter(item => item.maintainer !== userStore.nickName);
    if (unauthorizedData.length > 0) {
      proxy.$modal.msgWarning("不可删除他人维护的数据");
  };
  const removeContact = index => {
    if (formYYs.value.contactList.length > 1) {
      formYYs.value.contactList.splice(index, 1);
    }
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listCustomer({ ...searchForm.value, ...page }).then(res => {
      tableLoading.value = false;
      tableData.value = res.records;
      page.total = res.total;
    });
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  /** æäº¤ä¸Šä¼ æ–‡ä»¶ */
  function submitFileForm() {
    upload.isUploading = true;
    proxy.$refs["uploadRef"].submit();
  }
  /** å¯¼å…¥æŒ‰é’®æ“ä½œ */
  function handleImport() {
    upload.title = "客户导入";
    upload.open = true;
  }
  /** ä¸‹è½½æ¨¡æ¿ */
  function importTemplate() {
    proxy.download("/basic/customer/downloadTemplate", {}, "客户导入模板.xlsx");
  }
  // æ‰“开弹框
  const openForm = (type, row) => {
    operationType.value = type;
    form.value = {};
    form.value.maintainer = userStore.nickName;
    formYYs.value.contactList = [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ];
    form.value.maintenanceTime = getCurrentDate();
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    if (type === "edit") {
      getCustomer(row.id).then(res => {
        form.value = { ...res.data };
        formYYs.value.contactList = res.data.contactPerson
          .split(",")
          .map((item, index) => {
            return {
              contactPerson: item,
              contactPhone: res.data.contactPhone.split(",")[index],
            };
          });
      });
    }
    dialogFormVisible.value = true;
  };
  // æäº¤è¡¨å•
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitEdit();
        } else {
          submitAdd();
        }
      }
    });
  };
  // æäº¤æ–°å¢ž
  const submitAdd = () => {
    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 => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    });
  };
  // æäº¤ä¿®æ”¹
  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 => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/basic/customer/export", {}, "客户档案.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
      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);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      tableLoading.value = true;
      delCustomer(ids)
        .then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        })
        .finally(() => {
          tableLoading.value = false;
        });
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
      .then(() => {
        tableLoading.value = true;
        delCustomer(ids)
          .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 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();
    });
};
  };
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º 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}`;
}
onMounted(() => {
    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/basicData/enterpriseInfo/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,664 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜æ  -->
    <div class="page-header">
      <h2>企业门户</h2>
      <div class="header-actions">
        <el-button @click="handlePreview" :icon="View" type="info" plain>预览</el-button>
        <el-button @click="toggleEdit" :icon="isEdit ? 'Close' : 'Edit'" :type="isEdit ? 'default' : 'primary'">
          {{ isEdit ? '取消编辑' : '编辑' }}
        </el-button>
      </div>
    </div>
    <!-- ä¼ä¸šä¿¡æ¯å¡ç‰‡ -->
    <div class="enterprise-info-card" v-loading="loading">
      <!-- åŸºæœ¬ä¿¡æ¯åŒºåŸŸ -->
      <div class="info-section">
        <div class="section-header">
          <h3>基本信息</h3>
        </div>
        <el-descriptions :column="2" border class="info-descriptions">
          <el-descriptions-item label="公司名称">
            <el-input v-if="isEdit" v-model="form.companyName" placeholder="请输入公司名称" clearable />
            <span v-else class="content-text">{{ form.companyName || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="联系人">
            <el-input v-if="isEdit" v-model="form.contactPerson" placeholder="请输入联系人" clearable />
            <span v-else class="content-text">{{ form.contactPerson || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="联系电话">
            <el-input v-if="isEdit" v-model="form.contactPhone" placeholder="请输入联系电话" clearable />
            <span v-else class="content-text">{{ form.contactPhone || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="公司地址">
            <el-input v-if="isEdit" v-model="form.companyAddress" placeholder="请输入公司地址" clearable />
            <span v-else class="content-text">{{ form.companyAddress || '-' }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="公司网站">
            <el-input v-if="isEdit" v-model="form.website" placeholder="请输入公司网站" clearable />
            <a v-else-if="form.website" :href="form.website" target="_blank" class="link-text">{{ form.website }}</a>
            <span v-else class="content-text">-</span>
          </el-descriptions-item>
        </el-descriptions>
      </div>
      <!-- Logo和二维码区域 -->
      <div class="info-section">
        <div class="section-header">
          <h3>企业标识</h3>
        </div>
        <div class="logo-qr-container">
          <!-- å…¬å¸Logo -->
          <div class="upload-item">
            <span class="upload-label">公司Logo</span>
            <div class="upload-wrapper">
              <el-upload
                v-if="isEdit"
                class="logo-uploader"
                :show-file-list="false"
                :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')"
                action="#">
                <img v-if="form.companyLogo" :src="form.companyLogo" class="uploaded-image" alt="Image Preview" />
                <div v-else class="upload-placeholder">
                  <el-icon class="upload-icon"><Plus /></el-icon>
                  <span class="upload-text">上传Logo</span>
                </div>
              </el-upload>
              <img
                v-else-if="form.companyLogo"
                :src="form.companyLogo"
                class="display-image"
                alt="Image Preview"
              />
              <div v-else class="empty-placeholder">
                <el-icon :size="40"><Picture /></el-icon>
                <span>暂无Logo</span>
              </div>
            </div>
          </div>
          <!-- äºŒç»´ç  -->
          <div class="upload-item">
            <span class="upload-label">二维码</span>
            <div class="upload-wrapper">
              <el-upload
                v-if="isEdit"
                class="qr-uploader"
                :show-file-list="false"
                :before-upload="(file) => beforeLogoUpload(file, 'qrCode')"
                action="#">
                <img v-if="form.qrCode" :src="form.qrCode" class="uploaded-image" alt="Image Preview" />
                <div v-else class="upload-placeholder">
                  <el-icon class="upload-icon"><Plus /></el-icon>
                  <span class="upload-text">上传二维码</span>
                </div>
              </el-upload>
              <img
                v-else-if="form.qrCode"
                :src="form.qrCode"
                class="display-image"
                alt="Image Preview"
              />
              <div v-else class="empty-placeholder">
                <el-icon :size="40"><Picture /></el-icon>
                <span>暂无二维码</span>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- å…¬å¸ç®€ä»‹ -->
      <div class="info-section">
        <div class="section-header">
          <h3>公司简介</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.companyIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入公司简介..."
          />
          <div v-else class="content-display" v-html="form.companyIntro || '<span class=\'empty-text\'>暂无公司简介</span>'"></div>
        </div>
      </div>
      <!-- äº§å“ä»‹ç» -->
      <div class="info-section">
        <div class="section-header">
          <h3>产品介绍</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.productIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入产品介绍..."
          />
          <div v-else class="content-display" v-html="form.productIntro || '<span class=\'empty-text\'>暂无产品介绍</span>'"></div>
        </div>
      </div>
      <!-- è®¾å¤‡ä»‹ç» -->
      <div class="info-section">
        <div class="section-header">
          <h3>设备介绍</h3>
        </div>
        <div class="content-editor">
          <el-input
            v-if="isEdit"
            v-model="form.equipmentIntro"
            type="textarea"
            :rows="6"
            maxlength="2000"
            show-word-limit
            placeholder="请输入设备介绍..."
          />
          <div v-else class="content-display" v-html="form.equipmentIntro || '<span class=\'empty-text\'>暂无设备介绍</span>'"></div>
        </div>
      </div>
      <!-- æ“ä½œæŒ‰é’® -->
      <div v-if="isEdit" class="form-actions">
        <el-button type="primary" @click="handleSave" :loading="saving" size="large">保存</el-button>
        <el-button @click="handleCancel" size="large">取消</el-button>
      </div>
    </div>
    <!-- é¢„览弹窗 -->
    <el-dialog
      v-model="previewVisible"
      title="企业信息预览"
      width="70%"
      :destroy-on-close="true">
      <div class="preview-content">
        <div class="preview-header">
          <img v-if="form.companyLogo" :src="form.companyLogo" class="preview-logo" alt="Image Preview" />
          <div class="preview-title">
            <h1>{{ form.companyName || '公司名称' }}</h1>
            <p v-if="form.website">{{ form.website }}</p>
          </div>
        </div>
        <el-divider />
        <div class="preview-section">
          <h4>联系方式</h4>
          <p>联系人:{{ form.contactPerson || '-' }}</p>
          <p>联系电话:{{ form.contactPhone || '-' }}</p>
          <p>公司地址:{{ form.companyAddress || '-' }}</p>
        </div>
        <div class="preview-section">
          <h4>公司简介</h4>
          <div v-html="form.companyIntro || '暂无简介'"></div>
        </div>
        <div class="preview-section">
          <h4>产品介绍</h4>
          <div v-html="form.productIntro || '暂无介绍'"></div>
        </div>
        <div class="preview-section">
          <h4>设备介绍</h4>
          <div v-html="form.equipmentIntro || '暂无介绍'"></div>
        </div>
        <div v-if="form.qrCode" class="preview-section preview-qr">
          <h4>扫码关注</h4>
          <img :src="form.qrCode" class="qr-image" alt="Image Preview" />
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted } from 'vue'
  import { ElMessage } from 'element-plus'
  import { Plus, Picture, View } from '@element-plus/icons-vue'
  import { getEnterpriseInfo, saveEnterpriseInfo, uploadLogo, uploadQrCode } from '@/api/basicData/enterpriseInfo.js'
  const { proxy } = getCurrentInstance()
  const loading = ref(false)
  const saving = ref(false)
  const isEdit = ref(false)
  const previewVisible = ref(false)
  const uploadType = ref('')
  const form = reactive({
    id: null,
    companyName: '',
    companyLogo: '',
    companyIntro: '',
    productIntro: '',
    equipmentIntro: '',
    contactPerson: '',
    contactPhone: '',
    companyAddress: '',
    website: '',
    qrCode: ''
  })
  // æ·±æ‹·è´åŽŸå§‹æ•°æ®ï¼Œç”¨äºŽå–æ¶ˆæ—¶æ¢å¤
  let originalForm = {}
  // èŽ·å–ä¼ä¸šä¿¡æ¯
  const fetchInfo = () => {
    loading.value = true
    getEnterpriseInfo().then(res => {
      if (res.code === 200 && res.data) {
        // å°†å›¾ç‰‡è·¯å¾„拼接为完整地址
        const base = window.location.protocol + '//' + window.location.host
        if (res.data.companyLogo) {
          res.data.companyLogo = base + res.data.companyLogo
        }
        if (res.data.qrCode) {
          res.data.qrCode = base + res.data.qrCode
        }
        Object.assign(form, res.data)
        originalForm = JSON.parse(JSON.stringify(res.data))
      }
    }).finally(() => {
      loading.value = false
    })
  }
  // åˆ‡æ¢ç¼–辑模式
  const toggleEdit = () => {
    if (isEdit.value) {
      // å–消编辑,恢复原始数据
      Object.assign(form, originalForm)
      isEdit.value = false
    } else {
      // è¿›å…¥ç¼–辑模式
      originalForm = JSON.parse(JSON.stringify(form))
      isEdit.value = true
    }
  }
  // é¢„览
  const handlePreview = () => {
    previewVisible.value = true
  }
  // æ–‡ä»¶ä¸Šä¼ å‰æ ¡éªŒ
  const beforeLogoUpload = (file, type) => {
    const isImage = file.type.startsWith('image/')
    const isLt2M = file.size / 1024 / 1024 < 2
    if (!isImage) {
      ElMessage.error('只能上传图片文件!')
      return false
    }
    if (!isLt2M) {
      ElMessage.error('图片大小不能超过 2MB!')
      return false
    }
    uploadType.value = type
    uploadFileReq(file)
    return false
  }
  // ä¸Šä¼ æ–‡ä»¶è¯·æ±‚
  const uploadFileReq = (file) => {
    const uploadFn = uploadType.value === 'companyLogo' ? uploadLogo : uploadQrCode
    uploadFn(file).then(res => {
      if (res.code === 200) {
        const path = res.data.tempPath
        if (uploadType.value === 'companyLogo') {
          form.companyLogo = path
        } else {
          form.qrCode = path
        }
        ElMessage.success('上传成功')
      } else {
        ElMessage.error(res.msg || '上传失败')
      }
    }).catch(() => {
      ElMessage.error('上传失败')
    })
  }
  // ä¿å­˜
  const handleSave = () => {
    saving.value = true
    saveEnterpriseInfo(form).then(res => {
      if (res.code === 200) {
        ElMessage.success('保存成功')
        isEdit.value = false
        fetchInfo()
      } else {
        ElMessage.error(res.msg || '保存失败')
      }
    }).finally(() => {
      saving.value = false
    })
  }
  // å–消
  const handleCancel = () => {
    Object.assign(form, originalForm)
    isEdit.value = false
  }
  onMounted(() => {
    fetchInfo()
  })
</script>
<style scoped lang="scss">
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  background: #fff;
  border-radius: 8px;
  margin-bottom: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  h2 {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
    color: #303133;
  }
  .header-actions {
    display: flex;
    gap: 10px;
  }
}
.enterprise-info-card {
  background: #fff;
  padding: 24px;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.info-section {
  margin-bottom: 32px;
  &:last-of-type {
    margin-bottom: 0;
  }
  .section-header {
    margin-bottom: 16px;
    h3 {
      margin: 0;
      font-size: 16px;
      font-weight: 600;
      color: #303133;
      padding-bottom: 10px;
      border-bottom: 2px solid #409eff;
      display: inline-block;
      position: relative;
      &::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: -2px;
        width: 60px;
        height: 2px;
        background-color: #409eff;
      }
    }
  }
}
.info-descriptions {
  :deep(.el-descriptions__label) {
    width: 120px;
    background-color: #f5f7fa;
    font-weight: 500;
    color: #606266;
  }
  :deep(.el-descriptions__content) {
    color: #303133;
  }
}
.content-text {
  color: #606266;
}
.link-text {
  color: #409eff;
  text-decoration: none;
  &:hover {
    text-decoration: underline;
  }
}
.logo-qr-container {
  display: flex;
  gap: 40px;
}
.upload-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  .upload-label {
    font-size: 14px;
    color: #606266;
    margin-bottom: 12px;
    font-weight: 500;
  }
  .upload-wrapper {
    width: 140px;
    height: 140px;
  }
}
/* el-upload æ ¹èŠ‚ç‚¹æ˜¯å¤–å±‚ div,真正触发区在内层 .el-upload,需让内层铺满外层虚线框 */
.logo-uploader,
.qr-uploader {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 140px;
  height: 140px;
  border: 1px dashed #d9d9d9;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s;
  overflow: hidden;
  :deep(.el-upload) {
    flex: 1;
    min-height: 0;
    width: 100%;
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
    border: none;
    outline: none;
  }
  :deep(.uploaded-image),
  :deep(.upload-placeholder) {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    width: 100%;
  }
  &:hover {
    border-color: #409eff;
  }
  :deep(.uploaded-image) {
    object-fit: contain;
  }
  :deep(.upload-placeholder) {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: #fafafa;
    color: #8c939d;
    .upload-icon {
      font-size: 32px;
      margin-bottom: 8px;
    }
    .upload-text {
      font-size: 12px;
    }
  }
}
.display-image {
  width: 140px;
  height: 140px;
  object-fit: contain;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
}
.empty-placeholder {
  width: 140px;
  height: 140px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background: #fafafa;
  border-radius: 8px;
  border: 1px dashed #e4e7ed;
  color: #c0c4cc;
  span {
    margin-top: 8px;
    font-size: 12px;
  }
}
.content-editor {
  :deep(.el-textarea__inner) {
    border-radius: 6px;
    font-size: 14px;
    line-height: 1.8;
  }
}
.content-display {
  padding: 16px 20px;
  background: #fafafa;
  border-radius: 6px;
  border: 1px solid #ebeef5;
  line-height: 1.8;
  color: #606266;
  min-height: 120px;
  white-space: pre-wrap;
  :deep(.empty-text) {
    color: #c0c4cc;
    font-style: italic;
  }
}
.form-actions {
  display: flex;
  justify-content: center;
  gap: 16px;
  padding-top: 24px;
  margin-top: 16px;
  border-top: 1px solid #ebeef5;
}
/* é¢„览弹窗样式 */
.preview-content {
  .preview-header {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 8px;
    color: #fff;
    margin-bottom: 20px;
    .preview-logo {
      width: 80px;
      height: 80px;
      object-fit: contain;
      background: #fff;
      border-radius: 8px;
      padding: 8px;
    }
    .preview-title {
      h1 {
        margin: 0 0 8px 0;
        font-size: 28px;
        font-weight: 600;
      }
      p {
        margin: 0;
        opacity: 0.9;
        font-size: 14px;
      }
    }
  }
  .preview-section {
    margin-bottom: 24px;
    h4 {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
      margin: 0 0 12px 0;
      padding-bottom: 8px;
      border-bottom: 1px solid #ebeef5;
    }
    p {
      margin: 8px 0;
      color: #606266;
      line-height: 1.6;
    }
    :deep(div) {
      line-height: 1.8;
      color: #606266;
      white-space: pre-wrap;
    }
  }
  .preview-qr {
    text-align: center;
    .qr-image {
      width: 150px;
      height: 150px;
      margin-top: 12px;
      border-radius: 8px;
      border: 1px solid #ebeef5;
    }
  }
}
:deep(.el-divider) {
  margin: 16px 0;
}
</style>
src/views/basicData/supplierManage/filesDia.vue
@@ -28,18 +28,11 @@
          :tableData="tableData"
          :tableLoading="tableLoading"
          :isSelection="true"
          :page="{ total, current: page.current, size: page.size }"
          @pagination="paginationSearch"
          @selection-change="handleSelectionChange"
          height="500"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
          height="450"
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -60,7 +53,6 @@
  fileDel,
  fileListPage
} from "@/api/basicData/supplierManageFile.js";
import Pagination from "@/components/PIMTable/Pagination.vue";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
src/views/financialManagement/fixedAssets/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,313 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">资产名称:</span>
        <el-input v-model="searchForm.name"
                  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="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                style="width: 100%"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="资产名称"
                         prop="name"
                         show-overflow-tooltip />
        <el-table-column label="型号"
                         prop="model"
                         show-overflow-tooltip />
        <el-table-column label="ä»·æ ¼"
                         prop="price"
                         show-overflow-tooltip>
          <template #default="{ row }">
            Â¥{{ row.price ? row.price.toFixed(2) : '0.00' }}
          </template>
        </el-table-column>
        <el-table-column label="位置"
                         prop="address"
                         show-overflow-tooltip />
        <el-table-column label="创建时间"
                         prop="createTime"
                         show-overflow-tooltip>
          <template #default="{ row }">
            {{ row.createTime ? dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') : '-' }}
          </template>
        </el-table-column>
        <el-table-column fixed="right"
                         label="操作"
                         min-width="100"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit', scope.row);">编辑</el-button>
            <el-button link
                       type="danger"
                       size="small"
                       @click="handleDeleteSolo(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增固定资产' : '编辑固定资产'"
               width="600px"
               @close="closeDia">
      <el-form :model="form"
               label-width="100px"
               :rules="rules"
               ref="formRef">
        <el-form-item label="资产名称"
                      prop="name">
          <el-input v-model="form.name"
                    placeholder="请输入资产名称"
                    clearable />
        </el-form-item>
        <el-form-item label="型号"
                      prop="model">
          <el-input v-model="form.model"
                    placeholder="请输入型号"
                    clearable />
        </el-form-item>
        <el-form-item label="ä»·æ ¼"
                      prop="price">
          <el-input-number v-model="form.price"
                           :min="0"
                           :precision="2"
                           :step="0.01"
                           style="width: 100%"
                           placeholder="请输入价格" />
        </el-form-item>
        <el-form-item label="位置"
                      prop="address">
          <el-input v-model="form.address"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入位置" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import pagination from "@/components/PIMTable/Pagination.vue";
  import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
  import { ElMessageBox } from "element-plus";
  import useUserStore from "@/store/modules/user";
  import {
    listPage,
    add,
    update,
    delFixedAssets,
  } from "@/api/financialManagement/fixedAssets.js";
  import dayjs from "dayjs";
  const userStore = useUserStore();
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const selectedRows = ref([]);
  const tableLoading = ref(false);
  const loading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      name: "",
    },
    form: {
      id: null,
      name: "",
      model: "",
      price: 0,
      address: "",
    },
    rules: {
      name: [{ required: true, message: "请输入资产名称", trigger: "blur" }],
      model: [{ required: true, message: "请输入型号", trigger: "blur" }],
      price: [{ required: true, message: "请输入价格", trigger: "blur" }],
      address: [{ required: true, message: "请输入位置", trigger: "blur" }],
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listPage({ ...searchForm.value, ...page })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        total.value = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const openForm = async (type, row) => {
    operationType.value = type;
    dialogFormVisible.value = true;
    if (type === "add") {
      form.value = {
        id: null,
        name: "",
        model: "",
        price: 0,
        address: "",
      };
    } else {
      form.value = {
        id: row.id,
        name: row.name,
        model: row.model,
        price: row.price,
        address: row.address,
      };
    }
  };
  const submitForm = async () => {
    try {
      await proxy.$refs.formRef.validate();
      loading.value = true;
      if (operationType.value === "add") {
        await add(form.value);
        proxy.$modal.msgSuccess("新增资产成功");
      } else {
        await update(form.value);
        proxy.$modal.msgSuccess("修改资产成功");
      }
      closeDia();
      getList();
    } catch (error) {
      console.error("提交失败:", error);
      if (!error.errors) {
        proxy.$modal.msgError("操作失败,请重试");
      }
    } finally {
      loading.value = false;
    }
  };
  const closeDia = () => {
    proxy.$refs.formRef.resetFields();
    dialogFormVisible.value = false;
  };
  const handleDeleteSolo = row => {
    ElMessageBox.confirm("该资产将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        const ids = [row.id];
        delFixedAssets(ids)
          .then(res => {
            proxy.$modal.msgSuccess("删除成功");
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError("删除失败");
          });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleDelete = () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择要删除的数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        const ids = selectedRows.value.map(item => item.id);
        delFixedAssets(ids)
          .then(res => {
            proxy.$modal.msgSuccess("删除成功");
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError("删除失败");
          });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  onMounted(() => {
    getList();
  });
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/index.vue
@@ -76,6 +76,14 @@
                         prop="inboundNum"
                         width="90"
                         show-overflow-tooltip />
        <el-table-column label="缺货数量"
                         prop="outStockQuantity"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="缺货情况"
                         prop="shortageDescription"
                         width="150"
                         show-overflow-tooltip />
        <el-table-column label="含税单价"
                         prop="taxInclusiveUnitPrice"
                         width="100"
@@ -177,6 +185,25 @@
                               :min="0"
                               style="width: 100%"
                               v-model="scope.row.quantityStock" />
            </template>
          </el-table-column>
          <el-table-column label="缺货数量"
                           prop="outStockQuantity"
                           width="150">
            <template #default="scope">
              <el-input-number :step="0.01"
                               :min="0"
                               style="width: 100%"
                               v-model="scope.row.outStockQuantity" />
            </template>
          </el-table-column>
          <el-table-column label="缺货情况"
                           prop="shortageDescription"
                           width="150">
            <template #default="scope">
              <el-input v-model="scope.row.shortageDescription"
                        placeholder="请输入缺货情况"
                        clearable />
            </template>
          </el-table-column>
          <el-table-column label="税率(%)"
@@ -486,6 +513,8 @@
    const stockInData = {
      id: selectedRows.value[0].recordId,
      quantityStock: Number(selectedRows.value[0].quantityStock), // ä½¿ç”¨æ–°æ ¼å¼åŒ–函数
      outStockQuantity: selectedRows.value[0].outStockQuantity,
      shortageDescription: selectedRows.value[0].shortageDescription,
    };
    await updateStockIn(stockInData);
    proxy.$modal.msgSuccess("修改入库成功");
@@ -531,6 +560,8 @@
          // id: product.salesLedgerProductId,
          inboundQuantity: Number(product.quantityStock),
          productModelId: product.productModelId,
          outStockQuantity: product.outStockQuantity,
          shortageDescription: product.shortageDescription,
        })),
      };
      // è°ƒç”¨API
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>
@@ -31,10 +31,10 @@
          <el-icon><Plus /></el-icon>
          æ–°å¢žä»·æ ¼
        </el-button>
        <el-button type="success" @click="openBatchDiscountDialog">
        <!-- <el-button type="success" @click="openBatchDiscountDialog">
          <el-icon><Discount /></el-icon>
          æ‰¹é‡æŠ˜æ‰£
        </el-button>
        </el-button> -->
        <el-button type="info" @click="exportData">
          <el-icon><Download /></el-icon>
          å¯¼å‡ºæ•°æ®
@@ -62,17 +62,16 @@
            <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>
          </template>
        </el-table-column>
        <el-table-column label="供应商" prop="supplierName" width="150" />
        <el-table-column label="基础价格" width="120" align="right">
        <el-table-column label="供应商" prop="supplierName" width="200" />
        <el-table-column label="基础价格(不含税)" width="150" align="right">
          <template #default="{ row }">
            <span class="price-text">Â¥{{ row.basePrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="折扣信息" width="150">
        <el-table-column label="折扣信息" width="120" align="center">
          <template #default="{ row }">
            <div v-if="row.discountType">
              <el-tag :type="getDiscountTagType(row.discountType)" size="small">
@@ -83,9 +82,12 @@
            <span v-else class="no-discount">无折扣</span>
          </template>
        </el-table-column>
        <el-table-column label="实际价格" width="120" align="right">
        <!-- <el-table-column label="实际价格(不含税)" prop="actuallyPrice" width="150" align="right"> -->
        <!-- </el-table-column> -->
        <el-table-column label="实际价格(不含税)"  width="150" align="right">
          <template #default="{ row }">
            <span class="final-price">Â¥{{ calculateFinalPrice(row) }}</span>
            <!-- <span class="final-price">Â¥{{ calculateFinalPrice(row) }}</span> -->
            <span class="final-price" >Â¥{{ row.actuallyPrice?row.actuallyPrice:row.basePrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="价格控制" width="120">
@@ -103,13 +105,14 @@
        <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" />
        <el-table-column label="更新时间" prop="updateTime" width="180" sortable />
        <el-table-column label="生效时间" prop="effectiveTime" width="100" align="center" />
        <el-table-column label="更新时间" prop="updateTime" width="160" sortable align="center" />
        <el-table-column label="备注" width="200" prop="remark" align="center" show-overflow-tooltip />
        <el-table-column label="操作" width="250" align="center" fixed="right">
          <template #default="{ row }">
            <el-button type="primary" link @click="openDialog('edit', row)">
@@ -143,38 +146,65 @@
      <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">
            <el-form-item label="商品编码" prop="productCode">
              <el-input v-model="formData.productCode" placeholder="请输入商品编码" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="规格型号" prop="specification">
              <el-input v-model="formData.specification" placeholder="请输入规格型号" />
            </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-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-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <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>
          <el-col :span="12">
            <el-form-item label="基础价格" prop="basePrice">
              <el-input-number v-model="formData.basePrice" :min="0" :precision="2" placeholder="请输入基础价格" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="单位">
              <el-input v-model="formData.unit" placeholder="请输入单位" />
@@ -185,7 +215,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 +224,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 +237,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 +253,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 +361,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 +376,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 +398,12 @@
// è¡¨å•数据
const formData = reactive({
  productId: null,
  productName: '',
  productCode: '',
  specification: '',
  supplierId: null,
  supplierName: '',
  specificationId: null,
  basePrice: 0,
  unit: '',
  discountType: '',
@@ -405,24 +438,18 @@
// è¡¨å•验证规则
const formRules = {
  productName: [{ required: true, message: '请选择商品名称', trigger: 'change' }],
  productCode: [{ required: true, message: '请输入商品编码', trigger: 'blur' }],
  supplierName: [{ required: true, message: '请选择供应商', trigger: 'change' }],
  productId: [{ 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 +461,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 +482,39 @@
  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;
    }
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
    formData.specification = "";
  } else {
    formData.productName = "";
    modelOptions.value = [];
  }
};
const getStatusType = (status) => {
  const statusMap = {
    active: 'success',
@@ -461,6 +522,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 +553,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 +576,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 +594,10 @@
const resetFormData = () => {
  Object.assign(formData, {
    productId: null,
    productName: '',
    productCode: '',
    specification: '',
    supplierId: null,
    supplierName: '',
    basePrice: 0,
    unit: '',
@@ -543,13 +623,13 @@
  })
}
const removeTieredRow = (index) => {
  formData.tieredDiscount.splice(index, 1)
}
const handleSubmit = async () => {
  if (!formRef.value) return
    formData.actuallyPrice = Number(calculateFinalPrice(formData))
    if( formData.discountType === ''){
      formData.discountEndTime = '2099-12-31 23:59:59'
    }
  try {
    await formRef.value.validate()
    submitLoading.value = true
@@ -677,6 +757,7 @@
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  handleSearch()
  getProductOptions()
})
</script>
src/views/procurementManagement/procurementLedger/index.vue
@@ -250,6 +250,7 @@
                v-model="form.supplierId"
                placeholder="请选择"
                clearable
                @change="handleSupplierChange"
              >
                <el-option
                  v-for="item in supplierList"
@@ -552,6 +553,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 +642,83 @@
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- åŽ†å²é‡‡è´­ä»·æ ¼å‚è€ƒå¼¹çª— -->
    <el-dialog
      v-model="priceReferenceVisible"
      :title="`历史采购价格参考 ${productForm.supplierName}-${productForm.productName}-${productForm.productModel}`"
      width="1000px"
      append-to-body
    >
      <el-table
        :data="priceReferenceData"
        border
        v-loading="priceReferenceLoading"
        :default-sort="{ prop: 'updateTime', order: 'descending' }"
      >
        <!-- <el-table-column label="商品信息" min-width="200">
          <template #default="{ row }">
            <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>
          </template>
        </el-table-column> -->
        <!-- <el-table-column label="供应商" prop="supplierName" width="200" /> -->
        <el-table-column label="基础价格(不含税)" width="150" align="right">
          <template #default="{ row }">
            <span class="price-text">Â¥{{ row.basePrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="实际价格(不含税)" prop="actuallyPrice" width="150" align="right"/>
        <el-table-column label="折扣信息" width="120" align="center">
          <template #default="{ row }">
            <div v-if="row.discountType">
              <el-tag :type="getDiscountTagType(row.discountType)" size="small">
                {{ getDiscountText(row.discountType) }}
              </el-tag>
              <div class="discount-value">{{ row.discountValue }}{{ row.discountType === 'percentage' ? '%' : '元' }}</div>
            </div>
            <span v-else class="no-discount">无折扣</span>
          </template>
        </el-table-column>
        <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">
              <el-icon color="#F56C6C"><Warning /></el-icon>
            </div> -->
          </template>
        </el-table-column>
        <el-table-column label="生效时间" prop="effectiveTime" width="100" align="center" />
        <el-table-column label="更新时间" prop="updateTime" width="160" sortable align="center" />
        <el-table-column label="备注" width="200" prop="remark" align="center" show-overflow-tooltip />
        <el-table-column label="操作" width="100" align="center" fixed="right">
          <template #default="{ row }">
            <el-button type="success" link @click="selectPriceReference(row)">
              <el-icon><Check /></el-icon>
              å¼•用
            </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>
@@ -860,7 +943,7 @@
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { Search } from "@element-plus/icons-vue";
import { Search, Check } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import {
@@ -903,6 +986,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();
@@ -975,6 +1059,9 @@
    taxExclusiveTotalPrice: "",
    invoiceType: "",
        warnNum: "",
    supplierName: "",
    productName: "",
    productModel: "",
  },
  productRules: {
    productId: [{ required: true, message: "请选择", trigger: "change" }],
@@ -996,6 +1083,139 @@
  },
});
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 calculateFinalPrice = (row) => {
  let finalPrice = row.basePrice;
  if (row.discountType === "percentage") {
    finalPrice = row.basePrice * (1 - row.discountValue / 100);
  } else if (row.discountType === "fixed") {
    finalPrice = row.basePrice - row.discountValue;
  }
  let man = Math.max(finalPrice, 0);
  return man.toFixed(2);
};
const getDiscountTagType = (discountType) => {
  const typeMap = {
    percentage: "success",
    fixed: "warning",
    tiered: "info",
  };
  return typeMap[discountType] || "info";
};
const getDiscountText = (discountType) => {
  const textMap = {
    percentage: "百分比",
    fixed: "固定金额",
  };
  return textMap[discountType] || "未知";
};
const getStatusType = (status) => {
  const statusMap = {
    active: "success",
    pending: "warning",
    expired: "info",
  };
  return statusMap[status] || "info";
};
const getStatusText = (status) => {
  const statusMap = {
    active: "有效",
    pending: "待生效",
    expired: "已过期",
  };
  return statusMap[status] || "未知";
};
const showPriceReference = () => {
  if (form.value.supplierId) {
    const supplier = supplierList.value.find((item) => item.id === form.value.supplierId);
    if (supplier) {
      productForm.value.supplierName = supplier.supplierName;
    }
  }
  priceReferenceVisible.value = true;
  handlePriceReferenceSearch();
};
const handlePriceReferenceSearch = () => {
  priceReferenceLoading.value = true;
  // æ¨¡æ‹Ÿæœç´¢å‚数:productId æ˜ å°„为 productName æˆ– ID,这里根据 advancedPriceManagement çš„ API ç¡®å®šå‚æ•°
  // å‡è®¾é«˜çº§ä»·æ ¼ç®¡ç†çš„ listPage æŽ¥æ”¶ productId
  const query = {
    productId: productForm.value.productId,
    supplierId: form.value.supplierId,
    current: priceReferencePagination.current,
    size: priceReferencePagination.size
  };
  listAdvancedPrice(query).then(res => {
    console.log(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 taxExclusivePrice = row.basePrice;
  if (row.discountType === "percentage") {
    taxExclusivePrice = row.basePrice * (1 - row.discountValue / 100);
  } else if (row.discountType === "fixed") {
    taxExclusivePrice = row.basePrice - row.discountValue;
  }
  taxExclusivePrice = Number(Math.max(taxExclusivePrice, 0));
  // è®¾ç½®æ•°é‡ä¸º 1
  if (!productForm.value.quantity) {
    productForm.value.quantity = 1;
  }
  // å¦‚果已有税率,则计算含税单价
  if (productForm.value.taxRate) {
    const taxRate = Number(productForm.value.taxRate);
    productForm.value.taxInclusiveUnitPrice = (
      taxExclusivePrice *
      (1 + taxRate / 100)
    ).toFixed(2);
  } else {
    // å¦‚果没有税率,暂时将含税单价设为不含税价格,提示用户选择税率
    productForm.value.taxInclusiveUnitPrice = taxExclusivePrice.toFixed(2);
    proxy.$modal.msgWarning("请选择税率以准确计算含税价格");
  }
  // è‡ªåŠ¨è§¦å‘è®¡ç®—é€»è¾‘ï¼ˆè®¡ç®—æ€»ä»·ç­‰ï¼‰
  mathNum();
  priceReferenceVisible.value = false;
  proxy.$modal.msgSuccess("已引用历史价格(不含税)");
};
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
@@ -1230,13 +1450,38 @@
};
// æ‰“开产品弹框
const openProductForm = (type, row, index) => {
  if(!form.value.supplierId){
    proxy.$modal.msgWarning("请选择供应商");
    return;
  }
  productOperationType.value = type;
  productOperationIndex.value = index;
  productForm.value = {};
  productForm.value = {
    supplierName: "",
    productName: "",
    productModel: "",
  };
  proxy.resetForm("productFormRef");
  if (type === "edit") {
    productForm.value = { ...row };
    productForm.value = {
      ...row,
      productName: row.productCategory || "",
      productModel: row.specificationModel || ""
    };
    // ç¼–辑时,根据 productId é¢„加载规格型号列表,确保下拉框显示名称而非 ID
    if (productForm.value.productId) {
      modelList({ id: productForm.value.productId }).then((res) => {
        modelOptions.value = res;
      });
    }
  }
  // ç¡®ä¿ä¾›åº”商名称始终同步
  const supplier = supplierList.value.find((item) => item.id === form.value.supplierId);
  if (supplier) {
    productForm.value.supplierName = supplier.supplierName;
  }
  productFormVisible.value = true;
  getProductOptions();
};
@@ -1248,11 +1493,14 @@
const getModels = (value) => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    productForm.value.productName = productForm.value.productCategory;
    productForm.value.productModelId = "";
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  } else {
    productForm.value.productCategory = "";
    productForm.value.productName = "";
    modelOptions.value = [];
  }
};
@@ -1260,12 +1508,24 @@
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.productModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.productModel = null;
    productForm.value.unit = null;
  }
};
const handleSupplierChange = (value) => {
  const supplier = supplierList.value.find((item) => item.id === value);
  if (supplier) {
    form.value.supplierName = supplier.supplierName;
    productForm.value.supplierName = supplier.supplierName;
  } else {
    form.value.supplierName = "";
    productForm.value.supplierName = "";
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
src/views/salesManagement/salesLedger/index.vue
@@ -1669,7 +1669,7 @@
    };
    deliveryFormVisible.value = true;
  }).catch(err => {
    ElMessage.error(err.msg);
    // ElMessage.error(err);
  });
};