index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <template>
  2. <div class="containerTable">
  3. <div class="table_box">
  4. <div class="table-title-btn">
  5. <slot name="table-title-btn"></slot>
  6. </div>
  7. <el-table ref="multipleTableRef"
  8. :header-cell-style="{ 'background-color': '#F7F7F9', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
  9. :data="props.tableData" :height="tableHeight" :max-height="maxHeight" table-layout="fixed" style="width: 100%;"
  10. @selection-change="selectionChange" @select-all="selectAllChange" @cell-mouse-enter="handleMouseEnter"
  11. @cell-mouse-leave="handleMouseLeave" :highlight-current-row="highlightCurrentRow"
  12. @current-change="currentChange" v-loading="loading">
  13. <!-- 空内容自定义 -->
  14. <template #empty>
  15. <div class="flex f-c a-c ">
  16. <img src="../../assets/images/empty.png" alt="">
  17. <span style="font-size: 14px;color: #909399;">暂无数据</span>
  18. </div>
  19. </template>
  20. <el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" fixed="left" />
  21. <el-table-column type="expand" v-if="secondaryTable">
  22. <template #default="props">
  23. <div>
  24. <el-table :data="props.row.children">
  25. <el-table-column v-for="column in secondaryColumns" :key="column.prop" :prop="column.prop"
  26. :label="column.label" :sortable="column.sortable ?'custom' : false" :width="column.width">
  27. <template #header>
  28. <slot name="search" :slotData="column"></slot>
  29. </template>
  30. <template #default="scope">
  31. <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)"
  32. class="custom-link">
  33. {{ scope.row[column.prop] }}
  34. </RouterLink>
  35. <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
  36. {{ getTagTypeAndText(scope.row, column).text }}
  37. </el-tag>
  38. <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)"
  39. width="50px" height="50px"></ImagePreview>
  40. <span v-else>
  41. {{ getDisplayText(scope.row, column) }}
  42. </span>
  43. </template>
  44. </el-table-column>
  45. <el-table-column label="操作" fixed="right" :width="props.columnwidth">
  46. <template #default="scope">
  47. <slot name="secondary" :secondaryData="scope.row"></slot>
  48. </template>
  49. </el-table-column>
  50. </el-table>
  51. </div>
  52. </template>
  53. </el-table-column>
  54. <el-table-column v-if="showIndex" type="index" label="序号" width="55" />
  55. <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"
  56. :sortable="column.sortable ?'custom' : false" :width="column.width"
  57. :align="column.align ? column.align : 'center'">
  58. <template #header>
  59. <slot name="search" :slotData="column"></slot>
  60. </template>
  61. <template #default="scope" v-if="column.component === 'customSlot'">
  62. <slot name="customSlot" :data="scope.row"></slot>
  63. </template>
  64. <template #default="scope" v-else>
  65. <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)"
  66. class="custom-link">
  67. {{ scope.row[column.prop] }}
  68. </RouterLink>
  69. <div v-else-if="column.component === 'html'">
  70. <span v-html="htmlEvent(scope.row, column).html"></span>
  71. </div>
  72. <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
  73. {{ getTagTypeAndText(scope.row, column).text }}
  74. </el-tag>
  75. <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)" width="50px"
  76. height="50px"></ImagePreview>
  77. <div v-else>
  78. <div
  79. v-if="typeof getDisplayText(scope.row, column) === 'string' && getDisplayText(scope.row, column).includes('\n')"
  80. class="flex f-c ">
  81. <span v-for="(line, index) in getDisplayText(scope.row, column).split('\n')" :key="index">{{ line
  82. }}</span>
  83. </div>
  84. <span v-else>
  85. {{ getDisplayText(scope.row, column) }}
  86. </span>
  87. </div>
  88. </template>
  89. </el-table-column>
  90. <el-table-column label="操作" fixed="right" :width="props.columnwidth" v-if="props.showOperation" align="center">
  91. <template #default="scope">
  92. <div class="flex a-c j-c">
  93. <slot name="operation" :operationData="scope.row"></slot>
  94. </div>
  95. </template>
  96. </el-table-column>
  97. </el-table>
  98. </div>
  99. <div class="page_box" v-if="props.pageInfo">
  100. <el-pagination v-model:current-page="props.pageInfo.pageNum" v-model:page-size="props.pageInfo.pageSize"
  101. :page-sizes="props.pageInfo.sizes" layout="total, sizes, prev, pager, next, jumper"
  102. :total="props.pageInfo.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  103. </div>
  104. </div>
  105. </template>
  106. <script lang="ts" setup>
  107. import { defineProps, ref, onMounted, defineEmits } from "vue";
  108. import type { TableInstance } from "element-plus";
  109. interface TableRow {
  110. [key: string]: any;
  111. }
  112. const emit = defineEmits(['getList', 'selectAllChange', 'selectionChange', 'currentChange','handleMouseEnter','handleMouseLeave']);
  113. interface Props{
  114. tableData: Array<TableRow>;
  115. columnwidth: number,//操作栏宽度
  116. secondaryTable?: boolean,//是否展示二级表格
  117. highlightCurrentRow?: boolean,//是否可点击
  118. showOperation?: boolean,//操作显示/隐藏
  119. loading?:boolean,
  120. columns: {
  121. prop: string;
  122. label: string;
  123. sortable?: boolean;
  124. width?: string;
  125. style?: string;
  126. align?: string,
  127. component?: string,
  128. formatter?: (row: any, column: any, cellValue: any, index: number) => any;
  129. }[];
  130. secondaryColumns?: {
  131. prop: string;
  132. label: string;
  133. sortable?: boolean;
  134. width?: string;
  135. style?: string;
  136. component?: string,
  137. formatter?: (row: any, column: any, cellValue: any, index: number) => any;
  138. }[];
  139. pageInfo?: {
  140. pageNum: number;
  141. pageSize: number;
  142. sizes: number[];
  143. total: number;
  144. };
  145. showSelect: boolean;
  146. showIndex: boolean;
  147. tableheight?: (number | string),
  148. maxHeight?: (number | string),
  149. uniqueIdName?: string,
  150. };
  151. interface Row {
  152. [key: string]: any; // 假设Row是一个具有任意属性的对象
  153. }
  154. interface RouterLink {
  155. name: string;
  156. }
  157. const props = withDefaults(defineProps<Props>(), {
  158. showOperation: true, // 默认值为 true
  159. });
  160. const loading=ref(false)
  161. const tableHeight = ref(props.tableheight); // 初始化表格高度
  162. const newSelectionList = ref<string[]>([]);
  163. const multipleTableRef = ref<TableInstance>();
  164. const TableData = ref<TableRow[]>();
  165. watch(props, () => {
  166. if(props.loading!=undefined){
  167. loading.value=props.loading;
  168. }
  169. })
  170. onMounted(() => {
  171. let counter = 1;
  172. TableData.value = props.tableData.map((row) => ({
  173. ...row,
  174. _edit: false, // 设置默认非编辑状态
  175. counter: counter++
  176. }));
  177. handleResize();
  178. });
  179. function handleResize() {
  180. // 获取当前窗口的高度
  181. const height = window.innerHeight;
  182. // 在这里可以执行其他操作,比如更新某个元素的高度等
  183. const tableElement = multipleTableRef.value?.$el as HTMLElement;
  184. if (tableElement) {
  185. const offsetTop = tableElement.offsetTop; // 获取 offsetTop
  186. tableHeight.value = height - offsetTop - 230 + "px";
  187. }
  188. }
  189. // 监听窗口的 resize 事件
  190. window.addEventListener('resize', handleResize);
  191. const selectable = () => true;
  192. const handleSizeChange = (val: number) => {
  193. if (props.pageInfo) {
  194. props.pageInfo.pageSize = val
  195. emit('getList', props.pageInfo);
  196. }
  197. }
  198. //多选
  199. const selectAllChange = (value: any[]) => {
  200. newSelectionList.value = [];
  201. if (value.length > 0) {
  202. value.map(val => {
  203. newSelectionList.value.push(val.id)
  204. })
  205. } else {
  206. newSelectionList.value = [];
  207. }
  208. emit('selectAllChange', newSelectionList.value)
  209. }
  210. /**
  211. * //单元格移入事件
  212. * @param row 行数据
  213. */
  214. const handleMouseEnter=(row:any)=>{
  215. emit('handleMouseEnter', row)
  216. }
  217. /**
  218. * //单元格移出事件
  219. * @param row 行数据
  220. */
  221. const handleMouseLeave=(row:any)=>{
  222. emit('handleMouseLeave', row)
  223. }
  224. //行点击事件
  225. const currentChange = (currentRow: any, _oldCurrentRow: any) => {
  226. emit('currentChange', currentRow)
  227. }
  228. const selectionChange = (value: any[]) => {
  229. newSelectionList.value = [];
  230. if (value.length > 0) {
  231. value.map(val => {
  232. if (props.uniqueIdName) {
  233. newSelectionList.value.push(val[props.uniqueIdName])
  234. } else {
  235. newSelectionList.value.push(val.id)
  236. }
  237. })
  238. } else {
  239. newSelectionList.value = [];
  240. }
  241. emit('selectionChange', newSelectionList.value)
  242. }
  243. const handleCurrentChange = (val: number) => {
  244. if (props.pageInfo) {
  245. props.pageInfo.pageNum = val
  246. emit('getList', props.pageInfo);
  247. }
  248. }
  249. //空值处理
  250. const displayValue = (value: any) => {
  251. return value === null || value === undefined || value === '' ? '-' : value;
  252. }
  253. // 格式化内容配置
  254. function getRouterLinkTo(row: Row, column: any): RouterLink {
  255. return column.formatter ? column.formatter(row, column, row[column.prop]) : { name: 'OrganizationAdd' };
  256. }
  257. function getImgTo(row: Row, column: any) {
  258. return row[column.prop];
  259. }
  260. function getTagTypeAndText(row: Row, column: any) {
  261. // 使用 formatter 返回一个对象,包含 type 和 text
  262. return column.formatter ? column.formatter(row, column, row[column.prop]) : { type: 'primary', text: row[column.prop] };
  263. }
  264. function getDisplayText(row: Row, column: any): string {
  265. return column.formatter ? column.formatter(row, column, row[column.prop]) : displayValue(row[column.prop]);
  266. }
  267. function htmlEvent(row: Row, column: any) {
  268. return column.formatter ? column.formatter(row, column, row[column.prop]) : row[column.prop]
  269. }
  270. </script>
  271. <style lang="scss" scoped>
  272. .el-table .el-table__cell .routerlick > a {
  273. color: red;
  274. }
  275. .containerTable {
  276. display: flex;
  277. flex-direction: column;
  278. width: 100%;
  279. height: auto;
  280. }
  281. .custom-link {
  282. color: #0083ff;
  283. /* 设置文字颜色 */
  284. text-decoration: none;
  285. /* 去掉下划线 */
  286. }
  287. .table_box {
  288. flex: 1;
  289. margin-bottom: 10px;
  290. overflow-y: auto;
  291. .table_item_container {
  292. padding: 0;
  293. margin-left: 20px;
  294. .table_item {
  295. margin: 5px 0;
  296. }
  297. }
  298. .table-title-btn {
  299. margin-bottom: 20px;
  300. :deep(.el-button) {
  301. margin-right: 20px;
  302. }
  303. }
  304. }
  305. .page_box {
  306. display: flex;
  307. align-items: center;
  308. justify-content: flex-end;
  309. }
  310. </style>