gaoluyang
4 天以前 84d7be872e96547becfbd1647ec77ed56f9f2dde
src/views/basicData/customerFile/index.vue
@@ -9,17 +9,6 @@
                  @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>
@@ -27,6 +16,9 @@
      <div>
        <el-button type="primary"
                   @click="openForm('add')">新增客户</el-button>
        <el-button type="primary"
                   plain
                   @click="back">流入公海</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="info"
                   plain
@@ -37,6 +29,17 @@
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <!-- 按「客户分类」拆页签;「全部」不带分类查询,保证还没补分类的老数据也能看到 -->
    <el-tabs v-model="activeTab"
             @tab-change="handleTabChange"
             class="customer-tabs">
      <el-tab-pane label="全部"
                   name="全部" />
      <el-tab-pane label="销售客户"
                   name="销售客户" />
      <el-tab-pane label="代储客户"
                   name="代储客户" />
    </el-tabs>
    <div class="table_list">
      <PIMTable rowKey="id"
                :column="tableColumn"
@@ -45,7 +48,16 @@
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"></PIMTable>
                @pagination="pagination">
        <template #depositAmountSlot="{ row }">
          <el-tooltip v-if="row.exceedsTrustFund"
                      :content="`代储金额合计已超出该客户信托基金(信托基金:${row.trustFund})`"
                      placement="top">
            <el-text type="danger">{{ formatAmount(row.depositTotalAmount) }}</el-text>
          </el-tooltip>
          <span v-else>{{ formatAmount(row.depositTotalAmount) }}</span>
        </template>
      </PIMTable>
    </div>
    <el-dialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增客户信息' : '编辑客户信息'"
@@ -125,11 +137,24 @@
              <el-select v-model="form.customerType"
                         placeholder="请选择"
                         clearable>
                <el-option label="零售客户"
                           value="零售客户" />
                <el-option label="进销商客户"
                           value="进销商客户" />
                <el-option v-for="dict in customer_type"
                           :key="dict.value"
                           :label="dict.label"
                           :value="dict.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="信托基金(元):"
                          prop="trustFund">
              <el-input-number v-model="form.trustFund"
                               :min="0"
                               :precision="2"
                               :controls="false"
                               placeholder="请输入,可为空"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -211,7 +236,8 @@
                 :limit="1"
                 accept=".xlsx, .xls"
                 :headers="upload.headers"
                 :action="upload.url + '?updateSupport=' + upload.updateSupport"
                 :action="upload.url"
                 :data="upload.data"
                 :disabled="upload.isUploading"
                 :before-upload="upload.beforeUpload"
                 :on-progress="upload.onProgress"
@@ -457,6 +483,32 @@
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">信托基金(元):</span>
                <span class="info-value">{{ formatAmount(detailForm.trustFund) }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">代储金额(元):</span>
                <span class="info-value"
                      :class="{ 'text-danger': detailForm.exceedsTrustFund }">
                  {{ formatAmount(detailForm.depositTotalAmount) }}
                </span>
              </div>
            </el-col>
          </el-row>
          <el-row v-if="detailForm.exceedsTrustFund"
                  :gutter="20">
            <el-col :span="24">
              <el-alert type="warning"
                        show-icon
                        :closable="false"
                        :title="`该客户代储金额合计 ${formatAmount(detailForm.depositTotalAmount)} 元,已超出信托基金 ${formatAmount(detailForm.trustFund)} 元`" />
            </el-col>
          </el-row>
        </div>
      </div>
      <!-- 洽谈进度记录 -->
@@ -609,28 +661,30 @@
</template>
<script setup>
  import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
  import {
    onMounted,
    ref,
    reactive,
    getCurrentInstance,
    toRefs,
    watch,
  } from "vue";
  import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
  import {
    addCustomerPrivate,
    delCustomerPrivate,
    getCustomer,
    getCustomerPrivatePoolById,
    getCustomerPrivatePoolInfo,
    listCustomerPrivatePool,
    updateCustomerPrivatePool,
    addCustomerFollow,
    updateCustomerFollow,
    delCustomerFollow,
    addReturnVisit,
    getReturnVisit,
  } from "@/api/basicData/customerFile.js";
  import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer, backCustomer} from "@/api/basicData/customer.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 { customer_type } = proxy.useDict("customer_type");
  // 回访提醒相关
  const reminderDialogVisible = ref(false);
@@ -656,7 +710,7 @@
  const negotiationFormRef = ref();
  const negotiationForm = reactive({
    customerName: "",
      customerPrivatePoolId: "",
      customerId: "",
    followUpMethod: "",
    followUpLevel: "",
    followUpTime: "",
@@ -676,6 +730,11 @@
    content: [{ required: true, message: "请输入内容", trigger: "blur" }],
  };
  const formatAmount = value => {
    return value === null || value === undefined || value === ""
      ? "-"
      : Number(value).toFixed(2);
  };
  // 详情相关
  const detailDialogVisible = ref(false);
  const detailForm = reactive({
@@ -691,6 +750,9 @@
    contactPhone: "",
    maintainer: "",
    maintenanceTime: "",
    trustFund: "",
    depositTotalAmount: "",
    exceedsTrustFund: false,
  });
  const negotiationRecords = ref([]);
@@ -715,6 +777,7 @@
      label: "客户分类",
      prop: "customerType",
      width: 120,
      formatData: value => proxy.selectDictLabel(customer_type.value, value),
    },
    {
      label: "客户名称",
@@ -761,6 +824,22 @@
      width: 220,
    },
    {
      label: "信托基金(元)",
      prop: "trustFund",
      width: 140,
      formatData: value =>
        value === null || value === undefined || value === ""
          ? "-"
          : Number(value).toFixed(2),
    },
    {
      label: "代储金额(元)",
      prop: "depositTotalAmount",
      dataType: "slot",
      slot: "depositAmountSlot",
      width: 150,
    },
    {
      label: "开户行号",
      prop: "bankCode",
      width: 220,
@@ -802,7 +881,12 @@
        {
          name: "编辑",
          type: "text",
          showHide: row => !isCustomerEditLocked(row),
          clickFun: row => {
            if (isCustomerEditLocked(row)) {
              proxy.$modal.msgWarning("已被销售模块引用的私海客户不能编辑");
              return;
            }
            openForm("edit", row);
          },
        },
@@ -856,7 +940,7 @@
  const data = reactive({
    searchForm: {
      customerName: "",
      customerType: "",
      type: 0
    },
    form: {
      customerName: "",
@@ -871,6 +955,9 @@
      bankAccount: "",
      bankCode: "",
      customerType: "",
      // 金额字段给 null 而非 "",避免 el-input-number 把空串折算成 0
      trustFund: null,
      type: 0
    },
    rules: {
      customerName: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -901,7 +988,10 @@
    // 设置上传的请求头部
    headers: { Authorization: "Bearer " + getToken() },
    // 上传的地址
    url: import.meta.env.VITE_APP_BASE_API + "/customerPrivate/importData",
    url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
    data: {
      type: 0
    },
    // 文件上传前的回调
    beforeUpload: file => {
      console.log("文件即将上传", file);
@@ -970,14 +1060,37 @@
    page.size = obj.limit;
    getList();
  };
  // 全部 / 销售客户 / 代储客户三个页签,各自带客户分类查同一个列表接口
  const activeTab = ref("全部");
  // 「全部」不传分类(undefined 会被 axios 丢掉),销售/代储按字典值筛;
  // 字典的 value 未必就是中文标签,按 label 反查真实取值再传接口
  const activeCustomerType = () => {
    if (activeTab.value === "全部") {
      return undefined;
    }
    const list = customer_type.value || [];
    const hit = list.find(item => item.label === activeTab.value);
    return hit ? hit.value : activeTab.value;
  };
  // 页签切换:换客户分类重新查列表
  const handleTabChange = () => {
    page.current = 1;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listCustomerPrivatePool({ ...searchForm.value, ...page }).then(res => {
    listCustomer({
      ...searchForm.value,
      ...page,
      customerType: activeCustomerType(),
    }).then(res => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      page.total = res.data.total;
    });
  };
  // 字典是异步拉的,到达后按真实字典值重查一次,避免首屏用中文标签筛不到数据
  watch(customer_type, () => getList());
  // 表格选择数据
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
@@ -994,10 +1107,14 @@
  }
  /** 下载模板 */
  function importTemplate() {
    proxy.download("/customerPrivate/downloadTemplate", {}, "客户导入模板.xlsx");
    proxy.download("/basic/customer/downloadTemplate", {}, "客户导入模板.xlsx");
  }
  // 打开弹框
  const openForm = (type, row) => {
    if (type === "edit" && isCustomerEditLocked(row)) {
      proxy.$modal.msgWarning("已被销售模块引用的私海客户不能编辑");
      return;
    }
    operationType.value = type;
    form.value = {};
    form.value.maintainer = userStore.nickName;
@@ -1007,12 +1124,15 @@
        contactPhone: "",
      },
    ];
    form.value.type = 0;
    // 客户分类默认跟随当前页签
    form.value.customerType = activeCustomerType();
    form.value.maintenanceTime = getCurrentDate();
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    if (type === "edit") {
      getCustomerPrivatePoolById(row.id).then(res => {
      getCustomer(row.id).then(res => {
        form.value = { ...res.data };
        formYYs.value.contactList = res.data.contactPerson
          .split(",")
@@ -1025,6 +1145,12 @@
      });
    }
    dialogFormVisible.value = true;
  };
  const isCustomerEditLocked = row => {
    const salesReferenced = Number(row?.salesReferenceFlag || 0) === 1;
    const customerType = Number(row?.type);
    const usageStatus = Number(row?.usageStatus || 0);
    return salesReferenced && (customerType === 0 || (customerType === 1 && usageStatus === 1));
  };
  // 提交表单
  const submitForm = () => {
@@ -1049,7 +1175,7 @@
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    addCustomerPrivate(form.value).then(res => {
    addCustomer(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
@@ -1063,7 +1189,7 @@
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    updateCustomerPrivatePool(form.value).then(res => {
    updateCustomer(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
@@ -1082,7 +1208,12 @@
      type: "warning",
    })
      .then(() => {
        proxy.download("/customerPrivate/export", {}, "客户档案.xlsx");
        // 导出跟随当前页签的客户分类
        proxy.download(
          "/basic/customer/export",
          { type: 0, customerType: activeCustomerType() },
          "客户档案.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -1111,7 +1242,7 @@
    })
      .then(() => {
        tableLoading.value = true;
        delCustomerPrivate(ids)
        delCustomer(ids)
          .then(() => {
            proxy.$modal.msgSuccess("删除成功");
            getList();
@@ -1122,6 +1253,36 @@
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const back = () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    const ids = selectedRows.value.map(item => item.id);
    ElMessageBox.confirm("选中的客户将流入公海,是否确认?", "流入公海提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        tableLoading.value = true;
        return Promise.all(ids.map(id => backCustomer(id)))
          .then(() => {
            proxy.$modal.msgSuccess("流入公海成功");
            selectedRows.value = [];
            getList();
          })
          .finally(() => {
            tableLoading.value = false;
          });
      })
      .catch(error => {
        if (error === "cancel" || error === "close") {
          proxy.$modal.msg("已取消");
        }
      });
  };
@@ -1165,8 +1326,7 @@
        if (reminderForm.id) {
          submitvalue.value = {
            id: reminderForm.id,
                  customerPrivatePoolId: reminderForm.id,
                  customerPrivatePoolId: currentCustomerId.value,
                  customerId: currentCustomerId.value,
            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
            content: reminderForm.reminderContent,
            reminderTime: reminderForm.reminderTime,
@@ -1174,7 +1334,7 @@
          };
        } else {
          submitvalue.value = {
                  customerPrivatePoolId: currentCustomerId.value,
            customerId: currentCustomerId.value,
            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
            content: reminderForm.reminderContent,
            reminderTime: reminderForm.reminderTime,
@@ -1203,7 +1363,7 @@
  // 打开洽谈进度弹窗
  const openNegotiationDialog = row => {
    negotiationForm.customerName = row.customerName;
    negotiationForm.customerPrivatePoolId = row.id;
    negotiationForm.customerId = row.id;
    negotiationForm.followUpMethod = "";
    negotiationForm.followUpLevel = "";
    negotiationForm.followUpTime = "";
@@ -1232,7 +1392,7 @@
          // 修改操作
          updateCustomerFollow(negotiationForm).then(res => {
            // 更新本地数据
            getCustomer(negotiationForm.customerPrivatePoolId).then(res => {
            getCustomer(negotiationForm.customerId).then(res => {
              // 更新本地数据
              negotiationRecords.value = res.data.followUpList || [];
            });
@@ -1264,7 +1424,7 @@
  // 打开详情弹窗
  const openDetailDialog = row => {
    getCustomerPrivatePoolInfo(row.id).then(res => {
    getCustomer(row.id).then(res => {
      // 填充客户基本信息
      Object.assign(detailForm, res.data);
@@ -1285,7 +1445,7 @@
    // 将当前记录数据填充到表单
    Object.assign(negotiationForm, {
      customerName: row.customerName,
         customerPrivatePoolId: row.customerPrivatePoolId,
         customerId: row.customerId,
      followUpMethod: row.followUpMethod,
      followUpLevel: row.followUpLevel,
      followUpTime: row.followUpTime,
@@ -1313,7 +1473,7 @@
        // });
        delCustomerFollow(row.id).then(() => {
          // 删除成功后更新本地数据
          getCustomer(row.customerPrivatePoolId).then(res => {
          getCustomer(row.customerId).then(res => {
            // 更新本地数据
            negotiationRecords.value = res.data.followUpList || [];
          });
@@ -1413,7 +1573,7 @@
  const downloadAttachment = row => {
    if (row.url) {
      // proxy.download(row.url, {}, row.name);
      proxy.$download.name(row.url);
         proxy.$download.byUrl(row.url, row.originalFilename);
    } else {
      proxy.$modal.msgError("下载链接不存在");
    }
@@ -1480,6 +1640,19 @@
</script>
<style scoped lang="scss">
  /* 销售客户 / 代储客户页签:只要标签栏,内容由下方列表展示 */
  .customer-tabs {
    margin-bottom: 8px;
    :deep(.el-tabs__header) {
      margin-bottom: 0;
    }
    :deep(.el-tabs__content) {
      display: none;
    }
  }
  .detail-section {
    margin-bottom: 20px;
    padding: 15px;
@@ -1526,6 +1699,11 @@
    word-break: break-word;
  }
  .text-danger {
    color: var(--el-color-danger);
    font-weight: 600;
  }
  .no-records {
    text-align: center;
    padding: 30px;