mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
refactor: sql取消执行逻辑调整、前端使用vueuse重构部分代码
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@vueuse/core": "^10.7.0",
|
||||
"asciinema-player": "^3.6.2",
|
||||
"axios": "^1.6.2",
|
||||
"clipboard": "^2.0.11",
|
||||
@@ -21,7 +22,7 @@
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"monaco-editor": "^0.44.0",
|
||||
"monaco-editor": "^0.45.0",
|
||||
"monaco-sql-languages": "^0.11.0",
|
||||
"monaco-themes": "^0.4.4",
|
||||
"nprogress": "^0.2.0",
|
||||
@@ -32,7 +33,7 @@
|
||||
"splitpanes": "^3.1.5",
|
||||
"sql-formatter": "^14.0.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.10",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
@@ -54,7 +55,7 @@
|
||||
"prettier": "^3.1.0",
|
||||
"sass": "^1.69.0",
|
||||
"typescript": "^5.3.2",
|
||||
"vite": "^5.0.5",
|
||||
"vite": "^5.0.7",
|
||||
"vue-eslint-parser": "^9.3.2"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
@@ -28,6 +28,7 @@ import Setings from '@/layout/navBars/breadcrumb/setings.vue';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
import { getThemeConfig } from './common/utils/storage';
|
||||
import { useWatermark } from '@/common/sysconfig';
|
||||
import { useIntervalFn } from '@vueuse/core';
|
||||
|
||||
const setingsRef = ref();
|
||||
const route = useRoute();
|
||||
@@ -40,12 +41,6 @@ const openSetingsDrawer = () => {
|
||||
setingsRef.value.openDrawer();
|
||||
};
|
||||
|
||||
const prefers = matchMedia('(prefers-color-scheme: dark)');
|
||||
const switchDarkFollowOS = () => {
|
||||
// 跟随系统主题
|
||||
themeConfigStores.switchDark(prefers.matches);
|
||||
};
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
@@ -60,7 +55,6 @@ onMounted(() => {
|
||||
themeConfigStores.setThemeConfig({ themeConfig: tc });
|
||||
document.documentElement.style.cssText = getLocal('themeConfigStyle');
|
||||
}
|
||||
switchDarkFollowOS();
|
||||
|
||||
// 是否开启水印
|
||||
useWatermark().then((res) => {
|
||||
@@ -77,36 +71,35 @@ watch(
|
||||
setTimeout(() => {
|
||||
setWatermarkContent();
|
||||
refreshWatermarkTime();
|
||||
resume();
|
||||
}, 500);
|
||||
} else {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 刷新水印时间
|
||||
const { pause, resume } = useIntervalFn(() => {
|
||||
if (!themeConfig.value.isWatermark) {
|
||||
pause();
|
||||
}
|
||||
refreshWatermarkTime();
|
||||
}, 60000);
|
||||
|
||||
const setWatermarkContent = () => {
|
||||
themeConfigStores.setWatermarkUser();
|
||||
themeConfigStores.setWatermarkNowTime();
|
||||
};
|
||||
|
||||
let refreshWatermarkTimeInterval: any = null;
|
||||
/**
|
||||
* 刷新水印时间
|
||||
*/
|
||||
const refreshWatermarkTime = () => {
|
||||
if (refreshWatermarkTimeInterval) {
|
||||
clearInterval(refreshWatermarkTimeInterval);
|
||||
}
|
||||
refreshWatermarkTimeInterval = setInterval(() => {
|
||||
if (themeConfig.value.isWatermark) {
|
||||
themeConfigStores.setWatermarkNowTime();
|
||||
} else {
|
||||
clearInterval(refreshWatermarkTimeInterval);
|
||||
}
|
||||
}, 60000);
|
||||
themeConfigStores.setWatermarkNowTime();
|
||||
};
|
||||
|
||||
// 页面销毁时,关闭监听布局配置
|
||||
onUnmounted(() => {
|
||||
clearInterval(refreshWatermarkTimeInterval);
|
||||
mittBus.off('openSetingsDrawer', () => {});
|
||||
});
|
||||
|
||||
|
||||
@@ -49,32 +49,27 @@ class Api {
|
||||
* 请求对应的该api
|
||||
* @param {Object} param 请求该api的参数
|
||||
*/
|
||||
request(param: any = null, options: any = null, headers: any = null): Promise<any> {
|
||||
request(param: any = null, options: any = {}): Promise<any> {
|
||||
if (this.beforeHandler) {
|
||||
this.beforeHandler(param);
|
||||
}
|
||||
return request.request(this.method, this.url, param, headers, options);
|
||||
return request.request(this.method, this.url, param, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求对应的该api
|
||||
* 允许取消的请求, 使用Api.cancelReq(key) 取消请求
|
||||
* @param key 用于取消该key关联的请求
|
||||
* @param {Object} param 请求该api的参数
|
||||
*/
|
||||
requestCanCancel(key: string, param: any = null, options: any = null, headers: any = null): Promise<any> {
|
||||
allowCancelReq(key: string, param: any = null, options: RequestInit = {}): Promise<any> {
|
||||
let controller = Api.abortControllers.get(key);
|
||||
if (!controller) {
|
||||
controller = new AbortController();
|
||||
Api.abortControllers.set(key, controller);
|
||||
}
|
||||
if (options) {
|
||||
options.signal = controller.signal;
|
||||
} else {
|
||||
options = {
|
||||
signal: controller.signal,
|
||||
};
|
||||
}
|
||||
options.signal = controller.signal;
|
||||
|
||||
return this.request(param, options, headers);
|
||||
return this.request(param, options);
|
||||
}
|
||||
|
||||
/** 静态方法 **/
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import router from '../router';
|
||||
import Axios from 'axios';
|
||||
import config from './config';
|
||||
import { getClientId, getToken } from './utils/storage';
|
||||
import { templateResolve } from './utils/string';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
request,
|
||||
fetchReq,
|
||||
get,
|
||||
post,
|
||||
put,
|
||||
del,
|
||||
getApiUrl,
|
||||
};
|
||||
|
||||
export interface Result {
|
||||
/**
|
||||
* 响应码
|
||||
@@ -30,6 +39,7 @@ enum ResultEnum {
|
||||
}
|
||||
|
||||
const baseUrl: string = config.baseApiUrl;
|
||||
// const baseUrl: string = 'http://localhost:18888/api';
|
||||
// const baseWsUrl: string = config.baseWsUrl;
|
||||
|
||||
/**
|
||||
@@ -42,13 +52,13 @@ function notifyErrorMsg(msg: string) {
|
||||
}
|
||||
|
||||
// create an axios instance
|
||||
const service = Axios.create({
|
||||
const axiosInst = axios.create({
|
||||
baseURL: baseUrl, // url = base url + request url
|
||||
timeout: 60000, // request timeout
|
||||
});
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(
|
||||
axiosInst.interceptors.request.use(
|
||||
(config: any) => {
|
||||
// do something before request is sent
|
||||
const token = getToken();
|
||||
@@ -65,21 +75,8 @@ service.interceptors.request.use(
|
||||
);
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
// 获取请求返回结果
|
||||
const res: Result = response.data;
|
||||
if (res.code === ResultEnum.SUCCESS) {
|
||||
return res.data;
|
||||
}
|
||||
// 如果提示没有权限,则移除token,使其重新登录
|
||||
if (res.code === ResultEnum.NO_PERMISSION) {
|
||||
router.push({
|
||||
path: '/401',
|
||||
});
|
||||
}
|
||||
return Promise.reject(res);
|
||||
},
|
||||
axiosInst.interceptors.response.use(
|
||||
(response) => response,
|
||||
(e: any) => {
|
||||
const rejectPromise = Promise.reject(e);
|
||||
|
||||
@@ -125,35 +122,37 @@ service.interceptors.response.use(
|
||||
* @param {Object} uri uri
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
function request(method: string, url: string, params: any = null, headers: any = null, options: any = null): Promise<any> {
|
||||
if (!url) throw new Error('请求url不能为空');
|
||||
function request(method: string, url: string, params: any = null, options: any = {}): Promise<any> {
|
||||
if (!url) {
|
||||
throw new Error('请求url不能为空');
|
||||
}
|
||||
|
||||
// 简单判断该url是否是restful风格
|
||||
if (url.indexOf('{') != -1) {
|
||||
url = templateResolve(url, params);
|
||||
}
|
||||
const query: any = {
|
||||
|
||||
const req: any = {
|
||||
method,
|
||||
url: url,
|
||||
url,
|
||||
...options,
|
||||
};
|
||||
if (headers) {
|
||||
query.headers = headers;
|
||||
}
|
||||
|
||||
// post和put使用json格式传参
|
||||
if (method === 'post' || method === 'put') {
|
||||
query.data = params;
|
||||
req.data = params;
|
||||
} else {
|
||||
query.params = params;
|
||||
req.params = params;
|
||||
}
|
||||
return service
|
||||
.request(query)
|
||||
.then((res) => res)
|
||||
|
||||
return axiosInst
|
||||
.request(req)
|
||||
.then((response) => {
|
||||
// 获取请求返回结果
|
||||
const result: Result = response.data;
|
||||
return parseResult(result);
|
||||
})
|
||||
.catch((e) => {
|
||||
// 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
|
||||
if (e.msg && e?.code != ResultEnum.NO_PERMISSION) {
|
||||
notifyErrorMsg(e.msg);
|
||||
}
|
||||
return Promise.reject(e);
|
||||
});
|
||||
}
|
||||
@@ -165,20 +164,20 @@ function request(method: string, url: string, params: any = null, headers: any =
|
||||
* @param {Object} url uri
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
function get(url: string, params: any = null, headers: any = null, options: any = null): Promise<any> {
|
||||
return request('get', url, params, headers, options);
|
||||
function get(url: string, params: any = null, options: any = {}): Promise<any> {
|
||||
return request('get', url, params, options);
|
||||
}
|
||||
|
||||
function post(url: string, params: any = null, headers: any = null, options: any = null): Promise<any> {
|
||||
return request('post', url, params, headers, options);
|
||||
function post(url: string, params: any = null, options: any = {}): Promise<any> {
|
||||
return request('post', url, params, options);
|
||||
}
|
||||
|
||||
function put(url: string, params: any = null, headers: any = null, options: any = null): Promise<any> {
|
||||
return request('put', url, params, headers, options);
|
||||
function put(url: string, params: any = null, options: any = {}): Promise<any> {
|
||||
return request('put', url, params, options);
|
||||
}
|
||||
|
||||
function del(url: string, params: any = null, headers: any = null, options: any = null): Promise<any> {
|
||||
return request('delete', url, params, headers, options);
|
||||
function del(url: string, params: any = null, options: any = {}): Promise<any> {
|
||||
return request('delete', url, params, options);
|
||||
}
|
||||
|
||||
function getApiUrl(url: string) {
|
||||
@@ -191,11 +190,80 @@ export function joinClientParams(): string {
|
||||
return `token=${getToken()}&clientId=${getClientId()}`;
|
||||
}
|
||||
|
||||
export default {
|
||||
request,
|
||||
get,
|
||||
post,
|
||||
put,
|
||||
del,
|
||||
getApiUrl,
|
||||
};
|
||||
async function fetchReq(method: string, url: string, params: any = null, options: RequestInit = {}): Promise<any> {
|
||||
options.method = method;
|
||||
|
||||
if (params) {
|
||||
// post和put使用json格式传参
|
||||
if (method === 'post' || method === 'put') {
|
||||
options.body = JSON.stringify(params);
|
||||
} else {
|
||||
const searchParam = new URLSearchParams();
|
||||
Object.keys(params).forEach((key) => {
|
||||
const val = params[key];
|
||||
if (val) {
|
||||
searchParam.append(key, val);
|
||||
}
|
||||
});
|
||||
url = `${url}?${searchParam.toString()}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Part 1: Add headers and attach auth token
|
||||
const headers = new Headers(options.headers || {});
|
||||
|
||||
const token = getToken();
|
||||
if (token) {
|
||||
headers.set('Authorization', token);
|
||||
headers.set('ClientId', getClientId());
|
||||
}
|
||||
options.headers = headers;
|
||||
|
||||
try {
|
||||
const res: Response = await fetch(`${baseUrl}${url}`, options);
|
||||
if (!res.ok) {
|
||||
throw new Error(`请求响应错误: 状态码=${res.status}`);
|
||||
}
|
||||
|
||||
const jsonRes = await res.json();
|
||||
// 获取请求返回结果
|
||||
const result: Result = jsonRes;
|
||||
return parseResult(result);
|
||||
} catch (e: any) {
|
||||
const rejectPromise = Promise.reject(e);
|
||||
|
||||
if (e?.name == 'AbortError') {
|
||||
console.log('请求已取消');
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
if (e.message) {
|
||||
notifyErrorMsg(e.message);
|
||||
return rejectPromise;
|
||||
}
|
||||
|
||||
notifyErrorMsg('网络请求错误');
|
||||
console.error(e);
|
||||
return rejectPromise;
|
||||
}
|
||||
}
|
||||
|
||||
function parseResult(result: Result) {
|
||||
if (result.code === ResultEnum.SUCCESS) {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
// 如果提示没有权限,则移除token,使其重新登录
|
||||
if (result.code === ResultEnum.NO_PERMISSION) {
|
||||
router.push({
|
||||
path: '/401',
|
||||
});
|
||||
}
|
||||
|
||||
// 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
|
||||
if (result.msg && result?.code != ResultEnum.NO_PERMISSION) {
|
||||
notifyErrorMsg(result.msg);
|
||||
}
|
||||
|
||||
return Promise.reject(result);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import openApi from './openApi';
|
||||
|
||||
// 登录是否使用验证码配置key
|
||||
const AccountLoginSecurity = 'AccountLoginSecurity';
|
||||
const UseLoginCaptchaConfigKey = 'UseLoginCaptcha';
|
||||
const UseWatermarkConfigKey = 'UseWatermark';
|
||||
const MachineConfig = 'MachineConfig';
|
||||
|
||||
/**
|
||||
* 获取系统配置值
|
||||
@@ -43,15 +43,6 @@ export async function getAccountLoginSecurity(): Promise<any> {
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否使用登录验证码
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export async function useLoginCaptcha(): Promise<boolean> {
|
||||
return await getBoolConfigValue(UseLoginCaptchaConfigKey, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用水印信息配置
|
||||
*
|
||||
@@ -75,13 +66,6 @@ export async function useWatermark(): Promise<any> {
|
||||
}
|
||||
}
|
||||
|
||||
function convertBool(value: string, defaultValue: boolean) {
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value == '1' || value == 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取LDAP登录配置
|
||||
*
|
||||
@@ -91,3 +75,32 @@ export async function getLdapEnabled(): Promise<any> {
|
||||
const value = await openApi.getLdapEnabled();
|
||||
return convertBool(value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用水印信息配置
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export async function getMachineConfig(): Promise<any> {
|
||||
const value = await getConfigValue(MachineConfig);
|
||||
const defaultValue = {
|
||||
// 默认1gb
|
||||
uploadMaxFileSize: '1GB',
|
||||
};
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
const jsonValue = JSON.parse(value);
|
||||
return jsonValue;
|
||||
} catch (e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
function convertBool(value: string, defaultValue: boolean) {
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value == '1' || value == 'true';
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const vw = ref(document.documentElement.clientWidth);
|
||||
const vh = ref(document.documentElement.clientHeight);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
vw.value = document.documentElement.clientWidth;
|
||||
vh.value = document.documentElement.clientHeight;
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取视图宽高
|
||||
* @returns 视图宽高
|
||||
*/
|
||||
export function useViewport() {
|
||||
return { vw, vh };
|
||||
}
|
||||
@@ -15,6 +15,37 @@ export function formatByteSize(size: number, fixed = 2) {
|
||||
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
|
||||
}
|
||||
|
||||
/**
|
||||
* 容量转为对应的字节大小,如 1KB转为 1024
|
||||
* @param sizeString 1kb 1gb等
|
||||
* @returns
|
||||
*/
|
||||
export function convertToBytes(sizeStr: string) {
|
||||
sizeStr = sizeStr.trim();
|
||||
const unit = sizeStr.slice(-2);
|
||||
|
||||
const valueStr = sizeStr.slice(0, -2);
|
||||
const value = parseInt(valueStr, 10);
|
||||
|
||||
let bytes = 0;
|
||||
|
||||
switch (unit.toUpperCase()) {
|
||||
case 'KB':
|
||||
bytes = value * 1024;
|
||||
break;
|
||||
case 'MB':
|
||||
bytes = value * 1024 * 1024;
|
||||
break;
|
||||
case 'GB':
|
||||
bytes = value * 1024 * 1024 * 1024;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid size unit');
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化json字符串
|
||||
* @param txt json字符串
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
<script setup lang="ts" name="layoutTagsViewContextmenu">
|
||||
import { computed, reactive, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { ContextmenuItem } from './index';
|
||||
import { useViewport } from '@/common/use';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
@@ -57,7 +57,7 @@ const props = defineProps({
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['currentContextmenuClick']);
|
||||
|
||||
const { vw, vh } = useViewport();
|
||||
const { width: vw, height: vh } = useWindowSize();
|
||||
|
||||
// 定义变量内容
|
||||
const state = reactive({
|
||||
|
||||
@@ -1,212 +1,43 @@
|
||||
<template>
|
||||
<div class="dynamic-form">
|
||||
<el-form
|
||||
:model="form"
|
||||
ref="dynamicForm"
|
||||
:label-width="formInfo.labelWidth ? formInfo.labelWidth : '100px'"
|
||||
:size="formInfo.size ? formInfo.size : 'small'"
|
||||
>
|
||||
<el-row v-for="fr in formInfo.formRows" :key="fr.key">
|
||||
<el-col v-for="item in fr" :key="item.key" :span="item.span ? item.span : 24 / fr.length">
|
||||
<el-form-item :prop="item.name" :label="item.label" :label-width="item.labelWidth" :required="item.required" :rules="item.rules">
|
||||
<!-- input输入框 -->
|
||||
<el-input
|
||||
v-if="item.type === 'input'"
|
||||
v-model.trim="form[item.name]"
|
||||
:placeholder="item.placeholder"
|
||||
:type="item.inputType"
|
||||
clearable
|
||||
<div class="dynamic-form">
|
||||
<el-form v-bind="$attrs" ref="formRef" :model="formData" label-width="auto">
|
||||
<el-form-item v-for="item in formItems as any" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input v-if="!item.options" v-model="formData[item.model]" :placeholder="item.placeholder" autocomplete="off" clearable></el-input>
|
||||
|
||||
@change="item.change ? item.change(form) : ''"
|
||||
></el-input>
|
||||
|
||||
<!-- 普通文本信息(可用于不可修改字段等) -->
|
||||
<span v-else-if="item.type === 'text'">{{ form[item.name] }}</span>
|
||||
|
||||
<!-- select选择框 -->
|
||||
<!-- optionProps.label: 指定option中的label为options对象的某个属性值,默认就是label字段 -->
|
||||
<!-- optionProps.value: 指定option中的value为options对象的某个属性值,默认就是value字段 -->
|
||||
<el-select
|
||||
v-else-if="item.type === 'select'"
|
||||
v-model.trim="form[item.name]"
|
||||
:placeholder="item.placeholder"
|
||||
:filterable="item.filterable"
|
||||
:remote="item.remote"
|
||||
:remote-method="item.remoteMethod"
|
||||
@focus="item.focus ? item.focus(form) : ''"
|
||||
clearable
|
||||
:disabled="item.updateDisabled && form.id != null"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="i in item.options"
|
||||
:key="i.key"
|
||||
:label="i[item.optionProps ? item.optionProps.label || 'label' : 'label']"
|
||||
:value="i[item.optionProps ? item.optionProps.value || 'value' : 'value']"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row type="flex" justify="center">
|
||||
<slot name="btns" :submitDisabled="submitDisabled" :data="form" :submit="submit">
|
||||
<el-button @click="reset" size="small">重 置</el-button>
|
||||
<el-button type="primary" @click="submit" size="small">保 存</el-button>
|
||||
</slot>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-select v-else v-model="formData[item.model]" :placeholder="item.placeholder" filterable autocomplete="off" clearable style="width: 100%">
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { watch, ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DynamicForm',
|
||||
|
||||
props: {
|
||||
formInfo: { type: Object },
|
||||
formData: { type: [Object, Boolean] },
|
||||
},
|
||||
|
||||
setup(props: any, context) {
|
||||
const dynamicForm: any = ref();
|
||||
const state = reactive({
|
||||
form: {},
|
||||
submitDisabled: false,
|
||||
});
|
||||
|
||||
watch(props.formData, (newValue, oldValue) => {
|
||||
if (props.formData) {
|
||||
state.form = { ...props.formData };
|
||||
}
|
||||
});
|
||||
|
||||
const submit = () => {
|
||||
dynamicForm.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
// 提交的表单数据
|
||||
const subform = { ...state.form };
|
||||
const operation = state.form['id'] ? props.formInfo['updateApi'] : props.formInfo['createApi'];
|
||||
if (operation) {
|
||||
state.submitDisabled = true;
|
||||
operation.request(state.form).then(
|
||||
(res: any) => {
|
||||
ElMessage.success('保存成功');
|
||||
context.emit('submitSuccess', subform);
|
||||
state.submitDisabled = false;
|
||||
// this.cancel()
|
||||
},
|
||||
(e: any) => {
|
||||
state.submitDisabled = false;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ElMessage.error('表单未设置对应的提交权限');
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
context.emit('reset');
|
||||
resetFieldsAndData();
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单以及表单数据
|
||||
*/
|
||||
const resetFieldsAndData = () => {
|
||||
// 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
|
||||
const df: any = dynamicForm;
|
||||
df.resetFields();
|
||||
// 重置表单数据
|
||||
state.form = {};
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dynamicForm,
|
||||
submit,
|
||||
reset,
|
||||
resetFieldsAndData,
|
||||
};
|
||||
},
|
||||
const props = defineProps({
|
||||
formItems: { type: Array },
|
||||
modelValue: { type: Object },
|
||||
});
|
||||
// @Component({
|
||||
// name: 'DynamicForm'
|
||||
// })
|
||||
// export default class DynamicForm extends Vue {
|
||||
// @Prop()
|
||||
// formInfo: object
|
||||
// @Prop()
|
||||
// formData: [object,boolean]|undefined
|
||||
|
||||
// form = {}
|
||||
// submitDisabled = false
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
// @Watch('formData', { deep: true })
|
||||
// onRoleChange() {
|
||||
// if (this.formData) {
|
||||
// this.form = { ...this.formData }
|
||||
// }
|
||||
// }
|
||||
const formRef: any = ref();
|
||||
|
||||
// submit() {
|
||||
// const dynamicForm: any = this.$refs['dynamicForm']
|
||||
// dynamicForm.validate((valid: boolean) => {
|
||||
// if (valid) {
|
||||
// // 提交的表单数据
|
||||
// const subform = { ...this.form }
|
||||
// const operation = this.form['id']
|
||||
// ? this.formInfo['updateApi']
|
||||
// : this.formInfo['createApi']
|
||||
// if (operation) {
|
||||
// this.submitDisabled = true
|
||||
// operation.request(this.form).then(
|
||||
// (res: any) => {
|
||||
// ElMessage.success('保存成功')
|
||||
// this.$emit('submitSuccess', subform)
|
||||
// this.submitDisabled = false
|
||||
// // this.cancel()
|
||||
// },
|
||||
// (e: any) => {
|
||||
// this.submitDisabled = false
|
||||
// }
|
||||
// )
|
||||
// } else {
|
||||
// ElMessage.error('表单未设置对应的提交权限')
|
||||
// }
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
const formData: any = useVModel(props, 'modelValue', emit);
|
||||
|
||||
// reset() {
|
||||
// this.$emit('reset')
|
||||
// this.resetFieldsAndData()
|
||||
// }
|
||||
const validate = async (func: any) => {
|
||||
await formRef.value.validate(func);
|
||||
};
|
||||
|
||||
// /**
|
||||
// * 重置表单以及表单数据
|
||||
// */
|
||||
// resetFieldsAndData() {
|
||||
// // 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
|
||||
// const df: any = this.$refs['dynamicForm']
|
||||
// df.resetFields()
|
||||
// // 重置表单数据
|
||||
// this.form = {}
|
||||
// }
|
||||
const resetFields = () => {
|
||||
formRef.value.resetFields();
|
||||
};
|
||||
|
||||
// mounted() {
|
||||
// // 组件可能还没有初始化,第一次初始化的时候无法watch对象
|
||||
// this.form = { ...this.formData }
|
||||
// }
|
||||
|
||||
// }
|
||||
defineExpose({
|
||||
validate,
|
||||
resetFields,
|
||||
});
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
||||
@@ -1,60 +1,55 @@
|
||||
<template>
|
||||
<div class="form-dialog">
|
||||
<el-dialog :title="title" v-model="visible" :width="dialogWidth ? dialogWidth : '500px'">
|
||||
<dynamic-form ref="df" :form-info="formInfo" :form-data="formData" @submitSuccess="submitSuccess">
|
||||
<template #btns="props">
|
||||
<slot name="btns">
|
||||
<el-button :disabled="props.submitDisabled" type="primary" @click="props.submit" size="small">保 存</el-button>
|
||||
<el-button :disabled="props.submitDisabled" @click="close()" size="small">取 消</el-button>
|
||||
</slot>
|
||||
</template>
|
||||
</dynamic-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div class="form-dialog">
|
||||
<el-dialog @close="close" v-bind="$attrs" :title="title" v-model="dialogVisible" :width="width">
|
||||
<dynamic-form ref="df" :form-items="formItems" v-model="formData" />
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<slot name="btns">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirm">确 定</el-button>
|
||||
</slot>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { watch, ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import DynamicForm from './DynamicForm.vue';
|
||||
export default defineComponent({
|
||||
name: 'DynamicFormDialog',
|
||||
components: {
|
||||
DynamicForm,
|
||||
},
|
||||
|
||||
props: {
|
||||
visible: { type: Boolean },
|
||||
dialogWidth: { type: String },
|
||||
title: { type: String },
|
||||
formInfo: { type: Object },
|
||||
formData: { type: [Object, Boolean] },
|
||||
},
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
setup(props: any, context) {
|
||||
const df: any = ref();
|
||||
const emit = defineEmits(['update:visible', 'update:modelValue', 'close', 'confirm']);
|
||||
|
||||
const close = () => {
|
||||
// 更新父组件visible prop对应的值为false
|
||||
context.emit('update:visible', false);
|
||||
// 关闭窗口,则将表单数据置为null
|
||||
context.emit('update:formData', null);
|
||||
context.emit('close');
|
||||
// 取消动态表单的校验以及form数据
|
||||
setTimeout(() => {
|
||||
df.resetFieldsAndData();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const submitSuccess = (form: any) => {
|
||||
context.emit('submitSuccess', form);
|
||||
close();
|
||||
};
|
||||
|
||||
return {
|
||||
df,
|
||||
close,
|
||||
submitSuccess,
|
||||
};
|
||||
},
|
||||
const props = defineProps({
|
||||
title: { type: String },
|
||||
visible: { type: Boolean },
|
||||
width: { type: [String, Number], default: '500px' },
|
||||
formItems: { type: Array },
|
||||
modelValue: { type: Object },
|
||||
});
|
||||
|
||||
const df: any = ref();
|
||||
|
||||
const formData: any = useVModel(props, 'modelValue', emit);
|
||||
const dialogVisible: any = useVModel(props, 'visible', emit);
|
||||
|
||||
const close = () => {
|
||||
emit('close');
|
||||
// 取消动态表单的校验
|
||||
setTimeout(() => {
|
||||
formData.value = {};
|
||||
df.value.resetFields();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
df.value.validate((valid: any) => {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
emit('confirm', formData.value);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="dynamic-form-edit w100">
|
||||
<el-table :data="formItems" stripe class="w100" empty-text="暂无表单项">
|
||||
<el-table-column prop="name" label="model" min-width="100px">
|
||||
<template #header>
|
||||
<el-button class="ml0" type="primary" circle size="small" icon="Plus" @click="addItem()"> </el-button>
|
||||
<span class="ml10">model</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row['model']" placeholder="字段model" clearable> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="name" label="label" min-width="100px">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row['name']" placeholder="字段title" clearable> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="placeholder" label="字段说明" min-width="140px">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row['placeholder']" placeholder="字段说明" clearable> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="options" label="可选值" min-width="140px">
|
||||
<template #default="scope">
|
||||
<el-input v-model="scope.row['options']" placeholder="可选值 ,分割" clearable> </el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" wdith="20px">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" @click="deleteItem(scope.$index)" icon="delete" plain></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Array },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const formItems: any = useVModel(props, 'modelValue', emit);
|
||||
|
||||
const addItem = () => {
|
||||
formItems.value.push({});
|
||||
};
|
||||
|
||||
const deleteItem = (index: any) => {
|
||||
formItems.value.splice(index, 1);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -1,2 +1,3 @@
|
||||
export { default as DynamicForm } from './DynamicForm.vue';
|
||||
export { default as DynamicFormDialog } from './DynamicFormDialog.vue';
|
||||
export { default as DynamicFormDialog } from './DynamicFormDialog.vue';
|
||||
export { default as DynamicFormEdit } from './DynamicFormEdit.vue';
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div class="layout-navbars-breadcrumb-user" :style="{ flex: layoutUserFlexNum }">
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<el-switch
|
||||
@change="switchDark(state.isDark)"
|
||||
v-model="state.isDark"
|
||||
@change="switchDark()"
|
||||
v-model="isDark"
|
||||
active-action-icon="Moon"
|
||||
inactive-action-icon="Sunny"
|
||||
style="--el-switch-off-color: #c4c9c4; --el-switch-on-color: #2c2c2c"
|
||||
@@ -75,7 +75,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutBreadcrumbUser">
|
||||
import { ref, computed, reactive, onMounted } from 'vue';
|
||||
import { ref, computed, reactive, onMounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import screenfull from 'screenfull';
|
||||
@@ -83,17 +83,17 @@ import { resetRoute } from '@/router/index';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { clearSession, removeLocal } from '@/common/utils/storage';
|
||||
import { clearSession } from '@/common/utils/storage';
|
||||
import UserNews from '@/layout/navBars/breadcrumb/userNews.vue';
|
||||
import SearchMenu from '@/layout/navBars/breadcrumb/search.vue';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
import openApi from '@/common/openApi';
|
||||
import { saveThemeConfig, getThemeConfig } from '@/common/utils/storage';
|
||||
import { useDark, usePreferredDark } from '@vueuse/core';
|
||||
|
||||
const router = useRouter();
|
||||
const searchRef = ref();
|
||||
const state = reactive({
|
||||
isDark: false,
|
||||
isScreenfull: false,
|
||||
isShowUserNewsPopover: false,
|
||||
disabledI18n: 'zh-cn',
|
||||
@@ -165,8 +165,21 @@ const onHandleCommandClick = (path: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const switchDark = (isDark: boolean) => {
|
||||
themeConfigStore.switchDark(isDark);
|
||||
const isDark = useDark();
|
||||
const preDark = usePreferredDark();
|
||||
|
||||
watch(preDark, (newValue) => {
|
||||
isDark.value = newValue;
|
||||
switchDark();
|
||||
});
|
||||
|
||||
const switchDark = () => {
|
||||
themeConfig.value.isDark = isDark.value;
|
||||
if (isDark.value) {
|
||||
themeConfig.value.editorTheme = 'vs-dark';
|
||||
} else {
|
||||
themeConfig.value.editorTheme = 'vs';
|
||||
}
|
||||
saveThemeConfig(themeConfig.value);
|
||||
};
|
||||
|
||||
@@ -176,14 +189,14 @@ const onSearchClick = () => {
|
||||
};
|
||||
|
||||
// 组件大小改变
|
||||
const onComponentSizeChange = (size: string) => {
|
||||
removeLocal('themeConfig');
|
||||
themeConfig.value.globalComponentSize = size;
|
||||
saveThemeConfig(themeConfig.value);
|
||||
// proxy.$ELEMENT.size = size;
|
||||
initComponentSize();
|
||||
window.location.reload();
|
||||
};
|
||||
// const onComponentSizeChange = (size: string) => {
|
||||
// removeLocal('themeConfig');
|
||||
// themeConfig.value.globalComponentSize = size;
|
||||
// saveThemeConfig(themeConfig.value);
|
||||
// // proxy.$ELEMENT.size = size;
|
||||
// initComponentSize();
|
||||
// window.location.reload();
|
||||
// };
|
||||
|
||||
// 初始化全局组件大小
|
||||
const initComponentSize = () => {
|
||||
@@ -208,7 +221,7 @@ onMounted(() => {
|
||||
const themeConfig = getThemeConfig();
|
||||
if (themeConfig) {
|
||||
initComponentSize();
|
||||
state.isDark = themeConfig.isDark;
|
||||
isDark.value = themeConfig.isDark;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -146,18 +146,6 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
setThemeConfig(data: ThemeConfigState) {
|
||||
this.themeConfig = data.themeConfig;
|
||||
},
|
||||
// 切换暗模式
|
||||
switchDark(isDark: boolean) {
|
||||
this.themeConfig.isDark = isDark;
|
||||
const body = document.documentElement as HTMLElement;
|
||||
if (isDark) {
|
||||
body.setAttribute('class', 'dark');
|
||||
this.themeConfig.editorTheme = 'vs-dark';
|
||||
} else {
|
||||
body.setAttribute('class', '');
|
||||
this.themeConfig.editorTheme = 'vs';
|
||||
}
|
||||
},
|
||||
// 设置水印配置信息
|
||||
setWatermarkConfig(useWatermarkConfig: any) {
|
||||
this.themeConfig.watermarkText = [];
|
||||
|
||||
@@ -46,8 +46,8 @@ import { onMounted, reactive, ref, watch, toRefs, onUnmounted } from 'vue';
|
||||
import { NodeType, TagTreeNode } from './tag';
|
||||
import TagInfo from './TagInfo.vue';
|
||||
import { Contextmenu } from '@/components/contextmenu';
|
||||
import { useViewport } from '@/common/use';
|
||||
import { tagApi } from '../tag/api';
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
resourceType: {
|
||||
@@ -78,7 +78,7 @@ const emit = defineEmits(['nodeClick', 'currentContextmenuClick']);
|
||||
const treeRef: any = ref(null);
|
||||
const contextmenuRef = ref();
|
||||
|
||||
const { vh } = useViewport();
|
||||
const { height: vh } = useWindowSize();
|
||||
|
||||
const state = reactive({
|
||||
height: 600 as any,
|
||||
|
||||
@@ -45,14 +45,20 @@
|
||||
<el-button v-auth="perms.delDb" :disabled="selectionData.length < 1" @click="deleteDb()" type="danger" icon="delete">删除</el-button>
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
<resource-tag :resource-code="data.code" :resource-type="TagResourceTypeEnum.Db.value" />
|
||||
<template #type="{ data }">
|
||||
<el-tooltip :content="data.type" placement="top">
|
||||
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<template #host="{ data }">
|
||||
{{ `${data.host}:${data.port}` }}
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
<resource-tag :resource-code="data.code" :resource-type="TagResourceTypeEnum.Db.value" />
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<span v-if="actionBtns[perms.saveDb]">
|
||||
<el-button type="primary" @click="editDb(data)" link>编辑</el-button>
|
||||
@@ -173,6 +179,7 @@ import { DbType } from './dialect';
|
||||
import { tagApi } from '../tag/api';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getDbDialect } from './dialect/index';
|
||||
|
||||
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
|
||||
|
||||
@@ -186,7 +193,7 @@ const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect'), Tabl
|
||||
|
||||
const columns = ref([
|
||||
TableColumn.new('instanceName', '实例名'),
|
||||
TableColumn.new('type', '类型'),
|
||||
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
|
||||
TableColumn.new('host', 'ip:port').isSlot().setAddWidth(40),
|
||||
TableColumn.new('username', 'username'),
|
||||
TableColumn.new('name', '名称'),
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
>
|
||||
</template>
|
||||
|
||||
<template #type="{ data }">
|
||||
<el-tooltip :content="data.type" placement="top">
|
||||
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button @click="showInfo(data)" link>详情</el-button>
|
||||
<el-button v-if="actionBtns[perms.saveInstance]" @click="editInstance(data)" type="primary" link>编辑</el-button>
|
||||
@@ -66,6 +72,8 @@ import { dateFormat } from '@/common/utils/date';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn, TableQuery } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { getDbDialect } from './dialect';
|
||||
|
||||
const InstanceEdit = defineAsyncComponent(() => import('./InstanceEdit.vue'));
|
||||
|
||||
@@ -78,7 +86,7 @@ const queryConfig = [TableQuery.text('name', '名称')];
|
||||
|
||||
const columns = ref([
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('type', '类型'),
|
||||
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
|
||||
TableColumn.new('host', 'host:port').setFormatFunc((data: any) => `${data.host}:${data.port}`),
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('params', '连接参数'),
|
||||
|
||||
@@ -21,7 +21,6 @@ export const dbApi = {
|
||||
param.sql = Base64.encode(param.sql);
|
||||
}
|
||||
}),
|
||||
sqlExecCancel: Api.newPost('/dbs/{id}/exec-sql/cancel/{execId}'),
|
||||
// 保存sql
|
||||
saveSql: Api.newPost('/dbs/{id}/sql'),
|
||||
// 获取保存的sql
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<div style="float: right" class="fl">
|
||||
<div class="fr">
|
||||
<el-button @click="saveSql()" type="primary" icon="document-add" plain size="small">保存SQL</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -128,7 +128,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, nextTick, onMounted, reactive, toRefs, ref } from 'vue';
|
||||
import { h, nextTick, onMounted, reactive, toRefs, ref, onBeforeUnmount } from 'vue';
|
||||
import { getToken } from '@/common/utils/storage';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { format as sqlFormatter } from 'sql-formatter';
|
||||
@@ -150,8 +150,8 @@ import { ElNotification } from 'element-plus';
|
||||
import syssocket from '@/common/syssocket';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { getDbDialect } from '../../dialect';
|
||||
import { randomUuid } from '@/common/utils/string';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
import Api from '@/common/Api';
|
||||
|
||||
const emits = defineEmits(['saveSqlSuccess']);
|
||||
|
||||
@@ -252,6 +252,12 @@ onMounted(async () => {
|
||||
await getNowDbInst().loadDbHints(props.dbName);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
state.execResTabs.forEach((x: ExecResTab) => {
|
||||
Api.removeAbortKey(x.loadingKey);
|
||||
});
|
||||
});
|
||||
|
||||
const onRemoveTab = (targetId: number) => {
|
||||
let activeTab = state.activeTab;
|
||||
const tabs = [...state.execResTabs];
|
||||
@@ -347,7 +353,8 @@ const onRunSql = async (newTab = false) => {
|
||||
execRes.errorMsg = '';
|
||||
execRes.sql = '';
|
||||
|
||||
const loadingKey = randomUuid();
|
||||
// 用于取消执行
|
||||
const loadingKey = Api.genAbortKey(execRes.loadingKey);
|
||||
execRes.loadingKey = loadingKey;
|
||||
|
||||
const colAndData: any = await getNowDbInst().runSql(props.dbName, sql, execRemark, loadingKey);
|
||||
@@ -397,7 +404,7 @@ const onRunSql = async (newTab = false) => {
|
||||
const getSql = () => {
|
||||
let res = '' as string | undefined;
|
||||
// 编辑器还没初始化
|
||||
if (!monacoEditor?.getModel) {
|
||||
if (!monacoEditor?.getModel()) {
|
||||
return res;
|
||||
}
|
||||
// 选择选中的sql
|
||||
@@ -405,6 +412,7 @@ const getSql = () => {
|
||||
if (selection) {
|
||||
res = monacoEditor.getModel()?.getValueInRange(selection);
|
||||
}
|
||||
|
||||
// 整个编辑器的sql
|
||||
if (!res) {
|
||||
return monacoEditor.getModel()?.getValue();
|
||||
|
||||
@@ -133,7 +133,8 @@ import { ContextmenuItem, Contextmenu } from '@/components/contextmenu';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { exportCsv, exportFile } from '@/common/utils/export';
|
||||
import { dateStrFormat } from '@/common/utils/date';
|
||||
import { dbApi } from '../../api';
|
||||
import Api from '@/common/Api';
|
||||
import { useIntervalFn } from '@vueuse/core';
|
||||
|
||||
const emits = defineEmits(['dataDelete', 'sortChange', 'deleteData', 'selectionChange', 'changeUpdatedField']);
|
||||
|
||||
@@ -285,7 +286,9 @@ const selectionRowsMap: Map<number, any> = new Map();
|
||||
const cellUpdateMap: Map<number, UpdatedRow> = new Map();
|
||||
|
||||
// 数据加载时间计时器
|
||||
let execTimeInterval: any = null;
|
||||
const { pause, resume } = useIntervalFn(() => {
|
||||
state.execTime += 0.1;
|
||||
}, 100);
|
||||
|
||||
const state = reactive({
|
||||
dbId: 0, // 当前选中操作的数据库实例
|
||||
@@ -429,22 +432,17 @@ const setTableColumns = (columns: any) => {
|
||||
};
|
||||
|
||||
const startLoading = () => {
|
||||
if (execTimeInterval) {
|
||||
endLoading();
|
||||
}
|
||||
execTimeInterval = setInterval(() => {
|
||||
state.execTime += 0.1; // 每秒递增执行时间
|
||||
}, 100);
|
||||
state.execTime = 0;
|
||||
resume();
|
||||
};
|
||||
|
||||
const endLoading = () => {
|
||||
state.execTime = 0;
|
||||
clearInterval(execTimeInterval);
|
||||
pause();
|
||||
};
|
||||
|
||||
const cancelLoading = async () => {
|
||||
if (props.loadingKey) {
|
||||
await dbApi.sqlExecCancel.request({ id: state.dbId, execId: props.loadingKey });
|
||||
Api.cancelReq(props.loadingKey);
|
||||
endLoading();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -197,9 +197,17 @@ export class DbInst {
|
||||
* @param remark 执行备注
|
||||
*/
|
||||
async runSql(dbName: string, sql: string, remark: string = '', key: string = '') {
|
||||
if (key) {
|
||||
return await dbApi.sqlExec.allowCancelReq(key, {
|
||||
id: this.id,
|
||||
db: dbName,
|
||||
sql: sql.trim(),
|
||||
remark,
|
||||
});
|
||||
}
|
||||
|
||||
return await dbApi.sqlExec.request({
|
||||
id: this.id,
|
||||
execId: key,
|
||||
db: dbName,
|
||||
sql: sql.trim(),
|
||||
remark,
|
||||
|
||||
@@ -24,39 +24,17 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row style="margin-left: 30px; margin-bottom: 5px">
|
||||
<el-button @click="onAddParam" type="success">新增占位符参数</el-button>
|
||||
</el-row>
|
||||
<el-form-item :key="param" v-for="(param, index) in params" prop="params" :label="`参数${index + 1}`">
|
||||
<el-row>
|
||||
<el-col :span="5">
|
||||
<el-input v-model.trim="param.model" placeholder="内容中用{{.model}}替换"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model.trim="param.name" placeholder="字段名"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="2">
|
||||
<el-button @click="onDeleteParam(index)" type="danger">删除</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item class="w100">
|
||||
<template #label>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<span v-pre>1. 脚本内容中可使用{{.model}}作为占位符 </span>
|
||||
<br />2. 执行脚本时可输入对应表单内容对占位符进行替换后执行
|
||||
</template>
|
||||
<span> 参数<SvgIcon name="question-filled" /> </span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<dynamic-form-edit v-model="params" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item required prop="script" class="100w">
|
||||
@@ -82,6 +60,8 @@ import { ElMessage } from 'element-plus';
|
||||
import { machineApi } from './api';
|
||||
import { ScriptResultEnum } from './enums';
|
||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||
import { DynamicFormEdit } from '@/components/dynamic-form';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -171,14 +151,6 @@ watch(props, (newValue: any) => {
|
||||
}
|
||||
});
|
||||
|
||||
const onAddParam = () => {
|
||||
state.params.push({ name: '', model: '', placeholder: '' });
|
||||
};
|
||||
|
||||
const onDeleteParam = (idx: number) => {
|
||||
state.params.splice(idx, 1);
|
||||
};
|
||||
|
||||
const btnOk = () => {
|
||||
state.form.machineId = isCommon.value ? 9999999 : (machineId?.value as any);
|
||||
scriptForm.value.validate((valid: any) => {
|
||||
|
||||
@@ -45,35 +45,16 @@
|
||||
</page-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="脚本参数" v-model="scriptParamsDialog.visible" width="400px">
|
||||
<el-form ref="paramsForm" :model="scriptParamsDialog.params" label-width="auto">
|
||||
<el-form-item v-for="item in scriptParamsDialog.paramsFormItem as any" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input
|
||||
v-if="!item.options"
|
||||
v-model="scriptParamsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="scriptParamsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
filterable
|
||||
autocomplete="off"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="hasParamsRun()">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<dynamic-form-dialog
|
||||
title="脚本参数"
|
||||
width="400px"
|
||||
v-model:visible="scriptParamsDialog.visible"
|
||||
ref="paramsForm"
|
||||
:form-items="scriptParamsDialog.paramsFormItem"
|
||||
v-model="scriptParamsDialog.params"
|
||||
@confirm="hasParamsRun"
|
||||
>
|
||||
</dynamic-form-dialog>
|
||||
|
||||
<el-dialog title="执行结果" v-model="resultDialog.visible" width="50%">
|
||||
<div style="white-space: pre-line; padding: 10px; color: #000000">
|
||||
@@ -115,6 +96,7 @@ import { ScriptResultEnum, ScriptTypeEnum } from './enums';
|
||||
import ScriptEdit from './ScriptEdit.vue';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn, TableQuery } from '@/components/pagetable';
|
||||
import { DynamicFormDialog } from '@/components/dynamic-form';
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean },
|
||||
@@ -204,20 +186,9 @@ const runScript = async (script: any) => {
|
||||
|
||||
// 有参数的脚本执行函数
|
||||
const hasParamsRun = async () => {
|
||||
// 如果脚本参数弹窗显示,则校验参数表单数据通过后执行
|
||||
if (state.scriptParamsDialog.visible) {
|
||||
paramsForm.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
run(state.scriptParamsDialog.script);
|
||||
state.scriptParamsDialog.params = {};
|
||||
state.scriptParamsDialog.visible = false;
|
||||
state.scriptParamsDialog.script = null;
|
||||
paramsForm.value.resetFields();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
await run(state.scriptParamsDialog.script);
|
||||
state.scriptParamsDialog.visible = false;
|
||||
state.scriptParamsDialog.script = null;
|
||||
};
|
||||
|
||||
const run = async (script: any) => {
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="uploadSuccess"
|
||||
action=""
|
||||
:http-request="getUploadFile"
|
||||
:http-request="uploadFile"
|
||||
:headers="{ token }"
|
||||
:show-file-list="false"
|
||||
name="file"
|
||||
@@ -68,7 +68,7 @@
|
||||
ref="folderUploadRef"
|
||||
webkitdirectory
|
||||
directory
|
||||
@change="getFolder"
|
||||
@change="uploadFolder"
|
||||
style="display: none"
|
||||
/>
|
||||
</div>
|
||||
@@ -173,7 +173,7 @@
|
||||
|
||||
<el-table-column prop="size" label="大小" width="100" sortable>
|
||||
<template #default="scope">
|
||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatFileSize(scope.row.size) }} </span>
|
||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == '-'"> {{ formatByteSize(scope.row.size) }} </span>
|
||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && scope.row.dirSize"> {{ scope.row.dirSize }} </span>
|
||||
<span style="color: #67c23a; font-weight: bold" v-if="scope.row.type == 'd' && !scope.row.dirSize">
|
||||
<el-button @click="getDirSize(scope.row)" type="primary" link :loading="scope.row.loadingDirSize">计算</el-button>
|
||||
@@ -280,6 +280,8 @@ import { isTrue } from '@/common/assert';
|
||||
import MachineFileContent from './MachineFileContent.vue';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { getToken } from '@/common/utils/storage';
|
||||
import { formatByteSize, convertToBytes } from '@/common/utils/format';
|
||||
import { getMachineConfig } from '@/common/sysconfig';
|
||||
|
||||
const props = defineProps({
|
||||
machineId: { type: Number },
|
||||
@@ -326,14 +328,15 @@ const state = reactive({
|
||||
type: folderType,
|
||||
data: null as any,
|
||||
},
|
||||
file: null as any,
|
||||
machineConfig: { uploadMaxFileSize: '1GB' },
|
||||
});
|
||||
|
||||
const { basePath, nowPath, loading, fileNameFilter, progressNum, uploadProgressShow, fileContent, createFileDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
state.basePath = props.path;
|
||||
setFiles(props.path);
|
||||
state.machineConfig = await getMachineConfig();
|
||||
});
|
||||
|
||||
const filterFiles = computed(() =>
|
||||
@@ -616,16 +619,24 @@ function addFinderToList() {
|
||||
folderUploadRef.value.click();
|
||||
}
|
||||
|
||||
function getFolder(e: any) {
|
||||
function uploadFolder(e: any) {
|
||||
//e.target.files为文件夹里面的文件
|
||||
// 把文件夹数据放到formData里面,下面的files和paths字段根据接口来定
|
||||
var form = new FormData();
|
||||
form.append('basePath', state.nowPath);
|
||||
|
||||
let totalFileSize = 0;
|
||||
for (let file of e.target.files) {
|
||||
totalFileSize += file.size;
|
||||
form.append('files', file);
|
||||
form.append('paths', file.webkitRelativePath);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!checkUploadFileSize(totalFileSize)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 上传操作
|
||||
machineApi.uploadFile
|
||||
.request(form, {
|
||||
@@ -660,7 +671,7 @@ const onUploadProgress = (progressEvent: any) => {
|
||||
state.progressNum = complete;
|
||||
};
|
||||
|
||||
const getUploadFile = (content: any) => {
|
||||
const uploadFile = (content: any) => {
|
||||
const params = new FormData();
|
||||
const path = state.nowPath;
|
||||
params.append('file', content.file);
|
||||
@@ -695,7 +706,16 @@ const uploadSuccess = (res: any) => {
|
||||
};
|
||||
|
||||
const beforeUpload = (file: File) => {
|
||||
state.file = file;
|
||||
return checkUploadFileSize(file.size);
|
||||
};
|
||||
|
||||
const checkUploadFileSize = (fileSize: number) => {
|
||||
const bytes = convertToBytes(state.machineConfig.uploadMaxFileSize);
|
||||
if (fileSize > bytes) {
|
||||
ElMessage.error(`上传的文件超过系统配置的[${state.machineConfig.uploadMaxFileSize}]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const dontOperate = (data: any) => {
|
||||
@@ -704,27 +724,6 @@ const dontOperate = (data: any) => {
|
||||
return ls.indexOf(path) != -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param {*} value
|
||||
*/
|
||||
const formatFileSize = (size: any) => {
|
||||
const value = Number(size);
|
||||
if (size && !isNaN(value)) {
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
|
||||
let index = 0;
|
||||
let k = value;
|
||||
if (value >= 1024) {
|
||||
while (k > 1024) {
|
||||
k = k / 1024;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return `${k.toFixed(2)}${units[index]}`;
|
||||
}
|
||||
return '-';
|
||||
};
|
||||
|
||||
defineExpose({ showFileContent });
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="750px" :destroy-on-close="true">
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="900px" :destroy-on-close="true">
|
||||
<el-form ref="configForm" :model="form" label-width="auto">
|
||||
<el-form-item prop="name" label="配置项" required>
|
||||
<el-input v-model="form.name"></el-input>
|
||||
@@ -22,43 +22,10 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row style="margin-left: 30px; margin-bottom: 5px">
|
||||
<el-button @click="onAddParam" size="small" type="success">新增配置项</el-button>
|
||||
</el-row>
|
||||
<el-form-item :key="param" v-for="(param, index) in params" prop="params" :label="`参数${index + 1}`">
|
||||
<el-row>
|
||||
<el-col :span="5">
|
||||
<el-input v-model="param.model" placeholder="model"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.name" placeholder="字段名"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.placeholder" placeholder="字段说明"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||
</el-col>
|
||||
<span :span="1">
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
<el-col :span="2">
|
||||
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="配置项" class="w100">
|
||||
<dynamic-form-edit v-model="params" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="value" label="配置值:" required>
|
||||
<el-input v-model="form.value"></el-input>
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||
</el-form-item>
|
||||
@@ -76,6 +43,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, watch } from 'vue';
|
||||
import { configApi, accountApi } from '../api';
|
||||
import { DynamicFormEdit } from '@/components/dynamic-form';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
@@ -139,14 +107,6 @@ watch(props, (newValue: any) => {
|
||||
}
|
||||
});
|
||||
|
||||
const onAddParam = () => {
|
||||
state.params.push({ name: '', model: '', placeholder: '' });
|
||||
};
|
||||
|
||||
const onDeleteParam = (idx: number) => {
|
||||
state.params.splice(idx, 1);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
// 更新父组件visible prop对应的值为false
|
||||
emit('update:visible', false);
|
||||
|
||||
@@ -25,41 +25,20 @@
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog :before-close="closeSetConfigDialog" title="配置项设置" v-model="paramsDialog.visible" width="600px">
|
||||
<el-form v-if="paramsDialog.paramsFormItem.length > 0" ref="paramsFormRef" :model="paramsDialog.params" label-width="auto">
|
||||
<el-form-item v-for="item in paramsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input
|
||||
v-if="!item.options && !item.type"
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-checkbox
|
||||
v-else-if="item.type == 'checkbox'"
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
autocomplete="off"
|
||||
:label="item.placeholder"
|
||||
clearable
|
||||
/>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
filterable
|
||||
autocomplete="off"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-dialog @close="closeSetConfigDialog" title="配置项设置" v-model="paramsDialog.visible" width="600px">
|
||||
<dynamic-form
|
||||
ref="paramsFormRef"
|
||||
v-if="paramsDialog.paramsFormItem.length > 0"
|
||||
:form-items="paramsDialog.paramsFormItem"
|
||||
v-model="paramsDialog.params"
|
||||
/>
|
||||
|
||||
<el-form v-else ref="paramsFormRef" label-width="auto">
|
||||
<el-form-item label="配置值" required>
|
||||
<el-input v-model="paramsDialog.params" :placeholder="paramsDialog.config.remark" autocomplete="off" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeSetConfigDialog()">取 消</el-button>
|
||||
@@ -80,6 +59,7 @@ import { ElMessage } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import { DynamicForm } from '@/components/dynamic-form';
|
||||
|
||||
const perms = {
|
||||
saveConfig: 'config:save',
|
||||
@@ -163,7 +143,7 @@ const closeSetConfigDialog = () => {
|
||||
const setConfig = async () => {
|
||||
let paramsValue = state.paramsDialog.params;
|
||||
if (state.paramsDialog.paramsFormItem.length > 0) {
|
||||
await paramsFormRef.value.validate(async (valid: boolean) => {
|
||||
await paramsFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) {
|
||||
paramsValue = null as any;
|
||||
return false;
|
||||
|
||||
@@ -345,6 +345,11 @@
|
||||
resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz"
|
||||
integrity sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==
|
||||
|
||||
"@types/web-bluetooth@^0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
|
||||
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^6.7.4":
|
||||
version "6.7.4"
|
||||
resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz#057338df21b6062c2f2fc5999fbea8af9973ac6d"
|
||||
@@ -445,6 +450,16 @@
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@vue/compiler-core@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.11.tgz#9fa26f8c81b9b34365f94ce1ed4d0e6e6f94a2ac"
|
||||
integrity sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/shared" "3.3.11"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@vue/compiler-dom@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.10.tgz#183811252be6aff4ac923f783124bb1590301907"
|
||||
@@ -453,7 +468,31 @@
|
||||
"@vue/compiler-core" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
|
||||
"@vue/compiler-sfc@3.3.10", "@vue/compiler-sfc@^3.3.10":
|
||||
"@vue/compiler-dom@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz#36a76ea3a296d41bad133a6912cb0a847d969e4f"
|
||||
integrity sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/compiler-sfc@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz#acfae240c875d067e0e2c9a4e2d910074408c73b"
|
||||
integrity sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/compiler-core" "3.3.11"
|
||||
"@vue/compiler-dom" "3.3.11"
|
||||
"@vue/compiler-ssr" "3.3.11"
|
||||
"@vue/reactivity-transform" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
postcss "^8.4.32"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@vue/compiler-sfc@^3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.10.tgz#8eb97d42f276089ec58fd0565ef3a813bceeaa87"
|
||||
integrity sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==
|
||||
@@ -477,6 +516,14 @@
|
||||
"@vue/compiler-dom" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
|
||||
"@vue/compiler-ssr@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz#598942a73b64f2bd3f95908b104a7fbb55fc41a2"
|
||||
integrity sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/devtools-api@^6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
|
||||
@@ -493,43 +540,69 @@
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
|
||||
"@vue/reactivity@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.10.tgz#78fe3da319276d9e6d0f072037532928c472a287"
|
||||
integrity sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==
|
||||
"@vue/reactivity-transform@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz#2bd486f4eff60c8724309925618891e722fcfadc"
|
||||
integrity sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==
|
||||
dependencies:
|
||||
"@vue/shared" "3.3.10"
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/compiler-core" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
|
||||
"@vue/runtime-core@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.10.tgz#d7b78c5c0500b856cf9447ef81d4a1b1438fd5bb"
|
||||
integrity sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==
|
||||
"@vue/reactivity@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.11.tgz#91f8e6c9ac60a595a5278c836b197628fd947a0d"
|
||||
integrity sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/runtime-dom@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.10.tgz#130dfffb8fee8051671aaf80c5104d2020544950"
|
||||
integrity sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==
|
||||
"@vue/runtime-core@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.11.tgz#63defba57bc54c1dac68a95b56c2633b1419193d"
|
||||
integrity sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==
|
||||
dependencies:
|
||||
"@vue/runtime-core" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
"@vue/reactivity" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/runtime-dom@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz#1146d8d280b0fec4d2e18c4a4c8f8121d0cecc09"
|
||||
integrity sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==
|
||||
dependencies:
|
||||
"@vue/runtime-core" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
csstype "^3.1.2"
|
||||
|
||||
"@vue/server-renderer@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.10.tgz#f23d151f0e5021ebdc730052d9934c9178486742"
|
||||
integrity sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==
|
||||
"@vue/server-renderer@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.11.tgz#409aed8031a125791e2143552975ecd1958ad601"
|
||||
integrity sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
"@vue/compiler-ssr" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/shared@3.3.10":
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.10.tgz#1583a8d85a957d8b819078c465d2a11db7914b2f"
|
||||
integrity sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==
|
||||
|
||||
"@vue/shared@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.11.tgz#f6a038e15237edefcc90dbfe7edb806dd355c7bd"
|
||||
integrity sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==
|
||||
|
||||
"@vueuse/core@^10.7.0":
|
||||
version "10.7.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/core/-/core-10.7.0.tgz#34f2f02f179dc0dcffc2be70d6b1233e011404b9"
|
||||
integrity sha512-4EUDESCHtwu44ZWK3Gc/hZUVhVo/ysvdtwocB5vcauSV4B7NiGY5972WnsojB3vRNdxvAt7kzJWE2h9h7C9d5w==
|
||||
dependencies:
|
||||
"@types/web-bluetooth" "^0.0.20"
|
||||
"@vueuse/metadata" "10.7.0"
|
||||
"@vueuse/shared" "10.7.0"
|
||||
vue-demi ">=0.14.6"
|
||||
|
||||
"@vueuse/core@^9.1.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/core/-/core-9.2.0.tgz"
|
||||
@@ -540,11 +613,23 @@
|
||||
"@vueuse/shared" "9.2.0"
|
||||
vue-demi "*"
|
||||
|
||||
"@vueuse/metadata@10.7.0":
|
||||
version "10.7.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.7.0.tgz#7b05e6cfd376aa9bb339a81e16a89c12f3e88c03"
|
||||
integrity sha512-GlaH7tKP2iBCZ3bHNZ6b0cl9g0CJK8lttkBNUX156gWvNYhTKEtbweWLm9rxCPIiwzYcr/5xML6T8ZUEt+DkvA==
|
||||
|
||||
"@vueuse/metadata@9.2.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.2.0.tgz"
|
||||
integrity sha512-exN4KE6iquxDCdt72BgEhb3tlOpECtD61AUdXnUqBTIUCl70x1Ar/QXo3bYcvxmdMS2/peQyfeTzBjRTpvL5xw==
|
||||
|
||||
"@vueuse/shared@10.7.0":
|
||||
version "10.7.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.7.0.tgz#21e425cc5ede421e0cda38ac59a0beee6da86b1b"
|
||||
integrity sha512-kc00uV6CiaTdc3i1CDC4a3lBxzaBE9AgYNtFN87B5OOscqeWElj/uza8qVDmk7/U8JbqoONLbtqiLJ5LGRuqlw==
|
||||
dependencies:
|
||||
vue-demi ">=0.14.6"
|
||||
|
||||
"@vueuse/shared@9.2.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.2.0.tgz"
|
||||
@@ -1419,10 +1504,10 @@ mitt@^3.0.1:
|
||||
resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1"
|
||||
integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
|
||||
|
||||
monaco-editor@^0.44.0:
|
||||
version "0.44.0"
|
||||
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.44.0.tgz#3c0fe3655923bbf7dd647057302070b5095b6c59"
|
||||
integrity sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==
|
||||
monaco-editor@^0.45.0:
|
||||
version "0.45.0"
|
||||
resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.45.0.tgz#6939123a6254aea9fea2d647697f846306dd4448"
|
||||
integrity sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA==
|
||||
|
||||
monaco-sql-languages@^0.11.0:
|
||||
version "0.11.0"
|
||||
@@ -1859,10 +1944,10 @@ uuid@^9.0.1:
|
||||
resolved "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||
|
||||
vite@^5.0.5:
|
||||
version "5.0.5"
|
||||
resolved "https://registry.npmmirror.com/vite/-/vite-5.0.5.tgz#3eebe3698e3b32cea36350f58879258fec858a3c"
|
||||
integrity sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==
|
||||
vite@^5.0.7:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.npmmirror.com/vite/-/vite-5.0.7.tgz#ad081d735f6769f76b556818500bdafb72c3fe93"
|
||||
integrity sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==
|
||||
dependencies:
|
||||
esbuild "^0.19.3"
|
||||
postcss "^8.4.32"
|
||||
@@ -1880,6 +1965,11 @@ vue-demi@>=0.14.5:
|
||||
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.5.tgz#676d0463d1a1266d5ab5cba932e043d8f5f2fbd9"
|
||||
integrity sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==
|
||||
|
||||
vue-demi@>=0.14.6:
|
||||
version "0.14.6"
|
||||
resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz#dc706582851dc1cdc17a0054f4fec2eb6df74c92"
|
||||
integrity sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==
|
||||
|
||||
vue-eslint-parser@^9.3.1:
|
||||
version "9.3.1"
|
||||
resolved "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz#429955e041ae5371df5f9e37ebc29ba046496182"
|
||||
@@ -1913,16 +2003,16 @@ vue-router@^4.2.5:
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue@^3.3.10:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.npmmirror.com/vue/-/vue-3.3.10.tgz#6e19c1982ee655a14babe1610288b90005f02ab1"
|
||||
integrity sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==
|
||||
vue@^3.3.11:
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/vue/-/vue-3.3.11.tgz#898d97025f73cdb5fc4e3ae3fd07a54615232140"
|
||||
integrity sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.3.10"
|
||||
"@vue/compiler-sfc" "3.3.10"
|
||||
"@vue/runtime-dom" "3.3.10"
|
||||
"@vue/server-renderer" "3.3.10"
|
||||
"@vue/shared" "3.3.10"
|
||||
"@vue/compiler-dom" "3.3.11"
|
||||
"@vue/compiler-sfc" "3.3.11"
|
||||
"@vue/runtime-dom" "3.3.11"
|
||||
"@vue/server-renderer" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"mayfly-go/pkg/ws"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -84,9 +83,6 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
|
||||
|
||||
/** 数据库操作相关、执行sql等 ***/
|
||||
|
||||
// 取消执行sql函数map; key -> execId ; value -> cancelFunc
|
||||
var cancelExecSqlMap = sync.Map{}
|
||||
|
||||
func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
g := rc.GinCtx
|
||||
form := &form.DbSqlExecForm{}
|
||||
@@ -112,14 +108,9 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
DbConn: dbConn,
|
||||
}
|
||||
|
||||
ctx := rc.MetaCtx
|
||||
// 如果存在执行id,则保存取消函数,用于后续可能的取消操作
|
||||
if form.ExecId != "" {
|
||||
cancelCtx, cancel := context.WithTimeout(rc.MetaCtx, 55*time.Second)
|
||||
ctx = cancelCtx
|
||||
cancelExecSqlMap.Store(form.ExecId, cancel)
|
||||
defer cancelExecSqlMap.Delete(form.ExecId)
|
||||
}
|
||||
// 比前端超时时间稍微快一点,可以提示到前端
|
||||
ctx, cancel := context.WithTimeout(rc.MetaCtx, 58*time.Second)
|
||||
defer cancel()
|
||||
|
||||
sqls, err := sqlparser.SplitStatementToPieces(sql, sqlparser.WithDialect(dbConn.Info.Type.Dialect()))
|
||||
biz.ErrIsNil(err, "SQL解析错误,请检查您的执行SQL")
|
||||
@@ -150,14 +141,6 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
||||
rc.ResData = colAndRes
|
||||
}
|
||||
|
||||
func (d *Db) CancelExecSql(rc *req.Ctx) {
|
||||
execId := ginx.PathParam(rc.GinCtx, "execId")
|
||||
if cancelFunc, ok := cancelExecSqlMap.LoadAndDelete(execId); ok {
|
||||
rc.ReqParam = execId
|
||||
cancelFunc.(context.CancelFunc)()
|
||||
}
|
||||
}
|
||||
|
||||
// progressCategory sql文件执行进度消息类型
|
||||
const progressCategory = "execSqlFileProgress"
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ func InitDbRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewPost(":dbId/exec-sql", d.ExecSql).Log(req.NewLog("db-执行Sql")),
|
||||
|
||||
req.NewPost(":dbId/exec-sql/cancel/:execId", d.CancelExecSql).Log(req.NewLog("db-取消执行Sql")),
|
||||
|
||||
req.NewPost(":dbId/exec-sql-file", d.ExecSqlFile).Log(req.NewLogSave("db-执行Sql文件")),
|
||||
|
||||
req.NewGet(":dbId/dump", d.DumpSql).Log(req.NewLogSave("db-导出sql文件")).NoRes(),
|
||||
|
||||
Reference in New Issue
Block a user