index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <script>
  2. export default {
  3. name: "SuperTable",
  4. props: {
  5. // 数据
  6. value: {
  7. type: [Array],
  8. require: true,
  9. },
  10. // 字典
  11. dict: {
  12. type: [Object],
  13. require: true,
  14. },
  15. // 分页
  16. page: {
  17. type: [Object],
  18. require: false,
  19. },
  20. // 模板
  21. columns: {
  22. type: [Array],
  23. require: true,
  24. },
  25. // 是否显示序号
  26. index: {
  27. type: Boolean,
  28. default: false,
  29. },
  30. // 是否显示单选
  31. radio: {
  32. type: Boolean,
  33. default: false,
  34. },
  35. // 是否显示多选
  36. checkbox: {
  37. type: Boolean,
  38. default: false,
  39. },
  40. // 是否显示分页
  41. pagination: {
  42. type: Boolean,
  43. default: false,
  44. },
  45. // 是否列操作
  46. convenitentOperation: {
  47. type: Boolean,
  48. default: false,
  49. },
  50. // 是否禁止选择
  51. selectable: {
  52. type: Function,
  53. default: () => {},
  54. },
  55. },
  56. components: {
  57. ElDictTag: () => import("@/components/DictTag/index.vue"),
  58. ElDraggable: () => import("@/components/draggable/index.vue"),
  59. ElFilePreview: () => import("@/components/file-preview/index.vue"),
  60. ElComputedInput: () => import("@/components/computed-input/index.vue"),
  61. ElPopoverSelectV2: () => import("@/components/popover-select-v2/index.vue"),
  62. ElPopoverMultipleSelectV2: () =>
  63. import("@/components/popover-select-v2/multiple.vue"),
  64. ElComputedInputV2: () => import("@/components/computed-input-v2/index.vue"),
  65. ElPopoverTreeSelect: () =>
  66. import("@/components/popover-tree-select/index.vue"),
  67. ButtonHide: () => import("./hide.vue"),
  68. ButtonFreeze: () => import("./freeze.vue"),
  69. IconHide: () => import("./once/hide.vue"),
  70. IconSort: () => import("./once/sort.vue"),
  71. IconFreeze: () => import("./once/freeze.vue"),
  72. IconFilter: () => import("./once/filters.vue"),
  73. },
  74. data() {
  75. const { columns } = this.$props;
  76. const innerColumns = columns.map(({ item, attr }) => ({
  77. attr,
  78. item: { hidden: true, ...item },
  79. }));
  80. return {
  81. innerColumns: innerColumns,
  82. rowKey: "id",
  83. // 选择
  84. selectData: [],
  85. selectState: false,
  86. // 过滤
  87. filterData: [],
  88. filterState: false,
  89. };
  90. },
  91. computed: {
  92. innerValue: {
  93. get() {
  94. if (this.filterState) {
  95. return this.filterData;
  96. } else if (this.selectState) {
  97. return this.selectData;
  98. } else {
  99. return this.$props.value;
  100. }
  101. },
  102. set(value) {
  103. this.$emit("input", value);
  104. },
  105. },
  106. showColumns: {
  107. get() {
  108. return this.innerColumns.filter(({ item }) => item.hidden);
  109. },
  110. set() {},
  111. },
  112. filterRules: {
  113. get() {
  114. return Object.fromEntries(
  115. this.innerColumns
  116. .filter(({ item }) => item.filter && !!item.filter.length)
  117. .map(({ item }) => [item.key, item.filter])
  118. );
  119. },
  120. set() {},
  121. },
  122. },
  123. watch: {
  124. filterRules: {
  125. handler: function (newValue) {
  126. function multiFilter(array, filters) {
  127. const filterKeys = Object.keys(filters);
  128. // filters all elements passing the criteria
  129. return array.filter((item) => {
  130. // dynamically validate all filter criteria
  131. return filterKeys.every((key) => {
  132. //ignore when the filter is empty Anne
  133. if (!filters[key].length) return true;
  134. return !!~filters[key].indexOf(item[key]);
  135. });
  136. });
  137. }
  138. this.filterState = JSON.stringify(newValue) !== "{}";
  139. this.filterData = multiFilter(this.$props.value, newValue);
  140. },
  141. },
  142. },
  143. methods: {
  144. //
  145. onSelectionChange(value) {
  146. this.selectData = value;
  147. this.$emit("row-select", this.selectData);
  148. },
  149. //
  150. onRowClick(row, column, event) {
  151. const { radio, checkbox } = this.$props;
  152. // 单选
  153. if (radio) {
  154. this.$emit("row-select", [row]);
  155. }
  156. // 多选
  157. if (checkbox) {
  158. this.$refs.superTable.toggleRowSelection(
  159. this.innerValue.find((item) => item.id === row.id)
  160. );
  161. }
  162. },
  163. // 冻结
  164. onHide(prop) {
  165. this.$nextTick(() => {
  166. this.$refs.superTable.doLayout();
  167. });
  168. },
  169. // 排序
  170. onSort(prop) {
  171. const { key, sort } = prop;
  172. this.$nextTick(() => {
  173. this.$refs.superTable.sort(key, sort);
  174. this.$refs.superTable.doLayout();
  175. });
  176. },
  177. // 冻结
  178. onFreeze() {
  179. this.$nextTick(() => {
  180. this.$refs.superTable.doLayout();
  181. });
  182. },
  183. // 过滤
  184. onFilter() {
  185. this.$nextTick(() => {
  186. this.$refs.superTable.doLayout();
  187. });
  188. },
  189. onFilters(value) {
  190. const {
  191. item: { key },
  192. attr: { dictName },
  193. } = value;
  194. let dataList = [];
  195. const dict = this.dict.type[dictName];
  196. dataList = Array.from(
  197. new Set(this.innerValue.map((item) => item[key]).filter((item) => item))
  198. ).map((item) => ({
  199. text: dictName
  200. ? (dict.find((dictItem) => dictItem.value == item) || {}).label
  201. : item,
  202. value: item,
  203. }));
  204. return dataList;
  205. },
  206. // 继承el-table的Method
  207. extendMethod() {
  208. const refMethod = Object.entries(this.$refs["superTable"]);
  209. for (const [key, value] of refMethod) {
  210. if (!(key.includes("$") || key.includes("_"))) {
  211. this[key] = value;
  212. }
  213. }
  214. },
  215. },
  216. created() {},
  217. mounted() {
  218. this.extendMethod();
  219. },
  220. destroyed() {},
  221. };
  222. </script>
  223. <template>
  224. <div class="el-super-table">
  225. <el-table
  226. ref="superTable"
  227. border
  228. height="auto"
  229. :row-key="rowKey"
  230. :data="innerValue"
  231. :highlight-current-row="radio"
  232. @row-click="onRowClick"
  233. @selection-change="onSelectionChange"
  234. v-bind="$attrs"
  235. v-on="$listeners"
  236. style="flex: 1"
  237. >
  238. <!-- 多选 -->
  239. <el-table-column
  240. v-if="checkbox"
  241. :column-key="rowKey"
  242. fixed
  243. width="50"
  244. align="center"
  245. type="selection"
  246. reserve-selection
  247. >
  248. </el-table-column>
  249. <!-- 序号 -->
  250. <el-table-column
  251. v-if="index"
  252. :resizable="false"
  253. fixed
  254. width="50"
  255. label="序号"
  256. align="center"
  257. class="is-index"
  258. >
  259. <template slot-scope="scope">
  260. {{ scope.$index + 1 }}
  261. </template>
  262. </el-table-column>
  263. <el-table-column
  264. v-for="({ item, attr }, index) in showColumns"
  265. :key="item.key + index"
  266. :prop="item.key"
  267. :label="item.title"
  268. :fixed="item.fixed"
  269. :width="item.width || 200"
  270. show-overflow-tooltip
  271. >
  272. <template slot="header" slot-scope="scope">
  273. <template>
  274. <span v-if="item.require" style="color: #ff4949">*</span>
  275. <span
  276. :style="{
  277. color:
  278. item.sort ||
  279. item.fixed ||
  280. (item.filter && !!item.filter.length)
  281. ? '#1890ff'
  282. : '',
  283. }"
  284. >
  285. {{ item.title }}
  286. </span>
  287. <template>
  288. <icon-sort
  289. v-if="item.sortabled"
  290. v-model="item.sort"
  291. @sort="onSort(item)"
  292. ></icon-sort>
  293. <icon-freeze
  294. v-if="item.fixedabled"
  295. v-model="item.fixed"
  296. @freeze="onFreeze"
  297. ></icon-freeze>
  298. <icon-filter
  299. v-if="item.filterabled"
  300. v-model="item.filter"
  301. :filters="onFilters({ item, attr })"
  302. @filter="onFilter"
  303. ></icon-filter>
  304. <icon-hide
  305. v-if="item.hiddenabled"
  306. v-model="item.hidden"
  307. @hide="onHide"
  308. ></icon-hide>
  309. </template>
  310. </template>
  311. </template>
  312. <template slot-scope="scope">
  313. <slot :name="item.key" v-bind="scope" :item="item" :attr="attr">
  314. <template v-if="attr.is">
  315. <component
  316. v-if="attr.is === 'el-dict-tag'"
  317. v-bind="attr"
  318. :size="$attrs.size"
  319. :value="scope.row[item.key]"
  320. :options="dict.type[attr.dictName]"
  321. ></component>
  322. <component
  323. v-else-if="attr.is === 'el-popover-select-v2'"
  324. v-bind="attr"
  325. v-model="scope.row[item.key]"
  326. :size="$attrs.size"
  327. :source.sync="scope.row"
  328. >
  329. </component>
  330. <component
  331. v-else-if="attr.is === 'el-popover-multiple-select-v2'"
  332. v-bind="attr"
  333. v-model="scope.row[item.key]"
  334. :size="$attrs.size"
  335. :source.sync="scope.row"
  336. >
  337. </component>
  338. <component
  339. v-else-if="attr.is === 'el-select'"
  340. v-bind="attr"
  341. v-model="scope.row[item.key]"
  342. :size="$attrs.size"
  343. >
  344. <template>
  345. <el-option
  346. v-for="item in dict.type[attr.dictName]"
  347. :key="item.value"
  348. :label="item.label"
  349. :value="item.value"
  350. >
  351. </el-option>
  352. </template>
  353. </component>
  354. <component
  355. v-else
  356. v-bind="attr"
  357. v-model="scope.row[item.key]"
  358. :size="$attrs.size"
  359. style="width: 100%"
  360. >
  361. </component
  362. ></template>
  363. <template v-else>
  364. <component v-if="attr.formatter" is="span">{{
  365. attr.formatter(scope.row)
  366. }}</component>
  367. <component v-else is="span">{{
  368. scope.row[item.key] || "--"
  369. }}</component>
  370. </template>
  371. </slot>
  372. </template>
  373. </el-table-column>
  374. <slot></slot>
  375. </el-table>
  376. <div
  377. style="
  378. height: 50px;
  379. display: flex;
  380. justify-content: space-between;
  381. align-items: center;
  382. "
  383. >
  384. <div class="mr-4">
  385. <template v-if="checkbox">
  386. <el-button
  387. v-if="selectState"
  388. size="mini"
  389. @click="selectState = !selectState"
  390. >
  391. 所有列
  392. </el-button>
  393. <el-button
  394. v-else
  395. :disabled="!selectData.length"
  396. size="mini"
  397. @click="selectState = !selectState"
  398. >
  399. 选择列
  400. {{ selectData.length ? ` :${selectData.length}` : "" }}
  401. </el-button>
  402. </template>
  403. <template v-if="convenitentOperation">
  404. <button-hide v-model="innerColumns" @change="onHide"></button-hide>
  405. <!-- <button-freeze
  406. v-model="showColumns"
  407. @freeze="onFreeze"
  408. ></button-freeze> -->
  409. </template>
  410. </div>
  411. <pagination
  412. v-if="pagination"
  413. v-show="!selectState"
  414. :total="page.total"
  415. :page.sync="page.pageNum"
  416. :limit.sync="page.pageSize"
  417. @pagination="$emit('pagination', { ...$event })"
  418. style="height: 32px; padding: 0 !important; flex: 1; overflow-x: auto"
  419. />
  420. </div>
  421. </div>
  422. </template>
  423. <style lang="scss" scoped>
  424. .el-super-table {
  425. position: relative;
  426. flex: 1;
  427. display: flex;
  428. flex-direction: column;
  429. overflow: auto;
  430. }
  431. ::v-deep.el-super-table .el-table__header .cell {
  432. word-break: keep-all;
  433. white-space: nowrap;
  434. .icon-sort {
  435. display: none;
  436. }
  437. &:hover .icon-sort {
  438. display: inline-block;
  439. }
  440. .icon-freeze {
  441. display: none;
  442. }
  443. &:hover .icon-freeze {
  444. display: inline-block;
  445. }
  446. .icon-filter {
  447. display: none;
  448. }
  449. &:hover .icon-filter {
  450. display: inline-block;
  451. }
  452. .icon-hide {
  453. display: none;
  454. }
  455. &:hover .icon-hide {
  456. display: inline-block;
  457. }
  458. }
  459. </style>