index copy.vue 12 KB

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