Ver Fonte

薪酬计算

zhengnaiwen_citu há 6 meses atrás
pai
commit
d4fc0f1807

+ 5 - 0
src/api/salary.js

@@ -140,3 +140,8 @@ export function getSalaryCalculateTemplate () {
 export function uploadSalaryCalculateFiles () {
   return http.upload('/performance/file/upload')
 }
+
+// 绩效计算 模板
+export function getSalaryCalculateFiles (data) {
+  return http.post('/performance/file/list', data)
+}

+ 30 - 40
src/components/ECharts/index.vue → src/components/AutoComponents/ECharts/eCharts.js

@@ -1,9 +1,4 @@
-<template>
-  <div :id="id" class="chartBox"></div>
-</template>
 
-<script>
-import { generateUUID } from '@/utils'
 // 引入 ECharts 核心模块,核心模块提供了 ECharts 使用必须要的接口。
 import * as ECharts from 'echarts/core'
 // 引入柱状图图表,图表后缀都为 Chart
@@ -20,22 +15,12 @@ import {
 import { LabelLayout, UniversalTransition } from 'echarts/features'
 // 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
 import { CanvasRenderer } from 'echarts/renderers'
-export default {
-  name: 'e-charts',
-  data () {
-    return {
-      id: generateUUID(),
-      chart: null
-    }
-  },
-  beforeDestroy () {
-    window.removeEventListener('resize', this.onResize)
-    this.chart.dispose()
-  },
-  methods: {
-    onInit () {
-      // 注册必须的组件
-      ECharts.use([
+
+class EChartsComponent {
+  constructor (el) {
+    // 注册必须的组件
+    ECharts.use(
+      [
         TitleComponent,
         TooltipComponent,
         GridComponent,
@@ -46,25 +31,30 @@ export default {
         LabelLayout,
         UniversalTransition,
         CanvasRenderer
-      ])
-      this.$nextTick(() => {
-        this.chart = ECharts.init(document.getElementById(this.id))
-        window.addEventListener('resize', this.onResize)
-      })
-    },
-    onResize () {
-      this.chart.resize()
-    },
-    setOption (options) {
-      this.chart.setOption(options)
-    }
+      ]
+    )
+    this.el = ECharts.init(el)
+  }
+
+  // 设置属性
+  setOption (options) {
+    this.el.setOption(options)
+  }
+
+  // 柱状属性
+  setBarOption (options) {
+
   }
-}
-</script>
 
-<style lang="scss" scoped>
-.chartBox {
-  width: 100%;
-  height: 100%;
+  // 折线属性
+  setLineOption (options) {
+
+  }
+
+  // 饼图属性
+  setPieOption (options) {
+
+  }
 }
-</style>
+
+export default EChartsComponent

+ 45 - 0
src/components/AutoComponents/ECharts/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div>
+    <div class="tools"></div>
+    <div :ref="id" class="chartBox"></div>
+  </div>
+</template>
+
+<script>
+import { generateUUID } from '@/utils'
+import EChartsComponent from './eCharts'
+export default {
+  name: 'e-charts',
+  data () {
+    return {
+      id: generateUUID(),
+      chart: null
+    }
+  },
+  beforeDestroy () {
+    window.removeEventListener('resize', this.onResize)
+    this.chart.el.dispose()
+  },
+  methods: {
+    onInit () {
+      // 注册必须的组件
+      this.chart = new EChartsComponent(this.$refs[this.id])
+      this.$refs[this.id].style.width = this.$refs[this.id].offsetWidth + 'px'
+      this.$refs[this.id].style.height = this.$refs[this.id].offsetHeight + 'px'
+      window.addEventListener('resize', this.onResize)
+      return this.chart
+    },
+    onResize () {
+      this.chart.el.resize()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.chartBox {
+  width: 100%;
+  height: 100%;
+  min-height: 300px;
+}
+</style>

+ 9 - 9
src/components/AutoComponents/MSearch/index.vue

@@ -22,8 +22,8 @@
             v-if="item.type === 'input'"
             v-model="query[item.prop]"
             @keydown.enter.native="onSubmit"
-            :clearable="item.option?.clearable ?? true"
-            v-bind="item.option"
+            :clearable="item.options?.clearable ?? true"
+            v-bind="item.options"
             v-on="item.handles"
           ></el-input>
 
@@ -31,22 +31,22 @@
           <el-select
             v-if="item.type === 'select'"
             v-model="query[item.prop]"
-            :clearable="item.option?.clearable ?? true"
-            v-bind="item.option"
+            :clearable="item.options?.clearable ?? true"
+            v-bind="item.options"
             v-on="item.handles"
           >
             <el-option
-              v-for="option in item.option.items"
+              v-for="option in item.options.items"
               :key="option.value"
-              :label="option[item.option?.labelText ?? 'label']"
-              :value="option[item.option?.labelValue ?? 'value']"
+              :label="option[item.options?.labelText ?? 'label']"
+              :value="option[item.options?.labelValue ?? 'value']"
             ></el-option>
           </el-select>
 
           <el-autocomplete
             v-if="item.type === 'autocomplete'"
             v-model="query[item.prop]"
-            v-bind="item.option"
+            v-bind="item.options"
             v-on="item.handles"
           >
             <template v-for="slot in item.slots" :slot="scope">
@@ -58,7 +58,7 @@
           <el-date-picker
             v-if="item.type === 'datePicker'"
             v-model="query[item.prop]"
-            v-bind="item.option"
+            v-bind="item.options"
             v-on="item.handles"
           ></el-date-picker>
 

+ 2 - 2
src/views/bonus/allocation/index.vue

@@ -123,7 +123,7 @@ export default {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
-          option: {
+          options: {
             placeholder: '请选择月份',
             clearable: false,
             type: 'month',
@@ -138,7 +138,7 @@ export default {
           label: '员工类型',
           prop: 'employeeCategory',
           type: 'select',
-          option: {
+          options: {
             clearable: false,
             placeholder: '请选择员工类型',
             items: this.employeeCategoryItems

+ 32 - 0
src/views/home/components/performance.vue

@@ -0,0 +1,32 @@
+<template>
+  <m-card>
+    <e-charts ref="eChart" class="box"></e-charts>
+  </m-card>
+</template>
+
+<script>
+export default {
+  name: 'performance-statistics',
+  mounted () {
+    const chart = this.$refs.eChart.onInit()
+    chart.setOption({
+      xAxis: {
+        data: ['20', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+      },
+      yAxis: {},
+      series: [
+        {
+          type: 'bar',
+          data: [23, 24, 18, 25, 27, 28, 25]
+        }
+      ]
+    })
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.box {
+  height: 500px;
+}
+</style>

+ 7 - 3
src/views/home/index.vue

@@ -1,12 +1,16 @@
 <template>
-  <div>
-    <m-empty></m-empty>
+  <div class="pa-3 white">
+    <PerformanceStatistics></PerformanceStatistics>
   </div>
 </template>
 
 <script>
+import PerformanceStatistics from './components/performance.vue'
 export default {
-  name: 'home-index'
+  name: 'home-index',
+  components: {
+    PerformanceStatistics
+  }
 }
 </script>
 

+ 2 - 2
src/views/humanResources/panorama/dynamic/salary/payroll.vue

@@ -57,7 +57,7 @@ export default {
           prop: 'employeeName',
           type: 'input',
           hidden: this.$route.query.employeeNo,
-          option: {
+          options: {
             placeholder: '请输入姓名'
           }
         },
@@ -65,7 +65,7 @@ export default {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
-          option: {
+          options: {
             type: 'month',
             format: 'yyyy-MM',
             valueFormat: 'yyyy-MM',

+ 3 - 3
src/views/humanResources/payroll/index.vue

@@ -37,15 +37,15 @@ export default {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
-          option: {
+          options: {
             placeholder: '请选择月份',
             type: 'month',
             valueFormat: 'yyyy-MM',
             format: 'yyyy 年 MM 月'
           }
         },
-        { label: '部门', prop: 'organizationName', type: 'input', option: { placeholder: '请输入部门' } },
-        { label: '姓名', prop: 'employeeName', type: 'input', option: { placeholder: '请输入姓名' } }
+        { label: '部门', prop: 'organizationName', type: 'input', options: { placeholder: '请输入部门' } },
+        { label: '姓名', prop: 'employeeName', type: 'input', options: { placeholder: '请输入姓名' } }
       ],
       searchValues: {
         month: null,

+ 2 - 2
src/views/humanResources/roster/index.vue

@@ -52,7 +52,7 @@ export default {
       searchItems: [
         {
           label: '员工名称',
-          option: {
+          options: {
             placeholder: '请输入员工名称'
           },
           prop: 'employeeName',
@@ -60,7 +60,7 @@ export default {
         },
         {
           label: '机构名称',
-          option: {
+          options: {
             placeholder: '请输入机构名称'
           },
           prop: 'organizationName',

+ 1 - 1
src/views/humanResources/welfare/components/ListTemplate.vue

@@ -51,7 +51,7 @@ export default {
           label: '福利名称',
           prop: 'subsidyName',
           type: 'input',
-          option: {
+          options: {
             placeholder: '请输入福利名称'
           }
         }

+ 145 - 44
src/views/salary/calculate/index.vue

@@ -1,61 +1,103 @@
 <template>
   <div class="white pa-3">
-    <m-card shadow="never" class="content pa-3">
-      <m-form ref="form" :items="formItems" v-model="query" label-width="300px">
-        <template v-for="item in items" #[item.prop]>
+    <m-search :items="searchItems" v-model="searchValues" class="mb-3" @search="onSearch">
+      <template #button>
+        <m-button type="primary" plain @click="onSave">提交所有临时文件</m-button>
+      </template>
+    </m-search>
+    <m-table
+      :headers="headers"
+      :items="items"
+      :loading="loading"
+    >
+      <template #month>
+        {{ searchValues.month }}
+      </template>
+      <template #file="{ row }">
+        {{ filesValues[row.fileType]?.name }}
+      </template>
+      <template #actions="{ row }">
+        <div class="d-flex">
           <el-upload
-            :key="item.prop"
-            :ref="item.prop"
+            class="mr-3"
             action="#"
             :limit="1"
-            :file-list="query[item.prop] ? [query[item.prop]] : []"
-            :on-exceed="(files, fileList) => onExceed(files, fileList, item.prop)"
-            :http-request="e => onImport(e, item.prop)"
-            :on-remove="() => onRemove(item.prop)"
+            :show-file-list="false"
+            :on-exceed="(files, fileList) => onExceed(files, fileList, row.fileType)"
+            :http-request="e => onImport(e, row.fileType)"
+            :on-remove="() => onRemove(row.fileType)"
           >
-            <m-button slot="trigger" size="small" type="primary" plain>点击上传</m-button>
+            <m-button slot="trigger" text type="primary" @click="onUpdate(row)">更新文件</m-button>
           </el-upload>
-        </template>
-        <el-form-item>
-          <m-button icon="el-icon-check" type="primary" size="small" @click="onSave">提交</m-button>
-          <m-button icon="el-icon-s-promotion" type="primary" size="small">绩效计算</m-button>
-          <m-button icon="el-icon-time" type="primary" size="small">绩效计算历史记录</m-button>
-        </el-form-item>
-      </m-form>
-    </m-card>
+          <m-button text type="danger" v-show="filesValues[row.fileType]" @click="onDelete(row)">移除临时文件</m-button>
+        </div>
+      </template>
+    </m-table>
   </div>
 </template>
 
 <script>
 import {
   getSalaryCalculateTemplate,
-  uploadSalaryCalculateFiles
+  uploadSalaryCalculateFiles,
+  getSalaryCalculateFiles
 } from '@/api/salary'
+import { dateFormat } from '@/utils/date'
 export default {
   name: 'salary-calculate',
   data () {
     return {
-      query: {
-        month: null
+      searchValues: {
+        month: dateFormat('YYYY-mm', new Date()),
+        category: null
       },
-      items: []
+      filesValues: {},
+      categoryItems: [],
+      historyItems: [],
+      formItems: [],
+      headers: [
+        { label: '月份', prop: 'month' },
+        { label: '业务线', prop: 'category' },
+        { label: '文件', prop: 'history' },
+        { label: '临时文件', prop: 'file' },
+        { label: '操作', prop: 'actions' }
+      ],
+      loading: false
     }
   },
   computed: {
-    formItems () {
+    items () {
+      return this.categoryItems.find(e => e.category === this.searchValues.category)?.files ?? []
+    },
+    searchItems () {
       return [
         {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
           options: {
+            clearable: false,
             type: 'month',
             format: 'yyyy-MM',
             valueFormat: 'yyyy-MM',
             placeholder: '选择更新月份'
           }
         },
-        ...this.items
+        {
+          label: '业务线',
+          prop: 'category',
+          type: 'select',
+          options: {
+            clearable: false,
+            placeholder: '选择业务线',
+            items: this.categoryItems.map(e => {
+              return {
+                label: e.category,
+                value: e.category
+              }
+            })
+          }
+        }
       ]
     }
   },
@@ -66,43 +108,102 @@ export default {
     async onInit () {
       try {
         const { data } = await getSalaryCalculateTemplate()
-        this.items = data.map(e => {
-          this.$set(this.query, e.fileType, null)
-          return {
-            label: e.fileName,
-            prop: e.fileType,
-            rules: [
-              { required: true, message: '请上传文件', trigger: 'change' }
-            ]
-          }
-        })
+        this.categoryItems = data
+        if (!data.length) {
+          return
+        }
+        this.searchValues.category = data[0].category
+        this.onChange()
+        this.onGetHistory()
+      } catch (error) {
+        this.$message.error(error)
+      }
+    },
+    async onGetHistory () {
+      try {
+        const { data } = await getSalaryCalculateFiles(this.searchValues)
+        console.log(data)
       } catch (error) {
         this.$message.error(error)
       }
     },
-    onExceed (files, fileList, ref) {
-      this.query[ref] = files[0]
+    onSearch (query) {
+      if (!query.category) {
+        this.searchValues.category = this.categoryItems[0].category
+      }
+      this.onChange()
+      this.onGetHistory()
+    },
+    onChange () {
+      const items = this.categoryItems.find(e => e.category === this.searchValues.category)?.files ?? []
+      debugger
+      if (!items.length) {
+        this.filesValues = {}
+        return
+      }
+      this.filesValues = {
+        ...items.reduce((res, v) => {
+          res[v.fileType] = null
+          return res
+        }, {})
+      }
+    },
+    onExceed (files, fileList, key) {
+      this.filesValues[key] = files[0]
     },
     onImport (e, key) {
-      this.query[key] = e.file
+      this.filesValues[key] = e.file
     },
     onRemove (key) {
-      this.query[key] = null
-      console.log(this.query)
+      this.filesValues[key] = null
+    },
+    onUpdate () {},
+    onDelete (row) {
+      this.filesValues[row.fileType] = null
     },
     async onSave () {
-      try {
-        const { data } = await uploadSalaryCalculateFiles()
-        console.log(data)
-      } catch (error) {
-        this.$snackbar.error(error)
+      if (Object.values(this.filesValues).every(e => e === null)) {
+        this.$message.warning('请先上传文件')
+        return
       }
+      const h = this.$createElement
+      this.$confirm(h('div', [
+        h('p', undefined, '确定要更新文件吗?'),
+        h('p', undefined, `更新月份:${this.searchValues.month}`),
+        h('p', undefined, `更新物业线:${this.searchValues.category}`),
+        h('p', { style: 'color: red' }, '上传文件后,将覆盖之前的文件,请谨慎操作!')
+      ]), '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        try {
+          const obj = Object.keys(this.filesValues).reduce((res, key) => {
+            if (!this.filesValues[key]) {
+              return res
+            }
+            res.files.push(this.filesValues[key])
+            res.fileTypes.push(key)
+            return res
+          }, { files: [], fileTypes: [] })
+          const { data } = await uploadSalaryCalculateFiles({
+            ...obj,
+            ...this.searchValues
+          })
+          console.log(data)
+        } catch (error) {
+          this.$snackbar.error(error)
+        }
+      }).catch(_ => {})
     }
   }
 }
 </script>
 
 <style lang="scss" scoped>
+.d-flex {
+  display: flex;
+}
 .content {
   width: 50%;
   min-width: 500px;

+ 3 - 3
src/views/salary/comparison/salaryEmployeeComparison/index.vue

@@ -85,7 +85,7 @@ export default {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
-          option: {
+          options: {
             editable: false,
             clearable: false,
             placeholder: '请选择月份',
@@ -94,8 +94,8 @@ export default {
             format: 'yyyy 年 MM 月'
           }
         },
-        { label: '部门', prop: 'organizationName', type: 'input', option: { placeholder: '请输入部门' } },
-        { label: '姓名', prop: 'employeeName', type: 'input', option: { placeholder: '请输入姓名' } }
+        { label: '部门', prop: 'organizationName', type: 'input', options: { placeholder: '请输入部门' } },
+        { label: '姓名', prop: 'employeeName', type: 'input', options: { placeholder: '请输入姓名' } }
       ],
       searchValues: {
         month: dateFormat('YYYY-mm', new Date()),

+ 2 - 2
src/views/salary/comparison/salaryOptionComparison/index.vue

@@ -55,7 +55,7 @@ export default {
           label: '月份',
           prop: 'month',
           type: 'datePicker',
-          option: {
+          options: {
             editable: false,
             clearable: false,
             placeholder: '请选择月份',
@@ -68,7 +68,7 @@ export default {
           label: '方案',
           prop: 'versions',
           type: 'autocomplete',
-          option: {
+          options: {
             placeholder: '请选择方案',
             multiple: true,
             filterable: true,

+ 1 - 1
src/views/salary/solution/components/ListTemplate.vue

@@ -48,7 +48,7 @@ export default {
           label: '名称',
           prop: 'title',
           type: 'input',
-          option: {
+          options: {
             placeholder: '请输入方案名称'
           }
         }

+ 2 - 2
src/views/salary/solution/salarySolutionParameter/index.vue

@@ -38,12 +38,12 @@ export default {
   data () {
     return {
       searchItems: [
-        { label: '关键词', prop: 'keyword', type: 'input', option: { placeholder: '请输入关键词' } },
+        { label: '关键词', prop: 'keyword', type: 'input', options: { placeholder: '请输入关键词' } },
         {
           label: '分类',
           prop: 'category',
           type: 'autocomplete',
-          option: {
+          options: {
             clearable: true,
             placeholder: '请输入分类',
             fetchSuggestions: this.fetchSuggestions

+ 1 - 1
src/views/system/role/index.vue

@@ -59,7 +59,7 @@ export default {
         {
           label: '角色名称',
           prop: 'roleName',
-          option: {
+          options: {
             placeholder: '请输入角色名称'
           },
           type: 'input'

+ 1 - 1
src/views/system/user/index.vue

@@ -88,7 +88,7 @@ export default {
       searchItems: [
         {
           label: '用户查找',
-          option: {
+          options: {
             placeholder: '请输入用户昵称 / 账号'
           },
           prop: 'searchKey',