ManagementTable.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <template>
  2. <div
  3. @mousedown="startSelecting"
  4. @mousemove="updateSelection"
  5. @mouseup="stopSelecting"
  6. >
  7. <ele-pro-table
  8. ref="table"
  9. :needPage="false"
  10. :columns="columns"
  11. :datasource="tableData"
  12. @cell-click="cellClick"
  13. @row-click="rowClick"
  14. @header-click="headerClick"
  15. cache-key="systemRoleTable19"
  16. >
  17. <!-- 表头工具栏 -->
  18. <template v-slot:toolbar>
  19. <el-form class="ele-form-search">
  20. <el-row :gutter="15">
  21. <el-col :span="4">
  22. <el-form-item style="margin-bottom: 0">
  23. <el-input
  24. clearable
  25. :placeholder="type == 'person' ? '搜索人员' : '搜索班组'"
  26. v-model.trim="searchKey"
  27. @input="search"
  28. ></el-input>
  29. </el-form-item>
  30. </el-col>
  31. <el-col :span="4">
  32. <el-form-item style="margin-bottom: 0">
  33. <el-date-picker
  34. v-model="time"
  35. type="month"
  36. @change="timeChange"
  37. value-format="yyyy-MM"
  38. placeholder="选择月"
  39. >
  40. </el-date-picker>
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="16">
  44. <el-form-item class="btn-wrap" style="margin-bottom: 0">
  45. <el-button @click="save" type="primary">保存</el-button>
  46. <el-button @click="rest">重置</el-button>
  47. <el-button @click="anbp" type="primary">{{
  48. type == 'person' ? '按班排' : '按人排'
  49. }}</el-button>
  50. </el-form-item>
  51. </el-col>
  52. </el-row>
  53. </el-form>
  54. </template>
  55. <template v-slot:item="{ row }">
  56. {{ row.item.name }}
  57. </template>
  58. <template v-slot:bcHeader="{ column }">
  59. <div v-html="showheaderName(column.label)"></div>
  60. </template>
  61. <template v-slot:bc="{ column, row, $index }">
  62. <div
  63. class="tabel-cell-item"
  64. :style="`background-color: ${showColor(el)};`"
  65. v-for="el in showName(row, column)"
  66. :key="el.id"
  67. ref="tabelItem"
  68. >
  69. {{ el.name }}
  70. </div>
  71. </template>
  72. </ele-pro-table>
  73. <div v-if="isSelecting" class="selection-box" :style="selectionStyle"></div>
  74. </div>
  75. </template>
  76. <script>
  77. import * as dayjs from 'dayjs';
  78. import { debounce } from 'throttle-debounce';
  79. export default {
  80. props: ['selectedClasses', 'classesColor'],
  81. data() {
  82. const columnsDefault = {
  83. person: {
  84. prop: 'item',
  85. label: '姓名',
  86. slot: 'item'
  87. },
  88. team: {
  89. prop: 'item',
  90. label: '班组',
  91. slot: 'item'
  92. }
  93. };
  94. return {
  95. isSelecting: false,
  96. startX: 0,
  97. startY: 0,
  98. endX: 0,
  99. endY: 0,
  100. type: 'person', // team 班次 person 人员
  101. columnsDefault,
  102. tableData: [],
  103. //按人排数据
  104. personData: [],
  105. //按班排数据
  106. teamData: [],
  107. time: '',
  108. columns: [],
  109. searchKey: '',
  110. dict: {
  111. week: {
  112. 0: '周末',
  113. 1: '周一',
  114. 2: '周二',
  115. 3: '周三',
  116. 4: '周四',
  117. 5: '周五',
  118. 6: '周六'
  119. }
  120. }
  121. };
  122. },
  123. computed: {
  124. selectionStyle() {
  125. return {
  126. position: 'absolute',
  127. left: `${Math.min(this.startX, this.endX)}px`,
  128. top: `${Math.min(this.startY, this.endY)}px`,
  129. width: `${Math.abs(this.endX - this.startX)}px`,
  130. height: `${Math.abs(this.endY - this.startY)}px`,
  131. border: '2px dashed black'
  132. };
  133. }
  134. },
  135. created() {
  136. this.search = debounce(500, this.search);
  137. this.time = dayjs(new Date()).format('YYYY-MM');
  138. this.initColumns();
  139. },
  140. methods: {
  141. startSelecting(event) {
  142. // return
  143. this.isSelecting = true;
  144. this.startX = event.clientX;
  145. this.startY = event.clientY;
  146. this.endX = this.startX;
  147. this.endY = this.startY;
  148. console.log(this.startX, this.startY, this.endX, this.endY);
  149. },
  150. updateSelection(event) {
  151. // return
  152. if (this.isSelecting) {
  153. this.endX = event.clientX;
  154. this.endY = event.clientY;
  155. }
  156. },
  157. stopSelecting() {
  158. // return
  159. this.isSelecting = false;
  160. // 在这里处理框选结果,例如计算被框选的元素
  161. this.selectedItems = this.calculateSelectedItems();
  162. },
  163. calculateSelectedItems() {
  164. return this.tableData.filter((item, index) => {
  165. console.log(item, 'item');
  166. console.log(
  167. this.$refs[
  168. (this.type == 'person' ? item.item?.userId : item.item?.teamId) +
  169. index
  170. ]
  171. );
  172. const rect =
  173. this.$refs[
  174. (this.type == 'person' ? item.item?.userId : item.item?.teamId) +
  175. index
  176. ].getBoundingClientRect();
  177. console.log(rect);
  178. return (
  179. rect.left < this.endX &&
  180. rect.right > this.startX &&
  181. rect.top < this.endY &&
  182. rect.bottom > this.startY
  183. );
  184. });
  185. },
  186. // 人员班次数据切换
  187. setTableData() {
  188. switch (this.type) {
  189. case 'person':
  190. this.tableData = this.personData;
  191. break;
  192. case 'team':
  193. this.tableData = this.teamData;
  194. break;
  195. default:
  196. break;
  197. }
  198. },
  199. // 初始化columns
  200. initColumns() {
  201. let Month = dayjs(this.time).month() + 1;
  202. let MonthNum = dayjs(this.time).daysInMonth();
  203. let columns = [];
  204. for (let index = 1; index <= MonthNum; index++) {
  205. let week = dayjs(`${this.time}-${index}`).day();
  206. columns.push({
  207. prop: `${this.time}-${this.setNUm(index)} 00:00:00`,
  208. label: `${Month}-${index}/${week}`,
  209. slot: 'bc',
  210. headerSlot: 'bcHeader',
  211. width: 60
  212. });
  213. }
  214. this.columns = [];
  215. switch (this.type) {
  216. case 'person':
  217. this.columns.push(this.columnsDefault.person);
  218. break;
  219. case 'team':
  220. this.columns.push(this.columnsDefault.team);
  221. break;
  222. default:
  223. break;
  224. }
  225. this.columns = [].concat(this.columns, columns);
  226. },
  227. // 设置数据
  228. setData(personData, teamData) {
  229. this.personData = personData;
  230. this.teamData = teamData;
  231. this.setTableData();
  232. },
  233. showName(row, column) {
  234. let list = row[column.property];
  235. if (list) {
  236. list.forEach((n) => {
  237. n.name = n.name.slice(0, 4);
  238. n['userId'] = row.item?.userId;
  239. n['teamId'] = row.item?.teamId;
  240. });
  241. return list;
  242. } else {
  243. return [];
  244. }
  245. },
  246. showheaderName(label) {
  247. let list = label.split('/');
  248. let week = this.dict.week[list[1]];
  249. return `${list[0]}</br>${week}`;
  250. },
  251. // 设置颜色
  252. showColor(item) {
  253. return this.classesColor[item.id];
  254. },
  255. // 单元格点击
  256. cellClick(row, column) {
  257. const selectedClasses = JSON.parse(
  258. JSON.stringify(this.selectedClasses)
  259. );
  260. if (column.property !== 'item' && selectedClasses.length > 0) {
  261. if (selectedClasses[0].id == 'clear') {
  262. this.$delete(row, column.property);
  263. } else {
  264. this.$set(row, column.property, selectedClasses);
  265. }
  266. // 按班排同步人员
  267. if (this.type == 'team') {
  268. this.SyncCellClick(row, column);
  269. }
  270. }
  271. },
  272. // 行点击
  273. rowClick(row, column) {
  274. const selectedClasses = JSON.parse(
  275. JSON.stringify(this.selectedClasses)
  276. );
  277. if (column.property == 'item' && selectedClasses.length > 0) {
  278. if (selectedClasses[0].id == 'clear') {
  279. this.columns.forEach((n) => {
  280. if (n.prop !== 'item') {
  281. this.$delete(row, n.prop);
  282. }
  283. });
  284. } else {
  285. this.columns.forEach((n) => {
  286. if (n.prop !== 'item') {
  287. this.$set(row, n.prop, selectedClasses);
  288. }
  289. });
  290. }
  291. // 按班排同步人员
  292. if (this.type == 'team') {
  293. this.SyncRowClick(row, column);
  294. }
  295. }
  296. },
  297. // 列点击
  298. headerClick(column) {
  299. const selectedClasses = JSON.parse(
  300. JSON.stringify(this.selectedClasses)
  301. );
  302. if (column.property !== 'item' && selectedClasses.length > 0) {
  303. if (selectedClasses[0].id == 'clear') {
  304. this.tableData.forEach((n) => {
  305. this.$delete(n, column.property);
  306. });
  307. } else {
  308. this.tableData.forEach((n) => {
  309. this.$set(n, column.property, selectedClasses);
  310. });
  311. }
  312. // 按班排同步人员
  313. if (this.type == 'team') {
  314. this.SyncHeaderClick(column);
  315. }
  316. }
  317. },
  318. // 按班排同步人员-单元格点击
  319. SyncCellClick(row, column) {
  320. const selectedClasses = JSON.parse(
  321. JSON.stringify(this.selectedClasses)
  322. );
  323. let Person = this.getid_Person(row.item.teamId);
  324. if (selectedClasses[0].id == 'clear') {
  325. Person.forEach((n) => {
  326. this.$delete(n, column.property);
  327. });
  328. } else {
  329. Person.forEach((n) => {
  330. this.$set(n, column.property, selectedClasses);
  331. });
  332. }
  333. },
  334. // 按班排同步人员-行点击
  335. SyncRowClick(row, column) {
  336. const selectedClasses = JSON.parse(
  337. JSON.stringify(this.selectedClasses)
  338. );
  339. let Person = this.getid_Person(row.item.teamId);
  340. if (selectedClasses[0].id == 'clear') {
  341. Person.forEach((n) => {
  342. this.columns.forEach((nl) => {
  343. if (nl.prop !== 'item') {
  344. this.$delete(n, nl.prop);
  345. }
  346. });
  347. });
  348. } else {
  349. Person.forEach((n) => {
  350. this.columns.forEach((nl) => {
  351. if (nl.prop !== 'item') {
  352. this.$set(n, nl.prop, selectedClasses);
  353. }
  354. });
  355. });
  356. }
  357. },
  358. // 按班排同步人员-列点击
  359. SyncHeaderClick(column) {
  360. const selectedClasses = JSON.parse(
  361. JSON.stringify(this.selectedClasses)
  362. );
  363. if (selectedClasses[0].id == 'clear') {
  364. this.personData.forEach((n) => {
  365. this.$delete(n, column.property);
  366. });
  367. } else {
  368. this.personData.forEach((n) => {
  369. this.$set(n, column.property, selectedClasses);
  370. });
  371. }
  372. },
  373. // 根据班组id查询人员
  374. getid_Person(id) {
  375. let list = this.personData.filter((n) => {
  376. return n.item.teamId == id;
  377. });
  378. return list;
  379. },
  380. // 保存
  381. save() {
  382. this.$emit('save');
  383. },
  384. // 重置
  385. rest() {
  386. this.$emit('rest');
  387. },
  388. // 按人/班排
  389. anbp() {
  390. this.type = this.type == 'person' ? 'team' : 'person';
  391. this.initColumns();
  392. this.setTableData();
  393. },
  394. // 切换按班排班
  395. changanbp() {
  396. this.type = 'team';
  397. this.initColumns();
  398. this.setTableData();
  399. },
  400. // 选择时间
  401. timeChange() {
  402. this.initColumns();
  403. },
  404. // 补零
  405. setNUm(num) {
  406. let length = 2;
  407. return (num / Math.pow(10, length)).toFixed(length).substr(2);
  408. },
  409. // 搜索
  410. search(val) {
  411. switch (this.type) {
  412. case 'person':
  413. if (val !== '') {
  414. setTimeout(() => {
  415. this.tableData = this.personData.filter((n) => {
  416. return (
  417. n.item.name.toLowerCase().indexOf(val.toLowerCase()) > -1
  418. );
  419. });
  420. }, 200);
  421. } else {
  422. this.tableData = this.personData;
  423. }
  424. break;
  425. case 'team':
  426. if (val !== '') {
  427. setTimeout(() => {
  428. this.tableData = this.teamData.filter((n) => {
  429. return (
  430. n.item.name.toLowerCase().indexOf(val.toLowerCase()) > -1
  431. );
  432. });
  433. }, 200);
  434. } else {
  435. this.tableData = this.teamData;
  436. }
  437. break;
  438. default:
  439. break;
  440. }
  441. }
  442. }
  443. };
  444. </script>
  445. <style lang="scss" scoped>
  446. :deep(.el-table .el-table__cell) {
  447. cursor: pointer;
  448. height: 60px;
  449. }
  450. .tabel-cell-item {
  451. color: #fff;
  452. height: 60px;
  453. overflow: hidden;
  454. display: flex;
  455. justify-content: center;
  456. align-items: center;
  457. padding: 15px !important;
  458. }
  459. :deep(.el-table td.el-table__cell) {
  460. padding: 0;
  461. div {
  462. padding: 0;
  463. }
  464. }
  465. .btn-wrap {
  466. text-align: right;
  467. padding-right: 20px;
  468. }
  469. :deep(.el-button--medium) {
  470. padding: 10px 20px !important;
  471. }
  472. .selection-box {
  473. position: absolute;
  474. border: 2px dashed black;
  475. pointer-events: none; /* 确保选择框不会干扰鼠标事件 */
  476. }
  477. </style>