feat: 在线编辑删除数据库数据弹窗美化

This commit is contained in:
meilin.huang
2022-02-08 17:59:46 +08:00
parent 3deae14fb9
commit b892c7d152
6 changed files with 194 additions and 75 deletions

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);
});

View 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;

View 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>

View File

@@ -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>