mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-25 09:06:34 +08:00
feat: 在线编辑删除数据库数据弹窗美化
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
"countup.js": "^2.0.7",
|
"countup.js": "^2.0.7",
|
||||||
"cropperjs": "^1.5.11",
|
"cropperjs": "^1.5.11",
|
||||||
"echarts": "^5.1.1",
|
"echarts": "^5.1.1",
|
||||||
"element-plus": "^2.0.0",
|
"element-plus": "^2.0.1",
|
||||||
"@element-plus/icons-vue": "^0.2.4",
|
"@element-plus/icons-vue": "^0.2.4",
|
||||||
"jsonlint": "^1.6.3",
|
"jsonlint": "^1.6.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|||||||
@@ -40,11 +40,17 @@ import 'codemirror/mode/textile/textile.js';
|
|||||||
import 'codemirror/addon/hint/show-hint.css';
|
import 'codemirror/addon/hint/show-hint.css';
|
||||||
import 'codemirror/addon/hint/show-hint.js';
|
import 'codemirror/addon/hint/show-hint.js';
|
||||||
|
|
||||||
|
import { ElOption, ElSelect } from 'element-plus';
|
||||||
|
|
||||||
// 尝试获取全局实例
|
// 尝试获取全局实例
|
||||||
const CodeMirror = (window as any).CodeMirror || _CodeMirror;
|
const CodeMirror = (window as any).CodeMirror || _CodeMirror;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'codemirror',
|
name: 'codemirror',
|
||||||
|
components: {
|
||||||
|
ElOption,
|
||||||
|
ElSelect,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -70,7 +76,6 @@ export default defineComponent({
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props: any, { emit }) {
|
setup(props: any, { emit }) {
|
||||||
let { modelValue, language } = toRefs(props);
|
let { modelValue, language } = toRefs(props);
|
||||||
const textarea: any = ref(null);
|
const textarea: any = ref(null);
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { h, toRefs, reactive, computed, defineComponent, ref } from 'vue';
|
import { h, toRefs, reactive, computed, defineComponent, ref } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import _, { isNumber } from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import 'codemirror/addon/hint/show-hint.css';
|
import 'codemirror/addon/hint/show-hint.css';
|
||||||
// import base style
|
// import base style
|
||||||
@@ -218,6 +218,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|||||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { getSession } from '@/common/utils/storage';
|
import { getSession } from '@/common/utils/storage';
|
||||||
|
import SqlExecBox from './SqlExecBox';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SqlExec',
|
name: 'SqlExec',
|
||||||
@@ -726,17 +727,17 @@ export default defineComponent({
|
|||||||
const primaryKey = await getColumn(state.nowTableName);
|
const primaryKey = await getColumn(state.nowTableName);
|
||||||
const primaryKeyColumnName = primaryKey.columnName;
|
const primaryKeyColumnName = primaryKey.columnName;
|
||||||
const ids = deleteDatas.map((d: any) => `${wrapColumnValue(primaryKey, d[primaryKeyColumnName])}`).join(',');
|
const ids = deleteDatas.map((d: any) => `${wrapColumnValue(primaryKey, d[primaryKeyColumnName])}`).join(',');
|
||||||
const sql = `DELETE FROM \`${state.nowTableName}\` WHERE \`${primaryKeyColumnName}\` IN (${ids})`;
|
const sql = `DELETE FROM ${state.nowTableName} WHERE ${primaryKeyColumnName} IN (${ids})`;
|
||||||
|
|
||||||
promptExeSql(sql, null, () => {
|
promptExeSql(sql, null, () => {
|
||||||
if (!queryTab) {
|
if (!queryTab) {
|
||||||
state.dataTabs[state.activeName].execRes.data = state.dataTabs[state.activeName].execRes.data.filter(
|
state.dataTabs[state.activeName].execRes.data = state.dataTabs[state.activeName].execRes.data.filter(
|
||||||
(d: any) => !(deleteDatas.findIndex((x: any) => x[primaryKey] == d[primaryKey]) != -1)
|
(d: any) => !(deleteDatas.findIndex((x: any) => x[primaryKeyColumnName] == d[primaryKeyColumnName]) != -1)
|
||||||
);
|
);
|
||||||
state.dataTabs[state.activeName].selectionDatas = [];
|
state.dataTabs[state.activeName].selectionDatas = [];
|
||||||
} else {
|
} else {
|
||||||
state.queryTab.execRes.data = state.queryTab.execRes.data.filter(
|
state.queryTab.execRes.data = state.queryTab.execRes.data.filter(
|
||||||
(d: any) => !(deleteDatas.findIndex((x: any) => x[primaryKey] == d[primaryKey]) != -1)
|
(d: any) => !(deleteDatas.findIndex((x: any) => x[primaryKeyColumnName] == d[primaryKeyColumnName]) != -1)
|
||||||
);
|
);
|
||||||
state.queryTab.selectionDatas = [];
|
state.queryTab.selectionDatas = [];
|
||||||
}
|
}
|
||||||
@@ -774,8 +775,8 @@ export default defineComponent({
|
|||||||
const primaryKeyColumnName = primaryKey.columnName;
|
const primaryKeyColumnName = primaryKey.columnName;
|
||||||
// 更新字段列信息
|
// 更新字段列信息
|
||||||
const updateColumn = await getColumn(state.nowTableName, column.rawColumnKey);
|
const updateColumn = await getColumn(state.nowTableName, column.rawColumnKey);
|
||||||
const sql = `UPDATE \`${state.nowTableName}\` SET \`${column.rawColumnKey}\` = ${wrapColumnValue(updateColumn, input.value)}
|
const sql = `UPDATE ${state.nowTableName} SET ${column.rawColumnKey} = ${wrapColumnValue(updateColumn, input.value)}
|
||||||
WHERE \`${primaryKeyColumnName}\` = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`;
|
WHERE ${primaryKeyColumnName} = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`;
|
||||||
promptExeSql(sql, () => {
|
promptExeSql(sql, () => {
|
||||||
div.innerText = text;
|
div.innerText = text;
|
||||||
});
|
});
|
||||||
@@ -784,17 +785,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取表主键列名,目前先以默认表字段第一个字段
|
|
||||||
// * {
|
|
||||||
// * columnName,columnType等
|
|
||||||
// * }
|
|
||||||
// */
|
|
||||||
// const getTablePrimaryKeyColume = async (tableName: string) => {
|
|
||||||
// const cols = await getColumns(tableName);
|
|
||||||
// return cols[0];
|
|
||||||
// };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据字段列名获取字段列信息。
|
* 根据字段列名获取字段列信息。
|
||||||
* 若字段列名为空,则返回第一个字段列信息(用于获取主键等,目前先以默认表字段第一个字段)
|
* 若字段列名为空,则返回第一个字段列信息(用于获取主键等,目前先以默认表字段第一个字段)
|
||||||
@@ -828,57 +818,12 @@ export default defineComponent({
|
|||||||
* 弹框提示是否执行sql
|
* 弹框提示是否执行sql
|
||||||
*/
|
*/
|
||||||
const promptExeSql = (sql: string, cancelFunc: any = null, successFunc: any = null) => {
|
const promptExeSql = (sql: string, cancelFunc: any = null, successFunc: any = null) => {
|
||||||
ElMessageBox({
|
SqlExecBox({
|
||||||
title: '待执行SQL',
|
sql: sql,
|
||||||
message: h(
|
dbId: state.dbId as any,
|
||||||
'div',
|
runSuccessCallback: successFunc,
|
||||||
{
|
cancelCallback: cancelFunc,
|
||||||
class: 'el-textarea',
|
});
|
||||||
},
|
|
||||||
[
|
|
||||||
h('textarea', {
|
|
||||||
class: 'el-textarea__inner',
|
|
||||||
autocomplete: 'off',
|
|
||||||
rows: 8,
|
|
||||||
style: {
|
|
||||||
height: '300px',
|
|
||||||
width: '100%',
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
value: sqlFormatter(sql),
|
|
||||||
onInput: ($event: any) => (sql = $event.target.value),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: '执行',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
beforeClose: (action, instance, done) => {
|
|
||||||
if (action === 'confirm') {
|
|
||||||
instance.confirmButtonLoading = true;
|
|
||||||
instance.confirmButtonText = '执行中...';
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
setTimeout(() => {
|
|
||||||
instance.confirmButtonLoading = false;
|
|
||||||
}, 200);
|
|
||||||
}, 200);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
await runSql(sql);
|
|
||||||
if (successFunc) {
|
|
||||||
successFunc();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (cancelFunc) {
|
|
||||||
cancelFunc();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加新数据行
|
// 添加新数据行
|
||||||
@@ -889,11 +834,11 @@ export default defineComponent({
|
|||||||
// key: 字段名,value: 字段名提示
|
// key: 字段名,value: 字段名提示
|
||||||
let obj: any = {};
|
let obj: any = {};
|
||||||
columns.forEach((item: any) => {
|
columns.forEach((item: any) => {
|
||||||
obj[`\`${item.columnName}\``] = `'${item.columnName}[${item.columnType}]${item.nullable == 'YES' ? '' : '[not null]'}'`;
|
obj[`${item.columnName}`] = `'${item.columnName}[${item.columnType}]${item.nullable == 'YES' ? '' : '[not null]'}'`;
|
||||||
});
|
});
|
||||||
let columnNames = Object.keys(obj).join(',');
|
let columnNames = Object.keys(obj).join(',');
|
||||||
let values = Object.values(obj).join(',');
|
let values = Object.values(obj).join(',');
|
||||||
let sql = `INSERT INTO \`${state.nowTableName}\` (${columnNames}) VALUES (${values});`;
|
let sql = `INSERT INTO ${state.nowTableName} (${columnNames}) VALUES (${values});`;
|
||||||
promptExeSql(sql, null, () => {
|
promptExeSql(sql, null, () => {
|
||||||
onRefresh(tableNmae);
|
onRefresh(tableNmae);
|
||||||
});
|
});
|
||||||
|
|||||||
43
mayfly_go_web/src/views/ops/db/SqlExecBox.ts
Normal file
43
mayfly_go_web/src/views/ops/db/SqlExecBox.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { h, render, VNode } from 'vue'
|
||||||
|
import SqlExecDialog from './SqlExecDialog.vue'
|
||||||
|
|
||||||
|
export type SqlExecProps = {
|
||||||
|
sql: string
|
||||||
|
dbId: number,
|
||||||
|
runSuccessCallback?: Function,
|
||||||
|
cancelCallback?: Function
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxId = 'Sql-Exec-ID'
|
||||||
|
|
||||||
|
const renderBox = (): VNode => {
|
||||||
|
const props: SqlExecProps = {
|
||||||
|
sql: '',
|
||||||
|
dbId: 0,
|
||||||
|
} as any
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.id = boxId
|
||||||
|
// 创建 虚拟dom
|
||||||
|
const boxVNode = h(SqlExecDialog, props)
|
||||||
|
// 将虚拟dom渲染到 container dom 上
|
||||||
|
render(boxVNode, container)
|
||||||
|
// 最后将 container 追加到 body 上
|
||||||
|
document.body.appendChild(container)
|
||||||
|
|
||||||
|
return boxVNode
|
||||||
|
}
|
||||||
|
|
||||||
|
let boxInstance: any
|
||||||
|
|
||||||
|
const SqlExecBox = (props: SqlExecProps): void => {
|
||||||
|
if (boxInstance) {
|
||||||
|
const boxVue = boxInstance.component
|
||||||
|
// 调用open方法显示弹框
|
||||||
|
boxVue.ctx.open(props);
|
||||||
|
} else {
|
||||||
|
boxInstance = renderBox()
|
||||||
|
SqlExecBox(props)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SqlExecBox;
|
||||||
128
mayfly_go_web/src/views/ops/db/SqlExecDialog.vue
Normal file
128
mayfly_go_web/src/views/ops/db/SqlExecDialog.vue
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||||
|
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sql" :options="cmOptions" />
|
||||||
|
<div class="dialog-footer mt10">
|
||||||
|
<el-button @click="runSql" type="primary" :loading="btnLoading">执 行</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { toRefs, reactive, defineComponent } from 'vue';
|
||||||
|
import { dbApi } from './api';
|
||||||
|
import { ElDialog, ElButton } from 'element-plus';
|
||||||
|
// import base style
|
||||||
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
// 引入主题后还需要在 options 中指定主题才会生效
|
||||||
|
import 'codemirror/theme/base16-light.css';
|
||||||
|
import 'codemirror/addon/selection/active-line';
|
||||||
|
import { codemirror } from '@/components/codemirror';
|
||||||
|
import { format as sqlFormatter } from 'sql-formatter';
|
||||||
|
|
||||||
|
import { SqlExecProps } from './SqlExecBox';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'SqlExecDialog',
|
||||||
|
components: {
|
||||||
|
codemirror,
|
||||||
|
ElButton,
|
||||||
|
ElDialog,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
dbId: {
|
||||||
|
type: [Number],
|
||||||
|
},
|
||||||
|
sql: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props: any) {
|
||||||
|
const state = reactive({
|
||||||
|
dialogVisible: false,
|
||||||
|
sql: '',
|
||||||
|
dbId: 0,
|
||||||
|
btnLoading: false,
|
||||||
|
cmOptions: {
|
||||||
|
tabSize: 4,
|
||||||
|
mode: 'text/x-sql',
|
||||||
|
lineNumbers: true,
|
||||||
|
line: true,
|
||||||
|
indentWithTabs: true,
|
||||||
|
smartIndent: true,
|
||||||
|
matchBrackets: true,
|
||||||
|
theme: 'base16-light',
|
||||||
|
autofocus: true,
|
||||||
|
extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let runSuccessCallback: any;
|
||||||
|
let cancelCallback: any;
|
||||||
|
let runSuccess: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行sql
|
||||||
|
*/
|
||||||
|
const runSql = async () => {
|
||||||
|
try {
|
||||||
|
await dbApi.sqlExec.request({
|
||||||
|
id: state.dbId,
|
||||||
|
sql: state.sql.trim(),
|
||||||
|
});
|
||||||
|
runSuccess = true;
|
||||||
|
} catch (e) {
|
||||||
|
runSuccess = false;
|
||||||
|
}
|
||||||
|
if (runSuccessCallback) {
|
||||||
|
runSuccessCallback();
|
||||||
|
}
|
||||||
|
cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
state.dialogVisible = false;
|
||||||
|
// 没有执行成功,并且取消回调函数存在,则执行
|
||||||
|
if (!runSuccess && cancelCallback) {
|
||||||
|
cancelCallback();
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
state.dbId = 0;
|
||||||
|
state.sql = '';
|
||||||
|
runSuccessCallback = null;
|
||||||
|
cancelCallback = null;
|
||||||
|
runSuccess = false;
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const open = (props: SqlExecProps) => {
|
||||||
|
runSuccessCallback = props.runSuccessCallback;
|
||||||
|
cancelCallback = props.cancelCallback;
|
||||||
|
state.sql = sqlFormatter(props.sql);
|
||||||
|
state.dbId = props.dbId;
|
||||||
|
state.dialogVisible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
open,
|
||||||
|
runSql,
|
||||||
|
cancel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.codesql {
|
||||||
|
font-size: 9pt;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.dialog-footer {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -99,11 +99,9 @@
|
|||||||
:action="getUploadFile({ path: data.path })"
|
:action="getUploadFile({ path: data.path })"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
name="file"
|
name="file"
|
||||||
multiple
|
|
||||||
:limit="100"
|
|
||||||
style="display: inline-block; margin-left: 2px"
|
style="display: inline-block; margin-left: 2px"
|
||||||
>
|
>
|
||||||
<el-link @click.prevent icon="upload" :underline="false"> 上传 </el-link>
|
<el-link icon="upload" :underline="false"> 上传 </el-link>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user