workAttendance-list.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <template>
  2. <div
  3. v-loading="downloadLoading"
  4. element-loading-text="导出中"
  5. element-loading-spinner="el-icon-loading"
  6. >
  7. <el-breadcrumb separator=">">
  8. <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
  9. <el-breadcrumb-item>
  10. <a href="#">门禁管理</a>
  11. </el-breadcrumb-item>
  12. <el-breadcrumb-item>
  13. <a href="/business/workAttendance">考勤统计</a>
  14. </el-breadcrumb-item>
  15. </el-breadcrumb>
  16. <el-divider></el-divider>
  17. <!--
  18. 要resetFields起作用,必须配置:model和prop
  19. -->
  20. <el-form
  21. ref="queryForm"
  22. :model="queryModel"
  23. :rules="ruleValidate"
  24. inline
  25. class="demo-form-inline"
  26. >
  27. <div>
  28. <el-row>
  29. <el-form-item label="单位" prop="companyId">
  30. <!-- <el-select
  31. v-model="queryModel.companyId"
  32. size="mini"
  33. filterable
  34. placeholder="请选择"
  35. style="width:220px"
  36. >
  37. <el-option
  38. v-for="company in companyResult"
  39. :key="company.id"
  40. :label="company.name"
  41. :value="company.id"
  42. ></el-option>
  43. </el-select>-->
  44. <el-select-tree
  45. :props="props"
  46. :options="companyResult"
  47. v-model="queryModel.companyId"
  48. height="200"
  49. ></el-select-tree>
  50. &nbsp;
  51. <el-checkbox v-model="queryModel.subordinate">是否包含下级单位</el-checkbox>
  52. </el-form-item>
  53. <el-form-item label="统计区间" prop="timeRanges">
  54. <el-date-picker
  55. v-model="queryModel.timeRanges"
  56. type="daterange"
  57. range-separator="至"
  58. start-placeholder="开始日期"
  59. end-placeholder="结束日期"
  60. value-format="yyyy-MM-dd"
  61. :default-time="timeRangesDefaultTime"
  62. size="mini"
  63. ></el-date-picker>
  64. </el-form-item>
  65. <el-form-item label="姓名" prop="name">
  66. <el-input type="text" size="mini" v-model="queryModel.name" style="width:100px;"></el-input>
  67. </el-form-item>
  68. <!-- <el-form-item v-if="position1Show" :label="position1" prop="position1">
  69. <el-input type="text" size="mini" v-model="queryModel.position1"></el-input>
  70. </el-form-item>
  71. <el-form-item v-if="position2Show" :label="position2" prop="position2">
  72. <el-input type="text" size="mini" v-model="queryModel.position2"></el-input>
  73. </el-form-item>
  74. <el-form-item v-if="position3Show" :label="position3" prop="position3">
  75. <el-input type="text" size="mini" v-model="queryModel.position3"></el-input>
  76. </el-form-item> -->
  77. <el-form-item>
  78. <el-button
  79. type="primary"
  80. size="mini"
  81. style="margin-left: 8px"
  82. @click="handleQuery('queryForm')"
  83. >查询</el-button>&nbsp;
  84. <el-button
  85. type="info"
  86. size="mini"
  87. style="margin-left: 8px"
  88. @click="handleReset('queryForm')"
  89. >重置</el-button>
  90. </el-form-item>
  91. </el-row>
  92. </div>
  93. <!-- <div>
  94. <el-form-item v-if="position4Show" :label="position4" prop="position4">
  95. <el-input type="text" size="mini" v-model="queryModel.position4"></el-input>
  96. </el-form-item>
  97. <el-form-item v-if="position5Show" :label="position5" prop="position5">
  98. <el-input type="text" size="mini" v-model="queryModel.position5"></el-input>
  99. </el-form-item>
  100. </div> -->
  101. </el-form>
  102. <el-divider></el-divider>
  103. <el-row class="button-group">
  104. <el-button
  105. type="primary"
  106. size="small"
  107. plain
  108. icon="el-icon-download"
  109. :loading="downloadLoading"
  110. @click="exportXls"
  111. >导出数据</el-button>
  112. <el-button
  113. type="primary"
  114. size="small"
  115. plain
  116. icon="el-icon-edit"
  117. :loading="loading"
  118. @click="handleBatchUpdate"
  119. v-show="batchUpdateVisible"
  120. >重新生成考勤数据</el-button>
  121. </el-row>
  122. <el-table
  123. ref="formTable"
  124. stripe
  125. :data="tableData"
  126. :height="tableHeight"
  127. style="width: 100%"
  128. v-loading="loading"
  129. :element-loading-text="loadingText">
  130. <el-table-column label="序号" fixed="left" type="index" :index="indexMethod"></el-table-column>
  131. <el-table-column label="姓名" fixed="left" prop="name"></el-table-column>
  132. <el-table-column label="编号" prop="personId"></el-table-column>
  133. <el-table-column label="公司" prop="companyName" width="200"></el-table-column>
  134. <el-table-column label="上级部门" prop="parentDepartmentName" width="200"></el-table-column>
  135. <el-table-column label="部门" prop="departmentName" width="200"></el-table-column>
  136. <!-- <el-table-column prop="position1" :label="position1" v-if="position1Show"></el-table-column>
  137. <el-table-column prop="position2" :label="position2" v-if="position2Show"></el-table-column>
  138. <el-table-column prop="position3" :label="position3" v-if="position3Show"></el-table-column>
  139. <el-table-column prop="position4" :label="position4" v-if="position4Show"></el-table-column>
  140. <el-table-column prop="position5" :label="position5" v-if="position5Show"></el-table-column> -->
  141. <el-table-column label="出勤天数" prop="workDays"></el-table-column>
  142. <el-table-column label="请假天数" prop="restDays"></el-table-column>
  143. <el-table-column label="迟到次数" prop="lateNum"></el-table-column>
  144. <el-table-column label="早退次数" prop="leaveNum"></el-table-column>
  145. <el-table-column label="上班缺卡次数" prop="missCardOnWorkCount"></el-table-column>
  146. <el-table-column label="下班缺卡次数" prop="missCardOffWorkCount"></el-table-column>
  147. <el-table-column label="旷工天数" prop="missCardAllDayCount"></el-table-column>
  148. <template v-for="col in dayColumns">
  149. <el-table-column :label="col.label" :prop="col.name" :key="col.name" width="200px">
  150. <template slot-scope="{row}">
  151. <div style="display:flex;flex-direction:column;">
  152. <template v-for="(item,index) in row.workAttendanceMap[col.name]">
  153. <div v-html="showItem(item)" :key="index"></div>
  154. </template>
  155. </div>
  156. </template>
  157. </el-table-column>
  158. </template>
  159. </el-table>
  160. <el-pagination
  161. :current-page.sync="pageIndex"
  162. :total="totalElements"
  163. :page-sizes="pageSizeList"
  164. @current-change="changePage"
  165. @size-change="pageSizeChange"
  166. layout="total, sizes, prev, pager, next, jumper"
  167. ></el-pagination>
  168. </div>
  169. </template>
  170. <script>
  171. import Constant from "@/constant";
  172. import workAttendanceApi from "@/api/business/workAttendance";
  173. import companyPositionApi from "@/api/base/companyPosition";
  174. import companyInfoApi from "@/api/base/companyInfo";
  175. import permissionApi from "@/api/sys/permission";
  176. import SelectTree from "@/components/SelectTree";
  177. import NProgress from "nprogress"; // progress bar
  178. import "nprogress/nprogress.css"; // progress bar style
  179. export default {
  180. name: "BusinessWorkAttendanceList",
  181. data() {
  182. var self = this;
  183. return {
  184. ruleValidate: {
  185. companyId: [{ required: true, message: "不能为空", trigger: "blur" }],
  186. timeRanges: [
  187. { required: true, message: "请选择事件范围", trigger: "blur" }
  188. ]
  189. },
  190. queryModel: {
  191. companyId: "",
  192. timeRanges: "",
  193. subordinate: false,
  194. name: "",
  195. jobNumber: "",
  196. position1: "",
  197. position2: "",
  198. position3: "",
  199. position4: "",
  200. position5: ""
  201. },
  202. loading: false,
  203. loadingText: "加载中",
  204. tableData: [],
  205. pageIndex: 1,
  206. pageSize: 20,
  207. totalPages: 0,
  208. totalElements: 0,
  209. field: "",
  210. direction: "",
  211. pageSizeList: [20, 30, 50],
  212. multipleSelection: [],
  213. showModal: false,
  214. modalTitle: "",
  215. businessKey: "",
  216. downloadLoading: false,
  217. tableHeight: 400,
  218. timeRangesDefaultTime: [],
  219. companyResult: [],
  220. editorOption: {
  221. modules: {
  222. toolbar: "title" // 设置文本编辑器的头部是否展示
  223. },
  224. placeholder: "", // 文本框为空时 , 占位文本
  225. theme: "snow" // 或者为 `bubble`
  226. },
  227. downloadUrl: "",
  228. dayColumns: [],
  229. tmplKey: "",
  230. position1: "",
  231. position2: "",
  232. position3: "",
  233. position4: "",
  234. position5: "",
  235. position1Show: false,
  236. position2Show: false,
  237. position3Show: false,
  238. position4Show: false,
  239. position5Show: false,
  240. treeData: [],
  241. props: {
  242. // 配置项(必选)
  243. value: "id",
  244. label: "name",
  245. children: "children"
  246. },
  247. batchUpdateVisible: false
  248. };
  249. },
  250. created() {
  251. var self = this;
  252. companyInfoApi.list().then(function(response) {
  253. var jsonData = response.data;
  254. if (jsonData.result) {
  255. if (jsonData.data != null && jsonData.data != "") {
  256. self.companyResult = jsonData.data;
  257. }
  258. }
  259. });
  260. companyPositionApi.detailForCompany().then(function(response) {
  261. var jsonData = response.data.data;
  262. if (jsonData.position1Name != null && jsonData.position1Name != "") {
  263. self.position1 = jsonData.position1Name;
  264. self.position1Show = true;
  265. }
  266. if (jsonData.position2Name != null && jsonData.position2Name != "") {
  267. self.position2 = jsonData.position2Name;
  268. self.position2Show = true;
  269. }
  270. if (jsonData.position3Name != null && jsonData.position3Name != "") {
  271. self.position3 = jsonData.position3Name;
  272. self.position3Show = true;
  273. }
  274. if (jsonData.position4Name != null && jsonData.position4Name != "") {
  275. self.position4 = jsonData.position4Name;
  276. self.position4Show = true;
  277. }
  278. if (jsonData.position5Name != null && jsonData.position5Name != "") {
  279. self.position5 = jsonData.position5Name;
  280. self.position5Show = true;
  281. }
  282. });
  283. permissionApi.hasPermission("post","/business/workAttendance/batchUpdate")
  284. .then(response=>{
  285. this.batchUpdateVisible = response.data.result;
  286. });
  287. this.getCurrentMonthFirst();
  288. this.loadTree();
  289. },
  290. methods: {
  291. getSelectedValue(value) {
  292. this.queryModel.companyId = value;
  293. },
  294. loadTree() {
  295. var formData = new FormData();
  296. companyInfoApi.loadChildren(formData).then(resp => {
  297. var jsonData = resp.data;
  298. if (jsonData.result) {
  299. this.treeData = jsonData.data;
  300. } else {
  301. this.$message.error(jsonData.message + "");
  302. }
  303. });
  304. },
  305. loadChildren(tree, treeNode, resolve) {
  306. console.log(tree);
  307. var formData = new FormData();
  308. formData.append("parentId", tree.id);
  309. companyInfoApi.loadChildren(formData).then(resp => {
  310. var jsonData = resp.data;
  311. if (jsonData.result) {
  312. resolve(jsonData.data);
  313. } else {
  314. this.$message.error(jsonData.message + "");
  315. }
  316. });
  317. },
  318. indexMethod(index) {
  319. return (this.pageIndex - 1) * this.pageSize + (index + 1);
  320. },
  321. changePage(pageIndex) {
  322. var self = this;
  323. self.pageIndex = pageIndex;
  324. var formData = new FormData();
  325. formData.append("pageIndex", self.pageIndex);
  326. formData.append("pageSize", self.pageSize);
  327. if (self.queryModel.companyId == null) {
  328. self.queryModel.companyId = "";
  329. }
  330. formData.append("companyId", self.queryModel.companyId);
  331. formData.append("subordinate", self.queryModel.subordinate);
  332. formData.append("name", self.queryModel.name);
  333. var startDate = "";
  334. var endDate = "";
  335. var timeRanges = self.queryModel.timeRanges + "";
  336. if (timeRanges != "" && timeRanges != null) {
  337. timeRanges = timeRanges.split(",");
  338. startDate = timeRanges[0];
  339. endDate = timeRanges[1];
  340. }
  341. formData.append("startDate", startDate);
  342. formData.append("endDate", endDate);
  343. formData.append("position1", self.queryModel.position1);
  344. formData.append("position2", self.queryModel.position2);
  345. formData.append("position3", self.queryModel.position3);
  346. formData.append("position4", self.queryModel.position4);
  347. formData.append("position5", self.queryModel.position5);
  348. self.loading = true;
  349. self.loadingText = "加载中";
  350. workAttendanceApi.statList(formData).then(function(response) {
  351. var jsonData = response.data;
  352. self.loading = false;
  353. if (jsonData.result) {
  354. self.tableData = jsonData.data.data;
  355. self.totalPages = jsonData.data.totalPage;
  356. self.totalElements = jsonData.data.totalElements;
  357. self.dayColumns = jsonData.data.dayColumns;
  358. self.tmplKey = jsonData.data.tmplKey;
  359. //45为分页栏的高度
  360. //页面高度-列表上面的高度-分页栏高度
  361. self.tableHeight =
  362. window.innerHeight - self.$refs.formTable.$el.offsetTop - 100;
  363. } else {
  364. self.$message({
  365. type: "warning",
  366. message: jsonData.message
  367. });
  368. }
  369. });
  370. },
  371. pageSizeChange(pageSize) {
  372. this.pageSize = pageSize;
  373. this.changePage(1);
  374. },
  375. showItem(item) {
  376. var content = [];
  377. var arr = item.recordTime.split(" ");
  378. if (arr.length > 1) {
  379. content.push(arr[1]);
  380. } else {
  381. content.push(arr[0]);
  382. }
  383. content.push(" ");
  384. if (item.sources == "1") {
  385. content.push("内勤");
  386. } else {
  387. content.push("外勤");
  388. }
  389. if (item.classifier == 1) {
  390. content.push("上班");
  391. } else {
  392. content.push("下班");
  393. }
  394. var fontColor = "";
  395. if (item.result == "0") {
  396. content.push("缺卡");
  397. fontColor = "red";
  398. } else if (item.result == "1") {
  399. content.push("打卡");
  400. fontColor = "green";
  401. } else if (item.result == "2") {
  402. content.push("迟到");
  403. } else if (item.result == "3") {
  404. content.push("早退");
  405. }else if(item.result == "4"){
  406. content.push("请假");
  407. fontColor = "#11A8CD";
  408. }
  409. if(item.workOutRemark!=null){
  410. content.push("(" + item.workOutRemark + ")");
  411. }
  412. return `<font color='${fontColor}'>` + content.join("") + "</font>";
  413. },
  414. handleQuery() {
  415. var self = this;
  416. this.$refs["queryForm"].validate(valid => {
  417. if (valid) {
  418. self.changePage(1);
  419. }
  420. });
  421. },
  422. handleReset(name) {
  423. this.$refs[name].resetFields();
  424. },
  425. exportXls() {
  426. var self = this;
  427. //导出
  428. this.$refs["queryForm"].validate(valid => {
  429. if (valid) {
  430. self.downloadLoading = true;
  431. var formData = new FormData();
  432. if (self.queryModel.companyId == null) {
  433. self.queryModel.companyId = "";
  434. }
  435. formData.append("companyId", self.queryModel.companyId);
  436. formData.append("subordinate", self.queryModel.subordinate);
  437. formData.append("name", self.queryModel.name);
  438. var startDate = "";
  439. var endDate = "";
  440. var timeRanges = self.queryModel.timeRanges + "";
  441. if (timeRanges != "" && timeRanges != null) {
  442. timeRanges = timeRanges.split(",");
  443. startDate = timeRanges[0];
  444. endDate = timeRanges[1];
  445. }
  446. formData.append("startDate", startDate);
  447. formData.append("endDate", endDate);
  448. formData.append("position1", self.queryModel.position1);
  449. formData.append("position2", self.queryModel.position2);
  450. formData.append("position3", self.queryModel.position3);
  451. formData.append("position4", self.queryModel.position4);
  452. formData.append("position5", self.queryModel.position5);
  453. workAttendanceApi.exportXls(formData).then(function(response) {
  454. var jsonData = response.data;
  455. self.downloadLoading = false;
  456. if (jsonData.result) {
  457. if(document.location.href.startsWith("https://")){
  458. jsonData.data = jsonData.data.replace("http://","https://");
  459. }
  460. self.$message({
  461. type: "success",
  462. message: `报表已生成,<a href="${jsonData.data}">请点击链接下载</a>`,
  463. dangerouslyUseHTMLString: true,
  464. duration: 30000
  465. });
  466. } else {
  467. self.$message({
  468. type: "warning",
  469. message: jsonData.message
  470. });
  471. }
  472. });
  473. }
  474. });
  475. },
  476. //初始化日期
  477. getCurrentMonthFirst() {
  478. var self = this;
  479. var date = new Date();
  480. date.setDate(1);
  481. var month = parseInt(date.getMonth() + 1);
  482. var startTime = date.getFullYear() + "-" + month + "-" + date.getDate();
  483. date.setMonth(date.getMonth()+1);
  484. var lastDate = new Date(date.getTime()-1000*60*60*24);
  485. var endTime = lastDate.getFullYear() + "-" + month + "-" + lastDate.getDate();
  486. self.queryModel.timeRanges = [startTime, endTime];
  487. },
  488. handleBatchUpdate() {
  489. var self = this;
  490. self.$confirm("是否确认更新考勤数据?", "提示", {
  491. confirmButtonText: "确定",
  492. cancelButtonText: "取消",
  493. type: "warning",
  494. }).then(() => {
  495. var formData = new FormData();
  496. formData.append("companyId", this.queryModel.companyId);
  497. formData.append("startDate", this.queryModel.timeRanges[0]);
  498. formData.append("endDate", this.queryModel.timeRanges[1]);
  499. self.loading = true;
  500. self.loadingText = "考勤数据生成中...";
  501. workAttendanceApi.batchUpdate(formData).then(function(response) {
  502. var jsonData = response.data;
  503. self.loading = false;
  504. if (jsonData.result) {
  505. self.$message({
  506. type: "success",
  507. message: jsonData.message + ""
  508. });
  509. self.handleQuery();
  510. } else {
  511. self.$message({
  512. type: "warning",
  513. message: jsonData.message + ""
  514. });
  515. }
  516. });
  517. });
  518. }
  519. },
  520. mounted: function() {},
  521. components: {
  522. "el-select-tree": SelectTree
  523. }
  524. };
  525. </script>
  526. <style lang="scss" scoped>
  527. .el-breadcrumb {
  528. margin: 10px;
  529. line-height: 20px;
  530. }
  531. .el-divider {
  532. margin: 5px 0;
  533. }
  534. .demo-form-inline {
  535. margin-left: 10px;
  536. text-align: left;
  537. }
  538. .button-group {
  539. margin-left: 10px;
  540. text-align: left;
  541. }
  542. </style>