mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
refactor: 数据库表支持editor编辑调整
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="monaco-editor" style="border: 1px solid var(--el-border-color-light, #ebeef5); height: 100%">
|
||||
<div class="monaco-editor-content" ref="monacoTextarea" :style="{ height: height }"></div>
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage">
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage" filterable>
|
||||
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
@@ -294,17 +294,19 @@ const registeShell = () => {
|
||||
};
|
||||
|
||||
const format = () => {
|
||||
/*
|
||||
触发自动格式化;
|
||||
*/
|
||||
// 触发自动格式化;
|
||||
monacoEditorIns.trigger('', 'editor.action.formatDocument', '');
|
||||
};
|
||||
|
||||
const focus = () => {
|
||||
monacoEditorIns.focus();
|
||||
};
|
||||
|
||||
const getEditor = () => {
|
||||
return monacoEditorIns;
|
||||
};
|
||||
|
||||
defineExpose({ getEditor, format });
|
||||
defineExpose({ getEditor, format, focus });
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -7,8 +7,8 @@ export type MonacoEditorDialogProps = {
|
||||
language: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
confirmFn?: Function;
|
||||
cancelFn?: Function;
|
||||
confirmFn?: Function; // 点击确认的回调函数,入参editor value
|
||||
cancelFn?: Function; // 点击取消 或 关闭弹窗的回调函数
|
||||
};
|
||||
|
||||
const boxId = 'monaco-editor-dialog-id';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="state.title" v-model="state.dialogVisible" :width="state.width" @close="cancel">
|
||||
<monaco-editor ref="editorRef" :height="state.height" class="editor" :language="state.language" v-model="contentValue" />
|
||||
<monaco-editor ref="editorRef" :height="state.height" class="editor" :language="state.language" v-model="contentValue" can-change-mode />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
@@ -120,6 +120,7 @@ const open = (optionProps: MonacoEditorDialogProps) => {
|
||||
|
||||
setTimeout(() => {
|
||||
editorRef.value?.format();
|
||||
editorRef.value?.focus();
|
||||
}, 300);
|
||||
|
||||
state.dialogVisible = true;
|
||||
|
||||
@@ -237,7 +237,7 @@ body,
|
||||
}
|
||||
|
||||
.color-success {
|
||||
color: var(--el-color-success);
|
||||
color: var(--el-color-success) !important;
|
||||
}
|
||||
|
||||
.color-warning {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
<template>
|
||||
<el-input
|
||||
v-if="dataType == DataType.String"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@blur="emit('blur')"
|
||||
class="w100 mb4"
|
||||
input-style="text-align: center; height: 26px;"
|
||||
size="small"
|
||||
v-model="itemValue"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
<div class="string-input-container w100" v-if="dataType == DataType.String">
|
||||
<el-input
|
||||
v-if="dataType == DataType.String"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@blur="handlerBlur"
|
||||
class="w100 mb4"
|
||||
input-style="text-align: center; height: 26px;"
|
||||
size="small"
|
||||
v-model="itemValue"
|
||||
:placeholder="placeholder"
|
||||
/>
|
||||
<SvgIcon v-if="showEditorIcon" @mousedown="openEditor" class="string-input-container-icon color-success" name="FullScreen" :size="10" />
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
v-else-if="dataType == DataType.Number"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@blur="emit('blur')"
|
||||
@blur="handlerBlur"
|
||||
class="w100 mb4"
|
||||
input-style="text-align: center; height: 26px;"
|
||||
size="small"
|
||||
@@ -26,7 +29,7 @@
|
||||
v-else-if="dataType == DataType.Date"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@change="emit('blur')"
|
||||
@blur="emit('blur')"
|
||||
@blur="handlerBlur"
|
||||
class="edit-time-picker mb4"
|
||||
popper-class="edit-time-picker-popper"
|
||||
size="small"
|
||||
@@ -41,7 +44,7 @@
|
||||
v-else-if="dataType == DataType.DateTime"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@change="emit('blur')"
|
||||
@blur="emit('blur')"
|
||||
@blur="handlerBlur"
|
||||
class="edit-time-picker mb4"
|
||||
popper-class="edit-time-picker-popper"
|
||||
size="small"
|
||||
@@ -56,7 +59,7 @@
|
||||
v-else-if="dataType == DataType.Time"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
@change="emit('blur')"
|
||||
@blur="emit('blur')"
|
||||
@blur="handlerBlur"
|
||||
class="edit-time-picker mb4"
|
||||
popper-class="edit-time-picker-popper"
|
||||
size="small"
|
||||
@@ -68,16 +71,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Ref } from 'vue';
|
||||
import { Ref, ref, computed } from 'vue';
|
||||
import { ElInput } from 'element-plus';
|
||||
import { DataType } from '../../dialect/index';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import MonacoEditorDialog from '@/components/monaco/MonacoEditorDialog';
|
||||
|
||||
export interface ColumnFormItemProps {
|
||||
modelValue: string | number; // 绑定的值
|
||||
dataType: DataType; // 数据类型
|
||||
focus?: boolean; // 是否获取焦点
|
||||
placeholder?: string;
|
||||
columnName?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ColumnFormItemProps>(), {
|
||||
@@ -88,9 +94,75 @@ const props = withDefaults(defineProps<ColumnFormItemProps>(), {
|
||||
const emit = defineEmits(['update:modelValue', 'blur']);
|
||||
|
||||
const itemValue: Ref<any> = useVModel(props, 'modelValue', emit);
|
||||
|
||||
const showEditorIcon = computed(() => {
|
||||
return typeof itemValue.value === 'string' && itemValue.value.length > 50;
|
||||
});
|
||||
|
||||
const editorOpening = ref(false);
|
||||
|
||||
const openEditor = () => {
|
||||
editorOpening.value = true;
|
||||
// 编辑器语言,如:json、html、text
|
||||
let editorLang = getEditorLangByValue(itemValue.value);
|
||||
MonacoEditorDialog({
|
||||
content: itemValue.value,
|
||||
title: `编辑字段 [${props.columnName}]`,
|
||||
language: editorLang,
|
||||
confirmFn: (newVal: any) => {
|
||||
itemValue.value = newVal;
|
||||
closeEditorDialog();
|
||||
},
|
||||
cancelFn: closeEditorDialog,
|
||||
});
|
||||
};
|
||||
|
||||
const closeEditorDialog = () => {
|
||||
editorOpening.value = false;
|
||||
handlerBlur();
|
||||
};
|
||||
|
||||
const handlerBlur = () => {
|
||||
if (editorOpening.value) {
|
||||
return;
|
||||
}
|
||||
emit('blur');
|
||||
};
|
||||
|
||||
const getEditorLangByValue = (value: any) => {
|
||||
// 判断是否是json
|
||||
try {
|
||||
if (typeof JSON.parse(value) === 'object') {
|
||||
return 'json';
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
// 判断是否是html
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(value, 'text/html');
|
||||
if (Array.from(doc.body.childNodes).some((node) => node.nodeType === 1)) {
|
||||
return 'html';
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
return 'text';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.string-input-container {
|
||||
position: relative;
|
||||
}
|
||||
.string-input-container-icon {
|
||||
position: absolute;
|
||||
top: 5px; /* 调整图标的垂直位置 */
|
||||
right: 5px; /* 调整图标的水平位置 */
|
||||
}
|
||||
|
||||
.edit-time-picker {
|
||||
height: 26px;
|
||||
width: 100% !important;
|
||||
|
||||
@@ -79,8 +79,9 @@
|
||||
<div v-if="canEdit(rowIndex, columnIndex)">
|
||||
<ColumnFormItem
|
||||
v-model="rowData[column.dataKey!]"
|
||||
:data-type="nowUpdateCell.dataType"
|
||||
:data-type="column.dataType"
|
||||
@blur="onExitEditMode(rowData, column, rowIndex)"
|
||||
:column-name="column.columnName"
|
||||
focus
|
||||
/>
|
||||
</div>
|
||||
@@ -144,7 +145,6 @@ import { dateStrFormat } from '@/common/utils/date';
|
||||
import { useIntervalFn } from '@vueuse/core';
|
||||
import { ColumnTypeSubscript, DataType, DbDialect, DbType, getDbDialect } from '../../dialect/index';
|
||||
import ColumnFormItem from './ColumnFormItem.vue';
|
||||
import MonacoEditorDialog from '@/components/monaco/MonacoEditorDialog';
|
||||
|
||||
const emits = defineEmits(['dataDelete', 'sortChange', 'deleteData', 'selectionChange', 'changeUpdatedField']);
|
||||
|
||||
@@ -315,7 +315,6 @@ const state = reactive({
|
||||
loading: false,
|
||||
tableHeight: '600px',
|
||||
emptyText: '',
|
||||
editorLang: 'string',
|
||||
|
||||
execTime: 0,
|
||||
contextmenu: {
|
||||
@@ -488,7 +487,7 @@ const cancelLoading = async () => {
|
||||
* @param colIndex ci
|
||||
*/
|
||||
const canEdit = (rowIndex: number, colIndex: number) => {
|
||||
return state.table && nowUpdateCell?.rowIndex == rowIndex && nowUpdateCell?.colIndex == colIndex && state.editorLang === 'string';
|
||||
return state.table && nowUpdateCell?.rowIndex == rowIndex && nowUpdateCell?.colIndex == colIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -643,29 +642,6 @@ const onExportSql = async () => {
|
||||
);
|
||||
};
|
||||
|
||||
const getEditorLangByValue = (value: any) => {
|
||||
if (typeof value === 'string') {
|
||||
// 判断是否是json
|
||||
try {
|
||||
if (typeof JSON.parse(value) === 'object') {
|
||||
return 'json';
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
// 判断是否是html
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(value, 'text/html');
|
||||
if (Array.from(doc.body.childNodes).some((node) => node.nodeType === 1)) {
|
||||
return 'html';
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
return 'string';
|
||||
};
|
||||
|
||||
const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex = 0) => {
|
||||
if (!state.table) {
|
||||
return;
|
||||
@@ -678,21 +654,6 @@ const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex =
|
||||
oldValue: rowData[column.dataKey],
|
||||
dataType: column.dataType,
|
||||
};
|
||||
|
||||
// 编辑器语言,如:json、html、string,目前支持json、html使用MonacoEditor编辑器
|
||||
let editorLang = getEditorLangByValue(rowData[column.dataKey]);
|
||||
state.editorLang = editorLang;
|
||||
if (editorLang === 'html' || editorLang === 'json') {
|
||||
MonacoEditorDialog({
|
||||
content: rowData[column.dataKey],
|
||||
title: `编辑字段 [${column.dataKey}]`,
|
||||
language: editorLang,
|
||||
confirmFn: (newVal: any) => {
|
||||
rowData[column.dataKey] = newVal;
|
||||
onExitEditMode(rowData, column, rowIndex);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
@keyup.enter.native="onSelectByCondition"
|
||||
@select="handlerColumnSelect"
|
||||
popper-class="my-autocomplete"
|
||||
placeholder="可输入SQL条件表达式后回车或点击查询图标过滤结果, 可根据备注或字段名提示"
|
||||
placeholder="选择列 或 输入SQL条件表达式后回车或点击查询图标过滤结果, 输入时可根据字段名提示"
|
||||
@clear="selectData"
|
||||
size="small"
|
||||
clearable
|
||||
@@ -90,6 +90,38 @@
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #prepend>
|
||||
<el-popover :visible="state.condPopVisible" trigger="click" :width="320" placement="right">
|
||||
<template #reference>
|
||||
<el-button @click.stop="chooseCondColumnName" class="color-success" text size="small">选择列</el-button>
|
||||
</template>
|
||||
<el-table
|
||||
:data="filterCondColumns"
|
||||
max-height="500"
|
||||
size="small"
|
||||
@row-click="
|
||||
(...event: any) => {
|
||||
onConditionRowClick(event);
|
||||
}
|
||||
"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<el-table-column property="columnName" label="列名" show-overflow-tooltip>
|
||||
<template #header>
|
||||
<el-input
|
||||
ref="columnNameSearchInputRef"
|
||||
v-model="state.columnNameSearch"
|
||||
size="small"
|
||||
placeholder="输入列名或备注过滤"
|
||||
@click.stop="(e: any) => e.preventDefault()"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
|
||||
</el-table>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -126,6 +158,35 @@
|
||||
<span>{{ state.sql }}</span>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="conditionDialog.visible" :title="conditionDialog.title" width="420px">
|
||||
<el-row>
|
||||
<el-col :span="5">
|
||||
<el-select v-model="conditionDialog.condition">
|
||||
<el-option label="=" value="="> </el-option>
|
||||
<el-option label="LIKE" value="LIKE"> </el-option>
|
||||
<el-option label=">" value=">"> </el-option>
|
||||
<el-option label=">=" value=">="> </el-option>
|
||||
<el-option label="<" value="<"> </el-option>
|
||||
<el-option label="<=" value="<="> </el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-input
|
||||
@keyup.enter.native="onConfirmCondition"
|
||||
ref="condDialogInputRef"
|
||||
v-model="conditionDialog.value"
|
||||
:placeholder="conditionDialog.placeholder"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="onCancelCondition">取消</el-button>
|
||||
<el-button type="primary" @click="onConfirmCondition">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="addDataDialog.visible" :title="addDataDialog.title" :destroy-on-close="true" width="600px">
|
||||
<el-form ref="dataForm" :model="addDataDialog.data" label-width="auto" size="small">
|
||||
<el-form-item
|
||||
@@ -140,6 +201,7 @@
|
||||
v-model="addDataDialog.data[`${column.columnName}`]"
|
||||
:data-type="dbDialect.getDataType(column.columnType)"
|
||||
:placeholder="`${column.columnType} ${column.columnComment}`"
|
||||
:column-name="column.columnName"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -154,7 +216,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, Ref, ref, toRefs, watch } from 'vue';
|
||||
import { computed, onMounted, reactive, Ref, ref, toRefs, watch } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import { DbInst } from '@/views/ops/db/db';
|
||||
@@ -162,6 +224,7 @@ import DbTableData from './DbTableData.vue';
|
||||
import { DbDialect, getDbDialect } from '@/views/ops/db/dialect';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import ColumnFormItem from './ColumnFormItem.vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
dbId: {
|
||||
@@ -185,6 +248,8 @@ const props = defineProps({
|
||||
const dataForm: any = ref(null);
|
||||
const dbTableRef: Ref = ref(null);
|
||||
const condInputRef: Ref = ref(null);
|
||||
const columnNameSearchInputRef: Ref = ref(null);
|
||||
const condDialogInputRef: Ref = ref(null);
|
||||
|
||||
const defaultPageSize = DbInst.DefaultLimit;
|
||||
|
||||
@@ -208,6 +273,17 @@ const state = reactive({
|
||||
],
|
||||
count: 0,
|
||||
selectionDatas: [] as any,
|
||||
condPopVisible: false,
|
||||
columnNameSearch: '',
|
||||
conditionDialog: {
|
||||
title: '',
|
||||
placeholder: '',
|
||||
columnRow: null,
|
||||
dataTab: null,
|
||||
visible: false,
|
||||
condition: '=',
|
||||
value: null,
|
||||
},
|
||||
addDataDialog: {
|
||||
data: {},
|
||||
title: '',
|
||||
@@ -219,7 +295,7 @@ const state = reactive({
|
||||
dbDialect: {} as DbDialect,
|
||||
});
|
||||
|
||||
const { datas, condition, loading, columns, pageNum, pageSize, pageSizes, count, hasUpdatedFileds, addDataDialog, dbDialect } = toRefs(state);
|
||||
const { datas, condition, loading, columns, pageNum, pageSize, pageSizes, count, hasUpdatedFileds, conditionDialog, addDataDialog, dbDialect } = toRefs(state);
|
||||
|
||||
watch(
|
||||
() => props.tableHeight,
|
||||
@@ -243,8 +319,15 @@ onMounted(async () => {
|
||||
await onRefresh();
|
||||
|
||||
state.dbDialect = getDbDialect(getNowDbInst().type);
|
||||
useEventListener('click', handlerWindowClick);
|
||||
});
|
||||
|
||||
const handlerWindowClick = () => {
|
||||
if (state.condPopVisible) {
|
||||
state.condPopVisible = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onRefresh = async () => {
|
||||
state.pageNum = 1;
|
||||
await selectData();
|
||||
@@ -302,7 +385,7 @@ const getColumnTips = (queryString: string, callback: any) => {
|
||||
if (columnNameSearch) {
|
||||
columnNameSearch = columnNameSearch.toLowerCase();
|
||||
res = columns.filter((data: any) => {
|
||||
return data.columnName.toLowerCase().includes(columnNameSearch) || data.columnComment.includes(columnNameSearch);
|
||||
return data.columnName.toLowerCase().includes(columnNameSearch);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -331,6 +414,69 @@ const handlerColumnSelect = (column: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择条件列
|
||||
*/
|
||||
const chooseCondColumnName = () => {
|
||||
state.condPopVisible = !state.condPopVisible;
|
||||
if (state.condPopVisible) {
|
||||
columnNameSearchInputRef.value.clear();
|
||||
columnNameSearchInputRef.value.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 过滤条件列名
|
||||
*/
|
||||
const filterCondColumns = computed(() => {
|
||||
const columns = state.columns;
|
||||
let columnNameSearch = state.columnNameSearch;
|
||||
if (!columnNameSearch) {
|
||||
return columns;
|
||||
}
|
||||
columnNameSearch = columnNameSearch.toLowerCase();
|
||||
return columns.filter((data: any) => {
|
||||
return data.columnName.toLowerCase().includes(columnNameSearch) || data.columnComment.toLowerCase().includes(columnNameSearch);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 条件查询,点击列信息后显示输入对应的值
|
||||
*/
|
||||
const onConditionRowClick = (event: any) => {
|
||||
const row = event[0];
|
||||
state.conditionDialog.title = `请输入 [${row.columnName}] 的值`;
|
||||
state.conditionDialog.placeholder = `${row.columnType} ${row.columnComment}`;
|
||||
state.conditionDialog.columnRow = row;
|
||||
state.conditionDialog.visible = true;
|
||||
setTimeout(() => {
|
||||
condDialogInputRef.value.focus();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// 确认条件
|
||||
const onConfirmCondition = () => {
|
||||
const conditionDialog = state.conditionDialog;
|
||||
let condition = state.condition;
|
||||
if (condition) {
|
||||
condition += ` AND `;
|
||||
}
|
||||
const row = conditionDialog.columnRow as any;
|
||||
condition += `${row.columnName} ${conditionDialog.condition} `;
|
||||
state.condition = condition + DbInst.wrapColumnValue(row.columnType, conditionDialog.value);
|
||||
onCancelCondition();
|
||||
condInputRef.value.focus();
|
||||
};
|
||||
|
||||
const onCancelCondition = () => {
|
||||
state.conditionDialog.visible = false;
|
||||
state.conditionDialog.title = ``;
|
||||
state.conditionDialog.placeholder = ``;
|
||||
state.conditionDialog.value = null;
|
||||
state.conditionDialog.columnRow = null;
|
||||
state.conditionDialog.dataTab = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交事务,用于没有开启自动提交事务
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user