Browse Source

Merge remote-tracking branch 'origin/dev' into dev

liuc 3 months ago
parent
commit
6b560be2f0
20 changed files with 2969 additions and 90 deletions
  1. 25 0
      src/api/support/supportManageApi/index.js
  2. 105 73
      src/views/support-manage/competition/opponent/create-opponent/index.vue
  3. 7 8
      src/views/support-manage/competition/opponent/opponent-detail/components/CredentialInfo/modules/CredentialDetail.vue
  4. 118 9
      src/views/support-manage/competition/opponent/opponent-detail/index.vue
  5. 370 0
      src/views/support-manage/competition/quotation/create-quotation/index.vue
  6. 190 0
      src/views/support-manage/competition/quotation/index.vue
  7. 108 0
      src/views/support-manage/competition/quotation/quotation-detail/index.vue
  8. 152 0
      src/views/support-manage/components/TableInfoSelect.vue
  9. 165 0
      src/views/support-manage/mobile/configuration/components/CredentialInfo/index.vue
  10. 128 0
      src/views/support-manage/mobile/configuration/components/CredentialInfo/modules/CredentialDetail.vue
  11. 193 0
      src/views/support-manage/mobile/configuration/components/DateInfo/index.vue
  12. 146 0
      src/views/support-manage/mobile/configuration/components/DateInfo/modules/fileDetail.vue
  13. 158 0
      src/views/support-manage/mobile/configuration/components/PerformanceCase/components/create-classification/index.vue
  14. 130 0
      src/views/support-manage/mobile/configuration/components/PerformanceCase/components/performance-manage/index.vue
  15. 240 0
      src/views/support-manage/mobile/configuration/components/PerformanceCase/index.vue
  16. 174 0
      src/views/support-manage/mobile/configuration/components/ProjectInfo/index.vue
  17. 146 0
      src/views/support-manage/mobile/configuration/components/ProjectInfo/modules/HistoryProjDetail.vue
  18. 152 0
      src/views/support-manage/mobile/configuration/components/ProjectInfo/modules/ProjectQuotation.vue
  19. 101 0
      src/views/support-manage/mobile/configuration/index.vue
  20. 161 0
      src/views/support-manage/risk/serviceRiskProvider/index.vue

+ 25 - 0
src/api/support/supportManageApi/index.js

@@ -32,6 +32,23 @@ export const fetchCreateCompetitor = (data) => {
   });
 };
 
+//编辑竞争对手
+export const fetchUpdateCompetitor = (data) => {
+  return request({
+    url: `/supports/competitor/query/${data}`,
+    method: 'get',
+  });
+};
+
+//编辑竞争对手数据提交
+export const fetchUpdateCompetitorDate = (data) => {
+  return request({
+    url: `/supports/competitor/update`,
+    method: 'post',
+    data,
+  });
+};
+
 //竞争对手详情
 export const fetchCompetitorDetail = (data) => {
   return request({
@@ -40,6 +57,14 @@ export const fetchCompetitorDetail = (data) => {
   });
 };
 
+//资质新增
+export const fetchCredentialList = (data) => {
+  return request({
+    url: `/supports/competitor/qualification/create`,
+    method: 'post',
+    data,
+  });
+};
 //资质新增
 export const fetchCredentialAdd = (data) => {
   return request({

+ 105 - 73
src/views/support-manage/competition/opponent/create-opponent/index.vue

@@ -6,7 +6,7 @@
           <div class="basic-title">
             <div class="basic-title-left">
               <div class="basic-title-line"></div>
-              <span class="basic-title-text">竞争对手登记</span>
+              <span class="basic-title-text">{{ titleText }}</span>
             </div>
             <div class="basic-title-right">
               <a-button type="primary" @click="submit">提交</a-button>
@@ -88,7 +88,7 @@
             </a-row>
           </div>
         </div>
-        <div>
+        <div v-if="!query.id">
           <div class="basic-title">
             <div class="basic-title-left">
               <div class="basic-title-line"></div>
@@ -114,7 +114,7 @@
                         />
                       </template>
                       <template v-if="col === 'remark'">
-                        <a-input v-model:value="scheduleList[index][col]"/>
+                        <a-input v-model:value="scheduleList[index][col]" />
                       </template>
                     </div>
                   </template>
@@ -161,7 +161,7 @@
   </a-card>
 </template>
 <script setup>
-  import { onMounted, reactive, ref, useAttrs, watch } from 'vue';
+  import { onMounted, reactive, ref, useAttrs, watch, computed } from 'vue';
   import { useRouter } from 'vue-router';
   import { message, Modal } from 'ant-design-vue';
   import _ from 'lodash';
@@ -169,10 +169,29 @@
   import DictSelect from '/@/components/support/dict-select/index.vue';
   import Upload from '/@/components/support/file-upload/index.vue';
   import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
-  import { fetchCreateCompetitor } from '/@/api/support/supportManageApi/index.js';
+  import { fetchCreateCompetitor, fetchUpdateCompetitor, fetchUpdateCompetitorDate } from '/@/api/support/supportManageApi/index.js';
 
   const stepFormRef = ref(null);
   const router = useRouter();
+  const query = router.currentRoute.value.query;
+
+  const titleText = computed(() => {
+    return query.id ? '竞争对手编辑' : '竞争对手登记';
+  });
+
+  onMounted(() => {
+    if (query.id) {
+      updateOpponentInfo(query.id);
+    }
+  });
+
+  function updateOpponentInfo(param) {
+    fetchUpdateCompetitor(param).then((res) => {
+      Object.assign(formModel, res.data);
+      formModel.attribute = res.data.attribute[0].valueName;
+      defaultFileList.value = res.data.attachment;
+    });
+  }
 
   // --------------------- 数据 ---------------------
   const formModel = reactive({
@@ -188,6 +207,7 @@
   const labelCol = { style: { width: '125px', height: '40px' } };
 
   // ----------------------- 上传附件 ----------------------------
+
   // 已上传的附件列表
   const defaultFileList = ref([]);
   function changeAttachment(fileList) {
@@ -211,7 +231,11 @@
           content: '确定要提交吗?',
           okText: '确认',
           onOk() {
-            postFormData();
+            if (query.id) {
+              updateFormDate();
+            } else {
+              postFormData();
+            }
           },
           cancelText: '取消',
           onCancel() {},
@@ -222,13 +246,21 @@
       });
   };
 
+  //竞争对手编辑信息提交
+  const updateFormDate = () => {
+    fetchUpdateCompetitorDate(formModel).then((res) => {
+      message.success('修改成功');
+      router.push({ path: '/support-manage/competition/opponent' });
+    });
+  };
+  // 竞争对手新增信息提交
   const postFormData = () => {
     fetchCreateCompetitor(formModel).then((res) => {
       message.success('提交成功');
       router.push({ path: '/support-manage/competition/opponent' });
     });
   };
-  //------------------------地区-------------------------------------
+  //------------------------地区选择-------------------------------------
 
   const registeredAddress = ref([]);
   function changeArea(value, selectedOptions) {
@@ -270,7 +302,7 @@
       title: '资质名称',
       dataIndex: 'qualificationName',
       align: 'center',
-      width:'40%',
+      width: '40%',
       slots: {
         customRender: 'qualificationName',
       },
@@ -313,72 +345,72 @@
 
   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: 'registeredAddress',
-  //           title: '省市区',
-  //         },
-  //       ],
+  // 表格选择器配置项
+  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: 'registeredAddress',
+            title: '省市区',
+          },
+        ],
 
-  //     },
-  //     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);
-  //     },
-  //   },
-  // });
+      },
+      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() {

+ 7 - 8
src/views/support-manage/competition/opponent/opponent-detail/components/CredentialInfo/modules/CredentialDetail.vue

@@ -52,21 +52,20 @@
   const labelCol = { style: { width: '70px' } };
   const emits = defineEmits(['refreshTable']);
   const visible = ref(false);
-  const UploadRef = ref();
-  function showModal(params) {
+  function showModal({ data, query }) {
     visible.value = true;
-    form.competitorId = params.query.id;
+    form.competitorId = query.id;
 
-    if (params.data.id) {
-      form.id=params.params.id
-      getTaskInfo();
+    if (data.id) {
+      getTaskInfo(data);
     }
   }
-  function getTaskInfo() {
+  function getTaskInfo(data) {
     console.log('diaoyong diaoyong');
+    form.qualificationName = data.qualificationName[0].valueName;
+    form.remark = data.remark;
   }
   function closeModal() {
-    UploadRef.value.clear();
     visible.value = false;
     resetFormData();
   }

+ 118 - 9
src/views/support-manage/competition/opponent/opponent-detail/index.vue

@@ -2,14 +2,60 @@
   <div class="page-detail">
     <page-detail-layout :tabs="tabs" v-model:tab-active-key="tabActiveKey" :index-config="indexConfig" :title="headerIndexData?.competitorName">
       <template #titleRight>
-        <a-tag>
-          <img :src=logoImg alt="" />
-          <span class="title-render">{{ headerIndexData?.formerName }}</span>
-        </a-tag>
+        <a-dropdown>
+          <a-tag>
+            <img :src="logoImg" alt="" />
+            <span class="title-render">{{ headerIndexData?.formerName }}12121212</span>
+          </a-tag>
+          <template #overlay>
+            <a-menu style="width: 50%">
+              <a-menu-item>
+                <a-timeline style="margin-top: 20px">
+                  <a-timeline-item>
+                    <div class="step-item">
+                      <div class="step-item-header">
+                        <div class="sih-left">
+                          <a-avatar>元</a-avatar>
+                        </div>
+                        <div class="sih-right">
+                          <div class="sih-r-top">
+                            <span>元宏宇</span>
+                          </div>
+                          <div class="sih-r-bottom">
+                            <bs-ellipsis-text text="2025-01-01 10:20:30" />
+                          </div>
+                        </div>
+                      </div>
+                      <div class="step-item-title">跟进情况详细说明信息展示位置,跟进情况详细说明信息展示位置,跟进情况详细说明信息展示位置,</div>
+                    </div>
+                  </a-timeline-item>
+                  <a-timeline-item>
+                    <div class="step-item">
+                      <div class="step-item-header">
+                        <div class="sih-left">
+                          <a-avatar>元</a-avatar>
+                        </div>
+                        <div class="sih-right">
+                          <div class="sih-r-top">
+                            <span>元宏宇</span>
+                          </div>
+                          <div class="sih-r-bottom">
+                            <bs-ellipsis-text text="2025-01-01 10:20:30" />
+                          </div>
+                        </div>
+                      </div>
+                      <div class="step-item-title">跟进情况详细说明信息展示位置,跟进情况详细说明信息展示位置,跟进情况详细说明信息展示位置,</div>
+                    </div>
+                  </a-timeline-item>
+                </a-timeline>
+              </a-menu-item>
+            </a-menu>
+          </template>
+        </a-dropdown>
       </template>
 
       <template #toolBtn>
-        <a-button danger ghost type="primary" size="small" @click="handleUpdateCompetition">
+        <a-button danger ghost type="primary" size="small" @click="handleUpdateCompetitionInfo(route.query.id)">
           <UserDeleteOutlined />
           <span>信息编辑</span>
         </a-button>
@@ -45,7 +91,10 @@
 
 <script setup>
   import { onMounted, reactive, ref, watch } from 'vue';
-  import { useRoute } from 'vue-router';
+  import { useRoute,useRouter } from 'vue-router';
+
+  import { fetchCustomerHeaderInfo, fetchCustomerDetail } from '/@/api/customer-manage/index.js';
+  import { fetchCompetitorDetail } from '/@/api/support/supportManageApi/index.js';
   import PageDetailLayout from '/@/components/business/page-detail-layout/index.vue';
   import BasicInfo from '/@/views/support-manage/competition/opponent/opponent-detail/components/BasicInfo/index.vue';
   import CredentialInfo from '/@/views/support-manage/competition/opponent/opponent-detail/components/CredentialInfo/index.vue';
@@ -53,10 +102,8 @@
   import DateInfo from '/@/views/support-manage/competition/opponent/opponent-detail/components/DateInfo/index.vue';
   import logoImg from '/@/assets/images/page-detail-layout/customer/icon-biaoqianguanli.svg';
 
-  import { fetchCustomerHeaderInfo, fetchCustomerDetail } from '/@/api/customer-manage/index.js';
-  import { fetchCompetitorDetail } from '/@/api/support/supportManageApi/index.js';
-
   const route = useRoute();
+   const router = useRouter();
   const headerIndexData = ref(null);
 
   const getImgUrl = (name) => {
@@ -175,12 +222,74 @@
       ];
     }
   });
+  //竞争对手信息修改
+  const handleUpdateCompetitionInfo = (record) => {
+    console.log(record);
+    router.push({
+      path: '/support-manage/competition/opponent/create-opponent',
+      query: {
+        id: record,
+      },
+    });
+  };
 </script>
 
 <style lang="scss" scoped>
   .page-detail {
     width: 100%;
 
+    .step-item {
+      width: 100%;
+      .step-item-header {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        .sih-left {
+        }
+
+        .sih-right {
+          width: 100%;
+          .sih-r-top {
+            display: flex;
+            gap: 5px;
+            .sih-project {
+              display: flex;
+              align-items: center;
+              gap: 5px;
+            }
+          }
+
+          .sih-r-bottom {
+            color: #6c6c6c;
+            font-size: 14px;
+            display: flex;
+            gap: 20px;
+          }
+        }
+      }
+
+      .step-item-title {
+        margin-top: 13px;
+        font-size: 14px;
+        font-weight: 400;
+        color: #202224;
+      }
+
+      .step-item-photo {
+        margin-top: 6px;
+        display: flex;
+        flex-wrap: wrap;
+        gap: 20px;
+        .sip-item {
+          width: 94px;
+          height: 94px;
+          border-radius: 16px;
+          background: #f5f6f7;
+        }
+      }
+    }
+
     .tag-stl {
       display: flex;
       gap: 5px;

+ 370 - 0
src/views/support-manage/competition/quotation/create-quotation/index.vue

@@ -0,0 +1,370 @@
+<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="competitorName" class="smart-query-form-item">
+                  <DictSelect
+                    key-code="BLINK_ENTERPRISE_QUALIFICATION"
+                    v-model:value="formModel.formerName"
+                    placeholder="请选择资质名称"
+                    width="100%"
+                  />
+                </a-form-item>
+              </a-col>
+              <a-col :span="16">
+                <a-form-item label="项目名称" name="formerName" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.formerName" placeholder="请选择项目" @click="showSelectProj" />
+                  <table-info-select ref="ProjectSelectRef" @selectedRowList="handleSelect" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+            <a-row :gutter="24" class="smart-query-form-row">
+              <a-col :span="8">
+                <a-form-item label="标段名称" name="attribute" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.registeredCapital" placeholder="请输入标段名称" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="标段日期" name="registeredTime" class="smart-query-form-item">
+                  <a-date-picker v-model:value="formModel.registeredTime" placeholder="标段日期" value-format="YYYY-MM-DD" style="width: 100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="是否中标" name="registeredCapital" class="smart-query-form-item">
+                  <DictSelect
+                    key-code="BLINK_CUSTOMER_ATTRIBUTE"
+                    v-model:value="formModel.attribute"
+                    placeholder="请选择是否中标"
+                    width="100%"
+                    @change="resetFromDate"
+                  />
+                </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="registeredAddress" class="smart-query-form-item">
+                  <a-input-number v-model:value="formModel.address" placeholder="请输入注册详细地址" addon-after="元" />
+                </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="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 v-if="!query.id">
+          <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="handleAdd">添加</a-button>
+            </div>
+          </div>
+          <div class="steps-content">
+            <a-form-item name="children" class="smart-query-form-item">
+              <div class="steps-content">
+                <!-- <a-button type="primary" @click="handleAdd" style="margin-bottom: 8px">分配任务</a-button> -->
+                <a-table :pagination="false" :columns="columns" :data-source="scheduleList" bordered>
+                  <template v-for="col in ['qualificationName', 'remark']" #[col]="{ index }" :key="col">
+                    <div>
+                      <template v-if="col === 'qualificationName'">
+                        <DictSelect
+                          key-code="BLINK_ENTERPRISE_QUALIFICATION"
+                          v-model:value="scheduleList[index].qualificationName"
+                          placeholder="请选择资质名称"
+                          width="100%"
+                        />
+                      </template>
+                      <template v-if="col === 'remark'">
+                        <a-input v-model:value="scheduleList[index][col]" />
+                      </template>
+                    </div>
+                  </template>
+                  <template #operation="{ index }">
+                    <div class="editable-row-operations">
+                      <a @click="onDelete(index)">删除</a>
+                    </div>
+                  </template>
+                </a-table>
+              </div>
+            </a-form-item>
+          </div>
+        </div>
+      </div>
+    </a-form>
+  </a-card>
+</template>
+<script setup>
+  import { onMounted, reactive, ref, useAttrs, watch, computed } from 'vue';
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { useRouter } from 'vue-router';
+  import { 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 { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+  import { fetchCreateCompetitor, fetchUpdateCompetitor, fetchUpdateCompetitorDate } from '/@/api/support/supportManageApi/index.js';
+  import tableInfoSelect from '/@/views/support-manage/components/TableInfoSelect.vue';
+
+  const stepFormRef = ref(null);
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+
+  onMounted(() => {
+    if (query.id) {
+      updateOpponentInfo(query.id);
+    }
+  });
+
+  function updateOpponentInfo(param) {
+    fetchUpdateCompetitor(param).then((res) => {
+      Object.assign(formModel, res.data);
+      formModel.attribute = res.data.attribute[0].valueName;
+      defaultFileList.value = res.data.attachment;
+    });
+  }
+
+  // --------------------- 数据 ---------------------
+  const formModel = reactive({
+    competitorName: undefined, // 竞争对手名称
+  });
+
+  // --------------------- 校验规则 ---------------------
+  const formRules = {
+    // customerName: [{ required: true, message: '请输入客户名称', trigger: 'change' }],
+    // customerManagerMobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号码', trigger: 'blur' }],
+  };
+
+  const labelCol = { style: { width: '125px', height: '40px' } };
+
+  // ----------------------- 上传附件 ----------------------------
+
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    console.log('fileList', fileList);
+    defaultFileList.value = fileList;
+    formModel.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- 步骤相关 ---------------------------
+
+  const submit = () => {
+    for (let i = 0; i < scheduleList.value.length; i++) {
+      formModel.qualificationList = scheduleList.value;
+    }
+    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 = () => {
+    fetchCreateCompetitor(formModel).then((res) => {
+      message.success('提交成功');
+      router.push({ path: '/support-manage/competition/opponent' });
+    });
+  };
+  //---------------------------Children表单列定义------------------------------
+  const scheduleList = ref([]);
+
+  const columns = ref([
+    {
+      title: '序号',
+      dataIndex: 'seq',
+      align: 'center',
+      customRender: ({ index }) => index + 1,
+    },
+    {
+      title: '竞争对手名称',
+      dataIndex: 'qualificationName',
+      align: 'center',
+      slots: {
+        customRender: 'qualificationName',
+      },
+    },
+    {
+      title: '报价轮次',
+      dataIndex: 'remark',
+      align: 'center',
+      slots: {
+        customRender: 'remark',
+      },
+    },
+    {
+      title: '报价金额(元)',
+      dataIndex: 'remark',
+      align: 'center',
+      slots: {
+        customRender: 'remark',
+      },
+    },
+    {
+      title: '是否中标',
+      dataIndex: 'remark',
+      align: 'center',
+      slots: {
+        customRender: 'remark',
+      },
+    },
+    {
+      title: '报价日期',
+      dataIndex: 'remark',
+      align: 'center',
+      slots: {
+        customRender: 'remark',
+      },
+    },
+    {
+      title: '操作人',
+      dataIndex: 'remark',
+      align: 'center',
+      slots: {
+        customRender: 'remark',
+      },
+    },
+    {
+      title: '操作',
+      dataIndex: 'operation',
+      align: 'center',
+      slots: {
+        customRender: 'operation',
+      },
+    },
+  ]);
+
+  //---------------------------Children表单改变事件------------------------------
+
+  const handleAdd = () => {
+    const newData = {
+      qualificationName: '',
+      remark: '',
+    };
+    scheduleList.value.push(newData);
+  };
+  const onDelete = (index) => {
+    scheduleList.value = [...scheduleList.value.slice(0, index), ...scheduleList.value.slice(index + 1)];
+  };
+  //重置表单数据
+  function resetFromDate() {}
+  //------------------------------表格数据选择器-------------------------------------
+
+  const ProjectSelectRef = ref(null);
+
+  //表格选择器开窗
+  function showSelectProj() {
+    ProjectSelectRef.value.showModal();
+  }
+
+  //表格选择器数据回显
+  function handleSelect(data) {
+    console.log('data', data);
+  }
+</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>

+ 190 - 0
src/views/support-manage/competition/quotation/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <div>
+          <a-button type="primary" @click="handleOpponentRegistration">
+            <span>项目报价登记</span>
+          </a-button>
+        </div>
+      </template>
+    </bs-table>
+    <QuotationDetail ref="quotationDetailRef"></QuotationDetail>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+  import QuotationDetail from '/@/views/support-manage/competition/quotation/quotation-detail/index.vue';
+
+  const quotationDetailRef = ref();
+  const router = useRouter();
+
+  onMounted(() => {
+    refreshTable();
+  });
+
+  const { tableOptions, setTablePropsValue, getTablePropsValue, refreshTable } = useBsTable({
+    tableOptions: {
+      url: '/supports/competitor/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+          },
+          {
+            field: 'competitorName',
+            title: '是否参标',
+            align: 'center',
+          },
+          {
+            field: 'competitorId',
+            title: '投标项目名称',
+            align: 'center',
+          },
+          {
+            field: 'formerName',
+            title: '标段名称',
+            align: 'center',
+          },
+          {
+            field: 'address',
+            title: '投标日期',
+            align: 'center',
+          },
+          {
+            // field: 'flatsType',
+            title: '是否中标',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.attribute?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            field: 'registeredCapital',
+            title: '报价(元)',
+            align: 'center',
+          },
+          {
+            field: 'registeredTime',
+            title: '项目报价',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <a onClick={ViewProjectQuotation}>查看项目报价</a>;
+              },
+            },
+          },
+          {
+            field: 'employeeNumber',
+            title: '客户名称',
+            align: 'center',
+          },
+          {
+            field: 'createUserId',
+            title: '添加人',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.createUserId?.actualName}</span>;
+              },
+            },
+          },
+          {
+            field: 'createTime',
+            title: '添加时间',
+            align: 'center',
+          },
+          {
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '查看详情',
+                    code: 'view',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      goDetailPage(row);
+                    },
+                    extraProps: {},
+                  },
+                ],
+              },
+            },
+          },
+        ],
+      },
+      searchConfig: {
+        enabled: true,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入竞争对手名称',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择单位类型',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择资质',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        isFixed: false,
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+        total: 0,
+      },
+    },
+  });
+
+  const goDetailPage = (record) => {
+    console.log(record.id);
+    router.push({
+      path: '/support-manage/competition/opponent/opponent-detail',
+      query: {
+        id: record.id,
+      },
+    });
+  };
+
+  const handleOpponentRegistration = () => {
+    router.push('/support-manage/competition/quotation/create-quotation');
+  };
+
+  const ViewProjectQuotation = () => {
+    console.log('查看项目报价');
+    quotationDetailRef.value.showModal();
+  };
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 108 - 0
src/views/support-manage/competition/quotation/quotation-detail/index.vue

@@ -0,0 +1,108 @@
+
+<template>
+  <a-modal v-model:open="visible" width="80%" title="项目报价详情" @cancel="closeModal" destroyOnClose>
+    <div class="table-demo">
+      <bs-table v-bind="tableOptions"> </bs-table>
+    </div>
+  </a-modal>
+</template>
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { reactive, ref } from 'vue';
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            align: 'center',
+          },
+          {
+            title: '竞争对手名称',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价轮次',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价金额(元)',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '是否中标',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价日期',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作人',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作日期',
+            field: 'clueName',
+            align: 'center',
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        isFixed:false,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, status: selectedStatus.value });
+      },
+    },
+  });
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const visible = ref(false);
+  function showModal(params) {
+    visible.value = true;
+  }
+
+  function closeModal() {
+    visible.value = false;
+  }
+</script>
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 152 - 0
src/views/support-manage/components/TableInfoSelect.vue

@@ -0,0 +1,152 @@
+<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']);
+
+  // 用于回显勾中的行
+  const selectedKeys = ref([]);
+  const bsTableBean = useBsTable({
+    tableOptions: {
+      url: '/supports/competitor/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+          },
+          {
+            field: 'id',
+            title: 'ID',
+            align: 'center',
+          },
+          {
+            field: 'address',
+            title: 'address',
+            align: 'center',
+          },
+          // {
+          //   field: 'dictField',
+          //   title: '字典',
+          //   cellRender: {
+          //     name: 'CellDict',
+          //   },
+          // },
+          // {
+          //   field: 'name',
+          //   title: 'JSX插槽渲染',
+          //   visible: true,
+          //   slots: {
+          //     default({ row, column }) {
+          //       return <span style={{ color: 'red' }}>{row.name}</span>;
+          //     },
+          //   },
+          // },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      searchConfig: {
+        enabled: true,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入竞争对手名称',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择单位类型',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择资质',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enable: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {
+        enable: true,
+      },
+      // 每次查询接口之前,都会调用这个回调函数
+      tableSearchBeforeBiz() {
+        console.log('表格搜索前');
+      },
+      // 表格初始化之前,只加载一次
+      beforeMount() {
+        console.log('表格加载前');
+      },
+      // 表格初始化完成,只加载一次
+      mounted(gridRef) {
+        console.log('表格加载后', gridRef);
+      },
+    },
+  });
+  const bsModalBean = useBsModal({
+    modalOptions: {
+      width: '900px',
+      visible: false,
+      title: '表格选择器开窗',
+      modalExtraProps: {
+        destroyOnClose: true,
+      },
+    },
+  });
+
+  const { setModalPropsValue: setMVal } = bsModalBean;
+
+  const handleConfirm = ({ value, setLoading, close }) => {
+    emit('selectedRowList', value);
+    close();
+    setLoading(false);
+  };
+
+  const showModal = () => {
+    selectedKeys.value = [];
+    setMVal('visible', true);
+  };
+
+  defineExpose({
+    showModal,
+  });
+</script>

+ 165 - 0
src/views/support-manage/mobile/configuration/components/CredentialInfo/index.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #toolbarLeft>
+        <a-space>
+          <a-button type="primary" @click="goCredentialDetail">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>添加资质</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+    <CredentialDetail ref="CredentialDetailRef" @refreshTable="fetchTableData()"></CredentialDetail>
+  </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';
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+
+  import CredentialDetail from '/@/views/support-manage/competition/opponent/opponent-detail/components/CredentialInfo/modules/CredentialDetail.vue';
+  import { fetchCredentialDel } from '/@/api/support/supportManageApi/index.js';
+
+  const CredentialDetailRef = ref();
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/competitor/qualification/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 100,
+            align: 'center',
+          },
+
+          {
+            title: '资质名称',
+            field: 'clueName',
+            align: 'center',
+            width: '500px',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.qualificationName?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '备注',
+            field: 'remark',
+            align: 'center',
+          },
+          {
+            title: '添加人',
+            field: 'provinceCityDistrict',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.createUserId?.actualName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '添加时间',
+            field: 'createTime',
+            align: 'center',
+          },
+          {
+            align: 'center',
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '编辑',
+                    code: 'edit',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {goCredentialDetail(row)},
+                    extraProps: {},
+                  },
+                  {
+                    title: '删除',
+                    code: 'delete',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      gofetchCredentialDel(row);
+                    },
+                    extraProps: {
+                      danger: true,
+                    },
+                  },
+                ],
+              },
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        isFixed: false,
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+
+  //新增资质信息/编辑资质信息
+  function goCredentialDetail(data) {
+    CredentialDetailRef.value.showModal({data,query});
+  }
+
+  //查看项目详情
+  function gofetchCredentialDel(record) {
+    fetchCredentialDel(record.id).then((res) => {
+      fetchTableData();
+    });
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 128 - 0
src/views/support-manage/mobile/configuration/components/CredentialInfo/modules/CredentialDetail.vue

@@ -0,0 +1,128 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @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="qualificationName" class="smart-query-form-item">
+                  <DictSelect
+                    key-code="BLINK_ENTERPRISE_QUALIFICATION"
+                    v-model:value="form.qualificationName"
+                    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>
+          </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 { fetchCredentialAdd } from '/@/api/support/supportManageApi/index.js';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '70px' } };
+  const emits = defineEmits(['refreshTable']);
+  const visible = ref(false);
+  function showModal({ data, query }) {
+    visible.value = true;
+    form.competitorId = query.id;
+
+    if (data.id) {
+      getTaskInfo(data);
+    }
+  }
+  function getTaskInfo(data) {
+    console.log('diaoyong diaoyong');
+    form.qualificationName = data.qualificationName[0].valueName;
+    form.remark = data.remark;
+  }
+  function closeModal() {
+    visible.value = false;
+    resetFormData();
+  }
+
+  // ----------------------- form 表单操作 ---------------------
+  const formRef = ref();
+
+  const formDefault = {
+    qualificationName: undefined, // 资质名称
+    remark: undefined, //备注
+  };
+
+  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 fetchCredentialAdd(form);
+    message.success('提交成功');
+    emits('refreshTable');
+    onClose();
+  };
+
+  function onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 193 - 0
src/views/support-manage/mobile/configuration/components/DateInfo/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #toolbarLeft>
+        <a-space>
+          <a-button type="primary" @click="goDetailPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>上传附件</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+  </div>
+  <fileDetail ref="fileDetailRef" @refreshTable="refreshTable()"></fileDetail>
+  <FilePreviewModal ref="filePreviewModalRef" />
+</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 { message, Modal } from 'ant-design-vue';
+
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+  import { deleteFile, uploadFile } from '/@/api/market-manage/activity-manage';
+
+  import fileDetail from '/@/views/support-manage/competition/opponent/opponent-detail/components/DateInfo/modules/fileDetail.vue';
+  import FilePreviewModal from '/@/components/support/file-preview-modal/index.vue';
+
+  const fileDetailRef = ref();
+  const filePreviewModalRef = ref(null);
+
+  const {
+    refreshTable,
+    fetchTableData,
+    tableOptions,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/biz/file/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+          },
+          {
+            field: 'activityName',
+            title: '附件名称',
+            align: 'center',
+            slots: {
+              default: ({ row }) => {
+                return row.attachment[0].fileName;
+              },
+            },
+          },
+          {
+            field: 'activityCode',
+            title: '附件大小',
+            align: 'center',
+            slots: {
+              default: ({ row }) => {
+                if (row.attachment[0].fileSize / 1024 < 1024) {
+                  return (row.attachment[0].fileSize / 1024).toFixed(2) + 'KB';
+                } else if (row.attachment[0].fileSize / 1024 / 1024 < 1024) {
+                  return (row.attachment[0].fileSize / 1024 / 1024).toFixed(2) + 'MB';
+                } else if (row.attachment[0].fileSize / 1024 / 1024 / 1024 < 1024) {
+                  return (row.attachment[0].fileSize / 1024 / 1024 / 1024).toFixed(2) + 'GB';
+                }
+              },
+            },
+          },
+          {
+            field: 'activityType',
+            title: '操作人',
+            align: 'center',
+            slots: {
+              default: ({ row }) => {
+                return row.attachment[0].creatorName;
+              },
+            },
+          },
+          {
+            field: 'address',
+            title: '操作时间',
+            align: 'center',
+            slots: {
+              default: ({ row }) => {
+                return row.attachment[0].createTime;
+              },
+            },
+          },
+          {
+            // fixed: 'right',
+            align: 'center',
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '预览',
+                    code: 'view',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      filePreviewModalRef.value.showPreview(row.attachment[0]);
+                    },
+                    extraProps: {},
+                  },
+                  {
+                    title: '下载',
+                    code: 'edit',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      fileApi.downLoadFile(row.attachment[0].fileKey);
+                    },
+                    extraProps: {},
+                  },
+                  {
+                    title: '删除',
+                    code: 'delete',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      Modal.confirm({
+                        title: '提示',
+                        content: `确定要删除${row.attachment[0].fileName}吗?`,
+                        onOk: () => {
+                          deleteFile(row.id).then((res) => {
+                            if (res.ok) {
+                              message.success('删除成功');
+                              fetchTableData();
+                            }
+                          });
+                        },
+                      });
+                    },
+                    extraProps: {
+                      danger: true,
+                    },
+                  },
+                ],
+              },
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        isFixed: false,
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+  function goDetailPage() {
+    fileDetailRef.value.showModal(query.id);
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>

+ 146 - 0
src/views/support-manage/mobile/configuration/components/DateInfo/modules/fileDetail.vue

@@ -0,0 +1,146 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @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="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="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 { clientManageApi } from '/@/api/market-manage/index.js';
+  import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '70px' } };
+  const emits = defineEmits(['refreshTable']);
+  const visible = ref(false);
+  const UploadRef = ref();
+  function showModal(params) {
+    visible.value = true;
+    form.bizId = 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);
+  }
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    form.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- 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 onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 158 - 0
src/views/support-manage/mobile/configuration/components/PerformanceCase/components/create-classification/index.vue

@@ -0,0 +1,158 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @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="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="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"
+                  />
+                  <div><span style="color: gray">支持:png、jpg、大小不超过1M</span></div>
+                </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-input 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="remark" class="smart-query-form-item">
+                  <a-radio-group v-model:value="form.select">
+                    <a-radio :value="1">A</a-radio>
+                    <a-radio :value="2">B</a-radio>
+                  </a-radio-group>
+                </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 { clientManageApi } from '/@/api/market-manage/index.js';
+  import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '70px' } };
+  const emits = defineEmits(['refreshTable']);
+  const visible = ref(false);
+  const UploadRef = ref();
+  function showModal(params) {
+    visible.value = true;
+    form.bizId = 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);
+  }
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    form.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- 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 onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 130 - 0
src/views/support-manage/mobile/configuration/components/PerformanceCase/components/performance-manage/index.vue

@@ -0,0 +1,130 @@
+<!--
+  * 弹窗表单
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" width="80%" title="项目报价详情" @cancel="closeModal" destroyOnClose>
+    <div class="table-demo">
+      <bs-table v-bind="tableOptions"> </bs-table>
+    </div>
+  </a-modal>
+</template>
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { reactive, ref } from 'vue';
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            align: 'center',
+          },
+          {
+            title: '竞争对手名称',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价轮次',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价金额(元)',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '是否中标',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价日期',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作人',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作日期',
+            field: 'clueName',
+            align: 'center',
+          },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      pagerConfig: {
+        enabled: true,
+        isFixed:false,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, status: selectedStatus.value });
+      },
+    },
+  });
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const visible = ref(false);
+  function showModal(params) {
+    visible.value = true;
+  }
+
+  function closeModal() {
+    visible.value = false;
+  }
+</script>
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 240 - 0
src/views/support-manage/mobile/configuration/components/PerformanceCase/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #searchRight>
+        <a-space>
+          <a-button type="primary" @click="ToCreateClassification">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>创建分类</span>
+          </a-button>
+        </a-space>
+      </template>
+    </bs-table>
+    <CreateClassification ref="createClassificationRef"></CreateClassification>
+    <PerformanceManage ref="performanceManageRef"></PerformanceManage>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, h, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+  import { message, Modal } from 'ant-design-vue';
+
+  import CreateClassification from './components/create-classification/index.vue';
+  import PerformanceManage from './components/performance-manage/index.vue'
+
+  const createClassificationRef = ref();
+  const performanceManageRef = ref();
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 100,
+            align: 'center',
+          },
+          {
+            title: '分类名称',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '展示图',
+            // field: 'projectType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <a-button
+                    type='link'
+                    size='small'
+                    disabled={false}
+                    onClick={() => {
+                      goProjectQuotation();
+                    }}
+                  >
+                    查看项目报价
+                  </a-button>
+                );
+              },
+            },
+          },
+          {
+            title: '权重',
+            field: 'provinceCityDistrict',
+            align: 'center',
+          },
+          {
+            title: '状态',
+            field: 'landlordUnitId',
+            align: 'center',
+          },
+          {
+            title: '关联项目数量',
+            field: 'address',
+            align: 'center',
+          },
+          {
+            title: '配置人',
+            // field: 'tendersType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.projectType?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '产品类型',
+            // field: 'tendersType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.engineeringAttribute?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '创建时间',
+            field: 'planStartDate',
+            align: 'center',
+          },
+          {
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '编辑',
+                    code: 'edit',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                       createClassificationRef.value.showModal(row);
+                    },
+                    extraProps: {},
+                  },
+                  {
+                    title: '业绩管理',
+                    code: 'view',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      performanceManageRef.value.showModal();
+                    },
+                    extraProps: {},
+                  },
+                  {
+                    title: '删除',
+                    code: 'delete',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      Modal.confirm({
+                        title: '提示',
+                        content: `确定要移除该信息么吗?删除后将不再列表和小程序展示`,
+                        onOk: () => {
+                          // deleteFile(row.id).then((res) => {
+                          //   if (res.ok) {
+                          //     message.success('删除成功');
+                          //     fetchTableData();
+                          //   }
+                          // });
+                        },
+                      });
+                    },
+                  },
+                ],
+              },
+            },
+          },
+        ],
+      },
+      searchConfig: {
+        enabled: true,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入分类名称',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择状态',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        isFixed: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {
+        enable: false,
+      },
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+
+  //创建分类
+  function ToCreateClassification() {
+    createClassificationRef.value.showModal();
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 174 - 0
src/views/support-manage/mobile/configuration/components/ProjectInfo/index.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions">
+      <template #toolbarTop>
+        <a-space>
+          <a-button type="primary" @click="goHistroyProjectPage">
+            <template #icon>
+              <PlusOutlined />
+            </template>
+            <span>添加历史项目</span>
+          </a-button>
+        </a-space>
+      </template>
+      <template #toolbarLeft>
+        <a-space>
+          <span>润扬项目概算金额(元):1,348,223.89</span>
+        </a-space>
+      </template>
+    </bs-table>
+    <HistoryProjDetail ref="HistoryProjDetailRef" @refreshTable="refreshTable()"></HistoryProjDetail>
+    <ProjectQuotation ref="ProjectQuotationRef" @refreshTable="refreshTable()"></ProjectQuotation>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, h, ref, watch } from 'vue';
+  import { useRouter } from 'vue-router';
+
+  import HistoryProjDetail from '/@/views/support-manage/competition/opponent/opponent-detail/components/ProjectInfo/modules/HistoryProjDetail.vue';
+  import ProjectQuotation from '/@/views/support-manage/competition/opponent/opponent-detail/components/ProjectInfo/modules/ProjectQuotation.vue';
+
+  const HistoryProjDetailRef = ref();
+  const ProjectQuotationRef = ref();
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 100,
+            align: 'center',
+          },
+          {
+            title: '项目名称',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '项目报价',
+            // field: 'projectType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return (
+                  <a-button
+                    type='link'
+                    size='small'
+                    disabled={false}
+                    onClick={() => {
+                      goProjectQuotation();
+                    }}
+                  >
+                    查看项目报价
+                  </a-button>
+                );
+              },
+            },
+          },
+          {
+            title: '合同金额(元)',
+            field: 'provinceCityDistrict',
+            align: 'center',
+          },
+          {
+            title: '补充协议金额(元)',
+            field: 'landlordUnitId',
+            align: 'center',
+          },
+          {
+            title: '合同总金额(万)',
+            field: 'address',
+            align: 'center',
+          },
+          {
+            title: '项目类型',
+            // field: 'tendersType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.projectType?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '产品类型',
+            // field: 'tendersType',
+            align: 'center',
+            slots: {
+              default({ row, column }) {
+                return <span>{row?.engineeringAttribute?.[0]?.valueName || ''}</span>;
+              },
+            },
+          },
+          {
+            title: '项目来源',
+            field: 'bidOpenDate',
+            align: 'center',
+          },
+          {
+            title: '归属营销人员',
+            field: 'bidOpenDate',
+            align: 'center',
+          },
+          {
+            title: '创建时间',
+            field: 'planStartDate',
+            align: 'center',
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        isFixed: true,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams });
+      },
+    },
+  });
+
+  onMounted(() => {
+    console.log('表格已加载');
+  });
+
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+
+  //新增历史项目信息
+  function goHistroyProjectPage() {
+    HistoryProjDetailRef.value.showModal(query.id);
+  }
+  //查看项目报价
+  function goProjectQuotation() {
+    ProjectQuotationRef.value.showModal();
+  }
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 146 - 0
src/views/support-manage/mobile/configuration/components/ProjectInfo/modules/HistoryProjDetail.vue

@@ -0,0 +1,146 @@
+<!--
+  * 部门表单 弹窗
+  *
+  * @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="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="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 { clientManageApi } from '/@/api/market-manage/index.js';
+  import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const labelCol = { style: { width: '70px' } };
+  const emits = defineEmits(['refreshTable']);
+  const visible = ref(false);
+  const UploadRef = ref();
+  function showModal(params) {
+    visible.value = true;
+    form.bizId = 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);
+  }
+
+  // ----------------------- 上传附件 ----------------------------
+  // 已上传的附件列表
+  const defaultFileList = ref([]);
+  function changeAttachment(fileList) {
+    defaultFileList.value = fileList;
+    form.attachment = _.isEmpty(fileList) ? [] : fileList;
+  }
+
+  // ----------------------- 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 onClose() {
+    Object.assign(form, formDefault);
+    visible.value = false;
+  }
+</script>

+ 152 - 0
src/views/support-manage/mobile/configuration/components/ProjectInfo/modules/ProjectQuotation.vue

@@ -0,0 +1,152 @@
+<!--
+  * 弹窗表单
+  *
+  * @Author:    BoundLink
+  * @Date:      2022-08-08 20:46:18
+-->
+<template>
+  <a-modal v-model:open="visible" width="80%" title="项目报价详情" @cancel="closeModal" destroyOnClose>
+    <div class="table-demo">
+      <bs-table v-bind="tableOptions"> </bs-table>
+    </div>
+  </a-modal>
+</template>
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { reactive, ref } from 'vue';
+
+  const {
+    tableOptions,
+    fetchTableData,
+    setTablePropsValue: setValue,
+    getTablePropsValue: getValue,
+  } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            align: 'center',
+          },
+          {
+            title: '竞争对手名称',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价轮次',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价金额(元)',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '是否中标',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '报价日期',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作人',
+            field: 'clueName',
+            align: 'center',
+          },
+          {
+            title: '操作日期',
+            field: 'clueName',
+            align: 'center',
+          },
+          //   {
+          //     title: '报价轮次',
+          //     // field: 'projectType',
+          //     align: 'center',
+
+          //     slots: {
+          //       default({ row, column }) {
+          //         return (
+          //           <a-button
+          //             type='link'
+          //             size='small'
+          //             disabled={false}
+          //             onClick={() => {
+          //               goProjectQuotation();
+          //             }}
+          //           >
+          //             查看项目报价
+          //           </a-button>
+          //         );
+          //       },
+          //     },
+          //   },
+        ],
+        data: [
+          {
+            id: 1,
+            name: '测试数据',
+            dictField: [
+              {
+                dictKeyId: '',
+                dictValueId: '',
+                remark: '',
+                sort: 3,
+                status: 1,
+                valueCode: '03',
+                valueName: '微信公众号',
+              },
+            ],
+          },
+        ], // 模拟数据源
+      },
+      pagerConfig: {
+        enabled: true,
+        isFixed:false,
+        pageSize: 10,
+        pageNum: 1,
+      },
+      toolbarConfig: {},
+      tableSearchBeforeBiz() {
+        const searchParams = getValue('searchConfig.data');
+        setValue('searchConfig.data', { ...searchParams, status: selectedStatus.value });
+      },
+    },
+  });
+
+  // ----------------------- 对外暴漏 ---------------------
+  defineExpose({
+    showModal,
+  });
+
+  // ----------------------- modal 的显示与隐藏 ---------------------
+
+  const visible = ref(false);
+  function showModal(params) {
+    visible.value = true;
+  }
+
+  function closeModal() {
+    visible.value = false;
+  }
+</script>
+<style scoped lang="scss">
+  .table-demo {
+    background-color: #fff;
+    height: 100%;
+
+    .tabs {
+      padding: 0 24px;
+
+      :deep(.anticon) {
+        margin: 0;
+      }
+    }
+  }
+</style>

+ 101 - 0
src/views/support-manage/mobile/configuration/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <a-layout class="layout">
+    <!-- 左侧菜单 -->
+    <a-layout-sider width="190" class="sider">
+      <div class="sidebar-title">
+        <h3>支持中心</h3>
+      </div>
+
+      <a-menu :selected-keys="[activeMenu]" @click="handleMenuClick" mode="inline" theme="light">
+        <a-menu-item v-for="m in menuList" :key="m.key">
+          {{ m.label }}
+        </a-menu-item>
+      </a-menu>
+    </a-layout-sider>
+
+    <!-- 右侧主体 -->
+    <a-layout-content class="content-wrap">
+      <a-card :tab-list="currentTabList" :active-tab-key="activeTab" @tabChange="handleTabChange" :bordered="false">
+        <component :is="currentComponent" />
+      </a-card>
+    </a-layout-content>
+  </a-layout>
+</template>
+
+<script setup>
+  import { ref, computed, defineAsyncComponent } from 'vue';
+
+  /* ---------- 左侧菜单 ---------- */
+  const menuList = [
+    { key: 'intro', label: '业务服务' },
+    { key: 'cases', label: '荣誉' },
+    { key: 'certs', label: '客户评价' },
+    { key: 'speech', label: '社会公益' },
+  ];
+  const activeMenu = ref('intro');
+
+  /* ---------- 二级 tab ---------- */
+  const tabMap = {
+    intro: [
+      { key: 'introConfig', tab: '企业简介配置', component: defineAsyncComponent(() => import('./components/CredentialInfo/index.vue')) },
+      { key: 'honor', tab: '业绩案例', component: defineAsyncComponent(() => import('./components/PerformanceCase/index.vue')) },
+      { key: 'comments', tab: '资质证书', component: defineAsyncComponent(() => import('./components/DateInfo/index.vue')) },
+      { key: 'welfare', tab: '营销话术', component: defineAsyncComponent(() => import('./components/DateInfo/index.vue')) },
+    ],
+    cases: [{ key: 'caseList', tab: '公司荣誉', component: defineAsyncComponent(() => import('./components/PerformanceCase/index.vue')) }],
+    certs: [
+      { key: 'certList', tab: '客户表扬信', component: defineAsyncComponent(() => import('./components/CredentialInfo/index.vue')) },
+      { key: 'user', tab: '用户满意度调查', component: defineAsyncComponent(() => import('./components/CredentialInfo/index.vue')) },
+    ],
+    speech: [{ key: 'speechLib', tab: '社会工艺', component: defineAsyncComponent(() => import('./components/DateInfo/index.vue')) }],
+  };
+
+  const activeTab = ref('honor');
+
+  /* ---------- 计算属性 ---------- */
+  const currentTabList = computed(() => tabMap[activeMenu.value] || []);
+  const currentComponent = computed(() => {
+    const tab = currentTabList.value.find((t) => t.key === activeTab.value);
+    return tab?.component || null;
+  });
+
+  /* ---------- 事件 ---------- */
+  const handleMenuClick = ({ key }) => {
+    activeMenu.value = key;
+    activeTab.value = tabMap[key][0]?.key || '';
+  };
+
+  const handleTabChange = (key) => {
+    activeTab.value = key;
+  };
+</script>
+
+<style scoped>
+  ::v-deep .ant-layout-sider-children {
+    height: 100%;
+    background: #fff;
+  }
+  .layout {
+    height: 100vh;
+    background: #fff;
+  }
+  .sider {
+    height: 100%;
+    border-right: 1px solid #f0f0f0;
+  }
+  .content-wrap {
+    padding: 16px 24px;
+    overflow: auto;
+  }
+  .sidebar-title {
+    text-align: center;
+    padding: 16px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+  .sidebar-title h3 {
+    margin: 0;
+    font-size: 18px;
+    font-weight: 600;
+    color: #262626;
+  }
+</style>

+ 161 - 0
src/views/support-manage/risk/serviceRiskProvider/index.vue

@@ -0,0 +1,161 @@
+<template>
+  <div class="table-demo">
+    <bs-table v-bind="tableOptions" />
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import BsTable, { useBsTable } from '/@/components/BsUi/Table/index.js';
+  import { onMounted, ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
+
+  onMounted(() => {
+    refreshTable();
+  });
+  const { tableOptions, setTablePropsValue, getTablePropsValue, refreshTable } = useBsTable({
+    tableOptions: {
+      url: '/supports/project/queryPage',
+      gridOptions: {
+        // data:[],
+        loading: false,
+        columns: [
+          {
+            type: 'seq',
+            width: 80,
+          },
+          {
+            field: '',
+            title: '风险项',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '服务商名称',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '服务商ID',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '服务商地址',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '归属区域',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '服务商类型',
+            width: 150,
+            slots: {
+              default({ row, column }) {
+                // return <span>{row?.projectType?.[0].valueName}</span>;
+              },
+            },
+          },
+          {
+            field: '',
+            title: '归属销售工程师',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '操作人',
+            width: 150,
+          },
+          {
+            field: '',
+            title: '操作时间',
+            width: 150,
+          },
+          {
+            field: 'opt',
+            title: '操作',
+            width: '120px',
+            fixed: 'right',
+            align: 'center',
+          },
+          {
+            // fixed: 'right',
+            cellRender: {
+              name: 'CellOption',
+              extraProps: {
+                buttons: [
+                  {
+                    title: '查看详情',
+                    code: 'view',
+                    display: ({ row }) => {
+                      return DISPLAY_STATE.VISIBLE;
+                    },
+                    disabled({ row }) {
+                      return false;
+                    },
+                    onClick({ row }) {
+                      goDetailPage(row);
+                    },
+                    extraProps: {},
+                  },
+                ],
+              },
+            },
+          },
+        ],
+      },
+      searchConfig: {
+        enabled: true,
+        fieldSpan: 4,
+        fields: [
+          {
+            field: '',
+            component: 'a-input',
+            componentProps: {
+              placeholder: '请输入服务商名称',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择地址',
+            },
+          },
+          {
+            field: '',
+            component: 'a-select',
+            componentProps: {
+              placeholder: '请选择服务商类型',
+            },
+          },
+        ],
+      },
+      pagerConfig: {
+        enabled: true,
+        pageSize: 10,
+        pageNum: 1,
+        total: 0,
+      },
+    },
+  });
+
+  const router = useRouter();
+  const goDetailPage = (record) => {
+    console.log(record.id);
+    router.push({
+      path: '/support-manage/bidding/performance/performance-detail/',
+      query: {
+        id: record.id,
+      },
+    });
+  };
+</script>
+
+<style scoped lang="scss">
+  .table-demo {
+  }
+</style>