filter-page-table.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <template>
  2. <div id="data-table">
  3. <el-form :model="searchInfo" :inline="true" style="padding:8px 0" size="mini">
  4. <el-row type="flex" justify="end">
  5. <el-col :span="22" v-if="useFilter">
  6. <el-form-item v-for="(item, index) in filterList" :key="index">
  7. <template v-if="item.filter === 'select'">
  8. <el-select
  9. v-model="searchInfo[item.prop]"
  10. size="mini"
  11. clearable
  12. filterable
  13. :placeholder="`请选择${item.label}`"
  14. @clear="toClear(item.prop)"
  15. @change="data => filterReturn(data, item)"
  16. >
  17. <slot name="options" v-bind="{ item }"></slot>
  18. </el-select>
  19. </template>
  20. <template v-else-if="item.filter === 'date'">
  21. <el-date-picker
  22. v-model="searchInfo[item.prop]"
  23. value-format="yyyy-MM-dd"
  24. format="yyyy-MM-dd"
  25. type="daterange"
  26. range-separator="-"
  27. start-placeholder="开始日期"
  28. end-placeholder="结束日期"
  29. clearable
  30. >
  31. </el-date-picker>
  32. </template>
  33. <template v-else>
  34. <el-input v-model="searchInfo[item.prop]" clearable size="mini" :placeholder="`请输入${item.label}`" @clear="toClear(item.prop)"></el-input>
  35. </template>
  36. </el-form-item>
  37. <el-form-item>
  38. <el-button type="primary" size="mini" @click="filterSearch">查询</el-button>
  39. </el-form-item>
  40. </el-col>
  41. <el-col :span="2" style="text-align:right">
  42. <slot name="selfbtn"></slot>
  43. </el-col>
  44. </el-row>
  45. </el-form>
  46. <el-table
  47. ref="table"
  48. row-key="id"
  49. :data="data"
  50. border
  51. stripe
  52. size="mini"
  53. :max-height="height !== null ? height : ''"
  54. @select="handleSelectionChange"
  55. @select-all="handleSelectAll"
  56. v-bind="options"
  57. :show-summary="useSum"
  58. @row-click="rowClick"
  59. :summary-method="computedSum"
  60. >
  61. <el-table-column type="selection" width="55" v-if="select" prop="id" :reserve-selection="true"> </el-table-column>
  62. <template v-for="(item, index) in fields">
  63. <template v-if="!item.notable">
  64. <template v-if="item.custom">
  65. <el-table-column :key="index" align="center" :label="item.label" v-bind="item.options" :show-overflow-tooltip="item.showTip || true">
  66. <template v-slot="{ row }">
  67. <slot name="custom" v-bind="{ item, row }"></slot>
  68. </template>
  69. </el-table-column>
  70. </template>
  71. <template v-else>
  72. <el-table-column
  73. :key="index"
  74. align="center"
  75. :label="item.label"
  76. :prop="item.prop"
  77. :formatter="toFormatter"
  78. :sortable="sortable"
  79. v-bind="item.options"
  80. :show-overflow-tooltip="item.showTip || true"
  81. >
  82. </el-table-column>
  83. </template>
  84. </template>
  85. </template>
  86. <template v-if="opera.length > 0">
  87. <el-table-column label="操作" align="center" :width="operaWidth">
  88. <template v-slot="{ row, $index }">
  89. <template v-for="(item, index) in opera">
  90. <template v-if="display(item, row)">
  91. <el-tooltip v-if="item.icon" :key="index" effect="dark" :content="item.label" placement="bottom">
  92. <el-button
  93. :key="index"
  94. type="text"
  95. :icon="item.icon || ''"
  96. size="mini"
  97. @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)"
  98. ></el-button>
  99. </el-tooltip>
  100. <!-- <el-button v-else :key="index" type="text" size="mini" @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)">
  101. {{ item.label }}
  102. </el-button> -->
  103. <el-link
  104. v-else
  105. :key="`${item.model}-column-${index}`"
  106. :type="item.type || 'primary'"
  107. :icon="item.icon || ''"
  108. size="mini"
  109. style="padding-right:10px"
  110. :underline="false"
  111. @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
  112. >
  113. {{ item.label }}
  114. </el-link>
  115. </template>
  116. </template>
  117. </template>
  118. </el-table-column>
  119. </template>
  120. </el-table>
  121. <el-row type="flex" align="middle" justify="end" style="padding-top:1rem" v-if="usePage">
  122. <el-col :span="24" style="text-align:right;">
  123. <el-pagination
  124. background
  125. layout="total, prev, pager, next"
  126. :page-sizes="[10, 15, 20, 50, 100]"
  127. :total="total"
  128. :page-size="limit"
  129. :current-page.sync="currentPage"
  130. @current-change="changePage"
  131. @size-change="sizeChange"
  132. >
  133. </el-pagination>
  134. <!-- sizes -->
  135. </el-col>
  136. </el-row>
  137. </div>
  138. </template>
  139. <script>
  140. import _ from 'lodash';
  141. export default {
  142. name: 'data-table',
  143. props: {
  144. fields: { type: Array, required: true },
  145. data: { type: Array, required: true },
  146. opera: { type: Array, default: () => [] },
  147. toFormat: null,
  148. height: null,
  149. select: { type: Boolean, default: false },
  150. selected: { type: Array, default: () => [] },
  151. usePage: { type: Boolean, default: true },
  152. total: { type: Number, default: 0 },
  153. options: null,
  154. useSum: { type: Boolean, default: false },
  155. sumcol: { type: Array, default: () => [] },
  156. sumres: { type: String, default: 'total' },
  157. filter: { type: Array, default: () => [] },
  158. operaWidth: { type: Number, default: 200 },
  159. sortable: { type: Boolean, default: true },
  160. limit: { type: Number, default: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10 },
  161. },
  162. components: {},
  163. data: () => ({
  164. pageSelected: [],
  165. currentPage: 1,
  166. // limit: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10,
  167. searchInfo: {},
  168. useFilter: true,
  169. filterList: [],
  170. }),
  171. created() {},
  172. computed: {},
  173. methods: {
  174. toFormatter(row, column, cellValue, index) {
  175. let this_fields = this.fields.filter(fil => fil.prop === column.property);
  176. if (this_fields.length > 0) {
  177. let format = _.get(this_fields[0], `format`, false);
  178. if (format) {
  179. let res;
  180. if (_.isFunction(format)) {
  181. res = format(cellValue);
  182. } else {
  183. res = this.toFormat({
  184. model: this_fields[0].prop,
  185. value: cellValue,
  186. });
  187. }
  188. return res;
  189. } else return cellValue;
  190. }
  191. },
  192. handleOpera(data, method, confirm = false, methodZh, label, index, confirmWord) {
  193. let self = true;
  194. if (_.isFunction(methodZh)) {
  195. methodZh = methodZh(data);
  196. } else if (!_.isString(methodZh)) {
  197. methodZh = label;
  198. self = false;
  199. }
  200. if (confirm) {
  201. let word = self ? methodZh : `您确认${methodZh}该数据?`;
  202. if (confirmWord) word = confirmWord;
  203. this.$confirm(word, '提示', {
  204. confirmButtonText: '确定',
  205. cancelButtonText: '取消',
  206. type: 'warning',
  207. })
  208. .then(() => {
  209. this.$emit(method, { data, index });
  210. })
  211. .catch(() => {});
  212. } else {
  213. this.$emit(method, { data, index });
  214. }
  215. },
  216. handleSelectionChange(selection, row) {
  217. // console.log(selection);
  218. // console.log(row);
  219. //根据row是否再pageSelected中,判断是添加还是删除
  220. let res = [];
  221. if (this.pageSelected.find(i => i.id === row.id)) {
  222. res = this.pageSelected.filter(f => f.id !== row.id);
  223. } else {
  224. this.pageSelected.push(row);
  225. res = this.pageSelected;
  226. }
  227. this.$set(this, `pageSelected`, res);
  228. this.$emit(`handleSelect`, _.uniqBy(res, 'id'));
  229. },
  230. handleSelectAll(selection) {
  231. //处于没全选状态,选择之后一定是全选,只有处于全选状态时,才会反选(全取消)
  232. // console.log(selection);
  233. let res = [];
  234. if (selection.length > 0) {
  235. //全选
  236. res = _.uniqBy(this.pageSelected.concat(selection), 'id');
  237. } else {
  238. //全取消
  239. res = _.differenceBy(this.pageSelected, this.data, 'id');
  240. }
  241. this.$set(this, `pageSelected`, res);
  242. this.$emit(`handleSelect`, res);
  243. },
  244. initSelection() {
  245. this.$nextTick(() => {
  246. this.$refs.table.clearSelection();
  247. this.selected.forEach(info => {
  248. let d = this.data.filter(p => p.id === info.id);
  249. if (d.length > 0) this.$refs.table.toggleRowSelection(d[0]);
  250. });
  251. });
  252. },
  253. selectReset() {
  254. this.$refs.table.clearSelection();
  255. },
  256. display(item, row) {
  257. let display = _.get(item, `display`, true);
  258. if (display === true) return true;
  259. else {
  260. let res = display(row);
  261. return res;
  262. }
  263. },
  264. //
  265. changePage(page = this.currentPage) {
  266. this.$emit('query', { skip: (page - 1) * this.limit, limit: this.limit, ...this.searchInfo });
  267. },
  268. sizeChange(limit) {
  269. this.limit = limit;
  270. this.currentPage = 1;
  271. this.$emit('query', { skip: 0, limit: this.limit, ...this.searchInfo });
  272. },
  273. getFilterList() {
  274. let res = this.fields.filter(f => _.get(f, 'filter', false));
  275. this.$set(this, `useFilter`, res.length > 0);
  276. res.map(i => {
  277. if (i.filter === 'date' && this.searchInfo[i.porp] === undefined) this.$set(this.searchInfo, i.prop, []);
  278. });
  279. res = [...res, ...this.filter];
  280. this.$set(this, `filterList`, res);
  281. },
  282. filterSearch() {
  283. this.currentPage = 1;
  284. this.$emit('query', { skip: 0, limit: this.limit, ...this.searchInfo });
  285. },
  286. rowClick(row, column, event) {
  287. this.$emit(`rowClick`, row);
  288. },
  289. toClear(prop) {
  290. delete this.searchInfo[prop];
  291. },
  292. filterReturn(data, item) {
  293. let { prop, filterReturn } = item;
  294. if (filterReturn) this.$emit('filterReturn', { data, prop });
  295. },
  296. // 计算合计
  297. computedSum({ columns, data }) {
  298. if (columns.length <= 0 || data.length <= 0) return '';
  299. const result = [];
  300. const reg = new RegExp(/^\d+$/);
  301. for (const column of columns) {
  302. // 判断有没有prop属性
  303. const prop = _.get(column, 'property');
  304. if (!prop) {
  305. result.push('');
  306. continue;
  307. }
  308. // 判断是否需要计算
  309. const inlist = this.sumcol.find(f => f == prop);
  310. if (!inlist) {
  311. result.push('');
  312. continue;
  313. }
  314. let res = 0;
  315. // 整理出要计算的属性(只取出数字或者可以为数字的值)
  316. const resetList = data.map(i => {
  317. const d = _.get(i, prop);
  318. const res = reg.test(d);
  319. if (res) return d * 1;
  320. else return 0;
  321. });
  322. if (this.sumres === 'total') {
  323. res = this.totalComputed(resetList);
  324. } else if (this.sumres === 'avg') {
  325. res = this.avgComputed(resetList);
  326. } else if (this.sumres === 'max') {
  327. res = this.maxComputed(resetList);
  328. } else if (this.sumres === 'min') {
  329. res = this.minComputed(resetList);
  330. }
  331. result.push(res);
  332. }
  333. result[0] = '合计';
  334. return result;
  335. },
  336. // 合计计算
  337. totalComputed(data) {
  338. const total = data.reduce((p, n) => p + n, 0);
  339. return total;
  340. },
  341. // 平均值计算
  342. avgComputed(data) {
  343. const total = this.totalComputed(data);
  344. return _.round(_.divide(total, data.length), 2);
  345. },
  346. // 最大值计算
  347. maxComputed(data) {
  348. return _.max(data);
  349. },
  350. // 最小值计算
  351. minComputed(data) {
  352. return _.min(data);
  353. },
  354. },
  355. watch: {
  356. selected: {
  357. handler(val) {
  358. if (val.length > 0) {
  359. this.pageSelected = val;
  360. this.initSelection();
  361. }
  362. },
  363. immediate: true,
  364. },
  365. data: {
  366. handler(val, oval) {
  367. if (this.select) {
  368. this.initSelection();
  369. }
  370. },
  371. },
  372. fields: {
  373. handler(val, oval) {
  374. if (val) this.getFilterList();
  375. },
  376. immediate: true,
  377. },
  378. },
  379. };
  380. </script>
  381. <style lang="less" scoped></style>