Quellcode durchsuchen

fix: 新增门户客户管理、项目管理

liqh vor 5 Monaten
Ursprung
Commit
c2f0e7960e
27 geänderte Dateien mit 3388 neuen und 392 gelöschten Zeilen
  1. 1 1
      src/api/business-rules/rules-api.js
  2. 21 50
      src/api/market-manage/index.js
  3. 56 0
      src/components/BsUi/TableInfoSelect/TableInfoSelect.vue
  4. 3 3
      src/views/business-rules/risk-rules/components/rules-list/index.vue
  5. 3 4
      src/views/business-rules/risk-rules/modules/create-rules.vue
  6. 155 0
      src/views/market-manage/external-manage/account-manage/index.vue
  7. 148 0
      src/views/market-manage/external-manage/account-manage/modules/add-card.vue
  8. 274 0
      src/views/market-manage/external-manage/basic-info/index.vue
  9. 23 11
      src/views/market-manage/external-manage/customer-manage/customer-create/index.vue
  10. 60 103
      src/views/market-manage/external-manage/customer-manage/customer-list/index.vue
  11. 31 82
      src/views/market-manage/external-manage/customer-manage/single-customer/components/file-info.vue
  12. 67 116
      src/views/market-manage/external-manage/customer-manage/single-customer/components/project-list.vue
  13. 21 15
      src/views/market-manage/external-manage/customer-manage/single-customer/index.vue
  14. 8 7
      src/views/market-manage/external-manage/customer-manage/single-customer/modules/fileDetail.vue
  15. 430 0
      src/views/market-manage/external-manage/project-manage/project-create/index.vue
  16. 226 0
      src/views/market-manage/external-manage/project-manage/project-list/index.vue
  17. 58 0
      src/views/market-manage/external-manage/project-manage/single-project/components/customer-info.vue
  18. 210 0
      src/views/market-manage/external-manage/project-manage/single-project/components/file-info.vue
  19. 105 0
      src/views/market-manage/external-manage/project-manage/single-project/components/follow-up.vue
  20. 259 0
      src/views/market-manage/external-manage/project-manage/single-project/components/invoice-list.vue
  21. 262 0
      src/views/market-manage/external-manage/project-manage/single-project/components/project-list.vue
  22. 259 0
      src/views/market-manage/external-manage/project-manage/single-project/components/settlement-list.vue
  23. 170 0
      src/views/market-manage/external-manage/project-manage/single-project/index.vue
  24. 140 0
      src/views/market-manage/external-manage/project-manage/single-project/modules/add-decision.vue
  25. 100 0
      src/views/market-manage/external-manage/project-manage/single-project/modules/add-followup.vue
  26. 145 0
      src/views/market-manage/external-manage/project-manage/single-project/modules/fileDetail.vue
  27. 153 0
      src/views/market-manage/external-manage/project-manage/single-project/modules/get-projectnum.vue

+ 1 - 1
src/api/business-rules/rules-api.js

@@ -43,6 +43,6 @@ export const rulesApi = {
    * 查询单条规则详情
    */
   getRulesInfoList: (params) => {
-    return getRequest(`/supports/risk/selectRiskOne/${params}`);
+    return getRequest(`/supports/risk/rule/${params}`);
   },
 };

+ 21 - 50
src/api/market-manage/index.js

@@ -1,66 +1,37 @@
-import { get } from 'store';
 import { getRequest, postRequest } from '/src/lib/axios';
 
 export const clientManageApi = {
-  /**
-   * 查询委托方表头数据 @author wzs
-   */
-  getWTHeaderList: (customerId) => {
-    return getRequest(`/delegent/customer/header?customerId=${customerId}`);
-  },
-  /**
-   * 查询项目信息管理表头数据 @author wzs
-   */
-  getXMHeaderList: (projectId) => {
-    return getRequest(`/project/detail/show?projectId=${projectId}`);
-  },
-
-  /**
-   * 查询客户信息 @author wzs
-   * @param {string} customerId 客户id
-   * @returns {Promise} 客户信息
-   */
-  getCustomerInfo: (customerId) => {
-    return getRequest(`/delegent/customer/main?customerId=${customerId}`);
-  },
+  //---------------------客户-----------------------
 
-  // 上传附件
-  uploadDocument: (params) => {
-    return postRequest(`social/upload`, params);
-  },
-
-  // 释放公海
-  releaseSeas: (projectId) => {
-    return getRequest(`/social/release?projectId=${projectId}`);
+  //新增服务商信息
+  createServiceInfo: (params) => {
+    return postRequest(`/project/list`, params);
   },
 
-  // 开标结果登记
-  openBid: (params) => {
-    return postRequest(`/social/project/biding/result`, params);
+  //客户新增表单提交
+  CustomerAddition: (params) => {
+    return postRequest('/supports/customer/create', params);
   },
-
-  // 委托方管理列表头部卡片
-  customerCardInfo: () => {
-    return getRequest(`/delegent/customer/list`);
+  //客户新增表单提交
+  customerDetails: (params) => {
+    return getRequest(`/supports/customer/${params}`);
   },
 
-  // 项目首介记录
-  interventionsList: () => {
-    return postRequest(`/social/project/interventions`);
-  },
+  //---------------------项目-----------------------
 
-  // 项目列表
-  interventionsList: (params) => {
-    return postRequest(`/project/list`, params);
+  //项目新增表单提交
+  ProjectAddition: (params) => {
+    return postRequest('/supports/project/create', params);
   },
 
-  // 查询投标阶段状态
-  getBidStages: (projectId) => {
-    return getRequest(`/social/project/bid/stages?projectId=${projectId}`);
+  //单项目详情查询
+  ProjectDetails: (params) => {
+    return getRequest(`/supports/project/${params}`);
   },
 
-  //新增服务商信息
-  createServiceInfo: (params) => {
-    return postRequest(`/project/list`, params);
+  //---------------------文件-----------------------
+  //文件资料新增表单提交
+  FileAddition: (params) => {
+    return postRequest('/supports/biz/file/create', params);
   },
 };

+ 56 - 0
src/components/BsUi/TableInfoSelect/TableInfoSelect.vue

@@ -0,0 +1,56 @@
+<template>
+  <bs-modal-table-selector
+    :selected-keys="selectedKeys"
+    :bs-modal="bsModalBean"
+    :bs-table="bsTableBean"
+    @ok="handleConfirm"
+  ></bs-modal-table-selector>
+</template>
+
+<script setup lang="jsx">
+  import BsModalTableSelector from '/@/components/BsUi/ModalTableSelector/index.vue';
+  import { ref } from 'vue';
+  import { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { useBsModal } from '/@/components/BsUi/Modal/index.js';
+
+  const emit = defineEmits(['selectedRowList']);
+  // 接收父组件传递的 bsTableBean
+  const props = defineProps({
+    bsTableBean: {
+      type: Object,
+      required: true,
+    },
+  });
+
+  // 用于回显勾中的行
+  const selectedKeys = ref([]);
+
+  const bsModalBean = useBsModal({
+    modalOptions: {
+      width: '900px',
+      visible: false,
+      title: '表格选择器开窗',
+      modalExtraProps: {
+        destroyOnClose: true,
+      },
+    },
+  });
+
+  const { setModalPropsValue: setMVal } = bsModalBean;
+
+  const handleConfirm = ({ value, setLoading, close }) => {
+    // 将 value 发送给父组件
+    emit('selectedRowList', value);
+    close();
+    setLoading(false);
+  };
+
+  const showModal = () => {
+    selectedKeys.value = [];
+    setMVal('visible', true);
+  };
+
+  defineExpose({
+    showModal,
+  });
+</script>

+ 3 - 3
src/views/business-rules/risk-rules/components/rules-list/index.vue

@@ -62,12 +62,12 @@
           },
           {
             title: '风险等级',
-            field: 'riskLevel',
+            field: 'riskGrade',
             width: 85,
             ellipsis: true,
             slots: {
               default({ row, column }) {
-                return <span>{row?.riskLevel?.[0].valueName}</span>;
+                return <span>{row?.riskGrade?.[0].valueName}</span>;
               },
             },
           },
@@ -80,7 +80,7 @@
           {
             title: '指标定义',
             field: 'definedMetrics',
-            width: 100,
+            width: 120,
             ellipsis: true,
           },
           {

+ 3 - 4
src/views/business-rules/risk-rules/modules/create-rules.vue

@@ -194,7 +194,6 @@
 
   //路由
   const query = router.currentRoute.value.query;
-  console.log('query', query);
 
   onMounted(() => {
     if (query.id) {
@@ -223,11 +222,12 @@
       // 过滤出 data 中与 formModel 相关的字段并赋值
       const filteredData = Object.keys(data).reduce((acc, key) => {
         if (formModel.hasOwnProperty(key) && key !== 'remindTime') {
-          // 排除 remindTime 字段
           // 特殊处理 portalPage、enable 和 launchRiskTask,将其从字符串转换为布尔值
           if (['portalPage', 'enable', 'launchRiskTask'].includes(key)) {
             acc[key] = data[key] === 'true'; // 如果是 "true",转换为 true,否则为 false
-          } else {
+          } else if(['countData','operator','remindWay','riskDimension','riskGrade','riskModel','showPortalPage','unit'].includes(key)){
+            acc[key] = [data[key][0].valueCode];
+          }else {
             acc[key] = data[key];
           }
         }
@@ -239,7 +239,6 @@
       if (data.remindTime) {
         // 将字符串时间转换为 Day.js 对象
         formModel.remindTime = dayjs(data.remindTime, 'HH:mm');
-        console.log(' formModel.remindTime', formModel.remindTime);
       }
     }
   }

+ 155 - 0
src/views/market-manage/external-manage/account-manage/index.vue

@@ -0,0 +1,155 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增银行卡</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+  <AddCard ref="addCardRef"></AddCard>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  import AddCard from "/@/views/market-manage/external-manage/account-manage/modules/add-card.vue"
+
+  const addCardRef = ref();
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/customer/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+          
+          {
+            title: '户号',
+            field: 'customerName',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '银行卡号',
+            field: 'socialCode',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '开户行',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '行号',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '卡类型',
+            field: 'cooperationMark',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '开户行地址',
+            field: 'cooperationAmount',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '操作时间',
+            field: 'customerManager',
+            align: 'center',
+            width: 100,
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <>
+                  <a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    } }
+                  >
+                      编辑
+                    </a-button></>
+                );
+              },
+            },
+          },
+        ],
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'customerName',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入银行卡账号',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  
+  //新增银行卡信息
+  function goDetailPage(record) {
+    console.log("123123132",record);
+    addCardRef.value.showModal(record);
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 148 - 0
src/views/market-manage/external-manage/account-manage/modules/add-card.vue

@@ -0,0 +1,148 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" width="60%" title="银行卡信息" @ok="onSubmit" @cancel="closeModal" destroyOnClose>
+    <a-form class="smart-query-form" ref="formRef" labelWrap :label-col="labelCol" :model="form" :rules="rules">
+      <div class="cost-compose">
+        <div class="basic-info">
+          <div class="basic-info-form">
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="12">
+                <a-form-item label="开户名称" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="12">
+                <a-form-item label="银行卡账号" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="12">
+                <a-form-item label="开户行" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="12">
+                <a-form-item label="行号" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="12">
+                <a-form-item label="开户行地址" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="12">
+                <a-form-item label="卡类型" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+      </div>
+    </a-form>
+  </a-modal>
+</template>
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { theme, message, Modal } from 'ant-design-vue';
+  import _ from 'lodash';
+  import DictSelect from '/@/components/support/dict-select/index.vue';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '100px' } };
+  const emits = defineEmits(['refreshTable']);
+  const visible = ref(false);
+  const UploadRef = ref();
+  function showModal(params) {
+    visible.value = true;
+    if (params.id) {
+      updateFormData(params);
+    }
+  }
+
+  function closeModal() {
+    UploadRef.value.clear();
+    visible.value = false;
+    resetFormData();
+  }
+
+  // ----------------------- form 表单操作 ---------------------
+  const formRef = ref();
+
+  const formDefault = {
+    bizId: undefined, // 项目id
+    fileType: undefined, // 文件类别
+    remark: undefined, //备注
+    attachment: undefined, // 文件
+    bizModule: 'customer', //标记 文件来源
+  };
+
+  let form = reactive({
+    ...formDefault,
+  });
+
+  // 表单校验规则
+  const fromRules = {
+    // decisionName: [{ required: true, message: '决策人姓名不能为空' }],
+  };
+
+  // 重置表单数据
+  function resetFormData() {
+    Object.assign(form, formDefault);
+  }
+
+  // ----------------------- form 表单  ajax 操作 ---------------------
+  function onSubmit() {
+    console.log('form', form);
+    formRef.value
+      .validateFields()
+      .then((values) => {
+        Modal.confirm({
+          title: '提示',
+          content: '确定要提交吗?',
+          okText: '确认',
+          onOk() {
+            postFileData();
+          },
+          cancelText: '取消',
+          onCancel() {},
+        });
+      })
+      .catch((error) => {
+        console.log('step1_error', error);
+      });
+  }
+
+  const postFileData = async () => {
+    await clientManageApi.FileAddition(form);
+    message.success('提交成功');
+    emits('refreshTable');
+    onClose();
+  };
+
+   function updateFormData(data) {
+    console.log('kaishishfishf');
+  }
+
+  function onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 274 - 0
src/views/market-manage/external-manage/basic-info/index.vue

@@ -0,0 +1,274 @@
+<template>
+  <a-card>
+    <a-form class="smart-query-form" ref="stepFormRef" labelWrap :label-col="labelCol" :model="formModel" :rules="formRules">
+      <div class="approval-steps">
+        <div>
+          <div class="basic-title">
+            <div class="basic-title-left">
+              <div class="basic-title-line"></div>
+              <span class="basic-title-text">基本信息</span>
+            </div>
+            <div class="basic-title-right">
+              <a-button type="primary" @click="submit">提交</a-button>
+            </div>
+          </div>
+          <div class="steps-content">
+            <a-row :gutter="24" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="服务商名称" name="customerName" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.customerName" placeholder="请输入服务商名称" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="服务商地址" name="customerType" class="smart-query-form-item">
+                    <a-input v-model:value="formModel.customerName" placeholder="请输入服务商地址" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="服务商类型" name="customerType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_PROVIDER_TYPE" v-model:value="formModel.customerType" placeholder="请选择服务商类型" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="统一社会代码" name="socialCode" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.socialCode" placeholder="请输入统一社会代码" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="注册时间" name="companyRegisterDate" class="smart-query-form-item">
+                  <a-date-picker v-model:value="formModel.companyRegisterDate" placeholder="注册时间" value-format="YYYY-MM-DD" style="width: 100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="注册资本" name="companyRegisterCapital" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.companyRegisterCapital" placeholder="请输入注册资本" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="营业执照" name="attachment" class="smart-query-form-item">
+                  <Upload
+                    :defaultFileList="defaultFileList"
+                    :maxUploadSize="1"
+                    buttonText="上传营业执照"
+                    listType="picture-card"
+                    @change="changeAttachment"
+                  />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="经营范围" name="invoiceTitle" class="smart-query-form-item">
+                  <a-textarea v-model:value="formModel.invoiceTitle" placeholder="请输入经营范围" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+        <div>
+          <div class="basic-title">
+            <div class="basic-title-left">
+              <div class="basic-title-line"></div>
+              <span class="basic-title-text">联系人信息</span>
+            </div>
+            <div class="basic-title-right">
+              <!-- <a-button type="primary">提交</a-button> -->
+            </div>
+          </div>
+          <div class="steps-content">
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="联系人" name="invoiceTitle" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.invoiceTitle" placeholder="请输入联系人" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="联系电话" name="invoiceCode" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.invoiceCode" placeholder="请输入联系电话" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="职务" name="invoiceAddress" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.invoiceAddress" placeholder="请输入职务" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="身份证件号" name="invoiceMobile" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.invoiceMobile" placeholder="请输入身份证件号" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row upload-row">
+              <a-col :span="8">
+                <a-form-item label="身份证人像面" name="attachment" class="smart-query-form-item">
+                  <Upload
+                    :defaultFileList="defaultFileList"
+                    :maxUploadSize="1"
+                    buttonText="上传身份证人像面"
+                    listType="picture-card"
+                    @change="changeAttachment"
+                  />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="身份证国徽面" name="attachment" class="smart-query-form-item">
+                  <Upload
+                    :defaultFileList="defaultFileList"
+                    :maxUploadSize="1"
+                    buttonText="上传身份证国徽面"
+                    listType="picture-card"
+                    @change="changeAttachment"
+                  />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+      </div>
+    </a-form>
+  </a-card>
+</template>
+<script setup>
+  import { onMounted, reactive, ref, useAttrs } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { message, Modal } from 'ant-design-vue';
+  import _ from 'lodash';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
+  import DictSelect from '/@/components/support/dict-select/index.vue';
+  import Upload from '/@/components/support/file-upload/index.vue';
+
+  const stepFormRef = ref(null);
+  const router = useRouter();
+
+  // --------------------- 数据 ---------------------
+  const formModel = reactive({
+    customerCode: undefined, //服务商名称
+    customerName: undefined, //服务商地址
+    socialCode: undefined, //服务商类别
+    partnerLevel: undefined, //统一社会代码
+    customerStatus: undefined, //注册时间
+    address: undefined, //注册资本
+    customerType: undefined, //营业执照
+    customerSource: undefined, //经营范围
+    customerManager: undefined, //联系人
+    customerManagerMobile: undefined, //联系电话
+    companyRegisterCapital: undefined, //身份证件号
+    companyRegisterDate: undefined, //身份证正面
+    customerDescription: undefined, //身份证反面
+    
+  });
+
+  // --------------------- 校验规则 ---------------------
+  const formRules = {
+    // customerName: [{ required: true, message: '请输入客户名称', trigger: 'change' }],
+    customerManagerMobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+    invoiceMobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+  };
+
+  const labelCol = { style: { width: '110px', height: '50px' } };
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    formModel.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- 步骤相关 ---------------------------
+
+  const submit = () => {
+    console.log('formModel', formModel);
+    stepFormRef.value
+      .validateFields()
+      .then((values) => {
+        Modal.confirm({
+          title: '提示',
+          content: '确定要提交吗?',
+          okText: '确认',
+          onOk() {
+            postFormData();
+          },
+          cancelText: '取消',
+          onCancel() {},
+        });
+      })
+      .catch((error) => {
+        console.log('step1_error', error);
+      });
+  };
+
+  const postFormData = () => {
+    clientManageApi.CustomerAddition(formModel).then((res) => {
+      message.success('提交成功');
+      router.push({ path: '/market-manage/external-manage/customer-manage/customer-list' });
+    });
+  };
+</script>
+
+<style scoped lang="less">
+  .approval-steps {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    // width: 1350px;
+    margin: 0 auto;
+
+    .basic-title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 100%;
+      height: 40px;
+      padding: 0 16px 4px 16px;
+      border-bottom: 2px solid #f6f6f6;
+      background: #ffffff;
+      border-top-left-radius: 8px;
+      border-top-right-radius: 8px;
+
+      .basic-title-left {
+        height: 100%;
+        display: flex;
+        align-items: center;
+
+        .basic-title-line {
+          width: 4px;
+          height: 20px;
+          border-radius: 4px;
+          background: #267ef8;
+          margin-right: 5px;
+        }
+
+        .basic-title-text {
+          font-size: 16px;
+          font-style: normal;
+          font-weight: 600;
+          line-height: 20px;
+        }
+      }
+
+      .basic-title-right {
+        display: flex;
+        gap: 10px;
+      }
+    }
+
+    .steps-content {
+      margin-top: 14px;
+      .smart-query-form-item{
+      }
+    }
+
+    .steps-action {
+      display: flex;
+      justify-content: center;
+      margin-top: 18px;
+    }
+  }
+</style>

+ 23 - 11
src/views/market-manage/external-manage/customer-manage/customer-create/index.vue

@@ -9,20 +9,19 @@
               <span class="basic-title-text">基本信息</span>
             </div>
             <div class="basic-title-right">
-              <a-button type="primary" @click="reset">重置</a-button>
               <a-button type="primary" @click="submit">提交</a-button>
             </div>
           </div>
           <div class="steps-content">
-            <a-row :gutter="16" class="smart-query-form-row">
-              <a-col :span="8">
+            <a-row :gutter="24" class="smart-query-form-row">
+              <a-col :span="16">
                 <a-form-item label="客户名称" name="customerName" class="smart-query-form-item">
                   <a-input v-model:value="formModel.customerName" placeholder="请输入客户名称" />
                 </a-form-item>
               </a-col>
               <a-col :span="8">
                 <a-form-item label="客户类型" name="customerType" class="smart-query-form-item">
-                  <DictSelect key-code="BLINK_CUSTOMER_TYPE" v-model:value="formModel.customerType" placeholder="请选择客户类型" width="100%" />
+                  <DictSelect key-code="BLINK_PROVIDER_TYPE" v-model:value="formModel.customerType" placeholder="请选择客户类型" width="100%" />
                 </a-form-item>
               </a-col>
              
@@ -44,7 +43,7 @@
                   />
                 </a-form-item>
               </a-col>
-              <a-col :span="16">
+              <a-col :span="8">
                 <a-form-item label="详细地址" name="address" class="smart-query-form-item">
                   <a-input v-model:value="formModel.address" placeholder="请输入详细地址" />
                 </a-form-item>
@@ -62,12 +61,22 @@
                 </a-form-item>
               </a-col>
               <a-col :span="8">
-                <a-form-item label="客户对接人" name="customerManager" class="smart-query-form-item">
-                  <a-input v-model:value="formModel.customerManager" placeholder="请选择客户对接人" />
+                <a-form-item label="客户来源" name="customerSource" class="smart-query-form-item">
+                 <DictSelect
+                    key-code="BLINK_CUSTOMER_SOURCE"
+                    v-model:value="formModel.customerSource"
+                    placeholder="请选择客户来源"
+                    width="100%"
+                  />
                 </a-form-item>
               </a-col>
             </a-row>
             <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="客户对接人" name="customerManager" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.customerManager" placeholder="请选择客户对接人" />
+                </a-form-item>
+              </a-col>
               <a-col :span="8">
                 <a-form-item label="对接人电话" name="customerManagerMobile" class="smart-query-form-item">
                   <a-input v-model:value="formModel.customerManagerMobile" placeholder="请输入对接人电话" />
@@ -127,7 +136,7 @@
             </a-row>
             <a-row :gutter="16" class="smart-query-form-row">
               <a-col :span="8">
-                <a-form-item label="电话" name="invoiceMobile" class="smart-query-form-item">
+                <a-form-item label="开户电话" name="invoiceMobile" class="smart-query-form-item">
                   <a-input v-model:value="formModel.invoiceMobile" placeholder="请输入电话" />
                 </a-form-item>
               </a-col>
@@ -137,7 +146,7 @@
                 </a-form-item>
               </a-col>
               <a-col :span="8">
-                <a-form-item label="账号" name="invoiceBankAccount" class="smart-query-form-item">
+                <a-form-item label="银行账号" name="invoiceBankAccount" class="smart-query-form-item">
                   <a-input v-model:value="formModel.invoiceBankAccount" placeholder="请输入账号" />
                 </a-form-item>
               </a-col>
@@ -217,6 +226,7 @@
     areaName: undefined,
     address: undefined, //详细地址
     customerType: undefined, //客户类型
+    customerSource:undefined,//客户来源
     customerManager: undefined, //客户对接人
     customerManagerMobile: undefined, //客户对接人电话
     companyRegisterCapital: undefined, //注册资本
@@ -237,6 +247,8 @@
   // --------------------- 校验规则 ---------------------
   const formRules = {
     // customerName: [{ required: true, message: '请输入客户名称', trigger: 'change' }],
+    customerManagerMobile:[{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+    invoiceMobile:[{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }]
   };
 
   const labelCol = { style: { width: '110px' } };
@@ -273,9 +285,9 @@
   };
 
   const postFormData = () => {
-    customerApi.CustomerAddition(formModel).then((res) => {
+    clientManageApi.CustomerAddition(formModel).then((res) => {
       message.success('提交成功');
-      router.push({ path: '/teamwork/customer-manage/customer-list' });
+      router.push({ path: '/market-manage/external-manage/customer-manage/customer-list' });
     });
   };
   //------------------------地区-------------------------------------

+ 60 - 103
src/views/market-manage/external-manage/customer-manage/customer-list/index.vue

@@ -3,7 +3,7 @@
     <bs-table v-bind="tableOptions">
       <template #searchRight>
         <a-space>
-          <a-button type="primary" @click="goDetailPage">
+          <a-button type="primary" @click="goAddCustomerPage">
             <template #icon>
               <PlusOutlined />
             </template>
@@ -20,10 +20,6 @@
   import { onMounted, ref, watch } from 'vue';
   import { useRouter } from 'vue-router';
 
-  const props = defineProps({
-    selectedRulesId: String,
-  });
-
   const {
     tableOptions,
     fetchTableData,
@@ -31,7 +27,7 @@
     getTablePropsValue: getValue,
   } = useBsTable({
     tableOptions: {
-      url: '/supports/risk/rule/queryPage',
+      url: '/supports/customer/queryPage',
       gridOptions: {
         loading: false,
         columns: [
@@ -40,104 +36,70 @@
             width: 80,
             align: 'center',
           },
+          
           {
-            title: '风险维度',
-            field: 'riskDimension',
-            width: 85,
-            ellipsis: true,
-            formatter: ({ cellValue }) => {
-              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
-            },
-          },
-          {
-            title: '风险模板',
-            // field: 'riskModel',
-            width: 85,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.riskModel?.[0].valueName}</span>;
-              },
-            },
+            title: '客户名称',
+            field: 'customerName',
+            align: 'center',
+            width: 200,
           },
           {
-            title: '风险等级',
-            field: 'riskLevel',
-            width: 85,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.riskLevel?.[0].valueName}</span>;
-              },
-            },
+            title: '统一社会信用代码/ID',
+            field: 'socialCode',
+            align: 'center',
+            width: 200,
           },
           {
-            title: '风险指标',
-            field: 'riskMetrics',
-            width: 110,
-            ellipsis: true,
+            title: '省市区',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            width: 200,
           },
           {
-            title: '指标定义',
-            field: 'definedMetrics',
+            title: '客户类型',
+            // field: 'customerType',
+            align: 'center',
             width: 100,
-            ellipsis: true,
+            slots: {
+            default({ row, column }) {
+              return <span>{row?.customerType?.[0]?.valueName || ''}</span>;
+            },
+            },
           },
           {
-            title: '条件运算符',
-            field: 'operator',
+            title: '客户来源',
+            // field: 'customerSource',
+            align: 'center',
             width: 100,
-            ellipsis: true,
             slots: {
-              default({ row, column }) {
-                return <span>{row?.operator?.[0].valueName}</span>;
-              },
+            default({ row, column }) {
+              return <span>{row?.customerSource?.[0]?.valueName || ''}</span>;
+            },
             },
           },
           {
-            title: '条件控制值',
-            field: 'conditionalValue',
+            title: '是否合作项目',
+            field: 'cooperationMark',
+            align: 'center',
             width: 100,
-            ellipsis: true,
           },
           {
-            title: '提醒方式',
-            field: 'remindWay',
+            title: '合作金额(元)',
+            field: 'cooperationAmount',
+            align: 'center',
             width: 100,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.remindWay?.[0].valueName}</span>;
-              },
-            },
           },
           {
-            title: '提醒时间(每天)',
-            field: 'remindTime',
-            width: 120,
-            ellipsis: true,
-          },
-          {
-            title: '风险等级',
-            field: 'riskGrade',
+            title: '客户对接人',
+            field: 'customerManager',
+            align: 'center',
             width: 100,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.riskGrade?.[0].valueName}</span>;
-              },
-            },
           },
           {
-            title: '状态',
-            field: 'enable',
-            width: 100,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row.enable === 'true' ? '启用' : '禁用'}</span>;
-              },
-            },
+            title: '创建时间',
+            field: 'createTime',
+            align: 'center',
+            width: 150,
           },
           {
             field: 'opt',
@@ -148,15 +110,16 @@
             slots: {
               default({ row, column }) {
                 return (
+                  <>
                   <a-button
                     type='text'
                     size='small'
                     onClick={() => {
                       goDetailPage(row);
-                    }}
+                    } }
                   >
-                    编辑
-                  </a-button>
+                      客户详情
+                    </a-button></>
                 );
               },
             },
@@ -185,27 +148,11 @@
         fieldSpan: 4,
         fields: [
           {
-            field: 'name',
+            field: 'customerName',
             label: '',
             component: 'a-input',
             componentProps: {
-              placeholder: '请输入服务商名称',
-            },
-          },
-          {
-            field: 'name1',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择地址',
-            },
-          },
-          {
-            field: 'name2',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择服务商类型',
+              placeholder: '请输入客户名称',
             },
           },
         ],
@@ -218,7 +165,7 @@
       toolbarConfig: {},
       tableSearchBeforeBiz() {
         const searchParams = getValue('searchConfig.data');
-        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+        setValue('searchConfig.data', { ...searchParams });
       },
     },
   });
@@ -228,10 +175,20 @@
   });
 
   const router = useRouter();
-  function goDetailPage(record) {
+
+  //查看客户详情
+  function goAddCustomerPage(record) {
     console.log('record', record);
     router.push({
       path: '/market-manage/external-manage/customer-manage/customer-create',
+    });
+  }
+  
+  //查看客户详情
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/customer-manage/single-customer',
       query: {
         id: record.id,
       },

+ 31 - 82
src/views/market-manage/external-manage/customer-manage/single-customer/components/file-info.vue

@@ -13,25 +13,25 @@
       </template>
     </bs-table>
   </div>
+    <fileDetail ref="fileDetailRef" @refreshTable="refreshTable();"></fileDetail>
 </template>
 
 <script setup lang="jsx">
   import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
   import { onMounted, ref, watch } from 'vue';
   import { useRouter } from 'vue-router';
+  import fileDetail from '/@/views/market-manage/external-manage/customer-manage/single-customer/modules/fileDetail.vue';
 
-  const props = defineProps({
-    selectedRulesId: String,
-  });
+  const fileDetailRef = ref();
 
   const {
+    refreshTable,
     tableOptions,
-    fetchTableData,
     setTablePropsValue: setValue,
     getTablePropsValue: getValue,
   } = useBsTable({
     tableOptions: {
-      url: '/supports/risk/rule/queryPage',
+      url: '/supports/biz/file/queryPage',
       gridOptions: {
         loading: false,
         columns: [
@@ -41,40 +41,38 @@
             align: 'center',
           },
           {
-            title: '附件名称',
-            field: 'riskDimension',
-            width: 185,
-            ellipsis: true,
-            formatter: ({ cellValue }) => {
-              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
-            },
-          },
-          {
-            title: '附件大小',
-            field: 'riskModel',
-            width: 85,
-            ellipsis: true,
+            title: '文件名称',
+            // field: 'fileType',
+            align: 'center',
+            width: 300,
             slots: {
               default({ row, column }) {
-                return <span>{row?.riskModel?.[0].valueName}</span>;
+                return <span>{row?.attachment?.[0]?.fileName || ''}</span>;
               },
             },
           },
           {
-            title: '操作人',
-            field: 'riskLevel',
-            width: 120,
-            ellipsis: true,
+            title: '文件类别',
+            // field: 'fileType',
+            align: 'center',
+            width: 100,
             slots: {
               default({ row, column }) {
-                return <span>{row?.riskLevel?.[0].valueName}</span>;
+                return <span>{row?.fileType?.[0]?.valueName || ''}</span>;
               },
             },
           },
+          {
+            title: '备注',
+            field: 'remark',
+            align: 'center',
+            width: 250,
+          },
           {
             title: '操作时间',
-            field: 'riskMetrics',
+            field: 'createTime',
             width: 110,
+            align: 'center',
             ellipsis: true,
           },
           {
@@ -98,7 +96,7 @@
                     type='text'
                     size='small'
                     onClick={() => {
-                      goDetailPage(row);
+                      goDownLoad(row);
                     } }
                   >
                       下载
@@ -106,7 +104,7 @@
                       type='text'
                       size='small'
                       onClick={() => {
-                        goDetailPage(row);
+                        goDelete(row);
                       } }
                     >
                       删除
@@ -116,50 +114,17 @@
             },
           },
         ],
-        data: [
-          {
-            id: 1,
-            name: '测试数据',
-            dictField: [
-              {
-                dictKeyId: '',
-                dictValueId: '',
-                remark: '',
-                sort: 3,
-                status: 1,
-                valueCode: '03',
-                valueName: '微信公众号',
-              },
-            ],
-          },
-        ], // 模拟数据源
       },
       searchConfig: {
         enabled: false,
         fieldSpan: 4,
         fields: [
           {
-            field: 'name',
+            field: 'fileType',
             label: '',
             component: 'a-input',
             componentProps: {
-              placeholder: '请输入服务商名称',
-            },
-          },
-          {
-            field: 'name1',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择地址',
-            },
-          },
-          {
-            field: 'name2',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择服务商类型',
+              placeholder: '请输入文件名称',
             },
           },
         ],
@@ -172,35 +137,19 @@
       toolbarConfig: {},
       tableSearchBeforeBiz() {
         const searchParams = getValue('searchConfig.data');
-        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+        setValue('searchConfig.data', { ...searchParams });
       },
     },
   });
 
-  // 监听 selectedRulesId 的变化
-  watch(
-    () => props.selectedRulesId,
-    (newVal, oldVal) => {
-      if (newVal !== oldVal) {
-        fetchTableData(newVal);
-      }
-    },
-    { immediate: true }
-  );
-
   onMounted(() => {
     console.log('表格已加载');
   });
 
   const router = useRouter();
-  function goDetailPage(record) {
-    console.log('record', record);
-    router.push({
-      path: '/market-manage/external-manage/customer-manage/customer-create',
-      query: {
-        id: record.id,
-      },
-    });
+   const query = router.currentRoute.value.query;
+  function goDetailPage() {
+    fileDetailRef.value.showModal(query.id);
   }
 </script>
 

+ 67 - 116
src/views/market-manage/external-manage/customer-manage/single-customer/components/project-list.vue

@@ -3,16 +3,9 @@
     <bs-table v-bind="tableOptions">
       <template #searchRight>
         <a-space>
-          <a-button type="primary" @click="goDetailPage">
-            <template #icon>
-              <PlusOutlined />
-            </template>
-            <span>新增</span>
-          </a-button>
         </a-space>
       </template>
       <template #toolbarLeft>
-        <a-space> 累计客户:XXX|黑名单客户:XXX|S级客户 </a-space>
       </template>
     </bs-table>
   </div>
@@ -23,10 +16,6 @@
   import { onMounted, ref, watch } from 'vue';
   import { useRouter } from 'vue-router';
 
-  const props = defineProps({
-    selectedRulesId: String,
-  });
-
   const {
     tableOptions,
     fetchTableData,
@@ -34,7 +23,7 @@
     getTablePropsValue: getValue,
   } = useBsTable({
     tableOptions: {
-      url: '/supports/risk/rule/queryPage',
+      url: '/supports/project/queryPage',
       gridOptions: {
         loading: false,
         columns: [
@@ -43,107 +32,93 @@
             width: 80,
             align: 'center',
           },
+
           {
-            title: '风险维度',
-            field: 'riskDimension',
-            width: 85,
-            ellipsis: true,
-            formatter: ({ cellValue }) => {
-              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
-            },
+            title: '项目名称',
+            field: 'projectName',
+            align: 'center',
+            width: 200,
           },
           {
-            title: '风险模板',
-            // field: 'riskModel',
-            width: 85,
-            ellipsis: true,
+            title: '项目类型',
+            // field: 'projectType',
+            align: 'center',
+            width: 100,
             slots: {
               default({ row, column }) {
-                return <span>{row?.riskModel?.[0].valueName}</span>;
+                return <span>{row?.projectType?.[0]?.valueName || ''}</span>;
               },
             },
           },
           {
-            title: '风险等级',
-            field: 'riskLevel',
-            width: 85,
-            ellipsis: true,
+            title: '专业类型',
+            // field: 'majorType',
+            align: 'center',
+            width: 100,
             slots: {
               default({ row, column }) {
-                return <span>{row?.riskLevel?.[0].valueName}</span>;
+                return <span>{row?.majorType?.[0]?.valueName || ''}</span>;
               },
             },
           },
           {
-            title: '风险指标',
-            field: 'riskMetrics',
-            width: 110,
-            ellipsis: true,
+            title: '省市区',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            width: 200,
           },
           {
-            title: '指标定义',
-            field: 'definedMetrics',
+            title: '详细地址',
+            field: 'address',
+            align: 'center',
             width: 100,
-            ellipsis: true,
           },
           {
-            title: '条件运算符',
-            field: 'operator',
+            title: '合同金额',
+            field: 'contractAmount',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '招标方式',
+            // field: 'tendersType',
+            align: 'center',
             width: 100,
-            ellipsis: true,
             slots: {
               default({ row, column }) {
-                return <span>{row?.operator?.[0].valueName}</span>;
+                return <span>{row?.tendersType?.[0]?.valueName || ''}</span>;
               },
             },
           },
           {
-            title: '条件控制值',
-            field: 'conditionalValue',
+            title: '预计开标日期',
+            field: 'bidOpenDate',
+            align: 'center',
             width: 100,
-            ellipsis: true,
           },
           {
-            title: '提醒方式',
-            field: 'remindWay',
+            title: '预计开工日期',
+            field: 'planStartDate',
+            align: 'center',
             width: 100,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.remindWay?.[0].valueName}</span>;
-              },
-            },
           },
           {
-            title: '提醒时间(每天)',
-            field: 'remindTime',
-            width: 120,
-            ellipsis: true,
+            title: '预计完工日期',
+            field: 'planEndDate',
+            align: 'center',
+            width: 100,
           },
           {
-            title: '风险等级',
-            field: 'riskGrade',
-            width: 100,
-            ellipsis: true,
-            slots: {
-              default({ row, column }) {
-                return <span>{row?.riskGrade?.[0].valueName}</span>;
-              },
-            },
+            title: '预计工期',
+            field: 'planDays',
+            align: 'center',
+            width: 150,
           },
           {
-            title: '状态',
-            field: 'enable',
-            width: 100,
-            ellipsis: true,
-            // formatter: (cellValue) => {
-            //   return cellValue === 'true' ? '启用' : '禁用';
-            // },
-            slots: {
-              default({ row, column }) {
-                return <span>{row.enable === 'true' ? '启用' : '禁用'}</span>;
-              },
-            },
+            title: '备注',
+            field: 'projectRemark',
+            align: 'center',
+            width: 150,
           },
           {
             field: 'opt',
@@ -154,15 +129,17 @@
             slots: {
               default({ row, column }) {
                 return (
-                  <a-button
-                    type='text'
-                    size='small'
-                    onClick={() => {
-                      goDetailPage(row);
-                    }}
-                  >
-                    编辑
-                  </a-button>
+                  <>
+                    <a-button
+                      type='text'
+                      size='small'
+                      onClick={() => {
+                        goDetailPage(row);
+                      }}
+                    >
+                      项目详情
+                    </a-button>
+                  </>
                 );
               },
             },
@@ -191,27 +168,11 @@
         fieldSpan: 4,
         fields: [
           {
-            field: 'name',
+            field: 'projectName',
             label: '',
             component: 'a-input',
             componentProps: {
-              placeholder: '请输入服务商名称',
-            },
-          },
-          {
-            field: 'name1',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择地址',
-            },
-          },
-          {
-            field: 'name2',
-            label: '',
-            component: 'a-select',
-            componentProps: {
-              placeholder: '请选择服务商类型',
+              placeholder: '请输入项目名称',
             },
           },
         ],
@@ -224,31 +185,21 @@
       toolbarConfig: {},
       tableSearchBeforeBiz() {
         const searchParams = getValue('searchConfig.data');
-        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+        setValue('searchConfig.data', { ...searchParams});
       },
     },
   });
 
-  // 监听 selectedRulesId 的变化
-  watch(
-    () => props.selectedRulesId,
-    (newVal, oldVal) => {
-      if (newVal !== oldVal) {
-        fetchTableData(newVal);
-      }
-    },
-    { immediate: true }
-  );
-
   onMounted(() => {
     console.log('表格已加载');
   });
 
   const router = useRouter();
+
   function goDetailPage(record) {
     console.log('record', record);
     router.push({
-      path: '/market-manage/external-manage/customer-manage/customer-create',
+      path: '/market-manage/external-manage/project-manage/single-project/',
       query: {
         id: record.id,
       },

+ 21 - 15
src/views/market-manage/external-manage/customer-manage/single-customer/index.vue

@@ -7,25 +7,25 @@
             <div class="header-info">
               <div class="header-info-top">
                 <span>{{ singleCusValue?.customerName }}</span>
-                <a-tag color="#D4AF37" style="font-size: 10px;">{{ singleCusValue?.partnerLevel[0]?.valueName }}</a-tag>
+                <a-tag color="#D4AF37" style="font-size: 15px">{{ singleCusValue?.partnerLevel[0]?.valueName }}</a-tag>
               </div>
               <div class="header-info-bottom">
-                <div>客户类型:
+                <div>
+                  客户类型:
                   <span>{{ singleCusValue?.customerType[0]?.valueName }}</span>
                 </div>
-                <div>客户编码:
+                <div>
+                  客户编码:
                   <span>{{ singleCusValue?.customerCode }}</span>
                 </div>
-                <div>所在省市:
+                <div>
+                  所在省市:
                   <span>{{ `${singleCusValue?.provinceName}${singleCusValue?.cityName}` }}</span>
                 </div>
               </div>
             </div>
           </a-flex>
         </a-flex>
-        <!-- <a-space>
-          <a-button type="primary">编辑</a-button>
-        </a-space> -->
       </a-flex>
     </a-card>
     <a-card :bordered="false">
@@ -46,7 +46,7 @@
 
 <script setup>
   import { ref, defineAsyncComponent, onMounted, watchEffect } from 'vue';
-  // import { customerApi } from '/@/api/teamwork/customer/customer-api';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
   import { useRouter } from 'vue-router';
 
   const router = useRouter();
@@ -58,10 +58,10 @@
   });
 
   const descList = ref([
-    { id: '1', label: '合同数量', value: singleCusValue.contractQuantity },
-    { id: '2', label: '累计产值', value: singleCusValue.totalOutputValue },
-    { id: '3', label: '已开票', value: singleCusValue.invoiceAmount },
-    { id: '4', label: '未开票', value: singleCusValue.unlicensedAmount },
+    { id: '1', label: '合同数量', value: '' },
+    { id: '2', label: '累计产值', value: '' },
+    { id: '3', label: '已开票', value: '' },
+    { id: '4', label: '未开票', value: '' },
   ]);
 
   const tabKey = ref('0');
@@ -94,15 +94,21 @@
   ];
 
   onMounted(() => {
-    // getSingleCustomerList();
+    getSingleCustomerList();
   });
 
   // 查询列表
   function getSingleCustomerList() {
-    customerApi.getCustomerList(query.id).then(
+    clientManageApi.customerDetails(query.id).then(
       (res) => {
         singleCusValue.value = res.data;
-        console.log('singleCusValue', singleCusValue.value);
+        // 动态更新 descList
+        descList.value = [
+          { id: '1', label: '合同数量', value: singleCusValue.value.contractQuantity },
+          { id: '2', label: '累计产值', value: singleCusValue.value.totalOutputValue },
+          { id: '3', label: '已开票', value: singleCusValue.value.invoiceAmount },
+          { id: '4', label: '未开票', value: singleCusValue.value.unlicensedAmount },
+        ];
       },
       (err) => {
         console.error(err);

+ 8 - 7
src/views/market-manage/external-manage/customer-manage/single-customer/modules/fileDetail.vue

@@ -13,7 +13,7 @@
             <a-row :gutter="16" class="smart-query-form-row">
               <a-col :span="24">
                 <a-form-item label="文件类别" name="fileType" class="smart-query-form-item">
-                  <DictSelect key-code="MATE_DELIVERY_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                  <DictSelect key-code="BLINK_CUSTOMER_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
                 </a-form-item>
               </a-col>
             </a-row>
@@ -52,7 +52,7 @@
   import _ from 'lodash';
   import DictSelect from '/@/components/support/dict-select/index.vue';
   import Upload from '/@/components/support/file-upload/index.vue';
-  import { projectApi } from '/@/api/project/project-api';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
   import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
 
   // ----------------------- 对外暴漏 ---------------------
@@ -63,12 +63,12 @@
   // ----------------------- modal 的显示与隐藏 ---------------------
 
   const labelCol = { style: { width: '70px' } };
-  const emits = defineEmits(['refresh']);
+  const emits = defineEmits(['refreshTable']);
   const visible = ref(false);
   const UploadRef = ref();
   function showModal(params) {
     visible.value = true;
-    form.deliveryId = params;
+    form.bizId = params;
   }
 
   function closeModal() {
@@ -81,10 +81,11 @@
   const formRef = ref();
 
   const formDefault = {
-    deliveryId: undefined, // 项目id
+    bizId: undefined, // 项目id
     fileType: undefined, // 文件类别
     remark: undefined, //备注
     attachment: undefined, // 文件
+    bizModule: 'customer',//标记 文件来源
   };
 
   let form = reactive({
@@ -111,6 +112,7 @@
 
   // ----------------------- form 表单  ajax 操作 ---------------------
   function onSubmit() {
+    console.log('form', form);
     formRef.value
       .validateFields()
       .then((values) => {
@@ -119,7 +121,6 @@
           content: '确定要提交吗?',
           okText: '确认',
           onOk() {
-            console.log('form', form);
             postFileData();
           },
           cancelText: '取消',
@@ -132,7 +133,7 @@
   }
 
   const postFileData = async () => {
-    await projectApi.addFileList(form);
+    await clientManageApi.FileAddition(form);
     message.success('提交成功');
     emits('refreshTable');
     onClose();

+ 430 - 0
src/views/market-manage/external-manage/project-manage/project-create/index.vue

@@ -0,0 +1,430 @@
+<template>
+  <a-card>
+    <a-form class="smart-query-form" ref="stepFormRef" labelWrap :label-col="labelCol" :model="formModel" :rules="formRules">
+      <div class="approval-steps">
+        <div>
+          <div class="basic-title">
+            <div class="basic-title-left">
+              <div class="basic-title-line"></div>
+              <span class="basic-title-text">甲方信息</span>
+            </div>
+            <div class="basic-title-right">
+              <a-button type="primary" @click="submit">提交</a-button>
+            </div>
+          </div>
+          <div class="steps-content">
+            <a-row :gutter="24" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="甲方名称" name="customerName" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.customerName" placeholder="请选择客户名称" @click="showSelectProj" />
+                </a-form-item>
+                <table-info-select ref="customerSelectRef" @selectedRowList="handleSelect" :bs-table-bean="bsTableBean" />
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="甲方类型" name="customerType" class="smart-query-form-item">
+                  <DictSelect
+                    key-code="BLINK_CUSTOMER_INVOICE_TYPE"
+                    v-model:value="formModel.customerType"
+                    placeholder="请选择甲方类型"
+                    width="100%"
+                    disabled
+                  />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="省市区" name="provinceCityDistrictPartA" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.provinceCityDistrictPartA" placeholder="请输入省市区" disabled />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+        <div>
+          <div class="basic-title">
+            <div class="basic-title-left">
+              <div class="basic-title-line"></div>
+              <span class="basic-title-text">项目信息</span>
+            </div>
+            <div class="basic-title-right">
+              <!-- <a-button type="primary">提交</a-button> -->
+            </div>
+          </div>
+          <div class="steps-content">
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="16">
+                <a-form-item label="项目名称" name="projectName" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.projectName" placeholder="请输入项目名称" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="项目类型" name="projectType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_PROJECT_TYPE" v-model:value="formModel.projectType" placeholder="请选择项目类型" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="专业类型" name="majorType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_PROJECT_MAJOR_TYPE" v-model:value="formModel.majorType" placeholder="请选择专业类型" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="省市区" name="invoiceBankName" class="smart-query-form-item">
+                  <AreaCascader
+                    type="province_city_district"
+                    style="width: 100%"
+                    v-model:value="provinceCityDistrict"
+                    placeholder="请选择省市区"
+                    @change="changeArea"
+                  />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="建设地址" name="address" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.address" placeholder="请输入建设地址" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="预计合同额" name="contractAmount" class="smart-query-form-item">
+                  <a-input-number v-model:value="formModel.contractAmount" placeholder="请输入预计合同额" style="width: 100%" addon-after="万" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="招标方式" name="tendersType" class="smart-query-form-item">
+                  <DictSelect key-code="BLINK_PROJECT_TENDERS_TYPE" v-model:value="formModel.tendersType" placeholder="请选择招标方式" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="预计开标日期" name="bidOpenDate" class="smart-query-form-item">
+                  <a-date-picker v-model:value="formModel.bidOpenDate" placeholder="预计开标日期" value-format="YYYY-MM-DD" style="width: 100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="预计开工日期" name="planStartDate" class="smart-query-form-item">
+                  <a-date-picker v-model:value="formModel.planStartDate" placeholder="预计开工日期" value-format="YYYY-MM-DD" style="width: 100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="预计竣工日期" name="planEndDate" class="smart-query-form-item">
+                  <a-date-picker v-model:value="formModel.planEndDate" placeholder="预计竣工日期" value-format="YYYY-MM-DD" style="width: 100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="预计工期" name="planDays" class="smart-query-form-item">
+                  <a-input-number v-model:value="formModel.planDays" placeholder="请输入预计工期" style="width: 100%" addon-after="天" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="备注" name="projectRemark" class="smart-query-form-item">
+                  <a-textarea v-model:value="formModel.projectRemark" placeholder="请输入备注" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+        <div>
+          <div class="basic-title">
+            <div class="basic-title-left">
+              <div class="basic-title-line"></div>
+              <span class="basic-title-text">其他信息</span>
+            </div>
+            <div class="basic-title-right"></div>
+          </div>
+          <div class="steps-content">
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="附件" name="attachment" class="smart-query-form-item">
+                  <Upload
+                    :defaultFileList="defaultFileList"
+                    :maxUploadSize="10"
+                    :folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
+                    buttonText="上传附件"
+                    listType="text"
+                    extraMsg="最多上传10个附件"
+                    @change="changeAttachment"
+                  />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+      </div>
+    </a-form>
+  </a-card>
+</template>
+<script setup>
+  import { onMounted, reactive, ref, useAttrs } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { message, Modal } from 'ant-design-vue';
+
+  import _ from 'lodash';
+  import { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
+  import AreaCascader from '/@/components/framework/area-cascader/index.vue';
+  import DictSelect from '/@/components/support/dict-select/index.vue';
+  import Upload from '/@/components/support/file-upload/index.vue';
+  import tableInfoSelect from '/@/components/BsUi/TableInfoSelect/tableInfoSelect.vue'
+
+  const stepFormRef = ref(null);
+  const router = useRouter();
+
+  // --------------------- 数据定义 ---------------------
+  const formModel = reactive({
+    projectName: undefined, //项目名称
+    projectType: undefined, //项目类型
+    majorType: undefined, //专业类型
+    province: undefined, //省份
+    provinceName: undefined,
+    city: undefined, //城市
+    cityName: undefined,
+    district: undefined, //区或县
+    districtName: undefined,
+    address: undefined, //详细地址
+    contractAmount: undefined, //合同金额
+    tendersType: undefined, //招标方式
+    bidOpenDate: undefined, //预计开标日期
+    planStartDate: undefined, //预计开工日期
+    planEndDate: undefined, //预计完工日期
+    planDays: undefined, //预计工期
+    projectRemark: undefined, //备注
+    attachment: undefined, //附件集合
+  });
+
+  // ---------------------- 表单校验规则 ---------------------------
+  const formRules = {
+    // customerName: [{ required: true, message: '请输入客户名称', trigger: 'change' }],
+    customerManagerMobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+    invoiceMobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+  };
+
+  // ---------------------- 表格样式配置 ----------------------------
+  const labelCol = { style: { width: '110px', height: '50px' } };
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+   // 附件列表触发事件
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    formModel.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ---------------------- 提交步骤相关 ---------------------------
+  const submit = () => {
+    console.log('formModel', formModel);
+    stepFormRef.value
+      .validateFields()
+      .then((values) => {
+        Modal.confirm({
+          title: '提示',
+          content: '确定要提交吗?',
+          okText: '确认',
+          onOk() {
+            postFormData();
+          },
+          cancelText: '取消',
+          onCancel() {},
+        });
+      })
+      .catch((error) => {
+        console.log('step1_error', error);
+      });
+  };
+
+  const postFormData = () => {
+    clientManageApi.ProjectAddition(formModel).then((res) => {
+      message.success('提交成功');
+      router.push({ path: '/market-manage/external-manage/project-manage/project-list' });
+    });
+  };
+  //------------------------------地区-------------------------------------
+
+  const provinceCityDistrict = ref([]);
+  const provinceCityDistrictPartA = ref([]);
+  function changeArea(value, selectedOptions) {
+    Object.assign(formModel, {
+      province: '',
+      provinceName: '',
+      city: '',
+      cityName: '',
+      district: '',
+      districtName: '',
+    });
+    if (!_.isEmpty(selectedOptions)) {
+      // 地区组件地区信息拆分
+      formModel.province = provinceCityDistrict.value[0].value;
+      formModel.provinceName = provinceCityDistrict.value[0].label;
+      formModel.city = provinceCityDistrict.value[1].value;
+      formModel.cityName = provinceCityDistrict.value[1].label;
+      if (provinceCityDistrict.value[2]) {
+        formModel.district = provinceCityDistrict.value[2].value;
+        formModel.districtName = provinceCityDistrict.value[2].label;
+      }
+    }
+  }
+  //------------------------------表格数据选择器-------------------------------------
+
+  const customerSelectRef = ref(null);
+
+  //表格选择器配置项
+  const bsTableBean = useBsTable({
+    tableOptions: {
+      isLoadRequest: false,
+      url: '/supports/customer/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            field: 'id',
+            title: 'ID',
+          },
+          {
+            field: 'customerName',
+            title: '甲方名称',
+          },
+          {
+            field: 'customerType',
+            title: '甲方类型',
+            cellRender: {
+              name: 'CellDict',
+            },
+          },
+          {
+            field: 'provinceCityDistrict',
+            title: '省市区',
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'customerName',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入客户名称',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enable: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {
+        enable: false,
+      },
+      // 每次查询接口之前,都会调用这个回调函数
+      tableSearchBeforeBiz() {
+        console.log('表格搜索前');
+      },
+      // 表格初始化之前,只加载一次
+      beforeMount() {
+        console.log('表格加载前');
+      },
+      // 表格初始化完成,只加载一次
+      mounted(gridRef) {
+        console.log('表格加载后', gridRef);
+      },
+    },
+  });
+  //表格选择器开窗
+  function showSelectProj() {
+    customerSelectRef.value.showModal();
+  }
+  //表格选择器数据回显
+  function handleSelect(data) {
+    formModel.customerId = data[0].id;
+    formModel.customerName = data[0].customerName;
+    formModel.customerType = data[0].customerType[0].valueCode;
+    formModel.provinceCityDistrictPartA = data[0].provinceCityDistrict;
+  }
+</script>
+
+<style scoped lang="less">
+  .approval-steps {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    // width: 1350px;
+    margin: 0 auto;
+
+    .basic-title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 100%;
+      height: 40px;
+      padding: 0 16px 4px 16px;
+      border-bottom: 2px solid #f6f6f6;
+      background: #ffffff;
+      border-top-left-radius: 8px;
+      border-top-right-radius: 8px;
+
+      .basic-title-left {
+        height: 100%;
+        display: flex;
+        align-items: center;
+
+        .basic-title-line {
+          width: 4px;
+          height: 20px;
+          border-radius: 4px;
+          background: #267ef8;
+          margin-right: 5px;
+        }
+
+        .basic-title-text {
+          font-size: 16px;
+          font-style: normal;
+          font-weight: 600;
+          line-height: 20px;
+        }
+      }
+
+      .basic-title-right {
+        display: flex;
+        gap: 10px;
+      }
+    }
+
+    .steps-content {
+      margin-top: 14px;
+    }
+
+    .steps-action {
+      display: flex;
+      justify-content: center;
+      margin-top: 18px;
+    }
+  }
+</style>

+ 226 - 0
src/views/market-manage/external-manage/project-manage/project-list/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goAddProjectPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+
+          {
+            title: '项目名称',
+            field: 'projectName',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '项目类型',
+            // field: 'projectType',
+            align: 'center',
+            width: 100,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.projectType?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '专业类型',
+            // field: 'majorType',
+            align: 'center',
+            width: 100,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.majorType?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '省市区',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            width: 200,
+          },
+          {
+            title: '详细地址',
+            field: 'address',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '合同金额',
+            field: 'contractAmount',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '招标方式',
+            // field: 'tendersType',
+            align: 'center',
+            width: 100,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.tendersType?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '预计开标日期',
+            field: 'bidOpenDate',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '预计开工日期',
+            field: 'planStartDate',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '预计完工日期',
+            field: 'planEndDate',
+            align: 'center',
+            width: 100,
+          },
+          {
+            title: '预计工期',
+            field: 'planDays',
+            align: 'center',
+            width: 150,
+          },
+          {
+            title: '备注',
+            field: 'projectRemark',
+            align: 'center',
+            width: 150,
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <>
+                    <a-button
+                      type='text'
+                      size='small'
+                      onClick={() => {
+                        goDetailPage(row);
+                      }}
+                    >
+                      项目详情
+                    </a-button>
+                  </>
+                );
+              },
+            },
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'customerName',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入客户名称',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+
+  //新增项目信息
+  function goAddProjectPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/project-manage/project-create',
+    });
+  }
+
+  //查看项目详情
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/project-manage/single-project/',
+      query: {
+        id: record.id,
+      },
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 58 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/customer-info.vue

@@ -0,0 +1,58 @@
+<template>
+  <a-flex style="padding: 0 20px" vertical gap="large">
+    <a-descriptions v-for="desc in customerInfo" :key="desc.title" :title="desc.title" :column="4">
+      <a-descriptions-item v-for="item in desc.descItem" :key="item.id" :label="item.label" :span="item.span">
+        {{ item.value }}
+      </a-descriptions-item>
+    </a-descriptions>
+  </a-flex>
+</template>
+
+<script setup name="项目信息">
+import { ref, onMounted, watchEffect } from 'vue';
+
+const props = defineProps({
+  id: {
+    type: String,
+  },
+  descData: {
+    type: Object,
+  },
+});
+
+const customerInfo = ref([]);
+
+onMounted(() => {
+  updateCustomerInfo();
+});
+
+watchEffect(() => {
+  updateCustomerInfo();
+});
+
+function updateCustomerInfo() {
+  customerInfo.value = [
+    {
+      title: '基本信息',
+      descItem: [
+        { label: '客户编号', value: props.descData?.customerCode },
+        { label: '客户名称', value: props.descData?.customerName },
+      ],
+    },
+    {
+      title: '企业信息',
+      descItem: [
+        { label: '公司名称', value: props.descData?.customerName },
+        { label: '统一社会信用代码', value: props.descData?.socialCode },
+        { label: '电话', value: props.descData?.customerManagerMobile, span: 2 },
+        { label: '注册资本', value: props.descData?.companyRegisterCapital },
+        { label: '注册时间', value: props.descData?.companyRegisterDate },
+        { label: '注册地址', value: props.descData?.address, span: 2 },
+        { label: '企业简介', value: props.descData?.customerDescription },
+      ],
+    },
+  ];
+}
+</script>
+
+<style lang="less" scoped></style>

+ 210 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/file-info.vue

@@ -0,0 +1,210 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  const props = defineProps({
+    selectedRulesId: String,
+  });
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/risk/rule/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+          {
+            title: '附件名称',
+            field: 'riskDimension',
+            width: 185,
+            ellipsis: true,
+            formatter: ({ cellValue }) => {
+              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
+            },
+          },
+          {
+            title: '附件大小',
+            field: 'riskModel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskModel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '操作人',
+            field: 'riskLevel',
+            width: 120,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskLevel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '操作时间',
+            field: 'riskMetrics',
+            width: 110,
+            ellipsis: true,
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '200px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <><a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    } }
+                  >
+                    预览
+                  </a-button><a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    } }
+                  >
+                      下载
+                    </a-button><a-button
+                      type='text'
+                      size='small'
+                      onClick={() => {
+                        goDetailPage(row);
+                      } }
+                    >
+                      删除
+                    </a-button></>
+                );
+              },
+            },
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'name',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入服务商名称',
+            },
+          },
+          {
+            field: 'name1',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择地址',
+            },
+          },
+          {
+            field: 'name2',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择服务商类型',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+      },
+    },
+  });
+
+  // 监听 selectedRulesId 的变化
+  watch(
+    () => props.selectedRulesId,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData(newVal);
+      }
+    },
+    { immediate: true }
+  );
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/customer-manage/customer-create',
+      query: {
+        id: record.id,
+      },
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 105 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/follow-up.vue

@@ -0,0 +1,105 @@
+<template>
+  <div class="main">
+    <div class="top" @click="handleAddFollowup">
+      <a-textarea readonly placeholder="请填写跟进" :auto-size="{ minRows: 2, maxRows: 5 }" />
+    </div>
+    <div class="bottom" v-for="(item, index) in followInfo" :key="index">
+      <div class="bottom-left">
+        <span>{{ item.createTime }}</span>
+      </div>
+      <div class="bottom-center">
+        <span>头像</span>
+      </div>
+      <div class="bottom-right">
+        <div class="bottom-right-top">
+          <span>{{ item.createUserId }}</span>
+          <span>14:23:56</span>
+          <span>{{ item.contactType[0].valueName }}</span>
+        </div>
+        <div class="bottom-right-bottom">
+          <span>{{ item.content }}</span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <addFol ref="addFolRef" :visible="isModalVisible" @ok="handleOk" @cancel="handleCancel" @refresh="initPage">
+  </addFol>
+</template>
+<script setup lang="jsx">
+import { onMounted, ref } from 'vue';
+import addFol from '../modules/add-followup.vue';
+import { customerApi } from '/@/api/teamwork/customer/customer-api.js';
+
+// 定义 props
+const props = defineProps({
+  id: {
+    type: String,
+    default: () => ({}),
+  },
+});
+
+const followInfo = ref([]);
+// {
+//   createTime: null,
+//   avatar: null,
+//   createUserId: null,
+//   contactType: null,
+//   content: null,
+// }
+onMounted(() => {
+  initPage();
+});
+
+const addFolRef = ref();
+
+function initPage() {
+  customerApi.FollowUpQuery(props.id).then((res) => {
+    followInfo.value = res.data
+    console.log('followInfo', followInfo);
+  });
+}
+function handleAddFollowup() {
+  addFolRef.value.showModal(props.id);
+}
+</script>
+
+<style lang="less" scoped>
+.main {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+
+  .top {
+    cursor: pointer;
+    width: 100%;
+    height: 50px;
+    background-color: white;
+    border-radius: 8px;
+    display: flex;
+    align-items: center;
+  }
+
+  .bottom {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    gap: 10px;
+
+    .bottom-center {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 50px;
+      height: 50px;
+      background-color: white;
+      border-radius: 50%;
+    }
+
+    .bottom-right-top {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+    }
+  }
+}
+</style>

+ 259 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/invoice-list.vue

@@ -0,0 +1,259 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  const props = defineProps({
+    selectedRulesId: String,
+  });
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/risk/rule/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+          {
+            title: '风险维度',
+            field: 'riskDimension',
+            width: 85,
+            ellipsis: true,
+            formatter: ({ cellValue }) => {
+              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
+            },
+          },
+          {
+            title: '风险模板',
+            // field: 'riskModel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskModel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险等级',
+            field: 'riskLevel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskLevel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险指标',
+            field: 'riskMetrics',
+            width: 110,
+            ellipsis: true,
+          },
+          {
+            title: '指标定义',
+            field: 'definedMetrics',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '条件运算符',
+            field: 'operator',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.operator?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '条件控制值',
+            field: 'conditionalValue',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '提醒方式',
+            field: 'remindWay',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.remindWay?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '提醒时间(每天)',
+            field: 'remindTime',
+            width: 120,
+            ellipsis: true,
+          },
+          {
+            title: '风险等级',
+            field: 'riskGrade',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskGrade?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '状态',
+            field: 'enable',
+            width: 100,
+            ellipsis: true,
+            // formatter: (cellValue) => {
+            //   return cellValue === 'true' ? '启用' : '禁用';
+            // },
+            slots: {
+              default({ row, column }) {
+                return <span>{row.enable === 'true' ? '启用' : '禁用'}</span>;
+              },
+            },
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    }}
+                  >
+                    编辑
+                  </a-button>
+                );
+              },
+            },
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'name',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入服务商名称',
+            },
+          },
+          {
+            field: 'name1',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择地址',
+            },
+          },
+          {
+            field: 'name2',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择服务商类型',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+      },
+    },
+  });
+
+  // 监听 selectedRulesId 的变化
+  watch(
+    () => props.selectedRulesId,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData(newVal);
+      }
+    },
+    { immediate: true }
+  );
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/customer-manage/customer-create',
+      query: {
+        id: record.id,
+      },
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 262 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/project-list.vue

@@ -0,0 +1,262 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增</span>
+          </a-button>
+        </a-space>
+      </template>
+      <template #toolbarLeft>
+        <a-space> 累计客户:XXX|黑名单客户:XXX|S级客户 </a-space>
+      </template>
+    </bs-table>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  const props = defineProps({
+    selectedRulesId: String,
+  });
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/risk/rule/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+          {
+            title: '风险维度',
+            field: 'riskDimension',
+            width: 85,
+            ellipsis: true,
+            formatter: ({ cellValue }) => {
+              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
+            },
+          },
+          {
+            title: '风险模板',
+            // field: 'riskModel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskModel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险等级',
+            field: 'riskLevel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskLevel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险指标',
+            field: 'riskMetrics',
+            width: 110,
+            ellipsis: true,
+          },
+          {
+            title: '指标定义',
+            field: 'definedMetrics',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '条件运算符',
+            field: 'operator',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.operator?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '条件控制值',
+            field: 'conditionalValue',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '提醒方式',
+            field: 'remindWay',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.remindWay?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '提醒时间(每天)',
+            field: 'remindTime',
+            width: 120,
+            ellipsis: true,
+          },
+          {
+            title: '风险等级',
+            field: 'riskGrade',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskGrade?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '状态',
+            field: 'enable',
+            width: 100,
+            ellipsis: true,
+            // formatter: (cellValue) => {
+            //   return cellValue === 'true' ? '启用' : '禁用';
+            // },
+            slots: {
+              default({ row, column }) {
+                return <span>{row.enable === 'true' ? '启用' : '禁用'}</span>;
+              },
+            },
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    }}
+                  >
+                    编辑
+                  </a-button>
+                );
+              },
+            },
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'name',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入服务商名称',
+            },
+          },
+          {
+            field: 'name1',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择地址',
+            },
+          },
+          {
+            field: 'name2',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择服务商类型',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+      },
+    },
+  });
+
+  // 监听 selectedRulesId 的变化
+  watch(
+    () => props.selectedRulesId,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData(newVal);
+      }
+    },
+    { immediate: true }
+  );
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/customer-manage/customer-create',
+      query: {
+        id: record.id,
+      },
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 259 - 0
src/views/market-manage/external-manage/project-manage/single-project/components/settlement-list.vue

@@ -0,0 +1,259 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>新增</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  const props = defineProps({
+    selectedRulesId: String,
+  });
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/risk/rule/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+            align: 'center',
+          },
+          {
+            title: '风险维度',
+            field: 'riskDimension',
+            width: 85,
+            ellipsis: true,
+            formatter: ({ cellValue }) => {
+              return cellValue && cellValue.length > 0 ? cellValue[0].valueName : '';
+            },
+          },
+          {
+            title: '风险模板',
+            // field: 'riskModel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskModel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险等级',
+            field: 'riskLevel',
+            width: 85,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskLevel?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '风险指标',
+            field: 'riskMetrics',
+            width: 110,
+            ellipsis: true,
+          },
+          {
+            title: '指标定义',
+            field: 'definedMetrics',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '条件运算符',
+            field: 'operator',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.operator?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '条件控制值',
+            field: 'conditionalValue',
+            width: 100,
+            ellipsis: true,
+          },
+          {
+            title: '提醒方式',
+            field: 'remindWay',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.remindWay?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '提醒时间(每天)',
+            field: 'remindTime',
+            width: 120,
+            ellipsis: true,
+          },
+          {
+            title: '风险等级',
+            field: 'riskGrade',
+            width: 100,
+            ellipsis: true,
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.riskGrade?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            title: '状态',
+            field: 'enable',
+            width: 100,
+            ellipsis: true,
+            // formatter: (cellValue) => {
+            //   return cellValue === 'true' ? '启用' : '禁用';
+            // },
+            slots: {
+              default({ row, column }) {
+                return <span>{row.enable === 'true' ? '启用' : '禁用'}</span>;
+              },
+            },
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <a-button
+                    type='text'
+                    size='small'
+                    onClick={() => {
+                      goDetailPage(row);
+                    }}
+                  >
+                    编辑
+                  </a-button>
+                );
+              },
+            },
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: false,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: 'name',
+            label: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入服务商名称',
+            },
+          },
+          {
+            field: 'name1',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择地址',
+            },
+          },
+          {
+            field: 'name2',
+            label: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择服务商类型',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, key: props.selectedRulesId });
+      },
+    },
+  });
+
+  // 监听 selectedRulesId 的变化
+  watch(
+    () => props.selectedRulesId,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData(newVal);
+      }
+    },
+    { immediate: true }
+  );
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  function goDetailPage(record) {
+    console.log('record', record);
+    router.push({
+      path: '/market-manage/external-manage/customer-manage/customer-create',
+      query: {
+        id: record.id,
+      },
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 170 - 0
src/views/market-manage/external-manage/project-manage/single-project/index.vue

@@ -0,0 +1,170 @@
+<template>
+  <a-flex style="padding: 0 20px" vertical :gap="10">
+    <a-card :bordered="false">
+      <a-flex justify="space-between" align="center">
+        <a-flex align="center" gap="small">
+          <a-flex vertical gap="small">
+            <div class="header-info">
+              <div class="header-info-top">
+                <span>{{ singleProjValue?.customerName }}</span>
+                <a-tag color="#D4AF37" style="font-size: 15px">{{ singleProjValue?.partnerLevel?.[0]?.valueName }}</a-tag>
+              </div>
+              <div class="header-info-bottom">
+                <div>
+                  客户类型:
+                  <span>{{ singleProjValue?.customerType?.[0]?.valueName}}</span>
+                </div>
+                <div>
+                  客户编码:
+                  <span>{{ singleProjValue?.customerCode }}</span>
+                </div>
+                <div>
+                  所在省市:
+                  <span>{{ `${singleProjValue?.provinceName}${singleProjValue?.cityName}` }}</span>
+                </div>
+              </div>
+            </div>
+          </a-flex>
+        </a-flex>
+      </a-flex>
+    </a-card>
+    <a-card :bordered="false">
+      <a-flex vertical gap="small">
+        <div class="context">
+          <div v-for="item in descList" :key="item.id" class="context-item">
+            <span>{{ item.label }}</span>
+            <span>{{ item.value }}</span>
+          </div>
+        </div>
+      </a-flex>
+    </a-card>
+    <a-card :tab-list="tabList" :active-tab-key="tabKey" @tabChange="onTabChange">
+      <component :is="tabList[tabKey]['component']" :id="query.id" :descData="singleProjValue"></component>
+    </a-card>
+  </a-flex>
+</template>
+
+<script setup>
+  import { ref, defineAsyncComponent, onMounted } from 'vue';
+  import { clientManageApi } from '/@/api/market-manage/index.js';
+  import { useRouter } from 'vue-router';
+
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+  const singleProjValue = ref({
+    customerName: '',
+    partnerLevel: [],
+    customerType: [],
+  });
+
+  const descList = ref([
+    { id: '1', label: '合同数量', value: '' },
+    { id: '2', label: '累计产值', value: '' },
+    { id: '3', label: '已开票', value: '' },
+    { id: '4', label: '未开票', value: '' },
+  ]);
+
+  const tabKey = ref('0');
+  const tabList = [
+    {
+      key: '0',
+      tab: '基本信息',
+      component: defineAsyncComponent(() => import('./components/customer-info.vue')),
+    },
+    {
+      key: '1',
+      tab: '项目信息',
+      component: defineAsyncComponent(() => import('./components/project-list.vue')),
+    },
+    {
+      key: '2',
+      tab: '结算管理',
+      component: defineAsyncComponent(() => import('./components/settlement-list.vue')),
+    },
+    {
+      key: '3',
+      tab: '开票管理',
+      component: defineAsyncComponent(() => import('./components/invoice-list.vue')),
+    },
+    {
+      key: '4',
+      tab: '文件资料',
+      component: defineAsyncComponent(() => import('./components/file-info.vue')),
+    },
+  ];
+
+  onMounted(() => {
+    getSingleProjList();
+  });
+
+  // 查询列表
+  function getSingleProjList() {
+    clientManageApi.ProjectDetails(query.id).then(
+      (res) => {
+        singleProjValue.value = res.data;
+        // 动态更新 descList
+        descList.value = [
+          { id: '1', label: '合同数量', value: singleProjValue.value.contractQuantity },
+          { id: '2', label: '累计产值', value: singleProjValue.value.totalOutputValue },
+          { id: '3', label: '已开票', value: singleProjValue.value.invoiceAmount },
+          { id: '4', label: '未开票', value: singleProjValue.value.unlicensedAmount },
+        ];
+      },
+      (err) => {
+        console.error(err);
+      }
+    );
+  }
+
+  const onTabChange = (value) => {
+    tabKey.value = value;
+  };
+</script>
+
+<style lang="less" scoped>
+  .header-info {
+    .header-info-top {
+      display: flex;
+      align-items: center;
+      gap: 20px;
+      margin-bottom: 10px;
+
+      span {
+        font-size: 20px;
+        font-weight: 600;
+      }
+    }
+
+    .header-info-bottom {
+      display: flex;
+      align-items: center;
+      gap: 80px;
+
+      span {
+        font-size: 15px;
+        font-weight: 600;
+        color: rgb(0, 0, 0);
+      }
+    }
+  }
+
+  .context {
+    display: flex;
+    align-items: center;
+    gap: 100px;
+    margin-left: 20px;
+
+    .context-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 10px;
+    }
+
+    span:nth-child(1) {
+      font-size: 15px;
+      font-weight: 600;
+      color: rgb(1, 1, 1);
+    }
+  }
+</style>

+ 140 - 0
src/views/market-manage/external-manage/project-manage/single-project/modules/add-decision.vue

@@ -0,0 +1,140 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" title="新增决策人" @ok="handleOk" @cancel="closeModal" destroyOnClose>
+    <a-form ref="formRef" :model="formState" layout="vertical" :rules="fromRules">
+      <a-form-item label="决策人" name="decisionName">
+        <a-input v-model:value.trim="formState.decisionName" placeholder="请输入决策人姓名" />
+      </a-form-item>
+      <a-form-item label="职务" name="staffName">
+        <a-input v-model:value.trim="formState.staffName" placeholder="请输入职务" />
+      </a-form-item>
+      <a-form-item style="width: 100%" label="职务权重" name="staffWeight">
+        <a-input-number style="width: 100%" v-model:value.trim="formState.staffWeight" placeholder="请输入职务权重" />
+      </a-form-item>
+      <a-form-item label="电话" name="phoneNumber">
+        <a-input v-model:value.trim="formState.phoneNumber" placeholder="请输入电话" />
+      </a-form-item>
+      <a-form-item label="年龄" name="decisionAge">
+        <a-input-number style="width: 100%" v-model:value.trim="formState.decisionAge" placeholder="请输入年龄" />
+      </a-form-item>
+      <a-form-item label="性别" name="decisionSex">
+        <a-input v-model:value.trim="formState.decisionSex" placeholder="请输入性别" />
+      </a-form-item>
+    </a-form>
+  </a-modal>
+</template>
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { customerApi } from '/@/api/teamwork/customer/customer-api.js';
+  import { smartSentry } from '/@/lib/smart-sentry';
+  import { SmartLoading } from '/@/components/framework/smart-loading';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+  const emits = defineEmits(['refresh']);
+
+  const visible = ref(false);
+
+  function showModal(params) {
+    visible.value = true;
+    updateFormData(params);
+  }
+
+  function closeModal() {
+    visible.value = false;
+    resetFormData();
+  }
+
+  // ----------------------- form 表单操作 ---------------------
+  const formRef = ref();
+
+  const defaultPlatformAccountForm = {
+    customerId: undefined,
+    id: undefined,
+    decisionName: undefined, //决策人姓名
+    staffName: undefined, //职务
+    staffWeight: undefined, //职务权重
+    phoneNumber: undefined, //电话
+    decisionAge: undefined, //年龄
+    decisionSex: undefined, //性别
+  };
+
+  let formState = reactive({
+    ...defaultPlatformAccountForm,
+  });
+
+  // 表单校验规则
+  const fromRules = {
+    decisionName: [{ required: true, message: '决策人姓名不能为空' }],
+    staffName: [{ required: true, message: '职务不能为空' }],
+    staffWeight: [{ required: true, message: '职务权重不能为空' }],
+    phoneNumber: [{ required: true, message: '电话不能为空' }],
+    decisionAge: [{ required: true, message: '年龄不能为空' }],
+    decisionSex: [{ required: true, message: '性别不能为空' }],
+  };
+
+  // 更新表单数据
+  function updateFormData(data) {
+    console.log('-=-=-=-=', data);
+
+    Object.assign(formState, defaultPlatformAccountForm);
+    if (data) {
+      Object.assign(formState, data);
+    }
+    visible.value = true;
+  }
+
+  // 重置表单数据
+  function resetFormData() {
+    Object.assign(formState, defaultPlatformAccountForm);
+  }
+
+  async function handleOk() {
+    try {
+      await formRef.value.validate();
+      if (formState.id) {
+        updateDecision();
+      } else {
+        addDecision();
+      }
+    } catch (error) {}
+  }
+
+  // ----------------------- form 表单  ajax 操作 ---------------------
+  // 添加 ajax请求
+  async function addDecision() {
+    SmartLoading.show();
+    try {
+      await customerApi.DecisionAddition(formState);
+      emits('refresh');
+      closeModal();
+    } catch (error) {
+      smartSentry.captureError(error);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+
+  // 编辑 ajax请求
+  async function updateDecision() {
+    SmartLoading.show();
+    try {
+      await customerApi.DecisionUpdate(formState);
+      emits('refresh');
+      closeModal();
+    } catch (error) {
+      smartSentry.captureError(error);
+    } finally {
+      SmartLoading.hide();
+    }
+  }
+</script>

+ 100 - 0
src/views/market-manage/external-manage/project-manage/single-project/modules/add-followup.vue

@@ -0,0 +1,100 @@
+<!--
+  * 跟进方式 弹窗
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" title="新增跟进记录" @ok="handleOk" @cancel="closeModal" destroyOnClose>
+    <a-form ref="formRef" :model="formState" layout="vertical" :rules="formRules">
+      <a-form-item label="跟进内容" name="content">
+        <a-input v-model:value="formState.content" placeholder="请输入跟进内容" />
+      </a-form-item>
+      <a-form-item label="跟进方式" name="contactType" placeholder="请选择跟进方式">
+        <DictSelect key-code="BLINK_CUSTOMER_FOLLOW_TYPE" v-model:value="formState.contactType" width="100%" />
+      </a-form-item>
+    </a-form>
+  </a-modal>
+</template>
+<script setup>
+import message from 'ant-design-vue/lib/message';
+import { reactive, ref } from 'vue';
+import { customerApi } from '/@/api/teamwork/customer/customer-api.js';
+import { smartSentry } from '/@/lib/smart-sentry';
+import DictSelect from '/@/components/common/dict-select/index.vue';
+import { SmartLoading } from '/@/components/framework/smart-loading';
+
+// ----------------------- 对外暴漏 ---------------------
+defineExpose({
+  showModal,
+});
+
+// ----------------------- modal 的显示与隐藏 ---------------------
+const emits = defineEmits(['refresh']);
+
+const visible = ref(false);
+const customerId = ref(null);
+
+function showModal(id) {
+  visible.value = true;
+  customerId.value = id;
+  // updateFormData(data);
+}
+
+function closeModal() {
+  visible.value = false;
+  resetFormData();
+}
+
+// ----------------------- form 表单操作 ---------------------
+const formRef = ref();
+
+const defaultPlatformAccountForm = {
+  customerId: customerId, //客户id
+  content: undefined, //跟进内容
+  contactType: undefined, //跟进方式
+};
+
+let formState = reactive({
+  ...defaultPlatformAccountForm,
+});
+
+// 表单校验规则
+const formRules = {
+  content: [
+    { required: true, message: '跟进内容不能为空' },
+  ],
+  contactType: [
+    { required: true, message: '跟进方式不能为空' },
+  ],
+};
+
+// 重置表单数据
+function resetFormData() {
+  Object.assign(formState, defaultPlatformAccountForm);
+}
+
+async function handleOk() {
+  try {
+    await formRef.value.validate();
+    addFollowup();
+  } catch (error) {
+  }
+}
+
+// ----------------------- form 表单  ajax 操作 ---------------------
+// 添加 ajax请求
+async function addFollowup() {
+  SmartLoading.show();
+  try {
+    await customerApi.FollowUpAdd(formState);
+    closeModal();
+    message.success('提交成功');
+    emits('refresh');
+  } catch (error) {
+    smartSentry.captureError(error);
+  } finally {
+    SmartLoading.hide();
+  }
+}
+</script>

+ 145 - 0
src/views/market-manage/external-manage/project-manage/single-project/modules/fileDetail.vue

@@ -0,0 +1,145 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" width="40%" title="文件登记" @ok="onSubmit" @cancel="closeModal" destroyOnClose>
+    <a-form class="smart-query-form" ref="formRef" labelWrap :label-col="labelCol" :model="form" :rules="rules">
+      <div class="cost-compose">
+        <div class="basic-info">
+          <div class="basic-info-form">
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="文件类别" name="fileType" class="smart-query-form-item">
+                  <DictSelect key-code="MATE_DELIVERY_FILE_TYPE" v-model:value="form.fileType" placeholder="请选择文件类别" width="100%" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="备注" name="remark" class="smart-query-form-item">
+                  <a-textarea v-model:value="form.remark" placeholder="请输入备注" :auto-size="{ minRows: 2, maxRows: 5 }" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
+              <a-col :span="24">
+                <a-form-item label="文件" name="form.attachment" class="smart-query-form-item">
+                  <Upload
+                    ref="UploadRef"
+                    :defaultFileList="defaultFileList"
+                    :maxUploadSize="10"
+                    :folder="FILE_FOLDER_TYPE_ENUM.COMMON.value"
+                    buttonText="上传文件"
+                    listType="text"
+                    extraMsg="最多上传10个附件"
+                    @change="changeAttachment"
+                  />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </div>
+        </div>
+      </div>
+    </a-form>
+  </a-modal>
+</template>
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { theme, message, Modal } from 'ant-design-vue';
+  import _ from 'lodash';
+  import DictSelect from '/@/components/support/dict-select/index.vue';
+  import Upload from '/@/components/support/file-upload/index.vue';
+  import { projectApi } from '/@/api/project/project-api';
+  import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '70px' } };
+  const emits = defineEmits(['refresh']);
+  const visible = ref(false);
+  const UploadRef = ref();
+  function showModal(params) {
+    visible.value = true;
+    form.deliveryId = params;
+  }
+
+  function closeModal() {
+    UploadRef.value.clear();
+    visible.value = false;
+    resetFormData();
+  }
+
+  // ----------------------- form 表单操作 ---------------------
+  const formRef = ref();
+
+  const formDefault = {
+    deliveryId: undefined, // 项目id
+    fileType: undefined, // 文件类别
+    remark: undefined, //备注
+    attachment: undefined, // 文件
+  };
+
+  let form = reactive({
+    ...formDefault,
+  });
+
+  // 表单校验规则
+  const fromRules = {
+    // decisionName: [{ required: true, message: '决策人姓名不能为空' }],
+  };
+
+  // 重置表单数据
+  function resetFormData() {
+    Object.assign(form, formDefault);
+  }
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    form.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- form 表单  ajax 操作 ---------------------
+  function onSubmit() {
+    formRef.value
+      .validateFields()
+      .then((values) => {
+        Modal.confirm({
+          title: '提示',
+          content: '确定要提交吗?',
+          okText: '确认',
+          onOk() {
+            console.log('form', form);
+            postFileData();
+          },
+          cancelText: '取消',
+          onCancel() {},
+        });
+      })
+      .catch((error) => {
+        console.log('step1_error', error);
+      });
+  }
+
+  const postFileData = async () => {
+    await projectApi.addFileList(form);
+    message.success('提交成功');
+    emits('refreshTable');
+    onClose();
+  };
+
+  function onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 153 - 0
src/views/market-manage/external-manage/project-manage/single-project/modules/get-projectnum.vue

@@ -0,0 +1,153 @@
+<!--
+  * 跟进方式 弹窗
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" @ok="closeModal" destroyOnClose style="width: 90%; height: 90%">
+    <div class="title">
+      <div class="top-title">
+        <span>{{ recordData.deliveryName }}</span>
+        <a-tag>{{ recordData.status }}</a-tag>
+      </div>
+
+      <div class="bottom-title">
+        <span>项目ID:{{ recordData.deliveryCode }}</span>
+        <span>任务数:{{ recordData.taskCount }}</span>
+        <span>预计开始:{{ recordData.planStartDate }}</span>
+        <span>预计结束:{{ recordData.planEndDate }}</span>
+        <span>预计人天:{{ recordData.planDays }}</span>
+        <span>实际人天:{{ recordData.actualDays }}</span>
+        <span>交付经理:{{ recordData.deliveryManager }}</span>
+      </div>
+    </div>
+    <a-form ref="formRef" :model="formState" layout="vertical">
+      <CommonTable ref="commonTableRef" :tableOptions="tableConfig"> </CommonTable>
+    </a-form>
+  </a-modal>
+</template>
+<script setup>
+  import { reactive, ref } from 'vue';
+  import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+  const emits = defineEmits(['refresh']);
+
+  const visible = ref(false);
+  const commonTableRef = ref(null);
+  const recordData = reactive({});
+
+  function showModal(record) {
+    visible.value = true;
+    Object.assign(recordData, record);
+    console.log('recordDate', recordData);
+  }
+
+  // ----------------------- 表格配置 ---------------------
+
+  const tableConfig = ref({
+    // 设置网络请求地址
+    url: '/supports/customer/queryPage',
+
+    // 设置网络请求方式为GET
+    requestMethod: 'GET',
+    tableId: TABLE_ID_CONST.TEAMWORK.CUSTOMER_MANAGE.TASK_LIST,
+    columns: [
+      {
+        title: '任务名称',
+        dataIndex: 'socialCode',
+        align: 'center',
+      },
+      {
+        title: '任务ID',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '任务状态',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '预计开始',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '预计结束',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '预计人天',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '实际开始',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '实际结束',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '实际人天',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '前端开发',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+      {
+        title: '后端开发',
+        dataIndex: 'provinceCityDistrict',
+        align: 'center',
+      },
+    ],
+    tableAttrs: {},
+    showIndexColumn: true,
+    // 请求前传参
+    beforeFetch({ params }) {
+      return {
+        ...params,
+      };
+    },
+    // afterFetch({ data }) {},
+  });
+
+  function closeModal() {
+    visible.value = false;
+  }
+</script>
+<style lang="less" scoped>
+  .title {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    .top-title {
+      display: flex;
+      gap: 10px;
+      span {
+        font-size: 16px;
+      }
+    }
+    .bottom-title {
+      display: flex;
+      gap: 20px;
+      span {
+        font-size: 16px;
+      }
+    }
+  }
+</style>