Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

jzh 1 tahun lalu
induk
melakukan
4f8f3df496

+ 36 - 4
client/src/components/ExportToExcel/index.vue

@@ -18,26 +18,58 @@ interface IProp {
     e: { r: number; c: number } // 合并的结束单元格
   }[] // 合并单元格列表
   colsWidth?: { wch: number }[] // 列宽
+  title?: string // 标题
 }
 // 定义组件props
 const props = defineProps<IProp>()
-
+const titleStyle = {
+  font: {
+    bold: true,
+    size: 20,
+    color: { rgb: '#000000' }, // 设置颜色
+    name: 'Arial' // 设置字体为Arial
+  },
+  alignment: {
+    horizontal: 'center', // 水平居中
+    vertical: 'middle' // 垂直居中
+  },
+  '!important': true // 添加 !important 规则
+}
 // 导出Excel函数
 const exportToExcel = () => {
   // 从props获取数据
   const data = props.data
 
+  // 如果有标题,添加标题
+  if (props.title) {
+    // 创建一个包含样式信息的单元格对象(样式未生效,不知道是什么原因)
+    const titleCell = { v: props.title, s: titleStyle }
+    // 将单元格对象添加到数据中
+    data.unshift([titleCell])
+  }
+
   // 创建一个工作簿
   const wb = XLSX.utils.book_new()
 
   // 创建一个工作表
   const ws = XLSX.utils.aoa_to_sheet(data)
 
-  // 合并表头的单元格,定义多个合并范围
-  ws['!merges'] = props.mergeRanges ?? []
+  // 如果有标题, 则在第一行插入标题
+  if (props.title) {
+    // 添加标题并合并单元格
+    const mergeRange = { s: { r: 0, c: 0 }, e: { r: 0, c: data[1].length - 1 } } // 合并单元格范围
+    ws['!merges'] = [mergeRange] // 合并单元格
+  }
+
+  // 多个合并范围 合并单元格
+  if (props.mergeRanges) {
+    ws['!merges'] = props.mergeRanges
+  }
 
   // 设置列宽,定义多个列宽
-  ws['!cols'] = props.colsWidth ?? []
+  if (props.colsWidth) {
+    ws['!cols'] = props.colsWidth ?? []
+  }
 
   // 将工作表添加到工作簿中
   XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')

+ 0 - 25
client/src/router/modules/remaining.ts

@@ -273,23 +273,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
           title: '档案详情'
         }
       },
-      {
-        path: 'dailyStatisticAll',
-        component: () => import('@/views/OaSystem/personnelManagement/dailyStatisticAll/index.vue'),
-        name: 'dailyStatisticAll',
-        meta: {
-          title: '日报统计'
-        }
-      },
-      {
-        path: 'weeklyStatisticAll',
-        component: () =>
-          import('@/views/OaSystem/personnelManagement/weeklyStatisticAll/index.vue'),
-        name: 'weeklyStatisticAll',
-        meta: {
-          title: '周报统计'
-        }
-      },
       {
         path: 'projectStatistics',
         component: () => import('@/views/OaSystem/personnelManagement/ProjectStatistics/index.vue'),
@@ -322,14 +305,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
           title: '发出的日志'
         }
       },
-      {
-        path: 'completionRate',
-        component: () => import('@/views/OaSystem/personnelManagement/CompletionRate/index.vue'),
-        name: 'completionRate',
-        meta: {
-          title: '部门完成率'
-        }
-      },
       {
         path: 'newCustomer',
         component: () => import('@/views/OaSystem/marketCenter/khxjPage/index.vue'),

+ 27 - 61
client/src/views/OaSystem/attendanceCenter/dep.vue

@@ -32,9 +32,9 @@
           <ExportToExcel
             v-if="porps.deptsName == '公司'"
             :data="excelDataSource"
-            :file-name="`${moment(fromParams.month).format('YYYY-MM')}考勤统计.xlsx`"
-            :mergeRanges="mergeRanges"
+            :file-name="`${moment(fromParams.month).format('YYYY年MM月')}考勤统计.xlsx`"
             :colsWidth="colsWidth"
+            :title="`${moment(fromParams.month).format('YYYY年MM月')}-考勤数据表`"
           />
           <el-button type="success" style="background-color: #05ce9e" v-if="false">
             <img src="@/assets/imgs/OA/kq/kqqrIcon.png" class="mr-8px" alt="" />
@@ -296,7 +296,7 @@ const swStateClick = (row, item, index) => {
 }
 // 考勤对应的字典
 const attendanceTypeObj = {
-  1: '', // 出勤
+  1: '', // 出勤
   2: '迟', // '迟到'
   3: '早', // '早退'
   4: '旷', // '旷工'
@@ -329,73 +329,39 @@ const columList = [
 // 考勤表格
 const excelDataSource = ref<any>()
 // 合并的单元格
-const mergeRanges = [
-  { s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 合并第一行的前两列
-  { s: { r: 0, c: 1 }, e: { r: 1, c: 1 } } // 合并第二行的前两列
-]
+// const mergeRanges = [
+// { s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 合并第一行的前两列
+// { s: { r: 0, c: 1 }, e: { r: 1, c: 1 } } // 合并第二行的前两列
+// ]
 // 列宽设置
-const colsWidth = [{ wch: 30 }, { wch: 10 }]
+const colsWidth = ref([{ wch: 30 }, { wch: 10 }, { wch: 8 }])
 // 获取导出的数据表格
 const getExcelTable = (attendanceData) => {
-  const tableHead = [
-    [
-      '部门',
-      '姓名',
-      ...tableHeadList.value.map((item) => item.day),
-      '旷工(天)',
-      '迟到(次)',
-      '早退(次)',
-      '出差(天)',
-      '事假(天)',
-      '调休(天)',
-      '病假(天)',
-      '年假(天)',
-      '产假(天)',
-      '其他(天)'
-    ],
-    [
-      '',
-      '',
-      ...tableHeadList.value.map((item) => item.dayOfWeek),
-      '',
-      '',
-      '',
-      '',
-      '',
-      '',
-      '',
-      '',
-      '',
-      ''
-    ]
-  ]
-  const tableBody = attendanceData.map((item) => {
-    const data = item.attendArray.map((arr, i) => {
+  const tableHead = [['部门', '姓名', '时间', ...tableHeadList.value.map((item) => item.day)]]
+  // 日期单元格列宽设置
+  colsWidth.value = [...colsWidth.value, ...tableHeadList.value.map(() => ({ wch: 3 }))]
+  const tableBody: any = []
+  for (let index = 0; index < attendanceData.length; index++) {
+    const item = attendanceData[index]
+    const data1 = item.attendArray.map((arr, i) => {
       // 判断是不是工作日
       const isworkday = tableHeadList.value[i].isworkday
       if (!isworkday) return ''
       // 判断有没有数据
       if (!arr.swAttendanceStatus) return ''
-      return `${attendanceTypeObj[arr.swAttendanceStatus] ?? ''} / ${
-        attendanceTypeObj[arr.xwAttendanceStatus] ?? ''
-      }`
+      return attendanceTypeObj[arr.swAttendanceStatus] ?? ''
     })
-    return [
-      item.deptName,
-      item.nickName,
-      ...data,
-      item.kg,
-      item.cd,
-      item.zt,
-      item.cc,
-      item.sj,
-      item.tx,
-      item.bj,
-      item.nj,
-      item.cj,
-      item.qt
-    ]
-  })
+    const data2 = item.attendArray.map((arr, i) => {
+      const isworkday = tableHeadList.value[i].isworkday
+      if (!isworkday) return ''
+      if (!arr.swAttendanceStatus) return ''
+      return attendanceTypeObj[arr.xwAttendanceStatus] ?? ''
+    })
+    tableBody.push(
+      [item.deptName, item.nickName, '上午', ...data1],
+      [item.deptName, item.nickName, '下午', ...data2]
+    )
+  }
 
   return [...tableHead, ...tableBody]
 }

+ 201 - 50
client/src/views/OaSystem/personnelManagement/CompletionRate/index.vue

@@ -1,68 +1,219 @@
-<script lang="ts" setup>
-/**
- * @description 部门完成率
- */
-const tableData1 = [
-  {
-    id: 1,
-    date: '89.47%',
-    name: '职能板块',
-    children: [
-      {
-        id: 31,
-        date: '91.67%',
-        name: '人力中心'
-      },
-      {
-        id: 32,
-        date: '88.89%',
-        name: '财务中心'
-      }
-    ]
-  },
-  {
-    id: 2,
-    date: '77.95%',
-    name: '空间规划研究院',
-    children: [
-      {
-        id: 31,
-        date: '91.67%',
-        name: '人力中心'
-      },
-      {
-        id: 32,
-        date: '88.89%',
-        name: '财务中心'
-      }
-    ]
-  }
-]
-</script>
 <template>
   <div class="oa-sys-list-view">
-    <div class="title">部门完成率</div>
-    <div>
+    <div class="title">
+      <div>各板块/部门周报完成率</div>
+      <div>
+        <el-date-picker
+          v-model="selectDate"
+          type="month"
+          placeholder="选择月"
+          style="width: 200px"
+        />
+      </div>
+    </div>
+    <div class="table-box">
       <el-table
-        :data="tableData1"
+        class="el-table"
+        :data="dataSource"
         style="width: 100%"
         row-key="id"
         border
-        lazy
-        :load="load"
+        stripe
+        :load="loading"
         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
         default-expand-all
+        header-row-class-name="table-header"
+        :span-method="objectSpanMethod"
       >
-        <el-table-column prop="name" label="部门" />
-        <el-table-column prop="date" label="完成率" />
+        <el-table-column prop="deptFather" label="板块" align="center" class-name="dept-father" />
+        <el-table-column label="板块完成率" align="center">
+          <template #default="scope">
+            <el-tag effect="plain" type="primary">{{ computedRate(scope.row.fatherRate) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="deptName" label="部门" align="center" />
+        <el-table-column label="部门完成率" align="center">
+          <template #default="scope">
+            <el-progress :percentage="computedRate(scope.row.fillRate, true)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" align="center">
+          <template #default="scope">
+            <el-button type="primary" plain round @click="handleView(scope.row)">查看</el-button>
+          </template>
+        </el-table-column>
       </el-table>
     </div>
   </div>
 </template>
+<script lang="ts" setup>
+/**
+ * @description 部门完成率
+ */
+import request from '@/config/axios'
+import { handleTree } from '@/utils/tree'
+import moment from 'moment'
+
+const loading = ref(true)
+const dataSource = ref([])
+const selectDate = ref(moment().format('YYYY-MM'))
+
+onMounted(() => {
+  getRateData()
+})
+
+watch(selectDate, () => {
+  getRateData()
+})
+
+// 获取完成率数据
+const getRateData = async () => {
+  loading.value = true
+  request
+    .get({
+      url: '/adm/reportStatistics/query-dept-report-statistics',
+      params: {
+        year: moment(selectDate.value).format('YYYY'),
+        month: moment(selectDate.value).format('M')
+      }
+    })
+    .then((res) => {
+      ElMessage.success(`${moment(selectDate.value).format('YYYY-MM')} 数据查询成功`)
+      const rateTree = handleTree(res, 'deptId')
+      dataSource.value = setRateData(rateTree)
+      loading.value = false
+    })
+    .catch(() => {
+      ElMessage.error('查询失败,请稍后重试!')
+    })
+  // console.log('rateData', rateData)
+}
+// 处理完成率数据
+const rowSpanList: any = ref([])
+const setRateData = (rateTree) => {
+  const dataSource = rateTree?.[0].children ?? []
+  const rataData: any = []
+  for (let index = 0; index < dataSource.length; index++) {
+    const data = dataSource[index]
+    if (data.children && data.children.length > 0) {
+      for (const item of data.children) {
+        rataData.push({
+          deptFather: data.deptName,
+          fatherRate: data.fillRate,
+          deptName: item.deptName,
+          fillRate: item.fillRate,
+          deptId: item.deptId
+        })
+      }
+    } else {
+      rataData.push({
+        deptFather: '职能部门',
+        fatherRate: rateTree[0].fillRate,
+        deptName: data.deptName,
+        fillRate: data.fillRate,
+        deptId: data.deptId
+      })
+    }
+  }
+  rowSpanList.value = getDeptFatherCountsAndIndices(rataData)
+  return rataData
+}
+// 获取相同内容的数组下标
+const getDeptFatherCountsAndIndices = (data) => {
+  const countsAndIndices: any = []
+  let currentDeptFather = null
+  let startIndex = 0
+  for (let i = 0; i < data.length; i++) {
+    const item = data[i]
+    if (item.deptFather === currentDeptFather) {
+      continue
+    } else {
+      if (currentDeptFather !== null) {
+        countsAndIndices.push({
+          deptFather: currentDeptFather,
+          count: i - startIndex,
+          startIndex
+        })
+      }
+      currentDeptFather = item.deptFather
+      startIndex = i
+    }
+  }
+  if (currentDeptFather !== null) {
+    countsAndIndices.push({
+      deptFather: currentDeptFather,
+      count: data.length - startIndex,
+      startIndex
+    })
+  }
+  return countsAndIndices
+}
+// 设置表格单元格布局,按照相同数组下标进行分类
+const objectSpanMethod = ({ rowIndex, columnIndex }) => {
+  if (columnIndex === 0 || columnIndex === 1) {
+    const rowSpans = rowSpanList.value
+    const isSplit = rowSpans.find((item) => item.startIndex == rowIndex)
+    if (isSplit) {
+      const { count, startIndex, deptFather } = isSplit
+      return { rowspan: count, colspan: 1 }
+    } else {
+      return {
+        rowspan: 0,
+        colspan: 0
+      }
+    }
+  }
+}
+// 计算完成率
+const computedRate = (value, number?) => {
+  if (value === 0 || value === null) return 0
+  if (number) return (value * 100).toFixed(2)
+  return (value * 100).toFixed(2) + '%'
+}
+// 点击查看跳转详情
+const { push } = useRouter()
+const handleView = (row) => {
+  // console.log('点击查看跳转详情', row)
+  // 还需判断是跳转周报还是日报
+  push(`/oaSystem/DeptCenter/weeklyStatistic?deptId=${row.deptId}`)
+}
+</script>
 <style scoped lang="scss">
 .title {
+  height: 32px;
+  font-weight: bold;
+  font-size: 24px;
+  color: #121518;
+  display: flex;
+  justify-content: space-between;
+}
+
+.table-box {
+  position: relative;
+  width: 100%;
+  margin-top: 20px;
+  flex: 1;
+  .el-table {
+    width: 100%;
+    height: calc(100vh - 280px);
+    display: block;
+    overflow: hidden;
+    // background-color: none;
+  }
+}
+:deep(.table-header) {
+  th.el-table__cell {
+    font-weight: 400 !important;
+    color: #121518 !important;
+    background-color: #cfdae2 !important;
+  }
+}
+:deep(.dept-father) {
+  font-family:
+    Microsoft YaHei,
+    Microsoft YaHei;
+  font-weight: bold;
   font-size: 16px;
-  font-weight: 600;
-  margin-bottom: 10px;
+  color: #121518;
 }
 </style>

+ 4 - 1
client/src/views/OaSystem/personnelManagement/dailyCenter/OaCalendar.vue

@@ -97,7 +97,10 @@ onMounted(() => {
   })
   // 订阅提交(暂存)事件,执行重置事件
   PubsubService.subscribe('sendReportHandle-init', () => {
-    initDailyReportData()
+    initDailyReportData().then(() => {
+      // 提交后触发点击事件刷新右边的显示内容
+      $emit('click', dayReportMap.value[moment(todayValue.value).format('YYYY-MM-DD')])
+    })
   })
 })
 

+ 3 - 5
client/src/views/OaSystem/personnelManagement/dailyCenter/dailyCenter.vue

@@ -5,11 +5,9 @@
       <div class="calendarBox">
         <OaCalendar @click="dateOnChange" />
       </div>
-      <div class="contentBox" v-if="writeData?.isLog && !writeData?.isLog.isTemp">
-        <DetailBox :detail="writeData?.isLog" />
-      </div>
-      <div class="contentBox" v-else>
-        <EditorDetail :writeData="writeData" />
+      <div class="contentBox">
+        <DetailBox :detail="writeData?.isLog" v-if="writeData?.isLog && !writeData?.isLog.isTemp" />
+        <EditorDetail :writeData="writeData" v-else />
       </div>
     </div>
   </div>

+ 5 - 4
client/src/views/OaSystem/personnelManagement/dailyCenter/editorDetail.vue

@@ -173,10 +173,11 @@ const sendReportHandle = async (isTemp) => {
     message.success('日志发送成功')
     // 发布提交(暂存)事件
     PubsubService.publish('sendReportHandle-init', {})
-    isUpDate.value = true
-    if (isUpDate.value) {
-      updateLogId.value = result?.data ?? ''
-    }
+    // 禁止更新
+    // isUpDate.value = true
+    // if (isUpDate.value) {
+    //   updateLogId.value = result?.data ?? ''
+    // }
   } else if (result?.msg && isTemp) {
     message.success('日志暂存成功')
     // 发布提交(暂存)事件

+ 5 - 1
client/src/views/OaSystem/personnelManagement/weeklyCenter/WeekCalendar.vue

@@ -171,7 +171,11 @@ onMounted(async () => {
   await setDayOfMonth()
   // 订阅提交(暂存)事件,执行重置事件
   PubsubService.subscribe('sendWeeklyReportHandle-init', () => {
-    getLogListPlus()
+    getLogListPlus().then(() => {
+      // 提交后触发点击事件刷新右边的显示内容
+      const index = checkWeek.value.index
+      weekOnChange(sourceList.value[index], index)
+    })
   })
 })
 

+ 2 - 1
client/src/views/OaSystem/personnelManagement/weeklyCenter/editorDetail.vue

@@ -188,7 +188,8 @@ const sendReportHandle = async (isTemp) => {
     message.success('周报发送成功')
     // 发布提交(暂存)事件
     PubsubService.publish('sendWeeklyReportHandle-init', {})
-    isUpDate.value = true
+    // 禁止更新
+    // isUpDate.value = true
   } else if (result?.msg && isTemp) {
     message.success('周报暂存成功')
     // 发布提交(暂存)事件

+ 3 - 5
client/src/views/OaSystem/personnelManagement/weeklyCenter/weeklyCenter.vue

@@ -5,11 +5,9 @@
       <div class="calendarBox">
         <WeekCalendar :onChange="onChange" />
       </div>
-      <div class="contentBox" v-if="writeData.isLog && !writeData.isLog.isTemp">
-        <DetailBox :detail="writeData.isLog" />
-      </div>
-      <div class="contentBox" v-else>
-        <EditorDetail :weekDate="writeData.weekDate" :writeData="writeData" />
+      <div class="contentBox">
+        <DetailBox :detail="writeData.isLog" v-if="writeData.isLog && !writeData.isLog.isTemp" />
+        <EditorDetail :weekDate="writeData.weekDate" :writeData="writeData" v-else />
       </div>
     </div>
   </div>

+ 8 - 0
client/src/views/OaSystem/personnelManagement/weeklyStatistic/index.vue

@@ -109,7 +109,15 @@ const jumpToMyLog = (row: any): void => {
   // )
 }
 
+const { currentRoute } = useRouter()
+
 onMounted(() => {
+  // 如果有传部门id
+  const urlDeptId = currentRoute.value.query.deptId
+  if (urlDeptId) {
+    // console.log('urlDeptId', urlDeptId)
+    deptId.value = urlDeptId
+  }
   // 获取周报数据
   getWeeklyStatisticData()
 })

+ 2 - 2
client/src/views/OaSystem/projectCenter/projectBook/deptProject.vue

@@ -47,7 +47,7 @@
       </div>
       <div class="form" style="width: unset">
         <span class="formSpan">合同:</span>
-        <el-radio-group v-model="queryParams.isSign">
+        <el-radio-group v-model="queryParams.isSign" @change="searchHandle">
           <el-radio>全部</el-radio>
           <el-radio label="1">已签订</el-radio>
           <el-radio label="0">未签订</el-radio>
@@ -55,7 +55,7 @@
       </div>
       <div class="form" style="width: unset">
         <span class="formSpan">项目状态:</span>
-        <el-radio-group v-model="queryParams.xmzt">
+        <el-radio-group v-model="queryParams.xmzt" @change="searchHandle">
           <el-radio :label="1">进行中({{ process }})</el-radio>
           <el-radio :label="4">已验收({{ accepted }})</el-radio>
           <el-radio :label="2">已结项({{ finished }})</el-radio>

+ 1 - 0
zjugis-workflow/src/main/resources/templates/HandlerCaseCenter/index.ftl

@@ -43,6 +43,7 @@ javascripts=['/HandlerCaseCenter/js/config.js','/HandlerCaseCenter/js/index.js']
         }
         .handlercasecenter .dialog-pane li {
             margin-left: 60px;
+            vertical-align: top;
         }
         .handlercasecenter .dialog-pane li .title {
             font-weight: bold;

+ 62 - 6
zjugis-workflow/src/main/resources/templates/HandlerCaseCenter/js/index.js

@@ -42,23 +42,79 @@ var windowid;
 			}
 		}
 	}
-	//初始化流程弹框
-	function initProcessDialog(){
 
+    function createProcessByModalId(templateId, modalName) {
+        z.ui.loading(true, true);
         z.ui.ajax({
-            url: window.data.workflowUrl + "/HandlerCaseCenter/getCalBackActivity",
+            url: "/TProcessEngine/add",
             data: {
-                "activityInstanceId": data.activityInsId,
-                "flowInsId": data.flowInsId
+                "templateId": templateId
+            },
+            complete: function () {
+                z.ui.loading(false);
             },
             success: function (res) {
+                //给办件中心发消息
+                z.webcontainer.sendMessageCrossWindow(window.top, "handlerCaseCenter", {
+                    "type": "reload",
+                    "status": "1" // 代办tab
+                });
+                //给首页办件中心发消息
+                z.webcontainer.sendMessageCrossWindow(window.top, "indexModuleList", {
+                    "type": "reload",
+                    "status": "1" // 代办tab
+                });
+                openWebContainerTab("新建办件", modalName, res);
+            }
+        });
+    }
+
+    function openWebContainerTab(title, tip, url) {
+        z.webcontainer.openTab({
+            id: 'workflowMain_' + tip,
+            title: title,
+            tip: tip,
+            url: url,
+            iconclass: 'fa fa-file'
+        });
+    }
+	//初始化流程弹框
+	function initProcessDialog(){
 
+        z.ui.ajax({
+            url: window.data.workflowUrl + "/TFlowTemplateApi/selectFlowTemplateTree",
+            success: function (res) {
+				if(res.data && res.data.length > 0){
+					processItemHtml(res.data[0]['children'])
+				}
 			}
         });
         function processItemHtml(arr){
-
+        	if(arr && arr.length === 0) throw new Error("流程表单Tree不存在!");
+        	var htmlStr = "<ul>";
+        	for(var i = 0; i < arr.length; i++){
+        		var item = arr[i]
+        		htmlStr += `<li><p class="title">${item["name"]}</p>`;
+        		if(item['children'] && item['children'].length > 0){
+        			htmlStr += `<div class="process_list">`;
+        			for(var j = 0; j < item['children'].length; j++){
+        				var cItem = item['children'][j]
+						htmlStr += `<div class="process_item" data-id="${cItem['id']}" data-name="${cItem['name']}">${cItem['name']}</div>`
+					}
+					htmlStr += '</div>';
+				}
+				htmlStr += '</li>'
+			}
+			htmlStr += "</ul>"
+			$("#process-dialog").html(htmlStr);
+        	$(".process_item").click(function(){
+        		var id = $(this).attr("data-id");
+        		var name = $(this).attr("data-name");
+                createProcessByModalId(id, name)
+			})
 		}
 	}
+    initProcessDialog()
 	//绑定事件
 	function bindEvents() {
 		$("[name=flowCatalogBtn]").on({