src/views/procurementManagement/procurementLedger/index.vue
@@ -238,6 +238,17 @@
            </el-form-item>
          </el-col>
               <el-col :span="12">
            <el-form-item label="项目名称" prop="projectName">
              <el-input
                  v-model="form.projectName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
               </el-col>
        </el-row>
            <el-row :gutter="30">
          <el-col :span="12">
                  <el-form-item label="付款方式">
                     <el-input
                        v-model="form.paymentMethod"
@@ -246,8 +257,6 @@
                     />
                  </el-form-item>
               </el-col>
        </el-row>
            <el-row :gutter="30">
               <el-col :span="12">
                  <el-form-item label="签订日期:" prop="executionDate">
                     <el-date-picker
@@ -264,14 +273,26 @@
            </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:" prop="recorderId">
            <el-form-item label="审批人:" prop="approverId">
              <el-select
                  v-model="form.approverId"
                  placeholder="请选择审批人"
                  clearable
              >
                <el-option
                    v-for="item in userList"
                    :key="item.userId"
                    :label="item.nickName"
                    :value="item.userId"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="录入人:" prop="recorderId" v-show="false">
              <el-select
                v-model="form.recorderId"
                placeholder="请选择"
                clearable
                filterable
                default-first-option
                :reserve-keyword="false"
                  disabled
              >
                <el-option
                  v-for="item in userList"
@@ -305,6 +326,37 @@
              >删除</el-button
            >
          </el-form-item>
          <div class="select-button-group" style="width: 220px; margin: 20px 0;" v-if="operationType === 'add'">
            <el-select
                filterable
                allow-create
                :reserve-keyword="true"
                :default-first-option="false"
                v-model="templateName"
                :input-value="filterInputValue"
                @filter-change="onTemplateFilterChange"
                @change="onTemplateChange"
                style="width: 180px; border-right: none; border-radius: 4px 0 0 4px;"
                placeholder="请选择"
                class="no-arrow-select"
            >
              <el-option
                  v-for="item in templateList"
                  :key="item.value"
                  :label="item.templateName"
                  :value="item.templateName"
              ></el-option>
            </el-select>
            <!-- 按钮:与 Select 高度匹配,去掉左侧边框,无缝衔接 -->
            <el-button
                size="small"
                style="height: 32px; border-radius: 0 4px 4px 0; margin-left: -1px;"
                @click="handleButtonClick"
                :disabled="!templateName || templateName.trim() === '' || isTemplateNameDuplicate"
            >
              保存
            </el-button>
          </div>
        </el-row>
        <el-table
          :data="productData"
@@ -776,7 +828,7 @@
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { ElMessageBox,ElMessage } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import FileList from "./fileList.vue";
import {
@@ -795,6 +847,8 @@
  getPurchaseById,
  getOptions,
  createPurchaseNo,
  getPurchaseTemplateList,
  addPurchaseTemplate,
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import QRCode from "qrcode";
@@ -825,6 +879,103 @@
const qrCodeDialogVisible = ref(false);
const qrCodeUrl = ref("");
// 订单审批状态显示文本
const approvalStatusText = {
  0: '审批中',
  1: '审批通过',
  2: '审批失败'
};
const templateName = ref('');
const filterInputValue = ref('');
const templateList = ref([]);
const isTemplateNameDuplicate = ref(false); // 标记模板名称是否重复
// 检查模板名称是否重复
const checkTemplateNameDuplicate = (name) => {
  if (!name || name.trim() === '') {
    isTemplateNameDuplicate.value = false;
    return false;
  }
  const isDuplicate = templateList.value.some(item => item.templateName === name.trim());
  isTemplateNameDuplicate.value = isDuplicate;
  return isDuplicate;
};
// 防抖定时器
let duplicateCheckTimer = null;
const onTemplateFilterChange = (val) => {
  filterInputValue.value = val ?? '';
  // 清除之前的定时器
  if (duplicateCheckTimer) {
    clearTimeout(duplicateCheckTimer);
  }
  // 实时检查模板名称是否重复(防抖处理,避免频繁提示)
  if (val && val.trim()) {
    duplicateCheckTimer = setTimeout(() => {
      const isDuplicate = checkTemplateNameDuplicate(val);
      if (isDuplicate) {
        ElMessage({
          message: '模板名称已存在,请更换模板名称',
          type: 'warning',
          duration: 2000
        });
      }
    }, 300); // 300ms 防抖
  } else {
    isTemplateNameDuplicate.value = false;
  }
};
// allow-create 时,输入不存在的内容会作为 string 值返回;这里同步回输入框以确保文字不丢
const onTemplateChange = async (val) => {
  if (typeof val === 'string') {
    filterInputValue.value = val;
    // 选择或输入时检查重复
    checkTemplateNameDuplicate(val);
  }
  // 过滤数据,查找匹配的模板
  const matchedTemplate = templateList.value.find(item => item.templateName === val);
  if (matchedTemplate?.id) {
    // 如果找到模板,加载模板数据
    form.value = {
      ...form.value,
      ...matchedTemplate,
    };
    productData.value = matchedTemplate.productData || [];
    // 生成新的采购合同号
    try {
      const res = await createPurchaseNo();
      if (res?.data) {
        form.value.purchaseContractNumber = res.data;
      }
    } catch (error) {
      console.error('生成采购合同号失败:', error);
    }
  } else {
    // 如果没有找到模板,重置表单(保持当前表单状态)
    const currentFormData = { ...form.value };
    const currentProductData = [...productData.value];
    // 如果对话框未打开,先打开
    if (!dialogFormVisible.value) {
      operationType.value = 'add';
      dialogFormVisible.value = true;
    }
    // 等待下一个 tick 后恢复数据
    await nextTick();
    form.value = {
      ...form.value,
      ...currentFormData,
    };
    productData.value = currentProductData;
  }
};
// 用户信息表单弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
@@ -852,7 +1003,8 @@
    purchaseContractNumber: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
    approverId:[{ required: true, message: "请选择审批人", trigger: "change" }],
    projectName:[{ required:true, message:"请输入项目名称", trigger:"blur"}],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
@@ -933,6 +1085,83 @@
  page.current = 1;
  getList();
};
// 保存模板
const handleButtonClick = async () => {
  // 检查模板名称是否为空
  if (!templateName.value || templateName.value.trim() === '') {
    ElMessage({
      message: '请输入模板名称',
      type: 'warning',
    });
    return;
  }
  // 检查模板名称是否重复
  const isDuplicate = checkTemplateNameDuplicate(templateName.value);
  if (isDuplicate) {
    ElMessage({
      message: '模板名称已存在,请更换模板名称',
      type: 'warning',
    });
    return;
  }
  // 检查供应商是否选择
  if (!form.value.supplierId) {
    ElMessage({
      message: '请先选择供应商',
      type: 'warning',
    });
    return;
  }
  // 检查是否有产品数据
  // if (!productData.value || productData.value.length === 0) {
  //   ElMessage({
  //     message: '请先添加产品信息',
  //     type: 'warning',
  //   });
  //   return;
  // }
  try {
    let params = {
      productData: proxy.HaveJson(productData.value),
      supplierId: form.value.supplierId,
      paymentMethod: form.value.paymentMethod,
      recorderId: form.value.recorderId,
      approverId: form.value.approverId,
      templateName: templateName.value.trim()
    };
    console.log(params);
    let res = await addPurchaseTemplate(params);
    if (res && res.code === 200) {
      ElMessage({
        message: '模板保存成功',
        type: 'success',
      });
      // 保存成功后重新获取模板列表
      await getTemplateList();
      // 清空模板名称输入
      templateName.value = '';
      filterInputValue.value = '';
      isTemplateNameDuplicate.value = false;
    } else {
      ElMessage({
        message: res?.msg || '模板保存失败',
        type: 'error',
      });
    }
  } catch (error) {
    console.error('保存模板失败:', error);
    ElMessage({
      message: '模板保存失败,请稍后重试',
      type: 'error',
    });
  }
};
// 子表合计方法
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(
@@ -983,19 +1212,24 @@
};
const expandedRowKeys = ref([]);
// 展开行
const expandChange = (row, expandedRows) => {
const expandChange = async (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
      const res = await productList({ salesLedgerId: row.id, type: 2 });
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        tableData.value[index].children = res.data || [];
        expandedRowKeys.value.push(row.id);
      });
      }
    } catch (error) {
      console.log(error);
      console.error('加载产品列表失败:', error);
      proxy.$modal.msgError('加载产品列表失败');
      // 展开失败时,移除展开状态
      const index = expandedRows.findIndex(item => item.id === row.id);
      if (index > -1) {
        expandedRows.splice(index, 1);
      }
    }
  } else {
    expandedRowKeys.value = [];
@@ -1014,40 +1248,63 @@
  ]);
};
// 打开弹框
const openForm = (type, row) => {
const openForm = async (type, row) => {
  await getTemplateList()
  operationType.value = type;
  form.value = {};
  productData.value = [];
  fileList.value = [];
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      form.value.purchaseContractNumber = res.data;
    });
  }
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  getSalesNo().then((res) => {
    salesContractList.value = res;
  });
  getOptions().then((res) => {
    supplierList.value = res.data;
  });
  templateName.value = '';
  filterInputValue.value = '';
  isTemplateNameDuplicate.value = false;
  try {
    // 并行加载基础数据
    const [userRes, salesRes, supplierRes] = await Promise.all([
      userListNoPage(),
      getSalesNo(),
      getOptions()
    ]);
    userList.value = userRes.data || [];
    salesContractList.value = salesRes || [];
    // 供应商过滤出isWhite=0 的数据
    supplierList.value = (supplierRes.data || []).filter((item) => item.isWhite === 0);
    // 设置默认值
  form.value.recorderId = userStore.id;
  form.value.entryDate = getCurrentDate();
  if (type === "edit") {
    if (type === "add") {
      // 新增时生成采购合同号
      try {
        const purchaseNoRes = await createPurchaseNo();
        if (purchaseNoRes?.data) {
          form.value.purchaseContractNumber = purchaseNoRes.data;
        }
      } catch (error) {
        console.error('生成采购合同号失败:', error);
        proxy.$modal.msgWarning('生成采购合同号失败');
      }
    } else if (type === "edit" && row?.id) {
      // 编辑时加载数据
    currentId.value = row.id;
    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
      form.value = { ...res };
      productData.value = form.value.productData;
      if (form.value.salesLedgerFiles) {
        fileList.value = form.value.salesLedgerFiles;
      } else {
        fileList.value = [];
      try {
        const purchaseRes = await getPurchaseById({ id: row.id, type: 2 });
        form.value = { ...purchaseRes };
        productData.value = purchaseRes.productData || [];
        fileList.value = purchaseRes.salesLedgerFiles || [];
      } catch (error) {
        console.error('加载采购台账数据失败:', error);
        proxy.$modal.msgError('加载数据失败');
        return;
      }
    });
  }
  dialogFormVisible.value = true;
  } catch (error) {
    console.error('打开表单失败:', error);
    proxy.$modal.msgError('加载基础数据失败');
  }
};
// 上传前校检
function handleBeforeUpload(file) {
@@ -1586,11 +1843,41 @@
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 添加行类名方法
const tableRowClassName = ({ row }) => {
  return row.isInvalid ? 'invalid-row' : '';
};
// 获取模板信息
const getTemplateList =async ()=>{
  let res = await getPurchaseTemplateList()
  if(res && res.code===200 && Array.isArray(res.data)){
    templateList.value = res.data
  }
}
onMounted(() => {
  getList();
  getTemplateList();
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.invalid-row {
  opacity: 0.6;
  background-color: #f5f7fa;
}
.el-row{
  justify-content: space-between;
  align-items: center
}
.no-arrow-select {
  --el-select-suffix-icon-color: transparent; /* 隐藏默认下拉箭头 */
}
.select-button-group {
  display: flex;
  align-items: center;
}
</style>