mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: 在线编辑删除数据库数据弹窗美化
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
"countup.js": "^2.0.7",
|
||||
"cropperjs": "^1.5.11",
|
||||
"echarts": "^5.1.1",
|
||||
"element-plus": "^2.0.0",
|
||||
"element-plus": "^2.0.1",
|
||||
"@element-plus/icons-vue": "^0.2.4",
|
||||
"jsonlint": "^1.6.3",
|
||||
"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.js';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
// 尝试获取全局实例
|
||||
const CodeMirror = (window as any).CodeMirror || _CodeMirror;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'codemirror',
|
||||
components: {
|
||||
ElOption,
|
||||
ElSelect,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
@@ -70,7 +76,6 @@ export default defineComponent({
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
setup(props: any, { emit }) {
|
||||
let { modelValue, language } = toRefs(props);
|
||||
const textarea: any = ref(null);
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
<script lang="ts">
|
||||
import { h, toRefs, reactive, computed, defineComponent, ref } from 'vue';
|
||||
import { dbApi } from './api';
|
||||
import _, { isNumber } from 'lodash';
|
||||
import _ from 'lodash';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint.css';
|
||||
// import base style
|
||||
@@ -218,6 +218,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import SqlExecBox from './SqlExecBox';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SqlExec',
|
||||
@@ -726,17 +727,17 @@ export default defineComponent({
|
||||
const primaryKey = await getColumn(state.nowTableName);
|
||||
const primaryKeyColumnName = primaryKey.columnName;
|
||||
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, () => {
|
||||
if (!queryTab) {
|
||||
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 = [];
|
||||
} else {
|
||||
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 = [];
|
||||
}
|
||||
@@ -774,8 +775,8 @@ export default defineComponent({
|
||||
const primaryKeyColumnName = primaryKey.columnName;
|
||||
// 更新字段列信息
|
||||
const updateColumn = await getColumn(state.nowTableName, column.rawColumnKey);
|
||||
const sql = `UPDATE \`${state.nowTableName}\` SET \`${column.rawColumnKey}\` = ${wrapColumnValue(updateColumn, input.value)}
|
||||
WHERE \`${primaryKeyColumnName}\` = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`;
|
||||
const sql = `UPDATE ${state.nowTableName} SET ${column.rawColumnKey} = ${wrapColumnValue(updateColumn, input.value)}
|
||||
WHERE ${primaryKeyColumnName} = ${wrapColumnValue(primaryKey, row[primaryKeyColumnName])}`;
|
||||
promptExeSql(sql, () => {
|
||||
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
|
||||
*/
|
||||
const promptExeSql = (sql: string, cancelFunc: any = null, successFunc: any = null) => {
|
||||
ElMessageBox({
|
||||
title: '待执行SQL',
|
||||
message: h(
|
||||
'div',
|
||||
{
|
||||
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();
|
||||
}
|
||||
});
|
||||
SqlExecBox({
|
||||
sql: sql,
|
||||
dbId: state.dbId as any,
|
||||
runSuccessCallback: successFunc,
|
||||
cancelCallback: cancelFunc,
|
||||
});
|
||||
};
|
||||
|
||||
// 添加新数据行
|
||||
@@ -889,11 +834,11 @@ export default defineComponent({
|
||||
// key: 字段名,value: 字段名提示
|
||||
let obj: 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 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, () => {
|
||||
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 })"
|
||||
:show-file-list="false"
|
||||
name="file"
|
||||
multiple
|
||||
:limit="100"
|
||||
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-dropdown-item>
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user