Files
mayfly-go/frontend/src/views/ops/db/component/table/DbTableData.vue

857 lines
26 KiB
Vue
Raw Normal View History

2023-02-14 11:44:48 +08:00
<template>
<div class="db-table-data mt-1" :style="{ height: tableHeight }">
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2
ref="tableRef"
2025-04-20 21:01:01 +08:00
:header-height="showColumnTip && dbConfig.showColumnComment ? 48 : 30"
:row-height="30"
:row-class="rowClass"
2023-12-25 17:43:42 +08:00
:row-key="null"
:columns="state.columns"
:data="datas"
:width="width"
:height="height"
fixed
class="table"
:row-event-handlers="rowEventHandlers"
@scroll="onTableScroll"
>
<template #header="{ columns }">
<div v-for="(column, i) in columns" :key="i">
<div
:style="{
width: `${column.width}px`,
height: '100%',
textAlign: 'center',
borderRight: 'var(--el-table-border)',
}"
>
<!-- 行号列 -->
<div v-if="column.key == rowNoColumn.key" class="header-column-title">
2023-12-25 17:43:42 +08:00
<b class="el-text" tag="b"> {{ column.title }} </b>
</div>
<!-- 字段名列 -->
<div v-else @contextmenu="headerContextmenuClick($event, column)" style="position: relative">
<!-- 字段列的数据类型 -->
<div class="column-type">
<span v-if="column.dataTypeSubscript === 'icon-clock'">
<SvgIcon :size="9" name="Clock" style="cursor: unset" />
</span>
<span class="!text-[8px]" v-else>{{ column.dataTypeSubscript }}</span>
</div>
<div v-if="showColumnTip">
<div class="header-column-title">
<b :title="column.remark" class="el-text cursor-pointer">
{{ column.title }}
</b>
</div>
<!-- 字段备注信息 -->
2025-04-20 21:01:01 +08:00
<div
v-if="dbConfig.showColumnComment"
style="color: var(--el-color-info-light-3)"
class="!text-[10px] el-text el-text--small is-truncated"
>
{{ column.columnComment }}
2025-04-20 21:01:01 +08:00
</div>
</div>
<div v-else class="header-column-title">
<b class="el-text"> {{ column.title }} </b>
</div>
2024-01-31 12:53:27 +08:00
<!-- 字段列右部分内容 -->
<div class="column-right">
<span v-if="column.title == nowSortColumn?.columnName">
<SvgIcon color="var(--el-color-primary)" :name="nowSortColumn?.order == 'asc' ? 'top' : 'bottom'"></SvgIcon>
</span>
</div>
</div>
</div>
</div>
</template>
<template #cell="{ rowData, column, rowIndex, columnIndex }">
<div @contextmenu="dataContextmenuClick($event, rowIndex, column, rowData)" class="table-data-cell">
<!-- 行号列 -->
<div v-if="column.key == rowNoColumn.key">
2023-12-25 17:43:42 +08:00
<b class="el-text el-text--small">
{{ rowIndex + 1 }}
2023-12-25 17:43:42 +08:00
</b>
</div>
<!-- 数据列 -->
<div v-else @dblclick="onEnterEditMode(rowData, column, rowIndex, columnIndex)">
<div v-if="canEdit(rowIndex, columnIndex)">
<ColumnFormItem
v-model="rowData[column.dataKey!]"
:data-type="column.dataType"
@blur="onExitEditMode(rowData, column, rowIndex)"
:column-name="column.columnName"
focus
/>
</div>
<div v-else :class="isUpdated(rowIndex, column.dataKey) ? 'update_field_active ml-0.5 mr-0.5' : 'ml-0.5 mr-0.5'">
2023-12-25 17:43:42 +08:00
<span v-if="rowData[column.dataKey!] === null" style="color: var(--el-color-info-light-5)"> NULL </span>
2023-11-27 17:40:47 +08:00
2023-12-25 17:43:42 +08:00
<span v-else :title="rowData[column.dataKey!]" class="el-text el-text--small is-truncated">
{{ rowData[column.dataKey!] }}
2023-12-25 17:43:42 +08:00
</span>
</div>
</div>
</div>
</template>
<template v-if="state.loading" #overlay>
2025-04-20 21:01:01 +08:00
<div class="el-loading-mask flex flex-col items-center justify-center">
<div>
<SvgIcon class="is-loading" name="loading" color="var(--el-color-primary)" :size="28" />
<el-text class="ml-1" tag="b">{{ $t('db.execTime') }} - {{ state.execTime.toFixed(1) }}s</el-text>
</div>
<div v-if="loading && abortFn" class="!mt-2">
2024-11-20 22:43:53 +08:00
<el-button @click="cancelLoading" type="info" size="small" plain>{{ $t('common.cancel') }}</el-button>
</div>
</div>
</template>
<template #empty>
2025-04-20 21:01:01 +08:00
<el-empty class="text-center" :description="props.emptyText" :image-size="60" />
</template>
</el-table-v2>
</template>
</el-auto-resizer>
<el-dialog @close="state.genTxtDialog.visible = false" v-model="state.genTxtDialog.visible" :title="state.genTxtDialog.title" width="1000px">
2023-11-29 20:13:29 +08:00
<template #header>
<div class="mr-2" style="display: flex; justify-content: flex-end">
2024-11-20 22:43:53 +08:00
<el-button id="copyValue" @click="copyGenTxt(state.genTxtDialog.txt)" icon="CopyDocument" type="success" size="small">
{{ $t('db.oneClickCopy') }}
</el-button>
2023-11-29 20:13:29 +08:00
</div>
</template>
<el-input v-model="state.genTxtDialog.txt" type="textarea" :rows="20" />
</el-dialog>
2024-01-31 20:41:41 +08:00
<DbTableDataForm
v-if="state.tableDataFormDialog.visible"
:db-inst="getNowDbInst()"
:db-name="db"
:columns="columns!"
:title="state.tableDataFormDialog.title"
:table-name="table"
v-model:visible="state.tableDataFormDialog.visible"
v-model="state.tableDataFormDialog.data"
@submit-success="emits('changeUpdatedField')"
/>
<contextmenu :dropdown="state.contextmenu.dropdown" :items="state.contextmenu.items" ref="contextmenuRef" />
2023-02-14 11:44:48 +08:00
</div>
</template>
<script lang="ts" setup>
2024-11-23 17:23:18 +08:00
import { onBeforeUnmount, onMounted, reactive, ref, toRefs, watch, Ref } from 'vue';
2024-01-31 20:41:41 +08:00
import { ElInput, ElMessage } from 'element-plus';
import { copyToClipboard } from '@/common/utils/string';
import { DbInst, DbThemeConfig } from '@/views/ops/db/db';
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
import SvgIcon from '@/components/svgIcon/index.vue';
import { exportCsv, exportFile } from '@/common/utils/export';
import { formatDate } from '@/common/utils/format';
import { useIntervalFn, useStorage } from '@vueuse/core';
2024-12-08 13:04:23 +08:00
import { ColumnTypeSubscript, DataType, DbDialect, getDbDialect } from '../../dialect/index';
import ColumnFormItem from './ColumnFormItem.vue';
2024-01-31 20:41:41 +08:00
import DbTableDataForm from './DbTableDataForm.vue';
2024-11-20 22:43:53 +08:00
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
2023-02-14 11:44:48 +08:00
const emits = defineEmits(['dataDelete', 'sortChange', 'deleteData', 'selectionChange', 'changeUpdatedField']);
2023-02-14 11:44:48 +08:00
const props = defineProps({
dbId: {
type: Number,
required: true,
},
db: {
type: String,
required: true,
},
table: {
type: String,
default: '',
},
data: {
type: Array,
},
columns: {
type: Array<any>,
2023-02-14 11:44:48 +08:00
},
loading: {
type: Boolean,
default: false,
},
abortFn: {
type: Function,
},
2023-02-14 11:44:48 +08:00
emptyText: {
type: String,
2024-11-20 22:43:53 +08:00
default: 'No Data',
2023-02-14 11:44:48 +08:00
},
showColumnTip: {
type: Boolean,
default: false,
},
height: {
type: String,
default: '600px',
2023-10-31 12:36:04 +08:00
},
});
2023-02-14 11:44:48 +08:00
const contextmenuRef = ref();
const tableRef = ref();
/** 表头 menu items **/
2024-11-20 22:43:53 +08:00
const cmHeaderAsc = new ContextmenuItem('asc', 'db.asc')
.withIcon('top')
.withOnClick((data: any) => {
onTableSortChange({ columnName: data.dataKey, order: 'asc' });
})
.withHideFunc(() => !props.showColumnTip);
2024-11-20 22:43:53 +08:00
const cmHeaderDesc = new ContextmenuItem('desc', 'db.desc')
.withIcon('bottom')
.withOnClick((data: any) => {
onTableSortChange({ columnName: data.dataKey, order: 'desc' });
})
.withHideFunc(() => !props.showColumnTip);
2024-11-20 22:43:53 +08:00
const cmHeaderFixed = new ContextmenuItem('fixed', 'db.fixed')
.withIcon('Paperclip')
.withOnClick((data: any) => {
state.columns.forEach((column: any) => {
if (column.dataKey == data.dataKey) {
column.fixed = true;
}
});
})
.withHideFunc((data: any) => data.fixed);
2024-11-23 17:23:18 +08:00
const cmHeaderCancelFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd')
.withIcon('Minus')
2024-11-23 17:23:18 +08:00
.withOnClick((data: any) => {
state.columns.forEach((column: any) => {
if (column.dataKey == data.dataKey) {
column.fixed = false;
}
});
2024-11-23 17:23:18 +08:00
})
.withHideFunc((data: any) => !data.fixed);
/** 表数据 contextmenu items **/
2024-11-20 22:43:53 +08:00
const cmDataCopyCell = new ContextmenuItem('copyValue', 'common.copy')
.withIcon('CopyDocument')
.withOnClick(async (data: any) => {
await copyToClipboard(data.rowData[data.column.dataKey]);
})
.withHideFunc(() => {
// 选中多条则隐藏该复制按钮
2024-11-23 17:23:18 +08:00
return selectionRowsMap.value.size > 1;
});
2024-11-20 22:43:53 +08:00
const cmDataDel = new ContextmenuItem('deleteData', 'common.delete')
.withIcon('delete')
.withOnClick(() => onDeleteData())
.withHideFunc(() => {
return state.table == '';
});
2024-11-20 22:43:53 +08:00
const cmFormView = new ContextmenuItem('formView', 'db.formView').withIcon('Document').withOnClick(() => onEditRowData());
2024-07-19 17:06:11 +08:00
// .withHideFunc(() => {
// return state.table == '';
// });
2024-01-31 20:41:41 +08:00
const cmDataGenInsertSql = new ContextmenuItem('genInsertSql', 'Insert SQL')
.withIcon('tickets')
.withOnClick(() => onGenerateInsertSql())
.withHideFunc(() => {
return state.table == '';
});
2024-11-20 22:43:53 +08:00
const cmDataGenJson = new ContextmenuItem('genJson', 'db.genJson').withIcon('tickets').withOnClick(() => onGenerateJson());
2024-11-20 22:43:53 +08:00
const cmDataExportCsv = new ContextmenuItem('exportCsv', 'db.exportCsv').withIcon('document').withOnClick(() => onExportCsv());
2024-11-20 22:43:53 +08:00
const cmDataExportSql = new ContextmenuItem('exportSql', 'db.exportSql')
.withIcon('document')
.withOnClick(() => onExportSql())
.withHideFunc(() => {
return state.table == '';
});
class NowUpdateCell {
rowIndex: number;
colIndex: number;
dataType: DataType;
oldValue: any;
}
class UpdatedRow {
/**
* 主键值
*/
primaryValue: any;
/**
* 行数据
*/
rowData: any;
/**
* 修改到的列信息, columnName -> tablecelldata
*/
2024-11-23 17:23:18 +08:00
columnsMap = new Map<string, TableCellData>();
}
class TableCellData {
/**
* 旧值
*/
oldValue: any;
}
let dbDialect: DbDialect = null as any;
2024-11-23 17:23:18 +08:00
let nowSortColumn = ref(null) as any;
// 当前正在更新的单元格
2024-11-23 17:23:18 +08:00
let nowUpdateCell: Ref<NowUpdateCell> = ref(null) as any;
// 选中的数据, key->rowIndex value->primaryKeyValue
2024-11-23 17:23:18 +08:00
const selectionRowsMap = ref(new Map<number, any>());
// 更新单元格 key-> rowIndex value -> 更新行
2024-11-23 17:23:18 +08:00
const cellUpdateMap = ref(new Map<number, UpdatedRow>());
// 数据加载时间计时器
const { pause, resume } = useIntervalFn(() => {
state.execTime += 0.1;
}, 100);
2023-02-14 11:44:48 +08:00
const state = reactive({
dbId: 0, // 当前选中操作的数据库实例
dbType: '',
2023-10-31 12:36:04 +08:00
db: '', // 数据库名
2023-02-14 11:44:48 +08:00
table: '', // 当前的表名
datas: [],
columns: [] as any,
2023-02-14 11:44:48 +08:00
loading: false,
tableHeight: '600px',
execTime: 0,
contextmenu: {
dropdown: {
x: 0,
y: 0,
},
items: [] as ContextmenuItem[],
},
2024-01-31 20:41:41 +08:00
tableDataFormDialog: {
data: {},
title: '',
visible: false,
},
genTxtDialog: {
title: 'SQL',
visible: false,
txt: '',
},
2023-02-14 11:44:48 +08:00
});
2023-10-31 12:36:04 +08:00
const { tableHeight, datas } = toRefs(state);
2023-02-14 11:44:48 +08:00
const dbConfig = useStorage('dbConfig', DbThemeConfig);
/**
* 行号字段列
*/
const rowNoColumn = {
title: 'No.',
key: 'tableDataRowNo',
dataKey: 'tableDataRowNo',
width: 45,
fixed: true,
align: 'center',
headerClass: 'table-column',
class: 'table-column',
};
watch(
() => props.data,
(newValue: any) => {
setTableData(newValue);
}
);
watch(
() => props.columns,
(newValue: any) => {
2023-11-18 21:15:33 +08:00
// 赋值列字段值是否隐藏state.columns多了一列索引列
if (newValue.length + 1 == state.columns.length) {
for (let i = 0; i < newValue.length; i++) {
state.columns[i + 1].hidden = !newValue[i].show;
}
}
},
{
deep: true,
}
);
watch(
() => props.table,
(newValue: any) => {
state.table = newValue;
}
);
watch(
() => props.height,
(newValue: any) => {
state.tableHeight = newValue;
}
);
watch(
() => props.loading,
(newValue: any) => {
state.loading = newValue;
if (newValue) {
startLoading();
} else {
endLoading();
}
}
);
2023-02-14 11:44:48 +08:00
onMounted(async () => {
console.log('in DbTable mounted');
state.tableHeight = props.height;
state.loading = props.loading;
2023-02-14 11:44:48 +08:00
state.dbId = props.dbId;
state.dbType = getNowDbInst().type;
dbDialect = getDbDialect(state.dbType);
2023-02-14 11:44:48 +08:00
state.db = props.db;
state.table = props.table;
2023-11-24 12:12:47 +08:00
setTableData(props.data);
if (state.loading) {
startLoading();
}
});
onBeforeUnmount(() => {
endLoading();
});
const setTableData = (datas: any) => {
2024-01-31 20:41:41 +08:00
tableRef.value?.scrollTo({ scrollLeft: 0, scrollTop: 0 });
2024-11-23 17:23:18 +08:00
selectionRowsMap.value.clear();
cellUpdateMap.value.clear();
2024-12-08 13:04:23 +08:00
// formatDataValues(datas);
state.datas = datas;
setTableColumns(props.columns);
};
const setTableColumns = (columns: any) => {
state.columns = columns.map((x: any) => {
const columnName = x.columnName;
// 数据类型
2024-05-10 19:59:49 +08:00
x.dataType = dbDialect.getDataType(x.columnType);
x.dataTypeSubscript = ColumnTypeSubscript[x.dataType];
2024-05-10 19:59:49 +08:00
x.remark = `${x.columnType} ${x.columnComment ? ' | ' + x.columnComment : ''}`;
return {
...x,
key: columnName,
dataKey: columnName,
width: DbInst.flexColumnWidth(columnName, state.datas),
title: columnName,
align: x.dataType == DataType.Number ? 'right' : 'left',
headerClass: 'table-column',
class: 'table-column',
sortable: true,
hidden: !x.show,
};
});
2023-11-24 12:12:47 +08:00
if (state.columns.length > 0) {
state.columns.unshift(rowNoColumn);
}
};
const startLoading = () => {
state.execTime = 0;
resume();
};
const endLoading = () => {
pause();
};
const cancelLoading = async () => {
props.abortFn && props.abortFn();
endLoading();
};
/**
* 当前单元格是否允许编辑
* @param rowIndex ri
* @param colIndex ci
*/
const canEdit = (rowIndex: number, colIndex: number) => {
2024-11-23 17:23:18 +08:00
return state.table && nowUpdateCell.value?.rowIndex == rowIndex && nowUpdateCell.value?.colIndex == colIndex;
};
/**
* 判断当前单元格是否被更新了
* @param rowIndex ri
* @param columnName cn
*/
const isUpdated = (rowIndex: number, columnName: string) => {
2024-11-23 17:23:18 +08:00
return cellUpdateMap.value.get(rowIndex)?.columnsMap.get(columnName);
};
/**
* 判断当前行是否被选中
* @param rowIndex
*/
const isSelection = (rowIndex: number): boolean => {
2024-11-23 17:23:18 +08:00
return selectionRowsMap.value.get(rowIndex);
};
/**
* 选中指定行
* @param rowIndex
* @param rowData
* @param isMultiple 是否允许多选
*/
const selectionRow = (rowIndex: number, rowData: any, isMultiple = false) => {
if (isMultiple) {
// 如果重复点击,则取消改选中数据
2024-11-23 17:23:18 +08:00
if (selectionRowsMap.value.get(rowIndex)) {
selectionRowsMap.value.delete(rowIndex);
return;
}
} else {
2024-11-23 17:23:18 +08:00
selectionRowsMap.value.clear();
}
2024-11-23 17:23:18 +08:00
selectionRowsMap.value.set(rowIndex, rowData);
};
/**
* 行事件处理
*/
const rowEventHandlers = {
onClick: (e: any) => {
const event = e.event;
const rowIndex = e.rowIndex;
const rowData = e.rowData;
// 按住ctrl点击则新建标签页打开, metaKey对应mac command键
if (event.ctrlKey || event.metaKey) {
selectionRow(rowIndex, rowData, true);
return;
}
selectionRow(rowIndex, rowData);
},
};
const headerContextmenuClick = (event: any, data: any) => {
event.preventDefault(); // 阻止默认的右击菜单行为
const { clientX, clientY } = event;
state.contextmenu.dropdown.x = clientX;
state.contextmenu.dropdown.y = clientY;
2024-11-23 17:23:18 +08:00
state.contextmenu.items = [cmHeaderAsc, cmHeaderDesc, cmHeaderFixed, cmHeaderCancelFixed];
contextmenuRef.value.openContextmenu(data);
};
const dataContextmenuClick = (event: any, rowIndex: number, column: any, data: any) => {
event.preventDefault(); // 阻止默认的右击菜单行为
// 当前行未选中,则单行选中该行
if (!isSelection(rowIndex)) {
selectionRow(rowIndex, data);
}
const { clientX, clientY } = event;
state.contextmenu.dropdown.x = clientX;
state.contextmenu.dropdown.y = clientY;
2024-07-19 17:06:11 +08:00
state.contextmenu.items = [cmDataCopyCell, cmDataDel, cmFormView, cmDataGenInsertSql, cmDataGenJson, cmDataExportCsv, cmDataExportSql];
contextmenuRef.value.openContextmenu({ column, rowData: data });
};
/**
* 表排序字段变更
*/
const onTableSortChange = async (sort: any) => {
2024-11-23 17:23:18 +08:00
nowSortColumn.value = sort;
cancelUpdateFields();
emits('sortChange', sort);
};
/**
* 执行删除数据事件
*/
const onDeleteData = async () => {
2024-11-23 17:23:18 +08:00
const deleteDatas = Array.from(selectionRowsMap.value.values());
const db = state.db;
const dbInst = getNowDbInst();
dbInst.promptExeSql(db, await dbInst.genDeleteByPrimaryKeysSql(db, state.table, deleteDatas as any), null, () => {
emits('dataDelete', deleteDatas);
});
};
2024-01-31 20:41:41 +08:00
const onEditRowData = () => {
2024-11-23 17:23:18 +08:00
const selectionDatas = Array.from(selectionRowsMap.value.values());
2024-01-31 20:41:41 +08:00
if (selectionDatas.length > 1) {
2024-11-20 22:43:53 +08:00
ElMessage.warning(t('db.onlySelectOneData'));
2024-01-31 20:41:41 +08:00
return;
}
const data = selectionDatas[0];
state.tableDataFormDialog.data = { ...data };
2024-11-20 22:43:53 +08:00
state.tableDataFormDialog.title = state.table ? `'${props.table}' ${t('db.formView')}` : t('db.formView');
2024-01-31 20:41:41 +08:00
state.tableDataFormDialog.visible = true;
};
const onGenerateInsertSql = async () => {
2024-11-23 17:23:18 +08:00
const selectionDatas = Array.from(selectionRowsMap.value.values());
state.genTxtDialog.txt = await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas);
state.genTxtDialog.title = 'SQL';
state.genTxtDialog.visible = true;
};
const onGenerateJson = async () => {
2024-11-23 17:23:18 +08:00
const selectionDatas = Array.from(selectionRowsMap.value.values());
// 按列字段重新排序对象key
const jsonObj = [];
for (let selectionData of selectionDatas) {
2024-07-19 17:06:11 +08:00
let obj: any = {};
for (let column of state.columns) {
2023-11-18 21:15:33 +08:00
if (column.show) {
obj[column.title] = selectionData[column.dataKey];
}
}
jsonObj.push(obj);
}
state.genTxtDialog.txt = JSON.stringify(jsonObj, null, 4);
state.genTxtDialog.title = 'JSON';
state.genTxtDialog.visible = true;
};
2023-11-29 20:13:29 +08:00
const copyGenTxt = async (txt: string) => {
await copyToClipboard(txt);
state.genTxtDialog.visible = false;
};
/**
* 导出当前页数据
*/
const onExportCsv = () => {
const dataList = state.datas as any;
let columnNames = [];
for (let column of state.columns) {
if (column.show) {
columnNames.push(column.columnName);
}
}
2024-11-20 22:43:53 +08:00
exportCsv(`Data-${state.table}-${formatDate(new Date(), 'YYYYMMDDHHmm')}`, columnNames, dataList);
};
const onExportSql = async () => {
const selectionDatas = state.datas;
2024-11-20 22:43:53 +08:00
exportFile(`Data-${state.table}-${formatDate(new Date(), 'YYYYMMDDHHmm')}.sql`, await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas));
};
const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex = 0) => {
2024-12-08 13:04:23 +08:00
// 不存在表,或者已经在编辑中,则不处理
if (!state.table || nowUpdateCell.value) {
return;
}
2024-11-23 17:23:18 +08:00
nowUpdateCell.value = {
rowIndex: rowIndex,
colIndex: columnIndex,
oldValue: rowData[column.dataKey],
dataType: column.dataType,
};
};
const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
2024-12-08 13:04:23 +08:00
if (!nowUpdateCell.value) {
2023-12-22 17:04:06 +08:00
return;
}
2024-11-23 17:23:18 +08:00
const oldValue = nowUpdateCell.value.oldValue;
const newValue = rowData[column.dataKey];
// 未改变单元格值
if (oldValue == newValue) {
2024-11-23 17:23:18 +08:00
nowUpdateCell.value = null as any;
return;
}
2024-11-23 17:23:18 +08:00
let updatedRow = cellUpdateMap.value.get(rowIndex);
if (!updatedRow) {
updatedRow = new UpdatedRow();
updatedRow.rowData = rowData;
2024-11-23 17:23:18 +08:00
cellUpdateMap.value.set(rowIndex, updatedRow);
}
const columnName = column.dataKey;
let cellData = updatedRow.columnsMap.get(columnName);
if (cellData) {
// 多次修改情况,可能又修改回原值,则移除该修改单元格
if (cellData.oldValue == newValue) {
2024-11-23 17:23:18 +08:00
cellUpdateMap.value.delete(rowIndex);
}
} else {
cellData = new TableCellData();
cellData.oldValue = oldValue;
updatedRow.columnsMap.set(columnName, cellData);
}
2024-11-23 17:23:18 +08:00
nowUpdateCell.value = null as any;
changeUpdatedField();
};
const submitUpdateFields = async () => {
const dbInst = getNowDbInst();
2024-11-23 17:23:18 +08:00
if (cellUpdateMap.value.size == 0) {
2023-02-14 11:44:48 +08:00
return;
}
2023-02-14 11:44:48 +08:00
const db = state.db;
let res = '';
2024-01-31 20:41:41 +08:00
2024-11-23 17:23:18 +08:00
for (let updateRow of cellUpdateMap.value.values()) {
2024-01-31 20:41:41 +08:00
const rowData = { ...updateRow.rowData };
2024-07-19 17:06:11 +08:00
let updateColumnValue: any = {};
for (let k of updateRow.columnsMap.keys()) {
const v = updateRow.columnsMap.get(k);
if (!v) {
continue;
}
2024-01-31 20:41:41 +08:00
updateColumnValue[k] = rowData[k];
// 将更新的字段对应的原始数据还原(主要应对可能更新修改了主键等)
rowData[k] = v.oldValue;
}
2024-01-31 20:41:41 +08:00
res += await dbInst.genUpdateSql(db, state.table, updateColumnValue, rowData);
}
2023-10-31 12:36:04 +08:00
dbInst.promptExeSql(db, res, null, () => {
2024-11-23 17:23:18 +08:00
cellUpdateMap.value.clear();
changeUpdatedField();
});
2023-10-31 12:36:04 +08:00
};
2023-02-14 11:44:48 +08:00
const cancelUpdateFields = () => {
2024-11-23 17:23:18 +08:00
const updateRows = cellUpdateMap.value.values();
// 恢复原值
for (let updateRow of updateRows) {
const rowData = updateRow.rowData;
updateRow.columnsMap.forEach((v: TableCellData, k: string) => {
rowData[k] = v.oldValue;
2023-10-31 12:36:04 +08:00
});
}
2024-11-23 17:23:18 +08:00
cellUpdateMap.value.clear();
2023-02-14 11:44:48 +08:00
changeUpdatedField();
2023-10-31 12:36:04 +08:00
};
2023-02-14 11:44:48 +08:00
const changeUpdatedField = () => {
2024-11-23 17:23:18 +08:00
emits('changeUpdatedField', cellUpdateMap.value);
2023-10-31 12:36:04 +08:00
};
2023-02-14 11:44:48 +08:00
const rowClass = (row: any) => {
if (isSelection(row.rowIndex)) {
return 'data-selection';
}
return '';
};
const scrollLeftValue = ref(0);
const onTableScroll = (param: any) => {
scrollLeftValue.value = param.scrollLeft;
};
/**
* 激活表格恢复滚动位置否则会造成表头与数据单元格错位(暂不知为啥先这样解决)
*/
const active = () => {
setTimeout(() => tableRef.value.scrollToLeft(scrollLeftValue.value));
};
2023-02-14 11:44:48 +08:00
const getNowDbInst = () => {
2023-02-15 21:28:01 +08:00
return DbInst.getInst(state.dbId);
2023-10-31 12:36:04 +08:00
};
2023-02-14 11:44:48 +08:00
defineExpose({
active,
2023-02-14 11:44:48 +08:00
submitUpdateFields,
2023-10-31 12:36:04 +08:00
cancelUpdateFields,
});
2023-02-14 11:44:48 +08:00
</script>
<style lang="scss">
.db-table-data {
.table {
border-left: var(--el-table-border);
border-top: var(--el-table-border);
}
.table-column {
padding: 0 2px;
font-size: 12px;
border-right: var(--el-table-border);
}
.header-column-title {
height: 30px;
display: flex;
justify-content: center;
}
.table-data-cell {
width: 100%;
height: 100%;
line-height: 30px;
cursor: pointer;
}
.data-selection {
background-color: var(--el-table-current-row-bg-color);
}
.update_field_active {
2023-12-17 01:43:38 +08:00
background-color: var(--el-color-success-light-3);
}
.column-type {
color: var(--el-color-info-light-3);
font-weight: bold;
position: absolute;
top: -7px;
2025-02-20 17:07:13 +08:00
padding: 2px;
2024-01-31 12:53:27 +08:00
}
.column-right {
position: absolute;
top: 2px;
right: 0;
padding: 2px;
}
}
2023-02-14 11:44:48 +08:00
</style>