index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. <template>
  2. <el-table
  3. ref="table"
  4. v-bind="tableProps"
  5. v-el-table-infinite-scroll="scrollOver"
  6. :data="dealedTableData"
  7. >
  8. <el-table-column
  9. v-if="sequence || customSequence"
  10. :prop="customSequence ? 'index' : undefined"
  11. :type="customSequence ? undefined : 'index'"
  12. :fixed="hasFixedColumn"
  13. :width="50"
  14. align="center"
  15. >
  16. <template #header>
  17. <TableHeaderCell label="序号" />
  18. </template>
  19. </el-table-column>
  20. <el-table-column
  21. v-for="column in tableColumns"
  22. :key="column.columnName"
  23. v-bind="computedColumnProps(column)"
  24. >
  25. <template #header>
  26. <TableHeaderCell
  27. v-model:filter-values="filterValueMap[column.columnName]"
  28. v-model:sort-rule="sortRuleMap[column.columnName]"
  29. :label="labelFormatter(column.columnLabel)"
  30. :desc="column.columnDescribe"
  31. :show-desc="column.showDesc"
  32. :filter-options="filterOptionMap[column.columnName]"
  33. :sortable="!!column.needSort"
  34. filter-style="arrow"
  35. @update:filter-values="
  36. (filterValues) => {
  37. filterValuesChangeHandler(column.columnName, filterValues);
  38. }
  39. "
  40. @update:sort-rule="
  41. (sortRule) => {
  42. sortRuleChangeHandler(column.columnName, sortRule);
  43. }
  44. "
  45. />
  46. </template>
  47. <template v-if="column.customRender" #default="scope">
  48. <component :is="column.customRender(scope)" />
  49. </template>
  50. </el-table-column>
  51. </el-table>
  52. </template>
  53. <script setup lang="ts">
  54. import TableHeaderCell from "@/components/TableHeaderCell/index.vue";
  55. import type { CSSProperties, VNode } from "vue";
  56. import { TableColumnCtx } from "element-plus/es/components/table/src/table-column/defaults";
  57. import { CommonData, CommonTableColumn } from "~/common";
  58. import { Options, useTableFilterAndSort } from "@/hooks/useTableFilterAndSort";
  59. import { ElTable } from "element-plus";
  60. import { useTableSettingsStore } from "@/store/tableSettings";
  61. import { Query } from "@/api/webApi";
  62. const dataContent = ref({});
  63. const conditon = ref("");
  64. type SummaryMethod<T> = (data: {
  65. columns: TableColumnCtx<T>[];
  66. data: T[];
  67. }) => string[];
  68. type ColumnCls<T> = string | ((data: { row: T; rowIndex: number }) => string);
  69. type ColumnStyle<T> =
  70. | CSSProperties
  71. | ((data: { row: T; rowIndex: number }) => CSSProperties);
  72. type CellCls<T> =
  73. | string
  74. | ((data: {
  75. row: T;
  76. rowIndex: number;
  77. column: TableColumnCtx<T>;
  78. columnIndex: number;
  79. }) => string);
  80. type CellStyle<T> =
  81. | CSSProperties
  82. | ((data: {
  83. row: T;
  84. rowIndex: number;
  85. column: TableColumnCtx<T>;
  86. columnIndex: number;
  87. }) => CSSProperties);
  88. type Sort = {
  89. prop: string;
  90. order: "ascending" | "descending";
  91. init?: any;
  92. silent?: any;
  93. };
  94. type TreeNode = {
  95. expanded?: boolean;
  96. loading?: boolean;
  97. noLazyChildren?: boolean;
  98. indent?: number;
  99. level?: number;
  100. display?: boolean;
  101. };
  102. type Layout = "fixed" | "auto";
  103. type TableColumnProps<T> = {
  104. type?: string;
  105. index?: number | ((index: number) => number);
  106. columnKey?: string;
  107. width?: string | number;
  108. minWidth?: string | number;
  109. fixed?: boolean | string;
  110. renderHeader?: (data: { column: TableColumnCtx<T>; $index: number }) => VNode;
  111. resizable?: boolean;
  112. formatter?: (
  113. row: T,
  114. column: TableColumnCtx<T>,
  115. cellValue: any,
  116. index: number
  117. ) => VNode | string;
  118. showOverflowTooltip?: boolean;
  119. align?: string;
  120. headerAlign?: string;
  121. className?: string;
  122. labelClassName?: string;
  123. selectable?: (row: T, index: number) => boolean;
  124. reserveSelection?: boolean;
  125. };
  126. const props = withDefaults(
  127. defineProps<{
  128. data: CommonData[];
  129. size?: string;
  130. width?: string | number;
  131. height?: string | number;
  132. maxHeight?: string | number;
  133. fit?: boolean;
  134. stripe?: boolean;
  135. border?: boolean;
  136. rowKey?: string | ((row: CommonData) => string);
  137. showHeader?: boolean;
  138. showSummary?: boolean;
  139. sumText?: string;
  140. summaryMethod?: SummaryMethod<CommonData>;
  141. rowClassName?: ColumnCls<CommonData>;
  142. rowStyle?: ColumnStyle<CommonData>;
  143. cellClassName?: CellCls<CommonData>;
  144. cellStyle?: CellStyle<CommonData>;
  145. headerRowClassName?: ColumnCls<CommonData>;
  146. headerRowStyle?: ColumnStyle<CommonData>;
  147. headerCellClassName?: CellCls<CommonData>;
  148. headerCellStyle?: CellStyle<CommonData>;
  149. highlightCurrentRow?: boolean;
  150. currentRowKey?: string | number;
  151. emptyText?: string;
  152. expandRowKeys?: any[];
  153. defaultExpandAll?: boolean;
  154. defaultSort?: Sort;
  155. tooltipEffect?: string;
  156. spanMethod?: (data: {
  157. row: CommonData;
  158. rowIndex: number;
  159. column: TableColumnCtx<CommonData>;
  160. columnIndex: number;
  161. }) =>
  162. | number[]
  163. | {
  164. rowspan: number;
  165. colspan: number;
  166. }
  167. | undefined;
  168. selectOnIndeterminate?: boolean;
  169. indent?: number;
  170. treeProps?: {
  171. hasChildren?: string;
  172. children?: string;
  173. };
  174. lazy?: boolean;
  175. load?: (
  176. row: CommonData,
  177. treeNode: TreeNode,
  178. resolve: (data: CommonData[]) => void
  179. ) => void;
  180. className?: string;
  181. style?: CSSProperties;
  182. tableLayout?: Layout;
  183. flexible?: boolean;
  184. scrollbarAlwaysOn?: boolean;
  185. // 上面是el-table原生属性,下面是自定义属性
  186. columnProps?: TableColumnProps<CommonData>;
  187. columns: (CommonTableColumn & TableColumnProps<CommonData>)[];
  188. sequence?: boolean;
  189. customSequence?: boolean;
  190. filterSortOptions?: Options;
  191. cacheKeys?: string[];
  192. labelFormatter?: (label: string) => string;
  193. }>(),
  194. {
  195. size: "default",
  196. height: "100%",
  197. maxHeight: "100%",
  198. stripe: true,
  199. border: true,
  200. fit: true,
  201. showHeader: true,
  202. labelFormatter: (label: string) => label,
  203. }
  204. );
  205. const defaultSummaryMethod: SummaryMethod<CommonData> = ({ columns, data }) => {
  206. const sums: string[] = [];
  207. columns.forEach((column, index) => {
  208. const countColumn = tableColumns.value.find(
  209. (col) => column.property === col.columnName && col.needCount
  210. );
  211. if (countColumn) {
  212. const sumNumber = data.reduce((prev: number, curr: CommonData) => {
  213. const cellData = curr[column.property];
  214. if (countColumn.countMode === "all") {
  215. return prev + 1;
  216. }
  217. if (countColumn.countMode === "notNull") {
  218. return cellData ? prev + 1 : prev;
  219. }
  220. const value = Number(cellData);
  221. if (!Number.isNaN(value)) {
  222. prev += value;
  223. }
  224. return prev;
  225. }, 0);
  226. sums[index] = sumNumber.toString();
  227. }
  228. });
  229. sums[0] = "合计:" + (sums[0] ?? "");
  230. return sums;
  231. };
  232. const tableProps = computed(() => {
  233. const rawProps = toRaw(props);
  234. const result: { [x: string]: any } = {};
  235. Object.entries(rawProps).forEach(([key, value]) => {
  236. if (
  237. ![
  238. "columnProps",
  239. "columns",
  240. "sequence",
  241. "customSequence",
  242. "filterSortOptions",
  243. "cacheKeys",
  244. "labelFormatter",
  245. ].includes(key) &&
  246. (value ?? "") !== ""
  247. ) {
  248. result[key] = value;
  249. }
  250. if (props.columns.some((column) => column.needCount)) {
  251. result.showSummary = true;
  252. }
  253. if (!result.summaryMethod) {
  254. result.summaryMethod = defaultSummaryMethod;
  255. }
  256. });
  257. return result;
  258. });
  259. const computedColumnProps = computed(() => {
  260. const defaultColumnProps: TableColumnProps<CommonData> = {
  261. align: "center",
  262. };
  263. return (column: CommonTableColumn & TableColumnProps<CommonData>) => ({
  264. ...defaultColumnProps,
  265. ...props.columnProps,
  266. ...column,
  267. });
  268. });
  269. const gueryRoles = async () => {
  270. const { code, returnData } = await Query({
  271. id: DATACONTENT_ID.allId,
  272. dataContent: [
  273. sessionStorage.getItem("auth-id"),
  274. sessionStorage.getItem("User_Id"),
  275. ],
  276. });
  277. conditon.value = returnData.listValues[0].query_col_conditon;
  278. };
  279. gueryRoles();
  280. const tableColumns = ref<CommonTableColumn[]>([]);
  281. const tableData = ref<CommonData[]>([]);
  282. watchEffect(() => {
  283. tableColumns.value = [];
  284. if (conditon.value == null) {
  285. tableColumns.value = props.columns.reduce(
  286. (prevColumns: CommonTableColumn[], column) => {
  287. if (!column.hidden) {
  288. prevColumns.push({
  289. label: column.columnLabel,
  290. prop: column.columnName,
  291. ...column,
  292. });
  293. }
  294. return prevColumns;
  295. },
  296. []
  297. );
  298. } else {
  299. let arr = props.columns.reduce(
  300. (prevColumns: CommonTableColumn[], column) => {
  301. if (!column.hidden) {
  302. prevColumns.push({
  303. label: column.columnLabel,
  304. prop: column.columnName,
  305. ...column,
  306. });
  307. }
  308. return prevColumns;
  309. },
  310. []
  311. );
  312. conditon.value.split(",").forEach((element) => {
  313. arr.forEach((res) => {
  314. if (element === res.columnName) {
  315. tableColumns.value.push(res);
  316. }
  317. });
  318. });
  319. }
  320. tableData.value = props.data;
  321. });
  322. const hasFixedColumn = computed(() =>
  323. tableColumns.value.some((column) => column.fixed)
  324. );
  325. const {
  326. filterOptionMap,
  327. filterValueMap,
  328. sortRuleMap,
  329. dealedTableData,
  330. sortChangeHandler,
  331. } = useTableFilterAndSort(tableColumns, tableData, props.filterSortOptions);
  332. const { saveTableFilterValues } = useTableSettingsStore();
  333. watch(
  334. sortRuleMap,
  335. (map) => {
  336. emit("sortRuleChange", map);
  337. },
  338. { deep: true }
  339. );
  340. const sortRuleChangeHandler = (columnName: string, sortRule: string) => {
  341. sortRuleMap[columnName] = sortRule;
  342. sortChangeHandler(columnName, sortRule);
  343. };
  344. const filterValuesChangeHandler = (
  345. columnName: string,
  346. filterValues: string[]
  347. ) => {
  348. filterValueMap[columnName] = filterValues;
  349. emit("filterChange", filterValues);
  350. };
  351. if (props.cacheKeys?.length) {
  352. watch(filterValueMap, (map) => {
  353. const values: { [x: string]: string[] } = {};
  354. props.cacheKeys!.forEach((columnName) => {
  355. values[columnName] = map[columnName];
  356. });
  357. saveTableFilterValues(values);
  358. });
  359. }
  360. // 组件的inheritAttrs属性默认为true,此时组件上的属性(包括v-on)会被添加到根元素上
  361. const emit = defineEmits(["filterChange", "sortRuleChange", "scrollOver"]);
  362. const scrollOver = () => {
  363. emit("scrollOver");
  364. };
  365. const table = ref<InstanceType<typeof ElTable> | null>(null);
  366. defineExpose({
  367. table,
  368. });
  369. </script>
  370. <style scoped lang="scss">
  371. .el-table :deep {
  372. .el-table__cell {
  373. padding: 0;
  374. height: 40px;
  375. &.cell-filter {
  376. position: relative;
  377. &::before {
  378. content: "";
  379. position: absolute;
  380. width: 100%;
  381. height: 100%;
  382. top: 0;
  383. left: 0;
  384. z-index: 1;
  385. }
  386. &.cell-filter-yellow::before {
  387. opacity: 0.47;
  388. background-color: #eef3d6;
  389. }
  390. &.cell-filter-green::before {
  391. opacity: 0.73;
  392. background-color: #eef3d6;
  393. }
  394. &.cell-filter-cyan::before {
  395. opacity: 0.73;
  396. background-color: #d6e6f3;
  397. }
  398. .cell {
  399. position: relative;
  400. z-index: 2;
  401. }
  402. }
  403. &.align-left .cell {
  404. text-align: left;
  405. }
  406. .cell {
  407. padding: 0;
  408. font-size: 14px;
  409. color: #101116;
  410. font-family: DIN, Microsoft YaHei;
  411. &:not(.el-tooltip) {
  412. white-space: pre-line;
  413. }
  414. }
  415. }
  416. .el-table__header .el-table__cell {
  417. background: #ffffff;
  418. font-weight: bold;
  419. .cell {
  420. height: 100%;
  421. }
  422. }
  423. .el-table__body {
  424. .el-table__row--striped .el-table__cell {
  425. background-color: #f0f3f7;
  426. }
  427. .el-table__cell.cell-click .cell {
  428. color: #2d67e3;
  429. cursor: pointer;
  430. }
  431. }
  432. .el-scrollbar__bar {
  433. &.is-horizontal {
  434. height: 15px;
  435. }
  436. &.is-vertical {
  437. width: 15px;
  438. }
  439. }
  440. }
  441. </style>