@vela-studio/ui 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -0
- package/dist/index.d.ts +696 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +11786 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.js +10 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/style.css +1 -0
- package/index.ts +150 -0
- package/package.json +73 -0
- package/src/components/advanced/scripting/Scripting.vue +189 -0
- package/src/components/advanced/state/State.vue +231 -0
- package/src/components/advanced/trigger/Trigger.vue +256 -0
- package/src/components/basic/button/Button.vue +120 -0
- package/src/components/basic/container/Container.vue +22 -0
- package/src/components/chart/barChart/barChart.vue +176 -0
- package/src/components/chart/doughnutChart/doughnutChart.vue +128 -0
- package/src/components/chart/funnelChart/funnelChart.vue +128 -0
- package/src/components/chart/gaugeChart/gaugeChart.vue +144 -0
- package/src/components/chart/lineChart/lineChart.vue +188 -0
- package/src/components/chart/pieChart/pieChart.vue +114 -0
- package/src/components/chart/radarChart/radarChart.vue +115 -0
- package/src/components/chart/sankeyChart/sankeyChart.vue +144 -0
- package/src/components/chart/scatterChart/scatterChart.vue +162 -0
- package/src/components/chart/stackedBarChart/stackedBarChart.vue +184 -0
- package/src/components/content/html/Html.vue +104 -0
- package/src/components/content/iframe/Iframe.vue +111 -0
- package/src/components/content/markdown/Markdown.vue +174 -0
- package/src/components/controls/breadcrumb/Breadcrumb.vue +79 -0
- package/src/components/controls/buttonGroup/ButtonGroup.vue +93 -0
- package/src/components/controls/checkboxGroup/CheckboxGroup.vue +147 -0
- package/src/components/controls/dateRange/DateRange.vue +174 -0
- package/src/components/controls/multiSelect/MultiSelect.vue +155 -0
- package/src/components/controls/navButton/NavButton.vue +97 -0
- package/src/components/controls/pagination/Pagination.vue +94 -0
- package/src/components/controls/searchBox/SearchBox.vue +170 -0
- package/src/components/controls/select/Select.vue +134 -0
- package/src/components/controls/slider/Slider.vue +167 -0
- package/src/components/controls/switch/Switch.vue +107 -0
- package/src/components/data/cardGrid/CardGrid.vue +318 -0
- package/src/components/data/list/List.vue +282 -0
- package/src/components/data/pivot/Pivot.vue +270 -0
- package/src/components/data/table/Table.vue +150 -0
- package/src/components/data/timeline/Timeline.vue +315 -0
- package/src/components/group/Group.vue +75 -0
- package/src/components/kpi/box/Box.vue +98 -0
- package/src/components/kpi/countUp/CountUp.vue +193 -0
- package/src/components/kpi/progress/Progress.vue +159 -0
- package/src/components/kpi/stat/Stat.vue +205 -0
- package/src/components/kpi/text/Text.vue +74 -0
- package/src/components/layout/badge/Badge.vue +105 -0
- package/src/components/layout/col/Col.vue +114 -0
- package/src/components/layout/flex/Flex.vue +105 -0
- package/src/components/layout/grid/Grid.vue +89 -0
- package/src/components/layout/modal/Modal.vue +118 -0
- package/src/components/layout/panel/Panel.vue +162 -0
- package/src/components/layout/row/Row.vue +99 -0
- package/src/components/layout/tabs/Tabs.vue +117 -0
- package/src/components/media/image/Image.vue +132 -0
- package/src/components/media/video/Video.vue +115 -0
- package/src/components/v2/basic/BaseButton.vue +179 -0
- package/src/components/v2/kpi/KpiCard.vue +215 -0
- package/src/components/v2/layout/GridBox.vue +55 -0
- package/src/hooks/useDataSource.ts +123 -0
- package/src/types/gis.ts +251 -0
- package/src/utils/chartUtils.ts +349 -0
- package/src/utils/dataUtils.ts +403 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="v-sankey-chart" :style="{ width: '100%', height: '100%' }">
|
|
3
|
+
<v-chart :option="finalOption" autoresize class="echart" />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { use } from 'echarts/core'
|
|
10
|
+
import { CanvasRenderer } from 'echarts/renderers'
|
|
11
|
+
import { SankeyChart } from 'echarts/charts'
|
|
12
|
+
import { TitleComponent, TooltipComponent } from 'echarts/components'
|
|
13
|
+
import VChart from 'vue-echarts'
|
|
14
|
+
import type { EChartsOption } from 'echarts'
|
|
15
|
+
|
|
16
|
+
// 注册 ECharts 组件
|
|
17
|
+
use([TitleComponent, TooltipComponent, SankeyChart, CanvasRenderer])
|
|
18
|
+
|
|
19
|
+
// Sankey 节点类型
|
|
20
|
+
interface SankeyNode {
|
|
21
|
+
name: string
|
|
22
|
+
value?: number
|
|
23
|
+
depth?: number
|
|
24
|
+
itemStyle?: Record<string, unknown>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Sankey 连接类型
|
|
28
|
+
interface SankeyLink {
|
|
29
|
+
source: string
|
|
30
|
+
target: string
|
|
31
|
+
value: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 定义标准的 Props,不含任何业务逻辑 ID
|
|
35
|
+
const props = defineProps<{
|
|
36
|
+
// 基础数据 Props
|
|
37
|
+
data?: SankeyNode[]
|
|
38
|
+
links?: SankeyLink[]
|
|
39
|
+
|
|
40
|
+
// 样式配置 Props
|
|
41
|
+
title?: string
|
|
42
|
+
orient?: 'horizontal' | 'vertical'
|
|
43
|
+
left?: string
|
|
44
|
+
top?: string
|
|
45
|
+
right?: string
|
|
46
|
+
bottom?: string
|
|
47
|
+
nodeWidth?: number
|
|
48
|
+
nodeGap?: number
|
|
49
|
+
layoutIterations?: number
|
|
50
|
+
nodeAlign?: 'left' | 'right' | 'justify'
|
|
51
|
+
showLabel?: boolean
|
|
52
|
+
labelPosition?: 'left' | 'right' | 'top' | 'bottom'
|
|
53
|
+
labelFontSize?: number
|
|
54
|
+
labelColor?: string
|
|
55
|
+
lineColor?: string
|
|
56
|
+
lineOpacity?: number
|
|
57
|
+
lineCurveness?: number
|
|
58
|
+
|
|
59
|
+
// 高级覆盖
|
|
60
|
+
option?: EChartsOption
|
|
61
|
+
}>()
|
|
62
|
+
|
|
63
|
+
// 默认值配置
|
|
64
|
+
const defaultNodes: SankeyNode[] = [
|
|
65
|
+
{ name: 'a' },
|
|
66
|
+
{ name: 'b' },
|
|
67
|
+
{ name: 'c' },
|
|
68
|
+
{ name: 'd' },
|
|
69
|
+
{ name: 'e' },
|
|
70
|
+
{ name: 'f' },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
const defaultLinks: SankeyLink[] = [
|
|
74
|
+
{ source: 'a', target: 'b', value: 5 },
|
|
75
|
+
{ source: 'a', target: 'c', value: 3 },
|
|
76
|
+
{ source: 'b', target: 'd', value: 8 },
|
|
77
|
+
{ source: 'b', target: 'e', value: 3 },
|
|
78
|
+
{ source: 'c', target: 'e', value: 4 },
|
|
79
|
+
{ source: 'd', target: 'f', value: 6 },
|
|
80
|
+
{ source: 'e', target: 'f', value: 5 },
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
// 计算最终 Option
|
|
84
|
+
const finalOption = computed<EChartsOption>(() => {
|
|
85
|
+
// 如果有高级配置 option,优先使用
|
|
86
|
+
if (props.option && Object.keys(props.option).length > 0) return props.option
|
|
87
|
+
|
|
88
|
+
const data = props.data && props.data.length ? props.data : defaultNodes
|
|
89
|
+
const links = props.links && props.links.length ? props.links : defaultLinks
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
title: {
|
|
93
|
+
text: props.title || '',
|
|
94
|
+
left: 'center',
|
|
95
|
+
},
|
|
96
|
+
tooltip: {
|
|
97
|
+
trigger: 'item',
|
|
98
|
+
triggerOn: 'mousemove',
|
|
99
|
+
},
|
|
100
|
+
series: [
|
|
101
|
+
{
|
|
102
|
+
type: 'sankey',
|
|
103
|
+
data: data,
|
|
104
|
+
links: links,
|
|
105
|
+
emphasis: {
|
|
106
|
+
focus: 'adjacency',
|
|
107
|
+
},
|
|
108
|
+
orient: props.orient || 'horizontal',
|
|
109
|
+
left: props.left || '5%',
|
|
110
|
+
top: props.top || '10%',
|
|
111
|
+
right: props.right || '20%',
|
|
112
|
+
bottom: props.bottom || '10%',
|
|
113
|
+
nodeWidth: props.nodeWidth || 20,
|
|
114
|
+
nodeGap: props.nodeGap || 8,
|
|
115
|
+
layoutIterations: props.layoutIterations || 32,
|
|
116
|
+
nodeAlign: props.nodeAlign || 'justify',
|
|
117
|
+
label: {
|
|
118
|
+
show: props.showLabel !== false,
|
|
119
|
+
position: props.labelPosition || 'right',
|
|
120
|
+
fontSize: props.labelFontSize || 12,
|
|
121
|
+
color: props.labelColor || '#000',
|
|
122
|
+
},
|
|
123
|
+
lineStyle: {
|
|
124
|
+
color: props.lineColor || 'source',
|
|
125
|
+
opacity: props.lineOpacity || 0.2,
|
|
126
|
+
curveness: props.lineCurveness || 0.5,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<style scoped>
|
|
135
|
+
.v-sankey-chart {
|
|
136
|
+
width: 100%;
|
|
137
|
+
height: 100%;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.echart {
|
|
141
|
+
width: 100%;
|
|
142
|
+
height: 100%;
|
|
143
|
+
}
|
|
144
|
+
</style>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="v-scatter-chart" :style="{ width: '100%', height: '100%' }">
|
|
3
|
+
<v-chart :option="finalOption" autoresize class="echart" />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { use } from 'echarts/core'
|
|
10
|
+
import { CanvasRenderer } from 'echarts/renderers'
|
|
11
|
+
import { ScatterChart } from 'echarts/charts'
|
|
12
|
+
import {
|
|
13
|
+
TitleComponent,
|
|
14
|
+
TooltipComponent,
|
|
15
|
+
GridComponent,
|
|
16
|
+
LegendComponent,
|
|
17
|
+
} from 'echarts/components'
|
|
18
|
+
import VChart from 'vue-echarts'
|
|
19
|
+
import type { EChartsOption } from 'echarts'
|
|
20
|
+
|
|
21
|
+
// 注册 ECharts 组件
|
|
22
|
+
use([
|
|
23
|
+
TitleComponent,
|
|
24
|
+
TooltipComponent,
|
|
25
|
+
GridComponent,
|
|
26
|
+
LegendComponent,
|
|
27
|
+
ScatterChart,
|
|
28
|
+
CanvasRenderer,
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
// 定义标准的 Props,不含任何业务逻辑 ID
|
|
32
|
+
const props = defineProps<{
|
|
33
|
+
// 基础数据 Props
|
|
34
|
+
data?: Array<[number, number]>
|
|
35
|
+
|
|
36
|
+
// 样式配置 Props
|
|
37
|
+
title?: string
|
|
38
|
+
titleAlign?: 'left' | 'center' | 'right'
|
|
39
|
+
titleSize?: number
|
|
40
|
+
titleColor?: string
|
|
41
|
+
seriesName?: string
|
|
42
|
+
symbolSize?: number
|
|
43
|
+
color?: string
|
|
44
|
+
opacity?: number
|
|
45
|
+
showLegend?: boolean
|
|
46
|
+
legendLeft?: string
|
|
47
|
+
legendTop?: string
|
|
48
|
+
gridLeft?: string
|
|
49
|
+
gridRight?: string
|
|
50
|
+
gridBottom?: string
|
|
51
|
+
gridTop?: string
|
|
52
|
+
xAxisName?: string
|
|
53
|
+
yAxisName?: string
|
|
54
|
+
showXAxisSplitLine?: boolean
|
|
55
|
+
showYAxisSplitLine?: boolean
|
|
56
|
+
|
|
57
|
+
// 高级覆盖
|
|
58
|
+
option?: EChartsOption
|
|
59
|
+
}>()
|
|
60
|
+
|
|
61
|
+
// 默认值配置
|
|
62
|
+
const defaultData: Array<[number, number]> = [
|
|
63
|
+
[10.0, 8.04],
|
|
64
|
+
[8.07, 6.95],
|
|
65
|
+
[13.0, 7.58],
|
|
66
|
+
[9.05, 8.81],
|
|
67
|
+
[11.0, 8.33],
|
|
68
|
+
[14.0, 7.66],
|
|
69
|
+
[13.4, 6.81],
|
|
70
|
+
[10.0, 6.33],
|
|
71
|
+
[14.0, 8.96],
|
|
72
|
+
[12.5, 6.82],
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
// 计算最终 Option
|
|
76
|
+
const finalOption = computed<EChartsOption>(() => {
|
|
77
|
+
// 如果有高级配置 option,优先使用
|
|
78
|
+
if (props.option && Object.keys(props.option).length > 0) return props.option
|
|
79
|
+
|
|
80
|
+
const data = props.data && props.data.length ? props.data : defaultData
|
|
81
|
+
const seriesName = props.seriesName || 'Data'
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
title: {
|
|
85
|
+
text: props.title || '',
|
|
86
|
+
left: props.titleAlign || 'center',
|
|
87
|
+
textStyle: {
|
|
88
|
+
fontSize: props.titleSize || 16,
|
|
89
|
+
color: props.titleColor || '#333',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
tooltip: {
|
|
93
|
+
trigger: 'item',
|
|
94
|
+
formatter: (params: unknown) => {
|
|
95
|
+
const p = params as { seriesName: string; value: [number, number] }
|
|
96
|
+
return `${p.seriesName}<br/>${p.value[0]}, ${p.value[1]}`
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
legend: {
|
|
100
|
+
show: props.showLegend !== false,
|
|
101
|
+
left: props.legendLeft || 'center',
|
|
102
|
+
top: props.legendTop || 'bottom',
|
|
103
|
+
},
|
|
104
|
+
grid: {
|
|
105
|
+
left: props.gridLeft || '10%',
|
|
106
|
+
right: props.gridRight || '10%',
|
|
107
|
+
bottom: props.gridBottom || '15%',
|
|
108
|
+
top: props.gridTop || '15%',
|
|
109
|
+
containLabel: true,
|
|
110
|
+
},
|
|
111
|
+
xAxis: {
|
|
112
|
+
type: 'value',
|
|
113
|
+
name: props.xAxisName || '',
|
|
114
|
+
nameLocation: 'middle',
|
|
115
|
+
nameGap: 30,
|
|
116
|
+
splitLine: {
|
|
117
|
+
show: props.showXAxisSplitLine !== false,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
yAxis: {
|
|
121
|
+
type: 'value',
|
|
122
|
+
name: props.yAxisName || '',
|
|
123
|
+
nameLocation: 'middle',
|
|
124
|
+
nameGap: 40,
|
|
125
|
+
splitLine: {
|
|
126
|
+
show: props.showYAxisSplitLine !== false,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
series: [
|
|
130
|
+
{
|
|
131
|
+
name: seriesName,
|
|
132
|
+
type: 'scatter',
|
|
133
|
+
symbolSize: props.symbolSize || 10,
|
|
134
|
+
data: data,
|
|
135
|
+
itemStyle: {
|
|
136
|
+
color: props.color || '#5470c6',
|
|
137
|
+
opacity: props.opacity || 0.8,
|
|
138
|
+
},
|
|
139
|
+
emphasis: {
|
|
140
|
+
itemStyle: {
|
|
141
|
+
shadowBlur: 10,
|
|
142
|
+
shadowOffsetX: 0,
|
|
143
|
+
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
</script>
|
|
151
|
+
|
|
152
|
+
<style scoped>
|
|
153
|
+
.v-scatter-chart {
|
|
154
|
+
width: 100%;
|
|
155
|
+
height: 100%;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.echart {
|
|
159
|
+
width: 100%;
|
|
160
|
+
height: 100%;
|
|
161
|
+
}
|
|
162
|
+
</style>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="v-stacked-bar-chart" :style="{ width: '100%', height: '100%' }">
|
|
3
|
+
<v-chart :option="finalOption" autoresize class="echart" />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { use } from 'echarts/core'
|
|
10
|
+
import { CanvasRenderer } from 'echarts/renderers'
|
|
11
|
+
import { BarChart } from 'echarts/charts'
|
|
12
|
+
import {
|
|
13
|
+
TitleComponent,
|
|
14
|
+
TooltipComponent,
|
|
15
|
+
GridComponent,
|
|
16
|
+
LegendComponent,
|
|
17
|
+
} from 'echarts/components'
|
|
18
|
+
import VChart from 'vue-echarts'
|
|
19
|
+
import type { EChartsOption } from 'echarts'
|
|
20
|
+
|
|
21
|
+
// 注册 ECharts 组件
|
|
22
|
+
use([TitleComponent, TooltipComponent, GridComponent, LegendComponent, BarChart, CanvasRenderer])
|
|
23
|
+
|
|
24
|
+
// 定义标准的 Props,不含任何业务逻辑 ID
|
|
25
|
+
const props = defineProps<{
|
|
26
|
+
// 基础数据 Props
|
|
27
|
+
xAxisData?: string[]
|
|
28
|
+
seriesNames?: string[]
|
|
29
|
+
seriesData?: number[][]
|
|
30
|
+
|
|
31
|
+
// 样式配置 Props
|
|
32
|
+
title?: string
|
|
33
|
+
colors?: string[]
|
|
34
|
+
barWidth?: string
|
|
35
|
+
borderRadius?: number
|
|
36
|
+
showTooltip?: boolean
|
|
37
|
+
showLegend?: boolean
|
|
38
|
+
legendPosition?: 'top' | 'bottom' | 'left' | 'right'
|
|
39
|
+
showGrid?: boolean
|
|
40
|
+
xAxisName?: string
|
|
41
|
+
yAxisName?: string
|
|
42
|
+
showXAxisLine?: boolean
|
|
43
|
+
showXAxisLabel?: boolean
|
|
44
|
+
showYAxisLine?: boolean
|
|
45
|
+
showYAxisLabel?: boolean
|
|
46
|
+
showLabel?: boolean
|
|
47
|
+
|
|
48
|
+
// 高级覆盖
|
|
49
|
+
option?: EChartsOption
|
|
50
|
+
}>()
|
|
51
|
+
|
|
52
|
+
// 默认值配置
|
|
53
|
+
const defaultXAxis = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
54
|
+
const defaultSeriesNames = ['Series 1', 'Series 2', 'Series 3']
|
|
55
|
+
const defaultSeriesData = [
|
|
56
|
+
[120, 132, 101, 134, 90, 230, 210],
|
|
57
|
+
[220, 182, 191, 234, 290, 330, 310],
|
|
58
|
+
[150, 232, 201, 154, 190, 330, 410],
|
|
59
|
+
]
|
|
60
|
+
const defaultColors = ['#5470c6', '#91cc75', '#fac858']
|
|
61
|
+
|
|
62
|
+
// 计算最终 Option
|
|
63
|
+
const finalOption = computed<EChartsOption>(() => {
|
|
64
|
+
// 如果有高级配置 option,优先使用
|
|
65
|
+
if (props.option && Object.keys(props.option).length > 0) return props.option
|
|
66
|
+
|
|
67
|
+
const xAxisData = props.xAxisData && props.xAxisData.length ? props.xAxisData : defaultXAxis
|
|
68
|
+
const seriesNames =
|
|
69
|
+
props.seriesNames && props.seriesNames.length ? props.seriesNames : defaultSeriesNames
|
|
70
|
+
const seriesData =
|
|
71
|
+
props.seriesData && props.seriesData.length ? props.seriesData : defaultSeriesData
|
|
72
|
+
const colors = props.colors && props.colors.length ? props.colors : defaultColors
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
color: colors,
|
|
76
|
+
// 标题
|
|
77
|
+
title: props.title
|
|
78
|
+
? {
|
|
79
|
+
text: props.title,
|
|
80
|
+
left: 'center',
|
|
81
|
+
textStyle: {
|
|
82
|
+
fontSize: 16,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: undefined,
|
|
86
|
+
|
|
87
|
+
// 提示框
|
|
88
|
+
tooltip:
|
|
89
|
+
props.showTooltip !== false
|
|
90
|
+
? {
|
|
91
|
+
trigger: 'axis',
|
|
92
|
+
axisPointer: {
|
|
93
|
+
type: 'shadow',
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
: undefined,
|
|
97
|
+
|
|
98
|
+
// 图例
|
|
99
|
+
legend:
|
|
100
|
+
props.showLegend !== false
|
|
101
|
+
? {
|
|
102
|
+
[(props.legendPosition as string) || 'top']: 10,
|
|
103
|
+
data: seriesNames,
|
|
104
|
+
}
|
|
105
|
+
: undefined,
|
|
106
|
+
|
|
107
|
+
// 网格
|
|
108
|
+
grid: {
|
|
109
|
+
left: '6%',
|
|
110
|
+
right: '6%',
|
|
111
|
+
bottom: '8%',
|
|
112
|
+
top: props.title ? '15%' : '10%',
|
|
113
|
+
containLabel: true,
|
|
114
|
+
show: props.showGrid !== false,
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// X 轴
|
|
118
|
+
xAxis: {
|
|
119
|
+
type: 'category',
|
|
120
|
+
data: xAxisData,
|
|
121
|
+
name: props.xAxisName || '',
|
|
122
|
+
nameLocation: 'middle',
|
|
123
|
+
nameGap: 30,
|
|
124
|
+
axisLine: {
|
|
125
|
+
show: props.showXAxisLine !== false,
|
|
126
|
+
},
|
|
127
|
+
axisLabel: {
|
|
128
|
+
show: props.showXAxisLabel !== false,
|
|
129
|
+
},
|
|
130
|
+
splitLine: {
|
|
131
|
+
show: false,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Y 轴
|
|
136
|
+
yAxis: {
|
|
137
|
+
type: 'value',
|
|
138
|
+
name: props.yAxisName || '',
|
|
139
|
+
nameLocation: 'middle',
|
|
140
|
+
nameGap: 50,
|
|
141
|
+
axisLine: {
|
|
142
|
+
show: props.showYAxisLine !== false,
|
|
143
|
+
},
|
|
144
|
+
axisLabel: {
|
|
145
|
+
show: props.showYAxisLabel !== false,
|
|
146
|
+
},
|
|
147
|
+
splitLine: {
|
|
148
|
+
show: props.showGrid !== false,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// 系列
|
|
153
|
+
series: seriesData.map((data, index) => ({
|
|
154
|
+
name: seriesNames[index] || `Series ${index + 1}`,
|
|
155
|
+
type: 'bar',
|
|
156
|
+
stack: 'total',
|
|
157
|
+
data: data,
|
|
158
|
+
barWidth: props.barWidth || '60%',
|
|
159
|
+
itemStyle: {
|
|
160
|
+
borderRadius: index === seriesData.length - 1 ? props.borderRadius || 0 : 0,
|
|
161
|
+
},
|
|
162
|
+
label: props.showLabel
|
|
163
|
+
? {
|
|
164
|
+
show: true,
|
|
165
|
+
position: 'inside',
|
|
166
|
+
fontSize: 12,
|
|
167
|
+
}
|
|
168
|
+
: undefined,
|
|
169
|
+
})),
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<style scoped>
|
|
175
|
+
.v-stacked-bar-chart {
|
|
176
|
+
width: 100%;
|
|
177
|
+
height: 100%;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.echart {
|
|
181
|
+
width: 100%;
|
|
182
|
+
height: 100%;
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style="containerStyle" class="html-container" v-html="sanitizedHtml"></div>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { computed } from 'vue'
|
|
7
|
+
import type { CSSProperties } from 'vue'
|
|
8
|
+
import DOMPurify from 'dompurify'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
content?: string
|
|
13
|
+
sanitize?: boolean
|
|
14
|
+
allowedTags?: string
|
|
15
|
+
allowedAttributes?: string
|
|
16
|
+
padding?: number
|
|
17
|
+
backgroundColor?: string
|
|
18
|
+
textColor?: string
|
|
19
|
+
fontSize?: number
|
|
20
|
+
lineHeight?: number | string
|
|
21
|
+
borderRadius?: number
|
|
22
|
+
border?: string
|
|
23
|
+
overflow?: string
|
|
24
|
+
fontFamily?: string
|
|
25
|
+
}>(),
|
|
26
|
+
{
|
|
27
|
+
content: '<p>请输入 HTML 内容...</p>',
|
|
28
|
+
sanitize: true,
|
|
29
|
+
allowedTags: undefined,
|
|
30
|
+
allowedAttributes: undefined,
|
|
31
|
+
padding: 16,
|
|
32
|
+
backgroundColor: '#ffffff',
|
|
33
|
+
textColor: '#333333',
|
|
34
|
+
fontSize: 14,
|
|
35
|
+
lineHeight: 1.6,
|
|
36
|
+
borderRadius: 0,
|
|
37
|
+
border: 'none',
|
|
38
|
+
overflow: 'auto',
|
|
39
|
+
fontFamily: 'inherit',
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// 清理后的 HTML
|
|
44
|
+
const sanitizedHtml = computed(() => {
|
|
45
|
+
if (props.sanitize) {
|
|
46
|
+
return DOMPurify.sanitize(props.content, {
|
|
47
|
+
ALLOWED_TAGS: props.allowedTags ? props.allowedTags.split(',') : undefined,
|
|
48
|
+
ALLOWED_ATTR: props.allowedAttributes ? props.allowedAttributes.split(',') : undefined,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
return props.content
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// 样式
|
|
55
|
+
const containerStyle = computed<CSSProperties>(() => ({
|
|
56
|
+
width: '100%',
|
|
57
|
+
height: '100%',
|
|
58
|
+
padding: `${props.padding}px`,
|
|
59
|
+
backgroundColor: props.backgroundColor,
|
|
60
|
+
color: props.textColor,
|
|
61
|
+
fontSize: `${props.fontSize}px`,
|
|
62
|
+
lineHeight: String(props.lineHeight),
|
|
63
|
+
borderRadius: `${props.borderRadius}px`,
|
|
64
|
+
border: props.border,
|
|
65
|
+
overflow: props.overflow,
|
|
66
|
+
fontFamily: props.fontFamily,
|
|
67
|
+
}))
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.html-container {
|
|
72
|
+
box-sizing: border-box;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* 基础样式重置 */
|
|
76
|
+
.html-container :deep(*) {
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.html-container :deep(img) {
|
|
81
|
+
max-width: 100%;
|
|
82
|
+
height: auto;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.html-container :deep(table) {
|
|
86
|
+
border-collapse: collapse;
|
|
87
|
+
width: 100%;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.html-container :deep(table th),
|
|
91
|
+
.html-container :deep(table td) {
|
|
92
|
+
padding: 8px;
|
|
93
|
+
border: 1px solid #ddd;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.html-container :deep(a) {
|
|
97
|
+
color: #409eff;
|
|
98
|
+
text-decoration: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.html-container :deep(a:hover) {
|
|
102
|
+
text-decoration: underline;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style="containerStyle" class="iframe-container">
|
|
3
|
+
<iframe
|
|
4
|
+
v-if="url"
|
|
5
|
+
:src="url"
|
|
6
|
+
:style="iframeStyle"
|
|
7
|
+
:title="title"
|
|
8
|
+
:sandbox="sandbox"
|
|
9
|
+
:allow="allow"
|
|
10
|
+
loading="lazy"
|
|
11
|
+
referrerpolicy="no-referrer-when-downgrade"
|
|
12
|
+
></iframe>
|
|
13
|
+
<!-- 遮罩层:防止 iframe 拦截鼠标事件(编辑模式) -->
|
|
14
|
+
<div v-if="url && showMask" class="iframe-mask"></div>
|
|
15
|
+
<div v-else-if="!url" class="iframe-placeholder">
|
|
16
|
+
<el-icon><Link /></el-icon>
|
|
17
|
+
<span>{{ placeholder }}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed } from 'vue'
|
|
24
|
+
import type { CSSProperties } from 'vue'
|
|
25
|
+
import { Link } from '@element-plus/icons-vue'
|
|
26
|
+
import { ElIcon } from 'element-plus'
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(
|
|
29
|
+
defineProps<{
|
|
30
|
+
url?: string
|
|
31
|
+
title?: string
|
|
32
|
+
sandbox?: string
|
|
33
|
+
allow?: string
|
|
34
|
+
placeholder?: string
|
|
35
|
+
showMask?: boolean
|
|
36
|
+
backgroundColor?: string
|
|
37
|
+
borderRadius?: number
|
|
38
|
+
border?: string
|
|
39
|
+
opacity?: number
|
|
40
|
+
}>(),
|
|
41
|
+
{
|
|
42
|
+
url: '',
|
|
43
|
+
title: 'iframe',
|
|
44
|
+
sandbox: undefined,
|
|
45
|
+
allow: undefined,
|
|
46
|
+
placeholder: '请设置 iframe 地址',
|
|
47
|
+
showMask: true,
|
|
48
|
+
backgroundColor: '#ffffff',
|
|
49
|
+
borderRadius: 0,
|
|
50
|
+
border: '1px solid #dcdfe6',
|
|
51
|
+
opacity: 100,
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// 样式
|
|
56
|
+
const containerStyle = computed<CSSProperties>(() => ({
|
|
57
|
+
width: '100%',
|
|
58
|
+
height: '100%',
|
|
59
|
+
backgroundColor: props.backgroundColor,
|
|
60
|
+
borderRadius: `${props.borderRadius}px`,
|
|
61
|
+
overflow: 'hidden',
|
|
62
|
+
border: props.border,
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
const iframeStyle = computed<CSSProperties>(() => ({
|
|
66
|
+
width: '100%',
|
|
67
|
+
height: '100%',
|
|
68
|
+
border: 'none',
|
|
69
|
+
opacity: props.opacity / 100,
|
|
70
|
+
}))
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.iframe-container {
|
|
75
|
+
position: relative;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.iframe-mask {
|
|
79
|
+
position: absolute;
|
|
80
|
+
top: 0;
|
|
81
|
+
left: 0;
|
|
82
|
+
width: 100%;
|
|
83
|
+
height: 100%;
|
|
84
|
+
background: transparent;
|
|
85
|
+
z-index: 1;
|
|
86
|
+
pointer-events: auto;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* 运行时模式下移除遮罩 */
|
|
90
|
+
:global(.runtime-mode) .iframe-mask {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.iframe-placeholder {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
align-items: center;
|
|
99
|
+
width: 100%;
|
|
100
|
+
height: 100%;
|
|
101
|
+
color: #909399;
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
gap: 8px;
|
|
104
|
+
background-color: #f5f7fa;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.iframe-placeholder .el-icon {
|
|
108
|
+
font-size: 48px;
|
|
109
|
+
color: #c0c4cc;
|
|
110
|
+
}
|
|
111
|
+
</style>
|