mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: 数据库表数据支持字段设置、表格宽度自适应调整
This commit is contained in:
@@ -105,8 +105,7 @@
|
||||
</template>
|
||||
|
||||
<template #default="scope" v-else>
|
||||
<span>{{ item.formatFunc ? item.formatFunc(scope.row[item.prop]) : scope.row[item.prop]
|
||||
}}</span>
|
||||
<span>{{ item.getValueByData(scope.row)}}</span>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
@@ -224,7 +223,7 @@ watch(() => props.data, (newValue: any) => {
|
||||
if (newValue.length > 0) {
|
||||
props.columns.forEach(item => {
|
||||
if (item.autoWidth && item.show) {
|
||||
item.minWidth = TableColumn.flexColumnWidth(item.prop, item.label, props.data) + item.addWidth
|
||||
item.autoCalculateMinWidth(props.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,8 +45,15 @@ export class TableColumn {
|
||||
|
||||
align: string = "center"
|
||||
|
||||
/**
|
||||
* 指定格式化函数对原始值进行格式化,如时间格式化等
|
||||
* param1: data, param2: prop
|
||||
*/
|
||||
formatFunc: Function
|
||||
|
||||
/**
|
||||
* 是否显示该列
|
||||
*/
|
||||
show: boolean = true
|
||||
|
||||
constructor(prop: string, label: string) {
|
||||
@@ -54,6 +61,18 @@ export class TableColumn {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该列在指定行数据中的值
|
||||
* @param rowData 该行对应的数据
|
||||
* @returns 该列对应的值
|
||||
*/
|
||||
getValueByData(rowData: any) {
|
||||
if (this.formatFunc) {
|
||||
return this.formatFunc(rowData, this.prop);
|
||||
}
|
||||
return rowData[this.prop];
|
||||
}
|
||||
|
||||
static new(prop: string, label: string): TableColumn {
|
||||
return new TableColumn(prop, label)
|
||||
}
|
||||
@@ -74,6 +93,11 @@ export class TableColumn {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该列的格式化回调函数
|
||||
* @param func 格式化回调函数(参数为 -> data: 该行对应的数据,prop: 该列对应的prop属性值)
|
||||
* @returns
|
||||
*/
|
||||
setFormatFunc(func: Function): TableColumn {
|
||||
this.formatFunc = func;
|
||||
return this;
|
||||
@@ -84,7 +108,9 @@ export class TableColumn {
|
||||
* @returns this
|
||||
*/
|
||||
isTime(): TableColumn {
|
||||
this.setFormatFunc(dateFormat)
|
||||
this.setFormatFunc((data: any, prop: string) => {
|
||||
return dateFormat(data[prop])
|
||||
})
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -100,41 +126,49 @@ export class TableColumn {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 自动计算最小宽度
|
||||
* @param str 字符串
|
||||
* @param tableData 表数据
|
||||
* @param label 表头label也参与宽度计算
|
||||
* @returns 列宽度
|
||||
*/
|
||||
static flexColumnWidth = (str: any, label: string, tableData: any): number => {
|
||||
// str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
|
||||
str = str + '';
|
||||
let columnContent = '';
|
||||
autoCalculateMinWidth = (tableData: any) => {
|
||||
const prop = this.prop
|
||||
const label = this.label
|
||||
|
||||
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
|
||||
return 0;
|
||||
}
|
||||
if (!str || !str.length || str.length === 0 || str === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let maxWidthText = ""
|
||||
let maxWidthValue
|
||||
// 为了兼容formatFunc格式化回调函数
|
||||
let maxData
|
||||
// 获取该列中最长的数据(内容)
|
||||
let index = 0;
|
||||
for (let i = 0; i < tableData.length; i++) {
|
||||
if (!tableData[i][str]) {
|
||||
let nowData = tableData[i]
|
||||
let nowValue = nowData[prop]
|
||||
if (!nowValue) {
|
||||
continue;
|
||||
}
|
||||
const now_temp = tableData[i][str] + '';
|
||||
const max_temp = tableData[index][str] + '';
|
||||
if (now_temp.length > max_temp.length) {
|
||||
index = i;
|
||||
// 转为字符串比较长度
|
||||
let nowText = nowValue + "";
|
||||
if (nowText.length > maxWidthText.length) {
|
||||
maxWidthText = nowText;
|
||||
maxWidthValue = nowValue;
|
||||
maxData = nowData;
|
||||
}
|
||||
}
|
||||
columnContent = tableData[index][str] + '';
|
||||
if (this.formatFunc && maxWidthValue) {
|
||||
maxWidthText = this.formatFunc(maxData, prop) + ""
|
||||
}
|
||||
// 需要加上表格的内间距等,视情况加
|
||||
const contentWidth: number = getTextWidth(columnContent) + 30;
|
||||
const contentWidth: number = getTextWidth(maxWidthText) + 30;
|
||||
// 获取label的宽度,取较大的宽度
|
||||
const columnWidth: number = getTextWidth(label) + 60;
|
||||
const flexWidth: number = contentWidth > columnWidth ? contentWidth : columnWidth;
|
||||
return flexWidth > 400 ? 400 : flexWidth;
|
||||
// 设置上限与累加需要额外增加的宽度
|
||||
this.minWidth = (flexWidth > 400 ? 400 : flexWidth) + this.addWidth;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #hostPort="{ data }">
|
||||
{{ `${data.host}:${data.port}` }}
|
||||
</template>
|
||||
|
||||
<template #database="{ data }">
|
||||
<el-popover placement="right" trigger="click" :width="300">
|
||||
<template #reference>
|
||||
@@ -313,7 +309,7 @@ const state = reactive({
|
||||
columns: [
|
||||
TableColumn.new("tagPath", "标签路径").setSlot("tagPath").setAddWidth(20),
|
||||
TableColumn.new("name", "名称"),
|
||||
TableColumn.new("host", "host:port").setSlot("hostPort").setAddWidth(35),
|
||||
TableColumn.new("host", "host:port").setFormatFunc((data: any, _prop: string) => `${data.host}:${data.port}`),
|
||||
TableColumn.new("type", "类型"),
|
||||
TableColumn.new("database", "数据库").setSlot("database").setMinWidth(70),
|
||||
TableColumn.new("username", "用户名"),
|
||||
|
||||
@@ -517,7 +517,7 @@ const registerSqlCompletionItemProvider = () => {
|
||||
},
|
||||
kind: monaco.languages.CompletionItemKind.Property,
|
||||
detail: '', // 不显示detail, 否则选中时备注等会被遮挡
|
||||
insertText: fieldName + ' ', // create_time
|
||||
insertText: fieldName, // create_time
|
||||
range,
|
||||
sortText: 100 + index + '' // 使用表字段声明顺序排序,排序需为字符串类型
|
||||
});
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table @cell-dblclick="(row: any, column: any, cell: any, event: any) => cellClick(row, column, cell)"
|
||||
@sort-change="(sort: any) => onTableSortChange(sort)" @selection-change="onDataSelectionChange"
|
||||
:data="datas" size="small" :max-height="tableHeight" v-loading="loading" element-loading-text="查询中..."
|
||||
:empty-text="emptyText" highlight-current-row stripe border class="mt5">
|
||||
@sort-change="(sort: any) => onTableSortChange(sort)" @selection-change="onDataSelectionChange" :data="datas"
|
||||
size="small" :max-height="tableHeight" v-loading="loading" element-loading-text="查询中..." :empty-text="emptyText"
|
||||
highlight-current-row stripe border class="mt5">
|
||||
<el-table-column v-if="datas.length > 0 && table" type="selection" width="35" />
|
||||
<el-table-column min-width="100" :width="DbInst.flexColumnWidth(item, datas)" align="center"
|
||||
v-for="item in columnNames" :key="item" :prop="item" :label="item" show-overflow-tooltip
|
||||
:sortable="sortable">
|
||||
<template #header v-if="showColumnTip">
|
||||
<el-tooltip raw-content placement="top" effect="customized">
|
||||
<template #content> {{ getColumnTip(item) }} </template>
|
||||
{{ item }}
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<template v-for="(item, index) in columns">
|
||||
<el-table-column min-width="100" :width="DbInst.flexColumnWidth(item.columnName, datas)" align="center"
|
||||
v-if="item.show" :key="index" :prop="item.columnName" :label="item.columnName" show-overflow-tooltip
|
||||
:sortable="sortable">
|
||||
<template #header v-if="showColumnTip">
|
||||
<el-tooltip raw-content placement="top" effect="customized">
|
||||
<template #content> {{ getColumnTip(item) }} </template>
|
||||
{{ item.columnName }}
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -45,8 +49,8 @@ const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
},
|
||||
columnNames: {
|
||||
type: Array,
|
||||
columns: {
|
||||
type: Array<any>,
|
||||
},
|
||||
sortable: {
|
||||
type: [String, Boolean],
|
||||
@@ -76,7 +80,6 @@ const state = reactive({
|
||||
db: '', // 数据库名
|
||||
table: '', // 当前的表名
|
||||
datas: [],
|
||||
columnNames: [],
|
||||
columns: [],
|
||||
sortable: false,
|
||||
loading: false,
|
||||
@@ -92,7 +95,6 @@ const {
|
||||
datas,
|
||||
sortable,
|
||||
loading,
|
||||
columnNames,
|
||||
showColumnTip,
|
||||
} = toRefs(state);
|
||||
|
||||
@@ -114,24 +116,16 @@ const setState = (props: any) => {
|
||||
state.tableHeight = props.height;
|
||||
state.sortable = props.sortable;
|
||||
state.loading = props.loading;
|
||||
state.columnNames = props.columnNames;
|
||||
state.columns = props.columns;
|
||||
state.showColumnTip = props.showColumnTip;
|
||||
state.emptyText = props.emptyText;
|
||||
}
|
||||
|
||||
const getColumnTip = (columnName: string) => {
|
||||
// 优先从 table map中获取
|
||||
let columns = getNowDb().getColumns(state.table);
|
||||
if (!columns) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const column = columns.find((c: any) => c.columnName == columnName);
|
||||
const getColumnTip = (column: any) => {
|
||||
const comment = column.columnComment;
|
||||
return `${column.columnType} ${comment ? ' | ' + comment : ''}`;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 表排序字段变更
|
||||
*/
|
||||
@@ -269,7 +263,7 @@ const submitUpdateFields = () => {
|
||||
a.fields.forEach(f => {
|
||||
sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},`
|
||||
// 如果修改的字段是主键
|
||||
if(f.fieldName === primaryKeyName){
|
||||
if (f.fieldName === primaryKeyName) {
|
||||
primaryKey = f.oldValue
|
||||
}
|
||||
divs.push(f.div)
|
||||
@@ -305,10 +299,6 @@ const changeUpdatedField = () => {
|
||||
emits('changeUpdatedField', state.updatedFields);
|
||||
}
|
||||
|
||||
const getNowDb = () => {
|
||||
return DbInst.getInst(state.dbId).getDb(state.db);
|
||||
}
|
||||
|
||||
const getNowDbInst = () => {
|
||||
return DbInst.getInst(state.dbId);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</span>
|
||||
</el-row>
|
||||
<db-table ref="dbTableRef" :db-id="state.ti.dbId" :db="state.ti.db" :data="execRes.data" :table="state.table"
|
||||
:column-names="execRes.tableColumn" :loading="loading" :height="tableDataHeight"
|
||||
:columns="execRes.tableColumn" :loading="loading" :height="tableDataHeight"
|
||||
empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改" @selection-change="onDataSelectionChange"
|
||||
@change-updated-field="changeUpdatedField"></db-table>
|
||||
</div>
|
||||
@@ -327,7 +327,13 @@ const onRunSql = async () => {
|
||||
ElMessage.warning('未查询到结果集')
|
||||
}
|
||||
state.execRes.data = colAndData.res;
|
||||
state.execRes.tableColumn = colAndData.colNames;
|
||||
// 兼容表格字段配置
|
||||
state.execRes.tableColumn = colAndData.colNames.map((x: any) => {
|
||||
return {
|
||||
columnName: x,
|
||||
show: true,
|
||||
}
|
||||
});
|
||||
cancelUpdateFields()
|
||||
} catch (e: any) {
|
||||
state.execRes.data = [];
|
||||
@@ -486,7 +492,7 @@ const replaceSelection = (str: string, selection: any) => {
|
||||
const exportData = () => {
|
||||
const dataList = state.execRes.data as any;
|
||||
isTrue(dataList.length > 0, '没有数据可导出');
|
||||
exportCsv(`数据查询导出-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}`, state.execRes.tableColumn, dataList)
|
||||
exportCsv(`数据查询导出-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}`, state.execRes.tableColumn.map((x: any) => x.columnName), dataList)
|
||||
};
|
||||
|
||||
const beforeUpload = (file: File) => {
|
||||
|
||||
@@ -6,6 +6,18 @@
|
||||
</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-popover placement="bottom" title="表格字段配置" width="auto" trigger="click">
|
||||
<div v-for="(item, index) in columns" :key="index">
|
||||
<el-checkbox v-model="item.show"
|
||||
:label="`${!item.columnComment ? item.columnName : item.columnName + ' [' + item.columnComment + ']'}`"
|
||||
:true-label="true" :false-label="false" />
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-link icon="Operation" size="small" :underline="false"></el-link>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-link @click="onShowAddDataDialog()" type="primary" icon="plus" :underline="false"></el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
@@ -44,11 +56,10 @@
|
||||
<template #reference>
|
||||
<el-link type="success" :underline="false">选择列</el-link>
|
||||
</template>
|
||||
<el-table :data="columns" max-height="500" size="small" @row-click="
|
||||
(...event: any) => {
|
||||
onConditionRowClick(event);
|
||||
}
|
||||
" style="cursor: pointer">
|
||||
<el-table :data="columns" max-height="500" size="small" @row-click="(...event: any) => {
|
||||
onConditionRowClick(event);
|
||||
}
|
||||
" style="cursor: pointer">
|
||||
<el-table-column property="columnName" label="列名" show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column property="columnComment" label="备注" show-overflow-tooltip>
|
||||
@@ -65,9 +76,9 @@
|
||||
</el-row>
|
||||
|
||||
<db-table ref="dbTableRef" :db-id="state.ti.dbId" :db="state.ti.db" :data="datas" :table="state.table"
|
||||
:column-names="columnNames" :loading="loading" :height="tableHeight" :show-column-tip="true"
|
||||
:sortable="'custom'" @sort-change="(sort: any) => onTableSortChange(sort)"
|
||||
@selection-change="onDataSelectionChange" @change-updated-field="changeUpdatedField"></db-table>
|
||||
:columns="columns" :loading="loading" :height="tableHeight" :show-column-tip="true" :sortable="'custom'"
|
||||
@sort-change="(sort: any) => onTableSortChange(sort)" @selection-change="onDataSelectionChange"
|
||||
@change-updated-field="changeUpdatedField"></db-table>
|
||||
|
||||
<el-row type="flex" class="mt5" justify="center">
|
||||
<el-pagination small :total="count" @current-change="pageChange()" layout="prev, pager, next, total, jumper"
|
||||
@@ -156,7 +167,6 @@ const state = reactive({
|
||||
orderBy: '',
|
||||
condition: '', // 当前条件框的条件
|
||||
loading: false, // 是否在加载数据
|
||||
columnNames: [],
|
||||
columns: [] as any,
|
||||
pageNum: 1,
|
||||
count: 0,
|
||||
@@ -185,7 +195,6 @@ const {
|
||||
condition,
|
||||
loading,
|
||||
columns,
|
||||
columnNames,
|
||||
pageNum,
|
||||
count,
|
||||
hasUpdatedFileds,
|
||||
@@ -205,8 +214,10 @@ onMounted(async () => {
|
||||
notBlank(state.table, "TableData组件params.table信息不能为空")
|
||||
|
||||
const columns = await state.ti.getNowDbInst().loadColumns(state.ti.db, state.table);
|
||||
columns.forEach((x: any) => {
|
||||
x.show = true;
|
||||
})
|
||||
state.columns = columns;
|
||||
state.columnNames = columns.map((t: any) => t.columnName);
|
||||
await onRefresh();
|
||||
})
|
||||
|
||||
@@ -253,7 +264,13 @@ const selectData = async () => {
|
||||
const exportData = () => {
|
||||
const dataList = state.datas as any;
|
||||
isTrue(dataList.length > 0, '没有数据可导出');
|
||||
exportCsv(`数据导出-${state.table}-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}`, state.columnNames, dataList)
|
||||
let columnNames = [];
|
||||
for (let column of state.columns) {
|
||||
if (column.show) {
|
||||
columnNames.push(column.columnName);
|
||||
}
|
||||
}
|
||||
exportCsv(`数据导出-${state.table}-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}`, columnNames, dataList)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ export class DbInst {
|
||||
// 优先从 table map中获取
|
||||
let columns = db.getColumns(table);
|
||||
if (columns) {
|
||||
|
||||
return columns;
|
||||
}
|
||||
console.log(`load columns -> dbName: ${dbName}, table: ${table}`);
|
||||
@@ -301,32 +302,35 @@ export class DbInst {
|
||||
* @param flag 标志
|
||||
* @returns 列宽度
|
||||
*/
|
||||
static flexColumnWidth = (str: any, tableData: any) => {
|
||||
// str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
|
||||
str = str + '';
|
||||
let columnContent = '';
|
||||
static flexColumnWidth = (prop: any, tableData: any) => {
|
||||
if (!prop || !prop.length || prop.length === 0 || prop === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取列名称的长度 加上排序图标长度
|
||||
const columnWidth: number = getTextWidth(prop) + 40;
|
||||
// prop为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
|
||||
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
|
||||
return;
|
||||
}
|
||||
if (!str || !str.length || str.length === 0 || str === undefined) {
|
||||
return;
|
||||
return columnWidth;
|
||||
}
|
||||
|
||||
// 获取该列中最长的数据(内容)
|
||||
let maxWidthText = ""
|
||||
let maxWidthValue
|
||||
// 获取该列中最长的数据(内容)
|
||||
let index = 0;
|
||||
for (let i = 0; i < tableData.length; i++) {
|
||||
if (!tableData[i][str]) {
|
||||
let nowValue = tableData[i][prop]
|
||||
if (!nowValue) {
|
||||
continue;
|
||||
}
|
||||
const now_temp = tableData[i][str] + '';
|
||||
const max_temp = tableData[index][str] + '';
|
||||
if (now_temp.length > max_temp.length) {
|
||||
index = i;
|
||||
// 转为字符串比较长度
|
||||
let nowText = nowValue + "";
|
||||
if (nowText.length > maxWidthText.length) {
|
||||
maxWidthText = nowText;
|
||||
maxWidthValue = nowValue;
|
||||
}
|
||||
}
|
||||
columnContent = tableData[index][str] + '';
|
||||
const contentWidth: number = getTextWidth(columnContent) + 15;
|
||||
// 获取列名称的长度 加上排序图标长度
|
||||
const columnWidth: number = getTextWidth(str) + 40;
|
||||
const contentWidth: number = getTextWidth(maxWidthText) + 15;
|
||||
const flexWidth: number = contentWidth > columnWidth ? contentWidth : columnWidth;
|
||||
return flexWidth > 500 ? 500 : flexWidth;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user