Ver código fonte

项目功能优化

songxy 1 ano atrás
pai
commit
c63e459a13

+ 1 - 1
client/.env.dev

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=false
 
 # 请求路径
-VITE_BASE_URL='http://10.10.10.7:48080'
+VITE_BASE_URL='http://localhost:48080'
 
 # 上传路径
 VITE_UPLOAD_URL='http://10.10.10.7:48080/admin-api/infra/file/upload'

+ 109 - 0
client/src/components/TreeSelectV2/index.vue

@@ -0,0 +1,109 @@
+import { log } from 'console'; import { getNotifyTaskDetail } from '@/api/pay/notify'; import {
+number } from 'vue-types'; import { array } from 'vue-types';
+<template>
+  <div class="treeSelectV2">
+    <el-popover placement="bottom" trigger="click" :teleported="false">
+      <template #reference>
+        <el-input v-model="xzqdmName" readonly :disabled="disabled">
+          <template #suffix>
+            <el-icon><ArrowUp /></el-icon>
+          </template>
+        </el-input>
+      </template>
+      <template #default>
+        <template v-if="filterMethod != null">
+          <el-input v-model="filterName" @input="onQueryFilter" style="margin-bottom: 10px" />
+        </template>
+        <el-tree-v2
+          ref="treeRef"
+          @node-click="nodeClickHandle"
+          style="width: 100%"
+          :filter-method="filterMethod"
+          :props="props"
+          :data="data"
+          :render-after-expand="expend"
+        />
+      </template>
+    </el-popover>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ElTreeV2 } from 'element-plus'
+
+defineOptions({
+  name: 'TreeSelectV2'
+})
+const $props = defineProps({
+  data: {
+    type: Array,
+    default: () => []
+  },
+  modelValue: {
+    type: [String, Number]
+  },
+  props: {
+    type: Object,
+    default: () => null
+  },
+  disabled: {
+    type: Boolean,
+    default: false
+  },
+  expend: {
+    type: Boolean,
+    default: false
+  },
+  filterMethod: {
+    type: Function,
+    default: () => null
+  }
+})
+const $emit = defineEmits<{
+  (e: 'update:modelValue', v: any): void
+}>()
+const xzqdmName = ref<string>()
+const filterName = ref<string>()
+
+const treeRef = ref<InstanceType<typeof ElTreeV2>>()
+const onQueryFilter = (query: string) => {
+  treeRef.value!.filter(query)
+}
+const nodeClickHandle = (data) => {
+  xzqdmName.value = data['name']
+  $emit('update:modelValue', data['id'])
+}
+watchEffect(() => {
+  const item = treeRef.value?.getNode($props.modelValue)
+  console.log(item)
+  if (item) {
+    xzqdmName.value = item['label']
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.treeSelectV2 {
+  position: relative;
+  :deep(.el-popper) {
+    width: 100% !important;
+    left: 0px !important;
+    right: 0px !important;
+  }
+  :deep(.el-input__suffix) {
+    .el-icon {
+      color: var(--el-select-input-color);
+      font-size: var(--el-select-input-font-size);
+      transition: transform var(--el-transition-duration);
+      transform: rotateZ(0);
+      cursor: pointer;
+    }
+  }
+  :deep(.is-focus) {
+    .el-input__suffix .el-icon {
+      transform: rotateZ(-180deg);
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 10 - 10
client/src/config/axios/index.ts

@@ -35,25 +35,25 @@ export default {
     const res = await request({ method: 'POST', ...option }, prefix)
     return res.data as unknown as T
   },
-  postOriginal: async (option: any) => {
-    const res = await request({ method: 'POST', ...option })
+  postOriginal: async (option: any, prefix?: string) => {
+    const res = await request({ method: 'POST', ...option }, prefix)
     return res
   },
-  delete: async <T = any>(option: any) => {
-    const res = await request({ method: 'DELETE', ...option })
+  delete: async <T = any>(option: any, prefix?: string) => {
+    const res = await request({ method: 'DELETE', ...option }, prefix)
     return res.data as unknown as T
   },
-  put: async <T = any>(option: any) => {
-    const res = await request({ method: 'PUT', ...option })
+  put: async <T = any>(option: any, prefix?: string) => {
+    const res = await request({ method: 'PUT', ...option }, prefix)
     return res.data as unknown as T
   },
-  download: async <T = any>(option: any) => {
-    const res = await request({ method: 'GET', responseType: 'blob', ...option })
+  download: async <T = any>(option: any, prefix?: string) => {
+    const res = await request({ method: 'GET', responseType: 'blob', ...option }, prefix)
     return res as unknown as Promise<T>
   },
-  upload: async <T = any>(option: any) => {
+  upload: async <T = any>(option: any, prefix?: string) => {
     option.headersType = 'multipart/form-data'
-    const res = await request({ method: 'POST', ...option })
+    const res = await request({ method: 'POST', ...option }, prefix)
     return res as unknown as Promise<T>
   }
 }

+ 25 - 0
client/src/utils/business.ts

@@ -0,0 +1,25 @@
+/***
+ * 行业类别
+ * **/
+export interface IndustryInterface {
+  label: string
+  value: number
+}
+export const industryList = ref<IndustryInterface[]>([
+  {
+    label: '政府行业',
+    value: 3
+  },
+  {
+    label: 'IT行业',
+    value: 2
+  },
+  {
+    label: '公司内部项目',
+    value: 1
+  },
+  {
+    label: '其他',
+    value: 4
+  }
+])

+ 96 - 0
client/src/views/OaSystem/components/DistrictTree/index copy.vue

@@ -0,0 +1,96 @@
+<template>
+  <div class="treeSelectV2">
+    <el-popover placement="bottom" trigger="click" :teleported="false">
+      <template #reference>
+        <el-input v-model="xzqdmName" readonly>
+          <template #suffix>
+            <el-icon><ArrowUp /></el-icon>
+          </template>
+        </el-input>
+      </template>
+      <template #default>
+        <el-input @input="onQueryFilter" style="margin-bottom: 10px" />
+        <el-tree-v2
+          ref="treeRef"
+          @node-click="nodeClickHandle"
+          style="width: 100%"
+          filterable
+          :filter-method="filterNodeMethod"
+          :props="{ label: 'name', value: 'id' }"
+          :data="areaTree.list"
+          :render-after-expand="false"
+        />
+      </template>
+    </el-popover>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import request from '@/config/axios'
+
+defineOptions({
+  name: 'DistrictTree'
+})
+const $props = defineProps<{
+  modelValue: any
+}>()
+const $emit = defineEmits<{
+  (e: 'input', v: any): void
+}>()
+/**
+ * 获取行政区tree结构数据
+ */
+const xzqdm = ref<string | number>()
+const xzqdmName = ref<string>()
+
+const treeRef = ref<InstanceType<typeof ElTreeV2>>()
+const areaTree = shallowReactive({ list: [] })
+const onQueryFilter = (query: string) => {
+  treeRef.value!.filter(query)
+}
+const filterNodeMethod = (value: string, node: TreeNode) => {
+  if (!node) return
+  return node.name!.includes(value)
+}
+const queryAreaTreeAjax = (): void => {
+  const urlApi = `/system/area/tree`
+  request.get({ url: urlApi }).then((result) => {
+    areaTree.list = result
+    xzqdm.value = $props.modelValue
+  })
+}
+const nodeClickHandle = (data, node) => {
+  xzqdmName.value = data['name']
+  $emit('input', data['id'])
+}
+onMounted(() => {
+  queryAreaTreeAjax()
+})
+</script>
+
+<style lang="scss" scoped>
+.treeSelectV2 {
+  position: relative;
+  :deep(.el-popper) {
+    width: 100% !important;
+    left: 0px !important;
+    right: 0px !important;
+  }
+  :deep(.el-input__suffix) {
+    .el-icon {
+      color: var(--el-select-input-color);
+      font-size: var(--el-select-input-font-size);
+      transition: transform var(--el-transition-duration);
+      transform: rotateZ(0);
+      cursor: pointer;
+    }
+  }
+  :deep(.is-focus) {
+    .el-input__suffix .el-icon {
+      transform: rotateZ(-180deg);
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 77 - 0
client/src/views/OaSystem/components/DistrictTree/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="treeSelectV2">
+    <TreeSelectV2
+      :data="areaTree.list"
+      :props="{ label: 'name', value: 'id' }"
+      v-model="xzqdm"
+      :filter-method="filterNodeMethod"
+      :disabled="disabled"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import TreeSelectV2 from '@/components/TreeSelectV2/index.vue'
+import request from '@/config/axios'
+
+defineOptions({
+  name: 'DistrictTree'
+})
+const $props = defineProps({
+  disabled: {
+    type: Boolean,
+    default: false
+  },
+  modelValue: {
+    type: [Number, String],
+    default: null
+  }
+})
+const $emit = defineEmits<{
+  (e: 'update:modelValue', v: any): void
+}>()
+const xzqdm = ref<string | number>(Number($props.modelValue))
+const areaTree = shallowReactive({ list: [] })
+const filterNodeMethod = (value: string, node: any) => {
+  if (!node) return
+  return node.name!.includes(value)
+}
+const queryAreaTreeAjax = (): void => {
+  const urlApi = `/system/area/tree`
+  request.get({ url: urlApi }).then((result) => {
+    areaTree.list = result
+  })
+}
+onMounted(() => {
+  queryAreaTreeAjax()
+})
+watch(xzqdm, (nVal) => {
+  $emit('update:modelValue', nVal)
+})
+</script>
+
+<style lang="scss" scoped>
+.treeSelectV2 {
+  position: relative;
+  :deep(.el-popper) {
+    width: 100% !important;
+    left: 0px !important;
+    right: 0px !important;
+  }
+  :deep(.el-input__suffix) {
+    .el-icon {
+      color: var(--el-select-input-color);
+      font-size: var(--el-select-input-font-size);
+      transition: transform var(--el-transition-duration);
+      transform: rotateZ(0);
+      cursor: pointer;
+    }
+  }
+  :deep(.is-focus) {
+    .el-input__suffix .el-icon {
+      transform: rotateZ(-180deg);
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 48 - 0
client/src/views/OaSystem/components/ProjectTypeTree/index.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-tree-select
+    v-model="projectType"
+    style="width: 100%"
+    node-key="id"
+    filterable
+    check-strictly
+    :filter-node-method="filterNodeMethod"
+    :props="{ label: 'name' }"
+    :data="projectTypeTree.list"
+    :render-after-expand="false"
+  />
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import request from '@/config/axios'
+
+defineOptions({
+  name: 'ProjectTypeTree'
+})
+const $props = defineProps<{
+  modelValue: any
+}>()
+const $emit = defineEmits<{
+  (e: 'update:modelValue', v: any): void
+}>()
+/**
+ * 获取行政区tree结构数据
+ */
+const projectType = ref<string | number>()
+const projectTypeTree = shallowReactive({ list: [] })
+const filterNodeMethod = (value, data) => {
+  if (!data) return
+  return data.name.includes(value)
+}
+const queryAreaTreeAjax = (): void => {
+  const urlApi = `/project-type/tree`
+  request.get({ url: urlApi }, '/business').then((result) => {
+    projectTypeTree.list = result
+    projectType.value = $props.modelValue
+  })
+}
+queryAreaTreeAjax()
+watch(projectType, (nVal: any) => {
+  $emit('update:modelValue', nVal)
+})
+</script>

+ 57 - 0
client/src/views/OaSystem/components/UserOrgTree/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-tree-select
+    style="width: 100%"
+    v-model="receiveUserId"
+    :data="allUserList.list"
+    :filter-node-method="filterNodeMethod"
+    :render-after-expand="false"
+    filterable
+  />
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import request from '@/config/axios'
+import { handleTree } from '@/utils/tree'
+
+defineOptions({
+  name: 'UserOrgTree'
+})
+const $props = defineProps<{
+  modelValue: any
+}>()
+const $emit = defineEmits<{
+  (e: 'update:modelValue', v: any): void
+}>()
+const receiveUserId = ref<any[]>($props.modelValue)
+const allUserList = shallowReactive({ list: [] })
+let sourceAllUserList: any[] = []
+
+const getAllUserSimpleByList = (): void => {
+  const urlApi = '/system/dept/list-all-user-simple'
+  const params = {}
+  request.get({ url: urlApi, params }).then((result) => {
+    sourceAllUserList = handleTree(
+      result.map((item) => {
+        return {
+          label: item['name'],
+          value: item['id'],
+          parentId: item['parentId'],
+          children: item['children']
+        }
+      }),
+      'value'
+    )
+    allUserList.list = sourceAllUserList
+    receiveUserId.value = $props.modelValue
+  })
+}
+const filterNodeMethod = (value, data) => {
+  if (!data) return
+  return data.label.includes(value)
+}
+getAllUserSimpleByList()
+watch(receiveUserId, (nVal: any) => {
+  $emit('update:modelValue', nVal)
+})
+</script>

+ 2 - 1
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/index.scss

@@ -54,11 +54,12 @@
         td {
           border: 1px solid #E8EBF1;
           padding: 8px 20px;
+          width: 500px;
           &.th {
             background-color: #F2F4F8;
             color: #3F4855;
             text-align: right;
-            width: 180px;
+            width: 190px;
           }
         }
       }

+ 19 - 2
client/src/views/OaSystem/projectCenter/projectDetail/components/xmxx/index.vue

@@ -22,7 +22,14 @@
               </td>
               <td class="th">立项时间:</td>
               <td>
-                <el-input v-model="projectDetail['lxsj']" :disabled="!editor" />
+                <el-date-picker
+                  style="width: 100%"
+                  :disabled="true"
+                  v-model="projectDetail['lxsj']"
+                  type="date"
+                  @change="($evt) => (projectDetail['lxsj'] = $evt)"
+                  placeholder="请选择立项时间"
+                />
               </td>
             </tr>
             <tr>
@@ -38,7 +45,7 @@
             <tr>
               <td class="th">项目类别:</td>
               <td>
-                <ProjectTypeTree :disabled="!editor" />
+                <ProjectTypeTree v-model="projectDetail['projectTypeId']" :disabled="!editor" />
               </td>
               <td class="th">行业:</td>
               <td>
@@ -188,6 +195,16 @@ const currentIndex = ref<number>(0)
 const switchHandle: (i: number) => void = (i: number) => {
   currentIndex.value = i
 }
+
+const addProjectHandle = (): void => {
+  const urlApi = `/project`
+  request.put({ url: urlApi, data: projectDetail }, '/business').then((result) => {
+    projectWorkerRelationList.value = result
+  })
+}
+defineExpose({
+  addProjectHandle
+})
 </script>
 <style lang="scss" scoped>
 @import './index.scss';

+ 6 - 1
client/src/views/OaSystem/projectCenter/projectDetail/projectDetail.scss

@@ -20,6 +20,7 @@
             align-items: center;
             >h2 {
               font-size: 28px;
+              max-width: 760px;
             }
             >.subTitle {
               color: #8B969C;
@@ -242,7 +243,7 @@
     }
     >.tabContent {
       overflow-y: auto;
-      height: calc(100% - 60px);
+      height: calc(100% - 120px);
       >div {
         padding: 20px 30px;
       }
@@ -352,6 +353,10 @@
         }
       }
     }
+    >.btnGroup {
+      text-align: right;
+      padding-right: 20px;
+    }
   }
   .subProjectModalBox {
     >.close_icon {

+ 20 - 4
client/src/views/OaSystem/projectCenter/projectDetail/projectDetail.vue

@@ -52,18 +52,23 @@
           <p>
             <i class="icon"></i>
             <span class="title">责任部门:</span>
-            <span class="value">17500</span>
+            <span class="value">{{ projectDetail['xmjl'] }}</span>
           </p>
           <p>
             <i class="icon"></i>
             <span class="title">项目经理:</span>
-            <span class="value">17500</span>
+            <span class="value">{{ projectDetail['zrbm'] }}</span>
           </p>
         </div>
       </div>
     </div>
     <div class="detailContent">
-      <component :is="currentComponent" :detailData="projectDetail" :editor="isEditorProject" />
+      <component
+        :is="currentComponent"
+        :detailData="projectDetail"
+        ref="dynamicRef"
+        :editor="isEditorProject"
+      />
     </div>
     <div class="subProjectModalBox" v-show="subProjectShow">
       <ul class="tab">
@@ -171,6 +176,7 @@ import { useRoute } from 'vue-router'
 import request from '@/config/axios'
 
 defineOptions({ name: 'ProjectDetail' })
+const dynamicRef = ref<Component>()
 const XmxxComp = defineAsyncComponent(() => {
   return import('./components/xmxx/index.vue')
 })
@@ -212,10 +218,20 @@ const queryProjectDetail = async (id: any): Promise<void> => {
   const result = await request.get({ url: urlApi, params: sendData }, '/business')
   projectDetail.value = result
 }
+const queryProjectWithChild = async (id: any): Promise<void> => {
+  const urlApi = `/project-with-children`
+  const sendData = {
+    id: id
+  }
+  const result = await request.get({ url: urlApi, params: sendData }, '/business')
+  console.log('result~~~~~~~~~~')
+  console.log(result)
+}
 const route = useRoute()
 const query = route.query
 if (query.id) {
   queryProjectDetail(query.id)
+  queryProjectWithChild(query.id)
 }
 /***
  * 添加子项目
@@ -254,7 +270,6 @@ const addWorkerRelation = (): void => {
 }
 const submitProjectChild = () => {
   addProjectChild()
-  addWorkerRelation()
 }
 /**
  * 项目编辑
@@ -262,6 +277,7 @@ const submitProjectChild = () => {
 const isEditorProject = ref<boolean>(true)
 const editorProject: () => void = (): void => {
   isEditorProject.value = !isEditorProject.value
+  dynamicRef.value?.addProjectHandle()
 }
 </script>
 <style lang="scss" scoped>