feat: 数据库表格重构

This commit is contained in:
meilin.huang
2023-02-14 11:44:48 +08:00
parent 70b586e45a
commit d189ee7c22
5 changed files with 378 additions and 364 deletions

View File

@@ -116,7 +116,7 @@ const loadTableData = async (inst: any, schema: string, tableName: string) => {
tab.dbType = inst.type; tab.dbType = inst.type;
tab.db = schema; tab.db = schema;
tab.type = TabType.TableData; tab.type = TabType.TableData;
tab.other = { tab.params = {
table: tableName table: tableName
} }
state.tabs.set(label, tab) state.tabs.set(label, tab)
@@ -136,7 +136,7 @@ const addQueryTab = async (inst: any, db: string, sqlName: string = '') => {
} else { } else {
let count = 1; let count = 1;
state.tabs.forEach((v) => { state.tabs.forEach((v) => {
if (v.type == TabType.Query && !v.other.sqlName) { if (v.type == TabType.Query && !v.params.sqlName) {
count++; count++;
} }
}) })
@@ -153,7 +153,7 @@ const addQueryTab = async (inst: any, db: string, sqlName: string = '') => {
tab.dbType = inst.type; tab.dbType = inst.type;
tab.db = db; tab.db = db;
tab.type = TabType.Query; tab.type = TabType.Query;
tab.other = { tab.params = {
sqlName: sqlName, sqlName: sqlName,
dbs: instanceTreeRef.value.getSchemas(dbId) dbs: instanceTreeRef.value.getSchemas(dbId)
} }
@@ -258,7 +258,7 @@ const registerSqlCompletionItemProvider = () => {
const tokens = textBeforePointer.trim().split(/\s+/) const tokens = textBeforePointer.trim().split(/\s+/)
const lastToken = tokens[tokens.length - 1].toLowerCase() const lastToken = tokens[tokens.length - 1].toLowerCase()
const dbs = nowTab.other && nowTab.other.dbs; const dbs = nowTab.params && nowTab.params.dbs;
// console.log("光标前文本:=>" + textBeforePointerMulti) // console.log("光标前文本:=>" + textBeforePointerMulti)
// console.log("最后输入的:=>" + lastToken) // console.log("最后输入的:=>" + lastToken)
@@ -290,14 +290,6 @@ const registerSqlCompletionItemProvider = () => {
if (tableInfo.tableName) { if (tableInfo.tableName) {
let table = tableInfo.tableName let table = tableInfo.tableName
let db = tableInfo.dbName; let db = tableInfo.dbName;
// // 取出表名并提示
// let dbs = state.monacoOptions.dbTables[dbId + db]
// let columns = dbs ? (dbs[table] || []) : [];
// if ((!columns || columns.length === 0) && db) {
// state.monacoOptions.dbTables[dbId + db] = await loadHintTables(dbId, db)
// dbs = state.monacoOptions.dbTables[dbId + db]
// columns = dbs ? (dbs[table] || []) : [];
// }
// 取出表名并提示 // 取出表名并提示
let dbHits = await dbInst.loadDbHints(db) let dbHits = await dbInst.loadDbHints(db)
let columns = dbHits[table] let columns = dbHits[table]

View File

@@ -0,0 +1,323 @@
<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" 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>
</el-table>
</div>
</template>
<script lang="ts" setup>
import { onMounted, watch, reactive, toRefs } from 'vue';
import { DbInst, UpdateFieldsMeta, FieldsMeta } from '../db';
const emits = defineEmits(['sortChange', 'deleteData', 'selectionChange', 'changeUpdatedField'])
const props = defineProps({
dbId: {
type: Number,
required: true,
},
dbType: {
type: String,
default: ''
},
db: {
type: String,
required: true,
},
table: {
type: String,
default: '',
},
data: {
type: Array,
},
columnNames: {
type: Array,
},
sortable: {
type: [String, Boolean],
default: false,
},
loading: {
type: Boolean,
default: false,
},
emptyText: {
type: String,
default: '暂无数据',
},
showColumnTip: {
type: Boolean,
default: false,
},
height: {
type: String,
default: '600'
}
})
const state = reactive({
dbId: 0, // 当前选中操作的数据库实例
dbType: '',
db: '', // 数据库名
table: '', // 当前的表名
datas: [],
columnNames: [],
columns: [],
sortable: false,
loading: false,
selectionDatas: [] as any,
showColumnTip: false,
tableHeight: '600',
emptyText: '',
updatedFields: [] as UpdateFieldsMeta[],// 各个tab表被修改的字段信息
});
const {
tableHeight,
datas,
sortable,
loading,
columnNames,
showColumnTip,
} = toRefs(state);
watch(props, (newValue: any) => {
setState(newValue);
});
onMounted(async () => {
console.log('in DbTable mounted');
setState(props);
})
const setState = (props: any) => {
state.dbId = props.dbId;
state.dbType = props.dbType;
state.db = props.db;
state.table = props.table;
state.datas = props.data;
state.tableHeight = props.height;
state.sortable = props.sortable;
state.loading = props.loading;
state.columnNames = props.columnNames;
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 comment = column.columnComment;
return `${column.columnType} ${comment ? ' | ' + comment : ''}`;
};
/**
* 表排序字段变更
*/
const onTableSortChange = async (sort: any) => {
if (!sort.prop) {
return;
}
cancelUpdateFields();
emits('sortChange', sort);
};
const onDataSelectionChange = (datas: []) => {
state.selectionDatas = datas;
emits('selectionChange', datas);
};
// 监听单元格点击事件
const cellClick = (row: any, column: any, cell: any) => {
const property = column.property;
// 如果当前操作的表名不存在 或者 当前列的property不存在(如多选框),则不允许修改当前单元格内容
if (!state.table || !property) {
return;
}
let div: HTMLElement = cell.children[0];
if (div && div.tagName === 'DIV') {
// 转为字符串比较,可能存在数字等
let text = (row[property] || row[property] == 0 ? row[property] : '') + '';
let input = document.createElement('input');
input.setAttribute('value', text);
// 将表格width也赋值于输入框避免输入框长度超过表格长度
input.setAttribute('style', 'height:23px;text-align:center;border:none;' + div.getAttribute('style'));
cell.replaceChildren(input);
input.focus();
input.addEventListener('blur', async () => {
row[property] = input.value;
cell.replaceChildren(div);
if (input.value !== text) {
let currentUpdatedFields = state.updatedFields
const dbInst = getNowDbInst();
// 主键
const primaryKey = await dbInst.loadTableColumn(state.db, state.table);
const primaryKeyValue = row[primaryKey.columnName];
// 更新字段列信息
const updateColumn = await dbInst.loadTableColumn(state.db, state.table, property);
const newField = {
div, row,
fieldName: column.rawColumnKey,
fieldType: updateColumn.columnType,
oldValue: text,
newValue: input.value
} as FieldsMeta;
// 被修改的字段
const primaryKeyFields = currentUpdatedFields.filter((meta) => meta.primaryKey === primaryKeyValue)
let hasKey = false;
if (primaryKeyFields.length <= 0) {
primaryKeyFields[0] = {
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields: [newField]
}
} else {
hasKey = true
let hasField = primaryKeyFields[0].fields.some(a => {
if (a.fieldName === newField.fieldName) {
a.newValue = newField.newValue
}
return a.fieldName === newField.fieldName
})
if (!hasField) {
primaryKeyFields[0].fields.push(newField)
}
}
let fields = primaryKeyFields[0].fields
const fieldsParam = fields.filter((a) => {
if (a.fieldName === column.rawColumnKey) {
a.newValue = input.value
}
return a.fieldName === column.rawColumnKey
})
const field = fieldsParam.length > 0 && fieldsParam[0] || {} as FieldsMeta
if (field.oldValue === input.value) { // 新值=旧值
// 删除数据
div.classList.remove('update_field_active')
let delIndex: number[] = [];
currentUpdatedFields.forEach((a, i) => {
if (a.primaryKey === primaryKeyValue) {
a.fields = a.fields && a.fields.length > 0 ? a.fields.filter(f => f.fieldName !== column.rawColumnKey) : [];
a.fields.length <= 0 && delIndex.push(i)
}
});
delIndex.forEach(i => delete currentUpdatedFields[i])
currentUpdatedFields = currentUpdatedFields.filter(a => a)
} else {
// 新增数据
div.classList.add('update_field_active')
if (hasKey) {
currentUpdatedFields.forEach((value, index, array) => {
if (value.primaryKey === primaryKeyValue) {
array[index].fields = fields
}
})
} else {
currentUpdatedFields.push({
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields
})
}
}
state.updatedFields = currentUpdatedFields;
changeUpdatedField();
}
});
}
};
const submitUpdateFields = () => {
let currentUpdatedFields = state.updatedFields;
if (currentUpdatedFields.length <= 0) {
return;
}
const db = state.db;
let res = '';
let divs: HTMLElement[] = [];
currentUpdatedFields.forEach(a => {
let sql = `UPDATE ${state.table} SET `;
let primaryKey = a.primaryKey;
let primaryKeyType = a.primaryKeyType;
let primaryKeyName = a.primaryKeyName;
a.fields.forEach(f => {
sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},`
divs.push(f.div)
})
sql = sql.substring(0, sql.length - 1)
sql += ` WHERE ${primaryKeyName} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;`
res += sql;
})
DbInst.getInst(state.dbId).promptExeSql(db, res, () => { }, () => {
currentUpdatedFields = [];
divs.forEach(a => {
a.classList.remove('update_field_active');
})
state.updatedFields = [];
changeUpdatedField();
});
}
const cancelUpdateFields = () => {
state.updatedFields.forEach((a: any) => {
a.fields.forEach((b: any) => {
b.div.classList.remove('update_field_active')
b.row[b.fieldName] = b.oldValue
})
})
state.updatedFields = [];
changeUpdatedField();
}
const changeUpdatedField = () => {
emits('changeUpdatedField', state.updatedFields);
}
const getNowDb = () => {
return DbInst.getInst(state.dbId).getDb(state.db);
}
const getNowDbInst = () => {
return DbInst.getInst(state.dbId, state.dbType);
}
defineExpose({
submitUpdateFields,
cancelUpdateFields
})
</script>
<style lang="scss">
.update_field_active {
background-color: var(--el-color-success)
}
</style>

View File

@@ -51,33 +51,28 @@
<el-link type="success" :underline="false" @click="exportData"><span <el-link type="success" :underline="false" @click="exportData"><span
style="font-size: 12px">导出</span></el-link> style="font-size: 12px">导出</span></el-link>
</span> </span>
<span v-if="updatedFields.length > 0"> <span v-if="hasUpdatedFileds">
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-link type="success" :underline="false" @click="submitUpdateFields()"><span <el-link type="success" :underline="false" @click="submitUpdateFields()"><span
style="font-size: 12px">提交</span></el-link> style="font-size: 12px">提交</span></el-link>
</span> </span>
<span v-if="updatedFields.length > 0"> <span v-if="hasUpdatedFileds">
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-link type="warning" :underline="false" @click="cancelUpdateFields"><span <el-link type="warning" :underline="false" @click="cancelUpdateFields"><span
style="font-size: 12px">取消</span></el-link> style="font-size: 12px">取消</span></el-link>
</span> </span>
</el-row> </el-row>
<el-table @cell-dblclick="(row: any, column: any, cell: any, event: any) => cellClick(row, column, cell)" <db-table ref="dbTableRef" :db-id="state.ti.dbId" :db-type="state.ti.dbType" :db="state.ti.db"
@selection-change="onDataSelectionChange" size="small" :data="execRes.data" :max-height="250" :data="execRes.data" :table="state.table" :column-names="execRes.tableColumn" :loading="loading"
v-loading="loading" element-loading-text="查询中..." height="250" empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改" stripe border class="mt5"> @selection-change="onDataSelectionChange" @change-updated-field="changeUpdatedField"></db-table>
<el-table-column v-if="execRes.tableColumn.length > 0 && table" type="selection" witih="35" />
<el-table-column min-witih="100" :witih="DbInst.flexColumnWidth(item, execRes.data)" align="center"
v-for="item in execRes.tableColumn" :key="item" :prop="item" :label="item" show-overflow-tooltip>
</el-table-column>
</el-table>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, watch, onMounted, computed, reactive, toRefs } from 'vue'; import { nextTick, watch, onMounted, computed, reactive, toRefs, ref, Ref } from 'vue';
import { useStore } from '@/store/index.ts'; import { useStore } from '@/store/index.ts';
import { getSession } from '@/common/utils/storage'; import { getSession } from '@/common/utils/storage';
import { isTrue, notBlank } from '@/common/assert'; import { isTrue, notBlank } from '@/common/assert';
@@ -91,8 +86,8 @@ import { editor } from 'monaco-editor';
// 主题仓库 https://github.com/brijeshb42/monaco-themes // 主题仓库 https://github.com/brijeshb42/monaco-themes
// 主题例子 https://editor.bitwiser.in/ // 主题例子 https://editor.bitwiser.in/
import SolarizedLight from 'monaco-themes/themes/Solarized-light.json'; import SolarizedLight from 'monaco-themes/themes/Solarized-light.json';
import DbTable from '../DbTable.vue'
import { DbInst, UpdateFieldsMeta, FieldsMeta, TabInfo } from '../../db'; import { TabInfo } from '../../db';
import { exportCsv } from '@/common/utils/export'; import { exportCsv } from '@/common/utils/export';
import { dateStrFormat } from '@/common/utils/date'; import { dateStrFormat } from '@/common/utils/date';
import { dbApi } from '../../api'; import { dbApi } from '../../api';
@@ -119,6 +114,7 @@ const props = defineProps({
const store = useStore(); const store = useStore();
const token = getSession('token'); const token = getSession('token');
let monacoEditor = {} as editor.IStandaloneCodeEditor; let monacoEditor = {} as editor.IStandaloneCodeEditor;
const dbTableRef = ref(null) as Ref;
const state = reactive({ const state = reactive({
token, token,
@@ -135,7 +131,7 @@ const state = reactive({
}, },
selectionDatas: [] as any, selectionDatas: [] as any,
editorHeight: '500', editorHeight: '500',
updatedFields: [] as UpdateFieldsMeta[],// 各个tab表被修改的字段信息 hasUpdatedFileds: false,
}); });
const { const {
@@ -145,7 +141,7 @@ const {
table, table,
sqlName, sqlName,
loading, loading,
updatedFields, hasUpdatedFileds,
} = toRefs(state); } = toRefs(state);
watch(() => props.editorHeight, (newValue: any) => { watch(() => props.editorHeight, (newValue: any) => {
@@ -156,11 +152,11 @@ onMounted(async () => {
console.log('in query mounted'); console.log('in query mounted');
state.ti = props.data; state.ti = props.data;
state.editorHeight = props.editorHeight; state.editorHeight = props.editorHeight;
const other = state.ti.other; const params = state.ti.params;
state.dbs = other && other.dbs; state.dbs = params && params.dbs;
if (other && other.sqlName) { if (params && params.sqlName) {
state.sqlName = other.sqlName; state.sqlName = params.sqlName;
const res = await dbApi.getSql.request({ id: state.ti.dbId, type: 1, name: state.sqlName, db: state.ti.db }); const res = await dbApi.getSql.request({ id: state.ti.dbId, type: 1, name: state.sqlName, db: state.ti.db });
state.sql = res.sql; state.sql = res.sql;
} }
@@ -495,6 +491,11 @@ const onDataSelectionChange = (datas: []) => {
state.selectionDatas = datas; state.selectionDatas = datas;
}; };
const changeUpdatedField = (updatedFields: []) => {
// 如果存在要更新字段,则显示提交和取消按钮
state.hasUpdatedFileds = updatedFields && updatedFields.length > 0;
}
/** /**
* 执行删除数据事件 * 执行删除数据事件
*/ */
@@ -513,153 +514,12 @@ const onDeleteData = async () => {
}); });
}; };
// 监听单元格点击事件
const cellClick = (row: any, column: any, cell: any) => {
const property = column.property;
const table = state.table;
// 如果当前操作的表名不存在 或者 当前列的property不存在(如多选框),则不允许修改当前单元格内容
if (!table || !property) {
return;
}
let div: HTMLElement = cell.children[0];
if (div && div.tagName === 'DIV') {
// 转为字符串比较,可能存在数字等
let text = (row[property] || row[property] == 0 ? row[property] : '') + '';
let input = document.createElement('input');
input.setAttribute('value', text);
// 将表格witih也赋值于输入框避免输入框长度超过表格长度
input.setAttribute('style', 'height:23px;text-align:center;border:none;' + div.getAttribute('style'));
cell.replaceChildren(input);
input.focus();
input.addEventListener('blur', async () => {
row[property] = input.value;
cell.replaceChildren(div);
if (input.value !== text) {
let currentUpdatedFields = state.updatedFields
const dbInst = state.ti.getNowDbInst()
const db = state.ti.getNowDb();
// 主键
const primaryKey = await dbInst.loadTableColumn(state.ti.db, table);
const primaryKeyValue = row[primaryKey.columnName];
// 更新字段列信息
const updateColumn = db.getColumn(table, property);
const newField = {
div, row,
fieldName: column.rawColumnKey,
fieldType: updateColumn.columnType,
oldValue: text,
newValue: input.value
} as FieldsMeta;
// 被修改的字段
const primaryKeyFields = currentUpdatedFields.filter((meta) => meta.primaryKey === primaryKeyValue)
let hasKey = false;
if (primaryKeyFields.length <= 0) {
primaryKeyFields[0] = {
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields: [newField]
}
} else {
hasKey = true
let hasField = primaryKeyFields[0].fields.some(a => {
if (a.fieldName === newField.fieldName) {
a.newValue = newField.newValue
}
return a.fieldName === newField.fieldName
})
if (!hasField) {
primaryKeyFields[0].fields.push(newField)
}
}
let fields = primaryKeyFields[0].fields
const fieldsParam = fields.filter((a) => {
if (a.fieldName === column.rawColumnKey) {
a.newValue = input.value
}
return a.fieldName === column.rawColumnKey
})
const field = fieldsParam.length > 0 && fieldsParam[0] || {} as FieldsMeta
if (field.oldValue === input.value) { // 新值=旧值
// 删除数据
div.classList.remove('update_field_active')
let delIndex: number[] = [];
currentUpdatedFields.forEach((a, i) => {
if (a.primaryKey === primaryKeyValue) {
a.fields = a.fields && a.fields.length > 0 ? a.fields.filter(f => f.fieldName !== column.rawColumnKey) : [];
a.fields.length <= 0 && delIndex.push(i)
}
});
delIndex.forEach(i => delete currentUpdatedFields[i])
currentUpdatedFields = currentUpdatedFields.filter(a => a)
} else {
// 新增数据
div.classList.add('update_field_active')
if (hasKey) {
currentUpdatedFields.forEach((value, index, array) => {
if (value.primaryKey === primaryKeyValue) {
array[index].fields = fields
}
})
} else {
currentUpdatedFields.push({
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields
})
}
}
state.updatedFields = currentUpdatedFields;
}
});
}
};
const submitUpdateFields = () => { const submitUpdateFields = () => {
let currentUpdatedFields = state.updatedFields; dbTableRef.value.submitUpdateFields();
if (currentUpdatedFields.length <= 0) {
return;
}
const { db } = state.ti;
const table = state.table;
let res = '';
let divs: HTMLElement[] = [];
currentUpdatedFields.forEach(a => {
let sql = `UPDATE ${table} SET `;
let primaryKey = a.primaryKey;
let primaryKeyType = a.primaryKeyType;
let primaryKeyName = a.primaryKeyName;
a.fields.forEach(f => {
sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},`
divs.push(f.div)
})
sql = sql.substring(0, sql.length - 1)
sql += ` WHERE ${primaryKeyName} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;`
res += sql;
})
state.ti.getNowDbInst().promptExeSql(db, res, () => { }, () => {
currentUpdatedFields = [];
divs.forEach(a => {
a.classList.remove('update_field_active');
})
state.updatedFields = [];
});
} }
const cancelUpdateFields = () => { const cancelUpdateFields = () => {
state.updatedFields.forEach((a: any) => { dbTableRef.value.cancelUpdateFields();
a.fields.forEach((b: any) => {
b.div.classList.remove('update_field_active')
b.row[b.fieldName] = b.oldValue
})
})
state.updatedFields = [];
} }
</script> </script>

View File

@@ -28,13 +28,11 @@
</el-tooltip> </el-tooltip>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-tooltip v-if="state.updatedFields.length > 0" class="box-item" effect="dark" content="提交修改" <el-tooltip v-if="hasUpdatedFileds" class="box-item" effect="dark" content="提交修改" placement="top">
placement="top">
<el-link @click="submitUpdateFields()" type="success" :underline="false" class="f12">提交</el-link> <el-link @click="submitUpdateFields()" type="success" :underline="false" class="f12">提交</el-link>
</el-tooltip> </el-tooltip>
<el-divider v-if="state.updatedFields.length > 0" direction="vertical" border-style="dashed" /> <el-divider v-if="hasUpdatedFileds" direction="vertical" border-style="dashed" />
<el-tooltip v-if="state.updatedFields.length > 0" class="box-item" effect="dark" content="取消修改" <el-tooltip v-if="hasUpdatedFileds" class="box-item" effect="dark" content="取消修改" placement="top">
placement="top">
<el-link @click="cancelUpdateFields" type="warning" :underline="false" class="f12">取消</el-link> <el-link @click="cancelUpdateFields" type="warning" :underline="false" class="f12">取消</el-link>
</el-tooltip> </el-tooltip>
</el-col> </el-col>
@@ -65,22 +63,12 @@
</el-input> </el-input>
</el-col> </el-col>
</el-row> </el-row>
<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" <db-table ref="dbTableRef" :db-id="state.ti.dbId" :db-type="state.ti.dbType" :db="state.ti.db" :data="datas"
:data="datas" size="small" :max-height="tableHeight" v-loading="loading" element-loading-text="查询中..." :table="state.table" :column-names="columnNames" :loading="loading" :height="tableHeight"
empty-text="暂无数据" stripe border class="mt5"> :show-column-tip="true" :sortable="true" @sort-change="(sort: any) => onTableSortChange(sort)"
<el-table-column v-if="datas.length > 0" type="selection" width="35" /> @selection-change="onDataSelectionChange" @change-updated-field="changeUpdatedField"></db-table>
<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="'custom'">
<template #header>
<el-tooltip raw-content placement="top" effect="customized">
<template #content> {{ getColumnTip(item) }} </template>
{{ item }}
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-row type="flex" class="mt5" justify="center"> <el-row type="flex" class="mt5" justify="center">
<el-pagination small :total="count" @current-change="pageChange()" layout="prev, pager, next, total, jumper" <el-pagination small :total="count" @current-change="pageChange()" layout="prev, pager, next, total, jumper"
v-model:current-page="pageNum" :page-size="DbInst.DefaultLimit"></el-pagination> v-model:current-page="pageNum" :page-size="DbInst.DefaultLimit"></el-pagination>
@@ -115,16 +103,16 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, reactive, toRefs } from 'vue'; import { onMounted, watch, reactive, toRefs, ref, Ref } from 'vue';
import { isTrue, notEmpty } from '@/common/assert'; import { isTrue, notEmpty, notBlank } from '@/common/assert';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { DbInst, UpdateFieldsMeta, FieldsMeta, TabInfo } from '../../db'; import { DbInst, TabInfo } from '../../db';
import { exportCsv } from '@/common/utils/export'; import { exportCsv } from '@/common/utils/export';
import { dateStrFormat } from '@/common/utils/date'; import { dateStrFormat } from '@/common/utils/date';
import { notBlank } from '../../../../../common/assert'; import DbTable from '../DbTable.vue'
const emits = defineEmits(['genInsertSql', 'clickSqlName', 'clickSchemaTable', 'changeSchema', 'loadSqlNames']) const emits = defineEmits(['genInsertSql'])
const props = defineProps({ const props = defineProps({
data: { data: {
@@ -137,9 +125,10 @@ const props = defineProps({
} }
}) })
const dbTableRef = ref(null) as Ref;
const state = reactive({ const state = reactive({
ti: {} as TabInfo, ti: {} as TabInfo,
dbId: null, // 当前选中操作的数据库实例
table: '', // 当前的表名 table: '', // 当前的表名
datas: [], datas: [],
sql: '', // 当前数据tab执行的sql sql: '', // 当前数据tab执行的sql
@@ -161,7 +150,7 @@ const state = reactive({
value: null value: null
}, },
tableHeight: '600', tableHeight: '600',
updatedFields: [] as UpdateFieldsMeta[],// 各个tab表被修改的字段信息 hasUpdatedFileds: false,
}); });
const { const {
@@ -172,6 +161,7 @@ const {
columnNames, columnNames,
pageNum, pageNum,
count, count,
hasUpdatedFileds,
conditionDialog, conditionDialog,
} = toRefs(state); } = toRefs(state);
@@ -183,8 +173,8 @@ onMounted(async () => {
console.log('in table data mounted'); console.log('in table data mounted');
state.ti = props.data; state.ti = props.data;
state.tableHeight = props.tableHeight; state.tableHeight = props.tableHeight;
state.table = state.ti.other.table; state.table = state.ti.params.table;
notBlank(state.table, "TableData组件other.table信息不能为空") notBlank(state.table, "TableData组件params.table信息不能为空")
const columns = await state.ti.getNowDbInst().loadColumns(state.ti.db, state.table); const columns = await state.ti.getNowDbInst().loadColumns(state.ti.db, state.table);
state.columns = columns; state.columns = columns;
@@ -239,23 +229,6 @@ const exportData = () => {
}; };
const getColumnTip = (columnName: string) => {
// 优先从 table map中获取
let columns = getColumns();
if (!columns) {
return '';
}
const column = columns.find((c: any) => c.columnName == columnName);
const comment = column.columnComment;
return `${column.columnType} ${comment ? ' | ' + comment : ''}`;
};
const getColumns = () => {
return state.ti.getNowDb().getColumns(state.table);
};
/** /**
* 条件查询,点击列信息后显示输入对应的值 * 条件查询,点击列信息后显示输入对应的值
*/ */
@@ -319,6 +292,11 @@ const onDataSelectionChange = (datas: []) => {
state.selectionDatas = datas; state.selectionDatas = datas;
}; };
const changeUpdatedField = (updatedFields: []) => {
// 如果存在要更新字段,则显示提交和取消按钮
state.hasUpdatedFileds = updatedFields && updatedFields.length > 0;
}
/** /**
* 执行删除数据事件 * 执行删除数据事件
*/ */
@@ -336,151 +314,12 @@ const onGenerateInsertSql = async () => {
emits('genInsertSql', state.ti.getNowDbInst().genInsertSql(state.ti.db, state.table, state.selectionDatas)); emits('genInsertSql', state.ti.getNowDbInst().genInsertSql(state.ti.db, state.table, state.selectionDatas));
}; };
// 监听单元格点击事件
const cellClick = (row: any, column: any, cell: any) => {
const property = column.property;
// 如果当前操作的表名不存在 或者 当前列的property不存在(如多选框),则不允许修改当前单元格内容
if (!property) {
return;
}
let div: HTMLElement = cell.children[0];
if (div && div.tagName === 'DIV') {
// 转为字符串比较,可能存在数字等
let text = (row[property] || row[property] == 0 ? row[property] : '') + '';
let input = document.createElement('input');
input.setAttribute('value', text);
// 将表格width也赋值于输入框避免输入框长度超过表格长度
input.setAttribute('style', 'height:23px;text-align:center;border:none;' + div.getAttribute('style'));
cell.replaceChildren(input);
input.focus();
input.addEventListener('blur', async () => {
row[property] = input.value;
cell.replaceChildren(div);
if (input.value !== text) {
let currentUpdatedFields = state.updatedFields
const db = state.ti.getNowDb();
// 主键
const primaryKey = db.getColumn(state.table);
const primaryKeyValue = row[primaryKey.columnName];
// 更新字段列信息
const updateColumn = db.getColumn(state.table, property);
const newField = {
div, row,
fieldName: column.rawColumnKey,
fieldType: updateColumn.columnType,
oldValue: text,
newValue: input.value
} as FieldsMeta;
// 被修改的字段
const primaryKeyFields = currentUpdatedFields.filter((meta) => meta.primaryKey === primaryKeyValue)
let hasKey = false;
if (primaryKeyFields.length <= 0) {
primaryKeyFields[0] = {
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields: [newField]
}
} else {
hasKey = true
let hasField = primaryKeyFields[0].fields.some(a => {
if (a.fieldName === newField.fieldName) {
a.newValue = newField.newValue
}
return a.fieldName === newField.fieldName
})
if (!hasField) {
primaryKeyFields[0].fields.push(newField)
}
}
let fields = primaryKeyFields[0].fields
const fieldsParam = fields.filter((a) => {
if (a.fieldName === column.rawColumnKey) {
a.newValue = input.value
}
return a.fieldName === column.rawColumnKey
})
const field = fieldsParam.length > 0 && fieldsParam[0] || {} as FieldsMeta
if (field.oldValue === input.value) { // 新值=旧值
// 删除数据
div.classList.remove('update_field_active')
let delIndex: number[] = [];
currentUpdatedFields.forEach((a, i) => {
if (a.primaryKey === primaryKeyValue) {
a.fields = a.fields && a.fields.length > 0 ? a.fields.filter(f => f.fieldName !== column.rawColumnKey) : [];
a.fields.length <= 0 && delIndex.push(i)
}
});
delIndex.forEach(i => delete currentUpdatedFields[i])
currentUpdatedFields = currentUpdatedFields.filter(a => a)
} else {
// 新增数据
div.classList.add('update_field_active')
if (hasKey) {
currentUpdatedFields.forEach((value, index, array) => {
if (value.primaryKey === primaryKeyValue) {
array[index].fields = fields
}
})
} else {
currentUpdatedFields.push({
primaryKey: primaryKeyValue,
primaryKeyName: primaryKey.columnName,
primaryKeyType: primaryKey.columnType,
fields
})
}
}
state.updatedFields = currentUpdatedFields;
}
});
}
};
const submitUpdateFields = () => { const submitUpdateFields = () => {
let currentUpdatedFields = state.updatedFields; dbTableRef.value.submitUpdateFields();
if (currentUpdatedFields.length <= 0) {
return;
}
const { db } = state.ti;
let res = '';
let divs: HTMLElement[] = [];
currentUpdatedFields.forEach(a => {
let sql = `UPDATE ${state.table} SET `;
let primaryKey = a.primaryKey;
let primaryKeyType = a.primaryKeyType;
let primaryKeyName = a.primaryKeyName;
a.fields.forEach(f => {
sql += ` ${f.fieldName} = ${DbInst.wrapColumnValue(f.fieldType, f.newValue)},`
divs.push(f.div)
})
sql = sql.substring(0, sql.length - 1)
sql += ` WHERE ${primaryKeyName} = ${DbInst.wrapColumnValue(primaryKeyType, primaryKey)} ;`
res += sql;
})
state.ti.getNowDbInst().promptExeSql(db, res, () => { }, () => {
currentUpdatedFields = [];
divs.forEach(a => {
a.classList.remove('update_field_active');
})
state.updatedFields = [];
});
} }
const cancelUpdateFields = () => { const cancelUpdateFields = () => {
state.updatedFields.forEach((a: any) => { dbTableRef.value.cancelUpdateFields();
a.fields.forEach((b: any) => {
b.div.classList.remove('update_field_active')
b.row[b.fieldName] = b.oldValue
})
})
state.updatedFields = [];
} }
// 添加新数据行 // 添加新数据行

View File

@@ -405,7 +405,7 @@ export class TabInfo {
/** /**
* tab需要的其他信息 * tab需要的其他信息
*/ */
other: any params: any
getNowDbInst() { getNowDbInst() {
if (!this.dbType) { if (!this.dbType) {