|
@@ -1,5 +1,10 @@
|
|
|
<template>
|
|
|
<div class="table-page">
|
|
|
+ <!-- 添加列设置按钮 -->
|
|
|
+ <div class="table-settings" v-if="showColumnSetting">
|
|
|
+ <el-button type="text" @click="showColumnSettings"> <i class="el-icon-setting"></i> 列设置 </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
<el-table
|
|
|
:key="renderKey"
|
|
|
ref="elTablet"
|
|
@@ -15,19 +20,22 @@
|
|
|
@row-click="handleRowClick"
|
|
|
@selection-change="handleSelectionChange"
|
|
|
@current-change="handleCurrentChange"
|
|
|
+ @header-dragend="handleHeaderDragend"
|
|
|
highlight-current-row
|
|
|
default-expand-all
|
|
|
:cell-style="cellStyle"
|
|
|
:header-cell-style="{ color: '#515a6e', background: '#f5f5f5' }"
|
|
|
:tree-props="options.treeProps"
|
|
|
:row-style="{ cursor: options.cursor || '' }"
|
|
|
+ :resizable="true"
|
|
|
+ :fit="true"
|
|
|
>
|
|
|
<!--selection选择框-->
|
|
|
<el-table-column v-if="options.mutiSelect" type="selection" :reserve-selection="true" style="width: 50px" align="center"> </el-table-column>
|
|
|
<!--序号-->
|
|
|
<el-table-column v-if="options.index" label="序号" type="index" width="100" align="center"> </el-table-column>
|
|
|
<!--数据列-->
|
|
|
- <template v-for="(column, index) in columns">
|
|
|
+ <template v-for="(column, index) in localTableColumns">
|
|
|
<el-table-column :key="index" :prop="column.prop" :label="column.label" :align="column.align || 'center'" :width="column.width" :fixed="column.fixed" show-overflow-tooltip v-if="!column.isHidden">
|
|
|
<template slot-scope="scope">
|
|
|
<!-- 含有click函数 -->
|
|
@@ -122,10 +130,34 @@
|
|
|
@current-change="handleIndexChange"
|
|
|
style="padding: 20px; text-align: center"
|
|
|
></el-pagination>
|
|
|
+
|
|
|
+ <!-- 添加列设置弹窗 -->
|
|
|
+ <el-dialog v-if="showColumnSetting" title="列设置" :visible.sync="columnSettingsVisible" width="500px">
|
|
|
+ <div class="dialog-header">
|
|
|
+ <el-checkbox v-model="showAllColumns" @change="toggleAllColumns">全选</el-checkbox>
|
|
|
+ <el-button type="text" @click="handleInitConfig">初始化配置</el-button>
|
|
|
+ </div>
|
|
|
+ <el-divider></el-divider>
|
|
|
+ <draggable v-model="localColumns" handle=".drag-handle" @end="handleDragEnd">
|
|
|
+ <div v-for="(col, index) in localColumns" :key="index" class="column-item">
|
|
|
+ <i class="el-icon-rank drag-handle"></i>
|
|
|
+ <el-checkbox v-model="col.isShow" @change="handleColumnChange">
|
|
|
+ {{ col.label }}
|
|
|
+ </el-checkbox>
|
|
|
+ </div>
|
|
|
+ </draggable>
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="columnSettingsVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="saveColumnSettings">确定</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script>
|
|
|
import { deepClone } from '@/utils'
|
|
|
+import draggable from 'vuedraggable'
|
|
|
+import { post } from '@/http/index'
|
|
|
+
|
|
|
export default {
|
|
|
name: 'LTable',
|
|
|
components: {
|
|
@@ -150,7 +182,8 @@ export default {
|
|
|
if (data.props.column) params.column = data.props.column
|
|
|
return data.props.render(h, params)
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
+ draggable
|
|
|
},
|
|
|
props: {
|
|
|
dictData: Object, //字典对象
|
|
@@ -170,6 +203,17 @@ export default {
|
|
|
RowKey: {
|
|
|
type: String,
|
|
|
default: 'id'
|
|
|
+ },
|
|
|
+ tableId: {
|
|
|
+ // 表格唯一标识,用于保存和获取表格配置,为空时使用当前组件路径
|
|
|
+ type: String,
|
|
|
+ required: false, // 改为非必填
|
|
|
+ default: '' // 默认为空字符串
|
|
|
+ },
|
|
|
+ showColumnSetting: {
|
|
|
+ type: Boolean,
|
|
|
+ required: false,
|
|
|
+ default: true // 默认显示
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
@@ -198,7 +242,12 @@ export default {
|
|
|
renderKey: Math.random(),
|
|
|
logo: require('@/assets/logo.png'),
|
|
|
checkedKeys: false,
|
|
|
- editStatusMap: {}
|
|
|
+ editStatusMap: {},
|
|
|
+ columnSettingsVisible: false, // 控制列设置弹窗的显示/隐藏
|
|
|
+ localColumns: [], // 用于列设置弹窗
|
|
|
+ localTableColumns: [], // 用于实际表格渲染
|
|
|
+ showAllColumns: true,
|
|
|
+ currentTableId: ''
|
|
|
}
|
|
|
},
|
|
|
filters: {
|
|
@@ -208,13 +257,64 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
+ // 初始化本地表格数据
|
|
|
+ this.localTableColumns = this.columns.map((col) => ({
|
|
|
+ ...col,
|
|
|
+ isHidden: false
|
|
|
+ }))
|
|
|
+ this.initTableId()
|
|
|
+
|
|
|
if (this.defaultFetch) {
|
|
|
this.options.initTable && this.fetch()
|
|
|
} else {
|
|
|
this.options.initTable
|
|
|
}
|
|
|
+ this.initColumnSettings()
|
|
|
},
|
|
|
methods: {
|
|
|
+ /**
|
|
|
+ * 初始化表格ID
|
|
|
+ * 使用父组件路由路径和当前组件name生成唯一标识
|
|
|
+ */
|
|
|
+ initTableId() {
|
|
|
+ if (this.tableId) {
|
|
|
+ this.currentTableId = this.tableId
|
|
|
+ } else {
|
|
|
+ // 获取父组件实例
|
|
|
+ const parent = this.$parent
|
|
|
+ let parentPath = ''
|
|
|
+
|
|
|
+ // 获取父组件的路由路径
|
|
|
+ if (parent && parent.$route) {
|
|
|
+ parentPath = parent.$route.path
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前组件的name
|
|
|
+ const componentName = parent.$options.name || ''
|
|
|
+
|
|
|
+ // 生成唯一标识:父组件路径-组件名称-索引
|
|
|
+ let instanceId = `${parentPath}-${componentName}`
|
|
|
+
|
|
|
+ // 如果同一个父组件中有多个相同的表格组件,添加索引区分
|
|
|
+ if (parent) {
|
|
|
+ const siblings = parent.$children.filter((child) => child.$options.name === componentName)
|
|
|
+ const index = siblings.indexOf(this)
|
|
|
+ if (index > 0) {
|
|
|
+ instanceId += `-${index}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.currentTableId = instanceId
|
|
|
+ .replace(/^\//, '') // 移除开头的斜杠
|
|
|
+ .replace(/\//g, '-') // 将路径分隔符替换为横杠
|
|
|
+ .replace(/[^a-zA-Z0-9\-_]/g, '-') // 只保留字母、数字、横杠和下划线
|
|
|
+ .replace(/\-+/g, '-') // 将多个连续横杠替换为单个
|
|
|
+ .replace(/^\-|\-$/g, '') // 移除首尾的横杠
|
|
|
+ .toLowerCase() // 转为小写
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('当前表格ID:', this.currentTableId)
|
|
|
+ },
|
|
|
hasChildren(row, showType) {
|
|
|
//父级显示
|
|
|
if (showType && showType == '0') {
|
|
@@ -348,10 +448,244 @@ export default {
|
|
|
},
|
|
|
flattenArray(arr) {
|
|
|
return [].concat(...arr.map((item) => (Array.isArray(item.children) ? this.flattenArray(item.children) : item)))
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 初始化列设置
|
|
|
+ * 从后端获取用户保存的表格配置
|
|
|
+ */
|
|
|
+ async initColumnSettings() {
|
|
|
+ try {
|
|
|
+ const response = await post('/tableConfig/userTableConfig/getUserTableConfig', {
|
|
|
+ tableName: this.currentTableId
|
|
|
+ })
|
|
|
+
|
|
|
+ if (response.data) {
|
|
|
+ const columnConfig = JSON.parse(response.data.columnConfig)
|
|
|
+
|
|
|
+ // 更新 localColumns 和 localTableColumns
|
|
|
+ this.localColumns = this.columns.map((col) => {
|
|
|
+ const savedCol = columnConfig.columns.find((c) => c.prop === col.prop)
|
|
|
+ return {
|
|
|
+ ...col,
|
|
|
+ isShow: savedCol ? savedCol.isShow : true,
|
|
|
+ width: savedCol ? savedCol.width : col.width
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 更新表格显示列
|
|
|
+ this.updateTableColumns()
|
|
|
+ } else {
|
|
|
+ // 没有保存的配置,使用默认配置
|
|
|
+ this.localColumns = this.columns.map((col) => ({
|
|
|
+ ...col,
|
|
|
+ isShow: true
|
|
|
+ }))
|
|
|
+ this.localTableColumns = [...this.columns]
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.renderKey = Math.random()
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取表格配置失败:', error)
|
|
|
+ // 使用默认配置
|
|
|
+ this.localColumns = this.columns.map((col) => ({
|
|
|
+ ...col,
|
|
|
+ isShow: true
|
|
|
+ }))
|
|
|
+ this.localTableColumns = [...this.columns]
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 显示列设置弹窗
|
|
|
+ */
|
|
|
+ showColumnSettings() {
|
|
|
+ this.columnSettingsVisible = true
|
|
|
+ this.checkShowAllStatus()
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 切换全选状态
|
|
|
+ * @param {Boolean} val - 是否全选
|
|
|
+ */
|
|
|
+ toggleAllColumns(val) {
|
|
|
+ this.localColumns.forEach((col) => {
|
|
|
+ col.isShow = val
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单个列显示状态变化
|
|
|
+ */
|
|
|
+ handleColumnChange() {
|
|
|
+ this.checkShowAllStatus()
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否所有列都被选中,更新全选状态
|
|
|
+ */
|
|
|
+ checkShowAllStatus() {
|
|
|
+ this.showAllColumns = this.localColumns.every((col) => col.isShow)
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理拖拽排序完成事件
|
|
|
+ * 仅更新 localColumns 的顺序,不立即应用到表格
|
|
|
+ */
|
|
|
+ handleDragEnd() {
|
|
|
+ // 拖拽排序时只记录新的顺序,不立即更新表格显示
|
|
|
+ console.log('列顺序已更改,点击确定后生效')
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存列设置到后端
|
|
|
+ * 包括列的显示状态、顺序和宽度
|
|
|
+ */
|
|
|
+ async saveColumnSettings() {
|
|
|
+ const columnConfig = {
|
|
|
+ columns: this.localColumns.map(({ prop, isShow, width }, index) => ({
|
|
|
+ prop,
|
|
|
+ isShow,
|
|
|
+ width,
|
|
|
+ order: index
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ await post('/tableConfig/userTableConfig/save', {
|
|
|
+ tableName: this.currentTableId,
|
|
|
+ columnConfig: JSON.stringify(columnConfig)
|
|
|
+ })
|
|
|
+
|
|
|
+ // 更新表格显示列
|
|
|
+ this.updateTableColumns()
|
|
|
+
|
|
|
+ this.columnSettingsVisible = false
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.renderKey = Math.random()
|
|
|
+ })
|
|
|
+
|
|
|
+ this.$message.success('配置保存成功')
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存表格配置失败:', error)
|
|
|
+ this.$message.error('配置保存失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化列配置
|
|
|
+ * 重置为系统默认配置
|
|
|
+ */
|
|
|
+ async handleInitConfig() {
|
|
|
+ try {
|
|
|
+ await this.$confirm('确定要初始化列配置吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+
|
|
|
+ await post('/tableConfig/userTableConfig/initUserTableConfig', {
|
|
|
+ tableName: this.currentTableId
|
|
|
+ })
|
|
|
+
|
|
|
+ // 重新获取配置
|
|
|
+ await this.initColumnSettings()
|
|
|
+
|
|
|
+ this.$message.success('初始化配置成功')
|
|
|
+ } catch (error) {
|
|
|
+ if (error !== 'cancel') {
|
|
|
+ // 排除取消操作的错误
|
|
|
+ console.error('初始化配置失败:', error)
|
|
|
+ this.$message.error('初始化配置失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 新增方法:根据 localColumns 更新表格显示列
|
|
|
+ updateTableColumns() {
|
|
|
+ this.localTableColumns = this.localColumns.map((col) => ({
|
|
|
+ ...col,
|
|
|
+ isHidden: !col.isShow
|
|
|
+ }))
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理列宽拖动结束事件
|
|
|
+ * @param {Number} newWidth - 新的宽度
|
|
|
+ * @param {Number} oldWidth - 旧的宽度
|
|
|
+ * @param {Object} column - 列对象
|
|
|
+ */
|
|
|
+ handleHeaderDragend(newWidth, oldWidth, column) {
|
|
|
+ // 如果宽度没有变化,不处理
|
|
|
+ if (newWidth === oldWidth) return
|
|
|
+
|
|
|
+ // 更新本地配置
|
|
|
+ const targetColumn = this.localColumns.find((col) => col.prop === column.property)
|
|
|
+ if (targetColumn) {
|
|
|
+ targetColumn.width = newWidth
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新表格显示列
|
|
|
+ this.updateTableColumns()
|
|
|
+
|
|
|
+ // 保存到后台
|
|
|
+ this.saveColumnSettings()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 可选:添加 watch 来监听原始 columns 的变化
|
|
|
+ watch: {
|
|
|
+ columns: {
|
|
|
+ handler(newColumns) {
|
|
|
+ // 当原始列变化时,更新本地列
|
|
|
+ this.localTableColumns = newColumns.map((col) => ({
|
|
|
+ ...col,
|
|
|
+ isHidden: false
|
|
|
+ }))
|
|
|
+ },
|
|
|
+ deep: true
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
@import './index.scss';
|
|
|
+
|
|
|
+.table-settings {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+.column-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px;
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
+
|
|
|
+ .drag-handle {
|
|
|
+ cursor: move; // 显示移动光标
|
|
|
+ margin-right: 10px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.table-page {
|
|
|
+ .el-table {
|
|
|
+ // 添加过渡效果使宽度变化更平滑
|
|
|
+ ::v-deep .el-table__header-wrapper,
|
|
|
+ ::v-deep .el-table__body-wrapper {
|
|
|
+ th,
|
|
|
+ td {
|
|
|
+ transition: width 0.2s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 5px;
|
|
|
+}
|
|
|
</style>
|