src/views/salesManagement/salesQuotation/index.vue
@@ -229,40 +229,14 @@
          </template>
          <div class="form-content">
            <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
            <el-table-column prop="product" label="产品名称" width="200">
            <el-table-column prop="product" label="产品名称" width="220">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
                  <el-tree-select
                    v-model="scope.row.productId"
                    placeholder="请选择"
                    clearable
                    check-strictly
                    @change="getModels($event, scope.row)"
                    :data="productOptions"
                    :render-after-expand="false"
                    style="width: 100%"
                  />
                </el-form-item>
                <span>{{ scope.row.product || scope.row.productName || '--' }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="specification" label="图纸编号" width="200">
            <el-table-column prop="specification" label="图纸编号" width="220">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
                  <el-select
                    v-model="scope.row.specificationId"
                    placeholder="请选择"
                    clearable
                    @change="getProductModel($event, scope.row)"
                    style="width: 100%"
                  >
                    <el-option
                      v-for="item in scope.row.modelOptions || []"
                      :key="item.id"
                      :label="item.model"
                      :value="item.id"
                    />
                  </el-select>
                </el-form-item>
                <span>{{ scope.row.specification || '--' }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="unit" label="单位">
@@ -275,7 +249,7 @@
            <el-table-column prop="unitPrice" label="单价">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
                  <el-input-number v-model="scope.row.unitPrice" :min="0.01" :precision="2" :step="0.01" style="width: 100%" />
                </el-form-item>
              </template>
            </el-table-column>
@@ -313,6 +287,12 @@
      </el-form>
      </div>
    </FormDialog>
    <ProductSelectDialog
      v-model="productSelectVisible"
      :single="true"
      @confirm="handleProductSelectConfirm"
    />
    <!-- 查看详情对话框 -->
    <el-dialog v-model="viewDialogVisible" title="报价详情" width="800px">
@@ -359,6 +339,7 @@
import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
import Pagination from '@/components/PIMTable/Pagination.vue'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import ProductSelectDialog from '@/views/basicData/product/ProductSelectDialog.vue'
import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
import {userListNoPage} from "@/api/system/user.js";
import {customerList} from "@/api/salesManagement/salesLedger.js";
@@ -374,7 +355,6 @@
const quotationList = ref([])
const productOptions = ref([]);
const modelOptions = ref([]);
const pagination = reactive({
  total: 3,
  currentPage: 1,
@@ -383,6 +363,8 @@
const dialogVisible = ref(false)
const viewDialogVisible = ref(false)
const productSelectVisible = ref(false)
const activeProductIndex = ref(-1)
const dialogTitle = ref('新增报价')
const form = reactive({
  quotationNo: '',
@@ -541,65 +523,6 @@
   }
   return null;
}
const getModels = (value, row) => {
   if (!row) return;
   // 如果清空选择,则清空相关字段
   if (!value) {
      row.productId = '';
      row.product = '';
      row.modelOptions = [];
      row.specificationId = '';
      row.specification = '';
      row.unit = '';
      return;
   }
   // 更新 productId(v-model 已经自动更新,这里确保一致性)
   row.productId = value;
   // 找到对应的 label 并赋值给 row.product
   const label = findNodeById(productOptions.value, value);
   if (label) {
      row.product = label;
   }
   // 获取规格型号列表,设置到当前行的 modelOptions
   modelList({ id: value }).then((res) => {
      row.modelOptions = res || [];
   });
};
const getProductModel = (value, row) => {
   if (!row) return;
   // 如果清空选择,则清空相关字段
   if (!value) {
      row.specificationId = '';
      row.specification = '';
      row.unit = '';
      return;
   }
   // 更新 specificationId(v-model 已经自动更新,这里确保一致性)
   row.specificationId = value;
   const modelOptions = row.modelOptions || [];
   const index = modelOptions.findIndex((item) => item.id === value);
   if (index !== -1) {
      row.specification = modelOptions[index].model;
      row.unit = modelOptions[index].unit;
   } else {
      row.specification = '';
      row.unit = '';
   }
};
const findNodeById = (nodes, productId) => {
   for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
         return nodes[i].label; // 找到节点,返回 label
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
         const foundLabel = findNodeById(nodes[i].children, productId);
         if (foundLabel) {
            return foundLabel; // 在子节点中找到,返回 label
         }
      }
   }
   return null; // 没有找到节点,返回null
};
const handleView = (row) => {
  // 只复制需要的字段,避免将组件引用放入响应式对象
  currentQuotation.value = {
@@ -755,23 +678,51 @@
}
const addProduct = () => {
  form.products.push({
    productId: '',
    product: '',
    productName: '',
    specificationId: '',
    specification: '',
    quantity: 1,
    unit: '',
    unitPrice: 0,
    amount: 0,
    modelOptions: [] // 为每行添加独立的规格型号列表
  })
  activeProductIndex.value = -1
  productSelectVisible.value = true
}
const removeProduct = (index) => {
  form.products.splice(index, 1)
  calculateSubtotal()
}
const handleProductSelectConfirm = (rows) => {
  if (!rows || rows.length === 0 || activeProductIndex.value < 0) {
    if (!rows || rows.length === 0) {
      return
    }
    const selected = rows[0]
    form.products.push({
      productId: selected.id,
      product: selected.productName || '',
      productName: selected.productName || '',
      specificationId: selected.id,
      specification: selected.model || '',
      quantity: 1,
      unit: selected.unit || '',
    unitPrice: 0.01,
      amount: 0,
      modelOptions: [],
    })
    calculateSubtotal()
    activeProductIndex.value = -1
    return
  }
  const row = form.products[activeProductIndex.value]
  if (!row) {
    return
  }
  const selected = rows[0]
  row.productId = selected.id
  row.product = selected.productName || ''
  row.productName = selected.productName || ''
  row.specificationId = selected.id
  row.specification = selected.model || ''
  row.unit = selected.unit || row.unit || ''
  calculateAmount(row)
}
const calculateAmount = (product) => {
@@ -801,15 +752,17 @@
        return
      }
      // 审批人必填校验
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
      if (hasEmptyApprover) {
        ElMessage.error('请为所有审批节点选择审批人!')
      const hasInvalidPrice = form.products.some(product => Number(product.unitPrice) <= 0)
      if (hasInvalidPrice) {
        ElMessage.error('单价必须大于0')
        return
      }
      // 收集所有节点的审批人id
      form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
      // 收集所有节点的审批人id,允许为空
      form.approveUserIds = approverNodes.value
        .map(node => node.userId)
        .filter(userId => userId !== null && userId !== undefined && userId !== '')
        .join(',')
      
      // 计算所有产品的单价总和
      form.totalAmount = form.products.reduce((sum, product) => {