Selaa lähdekoodia

feat: 新增 项目成员与阶段组件

修改项目成员样式,新增项目阶段字段展示与操作接口
修改项目详情子项目查询逻辑
hotchicken1996 1 vuosi sitten
vanhempi
commit
5bef9b2b15

+ 14 - 0
client/src/interface/project.ts

@@ -1,5 +1,10 @@
 export type ProjectId = string
 
+// 项目相关查询params
+export interface ProjectQueryParams {
+  projectId: ProjectId
+}
+
 export interface ProjectRecord {
   /**
    * 归属公司ID
@@ -179,3 +184,12 @@ export interface SubProject {
    */
   zrbmId?: string
 }
+
+export interface ProjectMember {
+  workerId: string
+  responsibility?: string
+}
+
+export interface HandleProjectMemberDTO extends ProjectMember {
+  projectId: ProjectId
+}

+ 2 - 2
client/src/main.ts

@@ -53,7 +53,7 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue' //导入所有图
 
 import LinkRouteMap from './LinkRouteMap';
 //监听iframe postMessage发送的信息
-window.addEventListener("message", (evt) => {
+/*window.addEventListener("message", (evt) => {
   const eData = JSON.parse(evt.data)
   console.log("evt------------------")
   console.log(eData)
@@ -64,7 +64,7 @@ window.addEventListener("message", (evt) => {
     lRoutePayload['url'] = payload['url']
     subscribe.emit(lRoutePayload['path'], lRoutePayload)
   }
-}, false) ;
+}, false) ;*/
 // 创建实例
 const setupAll = async () => {
   const app = createApp(App)

+ 74 - 2
client/src/service/project.ts

@@ -1,5 +1,5 @@
 import request from '@/config/axios'
-import { ProjectRecord, ProjectId, SubProject } from '@/interface/project'
+import { ProjectRecord, ProjectId, SubProject, HandleProjectMemberDTO } from '@/interface/project'
 
 /**
  * 项目详情(无子项目)
@@ -20,6 +20,7 @@ export const getProjectById = async (projectId: ProjectId): Promise<ProjectRecor
  * @param projectId
  */
 export const getProjectWithChildrenById = async (projectId: ProjectId): Promise<ProjectRecord> => {
+  console.log('get project: ', projectId)
   return await request.get(
     {
       url: '/project-with-children',
@@ -30,7 +31,7 @@ export const getProjectWithChildrenById = async (projectId: ProjectId): Promise<
 }
 
 /**
- * 项目详情(包含子项目)
+ * 添加子项目
  * @param data
  */
 export const postSubProject = async (data: SubProject) => {
@@ -49,3 +50,74 @@ export const postSubProject = async (data: SubProject) => {
 export const getProjectType = async () => {
   return await request.get({ url: '/project-type/tree' }, '/business')
 }
+
+/**
+ * 项目成员
+ * @param projectId
+ */
+export const getProjectMember = async (projectId: ProjectId) => {
+  return await request.get(
+    {
+      url: '/project-worker-relation/list',
+      params: { projectId }
+    },
+    '/business'
+  )
+}
+
+/**
+ * 删除项目成员
+ * @param data
+ */
+export const deleteMember = async (data: HandleProjectMemberDTO) => {
+  return await request.post(
+    {
+      url: '/project-worker-relation/unbind',
+      data
+    },
+    '/business'
+  )
+}
+
+/**
+ * 新增项目成员
+ * @param data
+ */
+export const addMember = async (data: HandleProjectMemberDTO) => {
+  return await request.post(
+    {
+      url: '/project-worker-relation/bind',
+      data
+    },
+    '/business'
+  )
+}
+
+/**
+ * 项目里程碑
+ * @param projectId
+ */
+export const getProjectMileStone = async (projectId: ProjectId) => {
+  return await request.get(
+    {
+      url: '/project-milestone/list',
+      params: { projectId }
+    },
+    '/business'
+  )
+}
+
+/**
+ * 完成里程碑
+ * @param id
+ * @param projectId
+ */
+export const finishProjectMileStone = async (id: string, projectId: ProjectId) => {
+  return await request.get(
+    {
+      url: '/project-milestone/finish',
+      params: { id, projectId }
+    },
+    '/business'
+  )
+}

+ 4 - 1
client/src/views/OaSystem/components/UserOrgTree/index.vue

@@ -13,7 +13,6 @@
 <script setup lang="ts">
 import { useQuery } from '@tanstack/vue-query'
 import { getAllUserSimpleByList } from '@/service/system'
-import { filterNodeMethod } from '@/utils/tree'
 
 defineOptions({ name: 'UserOrgTree' })
 const props = defineProps<{ modelValue: any }>()
@@ -23,4 +22,8 @@ const receiveUserId = computed({
   set: (nVal: string) => emit('update:modelValue', nVal)
 })
 const { data: allUserList } = useQuery(['list-all-user-simple'], getAllUserSimpleByList)
+const filterNodeMethod = (value, data) => {
+  if (!data) return
+  return data.label.includes(value)
+}
 </script>

+ 361 - 0
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/ProjectMember.vue

@@ -0,0 +1,361 @@
+<script setup lang="ts">
+import { useMutation, useQuery } from '@tanstack/vue-query'
+import { addMember, deleteMember, getProjectMember } from '@/service/project'
+import { ProjectMember, ProjectQueryParams } from '@/interface/project'
+import { ElMessage } from 'element-plus'
+import UserOrgTree from '@/views/OaSystem/components/UserOrgTree/index.vue'
+
+const defaultMemberData = {
+  workerId: '',
+  responsibility: ''
+}
+/**
+ * 项目成员
+ */
+const props = defineProps<ProjectQueryParams>()
+const { projectId } = toRefs(props)
+const workerRelationBool = ref<boolean>(false)
+const projectWorkerRelation = ref<ProjectMember>(defaultMemberData)
+const { data: projectWorkerRelationList, refetch } = useQuery(
+  [getProjectMember.name, unref(projectId)],
+  async () => await getProjectMember(unref(projectId))
+)
+
+const { mutate: handleDelete } = useMutation(deleteMember, {
+  onSuccess: () => {
+    void refetch()
+    ElMessage({
+      message: '删除成功',
+      type: 'success'
+    })
+  }
+})
+
+const { mutate: handleAdd } = useMutation(addMember, {
+  onSuccess: () => {
+    void refetch()
+    workerRelationBool.value = false
+    projectWorkerRelation.value = defaultMemberData
+    ElMessage({
+      message: '添加成员成功',
+      type: 'success'
+    })
+  }
+})
+
+const handleDeleteMember = (workerId: string) => {
+  handleDelete({
+    workerId,
+    projectId: unref(projectId)
+  })
+}
+
+const handleAddMember = () => {
+  handleAdd({
+    ...projectWorkerRelation.value,
+    projectId: unref(projectId)
+  })
+}
+</script>
+
+<template>
+  <div class="xmcyBox">
+    <div class="project-member-header">
+      <div class="total"
+        >当前项目参与人员:<b>{{ projectWorkerRelationList?.length ?? 0 }}人</b></div
+      >
+      <div class="handle-add-btn" @click="workerRelationBool = true">+ 新增人员</div>
+    </div>
+    <ul>
+      <li v-for="(item, index) in projectWorkerRelationList" :key="index">
+        <div class="imgBox">
+          <img src="../../../../../../assets/imgs/avatar.gif" />
+          <i class="icon"></i>
+        </div>
+        <div class="userInfo">
+          <div class="titleBox">
+            <h4>{{ item['workerName'] }}</h4>
+            <span class="tag" v-if="item['responsibility']">{{ item['responsibility'] }}</span>
+          </div>
+          <p class="dept">{{ item['deptName'] }}</p>
+          <p class="txt">电话:{{ item['mobile'] }}</p>
+        </div>
+
+        <el-popconfirm
+          title="确定删除该成员?"
+          width="180px"
+          @confirm="handleDeleteMember(item.workerId)"
+        >
+          <template #reference>
+            <i class="del_icon">
+              <Icon icon="ep:delete" />
+            </i>
+          </template>
+        </el-popconfirm>
+      </li>
+    </ul>
+  </div>
+  <div class="subModalByRightBottom" style="width: 30vw" v-show="workerRelationBool">
+    <ul class="tab">
+      <li>添加成员</li>
+    </ul>
+    <span class="close_icon" @click="workerRelationBool = false"></span>
+    <div class="tabContent">
+      <div class="tabContentOne" style="padding: 20px 30px">
+        <div class="formBox">
+          <el-form label-width="120px">
+            <el-row>
+              <el-col :span="24">
+                <el-form-item label="人员选择">
+                  <UserOrgTree v-model="projectWorkerRelation['workerId']" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="24">
+              <el-col :span="24">
+                <el-form-item label="相关职责">
+                  <el-input v-model="projectWorkerRelation['responsibility']" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </div>
+      </div>
+    </div>
+    <div class="btnGroup">
+      <el-button type="default" @click="workerRelationBool = false">取消</el-button>
+      <el-button type="primary" @click="handleAddMember">保存</el-button>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.xmcyBox {
+  padding-top: 10px;
+
+  .project-member-header {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 10px;
+
+    .total {
+      font-size: 18px;
+      color: #626b70;
+
+      > b {
+        color: #121518;
+      }
+    }
+
+    .handle-add-btn {
+      background: #fb961f;
+      border-radius: 4px 4px 4px 4px;
+      font-size: 16px;
+      color: #ffffff;
+      padding: 4px 8px;
+      cursor: pointer;
+    }
+  }
+
+  > ul {
+    display: grid;
+    grid-template-columns: repeat(5, 1fr);
+    gap: 10px;
+
+    > li {
+      position: relative;
+      border: 1px solid #dee0e3;
+      padding: 10px;
+      border-radius: 4px;
+
+      &:not(.operation) {
+        position: relative;
+
+        > .del_icon {
+          position: absolute;
+          top: calc(50% - 7px);
+          right: 10px;
+          display: none;
+          cursor: pointer;
+        }
+
+        &:hover {
+          background-color: #f2f7ff;
+
+          > .del_icon {
+            display: block;
+          }
+        }
+
+        > div {
+          display: inline-block;
+          vertical-align: top;
+        }
+
+        > .imgBox {
+          width: 50px;
+          height: 50px;
+          position: relative;
+
+          > img {
+            width: 100%;
+            height: 100%;
+            border-radius: 50%;
+          }
+
+          margin-right: 8px;
+
+          > .icon {
+            position: absolute;
+            width: 15px;
+            height: 15px;
+            right: -1px;
+            top: -1px;
+            border-radius: 50%;
+            background: url('../../../../../../assets/imgs/OA/sex_icon1.png') no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+
+        > .userInfo {
+          > .titleBox {
+            display: flex;
+            align-items: center;
+
+            > h4 {
+              color: #2d333c;
+              font-size: 24px;
+            }
+
+            > .tag {
+              position: absolute;
+              top: 0;
+              right: 0;
+              display: inline-block;
+              background: #528dff;
+              box-shadow: 0px 2px 0px 0px rgba(82, 141, 255, 0.2);
+              border-radius: 0px 4px 0px 40px;
+              margin-left: 5px;
+              padding: 4px 10px 4px 16px;
+              font-size: 14px;
+              color: #fff;
+            }
+          }
+
+          > .dept {
+            color: #2d333c;
+            font-size: 18px;
+            margin-top: 15px;
+            padding-bottom: 5px;
+          }
+
+          > .txt {
+            font-size: 14px;
+            color: #69717c;
+            padding: 2px 0px;
+          }
+        }
+      }
+
+      &.operation {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        height: 166px;
+        cursor: pointer;
+
+        > i {
+          width: 20px;
+          height: 20px;
+          background: url('../../../../../../assets/imgs/OA/add_xmcy_icon.png') no-repeat;
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
+}
+
+.subModalByRightBottom {
+  position: fixed;
+  right: 0px;
+  bottom: 15px;
+  width: 45vw;
+  border-radius: 10px;
+  background: #ffffff;
+  box-shadow: -8px 0px 23px 0px rgba(203, 212, 221, 0.37);
+  opacity: 1;
+  padding-left: 0px;
+  padding-right: 0px;
+  padding-bottom: 20px;
+
+  > .close_icon {
+    display: block;
+    position: absolute;
+    right: 20px;
+    top: 20px;
+    width: 20px;
+    height: 20px;
+    background: url('../../../../../../assets/imgs/OA/layout/close.png') no-repeat;
+    background-size: 100% 100%;
+    cursor: pointer;
+  }
+
+  > ul {
+    border-bottom: 1px solid #dee0e3;
+    padding: 0px 10px;
+
+    > li {
+      display: inline-block;
+      font-size: 18px;
+      margin: 0px 20px;
+      padding: 15px 0px;
+      border-bottom: 2px solid transparent;
+      cursor: pointer;
+
+      &.active,
+      &:hover {
+        font-weight: bold;
+        color: #2e77e6;
+        border-bottom-color: #2e77e6;
+      }
+    }
+  }
+
+  > .tabContent {
+    overflow-y: auto;
+    height: calc(100% - 120px);
+
+    > div {
+      padding: 20px 30px;
+    }
+
+    > .tabContentOne {
+      padding-top: 0px;
+
+      h4 {
+        color: #2d333c;
+        margin: 35px 0px 5px 0px;
+      }
+    }
+
+    :deep(.el-form-item) {
+      flex-direction: column;
+    }
+
+    :deep(.el-form-item__label) {
+      justify-content: flex-start;
+      color: #505a69;
+      font-size: 16px;
+    }
+
+    :deep(.el-form-item--default) {
+      margin-bottom: 5px;
+    }
+  }
+
+  > .btnGroup {
+    text-align: right;
+    padding-right: 20px;
+  }
+}
+</style>

+ 62 - 0
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/ProjectMileStone.vue

@@ -0,0 +1,62 @@
+<script setup lang="ts">
+import { ProjectQueryParams } from '@/interface/project'
+import { useMutation, useQuery } from '@tanstack/vue-query'
+import { finishProjectMileStone, getProjectMileStone } from '@/service/project'
+
+const stoneStatus = ['未开始', '进行中', '已完成']
+
+/**
+ * 项目里程碑
+ */
+const props = defineProps<ProjectQueryParams>()
+const { projectId } = toRefs(props)
+const { data: projectMilestoneList, refetch } = useQuery(
+  [getProjectMileStone.name, unref(projectId)],
+  async () => await getProjectMileStone(unref(projectId))
+)
+const { mutate: handleFinish, isLoading } = useMutation(finishProjectMileStone, {
+  onSuccess: () => {
+    refetch()
+    ElMessage({
+      message: '里程碑修改成功!',
+      type: 'success'
+    })
+  }
+})
+</script>
+
+<template>
+  <div class="xmjdBox">
+    <div class="tableBox">
+      <el-table :data="projectMilestoneList" style="width: 100%">
+        <el-table-column type="sortnum" label="序号" width="100" />
+        <el-table-column prop="name" label="里程碑名称" />
+        <el-table-column prop="planFinishTime" label="计划完成时间" width="200" />
+        <el-table-column label="实际完成时间" prop="actualFinishTime" width="200" />
+        <el-table-column label="状态" prop="state" width="200">
+          <template #default="scope">
+            {{ stoneStatus?.[Number(scope.row?.state ?? 0)] ?? '-' }}
+          </template>
+        </el-table-column>
+        >
+        <el-table-column label="操作" width="200">
+          <template #default="scope">
+            <el-button
+              v-if="scope.row?.state !== 2"
+              @click="handleFinish(scope.row.id)"
+              type="text"
+              size="small"
+              >完成
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.xmjdBox {
+  margin-top: 20px;
+}
+</style>

+ 0 - 121
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/index.scss

@@ -106,127 +106,6 @@
           }
         }
       }
-
-      > .xmcyBox {
-        padding-top: 20px;
-
-        > ul {
-          display: grid;
-          grid-template-columns: repeat(4, 1fr);
-          gap: 10px;
-
-          > li {
-            border: 1px solid #DEE0E3;
-            padding: 10px;
-
-            &:not(.operation) {
-              position: relative;
-
-              > .del_icon {
-                position: absolute;
-                top: 10px;
-                right: 10px;
-                display: inline-block;
-                display: none;
-              }
-
-              &:hover {
-                background-color: #F2F7FF;
-                cursor: pointer;
-
-                > .del_icon {
-                  display: block;
-                }
-              }
-
-              > div {
-                display: inline-block;
-                vertical-align: top;
-              }
-
-              > .imgBox {
-                width: 50px;
-                height: 50px;
-                position: relative;
-
-                > img {
-                  width: 100%;
-                  height: 100%;
-                  border-radius: 50%;
-                }
-
-                margin-right: 8px;
-
-                > .icon {
-                  position: absolute;
-                  width: 15px;
-                  height: 15px;
-                  right: -1px;
-                  top: -1px;
-                  border-radius: 50%;
-                  background: url("../../../../../../assets/imgs/OA/sex_icon1.png") no-repeat;
-                  background-size: 100% 100%;
-                }
-
-              }
-
-              > .userInfo {
-                > .titleBox {
-                  display: flex;
-                  align-items: center;
-
-                  > h4 {
-                    color: #2D333C;
-                    font-size: 24px;
-                  }
-
-                  > .tag {
-                    margin-left: 5px;
-                    display: inline-block;
-                    padding: 0px 10px;
-                    border-radius: 20px;
-                    color: #2E77E6;
-                    border: 1px solid #2E77E6;
-                    font-size: 14px;
-                  }
-                }
-
-                > .dept {
-                  color: #2D333C;
-                  font-size: 18px;
-                  margin-top: 15px;
-                  padding-bottom: 5px;
-                }
-
-                > .txt {
-                  font-size: 14px;
-                  color: #69717C;
-                  padding: 2px 0px;
-                }
-              }
-            }
-
-            &.operation {
-              display: flex;
-              align-items: center;
-              justify-content: center;
-              height: 166px;
-              cursor: pointer;
-
-              > i {
-                width: 20px;
-                height: 20px;
-                background: url("../../../../../../assets/imgs/OA/add_xmcy_icon.png") no-repeat;
-                background-size: 100% 100%;
-              }
-            }
-          }
-        }
-      }
-
-      > .xmjdBox {
-        margin-top: 20px;
-      }
     }
   }
 }

+ 186 - 403
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/index.vue

@@ -2,17 +2,13 @@
   <div class="_ProjectDetail_xmxx">
     <div class="header">
       <ul>
-        <template v-if="projectDetail['children'].length > 0">
+        <template v-if="originData?.['children'].length > 0">
+          <li :class="{ active: isMaster }" @click="switchProjectInfo(_mainProjectId)">主项目</li>
           <li
-            :class="{ active: projectInfoIndex === 0 }"
-            @click="switchProjectInfo(projectDetail, 0)"
-            >主项目
-          </li>
-          <li
-            v-for="(item, index) in projectDetail['children']"
-            :class="{ active: projectInfoIndex === index + 1 }"
+            v-for="({ id }, index) in originData['children']"
+            :class="{ active: id === currentProjectId }"
             :key="index"
-            @click="switchProjectInfo(item, index + 1)"
+            @click="switchProjectInfo(id)"
             >子项目
           </li>
         </template>
@@ -23,300 +19,171 @@
         <li :class="{ active: currentIndex === 2 }" @click="switchHandle(2)">项目阶段</li>
       </ul>
     </div>
-    <div class="content">
+    <div class="content" v-if="projectDetail != null">
       <template v-if="currentIndex === 0">
-        <template v-if="projectInfoIndex === 0">
-          <div class="xmxxBox">
-            <div class="tableBox">
-              <h4 :style="{ marginTop: projectDetail['children'].length > 0 ? '15px' : '0px' }">
-                <i class="icon"></i>基本信息
-              </h4>
-              <table>
-                <tr>
-                  <td class="th">项目经理:</td>
-                  <td>
-                    <UserOrgTree
-                      class="form-item-disable-style"
-                      v-model="projectDetail['xmjlId']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">责任部门</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['zrbm']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                </tr>
-                <tr>
-                  <td class="th">项目类别:</td>
-                  <td>
-                    <ProjectTypeTree
-                      class="form-item-disable-style"
-                      v-model="projectDetail['projectTypeId']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">立项时间:</td>
-                  <td>
-                    <el-date-picker
-                      class="form-item-disable-style"
-                      style="width: 100%"
-                      :disabled="true"
-                      v-model="projectDetail['lxsj']"
-                      type="date"
-                      @change="($evt) => (projectDetail['lxsj'] = $evt)"
-                      placeholder="请选择立项时间"
-                    />
-                  </td>
-                </tr>
-                <tr>
-                  <td class="th">销售人员:</td>
-                  <td>
-                    <UserOrgTree
-                      class="form-item-disable-style"
-                      v-model="projectDetail['xsryId']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">行政区:</td>
-                  <td>
-                    <DistrictTree
-                      class="form-item-disable-style"
-                      v-model="projectDetail['xzqdm']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                </tr>
-                <tr>
-                  <td class="th">行业:</td>
-                  <td>
-                    <el-select
-                      class="form-item-disable-style"
-                      style="width: 100%"
-                      v-model="projectDetail['hyId']"
-                      placeholder="请选择行业"
-                      :disabled="!editor"
-                    >
-                      <el-option
-                        v-for="item in industryList"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                      />
-                    </el-select>
-                  </td>
-                  <td class="th">预估金额(元):</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['ygje']"
-                      :disabled="!editor"
+        <div class="xmxxBox">
+          <div class="tableBox">
+            <h4 :style="{ marginTop: (originData?.['children'].length ?? 0) > 0 ? '15px' : '0px' }">
+              <i class="icon"></i>基本信息
+            </h4>
+            <table>
+              <tr v-show="!isMaster">
+                <td class="th">项目名称:</td>
+                <td>
+                  <ProjectTypeTree
+                    class="form-item-disable-style"
+                    v-model="projectDetail['xmmc']"
+                    :disabled="!editor"
+                  />
+                </td>
+                <td class="th">项目编号:</td>
+                <td>
+                  <el-input
+                    class="form-item-disable-style"
+                    v-model="projectDetail['xmbh']"
+                    :disabled="!editor"
+                  />
+                </td>
+              </tr>
+              <tr>
+                <td class="th">项目经理:</td>
+                <td>
+                  <UserOrgTree
+                    class="form-item-disable-style"
+                    v-model="projectDetail['xmjlId']"
+                    :disabled="!editor"
+                  />
+                </td>
+                <td class="th">责任部门</td>
+                <td>
+                  <el-input
+                    class="form-item-disable-style"
+                    v-model="projectDetail['zrbm']"
+                    :disabled="!editor"
+                  />
+                </td>
+              </tr>
+              <tr v-show="isMaster">
+                <td class="th">项目类别:</td>
+                <td>
+                  <ProjectTypeTree
+                    class="form-item-disable-style"
+                    v-model="projectDetail['projectTypeId']"
+                    :disabled="!editor"
+                  />
+                </td>
+                <td class="th">立项时间:</td>
+                <td>
+                  <el-date-picker
+                    class="form-item-disable-style"
+                    style="width: 100%"
+                    :disabled="true"
+                    v-model="projectDetail['lxsj']"
+                    type="date"
+                    @change="($evt) => (projectDetail['lxsj'] = $evt)"
+                    placeholder=" "
+                  />
+                </td>
+              </tr>
+              <tr v-show="isMaster">
+                <td class="th">销售人员:</td>
+                <td>
+                  <UserOrgTree
+                    class="form-item-disable-style"
+                    v-model="projectDetail['xsryId']"
+                    :disabled="!editor"
+                  />
+                </td>
+                <td class="th">行政区:</td>
+                <td>
+                  <DistrictTree
+                    class="form-item-disable-style"
+                    v-model="projectDetail['xzqdm']"
+                    :disabled="!editor"
+                  />
+                </td>
+              </tr>
+              <tr v-show="isMaster">
+                <td class="th">行业:</td>
+                <td>
+                  <el-select
+                    class="form-item-disable-style"
+                    style="width: 100%"
+                    v-model="projectDetail['hyId']"
+                    placeholder="请选择行业"
+                    :disabled="!editor"
+                  >
+                    <el-option
+                      v-for="item in industryList"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
                     />
-                  </td>
-                </tr>
-                <tr>
-                  <td class="th">预估工期(天):</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['yggq']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">归属公司:</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['belongCompanyname']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                </tr>
-              </table>
-            </div>
-            <div class="tableBox">
-              <h4><i class="icon"></i>验收信息</h4>
-              <table>
-                <tr>
-                  <td class="th">验收时间:</td>
-                  <td>{{ projectDetail['yssj'] }}</td>
-                  <td class="th">验收单有效性:</td>
-                  <td>{{ projectDetail['ysdyxx'] }}</td>
-                </tr>
-              </table>
-            </div>
-            <div class="tableBox">
-              <h4><i class="icon"></i>产值信息</h4>
-              <table>
-                <tr>
-                  <td class="th">是否签合同:</td>
-                  <td>{{ projectDetail['isSign'] === 0 ? '否' : '是' }}</td>
-                  <td class="th">本项目成本(万):</td>
-                  <td>{{ projectDetail['xmcbys'] }}</td>
-                </tr>
-                <tr>
-                  <td class="th">本项目产值(万):</td>
-                  <td>{{ projectDetail['outputValue'] }}</td>
-                  <td class="th">利益分成(公司/部门):</td>
-                  <td>{{ projectDetail['shareRatio'] }}</td>
-                </tr>
-              </table>
-            </div>
+                  </el-select>
+                </td>
+                <td class="th">归属公司:</td>
+                <td>
+                  <el-input
+                    class="form-item-disable-style"
+                    v-model="projectDetail['belongCompanyname']"
+                    :disabled="!editor"
+                  />
+                </td>
+              </tr>
+              <tr>
+                <td class="th">预估工期(天):</td>
+                <td>
+                  <el-input
+                    class="form-item-disable-style"
+                    v-model="projectDetail['yggq']"
+                    :disabled="!editor"
+                  />
+                </td>
+                <td class="th">预估金额(元):</td>
+                <td>
+                  <el-input
+                    class="form-item-disable-style"
+                    v-model="projectDetail['ygje']"
+                    :disabled="!editor"
+                  />
+                </td>
+              </tr>
+            </table>
           </div>
-        </template>
-        <template v-else>
-          <div class="xmxxBox" v-if="currentIndex === 0">
-            <div class="tableBox">
-              <h4 :style="{ marginTop: projectDetail['children'].length > 0 ? '15px' : '0px' }">
-                <i class="icon"></i>基本信息
-              </h4>
-              <table>
-                <tr>
-                  <td class="th">项目经理:</td>
-                  <td>
-                    <UserOrgTree
-                      class="form-item-disable-style"
-                      v-model="projectDetail['xmjlId']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">责任部门</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['zrbm']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                </tr>
-                <tr>
-                  <td class="th">项目编号:</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['xmbh']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                  <td class="th">预估工期(天):</td>
-                  <td>
-                    <el-input
-                      class="form-item-disable-style"
-                      v-model="projectDetail['yggq']"
-                      :disabled="!editor"
-                    />
-                  </td>
-                </tr>
-              </table>
-            </div>
-            <div class="tableBox">
-              <h4><i class="icon"></i>产值信息</h4>
-              <table>
-                <tr>
-                  <td class="th">是否签合同:</td>
-                  <td>{{ projectDetail['isSign'] === 1 ? '是' : '否' }}</td>
-                  <td class="th">本项目成本(万):</td>
-                  <td>{{ projectDetail['xmcbys'] }}</td>
-                </tr>
-                <tr>
-                  <td class="th">本项目产值(万):</td>
-                  <td>{{ projectDetail['outputValue'] }}</td>
-                  <td class="th">利益分成(公司/部门):</td>
-                  <td>{{ projectDetail['shareRatio'] }}</td>
-                </tr>
-              </table>
-            </div>
+          <div class="tableBox" v-show="isMaster">
+            <h4><i class="icon"></i>验收信息</h4>
+            <table>
+              <tr>
+                <td class="th">验收时间:</td>
+                <td>{{ projectDetail['yssj'] }}</td>
+                <td class="th">验收单有效性:</td>
+                <td>{{ projectDetail['ysdyxx'] }}</td>
+              </tr>
+            </table>
           </div>
-        </template>
-      </template>
-      <div class="xmcyBox" v-else-if="currentIndex === 1">
-        <ul>
-          <li v-for="(item, index) in projectWorkerRelationList" :key="index">
-            <div class="imgBox">
-              <img src="../../../../../../assets/imgs/avatar.gif" />
-              <i class="icon"></i>
-            </div>
-            <div class="userInfo">
-              <div class="titleBox">
-                <h4>{{ item['workerName'] }}</h4>
-                <span class="tag" v-if="item['responsibility']">{{ item['responsibility'] }}</span>
-              </div>
-              <p class="dept">{{ item['deptName'] }}</p>
-              <p class="txt">毕业院校:浙江水利水电职业技术学院</p>
-              <p class="txt">学历:大专</p>
-              <p class="txt">电话:{{ item['mobile'] }}</p>
-            </div>
-
-            <el-popconfirm
-              title="确定删除该成员?"
-              width="180px"
-              @confirm="deleWorkerRelationHandle(item)"
-            >
-              <template #reference>
-                <i class="del_icon">
-                  <Icon icon="ep:delete" />
-                </i>
-              </template>
-            </el-popconfirm>
-          </li>
-          <li class="operation" @click="workerRelationBool = true">
-            <i class="icon"></i>项目成员
-          </li>
-        </ul>
-      </div>
-      <div class="xmjdBox" v-else>
-        <div class="tableBox">
-          <el-table :data="projectMilestoneList" style="width: 100%">
-            <el-table-column type="index" label="序号" width="100" />
-            <el-table-column prop="title" label="里程碑名称" />
-            <el-table-column prop="creatorName" label="计划完成时间" width="120" />
-            <el-table-column label="实际完成时间" prop="createTime" width="200" />
-            <el-table-column label="状态" prop="adddate" width="200" />
-            <el-table-column label="操作" width="180">
-              <template></template>
-            </el-table-column>
-          </el-table>
-        </div>
-      </div>
-    </div>
-    <div class="subModalByRightBottom" style="width: 30vw" v-show="workerRelationBool">
-      <ul class="tab">
-        <li>添加成员</li>
-      </ul>
-      <span class="close_icon" @click="workerRelationBool = false"></span>
-      <div class="tabContent">
-        <div class="tabContentOne" style="padding: 20px 30px">
-          <div class="formBox">
-            <el-form label-width="120px">
-              <el-row>
-                <el-col :span="24">
-                  <el-form-item label="人员选择">
-                    <UserOrgTree v-model="projectWorkerRelation['workerId']" :disabled="!editor" />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-              <el-row :gutter="24">
-                <el-col :span="24">
-                  <el-form-item label="相关职责">
-                    <el-input
-                      type="textarea"
-                      v-model="projectWorkerRelation['responsibility']"
-                      rows="6"
-                    />
-                  </el-form-item>
-                </el-col>
-              </el-row>
-            </el-form>
+          <div class="tableBox">
+            <h4><i class="icon"></i>产值信息</h4>
+            <table>
+              <tr>
+                <td class="th">是否签合同:</td>
+                <td>{{ projectDetail['isSign'] === 0 ? '否' : '是' }}</td>
+                <td class="th">本项目成本(万):</td>
+                <td>{{ projectDetail['xmcbys'] }}</td>
+              </tr>
+              <tr>
+                <td class="th">本项目产值(万):</td>
+                <td>{{ projectDetail['outputValue'] }}</td>
+                <td class="th">利益分成(公司/部门):</td>
+                <td>{{ projectDetail['shareRatio'] }}</td>
+              </tr>
+            </table>
           </div>
         </div>
-      </div>
-      <div class="btnGroup">
-        <el-button type="default" @click="workerRelationBool = false">取消</el-button>
-        <el-button type="primary" @click="bindProjectWorkerRelation">保存</el-button>
-      </div>
+      </template>
+      <!--项目成员-->
+      <ProjectMember v-else-if="currentIndex === 1" :projectId="_mainProjectId" />
+      <!--项目里程碑-->
+      <ProjectMileStone v-else :projectId="_mainProjectId" />
     </div>
     <AddSubProject
       :projectId="_mainProjectId"
@@ -327,7 +194,6 @@
   </div>
 </template>
 <script setup lang="ts">
-import { watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import request from '@/config/axios'
 import { industryList } from '@/utils/business'
@@ -337,44 +203,46 @@ import ProjectTypeTree from '@/views/OaSystem/components/ProjectTypeTree/index.v
 import AddSubProject from '@/views/OaSystem/projectCenter/projectDetail/components/xmxx/AddSubProject.vue'
 import { useQuery } from '@tanstack/vue-query'
 import { getProjectWithChildrenById } from '@/service/project'
+import ProjectMember from '@/views/OaSystem/projectCenter/projectDetail/components/xmxx/ProjectMember.vue'
+import ProjectMileStone from '@/views/OaSystem/projectCenter/projectDetail/components/xmxx/ProjectMileStone.vue'
 
 defineOptions({ name: 'XmxxComp' })
-const $props = defineProps({
-  detailData: {
-    type: Object,
-    default: () => ({})
-  },
-  editor: {
-    type: Boolean,
-    default: true
-  }
-})
-const route = useRoute()
-const query = route.query
-let _mainProjectId = query.id
+const props = defineProps<{ editor: boolean }>()
+const { editor } = toRefs(props)
+const { query } = useRoute()
+const { id: _mainProjectId = '', subId = '' } = query
+const currentProjectId = ref<string>(_mainProjectId)
+const isMaster = computed(() => _mainProjectId === currentProjectId.value)
 /***
  * 查询项目和子项目详情数据
  * **/
 const subProjectShow = ref<boolean>(false)
-const projectDetail = ref({
-  id: '',
-  children: []
-})
+let projectDetail = ref<any>({})
 
 /***
  * 查询项目详情
  * **/
-const { refetch } = useQuery(
+const { data: originData, refetch } = useQuery(
   [getProjectWithChildrenById.name, _mainProjectId],
   async () => await getProjectWithChildrenById(_mainProjectId as string),
   {
     onSuccess: (tData) => {
-      projectDetail.value = tData
+      switchProjectInfo(subId !== '' ? subId : _mainProjectId, tData)
     },
     enabled: (_mainProjectId ?? '') !== ''
   }
 )
 
+// 选择当前查看项目
+const switchProjectInfo = (switchId: string, data = originData.value) => {
+  currentProjectId.value = switchId
+  if (switchId === _mainProjectId) {
+    projectDetail.value = data
+  } else {
+    projectDetail.value = data?.children?.find(({ id }) => id === switchId)
+  }
+}
+
 const currentIndex = ref<number>(0)
 const switchHandle: (i: number) => void = (i: number) => {
   currentIndex.value = i
@@ -389,19 +257,9 @@ const handleCloseAddSub = () => {
   subProjectShow.value = false
 }
 const onHandleSuccess = () => {
-  console.log(' add sub successed!')
   refetch()
 }
 
-/**
- * 切换子项目信息
- * **/
-const projectInfoIndex = ref<number>(0)
-const switchProjectInfo = (item: any, key: number): void => {
-  _mainProjectId = item.id
-  projectInfoIndex.value = key
-  initDetailAjax()
-}
 /**
  * 项目编辑保存成操作
  * **/
@@ -416,82 +274,7 @@ const saveProjectHandle = (): void => {
     }
   })
 }
-/**
- * 项目阶段/项目里程碑
- * **/
-const projectMilestoneList = shallowRef()
-const queryMilestoneList = (): void => {
-  const urlApi = `/project-milestone/list?projectId=${_mainProjectId}`
-  request.get({ url: urlApi }, '/business').then((resultData) => {
-    projectWorkerRelationList.value = resultData
-  })
-}
-/**
- * 项目成员相关接口(查询、新增、删除)
- * **/
-const projectWorkerRelation = ref<{
-  workerId: string
-  responsibility: string
-}>({
-  workerId: '',
-  responsibility: ''
-})
-const workerRelationBool = ref<boolean>(false)
-const projectWorkerRelationList = shallowRef()
-const queryProjectWorkerRelation = (): void => {
-  const urlApi = `/project-worker-relation/list?projectId=${_mainProjectId}`
-  request.get({ url: urlApi }, '/business').then((resultData) => {
-    projectWorkerRelationList.value = resultData
-  })
-}
-const bindProjectWorkerRelation = (): void => {
-  const urlApi = `/project-worker-relation/bind`
-  const sendData = {
-    ...projectWorkerRelation.value,
-    projectId: _mainProjectId
-  }
-  request.post({ url: urlApi, data: sendData }, '/business').then((resultData) => {
-    if (!resultData) {
-      ElMessage({
-        message: '添加成员成功',
-        type: 'success'
-      })
-      queryProjectWorkerRelation()
-      workerRelationBool.value = false
-    }
-  })
-}
-const deleWorkerRelationHandle = (item): void => {
-  const urlApi = `/project-worker-relation/unbind`
-  const sendData = {
-    projectId: _mainProjectId,
-    workerId: item['workerId']
-  }
-  request.post({ url: urlApi, data: sendData }, '/business').then((resultData) => {
-    if (!resultData) {
-      ElMessage({
-        message: '删除成功',
-        type: 'success'
-      })
-      queryProjectWorkerRelation()
-    }
-  })
-}
-const initDetailAjax = () => {
-  queryProjectWorkerRelation()
-  queryMilestoneList()
-}
-watch(
-  () => $props.detailData,
-  (nVal) => {
-    _mainProjectId = nVal.id
-    // queryProjectWithChild()
-    initDetailAjax()
-  },
-  {
-    immediate: true
-  }
-)
+
 defineExpose({
   saveProjectHandle,
   handleAddSubProject