working-hours-records.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <basic-container>
  3. <avue-crud
  4. v-if="!headerLoading"
  5. v-bind="bindVal"
  6. v-on="onEvent"
  7. v-model="form"
  8. :page.sync="page"
  9. :before-open="handleBeforeOpen"
  10. :permission="permissionList"
  11. :header-cell-style="headerCellStyle"
  12. >
  13. <template slot="menuLeft">
  14. <el-button
  15. type="danger"
  16. size="small"
  17. icon="el-icon-delete"
  18. plain
  19. @click="handleDelete"
  20. >
  21. 清除工时数据
  22. </el-button>
  23. <el-button
  24. type="success"
  25. size="small"
  26. plain
  27. icon="el-icon-upload2"
  28. @click="handleImport"
  29. >
  30. 导入
  31. </el-button>
  32. <el-button
  33. type="warning"
  34. size="small"
  35. plain
  36. icon="el-icon-download"
  37. @click="handleExport"
  38. >
  39. 导出
  40. </el-button>
  41. <!-- <print-table-btn @click="printTable" /> -->
  42. </template>
  43. <template slot="body">
  44. <h3 class="page-title">{{ pageTitle }}</h3>
  45. <year-month-select v-model="params.yearAndMonth" :showAllYear="false"></year-month-select>
  46. </template>
  47. <!-- <template slot="monthlyHoursSearch">
  48. <div style="display: flex; align-items: center;">
  49. <span>使用工时/小时:</span>
  50. <avue-input-number v-model="params.monthlyHoursStart" :min="0" style="width: 100px !important;"></avue-input-number>
  51. <span style="width: 20px; text-align: center;">至</span>
  52. <avue-input-number v-model="params.monthlyHoursEnd" :min="0" style="width: 100px !important;"></avue-input-number>
  53. </div>
  54. </template> -->
  55. </avue-crud>
  56. <upload-excel-dialog ref="uploadExcelDialog" :showTempBtn="false" :uploadAfter="uploadAfter"/>
  57. <WideTablePrinter
  58. ref="printWideTable"
  59. :columns="wideTableColumns"
  60. :data="data"
  61. :print-title="pageTitle"
  62. :rows-per-page="30"
  63. :default-landscape="true"
  64. />
  65. </basic-container>
  66. </template>
  67. <script>
  68. import YearMonthSelect from "@/components/year-month-select";
  69. import UploadExcelDialog from "@/components/upload-excel-dialog";
  70. import {exportBloByPost} from "@/api/common";
  71. import {getToken} from "@/util/auth";
  72. import {downloadXls} from "@/util/util";
  73. import { deepClone, getMonthDatesWithMarkers } from "@/util/util";
  74. import moment from "moment";
  75. import ChineseHoliday from "@chnlib/chinese-holiday";
  76. import { mapGetters } from "vuex";
  77. export default window.$crudCommon({
  78. components: {
  79. YearMonthSelect,
  80. UploadExcelDialog,
  81. },
  82. data() {
  83. return {
  84. params: {
  85. yearAndMonth: "",
  86. },
  87. defaultColumns: [
  88. {
  89. label: '研发项目编号',
  90. prop: 'xmbh',
  91. span: 24,
  92. width: 100,
  93. align: 'center',
  94. type: 'input',
  95. showOverflowTooltip: true,
  96. disabled: true,
  97. },
  98. {
  99. label: '研发项目名称',
  100. prop: 'xmmc',
  101. type: 'input',
  102. span: 24,
  103. width: 110,
  104. align: 'center',
  105. search: true,
  106. showOverflowTooltip: true,
  107. disabled: true,
  108. },
  109. {
  110. label: '姓名',
  111. prop: 'name',
  112. span: 24,
  113. search: true,
  114. width: 90,
  115. cell: true,
  116. align: 'center',
  117. // type: 'select',
  118. showOverflowTooltip: true,
  119. // dicUrl: '',
  120. // dicFlag: true,
  121. // dicFormatter:(res)=>{
  122. // return res.data.records
  123. // },
  124. // dicQuery: {
  125. // current: 1,
  126. // size: 99999,
  127. // yearAndMonth: ''
  128. // },
  129. // props: {
  130. // label: 'name',
  131. // value: 'id',
  132. // },
  133. disabled: true,
  134. rules: [
  135. {
  136. required: true,
  137. message: '请选择技术人员',
  138. trigger: 'blur',
  139. },
  140. ],
  141. },
  142. {
  143. label: '工号',
  144. prop: 'number',
  145. type: 'input',
  146. span: 24,
  147. width: 90,
  148. align: 'center',
  149. search: true,
  150. disabled: true,
  151. showOverflowTooltip: true,
  152. },
  153. {
  154. label: '身份证号',
  155. prop: 'idCard',
  156. type: 'input',
  157. span: 24,
  158. search: true,
  159. disabled: true,
  160. width: 150,
  161. align: 'center',
  162. showOverflowTooltip: true,
  163. },
  164. {
  165. label: '科研人员类别',
  166. prop: 'personnelType',
  167. type: 'select',
  168. span: 24,
  169. width: 100,
  170. align: 'center',
  171. search: true,
  172. disabled: true,
  173. dicFlag: true,
  174. dicUrl: '/api/kd-system/dict/dictionary?code=renyuanleixing',
  175. props: {
  176. label: 'dictValue',
  177. value: 'dictKey',
  178. },
  179. showOverflowTooltip: true,
  180. },
  181. ],
  182. oldMonthData: [],
  183. wideTableColumns: [],
  184. printTitle: "",
  185. };
  186. },
  187. watch: {
  188. 'params.yearAndMonth'(newVal) {
  189. this.refreshTableCol(newVal);
  190. this.page.currentPage = 1;
  191. this.getList(this.page);
  192. }
  193. },
  194. computed: {
  195. ...mapGetters(['tag']),
  196. pageTitle() {
  197. let yearAndMonthCN = this.params.yearAndMonth ? moment(this.params.yearAndMonth).format('yyyy年MM月') : '';
  198. return `${yearAndMonthCN}${this.tag.label}`.replace(/每月/g, '');
  199. }
  200. },
  201. methods: {
  202. handleBeforeOpen(done, type) {
  203. this.oldMonthData = this.form.dailyAttendances || [];
  204. done();
  205. },
  206. refreshTableCol(yearAndMonth) {
  207. this.option.column = [];
  208. let yearMonth = moment(yearAndMonth).format("YYYY-MM").split("-");
  209. const monthDays = getMonthDatesWithMarkers(Number(yearMonth[0]), Number(yearMonth[1]));
  210. for (let i=monthDays.length; i<31; i++) {
  211. let _day = i+1
  212. monthDays.push({
  213. day: _day,
  214. display: false,
  215. hide: true
  216. });
  217. }
  218. let newColumns = deepClone(this.defaultColumns);
  219. monthDays.forEach((item, index) => {
  220. let col = {};
  221. if (item.hide) {
  222. col = {
  223. hide: true,
  224. display: false,
  225. };
  226. } else {
  227. col = {
  228. prop: moment(item.dateString).format('yyyyMMDD'),
  229. label: item.dayName + "" + item.day.toString(),
  230. type: 'number',
  231. precision: 1,
  232. min: 0,
  233. width: 50,
  234. align: 'center',
  235. isWorkDay: ChineseHoliday.isWorkday(item.dateString),
  236. showOverflowTooltip: true,
  237. };
  238. }
  239. if (!ChineseHoliday.isWorkday(item.dateString)) {
  240. col.labelClassName = 'red';
  241. col.labelClassName = 'red';
  242. }
  243. newColumns.push(col);
  244. });
  245. // newColumns.push({
  246. // prop: "monthlyHours",
  247. // label: "研发出勤/小时",
  248. // type: 'number',
  249. // precision: 1,
  250. // min: 0,
  251. // width: 70,
  252. // align: 'center',
  253. // showOverflowTooltip: true,
  254. // display: false,
  255. // search: true,
  256. // }, {
  257. // prop: "monthlyDays",
  258. // label: "研发出勤/天",
  259. // type: 'number',
  260. // precision: 1,
  261. // min: 0,
  262. // width: 70,
  263. // align: 'center',
  264. // showOverflowTooltip: true,
  265. // display: false,
  266. // });
  267. this.option.column = newColumns;
  268. },
  269. handleImport() {
  270. let excelParams = { yearAndMonth: this.params.yearAndMonth };
  271. this.$refs.uploadExcelDialog.open('/api/kd-scientific/xm/technician/hours/import', excelParams);
  272. },
  273. uploadAfter() {
  274. this.page.currentPage = 1;
  275. this.getList(this.page);
  276. },
  277. getFormData() {
  278. const newFormData = { ...this.form, yearAndMonth: this.params.yearAndMonth };
  279. let dailyAttendances = [];
  280. let _oldMonthData = this.oldMonthData;
  281. Object.keys(this.form).forEach(key => {
  282. if (key.indexOf(this.params.yearAndMonth) > -1) {
  283. let oldObj = _oldMonthData.find(old => old.workDate == key) || {};
  284. if (this.form[key] || oldObj.workHours || oldObj.workHours === 0) {
  285. let workHours = this.form[key];
  286. if (workHours === undefined || workHours === null || workHours === '') {
  287. workHours = null;
  288. }
  289. dailyAttendances.push({
  290. workDate: key,
  291. workHours
  292. });
  293. }
  294. delete newFormData[key];
  295. }
  296. });
  297. // 获取最新修改过的数据
  298. let updatedMonthData = [];
  299. dailyAttendances.forEach(item => {
  300. let oldObj = _oldMonthData.find(old => item.workDate === old.workDate);
  301. if (!!oldObj && item.workHours !== oldObj.workHours) {
  302. updatedMonthData.push(item);
  303. } else if (!oldObj) {
  304. updatedMonthData.push(item);
  305. }
  306. })
  307. newFormData.dailyAttendances = updatedMonthData;
  308. return newFormData;
  309. },
  310. getDelParams(row) {
  311. return { yearAndMonth: this.params.yearAndMonth, xmAndUnicodeList: [{ xmId: row.xmId, unicode: row.unicode }] };
  312. },
  313. getBatchDelParams() {
  314. let xmAndUnicodeList = [];
  315. this.data.forEach(item => {
  316. if (this.ids.indexOf(item.id) > -1) {
  317. xmAndUnicodeList.push({ xmId: item.xmId, unicode: item.unicode });
  318. }
  319. });
  320. return { yearAndMonth: this.params.yearAndMonth, xmAndUnicodeList };
  321. },
  322. handleExport() {
  323. exportBloByPost(`/api/kd-scientific/xm/technician/hours/export?${this.website.tokenHeader}=${getToken()}`, this.params).then(res => {
  324. downloadXls(res.data, `${this.pageTitle}.xlsx`);
  325. });
  326. },
  327. /**
  328. * 打印表格
  329. * @param isLandscape 是否横向打印
  330. */
  331. printTable(isLandscape) {
  332. this.wideTableColumns = this.$refs.crud.columnOption;
  333. this.$nextTick(() => {
  334. this.$refs.printWideTable.printTable(isLandscape);
  335. })
  336. },
  337. headerCellStyle ({ column }) {
  338. let color = column.labelClassName ? 'red' : 'rgba(0,0,0,.85)';
  339. return {
  340. color
  341. }
  342. }
  343. },
  344. }, {
  345. // 模块路径
  346. name: 'yfCostManage/basicDataSetting/workHoursRecords',
  347. res: ({ data }) => {
  348. let newList = [];
  349. data.records.forEach((item) => {
  350. let objData = {
  351. ...item,
  352. id: item.unicode+'_'+item.xmId,
  353. monthlyHours: (item.monthlyHours || 0).toFixed(1),
  354. monthlyDays: (item.monthlyDays || 0).toFixed(1)
  355. };
  356. item.dailyAttendances.forEach(hourItem => {
  357. objData[hourItem.workDate] = (hourItem.workHours || 0).toFixed(1);
  358. });
  359. newList.push(objData);
  360. });
  361. data.records = newList;
  362. return data;
  363. },
  364. });
  365. </script>