gaoluyang
2026-03-12 fe167dd71a1300aeae07522db990d6b3fdb77a0e
src/views/projectManagement/Management/components/formDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1503 @@
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="dialogTitle"
    width="95%"
    top="5vh"
    destroy-on-close
    @close="closeDialog"
  >
    <el-form
      ref="formRef"
      :model="form"
      :rules="rules"
      label-position="top"
      label-width="120px"
      :disabled="isView"
    >
      <div class="section">
        <div class="section-header" @click="toggleSection('base')">
          <div class="section-title">
            <span class="section-bar" />
            <span>基础资料</span>
          </div>
          <el-icon class="toggle-icon">
            <ArrowDown v-if="sectionCollapsed.base" />
            <ArrowUp v-else />
          </el-icon>
        </div>
        <div v-show="!sectionCollapsed.base" class="section-body">
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="单据编号" prop="billNo">
                <el-input v-model="form.billNo" placeholder="系统生成" disabled />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="项目名称" prop="projectName">
                <el-input v-model="form.projectName" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="客户名称" prop="customerName">
                <el-input v-model="form.customerName" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="立项日期" prop="setupDate">
                <el-date-picker
                  v-model="form.setupDate"
                  type="date"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  placeholder="请选择"
                  style="width: 100%"
                  clearable
                />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="项目来源" prop="projectSource">
                <el-input v-model="form.projectSource" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="立项人" prop="creatorName">
                <el-input v-model="form.creatorName" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="预计工期(天)" prop="estimatedDays">
                <el-input-number v-model="form.estimatedDays" :min="0" controls-position="right" style="width: 100%" />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="计划开始日期" prop="planStartDate">
                <el-date-picker
                  v-model="form.planStartDate"
                  type="date"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  placeholder="请选择"
                  style="width: 100%"
                  clearable
                />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="计划完成日期" prop="planEndDate">
                <el-date-picker
                  v-model="form.planEndDate"
                  type="date"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  placeholder="请选择"
                  style="width: 100%"
                  clearable
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="项目类型" prop="projectManagementPlanId">
                <el-select v-model="form.projectManagementPlanId" placeholder="请选择" clearable style="width: 100%">
                  <el-option v-for="opt in projectTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="项目金额" prop="projectAmount">
                <el-input-number v-model="form.projectAmount" :min="0" controls-position="right" style="width: 100%" />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="审核状态" prop="auditStatus">
                <el-select v-model="form.auditStatus" placeholder="请选择" clearable style="width: 100%">
                  <el-option v-for="d in project_management" :key="d.value" :label="d.label" :value="d.value" />
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="10" >
              <el-col :span="24">
                <el-upload
                  v-model:file-list="fileList"
                  :action="upload.url"
                  :headers="upload.headers"
                  multiple
                  :disabled="isView"
                  :before-upload="beforeUpload"
                  :on-success="handleUploadSuccess"
                  :on-error="handleUploadError"
                  name="files"
                  :on-remove="handleRemove"
                >
                  <el-button type="primary" :disabled="isView">上传文件</el-button>
                </el-upload>
                <div v-if="existingAttachments.length > 0" class="attachment-list">
                  <div
                    v-for="(att, idx) in existingAttachments"
                    :key="att.id || att.url || idx"
                    class="attachment-item"
                  >
                    <el-icon><Document /></el-icon>
                    <span class="attachment-name">{{ att.name || att.fileName || att.url || '附件' }}</span>
                    <el-button link type="primary" size="small" @click="downloadAttachment(att)">下载</el-button>
                  </div>
                </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="24">
              <el-form-item label="备注" prop="remark">
                <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入" maxlength="100" show-word-limit />
              </el-form-item>
            </el-col>
          </el-row>
        </div>
      </div>
      <div class="section">
        <div class="section-header" @click="toggleSection('product')">
          <div class="section-title">
            <span class="section-bar" />
            <span>产品信息</span>
          </div>
          <div class="section-actions" @click.stop>
            <el-button v-if="!isView" type="primary" @click="openProductForm('add')">添加</el-button>
            <el-button v-if="!isView" plain type="danger" @click="deleteProduct">删除</el-button>
            <el-icon class="toggle-icon" @click="toggleSection('product')">
              <ArrowDown v-if="sectionCollapsed.product" />
              <ArrowUp v-else />
            </el-icon>
          </div>
        </div>
        <div v-show="!sectionCollapsed.product" class="section-body">
          <el-table
            :data="productData"
            border
            show-summary
            :summary-method="summarizeProductTable"
            @selection-change="productSelected"
          >
            <el-table-column v-if="!isView" align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="产品大类" prop="productCategory" />
            <el-table-column label="规格型号" prop="specificationModel" />
            <el-table-column label="单位" prop="unit" />
            <el-table-column label="数量" prop="quantity" />
            <el-table-column label="税率(%)" prop="taxRate" />
            <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
            <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
            <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
            <el-table-column v-if="!isView" fixed="right" label="操作" min-width="60" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row, scope.$index)">编辑</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>
      <div class="section">
        <div class="section-header" @click="toggleSection('team')">
          <div class="section-title">
            <span class="section-bar" />
            <span>项目团队</span>
          </div>
          <div class="section-actions" @click.stop>
            <el-button v-if="!isView" type="primary" :icon="Plus" @click="addTeamRow">新增行</el-button>
            <el-icon class="toggle-icon" @click="toggleSection('team')">
              <ArrowDown v-if="sectionCollapsed.team" />
              <ArrowUp v-else />
            </el-icon>
          </div>
        </div>
        <div v-show="!sectionCollapsed.team" class="section-body">
          <PIMTable
            :column="teamColumns"
            :tableData="form.teamList"
            :tableLoading="false"
            :isSelection="false"
            :isShowPagination="false"
            height="220"
          >
            <template #memberId="{ row }">
              <el-select v-model="row.memberId" placeholder="请选择" filterable clearable style="width: 100%" :disabled="isView">
                <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
              </el-select>
            </template>
            <template #roleId="{ row }">
              <el-select v-model="row.roleId" placeholder="请选择" clearable style="width: 100%" :disabled="isView">
                <el-option v-for="r in roleOptions" :key="r.value" :label="r.label" :value="r.value" />
              </el-select>
            </template>
            <template #enterDate="{ row }">
              <el-date-picker
                v-model="row.enterDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #leaveDate="{ row }">
              <el-date-picker
                v-model="row.leaveDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #phone="{ row }">
              <el-input v-model="row.phone" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #teamRemark="{ row }">
              <el-input v-model="row.remark" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #teamAction="{ row, index }">
              <el-button v-if="!isView" link type="danger" :icon="Delete" @click="removeTeamRow(index)">删除</el-button>
              <span v-else>—</span>
            </template>
          </PIMTable>
        </div>
      </div>
      <!-- <div class="section">
        <div class="section-header" @click="toggleSection('phase')">
          <div class="section-title">
            <span class="section-bar" />
            <span>项目阶段</span>
          </div>
          <div class="section-actions" @click.stop>
            <el-button v-if="!isView" type="primary" :icon="Plus" @click="addPhaseRow">新增行</el-button>
            <el-icon class="toggle-icon" @click="toggleSection('phase')">
              <ArrowDown v-if="sectionCollapsed.phase" />
              <ArrowUp v-else />
            </el-icon>
          </div>
        </div>
        <div v-show="!sectionCollapsed.phase" class="section-body">
          <PIMTable
            :column="phaseColumns"
            :tableData="form.phaseList"
            :tableLoading="false"
            :isSelection="false"
            :isShowPagination="false"
            height="240"
          >
            <template #phaseName="{ row }">
              <el-input v-model="row.phaseName" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #phaseDesc="{ row }">
              <el-input v-model="row.description" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #ownerId="{ row }">
              <el-select v-model="row.ownerId" placeholder="请选择" filterable clearable style="width: 100%" :disabled="isView">
                <el-option v-for="u in userOptions" :key="u.value" :label="u.label" :value="u.value" />
              </el-select>
            </template>
            <template #planDays="{ row }">
              <el-input-number v-model="row.planDays" :min="0" controls-position="right" style="width: 100%" :disabled="isView" />
            </template>
            <template #planStart="{ row }">
              <el-date-picker
                v-model="row.planStartDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #planEnd="{ row }">
              <el-date-picker
                v-model="row.planEndDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #progress="{ row }">
              <el-input-number v-model="row.progress" :min="0" :max="100" controls-position="right" style="width: 100%" :disabled="isView" />
            </template>
            <template #actualStart="{ row }">
              <el-date-picker
                v-model="row.actualStartDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #actualEnd="{ row }">
              <el-date-picker
                v-model="row.actualEndDate"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="请选择"
                style="width: 100%"
                clearable
                :disabled="isView"
              />
            </template>
            <template #overdueDays="{ row }">
              <el-input-number v-model="row.overdueDays" :min="0" controls-position="right" style="width: 100%" :disabled="isView" />
            </template>
            <template #completion="{ row }">
              <el-input v-model="row.completionRemark" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #phaseAction="{ row, index }">
              <el-button v-if="!isView" link type="danger" :icon="Delete" @click="removePhaseRow(index)">删除</el-button>
              <span v-else>—</span>
            </template>
          </PIMTable>
        </div>
      </div> -->
      <div class="section">
        <div class="section-header" @click="toggleSection('address')">
          <div class="section-title">
            <span class="section-bar" />
            <span>收货地址</span>
          </div>
          <div class="section-actions" @click.stop>
            <el-button v-if="!isView" type="primary" :icon="Plus" @click="addAddressRow">新增行</el-button>
            <el-icon class="toggle-icon" @click="toggleSection('address')">
              <ArrowDown v-if="sectionCollapsed.address" />
              <ArrowUp v-else />
            </el-icon>
          </div>
        </div>
        <div v-show="!sectionCollapsed.address" class="section-body">
          <PIMTable
            :column="addressColumns"
            :tableData="form.addressList"
            :tableLoading="false"
            :isSelection="false"
            :isShowPagination="false"
            height="200"
          >
            <template #receiver="{ row }">
              <el-input v-model="row.receiver" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #receiverPhone="{ row }">
              <el-input v-model="row.phone" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #receiverAddress="{ row }">
              <el-input v-model="row.address" placeholder="请输入" clearable :disabled="isView" />
            </template>
            <template #addressAction="{ row, index }">
              <el-button v-if="!isView" link type="danger" :icon="Delete" @click="removeAddressRow(index)">删除</el-button>
              <span v-else>—</span>
            </template>
          </PIMTable>
        </div>
      </div>
      <div class="section">
        <div class="section-header" @click="toggleSection('contact')">
          <div class="section-title">
            <span class="section-bar" />
            <span>联系信息</span>
          </div>
          <el-icon class="toggle-icon">
            <ArrowDown v-if="sectionCollapsed.contact" />
            <ArrowUp v-else />
          </el-icon>
        </div>
        <div v-show="!sectionCollapsed.contact" class="section-body">
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="联系人姓名" prop="contactName">
                <el-input v-model="form.contactName" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="性别" prop="contactGender">
                <el-select v-model="form.contactGender" placeholder="请选择" clearable style="width: 100%">
                  <el-option label="男" value="1" />
                  <el-option label="女" value="2" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="生日" prop="contactBirthday">
                <el-date-picker
                  v-model="form.contactBirthday"
                  type="date"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  placeholder="请选择"
                  style="width: 100%"
                  clearable
                />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="邮箱" prop="contactEmail">
                <el-input v-model="form.contactEmail" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="部门" prop="contactDept">
                <el-input v-model="form.contactDept" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="职务" prop="contactJob">
                <el-input v-model="form.contactJob" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="手机号码" prop="contactMobile">
                <el-input v-model="form.contactMobile" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="微信号码" prop="contactWechat">
                <el-input v-model="form.contactWechat" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="QQ" prop="contactQq">
                <el-input v-model="form.contactQq" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="企业微信" prop="contactWorkWechat">
                <el-input v-model="form.contactWorkWechat" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="地址" prop="contactAddress">
                <el-input v-model="form.contactAddress" placeholder="请输入" clearable />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="24">
              <el-form-item label="备注" prop="contactRemark">
                <el-input v-model="form.contactRemark" type="textarea" :rows="2" placeholder="请输入" maxlength="200" show-word-limit />
              </el-form-item>
            </el-col>
          </el-row>
        </div>
      </div>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button v-if="!isView" type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDialog">{{ isView ? '关闭' : '取消' }}</el-button>
      </div>
    </template>
  </el-dialog>
  <FormDialog
    v-model="productFormVisible"
    :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
    :width="'40%'"
    :operation-type="productOperationType"
    @close="closeProductDia"
    @confirm="submitProduct"
    @cancel="closeProductDia"
  >
    <el-form ref="productFormRef" :model="productForm" label-width="140px" label-position="top" :rules="productRules">
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="产品大类:" prop="productCategoryId">
            <el-tree-select
              v-model="productForm.productCategoryId"
              placeholder="请选择"
              clearable
              check-strictly
              :data="productCategoryOptions"
              :render-after-expand="false"
              style="width: 100%"
              @change="getModels"
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="24">
          <el-form-item label="规格型号:" prop="productModelId">
            <el-select v-model="productForm.productModelId" placeholder="请选择" clearable filterable @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="30">
        <el-col :span="12">
          <el-form-item label="单位:" prop="unit">
            <el-input v-model="productForm.unit" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="税率(%):" prop="taxRate">
            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
              <el-option label="1" value="1" />
              <el-option label="6" value="6" />
              <el-option label="13" value="13" />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
            <el-input-number
              v-model="productForm.taxInclusiveUnitPrice"
              :step="0.01"
              :min="0"
              :precision="2"
              style="width: 100%"
              placeholder="请输入"
              clearable
              @change="calculateFromUnitPrice"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="数量:" prop="quantity">
            <el-input-number
              v-model="productForm.quantity"
              :step="0.1"
              :min="0"
              :precision="2"
              style="width: 100%"
              placeholder="请输入"
              clearable
              @change="calculateFromQuantity"
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
            <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
            <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="发票类型:" prop="invoiceType">
            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
              <el-option label="增普票" value="增普票" />
              <el-option label="增专票" value="增专票" />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
<script setup name="ProjectManagementFormDia">
import { computed, getCurrentInstance, reactive, ref, toRefs } from 'vue'
import { ArrowDown, ArrowUp, Delete, Plus, Document } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { getToken } from '@/utils/auth'
import PIMTable from '@/components/PIMTable/PIMTable.vue'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import { listPlan } from '@/api/projectManagement/projectType'
import { findRoleListPage } from '@/api/projectManagement/role'
import { userListAll } from '@/api/publicApi'
import { addProject, getProject, updateProject } from '@/api/projectManagement/project'
import { modelList, productTreeList } from '@/api/basicData/product'
import { delProduct as delSalesProduct } from '@/api/salesManagement/salesLedger'
const emit = defineEmits(['completed'])
const { proxy } = getCurrentInstance()
const { bill_status, project_management, plan_status } = proxy.useDict('bill_status', 'project_management', 'plan_status')
const dialogVisible = ref(false)
const operationType = ref('add')
const formRef = ref()
const fileList = ref([])
const existingAttachments = ref([])
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload',
  headers: { Authorization: 'Bearer ' + getToken() }
})
const projectTypeOptions = ref([])
const roleOptions = ref([])
const userOptions = ref([])
const productData = ref([])
const productSelectedRows = ref([])
const productCategoryOptions = ref([])
const modelOptions = ref([])
const productFormVisible = ref(false)
const productOperationType = ref('add')
const productFormRef = ref()
const productIndex = ref(0)
const isCalculating = ref(false)
const productFormData = reactive({
  productForm: {
    productCategoryId: undefined,
    productCategory: '',
    productModelId: undefined,
    specificationModel: '',
    unit: '',
    quantity: '',
    taxInclusiveUnitPrice: '',
    taxRate: '',
    taxInclusiveTotalPrice: '',
    taxExclusiveTotalPrice: '',
    invoiceType: ''
  },
  productRules: {
    productCategoryId: [{ required: true, message: '请选择', trigger: 'change' }],
    productModelId: [{ required: true, message: '请选择', trigger: 'change' }],
    unit: [{ required: true, message: '请输入', trigger: 'blur' }],
    quantity: [{ required: true, message: '请输入', trigger: 'blur' }],
    taxInclusiveUnitPrice: [{ required: true, message: '请输入', trigger: 'blur' }],
    taxRate: [{ required: true, message: '请选择', trigger: 'change' }],
    taxInclusiveTotalPrice: [{ required: true, message: '请输入', trigger: 'blur' }],
    taxExclusiveTotalPrice: [{ required: true, message: '请输入', trigger: 'blur' }],
    invoiceType: [{ required: true, message: '请选择', trigger: 'change' }]
  }
})
const { productForm, productRules } = toRefs(productFormData)
const data = reactive({
  form: {
    id: undefined,
    clientId: undefined,
    parentProjectId: undefined,
    projectManagementPlanId: undefined,
    managerId: undefined,
    salesmanId: undefined,
    salesmanName: '',
    actualStartDate: '',
    actualEndDate: '',
    departmentId: undefined,
    departmentName: '',
    orderDate: '',
    billNo: '',
    projectName: '',
    customerName: '',
    parentProjectName: '',
    setupDate: '',
    projectSource: '',
    creatorName: '',
    billStatus: '',
    projectStage: '',
    estimatedDays: 0,
    planStartDate: '',
    planEndDate: '',
    projectManagementPlanId: undefined,
    projectAmount: 0,
    auditStatus: '',
    remark: '',
    attachmentIds: [],
    teamList: [],
    phaseList: [],
    addressList: [],
    contactName: '',
    contactGender: '',
    contactBirthday: '',
    contactEmail: '',
    contactDept: '',
    contactJob: '',
    contactMobile: '',
    contactWechat: '',
    contactQq: '',
    contactWorkWechat: '',
    contactAddress: '',
    contactRemark: ''
  },
  rules: {
    projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }]
  }
})
const { form, rules } = toRefs(data)
const sectionCollapsed = reactive({
  base: false,
  product: false,
  team: false,
  phase: false,
  address: false,
  contact: false
})
const isView = computed(() => operationType.value === 'view')
const dialogTitle = computed(() => {
  if (operationType.value === 'add') return '新增项目'
  if (operationType.value === 'edit') return '编辑项目'
  return '项目详情'
})
const teamColumns = [
  { label: '姓名', prop: 'memberId', align: 'center', width: 180, dataType: 'slot', slot: 'memberId' },
  { label: '项目组角色', prop: 'roleId', align: 'center', width: 160, dataType: 'slot', slot: 'roleId' },
  { label: '进入日期', prop: 'enterDate', align: 'center', width: 160, dataType: 'slot', slot: 'enterDate' },
  { label: '离开日期', prop: 'leaveDate', align: 'center', width: 160, dataType: 'slot', slot: 'leaveDate' },
  { label: '联系方式', prop: 'phone', align: 'center', width: 180, dataType: 'slot', slot: 'phone' },
  { label: '备注', prop: 'remark', align: 'center', dataType: 'slot', slot: 'teamRemark' },
  { label: '操作', prop: 'action', align: 'center', width: 100, dataType: 'slot', slot: 'teamAction', fixed: 'right' }
]
const phaseColumns = [
  { label: '阶段名称', prop: 'phaseName', align: 'center', width: 160, dataType: 'slot', slot: 'phaseName' },
  { label: '描述', prop: 'description', align: 'center', width: 200, dataType: 'slot', slot: 'phaseDesc' },
  { label: '负责人', prop: 'ownerId', align: 'center', width: 160, dataType: 'slot', slot: 'ownerId' },
  { label: '预计工期(天)', prop: 'planDays', align: 'center', width: 140, dataType: 'slot', slot: 'planDays' },
  { label: '计划开始日期', prop: 'planStartDate', align: 'center', width: 160, dataType: 'slot', slot: 'planStart' },
  { label: '计划结束日期', prop: 'planEndDate', align: 'center', width: 160, dataType: 'slot', slot: 'planEnd' },
  { label: '进度(%)', prop: 'progress', align: 'center', width: 120, dataType: 'slot', slot: 'progress' },
  { label: '实际开始日期', prop: 'actualStartDate', align: 'center', width: 160, dataType: 'slot', slot: 'actualStart' },
  { label: '实际结束日期', prop: 'actualEndDate', align: 'center', width: 160, dataType: 'slot', slot: 'actualEnd' },
  { label: '逾期天数', prop: 'overdueDays', align: 'center', width: 120, dataType: 'slot', slot: 'overdueDays' },
  { label: '完成情况', prop: 'completionRemark', align: 'center', width: 200, dataType: 'slot', slot: 'completion' },
  { label: '操作', prop: 'action', align: 'center', width: 100, dataType: 'slot', slot: 'phaseAction', fixed: 'right' }
]
const addressColumns = [
  { label: '收货人', prop: 'receiver', align: 'center', width: 180, dataType: 'slot', slot: 'receiver' },
  { label: '联系方式', prop: 'phone', align: 'center', width: 180, dataType: 'slot', slot: 'receiverPhone' },
  { label: '收货地址', prop: 'address', align: 'center', dataType: 'slot', slot: 'receiverAddress' },
  { label: '操作', prop: 'action', align: 'center', width: 100, dataType: 'slot', slot: 'addressAction', fixed: 'right' }
]
function toggleSection(key) {
  sectionCollapsed[key] = !sectionCollapsed[key]
}
function resetFormData() {
  Object.assign(form.value, {
    id: undefined,
    clientId: undefined,
    parentProjectId: undefined,
    projectManagementPlanId: undefined,
    managerId: undefined,
    salesmanId: undefined,
    salesmanName: '',
    actualStartDate: '',
    actualEndDate: '',
    departmentId: undefined,
    departmentName: '',
    orderDate: '',
    billNo: '',
    projectName: '',
    customerName: '',
    parentProjectName: '',
    setupDate: '',
    projectSource: '',
    creatorName: '',
    billStatus: '',
    projectStage: '',
    estimatedDays: 0,
    planStartDate: '',
    planEndDate: '',
    projectManagementPlanId: undefined,
    projectAmount: 0,
    auditStatus: '',
    remark: '',
    attachmentIds: [],
    teamList: [],
    phaseList: [],
    addressList: [],
    contactName: '',
    contactGender: '',
    contactBirthday: '',
    contactEmail: '',
    contactDept: '',
    contactJob: '',
    contactMobile: '',
    contactWechat: '',
    contactQq: '',
    contactWorkWechat: '',
    contactAddress: '',
    contactRemark: ''
  })
  fileList.value = []
  productData.value = []
}
function formattedNumber(row, column, cellValue) {
  const val = Number(cellValue ?? 0)
  return Number.isFinite(val) ? val.toFixed(2) : '0.00'
}
function summarizeProductTable(param) {
  return proxy.summarizeTable(param, ['taxInclusiveTotalPrice', 'taxExclusiveTotalPrice'])
}
function productSelected(selection) {
  productSelectedRows.value = selection
}
function convertIdToValue(data) {
  return (Array.isArray(data) ? data : []).map(item => {
    const { id, children, ...rest } = item
    const newItem = {
      ...rest,
      value: id
    }
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children)
    }
    return newItem
  })
}
function findNodeById(nodes, productId) {
  for (let i = 0; i < (nodes || []).length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId)
      if (foundNode) return foundNode
    }
  }
  return null
}
function findNodeIdByLabel(nodes, label) {
  if (!label) return null
  for (let i = 0; i < (nodes || []).length; i++) {
    const node = nodes[i]
    if (node.label === label) return node.value
    if (node.children && node.children.length > 0) {
      const found = findNodeIdByLabel(node.children, label)
      if (found !== null && found !== undefined) return found
    }
  }
  return null
}
function getProductOptions() {
  return productTreeList().then(res => {
    const list = res?.data || res
    productCategoryOptions.value = convertIdToValue(list)
    return productCategoryOptions.value
  })
}
function getModels(value) {
  const categoryLabel = findNodeById(productCategoryOptions.value, value)
  productForm.value.productCategory = categoryLabel || ''
  modelList({ id: value }).then(res => {
    modelOptions.value = res?.data || res || []
  })
}
function getProductModel(value) {
  const index = (modelOptions.value || []).findIndex(item => item.id === value)
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model
    productForm.value.unit = modelOptions.value[index].unit
  } else {
    productForm.value.specificationModel = ''
    productForm.value.unit = ''
  }
}
async function openProductForm(type, row, index) {
  productOperationType.value = type
  productIndex.value = index || 0
  productForm.value = {}
  proxy.resetForm('productFormRef')
  if (!productCategoryOptions.value || productCategoryOptions.value.length === 0) {
    await getProductOptions()
  }
  if (type === 'edit' && row) {
    productForm.value = { ...row }
    try {
      const categoryId = findNodeIdByLabel(productCategoryOptions.value, productForm.value.productCategory)
      if (categoryId) {
        productForm.value.productCategoryId = categoryId
        const models = await modelList({ id: categoryId })
        modelOptions.value = models?.data || models || []
        const currentModel = (modelOptions.value || []).find(m => m.model === productForm.value.specificationModel)
        if (currentModel) {
          productForm.value.productModelId = currentModel.id
        }
      }
    } catch {}
  } else {
    productForm.value = {
      productCategoryId: undefined,
      productCategory: '',
      productModelId: undefined,
      specificationModel: '',
      unit: '',
      quantity: '',
      taxInclusiveUnitPrice: '',
      taxRate: '',
      taxInclusiveTotalPrice: '',
      taxExclusiveTotalPrice: '',
      invoiceType: ''
    }
  }
  productFormVisible.value = true
}
function closeProductDia() {
  proxy.resetForm('productFormRef')
  productFormVisible.value = false
}
function submitProduct() {
  productFormRef.value?.validate?.(valid => {
    if (!valid) return
    const payload = { ...productForm.value }
    if (productOperationType.value === 'add') {
      productData.value.push(payload)
    } else {
      productData.value[productIndex.value] = payload
    }
    closeProductDia()
  })
}
function deleteProduct() {
  if (!productSelectedRows.value || productSelectedRows.value.length === 0) {
    proxy.$modal?.msgWarning?.('请选择数据')
    return
  }
  const selectedIds = productSelectedRows.value.map(r => r?.id).filter(Boolean)
  if (operationType.value !== 'add' && selectedIds.length > 0) {
    delSalesProduct(selectedIds)
      .then(() => {
        proxy.$modal?.msgSuccess?.('删除成功')
        productData.value = productData.value.filter(row => !selectedIds.includes(row?.id))
        productSelectedRows.value = []
      })
      .catch(() => {})
    return
  }
  productData.value = productData.value.filter(row => !productSelectedRows.value.includes(row))
  productSelectedRows.value = []
}
function calculateFromTotalPrice() {
  if (isCalculating.value) return
  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice)
  const quantity = parseFloat(productForm.value.quantity)
  if (!totalPrice || !quantity || quantity <= 0) return
  isCalculating.value = true
  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2)
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(totalPrice, productForm.value.taxRate)
  }
  isCalculating.value = false
}
function calculateFromExclusiveTotalPrice() {
  if (!productForm.value.taxRate) {
    proxy.$modal?.msgWarning?.('请先选择税率')
    return
  }
  if (isCalculating.value) return
  const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice)
  const quantity = parseFloat(productForm.value.quantity)
  const taxRate = parseFloat(productForm.value.taxRate)
  if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) return
  isCalculating.value = true
  const taxRateDecimal = taxRate / 100
  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal)
  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2)
  productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2)
  isCalculating.value = false
}
function calculateFromQuantity() {
  if (!productForm.value.taxRate) {
    proxy.$modal?.msgWarning?.('请先选择税率')
    return
  }
  if (isCalculating.value) return
  const quantity = parseFloat(productForm.value.quantity)
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice)
  if (!quantity || quantity <= 0 || !unitPrice) return
  isCalculating.value = true
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2)
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
      productForm.value.taxInclusiveTotalPrice,
      productForm.value.taxRate
    )
  }
  isCalculating.value = false
}
function calculateFromUnitPrice() {
  if (!productForm.value.taxRate) {
    proxy.$modal?.msgWarning?.('请先选择税率')
    return
  }
  if (isCalculating.value) return
  const quantity = parseFloat(productForm.value.quantity)
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice)
  if (!quantity || quantity <= 0 || !unitPrice) return
  isCalculating.value = true
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2)
  if (productForm.value.taxRate) {
    productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
      productForm.value.taxInclusiveTotalPrice,
      productForm.value.taxRate
    )
  }
  isCalculating.value = false
}
function calculateFromTaxRate() {
  if (!productForm.value.taxRate) {
    proxy.$modal?.msgWarning?.('请先选择税率')
    return
  }
  if (isCalculating.value) return
  const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice)
  const taxRate = parseFloat(productForm.value.taxRate)
  if (!inclusiveTotalPrice || !taxRate) return
  isCalculating.value = true
  productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate)
  isCalculating.value = false
}
async function loadProjectTypeOptions() {
  try {
    const res = await listPlan({ current: 1, size: 999 })
    const records = res?.data?.records || res?.records || res?.rows || []
    projectTypeOptions.value = records.map(item => ({ label: item.name, value: item.id }))
  } catch {
    projectTypeOptions.value = []
  }
}
async function loadRoleOptions() {
  try {
    const res = await findRoleListPage({ pageNum: 1, pageSize: 999 })
    const records = res?.data?.records || res?.rows || res?.records || []
    roleOptions.value = records.map(item => ({ label: item.roleName || item.name, value: item.id }))
  } catch {
    roleOptions.value = []
  }
}
async function loadUserOptions() {
  try {
    const res = await userListAll()
    const list = res?.data || res?.rows || res || []
    userOptions.value = (Array.isArray(list) ? list : []).map(u => ({
      label: u.nickName || u.userName || u.username || u.name,
      value: u.userId || u.id
    }))
  } catch {
    userOptions.value = []
  }
}
function addTeamRow() {
  form.value.teamList.push({
    memberId: undefined,
    roleId: undefined,
    enterDate: '',
    leaveDate: '',
    phone: '',
    remark: ''
  })
}
function removeTeamRow(index) {
  if (index > -1) form.value.teamList.splice(index, 1)
}
function addPhaseRow() {
  form.value.phaseList.push({
    phaseName: '',
    description: '',
    ownerId: undefined,
    planDays: 0,
    planStartDate: '',
    planEndDate: '',
    progress: 0,
    actualStartDate: '',
    actualEndDate: '',
    overdueDays: 0,
    completionRemark: ''
  })
}
function removePhaseRow(index) {
  if (index > -1) form.value.phaseList.splice(index, 1)
}
function addAddressRow() {
  form.value.addressList.push({
    receiver: '',
    phone: '',
    address: ''
  })
}
function removeAddressRow(index) {
  if (index > -1) form.value.addressList.splice(index, 1)
}
function beforeUpload() {
  if (isView.value) return false
  proxy.$modal?.loading?.('正在上传文件,请稍候...')
  return true
}
function handleUploadError() {
  proxy.$modal?.closeLoading?.()
  ElMessage.error('上传文件失败')
}
function handleUploadSuccess(res, file) {
  console.log(res, file)
  proxy.$modal?.closeLoading?.()
  if (res?.code !== 200) {
    ElMessage.error(res?.msg || '上传失败')
    return
  }
  const attachmentId = res?.data?.[0]?.id ?? ""
  if (!attachmentId) return
  form.value.attachmentIds.push(attachmentId)
  console.log(form.value.attachmentIds)
  ElMessage.success('上传成功')
}
function handleRemove(file) {
  const attachmentId = file?.attachmentId
  if (!attachmentId) return
  form.value.attachmentIds = (form.value.attachmentIds || []).filter(id => id !== attachmentId)
}
async function openDialog(payload = {}) {
  operationType.value = payload.operationType || 'add'
  resetFormData()
  await Promise.all([loadProjectTypeOptions(), loadRoleOptions(), loadUserOptions(), getProductOptions()])
  if (payload.row?.id) {
    try {
      const res = await getProject(payload.row.id)
      const detail = res?.data?.data ?? res?.data ?? res
      const info = detail?.info || {}
      const shippingAddress = detail?.shippingAddress || {}
      const contractInfo = detail?.contractInfo || {}
      const normalizeId = v => {
        if (v === undefined || v === null || v === '') return undefined
        const n = Number(v)
        return Number.isNaN(n) ? v : n
      }
      const normalizeDictValue = v => {
        if (v === undefined || v === null || v === '') return ''
        return String(v)
      }
      const computeEstimatedDays = (start, end) => {
        if (!start || !end) return 0
        const startTime = new Date(`${start}T00:00:00`).getTime()
        const endTime = new Date(`${end}T00:00:00`).getTime()
        if (!Number.isFinite(startTime) || !Number.isFinite(endTime)) return 0
        if (endTime < startTime) return 0
        return Math.floor((endTime - startTime) / (24 * 60 * 60 * 1000)) + 1
      }
      Object.assign(form.value, {
        id: info.id,
        billNo: info.no ?? '',
        projectManagementPlanId: info.projectManagementPlanId ?? '',
        estimatedDays: Number(info.estimatedDays) || computeEstimatedDays(info.planStartTime, info.planEndTime) || 0,
        projectName: info.title ?? '',
        customerName: info.clientName ?? '',
        parentProjectName: info.projectManagementInfoParentName ?? '',
        setupDate: info.establishTime ?? '',
        projectSource: info.source ?? '',
        creatorName: info.managerName ?? '',
        billStatus: normalizeDictValue(info.status),
        projectStage: normalizeDictValue(info.stage ?? info.projectStage),
        planStartDate: info.planStartTime ?? '',
        planEndDate: info.planEndTime ?? '',
        projectAmount: info.orderAmount ?? 0,
        auditStatus: normalizeDictValue(info.reviewStatus),
        remark: info.remark ?? '',
        attachmentIds: Array.isArray(info.attachmentIds) ? info.attachmentIds : [],
        teamList: Array.isArray(info.teamList) ? info.teamList.map(t => ({
          memberId: normalizeId(t.userId),
          roleId: normalizeId(t.userRoleId),
          enterDate: t.joinTime,
          leaveDate: t.departTime,
          phone: t.contact,
          remark: t.remark
        })) : [],
        addressList: shippingAddress?.address
          ? [{
              receiver: shippingAddress.consignee,
              phone: shippingAddress.contract,
              address: shippingAddress.address
            }]
          : [],
        contactName: contractInfo.name ?? '',
        contactGender: contractInfo.sex === '男' ? '1' : contractInfo.sex === '女' ? '2' : '',
        contactBirthday: contractInfo.birthday ?? '',
        contactDept: contractInfo.department ?? '',
        contactJob: contractInfo.job ?? '',
        contactMobile: contractInfo.phoneNumber ?? '',
        contactEmail: contractInfo.email ?? '',
        contactQq: contractInfo.qq ?? '',
        contactWechat: contractInfo.wx ?? '',
        contactWorkWechat: contractInfo.lineaFissa ?? '',
        contactAddress: contractInfo.origineEtnica ?? '',
        contactRemark: contractInfo.rappresentanteLegale ?? ''
      })
      existingAttachments.value = Array.isArray(info.attachmentList)
        ? info.attachmentList.map(a => ({
            id: a.id ?? a.fileId,
            name: a.fileName ?? a.name,
            url: a.url ?? a.fileUrl ?? a.path
          }))
        : []
      const rawPhaseList =
        detail?.phaseList ||
        detail?.projectPhaseList ||
        detail?.projectStageList ||
        info?.phaseList ||
        info?.projectPhaseList ||
        []
      form.value.phaseList = Array.isArray(rawPhaseList)
        ? rawPhaseList.map(p => ({
            phaseName: p.phaseName ?? p.name ?? p.title ?? '',
            description: p.description ?? p.workContent ?? p.desc ?? '',
            ownerId: normalizeId(p.ownerId ?? p.leaderId ?? p.userId),
            planDays: Number(p.planDays ?? p.estimatedDuration ?? p.estimatedDays) || 0,
            planStartDate: p.planStartDate ?? p.planStartTime ?? p.startDate ?? '',
            planEndDate: p.planEndDate ?? p.planEndTime ?? p.endDate ?? '',
            progress: Number(p.progress ?? p.schedule) || 0,
            actualStartDate: p.actualStartDate ?? p.actualStartTime ?? '',
            actualEndDate: p.actualEndDate ?? p.actualEndTime ?? '',
            overdueDays: Number(p.overdueDays ?? p.overDays) || 0,
            completionRemark: p.completionRemark ?? p.remark ?? ''
          }))
        : []
      productData.value = detail?.salesLedgerProductList || detail?.productData || []
    } catch {}
  }
  if (form.value.teamList.length === 0 && !isView.value) addTeamRow()
  if (form.value.phaseList.length === 0 && !isView.value) addPhaseRow()
  dialogVisible.value = true
}
function downloadAttachment(att) {
  if (att?.name) {
    try {
      proxy.$download.name(att.url);
      return
    } catch (e) {}
  }
  ElMessage.warning('附件暂无下载地址')
}
function closeDialog() {
  dialogVisible.value = false
}
async function submitForm() {
  if (isView.value) {
    closeDialog()
    return
  }
  await formRef.value?.validate?.()
  if (!productData.value || productData.value.length === 0) {
    proxy.$modal?.msgWarning?.('请添加产品信息')
    return
  }
  const findLabel = (list, value) => (list || []).find(i => String(i.value) === String(value))?.label
  const teamList = (form.value.teamList || []).map(t => ({
    userId: t.memberId,
    userName: findLabel(userOptions.value, t.memberId),
    userRoleId: t.roleId,
    userRoleName: findLabel(roleOptions.value, t.roleId),
    joinTime: t.enterDate,
    departTime: t.leaveDate,
    contact: t.phone,
    remark: t.remark
  }))
  const shippingRow = (form.value.addressList || [])[0] || {}
  const shippingAddress = {
    id: undefined,
    consignee: shippingRow.receiver,
    contract: shippingRow.phone,
    address: shippingRow.address
  }
  const contractInfo = {
    id: undefined,
    name: form.value.contactName,
    sex: form.value.contactGender === '1' ? '男' : form.value.contactGender === '2' ? '女' : '',
    birthday: form.value.contactBirthday,
    department: form.value.contactDept,
    job: form.value.contactJob,
    phoneNumber: form.value.contactMobile,
    email: form.value.contactEmail,
    qq: form.value.contactQq,
    lineaFissa: form.value.contactWorkWechat,
    wx: form.value.contactWechat,
    origineEtnica: form.value.contactAddress,
    rappresentanteLegale: form.value.contactRemark
  }
  const info = {
    id: form.value.id ?? null,
    no: form.value.billNo,
    title: form.value.projectName,
    clientId: form.value.clientId ?? null,
    clientName: form.value.customerName,
    projectManagementInfoParentId: form.value.parentProjectId ?? null,
    projectManagementPlanId: form.value.projectManagementPlanId ?? null,
    establishTime: form.value.setupDate,
    source: form.value.projectSource,
    managerId: form.value.managerId ?? null,
    managerName: form.value.creatorName,
    salesmanId: form.value.salesmanId ?? null,
    salesmanName: form.value.salesmanName ?? '',
    planStartTime: form.value.planStartDate,
    planEndTime: form.value.planEndDate,
    actualStartTime: form.value.actualStartDate,
    actualEndTime: form.value.actualEndDate,
    status: form.value.billStatus === '' || form.value.billStatus === undefined || form.value.billStatus === null ? null : Number(form.value.billStatus),
    departmentId: form.value.departmentId ?? null,
    departmentName: form.value.departmentName ?? '',
    orderDate: form.value.orderDate,
    orderAmount: form.value.projectAmount,
    reviewStatus: form.value.auditStatus === '' || form.value.auditStatus === undefined || form.value.auditStatus === null ? null : Number(form.value.auditStatus),
    stage: form.value.projectStage === '' || form.value.projectStage === undefined || form.value.projectStage === null ? null : Number(form.value.projectStage),
    remark: form.value.remark,
    attachmentIds: Array.isArray(form.value.attachmentIds) ? form.value.attachmentIds : [],
    teamList
  }
  const payload = {
    info,
    shippingAddress,
    contractInfo,
    salesLedgerProductList: productData.value
  }
  const req = operationType.value === 'edit' ? updateProject : addProject
  const res = await req(payload)
  if (res?.code === 200) {
    ElMessage.success('保存成功')
    closeDialog()
    emit('completed')
    return
  }
  ElMessage.error(res?.msg || '保存失败')
}
defineExpose({ openDialog })
</script>
<style scoped lang="scss">
.section {
  border: 1px solid #ebeef5;
  border-radius: 8px;
  margin-bottom: 14px;
  background: #fff;
}
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  cursor: pointer;
}
.section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: #303133;
}
.section-bar {
  width: 3px;
  height: 14px;
  background: #002FA7;
  border-radius: 2px;
}
.section-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.toggle-icon {
  color: #909399;
}
.section-body {
  padding: 0 14px 14px;
}
.dialog-footer {
  display: flex;
  justify-content: center;
  gap: 12px;
}
.attachment-upload{
}
</style>