mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
refactor: 数据同步编辑页调整、echarts组件重构
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
"countup.js": "^2.7.0",
|
"countup.js": "^2.7.0",
|
||||||
"cropperjs": "^1.5.11",
|
"cropperjs": "^1.5.11",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-plus": "^2.4.4",
|
"element-plus": "^2.5.0",
|
||||||
"js-base64": "^3.7.5",
|
"js-base64": "^3.7.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"splitpanes": "^3.1.5",
|
"splitpanes": "^3.1.5",
|
||||||
"sql-formatter": "^14.0.0",
|
"sql-formatter": "^14.0.0",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue": "^3.4.6",
|
"vue": "^3.4.7",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"xterm": "^5.3.0",
|
"xterm": "^5.3.0",
|
||||||
"xterm-addon-fit": "^0.8.0",
|
"xterm-addon-fit": "^0.8.0",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||||
"@typescript-eslint/parser": "^6.7.4",
|
"@typescript-eslint/parser": "^6.7.4",
|
||||||
"@vitejs/plugin-vue": "^5.0.2",
|
"@vitejs/plugin-vue": "^5.0.2",
|
||||||
"@vue/compiler-sfc": "^3.4.6",
|
"@vue/compiler-sfc": "^3.4.7",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-plugin-vue": "^9.19.2",
|
"eslint-plugin-vue": "^9.19.2",
|
||||||
|
|||||||
@@ -1,176 +0,0 @@
|
|||||||
{
|
|
||||||
"seriesCnt": "4",
|
|
||||||
"backgroundColor": "rgba(0,0,0,0)",
|
|
||||||
"titleColor": "#008acd",
|
|
||||||
"subtitleColor": "#aaaaaa",
|
|
||||||
"textColorShow": false,
|
|
||||||
"textColor": "#333",
|
|
||||||
"markTextColor": "#eeeeee",
|
|
||||||
"color": [
|
|
||||||
"#2ec7c9",
|
|
||||||
"#b6a2de",
|
|
||||||
"#5ab1ef",
|
|
||||||
"#ffb980",
|
|
||||||
"#d87a80",
|
|
||||||
"#8d98b3",
|
|
||||||
"#e5cf0d",
|
|
||||||
"#97b552",
|
|
||||||
"#95706d",
|
|
||||||
"#dc69aa",
|
|
||||||
"#07a2a4",
|
|
||||||
"#9a7fd1",
|
|
||||||
"#588dd5",
|
|
||||||
"#f5994e",
|
|
||||||
"#c05050",
|
|
||||||
"#59678c",
|
|
||||||
"#c9ab00",
|
|
||||||
"#7eb00a",
|
|
||||||
"#6f5553",
|
|
||||||
"#c14089"
|
|
||||||
],
|
|
||||||
"borderColor": "#ccc",
|
|
||||||
"borderWidth": 0,
|
|
||||||
"visualMapColor": [
|
|
||||||
"#5ab1ef",
|
|
||||||
"#e0ffff"
|
|
||||||
],
|
|
||||||
"legendTextColor": "#333333",
|
|
||||||
"kColor": "#d87a80",
|
|
||||||
"kColor0": "#2ec7c9",
|
|
||||||
"kBorderColor": "#d87a80",
|
|
||||||
"kBorderColor0": "#2ec7c9",
|
|
||||||
"kBorderWidth": 1,
|
|
||||||
"lineWidth": 2,
|
|
||||||
"symbolSize": 3,
|
|
||||||
"symbol": "emptyCircle",
|
|
||||||
"symbolBorderWidth": 1,
|
|
||||||
"lineSmooth": true,
|
|
||||||
"graphLineWidth": 1,
|
|
||||||
"graphLineColor": "#aaaaaa",
|
|
||||||
"mapLabelColor": "#d87a80",
|
|
||||||
"mapLabelColorE": "rgb(100,0,0)",
|
|
||||||
"mapBorderColor": "#eeeeee",
|
|
||||||
"mapBorderColorE": "#444",
|
|
||||||
"mapBorderWidth": 0.5,
|
|
||||||
"mapBorderWidthE": 1,
|
|
||||||
"mapAreaColor": "#dddddd",
|
|
||||||
"mapAreaColorE": "rgba(254,153,78,1)",
|
|
||||||
"axes": [
|
|
||||||
{
|
|
||||||
"type": "all",
|
|
||||||
"name": "通用坐标轴",
|
|
||||||
"axisLineShow": true,
|
|
||||||
"axisLineColor": "#eeeeee",
|
|
||||||
"axisTickShow": true,
|
|
||||||
"axisTickColor": "#eeeeee",
|
|
||||||
"axisLabelShow": true,
|
|
||||||
"axisLabelColor": "#eeeeee",
|
|
||||||
"splitLineShow": true,
|
|
||||||
"splitLineColor": [
|
|
||||||
"#aaaaaa"
|
|
||||||
],
|
|
||||||
"splitAreaShow": false,
|
|
||||||
"splitAreaColor": [
|
|
||||||
"#eeeeee"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "category",
|
|
||||||
"name": "类目坐标轴",
|
|
||||||
"axisLineShow": true,
|
|
||||||
"axisLineColor": "#008acd",
|
|
||||||
"axisTickShow": true,
|
|
||||||
"axisTickColor": "#333",
|
|
||||||
"axisLabelShow": true,
|
|
||||||
"axisLabelColor": "#333",
|
|
||||||
"splitLineShow": false,
|
|
||||||
"splitLineColor": [
|
|
||||||
"#eee"
|
|
||||||
],
|
|
||||||
"splitAreaShow": false,
|
|
||||||
"splitAreaColor": [
|
|
||||||
"rgba(250,250,250,0.3)",
|
|
||||||
"rgba(200,200,200,0.3)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "value",
|
|
||||||
"name": "数值坐标轴",
|
|
||||||
"axisLineShow": true,
|
|
||||||
"axisLineColor": "#008acd",
|
|
||||||
"axisTickShow": true,
|
|
||||||
"axisTickColor": "#333",
|
|
||||||
"axisLabelShow": true,
|
|
||||||
"axisLabelColor": "#333",
|
|
||||||
"splitLineShow": true,
|
|
||||||
"splitLineColor": [
|
|
||||||
"#eee"
|
|
||||||
],
|
|
||||||
"splitAreaShow": true,
|
|
||||||
"splitAreaColor": [
|
|
||||||
"rgba(250,250,250,0.3)",
|
|
||||||
"rgba(200,200,200,0.3)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "log",
|
|
||||||
"name": "对数坐标轴",
|
|
||||||
"axisLineShow": true,
|
|
||||||
"axisLineColor": "#008acd",
|
|
||||||
"axisTickShow": true,
|
|
||||||
"axisTickColor": "#333",
|
|
||||||
"axisLabelShow": true,
|
|
||||||
"axisLabelColor": "#333",
|
|
||||||
"splitLineShow": true,
|
|
||||||
"splitLineColor": [
|
|
||||||
"#eee"
|
|
||||||
],
|
|
||||||
"splitAreaShow": true,
|
|
||||||
"splitAreaColor": [
|
|
||||||
"rgba(250,250,250,0.3)",
|
|
||||||
"rgba(200,200,200,0.3)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "time",
|
|
||||||
"name": "时间坐标轴",
|
|
||||||
"axisLineShow": true,
|
|
||||||
"axisLineColor": "#008acd",
|
|
||||||
"axisTickShow": true,
|
|
||||||
"axisTickColor": "#333",
|
|
||||||
"axisLabelShow": true,
|
|
||||||
"axisLabelColor": "#333",
|
|
||||||
"splitLineShow": true,
|
|
||||||
"splitLineColor": [
|
|
||||||
"#eee"
|
|
||||||
],
|
|
||||||
"splitAreaShow": false,
|
|
||||||
"splitAreaColor": [
|
|
||||||
"rgba(250,250,250,0.3)",
|
|
||||||
"rgba(200,200,200,0.3)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"axisSeperateSetting": true,
|
|
||||||
"toolboxColor": "#2ec7c9",
|
|
||||||
"toolboxEmphasisColor": "#18a4a6",
|
|
||||||
"tooltipAxisColor": "#008acd",
|
|
||||||
"tooltipAxisWidth": "1",
|
|
||||||
"timelineLineColor": "#008acd",
|
|
||||||
"timelineLineWidth": 1,
|
|
||||||
"timelineItemColor": "#008acd",
|
|
||||||
"timelineItemColorE": "#a9334c",
|
|
||||||
"timelineCheckColor": "#2ec7c9",
|
|
||||||
"timelineCheckBorderColor": "#2ec7c9",
|
|
||||||
"timelineItemBorderWidth": 1,
|
|
||||||
"timelineControlColor": "#008acd",
|
|
||||||
"timelineControlBorderColor": "#008acd",
|
|
||||||
"timelineControlBorderWidth": 0.5,
|
|
||||||
"timelineLabelColor": "#008acd",
|
|
||||||
"datazoomBackgroundColor": "rgba(47,69,84,0)",
|
|
||||||
"datazoomDataColor": "#efefff",
|
|
||||||
"datazoomFillColor": "rgba(182,162,222,0.2)",
|
|
||||||
"datazoomHandleColor": "#008acd",
|
|
||||||
"datazoomHandleWidth": "100",
|
|
||||||
"datazoomLabelColor": "#333333"
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// import * as echarts from 'echarts'
|
|
||||||
|
|
||||||
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
|
|
||||||
import * as echarts from 'echarts/core';
|
|
||||||
|
|
||||||
/** 图表后缀都为 Chart */
|
|
||||||
import { PieChart } from 'echarts/charts';
|
|
||||||
|
|
||||||
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
|
|
||||||
import { TitleComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, LegendComponent } from 'echarts/components';
|
|
||||||
|
|
||||||
// 标签自动布局,全局过渡动画等特性
|
|
||||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
|
||||||
|
|
||||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
|
||||||
|
|
||||||
// 注册必须的组件
|
|
||||||
echarts.use([
|
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
GridComponent,
|
|
||||||
DatasetComponent,
|
|
||||||
TransformComponent,
|
|
||||||
LegendComponent,
|
|
||||||
// BarChart,
|
|
||||||
LabelLayout,
|
|
||||||
UniversalTransition,
|
|
||||||
CanvasRenderer,
|
|
||||||
// LineChart,
|
|
||||||
PieChart,
|
|
||||||
]);
|
|
||||||
|
|
||||||
export default function (dom: any, theme: any = null, option: any) {
|
|
||||||
let chart = echarts.init(dom, theme);
|
|
||||||
chart.setOption(option);
|
|
||||||
return chart;
|
|
||||||
}
|
|
||||||
86
mayfly_go_web/src/components/echarts/ECharts.vue
Normal file
86
mayfly_go_web/src/components/echarts/ECharts.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div id="echarts" ref="chartRef" :style="echartsStyle" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="ECharts">
|
||||||
|
import { ref, onMounted, onBeforeUnmount, watch, computed, markRaw, nextTick } from 'vue';
|
||||||
|
import { EChartsType, ECElementEvent } from 'echarts/core';
|
||||||
|
import echarts, { ECOption } from './config';
|
||||||
|
import { useDebounceFn, useEventListener } from '@vueuse/core';
|
||||||
|
import { light } from './config/theme';
|
||||||
|
// import { useThemeConfig } from '@/store/themeConfig';
|
||||||
|
// import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
option: ECOption;
|
||||||
|
renderer?: 'canvas' | 'svg';
|
||||||
|
resize?: boolean;
|
||||||
|
theme?: Object | string;
|
||||||
|
width?: number | string;
|
||||||
|
height?: number | string;
|
||||||
|
onClick?: (event: ECElementEvent) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
renderer: 'canvas',
|
||||||
|
theme: light as any,
|
||||||
|
resize: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const echartsStyle = computed(() => {
|
||||||
|
return props.width || props.height ? { height: props.height + 'px', width: props.width + 'px' } : { height: '100%', width: '100%' };
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement | HTMLCanvasElement>();
|
||||||
|
const chartInstance = ref<EChartsType>();
|
||||||
|
|
||||||
|
const draw = () => {
|
||||||
|
if (chartInstance.value) {
|
||||||
|
chartInstance.value.setOption(props.option, { notMerge: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(props, () => {
|
||||||
|
draw();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = (event: ECElementEvent) => props.onClick && props.onClick(event);
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
if (!chartRef.value) return;
|
||||||
|
chartInstance.value = echarts.getInstanceByDom(chartRef.value);
|
||||||
|
|
||||||
|
if (!chartInstance.value) {
|
||||||
|
chartInstance.value = markRaw(
|
||||||
|
echarts.init(chartRef.value, props.theme, {
|
||||||
|
renderer: props.renderer,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
chartInstance.value.on('click', handleClick);
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
if (chartInstance.value && props.resize) {
|
||||||
|
chartInstance.value.resize({ animation: { duration: 300 } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedResize = useDebounceFn(resize, 300, { maxWait: 800 });
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => init());
|
||||||
|
useEventListener('resize', debouncedResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
chartInstance.value?.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getInstance: () => chartInstance.value,
|
||||||
|
resize,
|
||||||
|
draw,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
67
mayfly_go_web/src/components/echarts/config/index.ts
Normal file
67
mayfly_go_web/src/components/echarts/config/index.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { BarChart, LineChart, LinesChart, PieChart, ScatterChart, RadarChart, GaugeChart } from 'echarts/charts';
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
LegendComponent,
|
||||||
|
PolarComponent,
|
||||||
|
GeoComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import type {
|
||||||
|
BarSeriesOption,
|
||||||
|
LineSeriesOption,
|
||||||
|
LinesSeriesOption,
|
||||||
|
PieSeriesOption,
|
||||||
|
ScatterSeriesOption,
|
||||||
|
RadarSeriesOption,
|
||||||
|
GaugeSeriesOption,
|
||||||
|
} from 'echarts/charts';
|
||||||
|
import type { TitleComponentOption, TooltipComponentOption, GridComponentOption, DatasetComponentOption } from 'echarts/components';
|
||||||
|
import type { ComposeOption } from 'echarts/core';
|
||||||
|
// import 'echarts-liquidfill';
|
||||||
|
|
||||||
|
export type ECOption = ComposeOption<
|
||||||
|
| BarSeriesOption
|
||||||
|
| LineSeriesOption
|
||||||
|
| LinesSeriesOption
|
||||||
|
| PieSeriesOption
|
||||||
|
| RadarSeriesOption
|
||||||
|
| GaugeSeriesOption
|
||||||
|
| TitleComponentOption
|
||||||
|
| TooltipComponentOption
|
||||||
|
| GridComponentOption
|
||||||
|
| DatasetComponentOption
|
||||||
|
| ScatterSeriesOption
|
||||||
|
>;
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
LegendComponent,
|
||||||
|
PolarComponent,
|
||||||
|
GeoComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
BarChart,
|
||||||
|
LineChart,
|
||||||
|
LinesChart,
|
||||||
|
PieChart,
|
||||||
|
ScatterChart,
|
||||||
|
RadarChart,
|
||||||
|
GaugeChart,
|
||||||
|
LabelLayout,
|
||||||
|
UniversalTransition,
|
||||||
|
CanvasRenderer,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default echarts;
|
||||||
151
mayfly_go_web/src/components/echarts/config/theme.js
Normal file
151
mayfly_go_web/src/components/echarts/config/theme.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
const light = {
|
||||||
|
seriesCnt: '4',
|
||||||
|
backgroundColor: 'rgba(0,0,0,0)',
|
||||||
|
titleColor: '#008acd',
|
||||||
|
subtitleColor: '#aaaaaa',
|
||||||
|
textColorShow: false,
|
||||||
|
textColor: '#333',
|
||||||
|
markTextColor: '#eeeeee',
|
||||||
|
color: [
|
||||||
|
'#2ec7c9',
|
||||||
|
'#b6a2de',
|
||||||
|
'#5ab1ef',
|
||||||
|
'#ffb980',
|
||||||
|
'#d87a80',
|
||||||
|
'#8d98b3',
|
||||||
|
'#e5cf0d',
|
||||||
|
'#97b552',
|
||||||
|
'#95706d',
|
||||||
|
'#dc69aa',
|
||||||
|
'#07a2a4',
|
||||||
|
'#9a7fd1',
|
||||||
|
'#588dd5',
|
||||||
|
'#f5994e',
|
||||||
|
'#c05050',
|
||||||
|
'#59678c',
|
||||||
|
'#c9ab00',
|
||||||
|
'#7eb00a',
|
||||||
|
'#6f5553',
|
||||||
|
'#c14089',
|
||||||
|
],
|
||||||
|
borderColor: '#ccc',
|
||||||
|
borderWidth: 0,
|
||||||
|
visualMapColor: ['#5ab1ef', '#e0ffff'],
|
||||||
|
legendTextColor: '#333333',
|
||||||
|
kColor: '#d87a80',
|
||||||
|
kColor0: '#2ec7c9',
|
||||||
|
kBorderColor: '#d87a80',
|
||||||
|
kBorderColor0: '#2ec7c9',
|
||||||
|
kBorderWidth: 1,
|
||||||
|
lineWidth: 2,
|
||||||
|
symbolSize: 3,
|
||||||
|
symbol: 'emptyCircle',
|
||||||
|
symbolBorderWidth: 1,
|
||||||
|
lineSmooth: true,
|
||||||
|
graphLineWidth: 1,
|
||||||
|
graphLineColor: '#aaaaaa',
|
||||||
|
mapLabelColor: '#d87a80',
|
||||||
|
mapLabelColorE: 'rgb(100,0,0)',
|
||||||
|
mapBorderColor: '#eeeeee',
|
||||||
|
mapBorderColorE: '#444',
|
||||||
|
mapBorderWidth: 0.5,
|
||||||
|
mapBorderWidthE: 1,
|
||||||
|
mapAreaColor: '#dddddd',
|
||||||
|
mapAreaColorE: 'rgba(254,153,78,1)',
|
||||||
|
axes: [
|
||||||
|
{
|
||||||
|
type: 'all',
|
||||||
|
name: '通用坐标轴',
|
||||||
|
axisLineShow: true,
|
||||||
|
axisLineColor: '#eeeeee',
|
||||||
|
axisTickShow: true,
|
||||||
|
axisTickColor: '#eeeeee',
|
||||||
|
axisLabelShow: true,
|
||||||
|
axisLabelColor: '#eeeeee',
|
||||||
|
splitLineShow: true,
|
||||||
|
splitLineColor: ['#aaaaaa'],
|
||||||
|
splitAreaShow: false,
|
||||||
|
splitAreaColor: ['#eeeeee'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
name: '类目坐标轴',
|
||||||
|
axisLineShow: true,
|
||||||
|
axisLineColor: '#008acd',
|
||||||
|
axisTickShow: true,
|
||||||
|
axisTickColor: '#333',
|
||||||
|
axisLabelShow: true,
|
||||||
|
axisLabelColor: '#333',
|
||||||
|
splitLineShow: false,
|
||||||
|
splitLineColor: ['#eee'],
|
||||||
|
splitAreaShow: false,
|
||||||
|
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
name: '数值坐标轴',
|
||||||
|
axisLineShow: true,
|
||||||
|
axisLineColor: '#008acd',
|
||||||
|
axisTickShow: true,
|
||||||
|
axisTickColor: '#333',
|
||||||
|
axisLabelShow: true,
|
||||||
|
axisLabelColor: '#333',
|
||||||
|
splitLineShow: true,
|
||||||
|
splitLineColor: ['#eee'],
|
||||||
|
splitAreaShow: true,
|
||||||
|
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'log',
|
||||||
|
name: '对数坐标轴',
|
||||||
|
axisLineShow: true,
|
||||||
|
axisLineColor: '#008acd',
|
||||||
|
axisTickShow: true,
|
||||||
|
axisTickColor: '#333',
|
||||||
|
axisLabelShow: true,
|
||||||
|
axisLabelColor: '#333',
|
||||||
|
splitLineShow: true,
|
||||||
|
splitLineColor: ['#eee'],
|
||||||
|
splitAreaShow: true,
|
||||||
|
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'time',
|
||||||
|
name: '时间坐标轴',
|
||||||
|
axisLineShow: true,
|
||||||
|
axisLineColor: '#008acd',
|
||||||
|
axisTickShow: true,
|
||||||
|
axisTickColor: '#333',
|
||||||
|
axisLabelShow: true,
|
||||||
|
axisLabelColor: '#333',
|
||||||
|
splitLineShow: true,
|
||||||
|
splitLineColor: ['#eee'],
|
||||||
|
splitAreaShow: false,
|
||||||
|
splitAreaColor: ['rgba(250,250,250,0.3)', 'rgba(200,200,200,0.3)'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
axisSeperateSetting: true,
|
||||||
|
toolboxColor: '#2ec7c9',
|
||||||
|
toolboxEmphasisColor: '#18a4a6',
|
||||||
|
tooltipAxisColor: '#008acd',
|
||||||
|
tooltipAxisWidth: '1',
|
||||||
|
timelineLineColor: '#008acd',
|
||||||
|
timelineLineWidth: 1,
|
||||||
|
timelineItemColor: '#008acd',
|
||||||
|
timelineItemColorE: '#a9334c',
|
||||||
|
timelineCheckColor: '#2ec7c9',
|
||||||
|
timelineCheckBorderColor: '#2ec7c9',
|
||||||
|
timelineItemBorderWidth: 1,
|
||||||
|
timelineControlColor: '#008acd',
|
||||||
|
timelineControlBorderColor: '#008acd',
|
||||||
|
timelineControlBorderWidth: 0.5,
|
||||||
|
timelineLabelColor: '#008acd',
|
||||||
|
datazoomBackgroundColor: 'rgba(47,69,84,0)',
|
||||||
|
datazoomDataColor: '#efefff',
|
||||||
|
datazoomFillColor: 'rgba(182,162,222,0.2)',
|
||||||
|
datazoomHandleColor: '#008acd',
|
||||||
|
datazoomHandleWidth: '100',
|
||||||
|
datazoomLabelColor: '#333333',
|
||||||
|
};
|
||||||
|
|
||||||
|
export { light };
|
||||||
135
mayfly_go_web/src/views/ops/component/TagTreeResourceSelect.vue
Normal file
135
mayfly_go_web/src/views/ops/component/TagTreeResourceSelect.vue
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<el-tree-select
|
||||||
|
v-bind="$attrs"
|
||||||
|
ref="treeRef"
|
||||||
|
:highlight-current="true"
|
||||||
|
:indent="10"
|
||||||
|
:load="loadNode"
|
||||||
|
:props="treeProps"
|
||||||
|
lazy
|
||||||
|
node-key="key"
|
||||||
|
:expand-on-click-node="true"
|
||||||
|
filterable
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
v-model="modelValue"
|
||||||
|
@change="changeNode"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<span>
|
||||||
|
<span v-if="data.type.value == TagTreeNode.TagPath">
|
||||||
|
<tag-info :tag-path="data.label" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<slot v-else :node="node" :data="data" name="prefix"></slot>
|
||||||
|
|
||||||
|
<span class="ml3" :title="data.labelRemark">
|
||||||
|
<slot name="label" :data="data"> {{ data.label }}</slot>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<slot :node="node" :data="data" name="suffix"></slot>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tree-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref, watch, toRefs } from 'vue';
|
||||||
|
import { NodeType, TagTreeNode } from './tag';
|
||||||
|
import TagInfo from './TagInfo.vue';
|
||||||
|
import { tagApi } from '../tag/api';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
resourceType: {
|
||||||
|
type: [Number],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
tagPathNodeType: {
|
||||||
|
type: [NodeType],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
load: {
|
||||||
|
type: Function,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const treeProps = {
|
||||||
|
label: 'name',
|
||||||
|
children: 'zones',
|
||||||
|
isLeaf: 'isLeaf',
|
||||||
|
};
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const treeRef: any = ref(null);
|
||||||
|
|
||||||
|
const modelValue = defineModel('modelValue');
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
height: 600 as any,
|
||||||
|
filterText: '',
|
||||||
|
opend: {},
|
||||||
|
});
|
||||||
|
const { filterText } = toRefs(state);
|
||||||
|
|
||||||
|
onMounted(async () => {});
|
||||||
|
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
treeRef.value?.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterNode = (value: string, data: any) => {
|
||||||
|
if (!value) return true;
|
||||||
|
return data.label.includes(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载标签树节点
|
||||||
|
*/
|
||||||
|
const loadTags = async () => {
|
||||||
|
const tags = await tagApi.getResourceTagPaths.request({ resourceType: props.resourceType });
|
||||||
|
const tagNodes = [];
|
||||||
|
for (let tagPath of tags) {
|
||||||
|
tagNodes.push(new TagTreeNode(tagPath, tagPath, props.tagPathNodeType));
|
||||||
|
}
|
||||||
|
return tagNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载树节点
|
||||||
|
* @param { Object } node
|
||||||
|
* @param { Object } resolve
|
||||||
|
*/
|
||||||
|
const loadNode = async (node: any, resolve: any) => {
|
||||||
|
if (typeof resolve !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let nodes = [];
|
||||||
|
try {
|
||||||
|
if (node.level == 0) {
|
||||||
|
nodes = await loadTags();
|
||||||
|
} else if (props.load) {
|
||||||
|
nodes = await props.load(node);
|
||||||
|
} else {
|
||||||
|
nodes = await node.data.loadChildren();
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return resolve(nodes);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNode = (nodeKey: any) => {
|
||||||
|
let node = treeRef.value.getNode(nodeKey);
|
||||||
|
if (!node) {
|
||||||
|
throw new Error('未找到节点: ' + nodeKey);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeNode = (val: any) => {
|
||||||
|
// 触发改变时间,并传递节点数据
|
||||||
|
emit('change', getNode(val)?.data);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -7,47 +7,62 @@
|
|||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:close-on-press-escape="false"
|
:close-on-press-escape="false"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
width="700px"
|
width="850px"
|
||||||
>
|
>
|
||||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
||||||
<el-tabs v-model="tabActiveName" style="height: 450px">
|
<el-tabs v-model="tabActiveName" style="height: 450px">
|
||||||
<el-tab-pane label="基本信息" name="basic">
|
<el-tab-pane label="基本信息" name="basic">
|
||||||
<el-form-item prop="taskName" label="任务名" required>
|
<el-form-item>
|
||||||
<el-input v-model.trim="form.taskName" placeholder="请输入数据库别名" auto-complete="off" />
|
<el-row>
|
||||||
|
<el-col :span="11">
|
||||||
|
<el-form-item prop="taskName" label="任务名" required>
|
||||||
|
<el-input v-model.trim="form.taskName" placeholder="请输入数据库别名" auto-complete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="11">
|
||||||
|
<el-form-item prop="taskCron" label="cron" required>
|
||||||
|
<template #label>
|
||||||
|
cron
|
||||||
|
<el-tooltip effect="dark" content="只支持5位表达式,不支持秒级.如 0/2 * * * * 表示每两分钟执行" placement="top">
|
||||||
|
<el-icon>
|
||||||
|
<question-filled />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-input v-model="form.taskCron" placeholder="支持5位表达式,不支持秒级" auto-complete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="2">
|
||||||
|
<el-form-item prop="status" label="状态" label-width="60" required>
|
||||||
|
<el-switch
|
||||||
|
v-model="form.status"
|
||||||
|
inline-prompt
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="禁用"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="-1"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="taskCron" label="cron" required>
|
|
||||||
<el-input v-model="form.taskCron" placeholder="只支持5位表达式,不支持秒级.如 0/2 * * * * 表示每两分钟执行" auto-complete="off" />
|
<el-form-item prop="srcDbId" label="源数据库" required>
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="pageSize" label="分页大小" required>
|
|
||||||
<el-input-number v-model.trim="form.pageSize" placeholder="同步数据时查询的每页数据大小" auto-complete="off" size="small" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="updField" label="更新字段" required>
|
|
||||||
<el-input v-model.trim="form.updField" placeholder="查询数据源的时候会带上这个字段当前最大值" auto-complete="off" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="updFieldVal" label="更新值">
|
|
||||||
<el-input v-model.trim="form.updFieldVal" placeholder="更新字段当前最大值" auto-complete="off" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="status" label="状态" required>
|
|
||||||
<el-switch v-model="form.status" inline-prompt active-text="启用" inactive-text="禁用" :active-value="1" :inactive-value="-1" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="源数据库配置" name="srcDb">
|
|
||||||
<el-form-item prop="srcDbId" label="数据源" required>
|
|
||||||
<db-select-tree
|
<db-select-tree
|
||||||
|
placeholder="请选择源数据库"
|
||||||
v-model:db-id="form.srcDbId"
|
v-model:db-id="form.srcDbId"
|
||||||
v-model:db-name="form.srcDbName"
|
v-model:db-name="form.srcDbName"
|
||||||
v-model:tag-path="form.srcTagPath"
|
v-model:tag-path="form.srcTagPath"
|
||||||
@select-db="onSelectSrcDb"
|
@select-db="onSelectSrcDb"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="dataSql" label="数据sql" required>
|
|
||||||
<monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="目标数据库配置" name="targetDb">
|
<el-form-item prop="targetDbId" label="目标数据库" required>
|
||||||
<el-form-item prop="targetDbId" label="数据源" required>
|
|
||||||
<db-select-tree
|
<db-select-tree
|
||||||
|
placeholder="请选择目标数据库"
|
||||||
v-model:db-id="form.targetDbId"
|
v-model:db-id="form.targetDbId"
|
||||||
v-model:db-name="form.targetDbName"
|
v-model:db-name="form.targetDbName"
|
||||||
v-model:tag-path="form.targetTagPath"
|
v-model:tag-path="form.targetTagPath"
|
||||||
@@ -55,7 +70,11 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="targetTableName" label="目标表" required>
|
<el-form-item prop="dataSql" label="源数据sql" required>
|
||||||
|
<monaco-editor height="150px" class="task-sql" language="sql" v-model="form.dataSql" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="targetTableName" label="目标库表" required>
|
||||||
<el-select v-model="form.targetTableName" filterable placeholder="请选择目标数据库表">
|
<el-select v-model="form.targetTableName" filterable placeholder="请选择目标数据库表">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in state.targetTableList"
|
v-for="item in state.targetTableList"
|
||||||
@@ -65,7 +84,30 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item prop="pageSize" label="分页大小" required>
|
||||||
|
<el-input type="number" v-model.trim="form.pageSize" placeholder="同步数据时查询的每页数据大小" auto-complete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item prop="updField" label="更新字段" required>
|
||||||
|
<el-input v-model.trim="form.updField" placeholder="查询数据源的时候会带上这个字段当前最大值" auto-complete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item prop="updFieldVal" label="更新值">
|
||||||
|
<el-input v-model.trim="form.updFieldVal" placeholder="更新字段当前最大值" auto-complete="off" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="字段映射" name="field">
|
<el-tab-pane label="字段映射" name="field">
|
||||||
<el-form-item prop="fieldMap" label="字段映射" required>
|
<el-form-item prop="fieldMap" label="字段映射" required>
|
||||||
<el-table :data="form.fieldMap" :max-height="400" size="small">
|
<el-table :data="form.fieldMap" :max-height="400" size="small">
|
||||||
@@ -85,6 +127,7 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="sql预览" name="sqlPreview">
|
<el-tab-pane label="sql预览" name="sqlPreview">
|
||||||
<el-form-item prop="fieldMap" label="查询sql">
|
<el-form-item prop="fieldMap" label="查询sql">
|
||||||
<el-input type="textarea" v-model="state.previewDataSql" readonly :input-style="{ height: '190px' }" />
|
<el-input type="textarea" v-model="state.previewDataSql" readonly :input-style="{ height: '190px' }" />
|
||||||
@@ -226,48 +269,43 @@ watch(dialogVisible, async (newValue: boolean) => {
|
|||||||
}
|
}
|
||||||
state.tabActiveName = 'basic';
|
state.tabActiveName = 'basic';
|
||||||
const propsData = props.data as any;
|
const propsData = props.data as any;
|
||||||
if (propsData?.id) {
|
if (!propsData?.id) {
|
||||||
let data = await dbApi.getDatasyncTask.request({ taskId: propsData?.id });
|
|
||||||
state.form = data;
|
|
||||||
try {
|
|
||||||
state.form.fieldMap = JSON.parse(data.fieldMap);
|
|
||||||
} catch (e) {
|
|
||||||
state.form.fieldMap = [];
|
|
||||||
}
|
|
||||||
let { srcDbId, srcTagPath, srcDbName, targetTagPath, targetDbId } = state.form;
|
|
||||||
|
|
||||||
// 初始化src数据源
|
|
||||||
if (srcTagPath && srcDbId) {
|
|
||||||
// 通过tagPath查询实例列表
|
|
||||||
const dbInfoRes = await dbApi.dbs.request({ tagPath: srcTagPath });
|
|
||||||
dbInfoRes.list.forEach((a: any) => {
|
|
||||||
if (a.id === srcDbId) {
|
|
||||||
// 初始化实例
|
|
||||||
a.databases = a.database?.split(' ').sort() || [];
|
|
||||||
state.srcDbInst = DbInst.getOrNewInst(a);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化target数据源
|
|
||||||
if (targetTagPath && targetDbId) {
|
|
||||||
// 通过tagPath查询实例列表
|
|
||||||
const dbInfoRes = await dbApi.dbs.request({ tagPath: targetTagPath });
|
|
||||||
dbInfoRes.list.forEach((a: any) => {
|
|
||||||
if (a.id === targetDbId) {
|
|
||||||
// 初始化实例
|
|
||||||
a.databases = a.database?.split(' ').sort() || [];
|
|
||||||
state.targetDbInst = DbInst.getOrNewInst(a);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册sql代码提示
|
|
||||||
if (srcDbId && srcDbName) {
|
|
||||||
registerDbCompletionItemProvider(srcDbId, srcDbName, state.srcDbInst.databases, state.srcDbInst.type);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.form = basicFormData;
|
state.form = basicFormData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = await dbApi.getDatasyncTask.request({ taskId: propsData?.id });
|
||||||
|
state.form = data;
|
||||||
|
try {
|
||||||
|
state.form.fieldMap = JSON.parse(data.fieldMap);
|
||||||
|
} catch (e) {
|
||||||
|
state.form.fieldMap = [];
|
||||||
|
}
|
||||||
|
let { srcDbId, srcDbName, targetDbId } = state.form;
|
||||||
|
|
||||||
|
// 初始化src数据源
|
||||||
|
if (srcDbId) {
|
||||||
|
// 通过tagPath查询实例列表
|
||||||
|
const dbInfoRes = await dbApi.dbs.request({ id: srcDbId });
|
||||||
|
const db = dbInfoRes.list[0];
|
||||||
|
// 初始化实例
|
||||||
|
db.databases = db.database?.split(' ').sort() || [];
|
||||||
|
state.srcDbInst = DbInst.getOrNewInst(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化target数据源
|
||||||
|
if (targetDbId) {
|
||||||
|
// 通过tagPath查询实例列表
|
||||||
|
const dbInfoRes = await dbApi.dbs.request({ id: targetDbId });
|
||||||
|
const db = dbInfoRes.list[0];
|
||||||
|
// 初始化实例
|
||||||
|
db.databases = db.database?.split(' ').sort() || [];
|
||||||
|
state.targetDbInst = DbInst.getOrNewInst(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册sql代码提示
|
||||||
|
if (srcDbId && srcDbName) {
|
||||||
|
registerDbCompletionItemProvider(srcDbId, srcDbName, state.srcDbInst.databases, state.srcDbInst.type);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -277,7 +315,7 @@ watch(tabActiveName, async (newValue: string) => {
|
|||||||
await handleGetSrcFields();
|
await handleGetSrcFields();
|
||||||
await handleGetTargetFields();
|
await handleGetTargetFields();
|
||||||
break;
|
break;
|
||||||
case 'targetDb':
|
case 'dbConf':
|
||||||
await handleGetTargetFields();
|
await handleGetTargetFields();
|
||||||
if (state.form.targetDbId && state.form.targetDbName) {
|
if (state.form.targetDbId && state.form.targetDbName) {
|
||||||
await loadDbTables(state.form.targetDbId, state.form.targetDbName);
|
await loadDbTables(state.form.targetDbId, state.form.targetDbName);
|
||||||
@@ -418,8 +456,12 @@ const cancel = () => {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.sync-task-edit {
|
.sync-task-edit {
|
||||||
.el-select {
|
.el-select {
|
||||||
width: 360px;
|
// width: 360px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
// .el-input__inner {
|
||||||
|
// width: 100%; /* 将el-select内部输入框的宽度设置为100% */
|
||||||
|
// }
|
||||||
.task-sql {
|
.task-sql {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="db-select-tree">
|
<TagTreeResourceSelect
|
||||||
<div style="color: gray">{{ (tagPath || '') + ' - ' + (dbName || '请选择数据源schema') }}</div>
|
v-bind="$attrs"
|
||||||
<tag-tree :resource-type="TagResourceTypeEnum.Db.value" :tag-path-node-type="NodeTypeTagPath" ref="tagTreeRef">
|
v-model="selectNode"
|
||||||
<template #prefix="{ data }">
|
@change="changeNode"
|
||||||
<SvgIcon v-if="data.type.value == SqlExecNodeType.DbInst" :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
|
:resource-type="TagResourceTypeEnum.Db.value"
|
||||||
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
|
:tag-path-node-type="NodeTypeTagPath"
|
||||||
</template>
|
>
|
||||||
</tag-tree>
|
<template #prefix="{ data }">
|
||||||
</div>
|
<SvgIcon v-if="data.type.value == SqlExecNodeType.DbInst" :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
|
||||||
|
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
|
||||||
|
</template>
|
||||||
|
</TagTreeResourceSelect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
import TagTree from '@/views/ops/component/TagTree.vue';
|
|
||||||
import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
|
import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
|
||||||
import { dbApi } from '@/views/ops/db/api';
|
import { dbApi } from '@/views/ops/db/api';
|
||||||
import { sleep } from '@/common/utils/loading';
|
import { sleep } from '@/common/utils/loading';
|
||||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
import { DbType, getDbDialect } from '@/views/ops/db/dialect';
|
import { DbType, getDbDialect } from '@/views/ops/db/dialect';
|
||||||
|
import TagTreeResourceSelect from '../../component/TagTreeResourceSelect.vue';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
dbId: {
|
dbId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
},
|
},
|
||||||
@@ -47,6 +51,15 @@ class SqlExecNodeType {
|
|||||||
static PgSchema = 8;
|
static PgSchema = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectNode = computed({
|
||||||
|
get: () => {
|
||||||
|
return props.dbName ? `${props.tagPath} - ${props.dbId} - ${props.dbName}` : '';
|
||||||
|
},
|
||||||
|
set: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const DbIcon = {
|
const DbIcon = {
|
||||||
name: 'Coin',
|
name: 'Coin',
|
||||||
color: '#67c23a',
|
color: '#67c23a',
|
||||||
@@ -89,7 +102,7 @@ const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((p
|
|||||||
fn = PgNodeTypes;
|
fn = PgNodeTypes;
|
||||||
}
|
}
|
||||||
return dbs.map((x: any) => {
|
return dbs.map((x: any) => {
|
||||||
let tagTreeNode = new TagTreeNode(`${parentNode.key}.${x}`, x, fn)
|
let tagTreeNode = new TagTreeNode(`${parentNode.key}.${x}`, `${x}`, fn)
|
||||||
.withParams({
|
.withParams({
|
||||||
tagPath: params.tagPath,
|
tagPath: params.tagPath,
|
||||||
id: params.id,
|
id: params.id,
|
||||||
@@ -108,17 +121,6 @@ const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((p
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodeClickChangeDb = (nodeData: TagTreeNode) => {
|
|
||||||
const params = nodeData.params;
|
|
||||||
// postgres
|
|
||||||
emits('update:dbName', params.db);
|
|
||||||
emits('update:dbId', params.id);
|
|
||||||
emits('update:tagPath', params.tagPath);
|
|
||||||
emits('selectDb', params);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 数据库节点
|
// 数据库节点
|
||||||
const PgNodeTypes = new NodeType(SqlExecNodeType.Db).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
const PgNodeTypes = new NodeType(SqlExecNodeType.Db).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||||
// pg类数据库会多一层schema
|
// pg类数据库会多一层schema
|
||||||
@@ -137,23 +139,19 @@ const PgNodeTypes = new NodeType(SqlExecNodeType.Db).withLoadNodesFunc(async (pa
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const MysqlNodeTypes = new NodeType(SqlExecNodeType.Db).withNodeClickFunc(nodeClickChangeDb);
|
const MysqlNodeTypes = new NodeType(SqlExecNodeType.Db);
|
||||||
|
|
||||||
// postgres schema模式
|
// postgres schema模式
|
||||||
const NodeTypePostgresSchema = new NodeType(SqlExecNodeType.PgSchema).withNodeClickFunc(nodeClickChangeDb);
|
const NodeTypePostgresSchema = new NodeType(SqlExecNodeType.PgSchema);
|
||||||
|
|
||||||
|
const changeNode = (nodeData: TagTreeNode) => {
|
||||||
|
const params = nodeData.params;
|
||||||
|
// postgres
|
||||||
|
emits('update:dbName', params.db);
|
||||||
|
emits('update:dbId', params.id);
|
||||||
|
emits('update:tagPath', params.tagPath);
|
||||||
|
emits('selectDb', params);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
.db-select-tree {
|
|
||||||
.tag-tree {
|
|
||||||
height: auto !important;
|
|
||||||
overflow-x: hidden;
|
|
||||||
width: 560px;
|
|
||||||
.el-tree {
|
|
||||||
height: 150px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :lg="6" :md="6">
|
<el-col :lg="6" :md="6">
|
||||||
<div class="card-item-chart" ref="memRef"></div>
|
<ECharts height="200" :option="state.memOption" />
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :lg="6" :md="6">
|
<el-col :lg="6" :md="6">
|
||||||
<div class="card-item-chart" ref="cpuRef"></div>
|
<ECharts height="200" :option="state.cpuOption" />
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@@ -74,11 +74,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, watch, ref, nextTick } from 'vue';
|
import { toRefs, reactive, watch, nextTick } from 'vue';
|
||||||
import useEcharts from '@/common/echarts/useEcharts';
|
|
||||||
import tdTheme from '@/common/echarts/theme.json';
|
|
||||||
import { formatByteSize } from '@/common/utils/format';
|
import { formatByteSize } from '@/common/utils/format';
|
||||||
import { machineApi } from './api';
|
import { machineApi } from './api';
|
||||||
|
import ECharts from '@/components/echarts/ECharts.vue';
|
||||||
|
import { ECOption } from '@/components/echarts/config';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -94,22 +94,16 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId']);
|
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId']);
|
||||||
|
|
||||||
const cpuRef: any = ref();
|
|
||||||
const memRef: any = ref();
|
|
||||||
|
|
||||||
let cpuChart: any = null;
|
|
||||||
let memChart: any = null;
|
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
stats: {} as any,
|
stats: {} as any,
|
||||||
netInter: [] as any,
|
netInter: [] as any,
|
||||||
|
memOption: {},
|
||||||
|
cpuOption: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dialogVisible, stats, netInter } = toRefs(state);
|
const { dialogVisible, stats, netInter } = toRefs(state);
|
||||||
|
|
||||||
let charts = [] as any;
|
|
||||||
|
|
||||||
watch(props, async (newValue: any) => {
|
watch(props, async (newValue: any) => {
|
||||||
const visible = newValue.visible;
|
const visible = newValue.visible;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -139,15 +133,15 @@ const initMemStats = () => {
|
|||||||
value: mem.total - mem.available,
|
value: mem.total - mem.available,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const option = {
|
|
||||||
|
const option: ECOption = {
|
||||||
title: {
|
title: {
|
||||||
text: '内存',
|
text: '内存',
|
||||||
x: 'left',
|
|
||||||
textStyle: { fontSize: 15 },
|
textStyle: { fontSize: 15 },
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
valueFormatter: formatByteSize,
|
valueFormatter: (val: any) => formatByteSize(val),
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
top: '15%',
|
top: '15%',
|
||||||
@@ -180,13 +174,7 @@ const initMemStats = () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (memChart) {
|
state.memOption = option;
|
||||||
memChart.setOption(option, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const chart: any = useEcharts(memRef.value, tdTheme, option);
|
|
||||||
memChart = chart;
|
|
||||||
charts.push(chart);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initCpuStats = () => {
|
const initCpuStats = () => {
|
||||||
@@ -206,10 +194,10 @@ const initCpuStats = () => {
|
|||||||
value: cpu.user,
|
value: cpu.user,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const option = {
|
|
||||||
|
const option: ECOption = {
|
||||||
title: {
|
title: {
|
||||||
text: 'CPU使用率',
|
text: 'CPU使用率',
|
||||||
x: 'left',
|
|
||||||
textStyle: { fontSize: 15 },
|
textStyle: { fontSize: 15 },
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -247,13 +235,7 @@ const initCpuStats = () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (cpuChart) {
|
state.cpuOption = option;
|
||||||
cpuChart.setOption(option, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const chart: any = useEcharts(cpuRef.value, tdTheme, option);
|
|
||||||
cpuChart = chart;
|
|
||||||
charts.push(chart);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
@@ -262,21 +244,6 @@ const initCharts = () => {
|
|||||||
initCpuStats();
|
initCpuStats();
|
||||||
});
|
});
|
||||||
parseNetInter();
|
parseNetInter();
|
||||||
initEchartsResize();
|
|
||||||
};
|
|
||||||
|
|
||||||
const initEchartResizeFun = () => {
|
|
||||||
nextTick(() => {
|
|
||||||
for (let i = 0; i < charts.length; i++) {
|
|
||||||
setTimeout(() => {
|
|
||||||
charts[i].resize();
|
|
||||||
}, i * 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initEchartsResize = () => {
|
|
||||||
window.addEventListener('resize', initEchartResizeFun);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseNetInter = () => {
|
const parseNetInter = () => {
|
||||||
@@ -295,16 +262,6 @@ const parseNetInter = () => {
|
|||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
cpuChart = null;
|
|
||||||
memChart = null;
|
|
||||||
}, 200);
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss"></style>
|
||||||
.card-item-chart {
|
|
||||||
height: 200px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :lg="8" :md="8" class="redis-info">
|
<el-col :lg="8" :md="8" class="redis-info">
|
||||||
<div class="info-memory-chart" ref="memRef"></div>
|
<ECharts height="150" width="360" :option="state.memOption" />
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@@ -72,10 +72,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, watch, toRefs, ref, nextTick } from 'vue';
|
import { reactive, watch, toRefs, nextTick } from 'vue';
|
||||||
import { formatByteSize } from '@/common/utils/format';
|
import { formatByteSize } from '@/common/utils/format';
|
||||||
import useEcharts from '@/common/echarts/useEcharts';
|
import ECharts from '@/components/echarts/ECharts.vue';
|
||||||
import tdTheme from '@/common/echarts/theme.json';
|
import { ECOption } from '@/components/echarts/config';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -86,6 +86,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
type: [Boolean, Object],
|
type: [Boolean, Object],
|
||||||
|
default: () => {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,11 +96,9 @@ const state = reactive({
|
|||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
memInfo: {} as any,
|
memInfo: {} as any,
|
||||||
Keyspace: [] as any[],
|
Keyspace: [] as any[],
|
||||||
|
memOption: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
let memChart: any = null;
|
|
||||||
let memRef = ref(null);
|
|
||||||
|
|
||||||
const { dialogVisible, Keyspace } = toRefs(state);
|
const { dialogVisible, Keyspace } = toRefs(state);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -146,15 +145,14 @@ const initMemStats = () => {
|
|||||||
value: state.memInfo.used_memory,
|
value: state.memInfo.used_memory,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const option = {
|
const option: ECOption = {
|
||||||
title: {
|
title: {
|
||||||
text: '内存',
|
text: '内存',
|
||||||
x: 'left',
|
|
||||||
textStyle: { fontSize: 14 },
|
textStyle: { fontSize: 14 },
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
valueFormatter: formatByteSize,
|
valueFormatter: (val: any) => formatByteSize(val),
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
top: '15%',
|
top: '15%',
|
||||||
@@ -186,11 +184,8 @@ const initMemStats = () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (memChart) {
|
|
||||||
memChart.setOption(option, true);
|
state.memOption = option;
|
||||||
return;
|
|
||||||
}
|
|
||||||
memChart = useEcharts(memRef.value, tdTheme, option);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
@@ -202,11 +197,6 @@ const close = () => {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.redis-info {
|
.redis-info {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
|
||||||
.info-memory-chart {
|
|
||||||
width: 360px;
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row .title {
|
.row .title {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import "mayfly-go/pkg/model"
|
|
||||||
|
|
||||||
// InstanceQuery 数据库实例查询
|
// InstanceQuery 数据库实例查询
|
||||||
type InstanceQuery struct {
|
type InstanceQuery struct {
|
||||||
Id uint64 `json:"id" form:"id"`
|
Id uint64 `json:"id" form:"id"`
|
||||||
@@ -19,7 +17,7 @@ type DataSyncLogQuery struct {
|
|||||||
|
|
||||||
// 数据库查询实体,不与数据库表字段一一对应
|
// 数据库查询实体,不与数据库表字段一一对应
|
||||||
type DbQuery struct {
|
type DbQuery struct {
|
||||||
model.Model
|
Id uint64 `form:"id"`
|
||||||
|
|
||||||
Name string `orm:"column(name)" json:"name"`
|
Name string `orm:"column(name)" json:"name"`
|
||||||
Database string `orm:"column(database)" json:"database"`
|
Database string `orm:"column(database)" json:"database"`
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageP
|
|||||||
Select("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port, inst.username ").
|
Select("db.*, inst.name instance_name, inst.type instance_type, inst.host, inst.port, inst.username ").
|
||||||
Joins("JOIN t_db_instance inst ON db.instance_id = inst.id").
|
Joins("JOIN t_db_instance inst ON db.instance_id = inst.id").
|
||||||
Eq("db.instance_id", condition.InstanceId).
|
Eq("db.instance_id", condition.InstanceId).
|
||||||
|
Eq("db.id", condition.Id).
|
||||||
Like("db.database", condition.Database).
|
Like("db.database", condition.Database).
|
||||||
In("db.code", condition.Codes).
|
In("db.code", condition.Codes).
|
||||||
Eq0("db."+model.DeletedColumn, model.ModelUndeleted).
|
Eq0("db."+model.DeletedColumn, model.ModelUndeleted).
|
||||||
|
|||||||
Reference in New Issue
Block a user