Kaynağa Gözat

积分管理

zhengnaiwen_citu 5 ay önce
ebeveyn
işleme
98dbb07456

+ 123 - 115
src/views/accumulatePoints/accumulatePointsApply/accumulatePointsApplyEdit.vue

@@ -1,96 +1,88 @@
 <template>
   <m-dialog ref="dialog" title="积分申报" @sure="onSure">
-    <m-card shadow="never" v-loading="loading" style="min-height: 500px">
-      <template v-if="finished">
-        <el-descriptions
-          :column="1"
-          :labelStyle="{
-            width: '200px',
-            'justify-content': 'flex-end',
-            'padding-top': '5px'
-          }"
-        >
-          <el-descriptions-item label="提交月份">
-            <el-date-picker
-              v-model="month"
-              size="small"
-              type="month"
-              value-format="yyyy-MM"
-              placeholder="选择月份">
-            </el-date-picker>
-          </el-descriptions-item>
-        </el-descriptions>
-        <el-descriptions
-          v-for="item in items"
-          :key="item.ruleCategory"
-          :title="item.ruleCategory"
-          :column="1"
-          :labelStyle="{
-            width: '200px',
-            'justify-content': 'flex-end',
-            'padding-top': '5px'
-          }"
-        >
-          <el-descriptions-item
-            v-for="_item in item.item"
-            :key="_item.employeeScoreRuleItemId"
-            :label="_item.title"
-          >
-            <!-- <div style="width: 100%;"> -->
-              <template v-if="_item.inputType === 0">
+    <m-card
+      shadow="never"
+      class="mb-3"
+      v-for="(item, index) in queryItems"
+      :key="item.key"
+    >
+      <el-row>
+        <el-col class="mb-3">
+          <div class="d-flex align-center">
+            <div style="width: 50px;">
+              类别
+            </div>
+            <el-select v-model="item.employeeScoreRuleItemId" filterable size="small" @change="onChange($event, index)">
+              <el-option
+                v-for="option in items"
+                :key="option.employeeScoreRuleItemId"
+                :label="option.title"
+                :value="option.employeeScoreRuleItemId"
+              ></el-option>
+            </el-select>
+            <m-button class="ml-auto" type="danger" size="small" @click="onRemove(index)">移除</m-button>
+          </div>
+        </el-col>
+        <el-col :span="24">
+          <template v-if="item.inputType === 0">
+            <div class="d-flex">
+              <div style="width: 50px;">
+                积分
+              </div>
+              <el-input-number
+                size="small"
+                :min="0"
+                v-model="item.value"
+              ></el-input-number>
+              <UploadBtn v-model="item.images"></UploadBtn>
+            </div>
+          </template>
+          <template v-if="item.inputType === 2">
+            <div class="fullWidth">
+              <div class="d-flex mb-3">
+                <div style="width: 50px;">
+                  积分
+                </div>
                 <el-input-number
                   size="small"
                   :min="0"
-                  v-model="formValues[_item.employeeScoreRuleItemId].value"
+                  placeholder="请输入积分"
+                  v-model="item.value"
                 ></el-input-number>
-                <UploadBtn v-model="formValues[_item.employeeScoreRuleItemId].image"></UploadBtn>
-              </template>
-              <template v-if="_item.inputType === 2">
-                <m-card shadow="never" class="fullWidth">
-                  <div class="d-flex mb-3">
-                    <div style="width: 50px;">
-                      积分
-                    </div>
-                    <el-input-number
-                      size="small"
-                      :min="0"
-                      placeholder="请输入积分"
-                      v-model="formValues[_item.employeeScoreRuleItemId].value"
-                    ></el-input-number>
-                    <UploadBtn v-model="formValues[_item.employeeScoreRuleItemId].image"></UploadBtn>
-                  </div>
-                  <div class="d-flex">
-                    <div style="width: 50px;">
-                      描述
-                    </div>
-                    <el-input
-                      type="textarea"
-                      :rows="1"
-                      placeholder="请输入类目描述"
-                      v-model="formValues[_item.employeeScoreRuleItemId].desc"
-                    ></el-input>
-                  </div>
-                </m-card>
-              </template>
-              <template v-if="_item.inputType === 3">
-                <el-radio-group v-model="formValues[_item.employeeScoreRuleItemId].value" class="mt-2">
-                  <el-radio
-                    v-for="radio in _item.child"
-                    :key="radio.employeeScoreRuleItemId"
-                    :label="radio.employeeScoreRuleItemId">
-                    {{ radio.title }}
-                  </el-radio>
-                </el-radio-group>
-                <UploadBtn v-model="formValues[_item.employeeScoreRuleItemId].image"></UploadBtn>
-              </template>
-            <!-- </div> -->
-            <!-- <el-upload>
-              <m-button size="small" type="primary">附件上传</m-button>
-            </el-upload> -->
-          </el-descriptions-item>
-        </el-descriptions>
-      </template>
+                <UploadBtn v-model="item.images"></UploadBtn>
+              </div>
+              <div class="d-flex">
+                <div style="width: 50px;">
+                  描述
+                </div>
+                <el-input
+                  type="textarea"
+                  :rows="1"
+                  placeholder="请输入类目描述"
+                  v-model="item.desc"
+                ></el-input>
+              </div>
+            </div>
+          </template>
+          <template v-if="item.inputType === 3">
+            <div class="d-flex">
+              <el-radio-group v-model="item.value" class="mt-2">
+                <el-radio
+                  v-for="radio in item.child"
+                  :key="radio.employeeScoreRuleItemId"
+                  :label="radio.employeeScoreRuleItemId">
+                  {{ radio.title }}
+                </el-radio>
+              </el-radio-group>
+              <UploadBtn v-model="item.images"></UploadBtn>
+            </div>
+          </template>
+        </el-col>
+      </el-row>
     </m-card>
+    <div class="text-center mt-3">
+      <m-button type="orange" icon="el-icon-plus" @click="onAdd">添加一条申报</m-button>
+    </div>
   </m-dialog>
 </template>
 
@@ -108,60 +100,77 @@ export default {
   },
   data () {
     return {
-      month: null,
       loading: false,
-      finished: false,
       items: [],
+      key: 0,
+      queryItems: [],
       formValues: {}
     }
   },
-  created () {
-    const date = new Date()
-    this.month = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
-  },
   methods: {
     async open () {
       this.loading = true
+      this.key = 0
       this.$refs.dialog.open()
       try {
         const { data } = await getAccumulatePointRule()
-        this.items = data
-        this.formValues = this.items.reduce((acc, cur) => {
-          cur.item.forEach(item => {
-            acc[item.employeeScoreRuleItemId] = {
-              value: item.inputType === 3 ? null : 0,
-              desc: null,
-              maxScore: item.maxScore ? Number(item.maxScore) : item.maxScore,
-              image: []
-            }
-          })
+        this.items = data.reduce((acc, cur) => {
+          acc.push(...cur.item)
           return acc
-        }, {})
-        this.finished = true
+        }, [])
+        const item = this.items[0]
+        this.queryItems = [
+          {
+            ...item,
+            key: 0,
+            images: null,
+            value: item.inputType === 1 ? 0 : null
+          }
+        ]
       } catch (error) {
         this.$message.error(error)
       } finally {
         this.loading = false
       }
     },
+    onRemove (index) {
+      this.queryItems.splice(index, 1)
+    },
+    onChange (employeeScoreRuleItemId, index) {
+      const obj = this.items.find(_e => _e.employeeScoreRuleItemId === employeeScoreRuleItemId)
+      Object.assign(this.queryItems[index], {
+        ...obj,
+        images: null,
+        value: 0,
+        desc: null
+      })
+    },
+    onAdd () {
+      this.key++
+      const item = this.items[0]
+      this.queryItems.push({
+        ...item,
+        key: this.key,
+        images: null,
+        value: item.inputType === 1 ? 0 : null
+      })
+    },
     async onSure () {
-      const values = Object.values(this.formValues)
-      if (values.some(item => item.value === null)) {
+      if (this.queryItems.some(item => item.value === null)) {
         this.$message.error('请填写完整再提交')
         return
       }
-      const param = Object.keys(this.formValues).map(key => {
+      const param = this.queryItems.map(item => {
         return {
-          images: this.formValues[key].image,
-          inputValue: this.formValues[key].value,
-          desc: this.formValues[key].desc,
-          employeeScoreRuleItemId: key
+          images: item.images,
+          inputValue: item.value,
+          desc: item.desc,
+          employeeScoreRuleItemId: item.employeeScoreRuleItemId
         }
       })
       this.loading = true
       try {
         await saveAccumulatePoint({
-          month: this.month,
           item: param
         })
         this.$refs.dialog.close()
@@ -181,8 +190,7 @@ export default {
 .fullWidth {
   width: 100%;
 }
-.el-radio {
-  display: block;
-  margin-bottom: 5px;
+.ml-auto {
+  margin-left: auto;
 }
 </style>

+ 2 - 2
src/views/accumulatePoints/accumulatePointsApply/accumulatePointsApplyEditUpload.vue

@@ -10,12 +10,12 @@
       :on-change="onChange"
       :before-upload="beforeUpload"
     >
-      <m-button size="mini" :loading="loading" @click="currentFiles = []">
+      <m-button size="small" :loading="loading" @click="currentFiles = []">
         {{ previewUrl.length ? '重新上传' : '上传附件' }}
       </m-button>
     </el-upload>
 
-    <m-button size="mini" class="ml-3" v-if="previewUrl.length" @click="onOpen">
+    <m-button size="small" class="ml-3" v-if="previewUrl.length" @click="onOpen">
       查看附件 ({{ previewUrl.length }})
     </m-button>
     <el-image

+ 17 - 29
src/views/accumulatePoints/accumulatePointsApply/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="pa-3 white">
-    <m-search class="mb-3" :items="searchItems" v-model="searchValues" @search="onSearch"></m-search>
+    <!-- <m-search class="mb-3" :items="searchItems" v-model="searchValues" @search="onSearch"></m-search> -->
     <m-table
       v-loading="loading"
       :items="items"
@@ -10,18 +10,21 @@
       :total="total"
       @page-change="onPageChange"
     >
+      <template #score="{ row }">
+        <el-tag type="success">{{ row.score }}</el-tag>
+      </template>
       <template #card-tools>
         <!-- 当前总积分:
         <el-tag>985</el-tag> -->
+        <m-button type="orange" icon="el-icon-refresh" :loading="loading" @click="onInit">刷新</m-button>
         <m-button type="orange" icon="el-icon-plus" @click="onAdd">申报</m-button>
       </template>
-      <!-- <template #actions="{ row }">
+      <template #actions="{ row }">
         <m-button text type="primary" @click="onDetails(row)">查看明细</m-button>
-        <m-button text type="danger" @click="onApply(row)">重新申报</m-button>
-        <m-button text type="danger" @click="onDelete(row)">作废</m-button>
-      </template> -->
+      </template>
     </m-table>
     <AccumulatePointsApplyEdit ref="accumulatePointsApplyEditRefs" @refresh="onInit"></AccumulatePointsApplyEdit>
+    <AccumulatePointsApplyDetails ref="accumulatePointsApplyDetailsRefs"></AccumulatePointsApplyDetails>
   </div>
 </template>
 
@@ -30,35 +33,22 @@ import {
   getAccumulatePointList,
   deleteAccumulatePoint
 } from '@/api/accumulatePoint'
+import AccumulatePointsApplyDetails from '../components/accumulatePointsApplyDetails.vue'
 import AccumulatePointsApplyEdit from './accumulatePointsApplyEdit.vue'
 export default {
   name: 'accumulatePointsEntering',
   components: {
-    AccumulatePointsApplyEdit
+    AccumulatePointsApplyEdit,
+    AccumulatePointsApplyDetails
   },
   data () {
     return {
-      searchItems: [
-        {
-          label: '员工姓名',
-          prop: 'employeeName',
-          type: 'input',
-          options: {
-            placeholder: '请输入名称'
-          }
-        }
-      ],
-      searchValues: {
-        employeeName: null
-      },
       headers: [
-        { label: '月份', prop: 'month' },
         { label: '员工姓名', prop: 'employeeName' },
         { label: '机构', prop: 'organizationName' },
-        { label: '总积分', prop: 'score' },
-        // { label: '状态', prop: 'status' },
-        { label: '创建时间', prop: 'createDate' }
-        // { label: '操作', prop: 'actions' }
+        { label: '申报积分', prop: 'score', align: 'center' },
+        { label: '创建时间', prop: 'createDate' },
+        { label: '操作', prop: 'actions' }
       ],
       items: [],
       total: 0,
@@ -77,10 +67,6 @@ export default {
       this.loading = true
       try {
         const { data } = await getAccumulatePointList({
-          entity: {
-            ...this.searchValues
-            // recordType: 1
-          },
           ...this.pageInfo
         })
         this.items = data.records
@@ -110,7 +96,9 @@ export default {
         })
         .catch(_ => {})
     },
-    onDetails (item) {},
+    onDetails (item) {
+      this.$refs.accumulatePointsApplyDetailsRefs.open(item)
+    },
     onSearch () {
       this.pageInfo.current = 1
       this.onInit()

+ 15 - 0
src/views/accumulatePoints/accumulatePointsIntegralList/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'accumulatePointsIntegralList'
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 51 - 0
src/views/accumulatePoints/components/accumulatePointsApplyDetails.vue

@@ -0,0 +1,51 @@
+<template>
+  <m-dialog ref="dialog" title="积分明细">
+    <m-table
+      :headers="headers"
+      :items="items"
+    ></m-table>
+  </m-dialog>
+</template>
+
+<script>
+import {
+  getAccumulatePoint
+} from '@/api/accumulatePoint'
+export default {
+  name: 'accumulatePointsApplyEdit',
+
+  data () {
+    return {
+      loading: false,
+      items: [],
+      headers: []
+    }
+  },
+  methods: {
+    async open (item) {
+      this.loading = true
+      this.$refs.dialog.open()
+      try {
+        const { data } = await getAccumulatePoint({
+          employeeScoreId: item.employeeScoreId
+        })
+        console.log(data)
+      } catch (error) {
+        this.$message.error(error)
+      } finally {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.fullWidth {
+  width: 100%;
+}
+.el-radio {
+  display: block;
+  margin-bottom: 5px;
+}
+</style>

+ 1 - 1
src/views/humanResources/payroll/PayrollDrill.vue → src/views/payroll/staffWage/StaffWageDrill.vue

@@ -35,7 +35,7 @@ import {
   drilling
 } from '@/api/system'
 export default {
-  name: 'PayrollDrill',
+  name: 'StaffWageDrill',
   data () {
     return {
       loading: false,

+ 4 - 4
src/views/humanResources/payroll/index.vue → src/views/payroll/staffWage/index.vue

@@ -18,12 +18,12 @@
         <m-button v-if="!$attrs.panorama" type="orange" icon="el-icon-download" size="small" :loading="exportLoading" @click="onExport">导出报表</m-button>
       </template>
     </m-table>
-    <PayrollDrill ref="payrollDrillRefs"></PayrollDrill>
+    <StaffWageDrill ref="payrollDrillRefs"></StaffWageDrill>
   </div>
 </template>
 
 <script>
-import PayrollDrill from './PayrollDrill.vue'
+import StaffWageDrill from './StaffWageDrill.vue'
 import {
   getPayrollPage,
   downloadPayroll
@@ -34,9 +34,9 @@ import {
 } from '@/utils/panorama'
 // import { drilling } from '@/api/system'
 export default {
-  name: 'salary-payroll',
+  name: 'StaffWage',
   components: {
-    PayrollDrill
+    StaffWageDrill
   },
   data () {
     return {

+ 15 - 0
src/views/salary/calculate/salaryCalculateIndex/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'salaryCalculateIndex'
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 3 - 3
src/views/salary/calculate/salaryCalculateUpload/index.vue

@@ -9,7 +9,7 @@
     >
       <template #card-tools>
         <m-button size="small" type="orange" icon="el-icon-finished" :loading="submitLoading" @click="onSave">提交待上传文件</m-button>
-        <m-button size="small" type="orange" icon="el-icon-s-promotion" :loading="runLoading" @click="onRun">执行计算</m-button>
+        <!-- <m-button size="small" type="orange" icon="el-icon-s-promotion" :loading="runLoading" @click="onRun">执行计算</m-button> -->
       </template>
       <!-- <template #header>
         <div class="header">
@@ -217,8 +217,8 @@ export default {
           this.submitLoading = false
         }
       }).catch(_ => {})
-    },
-    onRun () {}
+    }
+    // onRun () {}
   }
 }
 </script>

+ 15 - 0
src/views/salary/claim/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'salaryClaim'
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 252 - 0
src/views/salary/upload/index.vue

@@ -0,0 +1,252 @@
+<template>
+  <div class="white pa-3">
+    <m-search :items="searchItems" v-model="searchValues" class="mb-3" @search="onSearch"></m-search>
+    <m-table
+      :card-title="$attrs.label"
+      :headers="headers"
+      :items="items"
+      v-loading="loading"
+    >
+      <template #card-tools>
+        <m-button size="small" type="orange" icon="el-icon-finished" :loading="submitLoading" @click="onSave">提交待上传文件</m-button>
+        <!-- <m-button size="small" type="orange" icon="el-icon-s-promotion" :loading="runLoading" @click="onRun">执行计算</m-button> -->
+      </template>
+      <!-- <template #header>
+        <div class="header">
+          <m-button size="small" type="primary" icon="el-icon-finished" :loading="submitLoading" @click="onSave">提交当前临时文件</m-button>
+        </div>
+      </template> -->
+      <template #month>
+        {{ queryValues.month }}
+      </template>
+      <template #file="{ row }">
+        {{ filesValues[row.fileType]?.name }}
+      </template>
+      <template #actions="{ row }">
+        <div class="d-flex">
+          <el-upload
+            class="mr-3"
+            action="#"
+            :limit="1"
+            accept=".xlsx,.xls"
+            :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" text type="primary">导入文件</m-button>
+          </el-upload>
+          <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,
+  getSalaryCalculateFiles
+} from '@/api/salary'
+import { dateFormat } from '@/utils/date'
+export default {
+  name: 'salaryUpload',
+  data () {
+    return {
+      searchValues: {
+        month: dateFormat('YYYY-mm', new Date()),
+        category: null
+      },
+      queryValues: {},
+      filesValues: {},
+      // categoryItems: [],
+      items: [],
+      historyItems: [],
+      formItems: [],
+      headers: [
+        { label: '月份', prop: 'month', width: 100 },
+        // { label: '绩效方案', prop: 'category' },
+        { label: '文件类型', prop: 'fileName' },
+        { label: '已上传文件', prop: 'history' },
+        { label: '待上传文件', prop: 'file' },
+        { label: '操作', prop: 'actions' }
+      ],
+      loading: false,
+      submitLoading: false,
+      runLoading: false
+    }
+  },
+  computed: {
+    // items () {
+    //   const items = this.categoryItems.find(e => e.category === this.queryValues.category)?.files
+    //   if (!items) {
+    //     return []
+    //   }
+    //   return items.map(e => {
+    //     return {
+    //       ...e,
+    //       history: this.historyItems.find(h => h.fileType === e.fileType)?.fileOriginalFilename
+    //     }
+    //   })
+    // },
+    searchItems () {
+      return [
+        {
+          label: '月份',
+          prop: 'month',
+          type: 'datePicker',
+          options: {
+            clearable: false,
+            type: 'month',
+            format: 'yyyy-MM',
+            valueFormat: 'yyyy-MM',
+            placeholder: '选择更新月份'
+          }
+        }
+        // {
+        //   label: '绩效方案',
+        //   prop: 'category',
+        //   type: 'select',
+        //   options: {
+        //     clearable: false,
+        //     placeholder: '选择业务线',
+        //     items: this.categoryItems.map(e => {
+        //       return {
+        //         label: e.category,
+        //         value: e.category
+        //       }
+        //     })
+        //   }
+        // }
+      ]
+    }
+  },
+  // created () {
+  //   this.onInit()
+  // },
+  methods: {
+    async onInit () {
+      this.onGetHistory()
+      // try {
+      //   const { data } = await getSalaryCalculateTemplate()
+      //   this.categoryItems = data
+      //   if (!data.length) {
+      //     return
+      //   }
+      //   this.searchValues.category = data[0].category
+      //   this.queryValues = { ...this.searchValues }
+      //   this.onChange()
+      //   this.onGetHistory()
+      // } catch (error) {
+      //   this.$message.error(error)
+      // }
+    },
+    async onGetHistory () {
+      try {
+        this.loading = true
+        const { data } = await getSalaryCalculateFiles(this.queryValues)
+        this.historyItems = data
+      } catch (error) {
+        this.$message.error(error)
+      } finally {
+        this.loading = false
+      }
+    },
+    onSearch (query) {
+      // if (!query.category) {
+      //   this.searchValues.category = this.categoryItems[0]?.category ?? null
+      // }
+      this.queryValues = { ...this.searchValues }
+      this.onGetHistory()
+    },
+    // onChange () {
+    //   const items = this.categoryItems.find(e => e.category === this.queryValues.category)?.files ?? []
+    //   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.filesValues[key] = e.file
+    },
+    onRemove (key) {
+      this.filesValues[key] = null
+    },
+    onDelete (row) {
+      this.filesValues[row.fileType] = null
+    },
+    async onSave () {
+      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.queryValues.month}`),
+        h('p', undefined, `更新物业线:${this.queryValues.category}`),
+        h('p', { style: 'color: red' }, '上传文件后,将覆盖之前的文件,请谨慎操作!')
+      ]), '提示').then(async () => {
+        this.submitLoading = true
+        try {
+          const formData = new FormData()
+          Object.keys(this.filesValues).forEach(key => {
+            if (!this.filesValues[key]) {
+              return
+            }
+            formData.append('files', this.filesValues[key])
+            formData.append('fileTypes', key)
+          })
+          formData.append('month', this.queryValues.month)
+          formData.append('category', this.queryValues.category)
+          await uploadSalaryCalculateFiles(formData)
+          this.$message.success('保存成功')
+          this.onGetHistory()
+        } catch (error) {
+          this.$message.error(error)
+        } finally {
+          this.submitLoading = false
+        }
+      }).catch(_ => {})
+    }
+    // onRun () {}
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.d-flex {
+  display: flex;
+}
+.content {
+  width: 50%;
+  min-width: 500px;
+  margin: 0 auto;
+}
+.buttons {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+::v-deep .el-upload-list__item .el-icon-close {
+    display: inline-block;
+    &::after {
+      content: '移除';
+    }
+}
+.header {
+  display: flex;
+  justify-content: flex-end;
+}
+</style>