mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into dev_20230213_1
This commit is contained in:
		@@ -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)
 | 
				
			||||||
@@ -291,14 +291,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]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										323
									
								
								mayfly_go_web/src/views/ops/db/component/DbTable.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								mayfly_go_web/src/views/ops/db/component/DbTable.vue
									
									
									
									
									
										Normal 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>
 | 
				
			||||||
@@ -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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 = [];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 添加新数据行
 | 
					// 添加新数据行
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -405,7 +405,7 @@ export class TabInfo {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * tab需要的其他信息
 | 
					     * tab需要的其他信息
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    other: any
 | 
					    params: any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getNowDbInst() {
 | 
					    getNowDbInst() {
 | 
				
			||||||
        if (!this.dbType) {
 | 
					        if (!this.dbType) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user