2023-02-14 11:44:48 +08:00
|
|
|
|
<template>
|
2023-12-07 23:57:11 +08:00
|
|
|
|
<div class="db-table-data mt5" :style="{ height: tableHeight }">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
<el-auto-resizer>
|
|
|
|
|
|
<template #default="{ height, width }">
|
|
|
|
|
|
<el-table-v2
|
|
|
|
|
|
ref="tableRef"
|
2024-01-09 12:19:20 +08:00
|
|
|
|
:header-height="showColumnTip && dbConfig.showColumnComment ? 45 : 30"
|
2023-11-20 12:21:27 +08:00
|
|
|
|
:row-height="30"
|
2023-11-17 13:31:28 +08:00
|
|
|
|
:row-class="rowClass"
|
2023-12-25 17:43:42 +08:00
|
|
|
|
:row-key="null"
|
2023-11-17 13:31:28 +08:00
|
|
|
|
:columns="state.columns"
|
|
|
|
|
|
:data="datas"
|
|
|
|
|
|
:width="width"
|
|
|
|
|
|
:height="height"
|
|
|
|
|
|
fixed
|
2023-11-18 15:22:25 +08:00
|
|
|
|
class="table"
|
2023-11-17 13:31:28 +08:00
|
|
|
|
:row-event-handlers="rowEventHandlers"
|
2024-05-31 12:12:40 +08:00
|
|
|
|
@scroll="onTableScroll"
|
2023-07-06 20:59:22 +08:00
|
|
|
|
>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
<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)',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
2023-12-14 21:27:11 +08:00
|
|
|
|
<!-- 行号列 -->
|
2024-01-09 12:19:20 +08:00
|
|
|
|
<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>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2023-12-14 21:27:11 +08:00
|
|
|
|
<!-- 字段名列 -->
|
2023-12-22 00:47:01 +08:00
|
|
|
|
<div v-else @contextmenu="headerContextmenuClick($event, column)" style="position: relative">
|
2023-12-19 19:23:33 +08:00
|
|
|
|
<!-- 字段列的数据类型 -->
|
2023-12-14 21:27:11 +08:00
|
|
|
|
<div class="column-type">
|
2023-12-22 00:47:01 +08:00
|
|
|
|
<span v-if="column.dataTypeSubscript === 'icon-clock'">
|
2024-05-08 21:04:25 +08:00
|
|
|
|
<SvgIcon :size="9" name="Clock" style="cursor: unset" />
|
2023-12-15 11:29:57 +08:00
|
|
|
|
</span>
|
2023-12-22 00:47:01 +08:00
|
|
|
|
<span class="font8" v-else>{{ column.dataTypeSubscript }}</span>
|
2023-12-14 21:27:11 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2023-12-22 00:47:01 +08:00
|
|
|
|
<div v-if="showColumnTip">
|
2024-01-09 12:19:20 +08:00
|
|
|
|
<div class="header-column-title">
|
|
|
|
|
|
<b :title="column.remark" class="el-text" style="cursor: pointer">
|
|
|
|
|
|
{{ column.title }}
|
|
|
|
|
|
</b>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 字段备注信息 -->
|
|
|
|
|
|
<span
|
|
|
|
|
|
v-if="dbConfig.showColumnComment"
|
|
|
|
|
|
style="color: var(--el-color-info-light-3)"
|
|
|
|
|
|
class="font10 el-text el-text--small is-truncated"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ column.columnComment }}
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2024-01-09 12:19:20 +08:00
|
|
|
|
<div v-else class="header-column-title">
|
2024-05-31 12:12:40 +08:00
|
|
|
|
<b class="el-text"> {{ column.title }} </b>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</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>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template #cell="{ rowData, column, rowIndex, columnIndex }">
|
2023-11-20 12:21:27 +08:00
|
|
|
|
<div @contextmenu="dataContextmenuClick($event, rowIndex, column, rowData)" class="table-data-cell">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
<!-- 行号列 -->
|
2023-12-14 21:27:11 +08:00
|
|
|
|
<div v-if="column.key == rowNoColumn.key">
|
2023-12-25 17:43:42 +08:00
|
|
|
|
<b class="el-text el-text--small">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
{{ rowIndex + 1 }}
|
2023-12-25 17:43:42 +08:00
|
|
|
|
</b>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 数据列 -->
|
2023-11-20 12:21:27 +08:00
|
|
|
|
<div v-else @dblclick="onEnterEditMode(rowData, column, rowIndex, columnIndex)">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
<div v-if="canEdit(rowIndex, columnIndex)">
|
2023-12-21 13:07:02 +08:00
|
|
|
|
<ColumnFormItem
|
2023-12-15 11:29:57 +08:00
|
|
|
|
v-model="rowData[column.dataKey!]"
|
2023-12-22 12:29:46 +08:00
|
|
|
|
:data-type="column.dataType"
|
2023-12-15 11:29:57 +08:00
|
|
|
|
@blur="onExitEditMode(rowData, column, rowIndex)"
|
2023-12-22 12:29:46 +08:00
|
|
|
|
:column-name="column.columnName"
|
2023-12-21 13:07:02 +08:00
|
|
|
|
focus
|
2023-12-15 11:29:57 +08:00
|
|
|
|
/>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2024-05-31 12:12:40 +08:00
|
|
|
|
<div v-else :class="isUpdated(rowIndex, column.dataKey) ? 'update_field_active ml2 mr2' : 'ml2 mr2'">
|
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">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
{{ rowData[column.dataKey!] }}
|
2023-12-25 17:43:42 +08:00
|
|
|
|
</span>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2023-12-07 01:07:34 +08:00
|
|
|
|
<template v-if="state.loading" #overlay>
|
|
|
|
|
|
<div class="el-loading-mask" style="display: flex; flex-direction: column; align-items: center; justify-content: center">
|
|
|
|
|
|
<div>
|
2023-12-07 23:57:11 +08:00
|
|
|
|
<SvgIcon class="is-loading" name="loading" color="var(--el-color-primary)" :size="28" />
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-text class="ml5" tag="b">{{ $t('db.execTime') }} - {{ state.execTime.toFixed(1) }}s</el-text>
|
2023-12-07 01:07:34 +08:00
|
|
|
|
</div>
|
2023-12-11 01:00:09 +08:00
|
|
|
|
<div v-if="loading && abortFn" class="mt10">
|
2024-11-20 22:43:53 +08:00
|
|
|
|
<el-button @click="cancelLoading" type="info" size="small" plain>{{ $t('common.cancel') }}</el-button>
|
2023-12-07 01:07:34 +08:00
|
|
|
|
</div>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template #empty>
|
|
|
|
|
|
<div style="text-align: center">
|
2024-05-23 17:18:22 +08:00
|
|
|
|
<el-empty :description="props.emptyText" :image-size="100" />
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</div>
|
2023-06-29 11:49:14 +08:00
|
|
|
|
</template>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</el-table-v2>
|
2023-06-29 11:49:14 +08:00
|
|
|
|
</template>
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</el-auto-resizer>
|
|
|
|
|
|
|
2023-11-20 12:21:27 +08:00
|
|
|
|
<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="mr15" 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>
|
2024-10-16 17:24:50 +08:00
|
|
|
|
<el-input v-model="state.genTxtDialog.txt" type="textarea" :rows="20" />
|
2023-11-17 13:31:28 +08:00
|
|
|
|
</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')"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
<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';
|
2023-11-20 12:21:27 +08:00
|
|
|
|
import { copyToClipboard } from '@/common/utils/string';
|
2024-06-24 17:17:57 +08:00
|
|
|
|
import { DbInst, DbThemeConfig } from '@/views/ops/db/db';
|
2023-12-16 23:10:38 +08:00
|
|
|
|
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
|
2023-11-17 13:31:28 +08:00
|
|
|
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
2023-11-18 15:22:25 +08:00
|
|
|
|
import { exportCsv, exportFile } from '@/common/utils/export';
|
2024-05-13 19:55:43 +08:00
|
|
|
|
import { formatDate } from '@/common/utils/format';
|
2024-01-09 12:19:20 +08:00
|
|
|
|
import { useIntervalFn, useStorage } from '@vueuse/core';
|
2024-12-08 13:04:23 +08:00
|
|
|
|
import { ColumnTypeSubscript, DataType, DbDialect, getDbDialect } from '../../dialect/index';
|
2023-12-21 13:07:02 +08:00
|
|
|
|
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
|
|
|
|
|
2023-11-17 13:31:28 +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,
|
|
|
|
|
|
},
|
2023-06-29 11:49:14 +08:00
|
|
|
|
columns: {
|
|
|
|
|
|
type: Array<any>,
|
2023-02-14 11:44:48 +08:00
|
|
|
|
},
|
|
|
|
|
|
loading: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false,
|
|
|
|
|
|
},
|
2023-12-11 01:00:09 +08:00
|
|
|
|
abortFn: {
|
|
|
|
|
|
type: Function,
|
2023-12-07 23:57:11 +08:00
|
|
|
|
},
|
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: {
|
2023-12-07 23:57:11 +08:00
|
|
|
|
type: String,
|
|
|
|
|
|
default: '600px',
|
2023-10-31 12:36:04 +08:00
|
|
|
|
},
|
|
|
|
|
|
});
|
2023-02-14 11:44:48 +08:00
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
const contextmenuRef = ref();
|
2023-11-17 13:31:28 +08:00
|
|
|
|
const tableRef = ref();
|
|
|
|
|
|
|
2023-12-19 19:23:33 +08:00
|
|
|
|
/** 表头 menu items **/
|
2023-11-18 15:22:25 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmHeaderAsc = new ContextmenuItem('asc', 'db.asc')
|
2023-12-19 19:23:33 +08:00
|
|
|
|
.withIcon('top')
|
|
|
|
|
|
.withOnClick((data: any) => {
|
|
|
|
|
|
onTableSortChange({ columnName: data.dataKey, order: 'asc' });
|
|
|
|
|
|
})
|
|
|
|
|
|
.withHideFunc(() => !props.showColumnTip);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmHeaderDesc = new ContextmenuItem('desc', 'db.desc')
|
2023-12-19 19:23:33 +08:00
|
|
|
|
.withIcon('bottom')
|
|
|
|
|
|
.withOnClick((data: any) => {
|
|
|
|
|
|
onTableSortChange({ columnName: data.dataKey, order: 'desc' });
|
|
|
|
|
|
})
|
|
|
|
|
|
.withHideFunc(() => !props.showColumnTip);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmHeaderFixed = new ContextmenuItem('fixed', 'db.fixed')
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.withIcon('Paperclip')
|
|
|
|
|
|
.withOnClick((data: any) => {
|
|
|
|
|
|
data.fixed = true;
|
|
|
|
|
|
})
|
|
|
|
|
|
.withHideFunc((data: any) => data.fixed);
|
|
|
|
|
|
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const cmHeaderCancelFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd')
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.withIcon('Minus')
|
2024-11-23 17:23:18 +08:00
|
|
|
|
.withOnClick((data: any) => {
|
|
|
|
|
|
data.fixed = false;
|
|
|
|
|
|
})
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.withHideFunc((data: any) => !data.fixed);
|
|
|
|
|
|
|
|
|
|
|
|
/** 表数据 contextmenu items **/
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmDataCopyCell = new ContextmenuItem('copyValue', 'common.copy')
|
2023-11-20 12:21:27 +08:00
|
|
|
|
.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;
|
2023-11-20 12:21:27 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmDataDel = new ContextmenuItem('deleteData', 'common.delete')
|
2023-11-17 13:31:28 +08:00
|
|
|
|
.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
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
const cmDataGenInsertSql = new ContextmenuItem('genInsertSql', 'Insert SQL')
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.withIcon('tickets')
|
2023-11-17 13:31:28 +08:00
|
|
|
|
.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());
|
2023-11-18 15:22:25 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmDataExportCsv = new ContextmenuItem('exportCsv', 'db.exportCsv').withIcon('document').withOnClick(() => onExportCsv());
|
2023-11-18 15:22:25 +08:00
|
|
|
|
|
2024-11-20 22:43:53 +08:00
|
|
|
|
const cmDataExportSql = new ContextmenuItem('exportSql', 'db.exportSql')
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.withIcon('document')
|
|
|
|
|
|
.withOnClick(() => onExportSql())
|
|
|
|
|
|
.withHideFunc(() => {
|
|
|
|
|
|
return state.table == '';
|
|
|
|
|
|
});
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
|
|
|
|
|
class NowUpdateCell {
|
|
|
|
|
|
rowIndex: number;
|
|
|
|
|
|
colIndex: number;
|
2023-12-15 11:29:57 +08:00
|
|
|
|
dataType: DataType;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
oldValue: any;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class UpdatedRow {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 主键值
|
|
|
|
|
|
*/
|
|
|
|
|
|
primaryValue: any;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 行数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
rowData: any;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 修改到的列信息, columnName -> tablecelldata
|
|
|
|
|
|
*/
|
2024-11-23 17:23:18 +08:00
|
|
|
|
columnsMap = new Map<string, TableCellData>();
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class TableCellData {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 旧值
|
|
|
|
|
|
*/
|
|
|
|
|
|
oldValue: any;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-14 21:27:11 +08:00
|
|
|
|
let dbDialect: DbDialect = null as any;
|
|
|
|
|
|
|
2024-11-23 17:23:18 +08:00
|
|
|
|
let nowSortColumn = ref(null) as any;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 当前正在更新的单元格
|
2024-11-23 17:23:18 +08:00
|
|
|
|
let nowUpdateCell: Ref<NowUpdateCell> = ref(null) as any;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 选中的数据, key->rowIndex value->primaryKeyValue
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const selectionRowsMap = ref(new Map<number, any>());
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新单元格 key-> rowIndex value -> 更新行
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const cellUpdateMap = ref(new Map<number, UpdatedRow>());
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
2023-12-07 23:57:11 +08:00
|
|
|
|
// 数据加载时间计时器
|
2023-12-09 16:17:26 +08:00
|
|
|
|
const { pause, resume } = useIntervalFn(() => {
|
|
|
|
|
|
state.execTime += 0.1;
|
|
|
|
|
|
}, 100);
|
2023-12-07 23:57:11 +08:00
|
|
|
|
|
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: [],
|
2023-11-17 13:31:28 +08:00
|
|
|
|
columns: [] as any,
|
2023-02-14 11:44:48 +08:00
|
|
|
|
loading: false,
|
2023-12-07 23:57:11 +08:00
|
|
|
|
tableHeight: '600px',
|
|
|
|
|
|
execTime: 0,
|
2023-11-17 13:31:28 +08:00
|
|
|
|
contextmenu: {
|
|
|
|
|
|
dropdown: {
|
|
|
|
|
|
x: 0,
|
|
|
|
|
|
y: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
items: [] as ContextmenuItem[],
|
|
|
|
|
|
},
|
2024-01-31 20:41:41 +08:00
|
|
|
|
tableDataFormDialog: {
|
|
|
|
|
|
data: {},
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
},
|
2023-11-20 12:21:27 +08:00
|
|
|
|
genTxtDialog: {
|
2023-11-17 13:31:28 +08:00
|
|
|
|
title: 'SQL',
|
|
|
|
|
|
visible: false,
|
2023-11-20 12:21:27 +08:00
|
|
|
|
txt: '',
|
2023-11-17 13:31:28 +08:00
|
|
|
|
},
|
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
|
|
|
|
|
2024-06-24 17:17:57 +08:00
|
|
|
|
const dbConfig = useStorage('dbConfig', DbThemeConfig);
|
2024-01-09 12:19:20 +08:00
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 行号字段列
|
|
|
|
|
|
*/
|
|
|
|
|
|
const rowNoColumn = {
|
|
|
|
|
|
title: 'No.',
|
|
|
|
|
|
key: 'tableDataRowNo',
|
|
|
|
|
|
dataKey: 'tableDataRowNo',
|
|
|
|
|
|
width: 45,
|
|
|
|
|
|
fixed: true,
|
|
|
|
|
|
align: 'center',
|
2023-11-20 12:21:27 +08:00
|
|
|
|
headerClass: 'table-column',
|
|
|
|
|
|
class: 'table-column',
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-11-17 13:31:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
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;
|
2023-12-07 23:57:11 +08:00
|
|
|
|
if (newValue) {
|
|
|
|
|
|
startLoading();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
endLoading();
|
|
|
|
|
|
}
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
);
|
2023-02-14 11:44:48 +08:00
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
console.log('in DbTable mounted');
|
2023-11-17 13:31:28 +08:00
|
|
|
|
state.tableHeight = props.height;
|
|
|
|
|
|
state.loading = props.loading;
|
2023-02-14 11:44:48 +08:00
|
|
|
|
|
|
|
|
|
|
state.dbId = props.dbId;
|
2023-12-15 11:29:57 +08:00
|
|
|
|
state.dbType = getNowDbInst().type;
|
2023-12-14 21:27:11 +08:00
|
|
|
|
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);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
2023-12-07 23:57:11 +08:00
|
|
|
|
if (state.loading) {
|
|
|
|
|
|
startLoading();
|
2023-12-07 01:07:34 +08:00
|
|
|
|
}
|
2023-12-07 23:57:11 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
endLoading();
|
|
|
|
|
|
});
|
2023-12-07 01:07:34 +08:00
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
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);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
state.datas = datas;
|
|
|
|
|
|
setTableColumns(props.columns);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const setTableColumns = (columns: any) => {
|
|
|
|
|
|
state.columns = columns.map((x: any) => {
|
|
|
|
|
|
const columnName = x.columnName;
|
2023-12-22 00:47:01 +08:00
|
|
|
|
// 数据类型
|
2024-05-10 19:59:49 +08:00
|
|
|
|
x.dataType = dbDialect.getDataType(x.columnType);
|
2023-12-22 00:47:01 +08:00
|
|
|
|
x.dataTypeSubscript = ColumnTypeSubscript[x.dataType];
|
2024-05-10 19:59:49 +08:00
|
|
|
|
x.remark = `${x.columnType} ${x.columnComment ? ' | ' + x.columnComment : ''}`;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
return {
|
|
|
|
|
|
...x,
|
|
|
|
|
|
key: columnName,
|
|
|
|
|
|
dataKey: columnName,
|
|
|
|
|
|
width: DbInst.flexColumnWidth(columnName, state.datas),
|
|
|
|
|
|
title: columnName,
|
2024-05-31 12:12:40 +08:00
|
|
|
|
align: x.dataType == DataType.Number ? 'right' : 'left',
|
2023-11-20 12:21:27 +08:00
|
|
|
|
headerClass: 'table-column',
|
|
|
|
|
|
class: 'table-column',
|
2023-11-17 13:31:28 +08:00
|
|
|
|
sortable: true,
|
|
|
|
|
|
hidden: !x.show,
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
2023-11-24 12:12:47 +08:00
|
|
|
|
if (state.columns.length > 0) {
|
|
|
|
|
|
state.columns.unshift(rowNoColumn);
|
|
|
|
|
|
}
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-12-07 23:57:11 +08:00
|
|
|
|
const startLoading = () => {
|
2023-12-09 16:17:26 +08:00
|
|
|
|
state.execTime = 0;
|
|
|
|
|
|
resume();
|
2023-12-07 23:57:11 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const endLoading = () => {
|
2023-12-09 16:17:26 +08:00
|
|
|
|
pause();
|
2023-12-07 23:57:11 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const cancelLoading = async () => {
|
2023-12-11 01:00:09 +08:00
|
|
|
|
props.abortFn && props.abortFn();
|
|
|
|
|
|
endLoading();
|
2023-12-07 23:57:11 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 当前单元格是否允许编辑
|
|
|
|
|
|
* @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;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前单元格是否被更新了
|
|
|
|
|
|
* @param rowIndex ri
|
|
|
|
|
|
* @param columnName cn
|
|
|
|
|
|
*/
|
2023-11-17 13:31:28 +08:00
|
|
|
|
const isUpdated = (rowIndex: number, columnName: string) => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
return cellUpdateMap.value.get(rowIndex)?.columnsMap.get(columnName);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前行是否被选中
|
|
|
|
|
|
* @param rowIndex
|
|
|
|
|
|
*/
|
|
|
|
|
|
const isSelection = (rowIndex: number): boolean => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
return selectionRowsMap.value.get(rowIndex);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 选中指定行
|
|
|
|
|
|
* @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);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
selectionRowsMap.value.clear();
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
2024-11-23 17:23:18 +08:00
|
|
|
|
selectionRowsMap.value.set(rowIndex, rowData);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 行事件处理
|
|
|
|
|
|
*/
|
|
|
|
|
|
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);
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-12-22 00:47:01 +08:00
|
|
|
|
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];
|
2023-12-22 00:47:01 +08:00
|
|
|
|
contextmenuRef.value.openContextmenu(data);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-20 12:21:27 +08:00
|
|
|
|
const dataContextmenuClick = (event: any, rowIndex: number, column: any, data: any) => {
|
2023-11-17 13:31:28 +08:00
|
|
|
|
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];
|
2023-11-20 12:21:27 +08:00
|
|
|
|
contextmenuRef.value.openContextmenu({ column, rowData: data });
|
2023-11-18 15:22:25 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 表排序字段变更
|
|
|
|
|
|
*/
|
|
|
|
|
|
const onTableSortChange = async (sort: any) => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
nowSortColumn.value = sort;
|
2023-11-18 15:22:25 +08:00
|
|
|
|
cancelUpdateFields();
|
|
|
|
|
|
emits('sortChange', sort);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行删除数据事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
const onDeleteData = async () => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const deleteDatas = Array.from(selectionRowsMap.value.values());
|
2023-11-18 15:22:25 +08:00
|
|
|
|
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];
|
2024-05-13 19:55:43 +08:00
|
|
|
|
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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
const onGenerateInsertSql = async () => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const selectionDatas = Array.from(selectionRowsMap.value.values());
|
2023-11-20 12:21:27 +08:00
|
|
|
|
state.genTxtDialog.txt = await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas);
|
|
|
|
|
|
state.genTxtDialog.title = 'SQL';
|
|
|
|
|
|
state.genTxtDialog.visible = true;
|
2023-11-18 15:22:25 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const onGenerateJson = async () => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
const selectionDatas = Array.from(selectionRowsMap.value.values());
|
2023-11-18 15:22:25 +08:00
|
|
|
|
// 按列字段重新排序对象key
|
|
|
|
|
|
const jsonObj = [];
|
|
|
|
|
|
for (let selectionData of selectionDatas) {
|
2024-07-19 17:06:11 +08:00
|
|
|
|
let obj: any = {};
|
2023-11-18 15:22:25 +08:00
|
|
|
|
for (let column of state.columns) {
|
2023-11-18 21:15:33 +08:00
|
|
|
|
if (column.show) {
|
|
|
|
|
|
obj[column.title] = selectionData[column.dataKey];
|
|
|
|
|
|
}
|
2023-11-18 15:22:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
jsonObj.push(obj);
|
|
|
|
|
|
}
|
2023-11-20 12:21:27 +08:00
|
|
|
|
state.genTxtDialog.txt = JSON.stringify(jsonObj, null, 4);
|
|
|
|
|
|
state.genTxtDialog.title = 'JSON';
|
|
|
|
|
|
state.genTxtDialog.visible = true;
|
2023-11-18 15:22:25 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-29 20:13:29 +08:00
|
|
|
|
const copyGenTxt = async (txt: string) => {
|
|
|
|
|
|
await copyToClipboard(txt);
|
|
|
|
|
|
state.genTxtDialog.visible = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 导出当前页数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
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);
|
2023-11-18 15:22:25 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-20 12:21:27 +08:00
|
|
|
|
const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex = 0) => {
|
2024-12-08 13:04:23 +08:00
|
|
|
|
// 不存在表,或者已经在编辑中,则不处理
|
|
|
|
|
|
if (!state.table || nowUpdateCell.value) {
|
2023-11-17 13:31:28 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-23 17:23:18 +08:00
|
|
|
|
nowUpdateCell.value = {
|
2023-11-17 13:31:28 +08:00
|
|
|
|
rowIndex: rowIndex,
|
|
|
|
|
|
colIndex: columnIndex,
|
2023-11-18 15:22:25 +08:00
|
|
|
|
oldValue: rowData[column.dataKey],
|
2023-12-22 00:47:01 +08:00
|
|
|
|
dataType: column.dataType,
|
2023-11-17 13:31:28 +08:00
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
const newValue = rowData[column.dataKey];
|
|
|
|
|
|
|
|
|
|
|
|
// 未改变单元格值
|
|
|
|
|
|
if (oldValue == newValue) {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
nowUpdateCell.value = null as any;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-23 17:23:18 +08:00
|
|
|
|
let updatedRow = cellUpdateMap.value.get(rowIndex);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
if (!updatedRow) {
|
|
|
|
|
|
updatedRow = new UpdatedRow();
|
|
|
|
|
|
updatedRow.rowData = rowData;
|
2024-11-23 17:23:18 +08:00
|
|
|
|
cellUpdateMap.value.set(rowIndex, updatedRow);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
} 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;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
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-11-17 13:31:28 +08:00
|
|
|
|
|
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 = {};
|
2023-11-17 13:31:28 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
2024-01-31 20:41:41 +08:00
|
|
|
|
res += await dbInst.genUpdateSql(db, state.table, updateColumnValue, rowData);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
2023-10-31 12:36:04 +08:00
|
|
|
|
|
2024-05-13 19:55:43 +08:00
|
|
|
|
dbInst.promptExeSql(db, res, null, () => {
|
2024-11-23 17:23:18 +08:00
|
|
|
|
cellUpdateMap.value.clear();
|
2024-02-29 22:12:50 +08:00
|
|
|
|
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();
|
2023-11-17 13:31:28 +08:00
|
|
|
|
// 恢复原值
|
|
|
|
|
|
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
|
|
|
|
});
|
2023-11-17 13:31:28 +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
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
const rowClass = (row: any) => {
|
|
|
|
|
|
if (isSelection(row.rowIndex)) {
|
|
|
|
|
|
return 'data-selection';
|
|
|
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-05-31 12:12:40 +08:00
|
|
|
|
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({
|
2024-05-31 12:12:40 +08:00
|
|
|
|
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>
|
|
|
|
|
|
|
2023-11-18 15:22:25 +08:00
|
|
|
|
<style lang="scss">
|
2023-11-17 13:31:28 +08:00
|
|
|
|
.db-table-data {
|
2023-11-18 15:22:25 +08:00
|
|
|
|
.table {
|
|
|
|
|
|
border-left: var(--el-table-border);
|
|
|
|
|
|
border-top: var(--el-table-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-20 12:21:27 +08:00
|
|
|
|
.table-column {
|
2023-11-17 13:31:28 +08:00
|
|
|
|
padding: 0 2px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
border-right: var(--el-table-border);
|
|
|
|
|
|
}
|
2023-11-20 12:21:27 +08:00
|
|
|
|
|
2024-01-09 12:19:20 +08:00
|
|
|
|
.header-column-title {
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-20 12:21:27 +08:00
|
|
|
|
.table-data-cell {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
line-height: 30px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
.data-selection {
|
2023-11-22 12:19:07 +08:00
|
|
|
|
background-color: var(--el-table-current-row-bg-color);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
2023-11-18 15:22:25 +08:00
|
|
|
|
|
2023-11-17 13:31:28 +08:00
|
|
|
|
.update_field_active {
|
2023-12-17 01:43:38 +08:00
|
|
|
|
background-color: var(--el-color-success-light-3);
|
2023-11-17 13:31:28 +08:00
|
|
|
|
}
|
2023-12-14 21:27:11 +08:00
|
|
|
|
|
|
|
|
|
|
.column-type {
|
|
|
|
|
|
color: var(--el-color-info-light-3);
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
position: absolute;
|
2024-05-08 21:04:25 +08:00
|
|
|
|
top: -7px;
|
2024-11-01 17:27:22 +08:00
|
|
|
|
padding: 3px;
|
2024-01-31 12:53:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.column-right {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 2px;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
padding: 2px;
|
2023-12-14 21:27:11 +08:00
|
|
|
|
}
|
2023-12-16 23:10:38 +08:00
|
|
|
|
}
|
2023-02-14 11:44:48 +08:00
|
|
|
|
</style>
|