incomeDetail.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <template>
  2. <div class="incomeDetail w-full h-full">
  3. <!-- 头部 -->
  4. <div class="incomeDetail-header flex-center">
  5. <div class="item-center">
  6. <i class="fa fa-align-justify" @click.prevent="switchMenu" style="cursor: pointer" />
  7. <div class="m-lr">选择月份</div>
  8. <el-date-picker size="small" v-model="selectMonth" :editable="false" format="yyyy-MM" value-format="yyyy-MM"
  9. @change="retrieveDataAgain" :clearable="false" type="month" placeholder="选择月份" />
  10. </div>
  11. <div class="item-center">
  12. <el-link type="primary" :underline="false" class="ml" @click="bonusDataExport">数据导出</el-link>
  13. <el-link type="primary" :underline="false" class="ml" @click="clickOnFundingData">数据上传</el-link>
  14. <el-link type="primary" :underline="false" class="ml" href="./upload/员工奖金模板.xlsx"
  15. :download="'员工奖金模板' + '.xlsx'">模板下载</el-link>
  16. </div>
  17. </div>
  18. <!-- 表格 -->
  19. <div class="incomeDetail-table">
  20. <el-table :data="tableList" highlight-current-row v-loading="tableListLoading" ref="tableListRef"
  21. @selection-change="deleteSel" :height="300" style="width: 100%;">
  22. <el-table-column type="selection" width="80" fixed="left"></el-table-column>
  23. <el-table-column prop="jobNumber" label="工号"></el-table-column>
  24. <el-table-column prop="userName" label="姓名"></el-table-column>
  25. <el-table-column prop="bonusType" label="奖金类型"></el-table-column>
  26. <el-table-column prop="totalBonusValue" label="奖金金额(元)"></el-table-column>
  27. <el-table-column prop="name" label="分摊月份">
  28. <template slot-scope="scope">
  29. {{ scope.row.startYM }} - {{ scope.row.endYM }}
  30. </template>
  31. </el-table-column>
  32. </el-table>
  33. <div style="padding: 10px 14px;">
  34. <el-button size="mini" type="primary" :disabled="deleteSelList.length == 0"
  35. @click="batchDeletion">批量删除</el-button>
  36. </div>
  37. </div>
  38. <!-- 图表 -->
  39. <div class="incomeDetail-echart flex-1">
  40. <!-- echarts 图 -->
  41. <div class="incomeDetail-echart-echart flex-1" v-loading="echartsLoading">
  42. <div id="clearfix" class="echarts-com w-full h-full">
  43. <div id="containerEcharts" class="w-full h-full"></div>
  44. </div>
  45. </div>
  46. </div>
  47. <!-- 资金数据导入 -->
  48. <el-dialog title="资金数据导入" v-if="fundsImportIntoVisable" :visible.sync="fundsImportIntoVisable"
  49. customClass="customWidth" width="500px">
  50. <div class="importOfFundDataClass">
  51. <el-form ref="form" label-width="100px">
  52. <el-form-item label="分摊月份">
  53. <el-date-picker v-model="sharingMonths" type="monthrange" value-format="yyyy-MM" range-separator="至"
  54. start-placeholder="开始月份" end-placeholder="结束月份" :size="'small'" :picker-options="pickerOptions"
  55. @change="sharingMonthsChange">
  56. </el-date-picker>
  57. </el-form-item>
  58. <el-form-item label="奖金类型">
  59. <el-cascader v-model="typeOfFundsValue" :options="typeOfFundsArray" :props="{ expandTrigger: 'hover' }"
  60. :size="'small'" :show-all-levels="false"></el-cascader>
  61. </el-form-item>
  62. <el-form-item label="导入模板">
  63. <el-upload class="upload-demo" :limit="1" :http-request="fundUpload" :size="'small'">
  64. <el-button size="small" type="primary" :size="'small'">点击上传</el-button>
  65. </el-upload>
  66. </el-form-item>
  67. </el-form>
  68. </div>
  69. <span slot="footer" class="dialog-footer">
  70. <el-button type="primary" @click="submitFunds()" :loading="dataImportLoading">{{ $t('btn.determine')
  71. }}</el-button>
  72. </span>
  73. </el-dialog>
  74. <!-- 数据导出 -->
  75. <el-dialog title="数据导出" v-if="dataExportVisable" :visible.sync="dataExportVisable" customClass="customWidth"
  76. width="500px">
  77. <div class="importOfFundDataClass">
  78. <el-form ref="form" label-width="100px">
  79. <el-form-item label="导出年份">
  80. <el-date-picker v-model="selectYear" type="year" value-format="yyyy" :size="'small'">
  81. </el-date-picker>
  82. </el-form-item>
  83. </el-form>
  84. </div>
  85. <span slot="footer" class="dialog-footer">
  86. <el-button type="primary" @click="submitExport()" :loading="dataExportLoading">导出</el-button>
  87. </span>
  88. </el-dialog>
  89. </div>
  90. </template>
  91. <script>
  92. import dayjs from 'dayjs';
  93. import { fundCascadingPanel, processingChartOptions } from './common'
  94. export default {
  95. components: {},
  96. data() {
  97. return {
  98. user: JSON.parse(sessionStorage.getItem("user")),
  99. permissions: JSON.parse(sessionStorage.getItem("permissions")),
  100. selectMonth: dayjs().format('YYYY-MM'),
  101. selectYear: dayjs().format('YYYY'),
  102. typeOfFundsValue: '',
  103. deleteSelList: [],
  104. missingFinanceUserList: [],
  105. sharingMonths: [],
  106. sharingMonthsTwo: [],
  107. tableList: [],
  108. tableListLoading: false,
  109. echartsLoading: false,
  110. dataExportLoading: false,
  111. dataImportLoading: false,
  112. showMissingDialog: false,
  113. fundsImportIntoVisable: false,
  114. dataExportVisable: false,
  115. typeOfFundsArray: fundCascadingPanel,
  116. pickerOptions: {
  117. disabledDate(time) {
  118. return time.getFullYear() > dayjs().format('YYYY');
  119. }
  120. },
  121. importTemplateData: null,
  122. echartsData: [],
  123. myChart: null,
  124. };
  125. },
  126. mounted() {
  127. this.retrieveDataAgain()
  128. this.scrollFunction()
  129. },
  130. methods: {
  131. retrieveDataAgain() {
  132. this.getEchartsData()
  133. this.getTableList()
  134. },
  135. submitExport() {
  136. this.http.downloadFile('/contractBonusDetail/exportContractBonus', {
  137. year: this.selectYear
  138. }, '奖金项目分摊.xlsx', err => {
  139. this.$message({
  140. message: err,
  141. type: 'error'
  142. })
  143. })
  144. },
  145. bonusDataExport() {
  146. this.selectYear = dayjs().format('YYYY')
  147. this.dataExportVisable = true
  148. },
  149. batchDeletion() {
  150. this.$confirm(this.$t(this.$t('doyouwanttodeleteit')), this.$t('other.prompts'), {
  151. confirmButtonText: this.$t('btn.determine'),
  152. cancelButtonText: this.$t('btn.cancel'),
  153. type: 'warning'
  154. }).then(() => {
  155. const ids = this.deleteSelList.map(item => item.id).join(',')
  156. this.postData(`/contractBonusSummary/deleteBonusSummary`, { ids }).then((res) => {
  157. this.$message.success('操作成功')
  158. this.retrieveDataAgain()
  159. })
  160. })
  161. },
  162. sharingMonthsChange(val) {
  163. const startData = val[0]
  164. const endData = val[1]
  165. const dateDetermination = dayjs(startData).isSame(dayjs(endData), 'year');
  166. if (!dateDetermination) {
  167. this.sharingMonths = JSON.parse(JSON.stringify(this.sharingMonthsTwo))
  168. this.$message.warning('请选择同一年的月份')
  169. return
  170. }
  171. this.sharingMonthsTwo = JSON.parse(JSON.stringify(val))
  172. },
  173. submitFunds() {
  174. if (!this.typeOfFundsValue || this.typeOfFundsValue.length == 0) {
  175. this.$message.warning('请选择奖金类型')
  176. return
  177. }
  178. if (!this.importTemplateData) {
  179. this.$message.warning('请选择导入模板')
  180. return
  181. }
  182. let formData = new FormData();
  183. formData.append("startYM", this.sharingMonths[0]);
  184. formData.append("endYM", this.sharingMonths[1]);
  185. formData.append("bonusType", this.typeOfFundsValue[this.typeOfFundsValue.length - 1]);
  186. formData.append("file", this.importTemplateData);
  187. this.dataImportLoading = true
  188. this.http.uploadFile('/contractBonusDetail/transTemplateData', formData,
  189. res => {
  190. this.dataImportLoading = false
  191. if (res.code == 'ok') {
  192. this.fundsImportIntoVisable = false
  193. this.$message({
  194. message: '操作成功',
  195. type: 'success'
  196. })
  197. this.retrieveDataAgain()
  198. } else {
  199. this.$message({
  200. message: res.msg,
  201. type: 'error'
  202. })
  203. }
  204. }, error => {
  205. this.dataImportLoading = false
  206. this.$message({
  207. message: error,
  208. type: 'error'
  209. })
  210. })
  211. },
  212. fundUpload(file) {
  213. this.importTemplateData = file.file
  214. },
  215. clickOnFundingData() {
  216. this.typeOfFundsValue = ''
  217. this.sharingMonths = [dayjs().format('YYYY-MM'), dayjs().format('YYYY-MM')]
  218. this.importTemplateData = null
  219. this.fundsImportIntoVisable = true
  220. },
  221. getTableList() {
  222. this.tableListLoading = true
  223. this.postData(`/contractBonusSummary/getBonusSummary`, { ym: this.selectMonth }).then((res) => {
  224. this.tableList = res.data || []
  225. }).finally(() => {
  226. this.tableListLoading = false
  227. })
  228. },
  229. getEchartsData() {
  230. this.echartsLoading = true
  231. this.postData(`/contractBonusDetail/getContractBonus`, { year: dayjs(this.selectMonth).format('YYYY') }).then(res => {
  232. // this.echartsData = res.data || []
  233. this.instantiateChart(res.data || [])
  234. }).finally(() => {
  235. this.echartsLoading = false
  236. })
  237. },
  238. instantiateChart(list = []) {
  239. const listNew = [...list]
  240. if (this.myChart) {
  241. this.myChart.dispose();
  242. this.myChart = null
  243. }
  244. this.myChart = echarts.init(document.getElementById("containerEcharts"));
  245. const option = processingChartOptions(listNew)
  246. // 设置图表
  247. this.myChart.resize({
  248. // width: this.widthHtval
  249. })
  250. this.myChart.setOption(option, { notMerge: true });
  251. },
  252. deleteSel(sel) {
  253. this.deleteSelList = sel
  254. },
  255. // 左右滚动
  256. scrollFunction() {
  257. this.domObj = document.getElementById('clearfix') // 通过id获取要设置的div
  258. if (this.domObj.attachEvent) { // IE
  259. this.domObj.attachEvent('onmousewheel', this.mouseScroll)
  260. } else if (this.domObj.addEventListener) {
  261. this.domObj.addEventListener('DOMMouseScroll', this.mouseScroll, false)
  262. }
  263. this.domObj.onmousewheel = this.domObj.onmousewheel = this.mouseScroll
  264. },
  265. mouseScroll(event) { // google 浏览器下
  266. let detail = event.wheelDelta || event.detail
  267. let moveForwardStep = -1
  268. let moveBackStep = 1
  269. let step = 0
  270. step = detail > 0 ? moveForwardStep * 100 : moveBackStep * 100
  271. event.preventDefault() // 阻止浏览器默认事件
  272. this.domObj.scrollLeft = this.domObj.scrollLeft + step
  273. },
  274. switchMenu() {
  275. this.$emit("switchMenu");
  276. },
  277. destroyChart() {
  278. if (this.myChart) {
  279. this.myChart.dispose();
  280. this.myChart = null;
  281. }
  282. },
  283. async postData(urls, param) {
  284. return new Promise((resolve, reject) => {
  285. this.http.post(urls, { ...param },
  286. res => {
  287. if (res.code == 'ok') {
  288. resolve(res)
  289. } else {
  290. this.$message({
  291. message: res.msg,
  292. type: 'error'
  293. })
  294. reject(res)
  295. }
  296. resolve(res)
  297. },
  298. error => {
  299. this.$message({
  300. message: error,
  301. type: "error"
  302. });
  303. reject(error)
  304. }
  305. )
  306. });
  307. },
  308. },
  309. beforeDestroy() {
  310. this.destroyChart();
  311. },
  312. };
  313. </script>
  314. <style lang="scss" scoped>
  315. .incomeDetail {
  316. color: #606266;
  317. position: relative;
  318. display: flex;
  319. flex-direction: column;
  320. .incomeDetail-header {
  321. height: 40px;
  322. background: #f2f2f2;
  323. padding: 10px;
  324. .incomeDetail-header-const {
  325. width: 440px;
  326. }
  327. }
  328. .incomeDetail-echart {
  329. display: flex;
  330. flex-direction: column;
  331. padding: 8px;
  332. .echarts-com {
  333. overflow-x: auto;
  334. position: relative;
  335. }
  336. }
  337. }
  338. .flex-1 {
  339. flex: 1;
  340. }
  341. .ml {
  342. margin-left: 14px;
  343. }
  344. .m-lr {
  345. margin: 0 12px;
  346. }
  347. .item-center {
  348. display: flex;
  349. align-items: center;
  350. }
  351. .flex-center {
  352. display: flex;
  353. align-items: center;
  354. justify-content: space-between;
  355. }
  356. .w-full {
  357. width: 100%;
  358. }
  359. .h-full {
  360. height: 100%;
  361. }
  362. </style>