Browse Source

fix:项目管理-项目列表

liqh 5 months ago
parent
commit
2c0833e4ae

+ 17 - 0
src/components/common/common-table/components/table.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="common-table-item">
+    <!-- 自定义顶部插槽内容 -->
+    <div v-if="tableConfig.renderTitleSlot" class="smart-table-title-slot" style="margin-bottom: 6px;background: white;">
+      <render-title-slot />
+    </div>
     <!-- 查询表单 -->
     <template v-if="props.search.length > 0">
       <Search :searchConfig="props.search" v-model:queryForm="queryForm" @search="onSearch" @reload="onReload" />
@@ -117,6 +121,9 @@
     renderTopSlot: {
       type: Function,
     },
+    renderTitleSlot:{
+      type:Function,
+    }
   });
 
   // 查询表单
@@ -141,10 +148,20 @@
     scroll: props.scroll,
     ...props.tableAttrs,
     renderTopSlot: props.renderTopSlot, // 添加 renderTopSlot 属性
+    renderTitleSlot:props.renderTitleSlot,//添加renderTitleSlot属性
   });
 
   const topInfo = ref({});
 
+    // 动态渲染顶部插槽内容
+  const renderTitleSlot = () => {
+    if (props.renderTitleSlot) {
+      // 将 queryForm 和其他参数传递给 renderTitleSlot 函数
+      return h(props.renderTitleSlot, topInfo.value);
+    }
+    return null;
+  };
+
   // 动态渲染顶部插槽内容
   const renderTopSlot = () => {
     if (props.renderTopSlot) {

+ 128 - 0
src/views/efficiency/efficiency-manage/efficiency-list/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <CommonTable ref="commonTableRef" :tableOptions="tableConfig"></CommonTable>
+</template>
+
+<script setup lang="jsx">
+  import { ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { smartSentry } from '/@/lib/smart-sentry';
+  import { fileApi } from '/@/api/support/file-api';
+  import useDict from '/@/utils/dict-util';
+  await useDict.init(['MATE_DELIVERY_STATUS']);
+
+  const commonTableRef = ref(null);
+  const tableConfig = ref({
+    url: '/mate/staff/efficiency/queryPage',
+    // 设置网络请求方式为GET
+    requestMethod: 'GET',
+    search: [
+      {
+        label: '归属年月',
+        field: 'yearMonth',
+        type: 'dateMonth',
+        attrs: {},
+      },
+    ],
+    columns: [
+      {
+        title: '归属年月',
+        dataIndex: 'yearMonth',
+        align: 'center',
+        width: '10%',
+      },
+      {
+        title: '文件名称',
+        dataIndex: 'attachment',
+        align: 'center',
+        width: '20%',
+        customRender: ({ text, record, index, column }) => {
+          return <div class='smart-table-operate'>{record.attachment[0].fileName}</div>;
+        },
+      },
+
+      {
+        title: '月度总预计人/天',
+        dataIndex: 'planDays',
+        align: 'center',
+        width: '10%',
+      },
+
+      {
+        title: '月度总实际人/天',
+        dataIndex: 'actualDays',
+        align: 'center',
+        width: 100,
+      },
+      {
+        title: '月度总和核算人/天',
+        dataIndex: 'settlementDays',
+        align: 'center',
+        width: 100,
+      },
+      {
+        title: '月度总上报人/天',
+        dataIndex: 'reportDays',
+        align: 'center',
+        width: 100,
+      },
+      {
+        title: '创建时间',
+        dataIndex: 'createTime',
+        align: 'center',
+        width: 100,
+      },
+      {
+        title: '操作',
+        dataIndex: 'action',
+        fixed: 'right',
+        align: 'center',
+        width: 100,
+        customRender: ({ text, record, index, column }) => {
+          return (
+            <div class='smart-table-operate'>
+              <a-button type='link' onClick={() => goDetailPage(record)}>
+                查看
+              </a-button>
+              <a-button type='link' onClick={() => download(record)}>
+                下载
+              </a-button>
+            </div>
+          );
+        },
+      },
+    ],
+    tableAttrs: {},
+    showIndexColumn: true,
+    beforeFetch({ params }) {
+      return {
+        ...params,
+      };
+    },
+    // afterFetch({ data }) {},
+  });
+  function goDetailPage(record) {
+    console.log('00000', record);
+    router.push({
+      path: '/project/efficiency-manage/single-efficiency',
+      query: {
+        id: record.id,
+        belongYear: record.belongYear,
+        belongMonth: record.belongMonth,
+      },
+    });
+  }
+
+  const router = useRouter();
+
+  // 下载文件
+  async function download(file) {
+    console.log('file', file);
+    try {
+      await fileApi.downLoadFile(file.attachment[0].fileKey);
+    } catch (e) {
+      smartSentry.captureError(e);
+    }
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 171 - 0
src/views/project/cost-manage/loan-manage/index.vue

@@ -0,0 +1,171 @@
+<template>
+  <CommonTable ref="commonTableRef" :tableOptions="tableConfig"> </CommonTable>
+</template>
+
+<script setup lang="jsx">
+  import { ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import useDict from '/@/utils/dict-util';
+  await useDict.init(['MATE_DELIVERY_STATUS']);
+
+  const commonTableRef = ref(null);
+  const tableConfig = ref({
+    url: '/supports/loan/apply/queryPage',
+    // 设置网络请求方式为GET
+    requestMethod: 'GET',
+    tableAttrs: {},
+    showIndexColumn: true,
+    search: [
+      {
+        label: '项目名称',
+        field: 'deliveryName',
+        type: 'input',
+        attrs: {},
+      },
+      {
+        label: '客户名称',
+        field: 'customerName',
+        type: 'input',
+        attrs: {},
+      },
+      {
+        label: '项目状态',
+        field: 'deliveryStatus',
+        type: 'select',
+        options: useDict.type.MATE_DELIVERY_STATUS,
+        attrs: {},
+      },
+    ],
+    columns: [
+      {
+        title: '借款人',
+        dataIndex: 'applicant',
+        align: 'center',
+        width: '150px',
+        ellipsis: true,
+      },
+      {
+        title: '借款金额',
+        dataIndex: 'loanAmount',
+        align: 'center',
+        width: '100px',
+        ellipsis: true,
+      },
+
+      {
+        title: '借款支付日期',
+        dataIndex: 'loanPurpose',
+        align: 'center',
+        width: '100px',
+        ellipsis: true,
+      },
+      {
+        title: '借款用途',
+        dataIndex: 'loanPurpose',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '借款归属项目',
+        dataIndex: 'belongProjectName',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '累计借款金额',
+        dataIndex: 'accumulateAmount',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '当前在手借款金额',
+        dataIndex: 'handLoanAmount',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '发起人',
+        dataIndex: 'createBy',
+        align: 'center',
+        width: '60px',
+      },
+      {
+        title: '发起日期',
+        dataIndex: 'createDate',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '操作',
+        dataIndex: 'createDate',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+        customRender: ({ text, record, index, column }) => {
+          return (
+            <div class='smart-table-operate'>
+              <a-button type='link' onClick={() => goDetailPage(record)}>
+                查看(借款单)
+              </a-button>
+              <span>|</span>
+              <a-button type='link' onClick={() => download(record)}>
+                发起报销
+              </a-button>
+            </div>
+          );
+        },
+      },
+    ],
+
+    beforeFetch({ params }) {
+      return {
+        ...params,
+      };
+    },
+    // afterFetch({ data }) {},
+  });
+
+  const router = useRouter();
+
+  function goDetailPage(record) {
+    sessionStorage.setItem('SessionStorageProjectId', record.id);
+    router.push({
+      path: '/project/project-manage/single-project',
+    });
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-progress-inner) {
+    width: 27px !important;
+    height: 27px !important;
+    font-size: 11px !important;
+  }
+  :deep(.ant-table-column[data-key='index']) {
+    width: 60px !important;
+  }
+
+  .smart-table-operate {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 8px;
+}
+
+.smart-table-operate a {
+  color: #1890ff;
+  text-decoration: none;
+  font-size: 14px;
+  cursor: pointer;
+}
+
+.smart-table-operate span {
+  color: #ccc;
+  margin: 0 5px;
+}
+</style>

+ 135 - 0
src/views/project/cost-manage/reimburse-manage/index.vue

@@ -0,0 +1,135 @@
+<template>
+  <CommonTable ref="commonTableRef" :tableOptions="tableConfig"> </CommonTable>
+</template>
+
+<script setup lang="jsx">
+  import { ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import useDict from '/@/utils/dict-util';
+  await useDict.init(['MATE_DELIVERY_STATUS']);
+
+  const commonTableRef = ref(null);
+  const tableConfig = ref({
+    url: '/supports/expense/statement/queryPage',
+    // 设置网络请求方式为GET
+    requestMethod: 'GET',
+    tableAttrs: {},
+    showIndexColumn: true,
+    search: [
+      {
+        label: '项目名称',
+        field: 'deliveryName',
+        type: 'input',
+        attrs: {},
+      },
+      {
+        label: '客户名称',
+        field: 'customerName',
+        type: 'input',
+        attrs: {},
+      },
+      {
+        label: '项目状态',
+        field: 'deliveryStatus',
+        type: 'select',
+        options: useDict.type.MATE_DELIVERY_STATUS,
+        attrs: {},
+      },
+    ],
+    columns: [
+     {
+        title: '收款人',
+        dataIndex: 'applicant',
+        align: 'center',
+        width: '150px',
+        ellipsis: true,
+      },
+      {
+        title: '报销总金额',
+        dataIndex: 'expenseAmount',
+        align: 'center',
+        width: '100px',
+        ellipsis: true,
+      },
+     
+      {
+        title: '冲抵借款金额',
+        dataIndex: 'offsetLoanAmount',
+        align: 'center',
+        width: '100px',
+        ellipsis: true,
+      },
+      {
+        title: '本次付款金额',
+        dataIndex: 'payAmount',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '是否付款',
+        dataIndex: 'belongProjectName',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '报销付款日期',
+        dataIndex: 'payDate',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+      },
+      {
+        title: '发起人',
+        dataIndex: 'createBy',
+        align: 'center',
+        width: '60px',
+      },
+      {
+        title: '发起日期',
+        dataIndex: 'createDate',
+        align: 'center',
+        width: '50px',
+        ellipsis: true,
+        customRender: ({ text, record, index, column }) => {
+          return (
+            <div class='smart-table-operate'>
+              <a-button type='link' onClick={() => goDetailPage(record)}>
+                查看(费用报销单)
+              </a-button>
+            </div>
+          );
+        },
+      },
+      
+    ],
+  
+    beforeFetch({ params }) {
+      return {
+        ...params,
+      };
+    },
+    // afterFetch({ data }) {},
+  });
+
+  const router = useRouter();
+
+  function goDetailPage(record) {
+    sessionStorage.setItem('SessionStorageProjectId', record.id);
+    router.push({
+      path: '/project/project-manage/single-project',
+    });
+  }
+</script>
+
+<style lang="less" scoped>
+  :deep(.ant-progress-inner) {
+    width: 27px !important;
+    height: 27px !important;
+    font-size: 11px !important;
+  }
+  :deep(.ant-table-column[data-key='index']) {
+    width: 60px !important;
+  }
+</style>

+ 109 - 77
src/views/project/project-manage/project-create/index.vue

@@ -9,13 +9,12 @@
               <span class="basic-title-text">项目信息</span>
             </div>
             <div class="basic-title-right">
-              <a-button type="primary" @click="reset">重置</a-button>
               <a-button type="primary" @click="submit">提交</a-button>
             </div>
           </div>
           <div class="steps-content">
             <a-row :gutter="16" class="smart-query-form-row">
-              <a-col :span="16">
+              <a-col :span="8">
                 <a-form-item label="项目名称" name="deliveryName" class="smart-query-form-item">
                   <a-input v-model:value="formModel.deliveryName" placeholder="请输入项目名称" />
                 </a-form-item>
@@ -25,68 +24,91 @@
                   <DictSelect key-code="MATE_DELIVERY_TYPE" v-model:value="formModel.deliveryType" placeholder="请选择项目类型" width="100%" />
                 </a-form-item>
               </a-col>
-
+              <a-col :span="8">
+                <a-form-item label="项目类别" name="deliveryType" class="smart-query-form-item">
+                  <DictSelect key-code="MATE_DELIVERY_TYPE" v-model:value="formModel.deliveryType" placeholder="请选择项目类型" width="100%" />
+                </a-form-item>
+              </a-col>
             </a-row>
             <a-row :gutter="16" class="smart-query-form-row">
-              
               <a-col :span="8">
-                <a-form-item label="项目状态" name="deliveryStatus" class="smart-query-form-item">
-                  <DictSelect key-code="MATE_DELIVERY_STATUS" v-model:value="formModel.deliveryStatus" placeholder="请选择项目状态" width="100%" />
+                <a-form-item label="项目状态" name="customerType" class="smart-query-form-item">
+                  <DictSelect key-code="MATE_CUSTOMER_TYPE" v-model:value="formModel.customerType" placeholder="请选择项目状态" width="100%" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="客户名称" name="customerName" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.customerName" placeholder="请输入客户名称" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="服务客户" name="deliveryType" class="smart-query-form-item">
+                  <DictSelect key-code="MATE_DELIVERY_TYPE" v-model:value="formModel.deliveryType" placeholder="请选择项目类型" width="100%" />
                 </a-form-item>
               </a-col>
+            </a-row>
+            <a-row :gutter="16" class="smart-query-form-row">
               <a-col :span="8">
-                <a-form-item label="交付经理" name="deliveryCharge1" class="smart-query-form-item">
+                <a-form-item label="客户经理" name="deliveryCharge1" class="smart-query-form-item">
                   <a-input
                     v-model:value="formModel.deliveryCharge1"
                     placeholder="请选择岗位人员"
-                    @click="showSelectUser('deliveryManager', index)"
-                    readonly
-                  />
-                  <EmployeeTableSelectModal
-                    ref="selectDeliveryManagerRef"
-                    type="radio"
-                    @selectDataList="selectedUser('deliveryManager', index, $event)"
+                    @click="showSelectUser('deliveryManager')"
+                    allowClear
                   />
+                  <EmployeeTableSelectModal ref="selectDeliveryManagerRef" type="radio" @selectDataList="selectedUser('deliveryManager', $event)" />
                 </a-form-item>
               </a-col>
               <a-col :span="8">
-                <a-form-item label="营销经理" name="marketManagerCode" class="smart-query-form-item">
+                <a-form-item label="项目经理" name="marketManagerCode" class="smart-query-form-item">
                   <a-input
                     v-model:value="formModel.marketManagerCode"
                     placeholder="请选择岗位人员"
-                    @click="showSelectMarketManager('marketManager', index)"
-                    readonly
+                    @click="showSelectUser('marketManager')"
+                    allowClear
                   />
-                  <EmployeeTableSelectModal
-                    ref="selectMarketManagerRef"
-                    type="radio"
-                    @selectDataList="selectedMarketManager('marketManager', index, $event)"
+                  <EmployeeTableSelectModal ref="selectMarketManagerRef" type="radio" @selectDataList="selectedUser('marketManager', $event)" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="产品经理" name="productManagerCode" class="smart-query-form-item">
+                  <a-input
+                    v-model:value="formModel.productManagerCode"
+                    placeholder="请选择岗位人员"
+                    @click="showSelectUser('productManager')"
+                    allowClear
                   />
+                  <EmployeeTableSelectModal ref="selectProductManagerRef" @selectDataList="selectedUser('productManager', $event)" />
                 </a-form-item>
               </a-col>
             </a-row>
-          </div>
-        </div>
-        <div>
-          <div class="basic-title">
-            <div class="basic-title-left">
-              <div class="basic-title-line"></div>
-              <span class="basic-title-text">客户信息</span>
-            </div>
-            <div class="basic-title-right">
-              <!-- <a-button type="primary">提交</a-button> -->
-            </div>
-          </div>
-          <div class="steps-content">
             <a-row :gutter="16" class="smart-query-form-row">
               <a-col :span="8">
-                <a-form-item label="客户名称" name="customerName" class="smart-query-form-item">
-                  <a-input v-model:value="formModel.customerName" placeholder="请输入客户名称" />
+                <a-form-item label="前端开发" name="frontendDeveloper" class="smart-query-form-item">
+                  <a-input
+                    v-model:value="formModel.frontendDeveloper"
+                    placeholder="请选择岗位人员"
+                    @click="showSelectUser('frontendDeveloper')"
+                    allowClear
+                  />
+                  <EmployeeTableSelectModal ref="selectFrontendDeveloperRef" @selectDataList="selectedUser('frontendDeveloper', $event)" />
                 </a-form-item>
               </a-col>
               <a-col :span="8">
-                <a-form-item label="客户类型" name="customerType" class="smart-query-form-item">
-                  <DictSelect key-code="MATE_CUSTOMER_TYPE" v-model:value="formModel.customerType" placeholder="请选择项目状态" width="100%" />
+                <a-form-item label="后端开发" name="backendDeveloper" class="smart-query-form-item">
+                  <a-input
+                    v-model:value="formModel.backendDeveloper"
+                    placeholder="请选择岗位人员"
+                    @click="showSelectUser('backendDeveloper')"
+                    allowClear
+                  />
+                  <EmployeeTableSelectModal ref="selectBackendDeveloperRef" @selectDataList="selectedUser('backendDeveloper', $event)" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="8">
+                <a-form-item label="测试" name="tester" class="smart-query-form-item">
+                  <a-input v-model:value="formModel.tester" placeholder="请选择岗位人员" @click="showSelectUser('tester')" allowClear />
+                  <EmployeeTableSelectModal ref="selectTesterRef" @selectDataList="selectedUser('tester', $event)" />
                 </a-form-item>
               </a-col>
             </a-row>
@@ -131,6 +153,7 @@
     </a-form>
   </a-card>
 </template>
+
 <script setup>
   import { onMounted, reactive, ref, useAttrs } from 'vue';
   import { useRouter } from 'vue-router';
@@ -145,22 +168,31 @@
   const stepFormRef = ref(null);
   const router = useRouter();
 
-  // --------------------- 数据 ---------------------
+  // 数据
   const formModel = reactive({
-    deliveryName: undefined, //项目名称
-    deliveryType: undefined, //项目类型
-    deliveryStatus: undefined, //项目状态
-    deliveryCharge: undefined, //项目负责人
-    deliveryCharge1: undefined,
-    marketManager: undefined, //营销经理
+    deliveryName: undefined, // 项目名称
+    deliveryType: undefined, // 项目类型
+    deliveryStatus: undefined, // 项目状态
+    deliveryCharge: undefined, // 项目负责人
+    deliveryCharge1: undefined, // 客户经理显示的字符串
+    deliveryCharge1List: [], // 客户经理选中的用户列表
+    marketManager: undefined, // 营销经理
     marketManagerCode: undefined,
-    customerName: undefined, //客户名称
-    customerType: undefined, //客户类型
-    remark: undefined, //	备注信息
-    attachment: undefined, //附件集合
+    productManager: undefined, // 产品经理
+    productManagerCode: undefined,
+    customerName: undefined, // 客户名称
+    customerType: undefined, // 客户类型
+    remark: undefined, // 备注信息
+    attachment: undefined, // 附件集合
+    frontendDeveloper: undefined, // 前端开发
+    frontendDeveloperList: [],
+    backendDeveloper: undefined, // 后端开发
+    backendDeveloperList: [],
+    tester: undefined, // 测试
+    testerList: [],
   });
 
-  // --------------------- 校验规则 ---------------------
+  // 校验规则
   const formRules = {
     deliveryName: [{ required: true, message: '请输入项目名称', trigger: 'change' }],
     deliveryType: [{ required: true, message: '请选择项目类型', trigger: 'change' }],
@@ -173,7 +205,7 @@
 
   const labelCol = { style: { width: '128px' } };
 
-  // ----------------------- 步骤相关 ---------------------------
+  // 步骤相关
   const query = router.currentRoute.value.query;
   console.log('query', query);
 
@@ -215,8 +247,7 @@
     }
   }
 
-  //-----------------------------提交\重置---------------------------------
-
+  // 提交
   const submit = () => {
     console.log('formModel', formModel);
     stepFormRef.value
@@ -245,43 +276,45 @@
     });
   };
 
+  // 重置
   const reset = () => {
     Object.keys(formModel).forEach((key) => {
       formModel[key] = undefined;
     });
   };
 
-  //---------------------------人员选择------------------------------
-
+  // 人员选择
   const selectDeliveryManagerRef = ref(null);
   const selectMarketManagerRef = ref(null);
-  function showSelectMarketManager(type) {
-    if (type === 'marketManager') {
-      selectMarketManagerRef.value.showModal();
-    }
-  }
+  const selectProductManagerRef = ref(null);
+  const selectFrontendDeveloperRef = ref(null);
+  const selectBackendDeveloperRef = ref(null);
+  const selectTesterRef = ref(null);
 
   function showSelectUser(type) {
-    if (type === 'deliveryManager') {
-      selectDeliveryManagerRef.value.showModal();
-    }
-  }
-  function selectedUser(field, index, list) {
-    if(field === 'deliveryManager'){
-      formModel.deliveryCharge1 = list[0].actualName;
-      formModel.deliveryCharge = list[0].loginName;
-    }
+    const refs = {
+      deliveryManager: selectDeliveryManagerRef,
+      marketManager: selectMarketManagerRef,
+      productManager: selectProductManagerRef,
+      frontendDeveloper: selectFrontendDeveloperRef,
+      backendDeveloper: selectBackendDeveloperRef,
+      tester: selectTesterRef,
+    };
+    refs[type].value.showModal();
   }
-    
 
-  function selectedMarketManager(field, index, list){
-    console.info(1111111111111111111111)
-    formModel.marketManagerCode = list[0].actualName;
-    formModel.marketManager = list[0].loginName;
+  function selectedUser(type, list) {
+    console.info('Selected users for', type, list);
+    const field = `${type}List`;
+    const displayField = type === 'deliveryManager' ? 'deliveryCharge1' : type;
+    formModel[field] = list.map((item) => ({
+      actualName: item.actualName,
+      loginName: item.loginName,
+    }));
+    formModel[displayField] = list.map((item) => item.actualName).join(', ');
   }
 
-  // ----------------------- 上传附件 ----------------------------
-  // 已上传的附件列表
+  // 上传附件
   const defaultFileList = ref([]);
   function changeAttachment(fileList) {
     defaultFileList.value = fileList;
@@ -294,7 +327,6 @@
     display: flex;
     flex-direction: column;
     gap: 20px;
-    // width: 1350px;
     margin: 0 auto;
 
     .basic-title {

+ 177 - 39
src/views/project/project-manage/project-list/index.vue

@@ -1,5 +1,77 @@
 <template>
-  <CommonTable ref="commonTableRef" :tableOptions="tableConfig"> </CommonTable>
+  <div style="background: white; padding: 15px 5px">
+    <div class="title-total">
+      <div style="margin: 10px; display: flex; align-items: center">
+        <div>
+          <a-button type="primary" @click="ToProjectInitiation" style="margin-right: 20px">项目登记</a-button>
+        </div>
+        <div>
+          <span>项目总数:</span>
+          <span>12个</span>
+        </div>
+        <span style="margin: 0 10px"> | </span>
+        <div>
+          <span>进行中项目:</span>
+          <span>12个</span>
+        </div>
+        <span style="margin: 0 10px"> | </span>
+        <div>
+          <span>代开始项目:</span>
+          <span>12个</span>
+        </div>
+        <span style="margin: 0 10px"> | </span>
+        <div>
+          <span>已完成项目:</span>
+          <span>12个</span>
+        </div>
+        <span style="margin: 0 10px"> | </span>
+        <div>
+          <span>暂停项目:</span>
+          <span>12个</span>
+        </div>
+      </div>
+    </div>
+    <div class="project-stats-container" v-if="!showDetails">
+      <div class="project-card">
+        <div class="project-header">
+          <span>外包项目</span>
+          <a href="#" class="view-all" @click.prevent="toggleDetails('outsource')">查看全部项目>></a>
+        </div>
+        <div class="project-content">
+          <div class="project-content-left">
+            <div class="project-stat">项目总数 <span class="project-number">234</span></div>
+            <div class="project-stat">进行中 <span class="project-number">34</span></div>
+            <div class="project-stat">待开始 <span class="project-number">45</span></div>
+            <div class="project-stat">已完成 <span class="project-number">43</span></div>
+            <div class="project-stat">暂停 <span class="project-number">67</span></div>
+          </div>
+          <div class="project-content-right">
+            <div style="width: 300px; height: 120px; background: gray"></div>
+          </div>
+        </div>
+      </div>
+      <div class="project-card">
+        <div class="project-header">
+          <span>交付项目</span>
+          <a href="#" class="view-all" @click.prevent="toggleDetails('delivery')">查看全部项目>></a>
+        </div>
+        <div class="project-content">
+          <div class="project-content-left">
+            <div class="project-stat">项目总数 <span class="project-number">234</span></div>
+            <div class="project-stat">进行中 <span class="project-number">34</span></div>
+            <div class="project-stat">待开始 <span class="project-number">45</span></div>
+            <div class="project-stat">已完成 <span class="project-number">43</span></div>
+            <div class="project-stat">暂停 <span class="project-number">67</span></div>
+          </div>
+          <div class="project-content-right">
+            <div style="width: 300px; height: 120px; background: gray"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <CommonTable v-if="showDetails" ref="commonTableRef" :tableOptions="tableConfig"></CommonTable>
+  <a-button v-if="showDetails" type="primary" @click="toggleDetails" style="margin-top: 10px">收起</a-button>
   <a-float-button
     @click="TohandleClick"
     description="工时提报"
@@ -19,6 +91,8 @@
   await useDict.init(['MATE_DELIVERY_STATUS']);
 
   const commonTableRef = ref(null);
+  const type = ref();
+  const showDetails = ref(false);
   const tableConfig = ref({
     url: '/mate/delivery/queryPage',
     // 设置网络请求方式为GET
@@ -56,7 +130,6 @@
       },
       {
         title: '项目名称',
-        // dataIndex: 'deliveryName',
         align: 'center',
         width: '150px',
         ellipsis: true,
@@ -72,7 +145,6 @@
       },
       {
         title: '项目类型',
-        // dataIndex: 'deliveryStatus',
         align: 'center',
         width: '100px',
         ellipsis: true,
@@ -82,7 +154,6 @@
       },
       {
         title: '项目状态',
-        // dataIndex: 'deliveryStatus',
         align: 'center',
         width: '50px',
         customRender: ({ text, record, index, column }) => {
@@ -164,46 +235,12 @@
         ellipsis: true,
       },
     ],
-    renderTopSlot(data) {
-      return (
-        <div style={{ display: 'flex' }}>
-          <div style={{ display: 'flex', alignItems: 'center' }}>
-            <div>
-              <span>已完成项目:</span>
-              <span style={{ fontWeight: 'bold', color: '#318586' }}>{data.已完成项目}个</span>
-            </div>
-            <span style={{ margin: '0 10px' }}> | </span>
-            <div>
-              <span>暂停项目:</span>
-              <span style={{ fontWeight: 'bold', color: '#e2462c' }}>{data.暂停项目}个</span>
-            </div>
-            <span style={{ margin: '0 10px' }}> | </span>
-            <div>
-              <span>未开始项目:</span>
-              <span style={{ fontWeight: 'bold', color: '#e2462c' }}>{data.未开始项目}个</span>
-            </div>
-            <span style={{ margin: '0 10px' }}> | </span>
-            <div>
-              <span>进行中项目:</span>
-              <span style={{ fontWeight: 'bold', color: '#ffad2a' }}>{data.进行中项目}个</span>
-            </div>
-            <span style={{ margin: '0 10px' }}> | </span>
-            <div>
-              <span>项目共计:</span>
-              <span style={{ fontWeight: 'bold', color: '#2B69F8' }}>{data.项目共计}个</span>
-            </div>
-            {/* <span style={{ margin: '0 10px' }}> | </span> */}
-          </div>
-        </div>
-      );
-    },
-
     beforeFetch({ params }) {
       return {
         ...params,
+        type: type.value,
       };
     },
-    // afterFetch({ data }) {},
   });
 
   const router = useRouter();
@@ -220,9 +257,110 @@
       path: '/project/report-manage/report-today/',
     });
   }
+
+  function toggleDetails(data) {
+    showDetails.value = !showDetails.value;
+    type.value = data;
+    console.log('type.value', type.value);
+  }
+  function ToProjectInitiation() {
+    router.push({
+      path: '/project/project-manage/project-create',
+    });
+  }
 </script>
 
 <style lang="less" scoped>
+  .title-total {
+    display: flex;
+    font-size: 16px;
+    font-family: Inter;
+    font-weight: 400;
+    font-style: Regular;
+    font-size: 16px;
+  }
+  .project-stats-container {
+    margin: 1px;
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+  }
+
+  .project-card {
+    background: linear-gradient(90deg, #ffffff 0%, #e1eaff 100%);
+    border-radius: 15px;
+    padding: 16px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+  }
+
+  .project-header {
+    display: flex;
+    justify-content: space-between;
+    font-weight: bold;
+    margin-bottom: 8px;
+    color: #165dff;
+    font-family: Microsoft YaHei UI;
+    font-weight: 400;
+    font-style: Regular;
+    font-size: 24px;
+  }
+
+  .project-content {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 10px;
+    margin-bottom: 16px;
+
+    .project-content-left {
+      display: flex;
+      flex: 2;
+      justify-content: space-around;
+      gap: 20px;
+    }
+    .project-content-rigth {
+      flex: 2;
+    }
+  }
+
+  .project-stat {
+    font-family: Inter;
+    font-weight: 400;
+    font-style: Regular;
+    font-size: 21px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 15px;
+    margin-bottom: 4px;
+    color: #666;
+  }
+
+  .project-number {
+    font-family: Inter;
+    font-weight: 700;
+    font-style: Bold;
+    font-size: 21px;
+    font-weight: bold;
+    color: #2c3e50;
+  }
+
+  .view-all {
+    align-self: flex-end;
+    color: #007bff;
+    text-decoration: none;
+    font-family: Inter;
+    font-weight: 400;
+    font-style: Regular;
+    font-size: 16px;
+  }
+
+  .view-all:hover {
+    text-decoration: underline;
+  }
+
   ::v-deep .ant-float-btn-description {
     font-size: 18px !important;
   }

+ 304 - 0
src/views/project/project-manage/single-project/components/financial-manage/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="projectRef" ref="projectRef">
+    <div class="task-overview">
+      <div class="task-overview-item">
+        <div class="task-overview-card">
+          <span>任务总数</span>
+          <div class="task-overview-value">
+            <span class="bold-value">12</span>
+            <span>个</span>
+          </div>
+        </div>
+      </div>
+      <div class="task-overview-item">
+        <div class="task-overview-card">
+          <span>预计人天</span>
+          <div class="task-overview-value">
+            <span class="bold-value">12</span>
+            <span>人天</span>
+          </div>
+        </div>
+      </div>
+      <div class="task-overview-item">
+        <div class="task-overview-card">
+          <span>累计发生人天</span>
+          <div class="task-overview-value">
+            <span class="bold-value">12</span>
+            <span>天</span>
+          </div>
+        </div>
+      </div>
+      <div class="task-overview-item">
+        <div class="task-overview-card">
+          <span>超预计人天数</span>
+          <div class="task-overview-value">
+            <span class="bold-value">12</span>
+            <span>天</span>
+          </div>
+        </div>
+      </div>
+      <div class="task-overview-item">
+        <div class="task-overview-card">
+          <span>超期任务数</span>
+          <div class="task-overview-value">
+            <span class="bold-value">12</span>
+            <span>个</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="tab-bar">
+      <div :class="isActive == item.comValue ? 'btn-current' : 'btn-style'" @click="selectBtn(item)" v-for="(item, index) in tabsList" :key="index">
+        <span>{{ item.comName }}</span>
+      </div>
+    </div>
+    <div style="margin-top: 10px">
+      <component :is="isActive" :id="SessionStorageProjectId" :ProjectInfoData="projectInfoTemp"> </component>
+    </div>
+  </div>
+</template>
+<script setup>
+  import { ref, onMounted, onUnmounted, shallowRef, reactive } from 'vue';
+  import { projectApi } from '/@/api/project/project-api';
+  import { useRouter } from 'vue-router';
+
+  import ProgressManage from '../progress-manage/index.vue';
+  import ProgressInfo from '../progress-info/index.vue';
+  import FileManage from '../file-manage/index.vue';
+  import FollowUp from '../follow-up/index.vue';
+
+  onMounted(() => {
+    reduceRatio();
+    getProjectInfo();
+  });
+
+  onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+  });
+
+  // ---------------- 适配屏幕缩放 ----------------
+  const projectRef = ref(null);
+
+  function reduceRatio() {
+    handleResize();
+    window.addEventListener('resize', handleResize);
+  }
+
+  function handleResize() {
+    const ratio = window.innerWidth / 1920;
+    projectRef.value.style.setProperty('--fitWidthRatio', ratio);
+  }
+
+  const isActive = shallowRef(ProgressManage);
+  const tabsList = ref([
+    { comValue: shallowRef(ProgressInfo), comName: '收款管理' },
+    { comValue: shallowRef(ProgressManage), comName: '费用报销' },
+    { comValue: shallowRef(FileManage), comName: '对公付款' },
+    { comValue: shallowRef(FollowUp), comName: '劳务支出' },
+    { comValue: shallowRef(FollowUp), comName: '票据管理' },
+  ]);
+
+  //切换页签
+  function selectBtn(item) {
+    isActive.value = item.comValue;
+  }
+  //--------------------初始化页面数据-------------------------------
+  const projectInfoTemp = ref({
+    deliveryName: '',
+    deliveryType: [],
+    deliveryStatus: [],
+  });
+  const router = useRouter();
+  const query = router.currentRoute.value.query;
+  const SessionStorageProjectId = sessionStorage.getItem('SessionStorageProjectId');
+
+  async function getProjectInfo() {
+    const res = await projectApi.getProjectInfo(SessionStorageProjectId);
+    projectInfoTemp.value = res.data;
+  }
+</script>
+<style scoped lang="less">
+  .projectRef {
+    --fitWidthRatio: 1; // 1920px
+    font-size: calc(var(--fitWidthRatio) * 16px); // 页面默认字体大小
+
+    width: 100%;
+    height: 100%;
+    .task-overview {
+      display: flex;
+      gap: 70px;
+      align-items: center;
+      justify-content: center;
+      padding: 10px;
+    }
+
+    .task-overview-item {
+      display: flex;
+      align-items: center;
+    }
+
+    .task-overview-card {
+      width: 180px;
+      height: 100px;
+      background: #f5f8ff;
+      border-radius: 10px;
+      border: 1px solid #d3dfff;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .task-overview-value {
+      margin-top: 10px;
+    }
+
+    .bold-value {
+      font-weight: bold;
+      color: #318586;
+      font-size: 20px;
+      margin-right: 30px;
+    }
+
+    .header-box {
+      width: 100%;
+      background-color: #ffffff;
+      border-radius: 8px;
+      padding: calc(var(--fitWidthRatio) * 15px) calc(var(--fitWidthRatio) * 24px);
+
+      .header-banner {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .header-banner-left {
+          flex: 7;
+          display: flex;
+          align-items: center;
+
+          .header-banner-title {
+            display: inline-block;
+            width: 100%;
+            font-size: calc(var(--fitWidthRatio) * 20px);
+            font-style: normal;
+            font-weight: 600;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
+
+        .header-banner-right {
+          display: flex;
+          justify-content: flex-end;
+          flex: 3;
+        }
+      }
+
+      .banner-content {
+        margin-top: calc(var(--fitWidthRatio) * 14px);
+        display: flex;
+        justify-content: flex-start;
+        width: 100%;
+        flex-wrap: wrap;
+        gap: calc(var(--fitWidthRatio) * 24px);
+
+        .number-style {
+          color: #2b69f8;
+          font-size: calc(var(--fitWidthRatio) * 14px);
+          font-family: Alibaba PuHuiTi 2;
+          font-style: normal;
+          font-weight: 400;
+        }
+
+        .info {
+          display: flex;
+          align-items: center;
+
+          .info-name {
+            display: block;
+            color: #797a7c;
+            font-size: calc(var(--fitWidthRatio) * 14px);
+            font-family: Alibaba PuHuiTi 2;
+            font-style: normal;
+            font-weight: 400;
+          }
+
+          .info-value {
+            display: block;
+            font-size: calc(var(--fitWidthRatio) * 14px);
+            font-family: Alibaba PuHuiTi 2;
+            font-style: normal;
+            font-weight: 500;
+          }
+        }
+      }
+    }
+
+    .tab-bar {
+      margin-top: calc(var(--fitWidthRatio) * 10px);
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      width: 100%;
+      height: calc(var(--fitWidthRatio) * 45px);
+      background: #ffffff;
+      padding: 0 calc(var(--fitWidthRatio) * 20px);
+      border-radius: calc(var(--fitWidthRatio) * 6px);
+
+      .btn-style {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: calc(var(--fitWidthRatio) * 90px);
+        border-radius: calc(var(--fitWidthRatio) * 4px);
+        border-top: calc(var(--fitWidthRatio) * 3px) solid #ffffff;
+        margin-right: calc(var(--fitWidthRatio) * 20px);
+
+        span {
+          white-space: nowrap;
+          font-family: PingFang SC !important;
+          display: block;
+          color: #37393a;
+          text-align: center;
+          font-size: calc(var(--fitWidthRatio) * 18px);
+          font-family: Alibaba PuHuiTi 2;
+          font-style: normal;
+          font-weight: 600;
+        }
+      }
+
+      .btn-style:nth-last-child(1) {
+        margin-right: 0;
+      }
+
+      .btn-current {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: calc(var(--fitWidthRatio) * 90px);
+        height: 100%;
+        border-top: calc(var(--fitWidthRatio) * 3px) solid #267ef8;
+        margin-right: calc(var(--fitWidthRatio) * 20px);
+        cursor: pointer;
+
+        span {
+          white-space: nowrap;
+          font-family: PingFang SC !important;
+          display: block;
+          color: #267ef8;
+          text-align: center;
+          font-size: calc(var(--fitWidthRatio) * 18px);
+          font-family: Alibaba PuHuiTi 2;
+          font-style: normal;
+          font-weight: 600;
+          // margin-bottom: 0.5em;
+        }
+      }
+
+      .btn-style:hover {
+        cursor: pointer;
+      }
+    }
+  }
+</style>

+ 115 - 13
src/views/project/project-manage/single-project/components/progress-manage/index.vue

@@ -43,17 +43,6 @@
           },
         },
       },
-      // {
-      //   label: '任务分析',
-      //   attrs: {
-      //     type: 'primary',
-      //     onClick: () => {
-      //       router.push({
-      //         path: '/project/task-manage/task-analysis',
-      //       });
-      //     },
-      //   },
-      // },
       {
         label: '结算分析',
         attrs: {
@@ -67,7 +56,7 @@
       },
     ],
     columns: [
-    {
+      {
         title: '任务名称',
         dataIndex: 'taskName',
         width: '200px',
@@ -142,7 +131,7 @@
           );
         },
       },
-      
+
       {
         title: '任务状态',
         align: 'center',
@@ -280,6 +269,119 @@
       },
     ],
 
+    renderTitleSlot(data) {
+      return (
+        <div style={{ display: 'flex', gap: '70px', alignItems: 'center', justifyContent: 'center', padding: '10px' }}>
+          <div style={{ display: 'flex', alignItems: 'center' }}>
+            <div
+              style={{
+                width: '180px',
+                height: '100px',
+                background: '#F5F8FF',
+                borderRadius: '10px',
+                border: '1px solid #D3DFFF',
+                display: 'flex',
+                flexDirection: 'column',
+                alignItems: 'center',
+                justifyContent: 'center',
+              }}
+            >
+              <span>任务总数</span>
+              <div style={{ marginTop: '10px' }}>
+                <span style={{ fontWeight: 'bold', color: '#318586', fontSize: '20px', marginRight: '30px' }}>12</span>
+                <span>个</span>
+              </div>
+            </div>
+          </div>
+          <div style={{ display: 'flex', alignItems: 'center' }}>
+            <div
+              style={{
+                width: '180px',
+                height: '100px',
+                background: '#F5F8FF',
+                borderRadius: '10px',
+                border: '1px solid #D3DFFF',
+                display: 'flex',
+                flexDirection: 'column',
+                alignItems: 'center',
+                justifyContent: 'center',
+                gap: '10px',
+              }}
+            >
+              <span>预计人天</span>
+              <div style={{ marginTop: '10px' }}>
+                <span style={{ fontWeight: 'bold', color: '#318586', fontSize: '20px', marginRight: '30px' }}>12</span>
+                <span>人天</span>
+              </div>
+            </div>
+          </div>
+          <div style={{ display: 'flex', alignItems: 'center' }}>
+            <div
+              style={{
+                width: '180px',
+                height: '100px',
+                background: '#F5F8FF',
+                borderRadius: '10px',
+                border: '1px solid #D3DFFF',
+                display: 'flex',
+                flexDirection: 'column',
+                alignItems: 'center',
+                justifyContent: 'center',
+              }}
+            >
+              <span>累计发生人天</span>
+              <div style={{ marginTop: '10px' }}>
+                <span style={{ fontWeight: 'bold', color: '#318586', fontSize: '20px', marginRight: '30px' }}>12</span>
+                <span>天</span>
+              </div>
+            </div>
+          </div>
+          <div style={{ display: 'flex', alignItems: 'center' }}>
+            <div
+              style={{
+                width: '180px',
+                height: '100px',
+                background: '#F5F8FF',
+                borderRadius: '10px',
+                border: '1px solid #D3DFFF',
+                display: 'flex',
+                flexDirection: 'column',
+                alignItems: 'center',
+                justifyContent: 'center',
+              }}
+            >
+              <span>超预计人天数</span>
+              <div style={{ marginTop: '10px' }}>
+                <span style={{ fontWeight: 'bold', color: '#318586', fontSize: '20px', marginRight: '30px' }}>12</span>
+                <span>天</span>
+              </div>
+            </div>
+          </div>
+          <div style={{ display: 'flex', alignItems: 'center' }}>
+            <div
+              style={{
+                width: '180px',
+                height: '100px',
+                background: '#F5F8FF',
+                borderRadius: '10px',
+                border: '1px solid #D3DFFF',
+                display: 'flex',
+                flexDirection: 'column',
+                alignItems: 'center',
+                justifyContent: 'center',
+                gap: '10px',
+              }}
+            >
+              <span>超期任务数</span>
+              <div style={{ marginTop: '10px' }}>
+                <span style={{ fontWeight: 'bold', color: '#318586', fontSize: '20px', marginRight: '30px' }}>12</span>
+                <span>个</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      );
+    },
     beforeFetch({ params }) {
       return {
         ...params,

+ 2 - 0
src/views/project/project-manage/single-project/index.vue

@@ -39,6 +39,7 @@
   import { useRouter } from 'vue-router';
 
   import ProgressManage from './components/progress-manage/index.vue';
+  import FinancialManage from './components/financial-manage/index.vue';
   import ProgressInfo from './components/progress-info/index.vue';
   import FileManage from './components/file-manage/index.vue';
   import FollowUp from './components/follow-up/index.vue';
@@ -71,6 +72,7 @@
     { comValue: shallowRef(ProgressManage), comName: '任务列表' },
     { comValue: shallowRef(FileManage), comName: '文件管理' },
     { comValue: shallowRef(FollowUp), comName: '项目跟进' },
+    { comValue: shallowRef(FinancialManage), comName: '财务管理' },
   ]);
 
   //切换页签