table.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. <template>
  2. <div v-loading="loading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)" class="newQueryTable">
  3. <template v-if="istableChild">
  4. <el-table v-el-table-infinite-scroll="load" :data="dealedTableData" :summary-method="getSummaries" :span-method="tableSpanMethod" stripe :show-summary="showSummary" border @cell-click="cellClick" :cell-class-name="cellClass" ref="table" height="100%" class="table infinite-list">
  5. <el-table-column v-for="col in tableColsCopy" :key="col.columnName" :prop="col.columnName" :label="col.groupName">
  6. <el-table-column v-for="childCol in col.children" :key="childCol.columnName" :prop="childCol.columnName" :label="childCol.columnLabel">
  7. <template #header>
  8. <el-tooltip :content="childCol.columnDescribe || childCol.columnLabel" placement="top">
  9. <template v-if="childCol.needFilters">
  10. <TableHeaderCell :label="childCol.columnLabel" :filter-options="tableDataFilters[childCol.columnName]" :filter-values.sync="filterValues[childCol.columnName]" :sortable="childCol.needSort" />
  11. </template>
  12. <template v-else>
  13. <div>{{ childCol.columnLabel }}</div>
  14. </template>
  15. </el-tooltip>
  16. </template>
  17. </el-table-column>
  18. </el-table-column>
  19. </el-table>
  20. </template>
  21. <template v-else>
  22. <el-table v-el-table-infinite-scroll="load" :data="dealedTableData" :summary-method="getSummaries" :span-method="tableSpanMethod" stripe :show-summary="showSummary" border @cell-click="cellClick" :cell-class-name="cellClass" ref="table" height="100%" class="table infinite-list">
  23. <el-table-column v-for="(item, index) in tableColsCopy" :sortable="item.needSort ? true : false" :key="index" :prop="item.columnName" :label="item.columnLabel" :show-overflow-tooltip="showOverflowTooltip">
  24. <template #header>
  25. <template v-if="item.needFilters">
  26. <TableHeaderCell :label="item.columnLabel" :filter-options="tableDataFilters[item.columnName]" :filter-values.sync="filterValues[item.columnName]" :sortable="item.needSort" />
  27. </template>
  28. <template v-else>
  29. <div>{{ item.columnLabel }}</div>
  30. </template>
  31. </template>
  32. </el-table-column>
  33. </el-table>
  34. </template>
  35. <template v-if="istableCol">
  36. <div :style="btnStyle" class="btns">
  37. <img class="btn-square btn-shadow" src="@/assets/baggage/ic_setting.png" title="列设置" @click="show">
  38. </div>
  39. <Dialog :flag="dialogFlag" width="600px" class="dialog-check-group">
  40. <div class="dialog-wrapper">
  41. <div class="title">列设置</div>
  42. <div style="height: 600px;" class="content">
  43. <el-scrollbar style="height: 100%">
  44. <el-tree ref="columnSetTree" :data="tableCols" :class="colsCheckClass" show-checkbox node-key="index" :default-expand-all="true" :props="{
  45. label: 'columnLabel',
  46. children: 'children',
  47. }" :default-checked-keys="checkedKeysTemp" @check="handleCheck" />
  48. </el-scrollbar>
  49. </div>
  50. <div class="foot right t30">
  51. <el-button size="medium" class="r24" type="primary" @click="onCheck('baggageTableData')">确定</el-button>
  52. <el-button size="medium" @click="hide">取消</el-button>
  53. </div>
  54. </div>
  55. </Dialog>
  56. </template>
  57. </div>
  58. </template>
  59. <script>
  60. import pf from '@/layout/mixin/publicFunc';
  61. import { getToken } from '@/utils/auth';
  62. import { getAuthData, formatOrder } from "@/utils/validate";
  63. import { throttledExportToExcel } from '@/utils/table';
  64. import tableColsMixin from '../mix/tableCols';
  65. import Dialog from '@/layout/components/Dialog/index.vue';
  66. import TableHeaderCell from "@/components/TableHeaderCell";
  67. export default {
  68. name: 'NewQueryTable',
  69. mixins: [pf, tableColsMixin],
  70. components: { Dialog, TableHeaderCell },
  71. props: {
  72. // 不换行,溢出隐藏
  73. showOverflowTooltip: {
  74. type: Boolean,
  75. default: true,
  76. },
  77. isTable: {
  78. type: Boolean,
  79. default: false,
  80. },
  81. tableTag: {
  82. type: Object,
  83. default: () => { }
  84. },
  85. istableCol: {
  86. type: Boolean,
  87. default: false,
  88. },
  89. istableChild: {
  90. type: Boolean,
  91. default: false,
  92. },
  93. btnStyle: {
  94. type: Object,
  95. default: () => { }
  96. },
  97. },
  98. data () {
  99. return {
  100. page: 0,
  101. pageSize: 20,
  102. queryId: '',
  103. noMore: false,
  104. loading: false,
  105. tableCols: [], //表头数据
  106. tableData: [], //表格数据
  107. tableColsCopy: [], //表头数据缓存
  108. tableDataFilters: {}, //表头-下拉数据
  109. filterValues: {}, //表头-下拉-选中数据
  110. tableDataCopy: [], //缓存table数据
  111. tableGroups: [], //表格分组数据
  112. colShowFilter: "", //表头-下拉-箭头
  113. spanArr: [], //表格分组数据缓存
  114. pos: 0, //表格分组计数
  115. dataContent: [],
  116. authBtns: [],
  117. authBtnCol: [],
  118. authBtnColName: [],
  119. tableArrs: [], //重组table-表头下拉
  120. tableOptions: {}, //弹框-下来数据缓存
  121. showSummary: false, //是否显示统计
  122. tableDataSortRules: {},
  123. }
  124. },
  125. computed: {
  126. //设置表头-下拉-选中数据
  127. dealedTableData () {
  128. const filtered = this.tableData.filter(item => {
  129. let flag = true
  130. Object.entries(this.filterValues).forEach(([key, arr]) => {
  131. if (arr.length && !arr.includes(String(item[key]))) {
  132. flag = false
  133. }
  134. })
  135. return flag
  136. })
  137. const sortRules = Object.entries(this.tableDataSortRules).reduce(
  138. (pre, [key, value]) => {
  139. if (value) {
  140. pre[0].push(key)
  141. value = value === 'ascending' ? 'asc' : 'desc'
  142. pre[1].push(value)
  143. }
  144. return pre
  145. },
  146. [[], []]
  147. )
  148. return this._.orderBy(filtered, sortRules[0], sortRules[1])
  149. },
  150. tableHeight () {
  151. return 100
  152. }
  153. },
  154. watch: {
  155. tableTag: {
  156. handler (val) {
  157. this.dataContent = val
  158. this.restTable()
  159. this.load()
  160. },
  161. deep: true
  162. }
  163. },
  164. mounted () {
  165. this.getColumnData()
  166. this.setCellClick()
  167. },
  168. updated () {
  169. this.$refs['table']?.doLayout()
  170. },
  171. methods: {
  172. //获取表头数据
  173. async getColumnData () {
  174. try {
  175. const { code, returnData } = await this.getQueryList(SERVICE_ID.sysUserAuthId, [{
  176. user_id: getToken('userid'),
  177. auth_id: this.AauthId
  178. }]);
  179. if (code == 0) {
  180. if (returnData && returnData.length) {
  181. if (!this.istableCol) {
  182. sessionStorage.setItem('tableColumns', JSON.stringify(returnData))
  183. this.$store.dispatch('auth/changeAuthMsg', returnData)
  184. }
  185. const msgDatas = returnData.filter((item) => item.needShow);
  186. const msgCounts = msgDatas.filter((item) => item.needCount);
  187. if (msgCounts.length) {
  188. this.showSummary = true
  189. }
  190. const msgDatasShows = formatOrder(msgDatas);
  191. if (this.istableChild) {
  192. const datas = _.cloneDeep(msgDatasShows)
  193. const cache = {};
  194. const indices = [];
  195. const others = []
  196. datas.forEach((item) => {
  197. if (!cache[item.groupName] && item.groupName) {
  198. cache[item.groupName] = item.groupName
  199. indices.push(item)
  200. } else {
  201. const newItem = _.cloneDeep(item)
  202. others.push(newItem)
  203. }
  204. })
  205. indices.map((item, index) => {
  206. item.tabIndex = index
  207. })
  208. others.forEach(item => {
  209. indices.forEach(p => {
  210. if (item.groupName == p.groupName && item.groupName) {
  211. item.tabIndex = p.tabIndex
  212. }
  213. })
  214. })
  215. indices.forEach((item) => {
  216. item.children = [_.cloneDeep(item), ...this.formatCaps(item.tabIndex, others)]
  217. })
  218. this.tableCols = _.cloneDeep(indices);
  219. } else {
  220. this.tableCols = _.cloneDeep(msgDatasShows);
  221. }
  222. this.tableColsCopy = _.cloneDeep(this.tableCols);
  223. this.initTableCols();
  224. }
  225. } else {
  226. this.$message.error("获取表头数据失败");
  227. }
  228. } catch (error) {
  229. console.log(error)
  230. }
  231. },
  232. formatCaps (order, arr) {
  233. const datas = []
  234. for (let i = 0; i < arr.length; i++) {
  235. const element = arr[i];
  236. if (element['tabIndex'] == order) {
  237. datas.push(element)
  238. }
  239. }
  240. return datas
  241. },
  242. //获取表格数据
  243. async getQuery (id, dataContent = this.dataContent, page, pageSize) {
  244. try {
  245. this.loading = true;
  246. const { code, returnData } = await this.getQueryListAuth(id, dataContent, page, pageSize);
  247. if (code == 0) {
  248. if (returnData.length === 0) {
  249. this.page--;
  250. this.noMore = true;
  251. this.loading = false;
  252. }
  253. this.tableData.push(...returnData);
  254. setTimeout(() => {
  255. this.initTableData();
  256. this.loading = false;
  257. }, 100);
  258. } else {
  259. this.page--;
  260. this.loading = false;
  261. this.$message.error("获取表格数据失败");
  262. }
  263. } catch (error) {
  264. this.page--;
  265. this.loading = false;
  266. }
  267. },
  268. restTable () {
  269. this.loading = false
  270. this.noMore = false
  271. this.page = 0
  272. this.tableData = []
  273. },
  274. load () {
  275. if (Object.keys(this.tableTag).length) {
  276. if (this.noMore || this.loading) {
  277. return;
  278. }
  279. this.getQuery(this.AqueryId, this.dataContent, ++this.page, this.pageSize);
  280. }
  281. },
  282. setCellClick () {
  283. const { auth_id } = this.$route.meta
  284. const { arrs } = getAuthData(auth_id)
  285. const table = arrs.filter(item => item.auth_type == 4)
  286. if (table && table.length) {
  287. const obj = table[0]
  288. const { arrs } = getAuthData(obj.auth_id)
  289. this.authBtnCol = arrs.filter(item => Number(item.service_type) == 6)
  290. if (this.authBtnCol.length) {
  291. this.authBtnCol.forEach(item => {
  292. this.authBtnColName.push(item.relation_data)
  293. })
  294. }
  295. }
  296. },
  297. //初始化表格
  298. initTableData () {
  299. this.tableDataCopy = _.cloneDeep(this.tableData);
  300. const datas = _.cloneDeep(this.tableColsCopy);
  301. // const reqUts = [];
  302. datas.forEach(async (item) => {
  303. this.tableDataFilters[item.columnName] = [];
  304. if (item.needGroup) {
  305. this.tableGroups.push(item.columnName);
  306. }
  307. // if (item.listqueryTemplateID || item.listqueryTemplateID == 0) {
  308. // this.tableArrs.push(item.columnName);
  309. // if (!this.tableOptions[item.columnName]) {
  310. // this.tableOptions[item.columnName] = await this.getSelectData(item.listqueryTemplateID);
  311. // }
  312. // }
  313. });
  314. const dats = this.setTableFilters(this.tableData, this.tableDataFilters);
  315. this.tableDataFilters = _.cloneDeep(dats);
  316. this.tableGroup(this.tableData);
  317. },
  318. setTableFilters (tableData, filters) {
  319. const tempSets = {}
  320. Object.keys(filters).forEach(key => {
  321. tempSets[key] = new Set()
  322. })
  323. tableData.forEach(item => {
  324. Object.keys(tempSets).forEach(key => {
  325. (item[key] ?? '') !== '' && tempSets[key].add(String(item[key]))
  326. })
  327. })
  328. Object.keys(tempSets).forEach(key => {
  329. filters[key] = _.orderBy(
  330. [...tempSets[key]].map(value => ({
  331. text: value,
  332. value
  333. })),
  334. o => o.value
  335. )
  336. })
  337. return filters
  338. },
  339. //分组
  340. tableGroup (tableData) {
  341. const spanArr = [];
  342. let pos = 0;
  343. let ifYj = this.tableGroups[0];
  344. for (let i = 0; i < tableData.length; i++) {
  345. if (i === 0) {
  346. spanArr.push(1);
  347. } else {
  348. if (tableData[i][ifYj] === tableData[i - 1][ifYj]) {
  349. spanArr[pos] += 1;
  350. spanArr.push(0);
  351. } else {
  352. spanArr.push(1);
  353. pos = i;
  354. }
  355. }
  356. }
  357. this.spanArr = spanArr;
  358. this.pos = pos;
  359. },
  360. popoverShowHandler (prop) {
  361. this.colShowFilter = prop;
  362. },
  363. popoverHideHandler () {
  364. this.colShowFilter = "";
  365. },
  366. //获取弹框-下拉数据
  367. async getSelectData (id) {
  368. // name ? [name] : name === null ? [null]: [],
  369. const { code, returnData } = await this.getQueryList(id, {});
  370. console.log(returnData)
  371. if (code == 0) {
  372. return returnData;
  373. } else {
  374. return [];
  375. }
  376. },
  377. //设置表头-下拉-箭头样式
  378. arrowClass () {
  379. return function (prop) {
  380. let classString = "";
  381. if (this.colShowFilter === prop) {
  382. return "arrow-active";
  383. }
  384. if (
  385. Object.entries(this.tableDataFilters).find(
  386. ([key, arr]) => this.filterValues[prop]
  387. )
  388. ) {
  389. classString += "arrow-blue";
  390. }
  391. return classString;
  392. };
  393. },
  394. //合计
  395. getSummaries (param) {
  396. const { columns, data } = param;
  397. const sums = [];
  398. columns.forEach((column, index) => {
  399. if (index === 0) {
  400. sums[index] = '合计:' + this.tableData.length
  401. }
  402. this.tableColsCopy.forEach((p) => {
  403. if (column.property == p.columnName && p.needCount) {
  404. const values = data.map((item) => Number(item[column.property]));
  405. if (!values.every((value) => isNaN(value))) {
  406. sums[index] = values.reduce((prev, curr) => {
  407. const value = Number(curr);
  408. if (!isNaN(value)) {
  409. return prev + curr;
  410. } else {
  411. return prev;
  412. }
  413. }, 0);
  414. sums[index] += "";
  415. }
  416. }
  417. });
  418. });
  419. return sums;
  420. },
  421. //分组
  422. tableSpanMethod ({ row, column, rowIndex, columnIndex }) {
  423. if (this.tableGroups.includes(column["property"])) {
  424. const _row = this.spanArr[rowIndex];
  425. const _col = _row > 0 ? 1 : 0;
  426. return {
  427. rowspan: _row,
  428. colspan: _col,
  429. };
  430. }
  431. },
  432. //表格-设置单元格样式
  433. cellClass ({ row, column, rowIndex, columnIndex }) {
  434. if (this.authBtnColName.includes(column.property)) {
  435. return 'is-click-btn'
  436. }
  437. },
  438. //表格-单元格点击
  439. cellClick (row, column) {
  440. const dataBtns = this.authBtnCol
  441. if (dataBtns && dataBtns.length) {
  442. const clickBtn = dataBtns.filter(item => item.relation_data == column.property)[0]
  443. if (clickBtn) {
  444. const { open_method, route_info, pass_parameters } = clickBtn
  445. if (open_method == 2) {
  446. if (pass_parameters) {
  447. const query = pass_parameters.split(',')
  448. const obj = {}
  449. query.forEach(item => {
  450. obj[item] = row[item]
  451. })
  452. this.$router.push({
  453. path: route_info,
  454. query: obj
  455. })
  456. }
  457. }
  458. }
  459. }
  460. },
  461. //导出
  462. exportHandler () {
  463. const table = this.$refs['table'].$el.cloneNode(true)
  464. // const { luggageNum, flightNo, flightDate } = this.query
  465. const fileName = `高级查询.xlsx`
  466. throttledExportToExcel(table, '高级查询', fileName)
  467. },
  468. }
  469. }
  470. </script>
  471. <style lang="scss" scoped>
  472. .newQueryTable {
  473. height: 100%;
  474. position: relative;
  475. ::v-deep .table {
  476. .is-click-btn {
  477. .cell {
  478. color: #409eff;
  479. cursor: pointer;
  480. position: relative;
  481. }
  482. }
  483. .cell {
  484. color: #000;
  485. text-align: center;
  486. }
  487. }
  488. .btns {
  489. position: absolute;
  490. top: -50px;
  491. right: 32px;
  492. z-index: 10;
  493. }
  494. }
  495. </style>