Browse Source

项目跟踪功能优化

songxy 9 months ago
parent
commit
8f580170a7

+ 14 - 10
client/src/components/TreeSelectV2/index.vue

@@ -131,16 +131,20 @@ const setExpandedKeys = async () => {
 const nodeClickHandle = (data) => {
   visiable.value = false
   $emit('update:modelValue', data[$props.props['value'] ?? 'value'])
-  //@ts-ignore
-  const item = treeRef.value?.getNode($props.modelValue)
-  if (item && $props.labelAll) {
-    const arrs = initLabelNameAll(item)
-    labelName.value = arrs.join('-')
-    $emit('node-click', data, arrs)
-  } else {
-    labelName.value = data[$props.props['label'] ?? 'label']
-    $emit('node-click', data, [labelName.value])
-  }
+  const timer = setTimeout(() => {
+    //@ts-ignore
+    const item = treeRef.value?.getNode($props.modelValue)
+    if (item && $props.labelAll) {
+      const arrs = initLabelNameAll(item)
+      console.log(arrs)
+      labelName.value = arrs.join('-')
+      $emit('node-click', data, arrs)
+    } else {
+      labelName.value = data[$props.props['label'] ?? 'label']
+      $emit('node-click', data, [labelName.value])
+    }
+    clearTimeout(timer)
+  }, 100)
 }
 
 watchEffect(() => {

+ 2 - 1
client/src/utils/dict.ts

@@ -159,5 +159,6 @@ export enum DICT_TYPE {
   STAFF_STATE_TYPES = 'staff_state_type',
   SEX_TYPES = 'sex_type',
   PROJECT_REPORT_STATUS = 'project_report_status',
-  TRACKING_PROJECT_STATE = 'tracking_project_state'
+  TRACKING_PROJECT_STATE = 'tracking_project_state',
+  PROJECT_SECTOR = 'project_sector'
 }

+ 21 - 3
client/src/views/OaSystem/projectCenter/projectTrack/common.ts

@@ -11,13 +11,20 @@ interface QueryParam extends IPageParam {
   xmmc: string
   gzryId: string
   xmbmId: string
+  xzqdm: string
+  zt: string
+  ssbk: string
 }
 export interface IFormType {
   id: string
   xmmc: string
+  ssbk: string
+  qyzjId: string
+  qyzj: string
   gzryId: string
   gzry: string
   jf: string
+  jfks: string
   sfjc: number
   ygje: number
   zbje: number
@@ -36,20 +43,27 @@ export const useMixins = () => {
     xmmc: '',
     gzryId: '',
     xmbmId: '',
+    xzqdm: '',
+    zt: '',
+    ssbk: '',
     pageNo: 1,
     pageSize: 15
   })
   const formData = ref<IFormType>({
     id: '',
     xmmc: '',
+    ssbk: '',
+    xmbmId: '',
+    qyzjId: '',
+    qyzj: '',
     gzryId: '',
     gzry: '',
     jf: '',
+    jfks: '',
     sfjc: 0,
     ygje: 0,
     zbje: 0,
     jfdjr: '',
-    xmbmId: '',
     xmbm: '',
     xzqdm: '',
     xzqmc: '',
@@ -63,14 +77,18 @@ export const useMixins = () => {
       formData.value = {
         id: '',
         xmmc: '',
+        ssbk: '',
+        xmbmId: '',
+        qyzjId: '',
+        qyzj: '',
         gzryId: user.user.id,
         gzry: user.user.nickname,
         jf: '',
+        jfks: '',
         sfjc: 0,
         ygje: 0,
-        zbje: '',
+        zbje: 0,
         jfdjr: '',
-        xmbmId: '',
         xmbm: '',
         xzqdm: '',
         xzqmc: '',

+ 356 - 0
client/src/views/OaSystem/projectCenter/projectTrack/index copy.vue

@@ -0,0 +1,356 @@
+<template>
+  <div class="oa-sys-list-view">
+    <div class="searchBox">
+      <div class="form">
+        <span class="formSpan">项目名称:</span>
+        <el-input v-model="queryParams.xmmc" placeholder="请输入项目名称" style="width: 210px" />
+      </div>
+      <div class="form">
+        <span class="formSpan">项目部门:</span>
+        <DeptTree v-model="queryParams['xmbmId']" placeholder="请选择部门" />
+      </div>
+      <div class="form">
+        <span class="formSpan">跟踪人员:</span>
+        <UserOrgTree v-model="queryParams['gzryId']" placeholder="请选择跟踪人员" />
+      </div>
+      <div class="from">
+        <div class="btnBox">
+          <el-button type="primary" style="background: #3485ff" @click="searchHandle">
+            <img src="@/assets/imgs/OA/search.png" class="mr-8px" alt="" />
+            查询</el-button
+          >
+          <el-button type="primary" @click="addOrEditHandle()">
+            <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
+            新增</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div class="infoBox">
+      <ul>
+        <li v-for="(item, index) in infoList" :key="index" class="mr-40px">
+          <img class="mr-8px" :src="getAssetURL(item.icon)" alt="" />
+          <p>{{ item.name }}:</p>
+          <h4 class="font-size-18px">{{ item.num.toFixed(0) }}{{ item.unit }}</h4>
+        </li>
+      </ul>
+    </div>
+    <div class="tableBox">
+      <div class="table" ref="tableRef">
+        <el-table
+          stripe
+          :data="tableData"
+          style="width: 100%; height: 100%"
+          :style="{ height: tableHeight + 'px' }"
+          :header-cell-style="{
+            background: '#E5F0FB',
+            color: '#233755',
+            height: '50px'
+          }"
+        >
+          <el-table-column label="序号" width="80">
+            <template #default="scope">{{ scope.$index + 1 }}</template>
+          </el-table-column>
+          <el-table-column prop="zt" label="状态" width="120">
+            <template #default="scope">
+              {{ getDictLabel(DICT_TYPE.TRACKING_PROJECT_STATE, scope.row.zt) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="xzqmc" label="行政区" width="180" :show-overflow-tooltip="true" />
+          <el-table-column
+            :show-overflow-tooltip="true"
+            prop="xmmc"
+            label="项目名称"
+            :min-width="320"
+          />
+          <el-table-column prop="gzry" label="跟踪人员" width="120" />
+          <el-table-column prop="sfjc" label="是否进场" width="120">
+            <template #default="scope">
+              {{ scope.row.sfjc == '0' ? '是' : '否' }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="ygje"
+            label="预估金额(元)"
+            :show-overflow-tooltip="true"
+            width="160"
+          />
+          <el-table-column
+            prop="zbje"
+            label="中标金额(元)"
+            :show-overflow-tooltip="true"
+            width="160"
+          />
+          <el-table-column prop="jf" label="甲方" width="220" :show-overflow-tooltip="true" />
+          <el-table-column
+            prop="jfdjr"
+            label="甲方对接人"
+            width="120"
+            :show-overflow-tooltip="true"
+          />
+          <el-table-column
+            prop="xmbm"
+            label="项目归属部门"
+            width="160"
+            :show-overflow-tooltip="true"
+          />
+          <el-table-column prop="latestModifyTime" label="更新时间" width="180">
+            <template #default="scope">
+              {{ formatDate(scope.row.latestModifyTime) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="xmjl" label="开关" width="100">
+            <template #default="scope">
+              <el-switch
+                v-model="scope.row.status"
+                :active-value="0"
+                @change="switchChange(scope.row)"
+                :inactive-value="1"
+              />
+            </template>
+          </el-table-column>
+
+          <el-table-column label="操作" width="80" fixed="right">
+            <template #default="scope">
+              <div class="operateBtn" @click="addOrEditHandle(scope.row)">
+                <span>编辑</span>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="pageBox">
+        <el-pagination
+          v-model:current-page="queryParams.pageNo"
+          :page-size="queryParams.pageSize"
+          background
+          layout="total, prev, pager, next, jumper"
+          :total="total"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+    <div class="editorConver" v-if="dialogVisible" :title="dialogTitle" width="660px" align-center>
+      <el-form
+        ref="ruleFormRef"
+        style="width: 600px"
+        label-width="auto"
+        class="demo-ruleForm"
+        status-icon
+      >
+        <el-form-item label="项目名称" prop="name">
+          <el-input v-model="formData.xmmc" />
+        </el-form-item>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="跟踪人员" prop="region">
+              <UserOrgTree
+                v-model="formData.gzryId"
+                placeholder="请选择跟踪人员"
+                @node-click="(item) => treeNodeClick(item, 'gzry')"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="甲方" prop="name">
+              <el-input v-model="formData.jf" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="甲方对接人" prop="name">
+              <el-input v-model="formData.jfdjr" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否进场" prop="region">
+              <el-select v-model="formData.sfjc" style="width: 100%">
+                <el-option label="是" :value="0" />
+                <el-option label="否" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="预估金额(万元)" prop="name">
+              <el-input v-model="formData.ygje" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="中标金额(万元)" prop="name">
+              <el-input v-model="formData.zbje" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="项目归属部门" prop="region">
+              <DeptTree
+                v-model="formData.xmbmId"
+                @node-click="(item) => treeNodeClick(item, 'xmbm')"
+                placeholder="请选择部门"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="行政区" prop="region">
+              <DistrictTree
+                class="form-item-disable-style"
+                placeholder="请选择行政区"
+                v-model="formData.xzqdm"
+                @node-click="(item, labels) => treeNodeClick(labels, 'xzqmc')"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="状态" prop="region">
+              <el-select v-model="formData.zt" placeholder="请选择状态" style="width: 100%">
+                <el-option
+                  v-for="(item, index) in getDictOptions(DICT_TYPE.TRACKING_PROJECT_STATE)"
+                  :key="index"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="谁可见" prop="region">
+              <UserOrgTree v-model="visibleUserIds" :multiple="true" placeholder="请选择跟谁可见" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="备注" prop="desc">
+          <el-input v-model="formData.bz" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <div class="dialog-footer">
+        <el-button @click="dialogVisible = false">关闭</el-button>
+        <el-button type="primary" @click="saveProjectTracking"> 保存 </el-button>
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE, getDictLabel, getDictOptions } from '@/utils/dict'
+import { IFormType, useMixins, infoList, user } from './common'
+import request from '@/config/axios'
+import { formatDate } from '@/utils/formatTime'
+import { getAssetURL } from '@/utils/auth'
+import UserOrgTree from '@/views/OaSystem/components/UserOrgTree/index.vue'
+import DeptTree from '@/views/OaSystem/components/DeptTree/index.vue'
+import DistrictTree from '@/views/OaSystem/components/DistrictTree/index.vue'
+
+defineOptions({ name: 'ProjectTrack' })
+
+const visibleUserIds = ref<string[]>([])
+const tableRef: any = ref(null)
+const tableHeight: any = ref(0)
+const { queryParams, formData, initFormData } = useMixins()
+
+const handleCurrentChange = (pageNo: number) => {
+  queryParams.pageNo = pageNo
+  queryProjectTrackByPage()
+}
+/**
+ * 项目跟踪:新增、编辑
+ */
+const dialogTitle = ref<string>('项目跟踪填报')
+const dialogVisible = ref<boolean>(false)
+const addOrEditHandle = (row?: IFormType) => {
+  dialogVisible.value = true
+  if (!row) {
+    dialogTitle.value = '项目跟踪填报'
+    initFormData()
+    visibleUserIds.value = []
+  } else {
+    dialogTitle.value = '项目跟踪编辑'
+    queryProjectTrackByDetail(row['id']).then((result) => {
+      initFormData(result as any)
+      visibleUserIds.value = result['visibleUserIds'].split(',')
+    })
+  }
+}
+const treeNodeClick = (item, type: string) => {
+  if (!item) return
+  if (type === 'xzqmc') {
+    formData.value[type] = item.join('-')
+    return
+  }
+  formData.value[type] = item['label'] || item['name']
+}
+const saveProjectTracking = async (): Promise<void> => {
+  const urlApi = `/projectTracking/save`
+  const sendData = {
+    ...formData.value
+  }
+  sendData['visibleUserIds'] = visibleUserIds.value.join(',')
+  const result = await request.post({ url: urlApi, data: sendData }, '/business')
+  if (result) {
+    searchHandle()
+    dialogVisible.value = false
+  }
+}
+const switchChange = (item) => {
+  updateStatus({
+    id: item['id'],
+    status: item['status']
+  })
+}
+const updateStatus = async (data: { id: string; status: string }): Promise<void> => {
+  const urlApi = `/update-status`
+  return await request.put({ url: urlApi, data: data }, '/business')
+}
+const tableData = ref<Array<any>>([])
+const total = ref<number>()
+const searchHandle: () => void = () => {
+  queryParams.pageNo = 1
+  queryProjectTrackingSummary()
+  queryProjectTrackByPage()
+}
+const queryProjectTrackByPage = async (): Promise<void> => {
+  const urlApi = `/projectTracking/page`
+  const sendData = {
+    ...queryParams
+  }
+  const result = await request.get({ url: urlApi, params: sendData }, '/business')
+  tableData.value = result['list']
+  total.value = result['total']
+}
+const queryProjectTrackByDetail = async (id: string): Promise<void> => {
+  const urlApi = `/projectTracking/getById`
+  const sendData = {
+    id
+  }
+  return await request.get({ url: urlApi, params: sendData }, '/business')
+}
+queryProjectTrackByPage()
+
+/**
+ * 项目金额、项目个数汇总
+ */
+const queryProjectTrackingSummary = async (): Promise<void> => {
+  const urlApi = `/projectTracking/summary`
+  const sendData = {
+    ...queryParams
+  }
+  const result = await request.get({ url: urlApi, params: sendData }, '/business')
+  infoList[0]['num'] = result ? result.amount ?? 0 : 0
+  infoList[1]['num'] = result ? result.nums ?? 0 : 0
+}
+queryProjectTrackingSummary()
+onMounted(() => {
+  tableHeight.value = tableRef.value.clientHeight
+})
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-radio) {
+  margin-right: 10px;
+}
+</style>

+ 374 - 174
client/src/views/OaSystem/projectCenter/projectTrack/index.vue

@@ -13,6 +13,37 @@
         <span class="formSpan">跟踪人员:</span>
         <UserOrgTree v-model="queryParams['gzryId']" placeholder="请选择跟踪人员" />
       </div>
+      <div class="form">
+        <span class="formSpan">状态:</span>
+        <el-select v-model="queryParams.zt" placeholder="请选择状态">
+          <el-option
+            v-for="(item, index) in getDictOptions(DICT_TYPE.TRACKING_PROJECT_STATE)"
+            :key="index"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
+      <div class="form">
+        <span class="formSpan">区域:</span>
+        <DistrictTree
+          class="form-item-disable-style"
+          placeholder="请选择区域"
+          v-model="queryParams.xzqdm"
+          style="width: 100%"
+        />
+      </div>
+      <div class="form">
+        <span class="formSpan">所属板块:</span>
+        <el-select v-model="queryParams.ssbk" placeholder="请选择所属板块" clearable>
+          <el-option
+            v-for="dict in getDictOptions(DICT_TYPE.PROJECT_SECTOR)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </div>
       <div class="from">
         <div class="btnBox">
           <el-button type="primary" style="background: #3485ff" @click="searchHandle">
@@ -31,7 +62,7 @@
         <li v-for="(item, index) in infoList" :key="index" class="mr-40px">
           <img class="mr-8px" :src="getAssetURL(item.icon)" alt="" />
           <p>{{ item.name }}:</p>
-          <h4 class="font-size-18px">{{ item.num }}{{ item.unit }}</h4>
+          <h4 class="font-size-18px">{{ item.num.toFixed(0) }}{{ item.unit }}</h4>
         </li>
       </ul>
     </div>
@@ -41,6 +72,7 @@
           stripe
           :data="tableData"
           style="width: 100%; height: 100%"
+          highlight-current-row
           :style="{ height: tableHeight + 'px' }"
           :header-cell-style="{
             background: '#E5F0FB',
@@ -48,35 +80,53 @@
             height: '50px'
           }"
         >
-          <el-table-column label="序号" width="80">
+          <el-table-column label="序号" width="60" align="center">
             <template #default="scope">{{ scope.$index + 1 }}</template>
           </el-table-column>
-          <el-table-column prop="xzqmc" label="行政区" width="180" :show-overflow-tooltip="true" />
+          <el-table-column prop="zt" label="状态" width="80">
+            <template #default="scope">
+              {{ getDictLabel(DICT_TYPE.TRACKING_PROJECT_STATE, scope.row.zt) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="xzqmc" label="区域" width="120">
+            <template #default="scope">
+              {{ formatDistrict(scope.row.xzqmc)[0] }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="xzqmc" label="县市区" width="100">
+            <template #default="scope">
+              {{ formatDistrict(scope.row.xzqmc)[1] }}
+            </template>
+          </el-table-column>
           <el-table-column
             :show-overflow-tooltip="true"
             prop="xmmc"
             label="项目名称"
-            :min-width="220"
+            :min-width="320"
           />
+          <el-table-column prop="zt" label="所属板块" width="120">
+            <template #default="scope">
+              {{ getDictLabel(DICT_TYPE.PROJECT_SECTOR, scope.row.ssbk) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="gzry" label="跟踪人员" width="120" />
+          <el-table-column prop="gzry" label="区域总监" width="120" />
           <el-table-column prop="sfjc" label="是否进场" width="120">
             <template #default="scope">
               {{ scope.row.sfjc == '0' ? '是' : '否' }}
             </template>
           </el-table-column>
-          <el-table-column prop="gzry" label="跟踪人员" width="120" />
-          <el-table-column
-            prop="ygje"
-            label="预估金额(万元)"
-            :show-overflow-tooltip="true"
-            width="160"
-          />
-          <el-table-column
-            prop="zbje"
-            label="中标金额(万元)"
-            :show-overflow-tooltip="true"
-            width="160"
-          />
-          <el-table-column prop="jf" label="甲方" width="220" :show-overflow-tooltip="true" />
+          <el-table-column prop="ygje" label="预估金额(元)" width="140" align="center">
+            <template #default="scope">
+              {{ (scope.row.ygje ?? 0).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="zbje" label="中标金额(元)" width="140" align="center">
+            <template #default="scope">
+              {{ (scope.row.zbje ?? 0).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="jf" label="甲方单位" width="220" :show-overflow-tooltip="true" />
           <el-table-column
             prop="jfdjr"
             label="甲方对接人"
@@ -89,11 +139,6 @@
             width="160"
             :show-overflow-tooltip="true"
           />
-          <el-table-column prop="zt" label="状态" width="120">
-            <template #default="scope">
-              {{ getDictLabel(DICT_TYPE.TRACKING_PROJECT_STATE, scope.row.zt) }}
-            </template>
-          </el-table-column>
           <el-table-column prop="latestModifyTime" label="更新时间" width="180">
             <template #default="scope">
               {{ formatDate(scope.row.latestModifyTime) }}
@@ -124,189 +169,227 @@
           v-model:current-page="queryParams.pageNo"
           :page-size="queryParams.pageSize"
           background
-          layout="total, prev, pager, next, jumper"
+          layout="sizes, total, prev, pager, next, jumper"
           :total="total"
+          :page-sizes="[15, 20, 30, 40]"
+          @size-change="handleSizeChange"
           @current-change="handleCurrentChange"
         />
       </div>
     </div>
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="660px" align-center>
-      <el-form
-        ref="ruleFormRef"
-        style="width: 600px"
-        label-width="auto"
-        class="demo-ruleForm"
-        status-icon
-      >
-        <el-form-item label="项目名称" prop="name">
-          <el-input v-model="formData.xmmc" />
-        </el-form-item>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="跟踪人员" prop="region">
-              <UserOrgTree
-                v-model="formData.gzryId"
-                placeholder="请选择跟踪人员"
-                @node-click="(item) => treeNodeClick(item, 'gzry')"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="甲方" prop="name">
-              <el-input v-model="formData.jf" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="甲方对接人" prop="name">
-              <el-input v-model="formData.jfdjr" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="是否进场" prop="region">
-              <el-select v-model="formData.sfjc" style="width: 100%">
-                <el-option label="是" :value="0" />
-                <el-option label="否" :value="1" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="预估金额(万元)" prop="name">
-              <el-input v-model="formData.ygje" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="中标金额(万元)" prop="name">
-              <el-input v-model="formData.zbje" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="项目归属部门" prop="region">
-              <DeptTree
-                v-model="formData.xmbmId"
-                @node-click="(item) => treeNodeClick(item, 'xmbm')"
-                placeholder="请选择部门"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="行政区" prop="region">
-              <DistrictTree
-                class="form-item-disable-style"
-                placeholder="请选择行政区"
-                v-model="formData.xzqdm"
-                @node-click="(item, labels) => treeNodeClick(labels, 'xzqmc')"
-                style="width: 100%"
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="状态" prop="region">
-              <el-select v-model="formData.zt" placeholder="请选择状态" style="width: 100%">
-                <el-option
-                  v-for="(item, index) in getDictOptions(DICT_TYPE.TRACKING_PROJECT_STATE)"
-                  :key="index"
-                  :label="item.label"
-                  :value="item.value"
+    <div class="editorCover" v-if="dialogVisible" :title="dialogTitle" align-center>
+      <div class="cover-header">
+        <h2>{{ dialogTitle }}</h2>
+      </div>
+      <div class="cover-content">
+        <el-form
+          ref="ruleFormRef"
+          :rules="rules"
+          :model="formData"
+          style="width: 100%"
+          label-width="auto"
+          class="demo-ruleForm"
+          status-icon
+        >
+          <el-form-item label="项目名称" prop="xmmc">
+            <el-input v-model="formData.xmmc" />
+          </el-form-item>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="所属板块" prop="ssbk">
+                <el-select
+                  v-model="formData.ssbk"
+                  placeholder="请选择所属板块"
+                  clearable
+                  style="width: 100%"
+                >
+                  <el-option
+                    v-for="dict in getDictOptions(DICT_TYPE.PROJECT_SECTOR)"
+                    :key="dict.value"
+                    :label="dict.label"
+                    :value="dict.value"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="所属部门" prop="xmbmId">
+                <DeptTree
+                  v-model="formData.xmbmId"
+                  @node-click="(item) => treeNodeClick(item, 'xmbm')"
+                  placeholder="请选择部门"
                 />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="谁可见" prop="region">
-              <UserOrgTree v-model="visibleUserIds" :multiple="true" placeholder="请选择跟谁可见" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-form-item label="备注" prop="desc">
-          <el-input v-model="formData.bz" type="textarea" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="dialogVisible = false">关闭</el-button>
-          <el-button type="primary" @click="saveProjectTracking"> 保存 </el-button>
-        </div>
-      </template>
-    </el-dialog>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="跟踪人员" prop="gzryId">
+                <UserOrgTree
+                  v-model="formData.gzryId"
+                  placeholder="请选择跟踪人员"
+                  @node-click="(item) => treeNodeClick(item, 'gzry')"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="区域总监" prop="qyzjId">
+                <UserOrgTree
+                  v-model="formData.qyzjId"
+                  placeholder="请选择区域总监"
+                  @node-click="(item) => treeNodeClick(item, 'qyzj')"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="甲方单位" prop="jfId">
+                <TreeSelectV2
+                  :data="contractTree"
+                  :props="{ label: 'name', value: 'id' }"
+                  v-model="formData.jfId"
+                  :filter-method="filterNodeMethod"
+                  @node-click="(item) => treeNodeClick(item, 'jf')"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="甲方科室" prop="jfks">
+                <el-input v-model="formData.jfks" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="甲方对接人" prop="jfdjr">
+                <el-input v-model="formData.jfdjr" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="是否进场" prop="sfjc">
+                <el-select v-model="formData.sfjc" style="width: 100%">
+                  <el-option label="是" :value="0" />
+                  <el-option label="否" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="预估金额(元)" prop="ygje">
+                <el-input v-model="formData.ygje" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="中标金额(元)" prop="zbje">
+                <el-input v-model="formData.zbje" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="行政区" prop="xzqdm">
+                <DistrictTree
+                  class="form-item-disable-style"
+                  placeholder="请选择行政区"
+                  v-model="formData.xzqdm"
+                  @node-click="(item, labels) => treeNodeClick(labels, 'xzqmc')"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="谁可见" prop="visibleUserIds">
+                <UserOrgTree
+                  v-model="visibleUserIds"
+                  :multiple="true"
+                  placeholder="请选择跟谁可见"
+                  @change="visibleUserChange"
+                />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="100">
+            <el-col :span="12">
+              <el-form-item label="状态" prop="zt">
+                <el-select v-model="formData.zt" placeholder="请选择状态" style="width: 100%">
+                  <el-option
+                    v-for="(item, index) in getDictOptions(DICT_TYPE.TRACKING_PROJECT_STATE)"
+                    :key="index"
+                    :label="item.label"
+                    :value="item.value"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-form-item label="备注" prop="desc">
+            <el-input v-model="formData.bz" rows="5" type="textarea" />
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="cover-footer">
+        <el-button @click="dialogVisible = false">关闭</el-button>
+        <el-button type="primary" @click="saveProjectTracking(ruleFormRef)"> 保存 </el-button>
+      </div>
+    </div>
   </div>
 </template>
 <script setup lang="ts">
+import type { FormInstance, FormRules } from 'element-plus'
+import { useQuery } from '@tanstack/vue-query'
 import { DICT_TYPE, getDictLabel, getDictOptions } from '@/utils/dict'
 import { IFormType, useMixins, infoList, user } from './common'
 import request from '@/config/axios'
 import { formatDate } from '@/utils/formatTime'
+import { filterNodeMethod } from '@/utils/tree'
 import { getAssetURL } from '@/utils/auth'
+import TreeSelectV2 from '@/components/TreeSelectV2/index.vue'
 import UserOrgTree from '@/views/OaSystem/components/UserOrgTree/index.vue'
 import DeptTree from '@/views/OaSystem/components/DeptTree/index.vue'
 import DistrictTree from '@/views/OaSystem/components/DistrictTree/index.vue'
+import { queryCustomerTree } from '@/service/contract'
 
 defineOptions({ name: 'ProjectTrack' })
 
+/***
+ * 查询委托方数据
+ * **/
+const { data: contractTree } = useQuery(['contractTree'], async () => await queryCustomerTree(), {
+  staleTime: Infinity
+})
+
 const visibleUserIds = ref<string[]>([])
 const tableRef: any = ref(null)
 const tableHeight: any = ref(0)
 const { queryParams, formData, initFormData } = useMixins()
 
+const handleSizeChange = (size) => {
+  queryParams.pageNo = 1
+  queryParams.pageSize = size
+  queryProjectTrackByPage()
+}
 const handleCurrentChange = (pageNo: number) => {
   queryParams.pageNo = pageNo
   queryProjectTrackByPage()
 }
-/**
- * 项目跟踪:新增、编辑
- */
-const dialogTitle = ref<string>('项目跟踪填报')
-const dialogVisible = ref<boolean>(false)
-const addOrEditHandle = (row?: IFormType) => {
-  dialogVisible.value = true
-  if (!row) {
-    dialogTitle.value = '项目跟踪填报'
-    initFormData()
-    visibleUserIds.value = []
-  } else {
-    dialogTitle.value = '项目跟踪编辑'
-    queryProjectTrackByDetail(row['id']).then((result) => {
-      initFormData(result as any)
-      visibleUserIds.value = result['visibleUserIds'].split(',')
-    })
-  }
-}
-const treeNodeClick = (item, type: string) => {
-  if (!item) return
-  if (type === 'xzqmc') {
-    formData.value[type] = item.join('-')
-    return
+const formatDistrict = (xzqmc: string) => {
+  if (!xzqmc) return ''
+  const narr: string[] = []
+  const arrs = xzqmc.split('-')
+  if (arrs.length === 1) {
+    narr.push(arrs[0], '省级')
+    return narr
   }
-  formData.value[type] = item['label'] || item['name']
-}
-const saveProjectTracking = async (): Promise<void> => {
-  const urlApi = `/projectTracking/save`
-  const sendData = {
-    ...formData.value
+  if (arrs.length === 2) {
+    narr.push(`${arrs[0]}-${arrs[1]}`, '市级')
   }
-  sendData['visibleUserIds'] = visibleUserIds.value.join(',')
-  const result = await request.post({ url: urlApi, data: sendData }, '/business')
-  if (result) {
-    searchHandle()
-    dialogVisible.value = false
+  if (arrs.length === 3) {
+    narr.push(`${arrs[0]}-${arrs[1]}`, arrs[2])
   }
-}
-const switchChange = (item) => {
-  updateStatus({
-    id: item['id'],
-    status: item['status']
-  })
-}
-const updateStatus = async (data: { id: string; status: string }): Promise<void> => {
-  const urlApi = `/update-status`
-  return await request.put({ url: urlApi, data: data }, '/business')
+  return narr
 }
 const tableData = ref<Array<any>>([])
 const total = ref<number>()
@@ -346,6 +429,83 @@ const queryProjectTrackingSummary = async (): Promise<void> => {
   infoList[1]['num'] = result ? result.nums ?? 0 : 0
 }
 queryProjectTrackingSummary()
+
+/**
+ * 项目跟踪:新增、编辑
+ */
+const dialogTitle = ref<string>('项目跟踪填报')
+const dialogVisible = ref<boolean>(false)
+const addOrEditHandle = (row?: IFormType) => {
+  dialogVisible.value = true
+  if (!row) {
+    dialogTitle.value = '项目跟踪填报'
+    initFormData()
+    visibleUserIds.value = []
+  } else {
+    dialogTitle.value = '项目跟踪编辑'
+    queryProjectTrackByDetail(row['id']).then((result) => {
+      initFormData(result as any)
+      visibleUserIds.value = result['visibleUserIds'].split(',')
+    })
+  }
+}
+const treeNodeClick = (item, type: string) => {
+  if (!item) return
+  if (type === 'xzqmc') {
+    formData.value[type] = item.join('-')
+    return
+  }
+  formData.value[type] = item['label'] || item['name']
+}
+const visibleUserChange = (item) => {
+  formData.value['visibleUserIds'] = item.join(',')
+}
+const rules = reactive<FormRules<IFormType>>({
+  xmmc: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
+  ssbk: [{ required: true, message: '所属板块不能为空', trigger: 'change' }],
+  xmbmId: [{ required: true, message: '所属部门不能为空', trigger: 'change' }],
+  gzryId: [{ required: true, message: '跟踪人员不能为空', trigger: 'change' }],
+  qyzjId: [{ required: true, message: '区域总监不能为空', trigger: 'change' }],
+  jfId: [{ required: true, message: '甲方单位不能为空', trigger: 'blur' }],
+  jfks: [{ required: true, message: '甲方科室不能为空', trigger: 'blur' }],
+  jfdjr: [{ required: true, message: '甲方对接人不能为空', trigger: 'blur' }],
+  sfjc: [{ required: true, message: '是否进场不能为空', trigger: 'change' }],
+  ygje: [{ required: true, message: '预估金额不能为空', trigger: 'blur' }],
+  zbje: [{ required: true, message: '中标金额不能为空', trigger: 'blur' }],
+  xzqdm: [{ required: true, message: '行政区不能为空', trigger: 'change' }],
+  visibleUserIds: [{ required: true, message: '谁可见不能为空', trigger: 'change' }],
+  zt: [{ required: true, message: '状态不能为空', trigger: 'change' }]
+})
+const ruleFormRef = ref<FormInstance>()
+const saveProjectTracking = async (formEl: FormInstance | undefined): Promise<void> => {
+  if (!formEl) return
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      const urlApi = `/projectTracking/save`
+      const sendData = {
+        ...formData.value
+      }
+      // sendData['visibleUserIds'] = visibleUserIds.value.join(',')
+      const result = await request.post({ url: urlApi, data: sendData }, '/business')
+      if (result) {
+        searchHandle()
+        dialogVisible.value = false
+      }
+    } else {
+      console.log('error submit!', fields)
+    }
+  })
+}
+const switchChange = (item) => {
+  updateStatus({
+    id: item['id'],
+    status: item['status']
+  })
+}
+const updateStatus = async (data: { id: string; status: string }): Promise<void> => {
+  const urlApi = `/update-status`
+  return await request.put({ url: urlApi, data: data }, '/business')
+}
 onMounted(() => {
   tableHeight.value = tableRef.value.clientHeight
 })
@@ -355,4 +515,44 @@ onMounted(() => {
 :deep(.el-radio) {
   margin-right: 10px;
 }
+.searchBox {
+  display: flex;
+  white-space: nowrap;
+  .form {
+    margin-right: 16px !important;
+  }
+  .formSpan {
+    width: auto !important;
+  }
+}
+.oa-sys-list-view {
+  position: relative;
+  .editorCover {
+    position: absolute;
+    left: 0px;
+    right: 0px;
+    top: 0px;
+    bottom: 0px;
+    z-index: 9;
+    background: #fff;
+    display: flex;
+    flex-direction: column;
+    border-radius: 20px;
+    > div {
+      &.cover-header {
+        padding: 20px 30px;
+      }
+      &.cover-content {
+        padding: 20px 100px;
+        flex: 1px;
+        flex-grow: 1;
+        overflow: auto;
+      }
+      &.cover-footer {
+        text-align: right;
+        padding: 40px;
+      }
+    }
+  }
+}
 </style>