فهرست منبع

提交工时成本统计模块重构

Lijy 7 ماه پیش
والد
کامیت
84044557a5

+ 19 - 1
fhKeeper/formulahousekeeper/timesheet/src/components/echartsEchar.vue

@@ -18,6 +18,14 @@ export default {
       type: Object,
       default: null,
     },
+    widthHtval: {
+      type: [String, Boolean],
+      default: false
+    },
+    clickOnTheEvent: { // 是否开启点击事件
+      type: [Boolean],
+      default: false
+    }
   },
   components: {},
   data() {
@@ -37,7 +45,12 @@ export default {
   watch: {
     options() {
       if (this.myChart) {
-        this.myChart.setOption(this.options);
+        if (this.widthHtval) {
+          this.myChart.resize({
+            width: this.widthHtval
+          })
+        }
+        this.myChart.setOption(this.options, { notMerge: true });
       }
     },
   },
@@ -47,6 +60,11 @@ export default {
   mounted() {
     this.myChart = echarts.init(document.getElementById(this.uuid));
     this.myChart.setOption(this.options);
+    if (this.clickOnTheEvent) {
+      this.myChart.getZr().on('click', params => {
+        this.$emit('chartClickEvents', { params, myChart: this.myChart })
+      })
+    }
   },
   methods: {
     idGen() {

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet/src/components/selectProject.vue

@@ -1,6 +1,6 @@
 <template>
   <el-select v-model="selectedValue" :size="size" filterable remote @change="updateValue" :placeholder="placeholder"
-    :clearable="clearable" :remote-method="projectListRemotemethod" :loading="newProjectListLoading"
+    :clearable="clearable" :remote-method="projectListRemotemethod" :loading="newProjectListLoading" :style="`width: ${width}`"
     @visible-change="visibleChangeProjrct" @focus="peojectFocus">
     <div ref="mySelectProject" class="select-project-class">
       <el-option v-for="item in newProjectList" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode"
@@ -34,6 +34,10 @@ export default {
     clearable: {
       type: [Boolean],
       default: false
+    },
+    width: {
+      type: [String],
+      default: '200px'
     }
   },
   data() {

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 993 - 1515
fhKeeper/formulahousekeeper/timesheet/src/views/project/cost.vue


+ 29 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/project/costData.js

@@ -0,0 +1,29 @@
+/**
+ * 柱状图图表数据
+ * 初始化数据,使用时使用 解构赋值:如:xxx.xAxis = { ...xxx.xAxis, data: xxxxx }
+ */
+export const barChartOptions = {
+  grid: {
+    top: "10%", // 上间距
+    right: "5%", // 右间距
+    bottom: "10%", // 下间距
+    left: "5%", // 左间距
+  },
+  xAxis: {
+    type: "category",
+    data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+    axisLabel: {
+      interval: 0,
+      rotate: 20,
+    },
+  },
+  yAxis: {
+    type: "value",
+  },
+  series: [
+    {
+      data: [120, 200, 150, 80, 70, 110, 130],
+      type: "bar",
+    },
+  ],
+};

+ 477 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/project/costReportExport.vue

@@ -0,0 +1,477 @@
+<template>
+  <el-dialog title="报表导出" :visible.sync="value" top="7.8vh" customClass="customWidth" width="500px"
+    :before-close="handleClose">
+    <el-form ref="exportFormRef" :model="exportParam">
+      <!-- 头部 -->
+      <div style="text-align: center;margin-bottom: 20px">
+        <el-radio-group size="medium" v-model="exportParam.reportType">
+          <el-radio-button :label="0">常规报表</el-radio-button>
+          <el-radio-button :label="1">月度报表</el-radio-button>
+        </el-radio-group>
+      </div>
+      <!-- 常规报表 -->
+      <template v-if="exportParam.reportType == 0">
+
+        <el-form-item prop="projectCategoryId" :label="$t('projectclassification')"
+          v-if="singleChoiceType == $t('projectclassification') || singleChoiceType == $t('other.project')">
+          <el-select v-model="exportParam.projectCategoryId" :placeholder="$t('classificationitems')" clearable
+            style="width:350px;" filterable="true" @change="filterCategory">
+            <el-option v-for="item in categoryList" :key="item.id" :label="item.name" :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item prop="projectId" :label="'选择项目'" v-if="!['人员', '项目分类', '部门', '主项目'].includes(singleChoiceType)">
+          <select-project v-model="exportParam.projectId" :size="'medium'" :placeholder="'全部项目'" width="350px"
+            clearable></select-project>
+        </el-form-item>
+
+        <el-form-item prop="exportContent" :label="$t('daoChuNeiRon')"
+          v-if="permissions.countCost && permissions.countHours && (['项目', '项目分类'].includes(singleChoiceType))">
+          <el-select v-model="exportParam.exportContent" style="width:350px;" filterable="true"
+            popper-class="projectSelectPopperClass">
+            <el-option :label="$t('gongShiHeChengBen')" value="hoursAndCost"></el-option>
+            <el-option :label="$t('jingGongShi')" value="hours"></el-option>
+            <el-option :label="$t('jingChenBen')" value="cost"></el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item :label="$t('departmentchoice')" v-if="singleChoiceType == $t('other.project')">
+          <el-cascader v-if="user.userNameNeedTranslate != 1" v-model="exportParam.deptId" :options="departmentList"
+            :placeholder="$t('defaultText.pleaseChoose')" :props="{ checkStrictly: true, expandTrigger: 'hover' }"
+            clearable filterable style="width:350px;"></el-cascader>
+
+          <vueCascader :size="'medium'" :widthStr="'350'" :clearable="true" :subject="departmentList" :radios="true"
+            :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1"></vueCascader>
+        </el-form-item>
+
+        <el-form-item prop="userIds" :label="$t('screening.selectPeople')" v-if="singleChoiceType == $t('ren-yuan')">
+          <el-select v-if="user.userNameNeedTranslate != '1'" v-model="exportParam.userIds"
+            :placeholder="$t('lable.allStaff')" multiple="true" clearable style="width:350px;" filterable="true">
+            <el-option v-for="item in hasReportUserList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+          </el-select>
+
+          <selectCat v-if="user.userNameNeedTranslate == '1'" :size="'medium'" :widthStr="'350'" :filterable="true"
+            :subject="hasReportUserList" :clearable="true" :multiSelect="true" @selectCal="selectCal"
+            :distinction="'1'"></selectCat>
+        </el-form-item>
+
+        <el-form-item prop="userIds" :label="$t('screening.selectPeople')"
+          v-if="['项目', '项目分类'].includes(singleChoiceType)">
+          <el-select v-if="user.userNameNeedTranslate != '1'" v-model="exportParam.userIds"
+            :placeholder="$t('lable.allStaff')" multiple="true" clearable style="width:350px;" filterable="true">
+            <el-option v-for="item in users" :key="item.id" :label="item.name" :value="item.id"></el-option>
+          </el-select>
+
+          <selectCat v-if="user.userNameNeedTranslate == '1'" :size="'medium'" :widthStr="'350'" :filterable="true"
+            :subject="users" :clearable="true" :multiSelect="true" @selectCal="selectCal" :distinction="'1'">
+          </selectCat>
+        </el-form-item>
+
+        <el-form-item prop="projectId"
+          :label="user.timeType.fixMonthcost == 0 ? $t('time.dateRange') : $t('Selectmonth')">
+          <el-date-picker v-show="user.timeType.fixMonthcost == 0" v-model="exportParam.dateRange" :editable="false"
+            format="yyyy-MM-dd" value-format="yyyy-MM-dd" :clearable="false" :range-separator="$t('other.to')"
+            type="daterange" :start-placeholder="$t('time.startDate')"
+            :end-placeholder="$t('time.endDate')"></el-date-picker>
+
+
+          <el-date-picker v-show="user.timeType.fixMonthcost == 1" v-model="dateRange" :editable="false"
+            format="yyyy-MM" value-format="yyyy-MM" @change="getEchart" :clearable="true" type="month"></el-date-picker>
+        </el-form-item>
+
+        <el-form-item :label="$t('screening.selectPeople')" v-if="false">
+          <el-select v-if="user.userNameNeedTranslate != '1'" v-model="exportParam.userId"
+            :placeholder="$t('lable.allStaff')" style="width: 350px" filterable="true" clearable="true">
+            <span v-for="(item, index) in users" :key="index">
+              <el-option :label="item.name" :value="item.id"></el-option>
+            </span>
+          </el-select>
+
+          <selectCat v-if="user.userNameNeedTranslate == '1'" :size="'medium'" :distinction="'4'" :widthStr="'350'"
+            :subject="users" :clearable="true" @selectCal="selectCal"></selectCat>
+        </el-form-item>
+
+        <el-form-item prop="type" :label="$t('choosethestyle')" v-if="['项目', '项目分类'].includes(singleChoiceType)">
+          <el-select v-model="exportParam.type" :placeholder="$t('choosethestyle')" style="width:350px;">
+            <el-option
+              :label="singleChoiceType == $t('projectclassification') ? $t('classifiedontheline') : $t('Itemontheline')"
+              value="0"></el-option>
+            <el-option
+              :label="singleChoiceType == $t('projectclassification') ? $t('classifiedcolumns') : $t('itemisonthecolumn')"
+              value="1"></el-option>
+          </el-select>
+          <div class="prompt">
+            <el-popover placement="top" width="1200" trigger="hover">
+              <img src="../../assets/image/hanglie.png" alt="" width="100%"
+                v-if="this.singleChoiceType != $t('projectclassification')">
+              <img src="../../assets/image/hanglie_corp.png" alt="" width="100%" v-else>
+              <i class="el-icon-question" slot="reference" />
+            </el-popover>
+          </div>
+        </el-form-item>
+
+        <el-form-item
+          v-if="exportParam.type == 1 && permissions.countHours && (singleChoiceType == $t('other.project'))">
+          <el-checkbox v-model="exportParam.withPercent">{{ $t('gongHhiZhanbiXmu') }}</el-checkbox>
+        </el-form-item>
+
+        <el-form-item v-if="['项目', '项目分类', '主项目'].includes(singleChoiceType) && exportParam.type == '0'">
+          <el-checkbox v-model="exportParam.projectSum">{{ $t('individualprojectdata') }}</el-checkbox>
+        </el-form-item>
+
+        <el-form-item v-if="singleChoiceType == $t('ren-yuan') && user.timeType.mainProjectState == 1">
+          <el-checkbox v-model="exportParam.mainProjectColumn">{{ $t('hanZhuXiangMu') }}</el-checkbox>
+        </el-form-item>
+      </template>
+      <!-- 月度报表 -->
+      <template v-if="exportParam.reportType == 1">
+        <el-form-item prop="date" :label="this.$t('Selectmonth')">
+          <el-date-picker size="small" v-model="exportParam.date" :editable="false" format="yyyy-MM"
+            value-format="yyyy-MM" :clearable="false" type="month" :placeholder="$t('Selectmonth')"
+            style="margin-right: 20px"></el-date-picker>
+        </el-form-item>
+      </template>
+    </el-form>
+
+    <!-- 导出按钮 -->
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="exportParam.reportType == 0 ? exportProjectData() : exportMonthlyProjectData()"
+        style="width:100%;" :loading="exporting">{{ $t('export.export') }}</el-button>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import selectProject from "../../components/selectProject.vue";
+import selectCat from "@/components/select.vue"
+import vueCascader from "@/components/cascader.vue"
+export default {
+  components: {
+    selectProject,
+    selectCat,
+    vueCascader
+  },
+  props: {
+    value: { // 双向数据绑定
+      type: [String, Number, Array],
+      required: true
+    },
+    singleChoiceType: {
+      type: String,
+      default: '项目'
+    },
+    hasReportUserList: {
+      type: Array,
+      default: []
+    },
+    chartDate: {
+      type: Array,
+      default: []
+    },
+    theCustomListId: {
+      type: [String, Number],
+      default: ''
+    },
+    theCustomListPlant: {
+      type: [String, Number],
+      default: ''
+    },
+  },
+  data() {
+    return {
+      user: JSON.parse(sessionStorage.getItem("user")),
+      permissions: JSON.parse(sessionStorage.getItem("permissions")),
+      categoryList: [],
+      departmentList: [],
+      dateRange: [],
+      projectList: [],
+      newProjectList: [],
+      exporting: false,
+      exportParam: {
+        reportType: 0,
+        projectId: null,
+        dateRange: [],
+        userId: null,
+        type: '0',
+        withPercent: false,
+        date: null,
+        exportContent: 'hoursAndCost'
+      }
+    }
+  },
+  methods: {
+    exportMonthlyProjectData() {
+      var url = '/project/exportTimeByProjectAndEmployee';
+      var fileName = this.exportParam.date + '月度工时统计表.xlsx';
+      this.exporting = true;
+      this.postData(url, this.exportParam).then((res) => {
+        const aTag = document.createElement('a');
+        aTag.download = fileName;
+        aTag.href = res.data;
+        aTag.click()
+      }).finally(() => {
+        this.exporting = false
+        this.updateValue()
+      })
+    },
+    exportProjectData() {
+      let param = { stateKey: 1 }
+      if (this.exportParam.dateRange != null) {
+        param = {
+          startDate: this.exportParam.dateRange[0],
+          endDate: this.exportParam.dateRange[1],
+          stateKey: 1
+        };
+      }
+      var url = "/project/exportTimeCost";
+      var fileName = this.$t('projectmanhourcoststatistics') + '.xlsx';
+      if (this.singleChoiceType == this.$t('zhu-xiang-mu')) {
+        param.withMainProject = 1;
+      }
+      if (this.singleChoiceType == this.$t('other.project')) {
+        if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
+          var ids = '';
+          this.exportParam.userIds.forEach(u => {
+            ids += u + ',';
+          })
+          param.userIds = ids.substring(0, ids.length - 1);
+        }
+        if (this.exportParam.projectCategoryId) {
+          param.projectCategoryId = this.exportParam.projectCategoryId
+        }
+        //是否含工时占比显示
+        if (this.exportParam.withPercent) {
+          param.withPercent = 1;
+        }
+      }
+      if (this.singleChoiceType == this.$t('ren-yuan')) {
+        //  console.log(this.exportParam.userIds);
+        fileName = this.$t('labortimecoststatistics') + '.xlsx';
+        url = '/department/exportUserStatistic';
+        if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
+          var ids = '';
+          this.exportParam.userIds.forEach(u => {
+            ids += u + ',';
+          })
+          param.userIds = ids.substring(0, ids.length - 1);
+        }
+        param.mainProjectColumn = this.exportParam.mainProjectColumn;
+      }
+      if (this.singleChoiceType == this.$t('projectclassification')) {
+        fileName = this.$t('projectclassificationlaborosttatistics') + '.xlsx';
+        url = '/project/exportTimeCostByCategory'
+        if (this.exportParam.projectCategoryId) {
+          param.projectCategoryId = this.exportParam.projectCategoryId
+        }
+        if (this.exportParam.userIds != null && this.exportParam.userIds.length > 0) {
+          var ids = '';
+          this.exportParam.userIds.forEach(u => {
+            ids += u + ',';
+          })
+          param.userIds = ids.substring(0, ids.length - 1);
+        }
+      }
+      if (this.singleChoiceType == this.$t('lable.department')) {
+        fileName = this.$t('departmenthourscoststatistics') + '.xlsx'
+        url = '/department/exportDeptStatistic'
+      }
+
+      if (this.exportParam.projectId && this.singleChoiceType != this.$t('ren-yuan') && this.singleChoiceType != this.$t('projectclassification')) {
+        param.projectId = this.exportParam.projectId;
+      }
+      if (this.exportParam.userId) {
+        if (this.singleChoiceType == this.$t('lable.department') || this.singleChoiceType == this.$t('ren-yuan')) {
+          param.userId = this.exportParam.userId;
+        }
+      }
+      if (this.exportParam.type == 1) {
+        this.exportParam.projectSum = null
+      }
+      if (this.exportParam.projectSum != null) {
+        if (this.singleChoiceType == this.$t('other.project') || this.singleChoiceType == this.$t('lable.department') || this.singleChoiceType == this.$t('projectclassification') || this.singleChoiceType == this.$t('zhu-xiang-mu')) {
+          param.projectSum = this.exportParam.projectSum;
+        }
+      }
+      if (!this.theCustomListFlg) {
+        param.type = this.exportParam.type * 1
+      }
+      console.log(this.singleChoiceType)
+
+      if (this.theCustomListFlg) {
+        url = '/project/exportTimeCostByUserCustom'
+        fileName = this.singleChoiceType + this.$t('statistical') + '.xlsx'
+        param.customId = this.theCustomListId
+        param.fieldName = this.theCustomListPlant
+      }
+      if (this.exportParam.deptId) {
+        if (this.exportParam.deptId.length > 0) {
+          param.deptId = this.exportParam.deptId[this.exportParam.deptId.length - 1]
+        }
+      }
+      if (this.permissions.countCost && this.permissions.countHours && (this.singleChoiceType == this.$t('other.project') || this.singleChoiceType == this.$t('projectclassification'))) {
+        param.exportContent = this.exportParam.exportContent
+      }
+
+      if (this.singleChoiceType == this.namess) {
+        url = '/project/exportDegreeCost'
+        param = {
+          startDate: this.exportParam.dateRange[0],
+          endDate: this.exportParam.dateRange[1],
+          projectId: this.exportParam.projectId,
+        }
+        fileName = this.singleChoiceType + this.$t('chenBenTongJi') + '.xlsx'
+      }
+      this.exporting = true;
+      this.postData(url, param).then((res) => {
+        this.exporting = false;
+        const aTag = document.createElement('a');
+        aTag.download = fileName;
+        aTag.href = res.data;
+        aTag.click();
+      }).finally(() => {
+        this.exportDialog = false;
+        this.updateValue()
+      })
+    },
+    resetForm() {
+      if (this.$refs['exportFormRef']) {
+        this.$refs['exportFormRef'].resetFields();
+      }
+      this.exportParam = {
+        reportType: 0,
+        projectId: null,
+        dateRange: this.chartDate,
+        userId: null,
+        type: '0',
+        withPercent: false,
+        date: this.dayjs().format('YYYY-MM'),
+        exportContent: 'hoursAndCost'
+      }
+    },
+    getUsers() {
+      this.postData(`/user/getSimpleActiveUserList`, {}).then((res) => {
+        this.users = res.data;
+      })
+    },
+    getMyProjectList() {
+      this.postData(`/project/getProjectList`, {
+        category: this.exportParam.projectCategoryId || ''
+      }).then((res) => {
+        this.projectList = res.data;
+        this.newProjectList = res.data
+      })
+    },
+    getCategoryList() {
+      this.postData(`/project-category/list`, {}).then((res) => {
+        this.categoryList = res.data
+      })
+    },
+    getDepartmentList() {
+      this.postData(this.port.manage.depList, {}).then((res) => {
+        let dptlist = JSON.parse(JSON.stringify(res.data));
+        this.departmentList = this.changeArr(dptlist);
+      })
+    },
+    changeArr(arr) {
+      for (var i = 0; i < arr.length; i++) {
+        if (arr[i].id != -1 && arr[i].id != 0) {
+          if (arr[i].children != null && arr[i].children.length > 0) {
+            arr[i].children = this.changeArr(arr[i].children);
+          }
+          arr[i].id && (arr[i].value = arr[i].id);
+          delete arr[i].id;
+        }
+      }
+      for (var i in arr) {
+        if (arr[i].id == -1 || arr[i].id == 0) {
+          arr.splice(i, 1)
+        }
+      }
+      return arr;
+    },
+    filterCategory() {
+      this.exportParam.projectId = ''
+      const id = this.exportParam.projectCategoryId
+      if (!id) {
+        this.projectList = JSON.parse(JSON.stringify(this.newProjectList))
+        return
+      }
+      const list = JSON.parse(JSON.stringify(this.newProjectList))
+      this.projectList = list.filter(item => item.category == id)
+    },
+    // 自定义事件
+    selectCal(obj) {
+      if (obj.distinction == 1) {
+        let arr = []
+        for (var i in obj.arrUserList) {
+          arr.push(obj.arrUserList[i].id)
+        }
+        this.exportParam.userIds = arr
+      } else if (obj.distinction == 2) {
+        this.personnelValue = obj.name
+        this.personnel()
+      } else if (obj.distinction == 4) {
+        this.exportParam.userId = obj.id
+      }
+    },
+    vueCasader(obj) {
+      if (obj.distinction == 1) {
+        let arr = []
+        arr.push(obj.id)
+        this.exportParam.deptId = arr
+      }
+    },
+    handleClose(done) {
+      this.updateValue(false)
+      done()
+    },
+    updateValue(value) { // 更新数据
+      this.resetForm()
+      this.$emit('input', value);
+      this.$emit('change', value);
+    },
+    // 单独封装请求
+    async postData(urls, param) {
+      return new Promise((resolve, reject) => {
+        this.http.post(urls, { ...param },
+          res => {
+            if (res.code == 'ok') {
+              resolve(res)
+            } else {
+              this.$message({
+                message: res.msg,
+                type: 'error'
+              })
+              reject(res)
+            }
+            resolve(res)
+          },
+          error => {
+            this.$message({
+              message: error,
+              type: "error"
+            });
+            reject(error)
+          }
+        )
+      });
+    },
+  },
+  mounted() {
+    this.exportParam.dateRange = this.chartDate
+    this.exportParam.date = this.dayjs().format('YYYY-MM')
+    this.exportParam.exportContent = 'hoursAndCost'
+    this.getUsers()
+    this.getCategoryList()
+    this.getDepartmentList()
+    this.getMyProjectList()
+  },
+}
+</script>
+<style lang="scss" scoped>
+.prompt {
+  position: absolute;
+  right: 10px;
+  top: 0;
+}
+</style>