Compare commits

...

72 Commits

Author SHA1 Message Date
奔跑的面条
5b37edc558 feat: 优化监听vCharts配置的写法 2024-12-24 17:35:53 +08:00
奔跑的面条
a698191873 feat: 处理field为字符串的修改场景 2024-12-23 09:32:16 +08:00
奔跑的面条
3ae9aac2d9 feat: 新增数据mock,数据字段映射 2024-12-22 12:51:02 +08:00
skie1997
152786c652 feat: supply option panel of gloabl and bar chart setting 2024-12-20 16:43:55 +08:00
奔跑的面条
026e4809c8 feat: 优化主题切换代码 2024-12-18 09:01:21 +08:00
奔跑的面条
5d6850b47d feat: 完善颜色选择 2024-12-17 21:35:37 +08:00
奔跑的面条
844b95987e feat: 修改切换主题的UI 2024-12-16 21:01:26 +08:00
奔跑的面条
b3d9005f7d feat: vchart 全局主题切换 2024-12-16 20:57:19 +08:00
奔跑的面条
f48ada2859 feat: 新增堆叠图 2024-12-16 20:05:07 +08:00
奔跑的面条
6106a8fc5c feat: 初始化图表整体流程 2024-12-15 12:48:36 +08:00
奔跑的面条
0972ea0e28 feat: 初始化 VChart 2024-12-15 00:45:49 +08:00
奔跑的面条
8d3ff7cc26 fix: 修改预览的临时存储方式 2024-12-02 10:40:35 +08:00
奔跑的面条
55d7fa84eb build: 修改淘宝源地址 2024-11-21 21:03:54 +08:00
奔跑的面条
e420796e33 fix: 处理图标内置旋转没生效的问题 2024-11-21 21:02:28 +08:00
奔跑的面条
78d8d57117 !249 fix https://gitee.com/dromara/go-view/issues/I9V40T 基础分页表格使用动态请求时,表格数据不更新
Merge pull request !249 from Ming/dev
2024-11-21 12:55:48 +00:00
奔跑的面条
663733b4d4 !247 修复点击无数据源组件后,再切换成有数据源的组件时,无法操作数据内容问题
Merge pull request !247 from Liuunicorn/N/A
2024-11-20 12:16:43 +00:00
Ming
68955f8f32 fix:table组件无法发起api请求 2024-11-09 01:34:01 +08:00
Liuunicorn
286bcc5576 修复点击无数据源组件后,再切换成有数据源的组件时,无法操作数据内容问题
Signed-off-by: Liuunicorn <8532757+Liuunicorn@user.noreply.gitee.com>
2024-09-19 07:16:11 +00:00
奔跑的面条
84890550c8 feat: 左下角新增Pro入口 2024-08-29 22:46:10 +08:00
奔跑的面条
17a912e558 feat: 修改跳转地址 2024-08-29 22:18:16 +08:00
奔跑的面条
0a12357997 feat: 只处理 'formatter' key 为函数结构,防止错误字段解析 2024-08-29 22:13:04 +08:00
奔跑的面条
2201c09219 feat: 优化饼图标签模糊的问题 2024-08-06 21:07:40 +08:00
zhong
ef912f91b2 update src/views/chart/ContentEdit/components/EditRule/index.vue.
修改 宽度*2  低于1920时拖不动和修改宽高时滚动条没变化

Signed-off-by: zhong <1781507490@qq.com>
2024-07-01 15:41:41 +08:00
奔跑的面条
0866ceccac feat: 标记分组预览时候的ID 2024-06-05 18:21:43 +08:00
奔跑的面条
526d01b16c fix: 解决动态返回dataset的字段数量不会自动适配的问题 2024-05-27 21:13:45 +08:00
奔跑的面条
7e424bfcda !236 update src/views/chart/ContentEdit/components/EditRule/index.vue.
Merge pull request !236 from zhong/N/A
2024-05-14 11:58:59 +08:00
奔跑的面条
8eb86f2a8e feat: 处理移动端拖拽 2024-04-26 16:54:36 +08:00
奔跑的面条
1acb949f33 !233 解决BarLine中折线图不跟随主题色改变
Merge pull request !233 from 万福熊/dev
2024-04-26 02:41:15 +00:00
万福熊
2e941b8a12 fix: 解决BarLine中折线图不跟随主题色改变 2024-04-26 10:12:28 +08:00
奔跑的面条
a9d010c0d9 fix: 处理成组层级不对的问题 2024-04-26 10:07:36 +08:00
奔跑的面条
8e22972551 feat: 优化地址请求健壮性 2024-04-24 14:38:39 +08:00
奔跑的面条
47af4593e6 !232 fix:解决拖动左侧组件到右侧配置面板(不是编辑器内)后,编辑器界面,不能操作问题
Merge pull request !232 from Vae/dev-fix-dragend
2024-04-19 01:38:09 +00:00
Vae
852b1e5317 解决拖动左侧组件到右侧配置面板(不是编辑器内)后,编辑器界面,不能操作问题 2024-04-18 16:45:58 +08:00
奔跑的面条
026b07721a perf: 优化顶部布局 2024-04-17 10:18:49 +08:00
奔跑的面条
3bfbee4cc1 perf: 优化顶部布局 2024-04-17 10:15:47 +08:00
奔跑的面条
d7c4090c96 perf: 优化图标顺序 2024-04-10 16:16:53 +08:00
奔跑的面条
86e47190b6 feat: 修改输入框展示图 2024-04-10 16:11:26 +08:00
奔跑的面条
e2d93053ba fix: 处理初始化会请求两次的问题 2024-04-10 15:24:10 +08:00
奔跑的面条
943cabf5cd perf: 去除多余流程组件,优化流程线交互 2024-04-10 15:08:51 +08:00
奔跑的面条
7d5af8f4f1 feat: 去除部分流程内容 2024-04-10 12:18:01 +08:00
奔跑的面条
0c9ee29d02 !227 新增流程通用组件
Merge pull request !227 from 阿飞/dev1
2024-04-10 03:50:47 +00:00
奔跑的面条
923b27d982 feat: 市场引导 2024-04-10 11:25:27 +08:00
奔跑的面条
ae6641daa0 perf: 优化框选的样式 2024-04-10 11:05:23 +08:00
奔跑的面条
792c403f21 perf: 处理组件悬浮圆角 2024-04-10 10:54:03 +08:00
奔跑的面条
18889bc5cf perf: 还原拖拽标记 2024-04-10 10:49:56 +08:00
奔跑的面条
81fc343a08 !229 feat:删除画布背景和组件高亮的圆角,优化组件高亮后的样式,方便布局和定位,减少设计者的干扰
Merge pull request !229 from QuietlyChan/feat-ui
2024-04-10 02:48:16 +00:00
奔跑的面条
476c680574 !231 fix:修复编辑区域右下角工具栏样式不居中bug,并调整导入导出图标
Merge pull request !231 from QuietlyChan/fix-ui
2024-04-10 02:44:42 +00:00
QuietlyChan
319edf6a9c fix:修复编辑区域右下角工具栏样式不居中bug,并调整导入导出图标 2024-04-09 16:00:17 +08:00
QuietlyChan
39a7815d42 feat:删除画布背景和组件高亮的圆角,优化组件高亮后的样式,方便布局和定位,减少设计者的干扰 2024-04-03 17:22:30 +08:00
luoyp
ebc084f7e8 feat: 菜单栏新增流程项 2024-03-27 10:58:54 +08:00
luoyp
c7022cd7fc feat: 新增流程图相关组件 2024-03-27 10:58:21 +08:00
奔跑的面条
04bc68c3ce perf: 优化底部展示 2024-03-21 08:25:01 +08:00
奔跑的面条
fe8ca5a447 !225 feat: 数据接口管理弹窗增加取消按钮,并增加ESC关闭弹窗
Merge pull request !225 from QuietlyChan/fix
2024-03-21 00:22:34 +00:00
奔跑的面条
9aaf06abeb perf: 优化地图的悬停文案 2024-03-21 08:10:26 +08:00
QuietlyChan
4355f3dac0 feat: 登录页面增加回车快捷键登录 2024-03-18 17:52:47 +08:00
QuietlyChan
5b0840b92d feat: 数据接口管理弹窗增加取消按钮,并增加ESC关闭弹窗 2024-03-18 17:47:36 +08:00
奔跑的面条
82349ba28b fix: 解决视频组件动态数据源的问题 2024-03-14 17:12:22 +08:00
奔跑的面条
caad259e71 feat: 处理未展示南海,进入下级还会出现的问题 2024-03-14 11:55:54 +08:00
奔跑的面条
87604c9cdb feat: 公共接口弹窗esc关闭问题 2024-03-14 11:46:46 +08:00
奔跑的面条
8801dbf64d build: 去除login页面多余内容 2024-03-14 11:43:11 +08:00
奔跑的面条
c578260fc7 build: 解决部分用户vue3-sketch-ruler打包报错的问题 2024-03-14 11:40:23 +08:00
奔跑的面条
99d398ae46 fix: 修改 TS 错误 2024-03-14 10:22:47 +08:00
奔跑的面条
1640a0580d !224 feat:按照GIS思想优化高德地图组件参数UI,并增加全新卫星图层、路网图层、实时交通图层的配置
Merge pull request !224 from QuietlyChan/dev
2024-03-14 02:21:53 +00:00
QuietlyChan
a60bf69197 feat:按照GIS思想优化高德地图组件参数UI,并增加全新卫星图层、路网图层、实时交通图层的配置 2024-03-09 16:35:43 +08:00
奔跑的面条
a4bb393913 !223 fix:解决在画布拖拽图表仅进行点击操作但未移动时,仍会将移动操作保存到操作历史记录中的问题
Merge pull request !223 from DrssX/dev
2024-03-06 03:09:32 +00:00
奔跑的面条
fddb834806 !219 update src/utils/type.ts.
Merge pull request !219 from chillley/N/A
2024-03-06 03:06:41 +00:00
奔跑的面条
5d6646ccda !222 build.brotliSize(改为了 build.reportCompressedSize)
Merge pull request !222 from uglyp/master-fetch-dev
2024-03-06 11:04:44 +08:00
奔跑的面条
bbb1c97c41 build: 解决编译ts报错 2024-03-06 11:03:11 +08:00
Async
a8ea82e125 fix: 修复画布中进行拖拽图表但未移动时仍会将该操作保存至历史记录问题 2024-03-03 12:01:43 +08:00
奔跑的面条
f4de3189ec perf: 修改散点图的tooltip问题 2024-01-07 00:28:04 +08:00
chillley
2dec349f18 update src/utils/type.ts.
toNumber方法传入参数toFixedNumber并未生效 , toFixed(2) 为固定保存两位小数 , 此处PR修复了参数传入并生效问题

Signed-off-by: chillley <chillley@163.com>
2023-12-22 06:45:29 +00:00
奔跑的面条
ac4e4aa0a2 fix: 解决柱状图&折线图的问题 2023-12-21 21:25:05 +08:00
118 changed files with 5824 additions and 2135 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ dist-ssr
*.local
.vscode
.idea
pnpm-lock

View File

@@ -7,10 +7,8 @@
<meta name="description" content="GoView 是高效、高性能的拖拽式低代码数据可视化开发平台,将页面元素封装为基础组件,无需编写代码即可完成业务需求。">
<meta name="keywords" content="GoViewgoview低代码可视化">
<meta name="author" content="奔跑的面条,面条">
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=0">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=yes" media="(min-width: 769px)">
<link rel="icon" href="./favicon.ico" />
<title>GoView</title>
<link rel="stylesheet" href="./index.css" />

View File

@@ -2,7 +2,7 @@
"name": "go-view",
"version": "1.1.11",
"engines": {
"node": ">=16.14 <18.0.0"
"node": ">=16.14"
},
"scripts": {
"dev": "vite --host",
@@ -21,6 +21,8 @@
"@types/crypto-js": "^4.1.1",
"@types/keymaster": "^1.6.30",
"@types/lodash": "^4.14.184",
"@visactor/vchart": "^1.12.12",
"@visactor/vchart-theme": "^1.12.2",
"animate.css": "^4.1.1",
"axios": "^1.4.0",
"color": "^4.2.3",
@@ -37,11 +39,11 @@
"keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0",
"naive-ui": "2.34.3",
"naive-ui": "2.40.3",
"pinia": "^2.0.13",
"screenfull": "^6.0.1",
"three": "^0.145.0",
"vue": "^3.2.31",
"vue": "^3.5.13",
"vue-demi": "^0.13.1",
"vue-i18n": "9.2.2",
"vue-router": "4.0.12",

5116
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import test from './test.mock'
import vchart from './vchart.mock'
import { MockMethod } from 'vite-plugin-mock'
import { RequestHttpEnum } from '@/enums/httpEnum'
@@ -22,6 +23,8 @@ export const threeEarth01Url = '/mock/threeEarth01Data'
export const sankeyUrl = '/mock/sankey'
export const graphUrl = '/mock/graphData'
export const vchartBarDataUrl = '/mock/vchart/barDataUrl'
const mockObject: MockMethod[] = [
{
// 正则
@@ -115,6 +118,11 @@ const mockObject: MockMethod[] = [
method: RequestHttpEnum.GET,
response: () => test.graphData
},
{
url: vchartBarDataUrl,
method: RequestHttpEnum.GET,
response: () => vchart.bar
}
]
export default mockObject

View File

@@ -0,0 +1,10 @@
import bar from './vchart/bar.json'
export default {
bar: {
code: 0,
status: 200,
msg: '请求成功',
data: bar
}
}

View File

@@ -0,0 +1,16 @@
{
"values": [
{ "type": "Nail polish", "year": "Africa", "value|100-900": 3 },
{ "type": "Nail polish", "year": "EU", "value|100-900": 3 },
{ "type": "Nail polish", "year": "China", "value|100-900": 3 },
{ "type": "Nail polish", "year": "USA", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "Africa", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "EU", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "China", "value|100-900": 3 },
{ "type": "Eyebrow pencil", "year": "USA", "value|100-900": 3 },
{ "type": "Rouge", "year": "Africa", "value|100-900": 3 },
{ "type": "Rouge", "year": "EU", "value|100-900": 3 },
{ "type": "Rouge", "year": "China", "value|100-900": 3 },
{ "type": "Rouge", "year": "USA", "value|100-900": 3 }
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,3 @@
import GoVChart from './index.vue'
export { GoVChart }

View File

@@ -0,0 +1,242 @@
<template>
<div
ref="vChartRef"
v-on="{
...Object.fromEntries(event.map((eventName: string) => [eventName, (eventData: MouseEvent) => eventHandlers(eventData, eventName)]))
}"
></div>
</template>
<script setup lang="ts">
import { ref, PropType, watch, onBeforeUnmount, nextTick, toRaw, toRefs } from 'vue'
import { VChart, type IVChart, type IInitOption, type ISpec } from '@visactor/vchart'
import { transformHandler } from './transformProps'
import { IOption } from '@/packages/components/VChart/index.d'
// 事件说明 v1.13.0 https://www.visactor.io/vchart/api/API/event
const event = [
'mousedown',
'mouseup',
'mouseupoutside',
'rightdown',
'rightup',
'rightupoutside',
'click',
'dblclick',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
'wheel',
'touchstart',
'touchend',
'touchendoutside',
'touchmove',
'touchcancel',
'tap',
'dragstart',
'dragend',
'drag',
'dragenter',
'dragleave',
'dragover',
'drop',
'pan',
'panstart',
'panend',
'press',
'pressup',
'pressend',
'pinch',
'pinchstart',
'pinchend',
'swipe',
'dimensionHover',
'dimensionClick',
'dataZoomChange',
'scrollBarChange',
'brushStart',
'brushChange',
'brushEnd',
'brushClear',
'drill',
'legendItemClick',
'legendItemHover',
'legendItemUnHover',
'legendFilter',
'initialized',
'rendered',
'renderFinished',
'animationFinished',
'layoutStart',
'layoutEnd',
'afterResizef'
]
const emit = defineEmits([
'mousedown',
'mouseup',
'mouseupoutside',
'rightdown',
'rightup',
'rightupoutside',
'click',
'dblclick',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
'wheel',
'touchstart',
'touchend',
'touchendoutside',
'touchmove',
'touchcancel',
'tap',
'dragstart',
'dragend',
'drag',
'dragenter',
'dragleave',
'dragover',
'drop',
'pan',
'panstart',
'panend',
'press',
'pressup',
'pressend',
'pinch',
'pinchstart',
'pinchend',
'swipe',
'dimensionHover',
'dimensionClick',
'dataZoomChange',
'scrollBarChange',
'brushStart',
'brushChange',
'brushEnd',
'brushClear',
'drill',
'legendItemClick',
'legendItemHover',
'legendItemUnHover',
'legendFilter',
'initialized',
'rendered',
'renderFinished',
'animationFinished',
'layoutStart',
'layoutEnd',
'afterResizef'
])
const props = defineProps({
option: {
type: Object as PropType<
IOption & {
dataset: any
}
>,
required: true
},
initOptions: {
type: Object as PropType<
IInitOption & {
deepWatch?: boolean | number
}
>,
required: false,
default: () => ({})
}
})
const vChartRef = ref()
let chart: IVChart
// 解构 props.option排除 dataset
const { dataset, ...restOfOption } = toRefs(props.option)
// 排除 data 监听
watch(
() => ({
...restOfOption
}),
() => {
nextTick(() => {
createOrUpdateChart(props.option)
})
},
{
deep: props.initOptions?.deepWatch || true,
immediate: true
}
)
watch(
() => dataset.value,
() => {
nextTick(() => {
createOrUpdateChart(props.option)
})
},
{
deep: false
}
)
// 更新
const createOrUpdateChart = (
chartProps: IOption & {
dataset: any
}
) => {
if (vChartRef.value && !chart) {
const spec = transformHandler[chartProps.category](chartProps)
chart = new VChart(
{ ...spec, data: chartProps.dataset },
{
dom: vChartRef.value,
...props.initOptions
}
)
chart.renderSync()
return true
} else if (chart) {
const spec = transformHandler[chartProps.category](chartProps)
chart.updateSpec({ ...spec, data: toRaw(chartProps.dataset), dataset: undefined })
return true
}
return false
}
// 刷新
const refresh = () => {
if (chart) {
chart.renderSync()
}
}
// 抛出事件
const eventHandlers = (eventData: MouseEvent, eventName: string) => {
if (event.includes(eventName)) emit(eventName as any, eventData)
}
// 卸载
onBeforeUnmount(() => {
if (chart) {
chart.release()
}
})
defineExpose({
// 重刷新
refresh,
release: () => {
if (chart) {
chart.release()
}
}
})
</script>

View File

@@ -0,0 +1,32 @@
import { cloneDeep } from "lodash"
export default (chartProps: any) => {
const spec = cloneDeep(chartProps)
delete spec.category
// tooltip
const keyFill = spec.tooltip.style.keyLabel.fill
const valueFill = spec.tooltip.style.valueLabel.fill
const titleFill = spec.tooltip.style.titleLabel.keyFill
delete spec.tooltip.style.keyLabel.fill
delete spec.tooltip.style.valueLabel.fill
delete spec.tooltip.style.titleLabel.keyFill
spec.tooltip.style.keyLabel.fontColor = keyFill
spec.tooltip.style.valueLabel.fontColor = valueFill
spec.tooltip.style.titleLabel.fontColor = titleFill
// axis
const { name: xAxisName, ...restXAxisProps } = chartProps.xAxis
const { name: yAxisName, ...restYAxisProps } = chartProps.yAxis
spec.axes = [{
orient: 'bottom',
...restXAxisProps
}, {
orient: 'left',
...restYAxisProps
}]
delete spec.xAxis
delete spec.yAxis
console.log('spec-transform', spec)
return spec
}

View File

@@ -0,0 +1,8 @@
import { ChatCategoryEnum, IOption } from "@/packages/components/VChart/index.d";
import bars from './bars'
export const transformHandler: {
[key: string]: (args: IOption) => any
} = {
[ChatCategoryEnum.BAR]: bars,
// todo: more charts handler
}

View File

@@ -0,0 +1,71 @@
<template>
<collapse-item v-model:name="axis.name">
<template #header>
<n-switch v-model:value="axis.visible" size="small"></n-switch>
</template>
<setting-item-box name="轴标签">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.label.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name="角度">
<n-input-number v-model:value="axis.label.style.angle" :min="0" :max="360" size="small" />
</setting-item>
<FontStyle :style="axis.label.style"></FontStyle>
</setting-item-box>
<setting-item-box name="轴标题">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.title.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name="标题内容">
<n-input v-model:value="axis.title.style.text" size="small"></n-input>
</setting-item>
<FontStyle :style="axis.title.style"></FontStyle>
</setting-item-box>
<setting-item-box name="轴线">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.domainLine.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name=""> </setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="axis.domainLine.style.lineWidth" :min="0" size="small" />
</setting-item>
<setting-item name="颜色">
<n-color-picker v-model:value="axis.domainLine.style.stroke" size="small" />
</setting-item>
</setting-item-box>
<setting-item-box name="网格线">
<setting-item name="可见性">
<n-space>
<n-switch v-model:value="axis.grid.visible" size="small"></n-switch>
</n-space>
</setting-item>
<setting-item name=""> </setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="axis.grid.style.lineWidth" :min="0" size="small" />
</setting-item>
<setting-item name="颜色">
<n-color-picker v-model:value="axis.grid.style.stroke" size="small" />
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
axis: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,38 @@
<template>
<!-- todo 补充常用配置项 -->
<div v-if="optionData.legends">
<div v-for="(legendItem, index) in optionData.legends" :key="index">
<collapse-item name="图例">
<template #header>
<n-switch v-model:value="legendItem.visible" size="small"></n-switch>
</template>
<setting-item-box name="布局">
<setting-item name="位置">
<n-select v-model:value="legendItem.orient" size="small" :options="legendsConfig.orient" />
</setting-item>
<setting-item name="对齐方式">
<n-select v-model:value="legendItem.position" size="small" :options="legendsConfig.position" />
</setting-item>
</setting-item-box>
<setting-item-box name="项配置">
<FontStyle :style="legendItem.item.label.style"></FontStyle>
</setting-item-box>
</collapse-item>
</div>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { legendsConfig } from '@/packages/chartConfiguration/vcharts/index'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,47 @@
<template>
<!-- todo 补充常用配置项 -->
<div v-if="optionData.tooltip">
<collapse-item name="提示框">
<template #header>
<n-switch v-model:value="optionData.tooltip.visible" size="small"></n-switch>
</template>
<setting-item-box name="框">
<setting-item name="填充">
<n-color-picker v-model:value="optionData.tooltip.style.panel.backgroundColor" size="small" />
</setting-item>
<setting-item name="瞄边">
<n-color-picker v-model:value="optionData.tooltip.style.panel.border.color" size="small" />
</setting-item>
<setting-item name="粗细">
<n-input-number v-model:value="optionData.tooltip.style.panel.border.width" :min="0" size="small" />
</setting-item>
<setting-item name="圆角">
<n-input-number v-model:value="optionData.tooltip.style.panel.border.radius" :min="0" size="small" />
</setting-item>
</setting-item-box>
<setting-item-box name="标题">
<FontStyle :style="optionData.tooltip.style.titleLabel"></FontStyle>
</setting-item-box>
<setting-item-box name="名称">
<FontStyle :style="optionData.tooltip.style.keyLabel"></FontStyle>
</setting-item-box>
<setting-item-box name="值">
<FontStyle :style="optionData.tooltip.style.valueLabel"></FontStyle>
</setting-item-box>
</collapse-item>
</div>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import FontStyle from './common/FontStyle.vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,18 @@
<template>
<!-- 图例 -->
<Legends :optionData="optionData"></Legends>
<Tooltip :optionData="optionData"></Tooltip>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
import Legends from './Legends.vue'
import Tooltip from './Tooltip.vue'
const props = defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,33 @@
<template>
<!-- todo 补充常用配置项 -->
<!-- <div v-if="style"> -->
<!-- <setting-item-box v-if="style" name=""> -->
<setting-item name="颜色">
<n-color-picker v-model:value="style.fill" size="small" />
</setting-item>
<setting-item name="大小">
<n-input-number v-model:value="style.fontSize" :min="1" size="small" />
</setting-item>
<setting-item name="字体">
<n-select v-model:value="style.fontFamily" :options="fontStyleConfig.fontFamily" size="small" />
</setting-item>
<setting-item name="字重">
<n-select v-model:value="style.fontSize" :options="fontStyleConfig.fontWeight" size="small" />
</setting-item>
<!-- </setting-item-box> -->
<!-- </div> -->
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { fontStyleConfig } from '@/packages/chartConfiguration/vcharts/index'
import { FontType } from '@/settings/vchartThemes/index'
import { SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
style: {
type: Object as PropType<FontType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,4 @@
import VChartGlobalSetting from './VChartGlobalSetting.vue'
import Axis from './Axis.vue'
export { VChartGlobalSetting, Axis }

View File

@@ -5,4 +5,5 @@ export * from '@/hooks/useChartDataFetch.hook'
export * from '@/hooks/useChartDataPondFetch.hook'
export * from '@/hooks/useLifeHandler.hook'
export * from '@/hooks/useLang.hook'
export * from '@/hooks/useChartInteract.hook'
export * from '@/hooks/useChartInteract.hook'
export * from '@/hooks/useVCharts.hook'

View File

@@ -7,6 +7,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { RequestDataTypeEnum } from '@/enums/httpEnum'
import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils'
import { setOption } from '@/packages/public/chart'
import { isNil } from 'lodash'
// 获取类型
type ChartEditStoreType = typeof useChartEditStore
@@ -35,7 +36,7 @@ export const useChartDataFetch = (
const echartsUpdateHandle = (dataset: any) => {
if (chartFrame === ChartFrameEnum.ECHARTS) {
if (vChartRef.value) {
setOption(vChartRef.value, { dataset: dataset })
setOption(vChartRef.value, { dataset: dataset }, false)
}
}
}
@@ -101,14 +102,12 @@ export const useChartDataFetch = (
)
// 定时时间
const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value
const time = targetInterval && !isNil(targetInterval.value) ? targetInterval.value : globalRequestInterval.value
// 单位
const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value
const unit = targetInterval && !isNil(targetInterval.value) ? targetUnit.value : globalUnit.value
// 开启轮询
if (time) {
fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
} else {
fetchFn()
}
}
// eslint-disable-next-line no-empty

View File

@@ -0,0 +1,164 @@
import { watch } from 'vue'
import { VChart, type ITheme } from '@visactor/vchart'
import light from '@visactor/vchart-theme/public/light.json'
import dark from '@visactor/vchart-theme/public/dark.json'
import vScreenVolcanoBlue from '@visactor/vchart-theme/public/vScreenVolcanoBlue.json'
import vScreenClean from '@visactor/vchart-theme/public/vScreenClean.json'
import vScreenOutskirts from '@visactor/vchart-theme/public/vScreenOutskirts.json'
import vScreenBlueOrange from '@visactor/vchart-theme/public/vScreenBlueOrange.json'
import vScreenFinanceYellow from '@visactor/vchart-theme/public/vScreenFinanceYellow.json'
import vScreenWenLvCyan from '@visactor/vchart-theme/public/vScreenWenLvCyan.json'
import vScreenElectricGreen from '@visactor/vchart-theme/public/vScreenElectricGreen.json'
import vScreenECommercePurple from '@visactor/vchart-theme/public/vScreenECommercePurple.json'
import vScreenRedBlue from '@visactor/vchart-theme/public/vScreenRedBlue.json'
import vScreenPartyRed from '@visactor/vchart-theme/public/vScreenPartyRed.json'
// 行业色板
import veODesignLightFinance from '@visactor/vchart-theme/public/veODesignLightFinance.json'
import veODesignDarkFinance from '@visactor/vchart-theme/public/veODesignDarkFinance.json'
import veODesignLightGovernment from '@visactor/vchart-theme/public/veODesignLightGovernment.json'
import veODesignDarkGovernment from '@visactor/vchart-theme/public/veODesignDarkGovernment.json'
import veODesignLightConsumer from '@visactor/vchart-theme/public/veODesignLightConsumer.json'
import veODesignDarkConsumer from '@visactor/vchart-theme/public/veODesignDarkConsumer.json'
import veODesignLightAutomobile from '@visactor/vchart-theme/public/veODesignLightAutomobile.json'
import veODesignDarkAutomobile from '@visactor/vchart-theme/public/veODesignDarkAutomobile.json'
import veODesignLightCulturalTourism from '@visactor/vchart-theme/public/veODesignLightCulturalTourism.json'
import veODesignDarkCulturalTourism from '@visactor/vchart-theme/public/veODesignDarkCulturalTourism.json'
import veODesignLightMedical from '@visactor/vchart-theme/public/veODesignLightMedical.json'
import veODesignDarkMedical from '@visactor/vchart-theme/public/veODesignDarkMedical.json'
import veODesignLightNewEnergy from '@visactor/vchart-theme/public/veODesignLightNewEnergy.json'
import veODesignDarkNewEnergy from '@visactor/vchart-theme/public/veODesignDarkNewEnergy.json'
const themeMap = {
// 明亮
light: light,
// 暗黑
dark: dark,
// 火山蓝
vScreenVolcanoBlue: vScreenVolcanoBlue,
// 党建红
vScreenPartyRed: vScreenPartyRed,
// 清新蜡笔
vScreenClean: vScreenClean,
// 郊外
vScreenOutskirts: vScreenOutskirts,
// 汽车蓝橙
vScreenBlueOrange: vScreenBlueOrange,
// 金融黄
vScreenFinanceYellow: vScreenFinanceYellow,
// 文旅青
vScreenWenLvCyan: vScreenWenLvCyan,
// 电力绿
vScreenElectricGreen: vScreenElectricGreen,
// 电商紫
vScreenECommercePurple: vScreenECommercePurple,
// 红蓝
vScreenRedBlue: vScreenRedBlue,
// 金融行业色板
veODesignLightFinance: veODesignLightFinance,
veODesignDarkFinance: veODesignDarkFinance,
// 政府行业色板
veODesignLightGovernment: veODesignLightGovernment,
veODesignDarkGovernment: veODesignDarkGovernment,
// 消费行业色板
veODesignLightConsumer: veODesignLightConsumer,
veODesignDarkConsumer: veODesignDarkConsumer,
// 汽车行业色板
veODesignLightAutomobile: veODesignLightAutomobile,
veODesignDarkAutomobile: veODesignDarkAutomobile,
// 文旅行业色板
veODesignLightCulturalTourism: veODesignLightCulturalTourism,
veODesignDarkCulturalTourism: veODesignDarkCulturalTourism,
// 医疗行业色板
veODesignLightMedical: veODesignLightMedical,
veODesignDarkMedical: veODesignDarkMedical,
// 新能源行业色板
veODesignLightNewEnergy: veODesignLightNewEnergy,
veODesignDarkNewEnergy: veODesignDarkNewEnergy
}
export const useVCharts = () => {
const getThemeMap = () => {
return themeMap
}
// 注册主题(支持自定义主题)
const registerTheme = (themeName: keyof typeof themeMap, theme: any) => {
VChart.ThemeManager.registerTheme(themeName, (themeMap[themeName] as any) || theme)
}
// 设置当前主题
const setCurrentTheme = (themeName = 'vScreenVolcanoBlue') => {
VChart.ThemeManager.setCurrentTheme(themeName)
}
// 判断主题是否存在
const themeExist = (name: string): boolean => {
return VChart.ThemeManager.themeExist(name)
}
// 获取主题
const getTheme = (name: string): ITheme => {
return VChart.ThemeManager.getTheme(name)
}
// 获取当前主题
const getCurrentTheme = (): ITheme => {
return VChart.ThemeManager.getCurrentTheme()
}
// 设置主题
const setTheme = (name: keyof typeof themeMap): boolean => {
if (themeExist(name)) {
setCurrentTheme(name)
return true
} else {
// 先注册
const theme = themeMap[name]
if (theme) {
registerTheme(name, theme)
setCurrentTheme(name)
return true
} else {
// 注册默认主题
registerTheme('vScreenVolcanoBlue', vScreenVolcanoBlue)
}
}
return false
}
return {
getThemeMap,
registerTheme,
setCurrentTheme,
themeExist,
getTheme,
setTheme,
getCurrentTheme
}
}
// 主题初始化
export const useInitVChartsTheme = (chartEditStore: any) => {
const vCharts = useVCharts()
const initVChartsThemeIns = watch(
() => chartEditStore.getEditCanvasConfig.vChartThemeName,
(newTheme: string) => {
vCharts.setTheme(newTheme as any)
},
{
immediate: true
}
)
return {
initVChartsThemeIns
}
}

View File

@@ -38,18 +38,16 @@ const isProject = computed(() => {
</script>
<style lang="scss" scoped>
$min-width: 520px;
@include go(header) {
&-box {
display: grid;
grid-template-columns: repeat(3, 33%);
display: flex;
justify-content: space-between;
&.is-project {
grid-template-columns: none;
}
.header-item {
display: flex;
align-items: center;
min-width: $min-width;
&.left {
justify-content: start;
}

View File

@@ -3,5 +3,11 @@ export const labelConfig = {
{ label: '外侧', value: 'outside' },
{ label: '内部', value: 'inside' },
{ label: '中心', value: 'center' }
],
fontWeight: [
{ label: '正常', value: 'normal' },
{ label: '普通加粗', value: 'bold' },
{ label: '加粗', value: 'bolder' },
{ label: '细体', value: 'lighter' }
]
}

View File

@@ -0,0 +1 @@
export * from './legends'

View File

@@ -0,0 +1,99 @@
export const legendsConfig = {
// 位置
orient: [
{
label: '顶部',
value: 'top'
},
{
label: '底部',
value: 'bottom'
},
{
label: '左侧',
value: 'left'
},
{
label: '右侧',
value: 'right'
}
],
// 对齐方式
position: [
{
label: '起始',
value: 'start'
},
{
label: '居中',
value: 'middle'
},
{
label: '末尾',
value: 'end'
}
],
// 每一项的图例位置
align: [
{
label: '居左',
value: 'left'
},
{
label: '居右',
value: 'right'
}
]
}
export const fontStyleConfig = {
// 字重
fontWeight: [
{
label: '100',
value: 100
},
{
label: '200',
value: 200
},
{
label: '300',
value: 300
},
{
label: '400',
value: 400
},
{
label: '500',
value: 500
},
{
label: '600',
value: 600
},
{
label: '正常',
value: "normal"
},
{
label: '加粗',
value: "bold"
}
],
fontFamily: [
{
label: '宋体',
value: 'SimSun'
},
{
label: '黑体',
value: 'SimHei'
},
{
label: '楷体',
value: '楷体'
}
]
}

View File

@@ -28,7 +28,7 @@ export const option = {
type: 'shadow'
}
},
xAxis: {
xAxis: {
show: true,
type: 'category'
},

View File

@@ -1,8 +1,14 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()"
<v-chart
ref="vChartRef"
:init-options="initOptions"
:theme="themeColor"
:option="option"
:update-options="{
replaceMerge: replaceMergeArr
}" autoresize></v-chart>
}"
autoresize
></v-chart>
</template>
<script setup lang="ts">
@@ -85,5 +91,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -4,7 +4,6 @@
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
:update-options="{
replaceMerge: replaceMergeArr
}"
@@ -23,7 +22,6 @@ import { mergeTheme } from '@/packages/public/chart'
import config, { includes, seriesItem } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import isObject from 'lodash/isObject'
import cloneDeep from 'lodash/cloneDeep'
@@ -79,5 +77,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -33,7 +33,6 @@ export const lineSeriesItem = {
},
symbolSize: 5, //设定实心点的大小
itemStyle: {
color: '#FFE47A',
borderWidth: 1
},
lineStyle: {

View File

@@ -4,9 +4,30 @@
<CollapseItem
v-for="(item, index) in seriesList"
:key="index"
:name="`${item.type == 'bar' ? '柱状图' : '折线图'}`"
:name="`系列${index + 1}`"
:expanded="true"
>
<template #header>
<n-text class="go-fs-13" depth="3">
{{ item.type == 'bar' ? '「柱状图」' : '「折线图」' }}
</n-text>
</template>
<SettingItemBox name="类型">
<SettingItem name="宽度">
<n-select
:value="item.type"
size="small"
:options="[
{ label: '柱状图', value: 'bar' },
{ label: '折线图', value: 'line' }
]"
@update:value="(value: any) => {
updateHandle(item, value)
}"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="图形" v-if="item.type == 'bar'">
<SettingItem name="宽度">
<n-input-number
@@ -34,6 +55,12 @@
<SettingItem name="类型">
<n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
</SettingItem>
<setting-item>
<n-space>
<n-switch v-model:value="item.smooth" size="small" />
<n-text>曲线</n-text>
</n-space>
</setting-item>
</SettingItemBox>
<SettingItemBox name="实心点" v-if="item.type == 'line'">
<SettingItem name="大小">
@@ -63,10 +90,10 @@
<n-select
v-model:value="item.label.position"
:options="[
{ label: 'top', value: 'top' },
{ label: 'left', value: 'left' },
{ label: 'right', value: 'right' },
{ label: 'bottom', value: 'bottom' }
{ label: '顶部', value: 'top' },
{ label: '左侧', value: 'left' },
{ label: '右侧', value: 'right' },
{ label: '底部', value: 'bottom' }
]"
/>
</setting-item>
@@ -75,10 +102,18 @@
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { PropType, computed, toRaw } from 'vue'
import { merge, cloneDeep } from 'lodash';
import GlobalSetting from '@/components/Pages/ChartItemSetting/GlobalSetting.vue'
import CollapseItem from '@/components/Pages/ChartItemSetting/CollapseItem.vue'
import SettingItemBox from '@/components/Pages/ChartItemSetting/SettingItemBox.vue'
import SettingItem from '@/components/Pages/ChartItemSetting/SettingItem.vue'
import { lineConf } from '@/packages/chartConfiguration/echarts'
import { GlobalThemeJsonType } from '@/settings/chartThemes'
import { barSeriesItem, lineSeriesItem } from './config'
const props = defineProps({
optionData: {
@@ -90,4 +125,14 @@ const props = defineProps({
const seriesList = computed(() => {
return props.optionData.series
})
const updateHandle = (item:any, value:string) => {
const _label = cloneDeep(toRaw(item.label))
lineSeriesItem.label = _label
if (value === 'line') {
merge(item, lineSeriesItem)
} else {
merge(item, barSeriesItem)
}
}
</script>

View File

@@ -4,7 +4,9 @@
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
:update-options="{
replaceMerge: replaceMergeArr
}"
autoresize
></v-chart>
</template>
@@ -12,6 +14,7 @@
<script setup lang="ts">
import { ref, computed, watch, PropType, nextTick } from 'vue'
import VChart from 'vue-echarts'
import { isObject, cloneDeep } from 'lodash'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
@@ -51,17 +54,35 @@ const option = computed(() => {
watch(
() => props.chartConfig.option.dataset,
(newData, oldData) => {
if (newData.dimensions.length !== oldData.dimensions.length) {
const seriesArr = []
for (let i = 0; i < newData.dimensions.length - 1; i++) {
seriesArr.push(barSeriesItem, lineSeriesItem)
(newData: any, oldData) => {
try {
if (!isObject(newData) || !('dimensions' in newData)) return
if (Array.isArray((newData as any)?.dimensions)) {
const seriesArr: typeof barSeriesItem[] = []
// 对oldData进行判断防止传入错误数据之后对旧维度判断产生干扰
// 此处计算的是dimensions的Y轴维度若是dimensions.length为0或1则默认为1排除X轴维度干扰
const oldDimensions =
Array.isArray(oldData?.dimensions) && oldData.dimensions.length >= 1 ? oldData.dimensions.length : 1
const newDimensions = (newData as any).dimensions.length >= 1 ? (newData as any).dimensions.length : 1
const dimensionsGap = newDimensions - oldDimensions
if (dimensionsGap < 0) {
props.chartConfig.option.series.splice(newDimensions - 1)
} else if (dimensionsGap > 0) {
if (!oldData || !oldData?.dimensions || !Array.isArray(oldData?.dimensions) || !oldData?.dimensions.length) {
props.chartConfig.option.series = []
}
for (let i = 0; i < dimensionsGap; i++) {
seriesArr.push(cloneDeep(barSeriesItem))
}
props.chartConfig.option.series.push(...seriesArr)
}
replaceMergeArr.value = ['series']
nextTick(() => {
replaceMergeArr.value = []
})
}
replaceMergeArr.value = ['series']
props.chartConfig.option.series = seriesArr
nextTick(() => {
replaceMergeArr.value = []
})
} catch (error) {
console.log(error)
}
},
{
@@ -69,5 +90,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -4,7 +4,6 @@
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
:update-options="{
replaceMerge: replaceMergeArr
}"
@@ -79,5 +78,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -1,5 +1,5 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" :manual-update="isPreview()" autoresize>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" autoresize>
</v-chart>
</template>
@@ -82,5 +82,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -1,5 +1,5 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" :manual-update="isPreview()" autoresize></v-chart>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" autoresize></v-chart>
</template>
<script setup lang="ts">
@@ -80,5 +80,7 @@ watch(
option.value = props.chartConfig.option
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -1,5 +1,5 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" :manual-update="isPreview()" autoresize>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option.value" autoresize>
</v-chart>
</template>
@@ -79,5 +79,7 @@ watch(
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -16,8 +16,7 @@ export enum ThemeEnum {
MACARON = 'macaron',
BLUE = 'blue',
DARKBLUE = 'darkblue',
WINE = 'wine',
WEIXIN = 'tileLayer'
WINE = 'wine'
}
export enum LangEnum {
@@ -31,6 +30,11 @@ export enum ViewModeEnum {
STEREOSCOPIC = '3D'
}
export const ShowHideEnum = {
SHOW: true,
HIDE: false
}
export enum FeaturesEnum {
BG = 'bg',
POINT = 'point',
@@ -71,6 +75,25 @@ export const option = {
},
mapMarkerType: MarkerEnum.CIRCLE_MARKER,
viewMode: ViewModeEnum.PLANE,
showLabel: ShowHideEnum.SHOW,
satelliteTileLayer: {
show: ShowHideEnum.HIDE,
zIndex: 1,
opacity: 1,
zooms: [3, 18]
},
roadNetTileLayer: {
show: ShowHideEnum.HIDE,
zIndex: 2,
opacity: 1,
zooms: [3, 18]
},
trafficTileLayer: {
show: ShowHideEnum.HIDE,
zIndex: 3,
opacity: 1,
zooms: [3, 18]
},
lang: LangEnum.ZH_CN,
features: [FeaturesEnum.BG, FeaturesEnum.POINT, FeaturesEnum.ROAD, FeaturesEnum.BUILDING]
}

View File

@@ -22,13 +22,21 @@
<n-select size="small" v-model:value="optionData.mapOptions.amapStyleKey" :options="themeOptions" />
</setting-item>
</setting-item-box>
<setting-item-box name="内容" :alone="true">
<setting-item-box name="显示要素" :alone="true">
<n-checkbox-group v-model:value="optionData.mapOptions.features">
<n-space item-style="display: flex;">
<n-checkbox :value="item.value" :label="item.label" v-for="(item, index) in featuresOptions" :key="index" />
</n-space>
</n-checkbox-group>
</setting-item-box>
<setting-item-box name="文字标注" :alone="true">
<setting-item>
<n-space>
<n-switch v-model:value="optionData.mapOptions.showLabel" size="small" />
<n-text>是否显示</n-text>
</n-space>
</setting-item>
</setting-item-box>
<setting-item-box name="位置">
<setting-item name="经度">
<n-input-number v-model:value="optionData.mapOptions.amapLon" :show-button="false" size="small">
@@ -76,11 +84,94 @@
</setting-item>
</setting-item-box>
</collapse-item>
<collapse-item name="图层" :expanded="true">
<setting-item-box name="卫星图层">
<setting-item>
<n-space>
<n-switch v-model:value="optionData.mapOptions.satelliteTileLayer.show" size="small" />
<n-text>是否显示</n-text>
</n-space>
</setting-item>
<setting-item name="叠加顺序值">
<n-input-number
v-model:value="optionData.mapOptions.satelliteTileLayer.zIndex"
:min="0"
size="small"
></n-input-number>
</setting-item>
<setting-item name="透明度">
<n-input-number
v-model:value="optionData.mapOptions.satelliteTileLayer.opacity"
:min="0"
:max="1"
step="0.1"
size="small"
></n-input-number>
</setting-item>
<setting-item name="缩放级别范围">
<n-slider v-model:value="optionData.mapOptions.satelliteTileLayer.zooms" range :step="1" :max="18" :min="3" />
</setting-item>
</setting-item-box>
<setting-item-box name="路网图层">
<setting-item>
<n-space>
<n-switch v-model:value="optionData.mapOptions.roadNetTileLayer.show" size="small" />
<n-text>是否显示</n-text>
</n-space>
</setting-item>
<setting-item name="叠加顺序值">
<n-input-number
v-model:value="optionData.mapOptions.roadNetTileLayer.zIndex"
:min="0"
size="small"
></n-input-number>
</setting-item>
<setting-item name="透明度">
<n-input-number
v-model:value="optionData.mapOptions.roadNetTileLayer.opacity"
:min="0"
:max="1"
step="0.1"
size="small"
></n-input-number>
</setting-item>
<setting-item name="缩放级别范围">
<n-slider v-model:value="optionData.mapOptions.roadNetTileLayer.zooms" range :step="1" :max="18" :min="3" />
</setting-item>
</setting-item-box>
<setting-item-box name="实时交通">
<setting-item>
<n-space>
<n-switch v-model:value="optionData.mapOptions.trafficTileLayer.show" size="small" />
<n-text>是否显示</n-text>
</n-space>
</setting-item>
<setting-item name="叠加顺序值">
<n-input-number
v-model:value="optionData.mapOptions.trafficTileLayer.zIndex"
:min="0"
size="small"
></n-input-number>
</setting-item>
<setting-item name="透明度">
<n-input-number
v-model:value="optionData.mapOptions.trafficTileLayer.opacity"
:min="0"
:max="1"
step="0.1"
size="small"
></n-input-number>
</setting-item>
<setting-item name="缩放级别范围">
<n-slider v-model:value="optionData.mapOptions.trafficTileLayer.zooms" range :step="1" :max="18" :min="3" />
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option, MarkerEnum, ThemeEnum, LangEnum, ViewModeEnum, FeaturesEnum } from './config'
import { option, MarkerEnum, ThemeEnum, LangEnum, ViewModeEnum, ShowHideEnum, FeaturesEnum } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
defineProps({
@@ -134,10 +225,6 @@ const themeOptions = [
{
value: ThemeEnum.WINE,
label: '酱籽'
},
{
value: ThemeEnum.WEIXIN,
label: '卫星'
}
]
@@ -170,19 +257,19 @@ const viewModeOptions = [
const featuresOptions = [
{
value: FeaturesEnum.BG,
label: '显示地图背景'
label: '区域面'
},
{
value: FeaturesEnum.POINT,
label: '显示标识'
label: '标注'
},
{
value: FeaturesEnum.ROAD,
label: '显示道路'
label: '道路'
},
{
value: FeaturesEnum.BUILDING,
label: '显示建筑'
label: '建筑'
}
]

View File

@@ -28,9 +28,13 @@ let {
amapStyleKeyCustom,
features,
viewMode,
showLabel,
pitch,
skyColor,
marker
marker,
satelliteTileLayer,
roadNetTileLayer,
trafficTileLayer
} = toRefs(props.chartConfig.option.mapOptions)
let mapIns: any = null
@@ -42,7 +46,7 @@ const initMap = (newData: any) => {
// 初始化
AMapLoader.load({
key: amapKey.value, //api服务key--另外需要在public中使用安全密钥
version: '1.4.8', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
version: '1.4.15', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete'] // 需要使用的的插件列表
})
.then(AMap => {
@@ -56,17 +60,40 @@ const initMap = (newData: any) => {
pitch: pitch.value, // 地图俯仰角度,有效范围 0 度- 83 度
skyColor: skyColor.value,
viewMode: viewMode.value, // 地图模式
showLabel: showLabel.value, // 是否显示地图文字标记
willReadFrequently: true
})
dataHandle(props.chartConfig.option.dataset)
let satellite = new AMap.TileLayer.Satellite()
let roadNet = new AMap.TileLayer.RoadNet()
if (newData.amapStyleKey === ThemeEnum.WEIXIN) {
mapIns.add([satellite, roadNet])
} else {
mapIns.remove([satellite, roadNet])
mapIns.setMapStyle(`amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}`)
let satelliteLayer = new AMap.TileLayer.Satellite({
zIndex: satelliteTileLayer.value.zIndex,
opacity: satelliteTileLayer.value.opacity,
zooms: satelliteTileLayer.value.zooms
})
let roadNetLayer = new AMap.TileLayer.RoadNet({
zIndex: roadNetTileLayer.value.zIndex,
opacity: roadNetTileLayer.value.opacity,
zooms: roadNetTileLayer.value.zooms
})
let trafficLayer = new AMap.TileLayer.Traffic({
zIndex: trafficTileLayer.value.zIndex,
opacity: trafficTileLayer.value.opacity,
zooms: trafficTileLayer.value.zooms
})
mapIns.remove([satelliteLayer, roadNetLayer, trafficLayer])
if (satelliteTileLayer.value.show) {
mapIns.add([satelliteLayer])
}
if (roadNetTileLayer.value.show) {
mapIns.add([roadNetLayer])
}
if (trafficTileLayer.value.show) {
mapIns.add([trafficLayer])
}
mapIns.setMapStyle(
`amap://styles/${amapStyleKeyCustom.value !== '' ? amapStyleKeyCustom.value : amapStyleKey.value}`
)
})
.catch(e => {})
}

View File

@@ -81,8 +81,8 @@
</SettingItem>
</SettingItemBox>
<SettingItemBox name="悬浮 (预览可见)">
<SettingItem name="禁用">
<SettingItemBox name="鼠标悬停聚焦">
<SettingItem name="禁用(预览可见)">
<n-space>
<n-switch v-model:value="seriesList[1].emphasis.disabled" size="small"></n-switch>
</n-space>

View File

@@ -171,7 +171,19 @@ const backLevel = () => {
// 切换地图
const checkOrMap = async (newData: string) => {
await getGeojson(newData)
if (newData === 'china') {
if (props.chartConfig.option.mapRegion.showHainanIsLands) {
// 显示南海
hainanLandsHandle(true)
vEchartsSetOption()
} else {
// 隐藏南海
hainanLandsHandle(false)
vEchartsSetOption()
}
} else {
await getGeojson(newData)
}
props.chartConfig.option.geo.map = newData
props.chartConfig.option.series.forEach((item: any) => {
if (item.type === 'map') item.map = newData

View File

@@ -52,7 +52,11 @@ const option = {
show: false,
position: 'center',
formatter: '{b}',
fontSize:12
fontWeight: 'normal',
fontSize: 14,
color: '#454E54',
textBorderColor: '#ffffff',
textBorderWidth: 1
},
emphasis: {
label: {

View File

@@ -18,6 +18,20 @@
<n-text :depth="3">无鼠标点击图例场景时可强行打开图例</n-text>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="图形">
<setting-item name="内圈范围">
<n-input v-model:value="optionData.series[0].radius[0]" size="small"></n-input>
</setting-item>
<setting-item name="外圈范围">
<n-input v-model:value="optionData.series[0].radius[1]" size="small"></n-input>
</setting-item>
<setting-item name="X轴中心">
<n-input v-model:value="optionData.series[0].center[0]" size="small"></n-input>
</setting-item>
<setting-item name="Y轴中心">
<n-input v-model:value="optionData.series[0].center[1]" size="small"></n-input>
</setting-item>
</SettingItemBox>
<SettingItemBox name="标签">
<SettingItem>
<n-space>
@@ -38,28 +52,67 @@
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</setting-item>
</SettingItemBox>
<setting-item-box name="字体">
<setting-item name="大小">
<n-input-number v-model:value="optionData.series[0].label.fontSize" size="small" :min="0"></n-input-number>
</setting-item>
<setting-item name="颜色" v-if="optionData.series[0].label.color">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.series[0].label.color"></n-color-picker>
</setting-item>
<SettingItem name="文字加粗" v-if="optionData.series[0].label.fontWeight">
<n-select
v-model:value="optionData.series[0].label.fontWeight"
size="small"
:options="labelConfig.fontWeight"
/>
</SettingItem>
<setting-item name="文字边框大小" v-if="optionData.series[0].label.textBorderWidth">
<n-input-number
v-model:value="optionData.series[0].label.textBorderWidth"
size="small"
:min="0"
></n-input-number>
</setting-item>
<setting-item name="文字边框色" v-if="optionData.series[0].label.textBorderColor">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.series[0].label.textBorderColor"
></n-color-picker>
</setting-item>
</setting-item-box>
<setting-item-box name="圆角">
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
<n-text>圆角大小</n-text>
</n-space>
<setting-item name="圆角大小">
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
</setting-item>
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
<n-text>线条宽度</n-text>
</n-space>
<setting-item name="线条宽度">
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="鼠标聚焦">
<setting-item name="文字大小">
<n-input-number
v-model:value="optionData.series[0].emphasis.label.fontSize"
size="small"
:min="0"
></n-input-number>
</setting-item>
<SettingItem name="文字加粗" v-if="optionData.series[0].emphasis.label.fontWeight">
<n-select
v-model:value="optionData.series[0].emphasis.label.fontWeight"
size="small"
:options="labelConfig.fontWeight"
/>
</SettingItem>
</setting-item-box>
</CollapseItem>
</template>

View File

@@ -47,10 +47,9 @@ export const option = {
tooltip: {
showDelay: 0,
formatter: (params: { value: string | any[]; seriesName: string; name: string }) => {
// console.log(params)
return params.value.length > 1
? `${params.seriesName}<br />${params.value[0]} ${params.value[1]}`
: `${params.seriesName}<br />${params.name} ${params.value}`
: `${params.name} ${params.value}`
},
axisPointer: {
show: true,

View File

@@ -0,0 +1,19 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { FlowChartLineConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
endWidth: 15,
lineWidth: 2, //线条粗细
lineNum: 2, //向下数量
lineNumUp: 2, //向上数量
backgroundCol: '#303a4c', //线条背景
animateCol: '#3788ea' //流动动画背景
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = FlowChartLineConfig.key
public chartConfig = cloneDeep(FlowChartLineConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,37 @@
<template>
<CollapseItem name="线条" :expanded="true">
<SettingItemBox name="折线数量">
<SettingItem name="向下增加">
<n-input-number size="small" :min="0" v-model:value="optionData.lineNum"></n-input-number>
</SettingItem>
<SettingItem name="向上增加">
<n-input-number size="small" :min="0" v-model:value="optionData.lineNumUp"></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="折线样式">
<SettingItem name="折线粗细">
<n-input-number size="small" :min="1" v-model:value="optionData.lineWidth"></n-input-number>
</SettingItem>
<SettingItem name="背景条颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.backgroundCol"></n-color-picker>
</SettingItem>
<SettingItem name="流动颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.animateCol"></n-color-picker>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { option } from './config'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
</script>

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum,ChatCategoryEnumName } from '../../index.d'
export const FlowChartLineConfig: ConfigType = {
key: 'FlowChartLine',
chartKey: 'VFlowChartLine',
conKey: 'VCFlowChartLine',
title: '流程线',
category: ChatCategoryEnum.FlowChart,
categoryName: ChatCategoryEnumName.FlowChart,
package: PackagesCategoryEnum.DECORATES,
chartFrame: ChartFrameEnum.STATIC,
image: 'flow-zhexian.png'
}

View File

@@ -0,0 +1,93 @@
<template>
<svg :width="w" :height="h">
<polyline :stroke-width="lineWidth" :points="getStartPoint(-1, '')" :stroke="backgroundCol" fill="none" />
<polyline
:stroke-width="lineWidth"
class="g-dashed-line"
:points="getStartPoint(-1, '')"
:stroke="animateCol"
fill="none"
/>
<polyline
:stroke-width="lineWidth"
v-for="(item, index) in lineNum"
:key="index"
:points="getStartPoint(index + 1, 'down')"
:stroke="backgroundCol"
fill="none"
/>
<polyline
:stroke-width="lineWidth"
class="g-dashed-line"
v-for="(item, index) in lineNum"
:key="index"
:points="getStartPoint(index + 1, 'down')"
:stroke="animateCol"
fill="none"
/>
<polyline
:stroke-width="lineWidth"
v-for="(item, index) in lineNumUp"
:key="index"
:points="getStartPoint(index + 1, 'up')"
:stroke="backgroundCol"
fill="none"
/>
<polyline
:stroke-width="lineWidth"
class="g-dashed-line"
v-for="(item, index) in lineNumUp"
:key="index"
:points="getStartPoint(index + 1, 'up')"
:stroke="animateCol"
fill="none"
/>
</svg>
</template>
<script setup lang="ts">
import { PropType, toRefs, computed } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const { lineNum, lineNumUp, lineWidth, backgroundCol, animateCol } = toRefs(props.chartConfig.option)
const getStartPoint = (num: number, direction: string) => {
const lineLength = w.value / 2
const lineColLength =
h.value / (lineNumUp.value + lineNum.value) - lineWidth.value / (lineNumUp.value + lineNum.value)
if (num === -1 && direction === '') {
return `0,${h.value / 2} ${lineLength},${h.value / 2} ${lineLength * 2},${h.value / 2}`
} else if (num !== -1 && direction === 'down') {
return `0,${h.value / 2} ${lineLength},${h.value / 2} ${lineLength},${h.value / 2 + num * lineColLength},${
lineLength * 2
},${h.value / 2 + num * lineColLength}`
} else if (num !== -1 && direction === 'up') {
return `0,${h.value / 2} ${lineLength},${h.value / 2} ${lineLength},${h.value / 2 - num * lineColLength},${
lineLength * 2
},${h.value / 2 - num * lineColLength}`
}
}
</script>
<style scoped>
.g-dashed-line {
stroke-dasharray: 20 130;
stroke-dashoffset: 0;
animation: move 3s infinite linear;
}
@keyframes move {
0% {
stroke-dashoffset: 20;
}
100% {
stroke-dashoffset: -130;
}
}
</style>

View File

@@ -0,0 +1,3 @@
import { FlowChartLineConfig } from './FlowChartLine/index'
export default [FlowChartLineConfig]

View File

@@ -0,0 +1,20 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { CirclePointConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
import { chartInitConfig } from '@/settings/designSetting'
export const option = {
outCircle: 15,
inCircle: 5,
outCircleColor: '#3f5261',
inCircleColor: '#fff',
outCircleWidth: 2
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = CirclePointConfig.key
public attr = { ...chartInitConfig, w: 97, h: 97, zIndex: 1 }
public chartConfig = cloneDeep(CirclePointConfig)
public option = cloneDeep(option)
}

View File

@@ -0,0 +1,51 @@
<template>
<CollapseItem name="线条" :expanded="true">
<SettingItemBox name="具体">
<SettingItem name="外圆环半径">
<n-input-number
size="small"
v-model:value="optionData.outCircle"
></n-input-number>
</SettingItem>
<SettingItem name="内部圆形半径">
<n-input-number
size="small"
v-model:value="optionData.inCircle"
></n-input-number>
</SettingItem>
<SettingItem name="外圆环粗细">
<n-input-number
size="small"
v-model:value="optionData.outCircleWidth"
></n-input-number>
</SettingItem>
<SettingItem name="外圆环颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.outCircleColor"></n-color-picker>
</SettingItem>
<SettingItem name="内部圆形颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.inCircleColor"></n-color-picker>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import {
CollapseItem,
SettingItemBox,
SettingItem
} from '@/components/Pages/ChartItemSetting'
import { option } from './config'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
</script>

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum,ChatCategoryEnumName } from '../../index.d'
export const CirclePointConfig: ConfigType = {
key: 'CirclePoint',
chartKey: 'VCirclePoint',
conKey: 'VCCirclePoint',
title: '圆点光环',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.DECORATES,
chartFrame: ChartFrameEnum.STATIC,
image: 'flow-circle.png'
}

View File

@@ -0,0 +1,28 @@
<template>
<svg :width="w" :height="h">
<defs>
<filter id="blurFilter" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceGraphic" stdDeviation="1" />
</filter>
</defs>
<circle :cx="w / 2 " :cy="h / 2" :r="inCircle" :fill="inCircleColor" filter="url(#blurFilter)"/>
<!-- 外部圆环 -->
<circle :cx="w / 2 " :cy="h / 2" :r="outCircle" fill="none" :stroke="outCircleColor" :stroke-width="outCircleWidth"/>
</svg>
</template>
<script setup lang="ts">
import { PropType, toRefs } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true,
},
})
const { w, h } = toRefs(props.chartConfig.attr)
const { outCircle,inCircle,outCircleColor,inCircleColor,outCircleWidth} = toRefs(props.chartConfig.option)
</script>

View File

@@ -6,6 +6,7 @@ import { CountDownConfig } from './CountDown/index'
import { FlipperNumberConfig } from './FlipperNumber'
import { PipelineHConfig } from './PipelineH/index'
import { PipelineVConfig } from './PipelineV/index'
import { CirclePointConfig } from './CirclePoint/index'
export default [
NumberConfig,
@@ -15,5 +16,6 @@ export default [
ClockConfig,
FullScreenConfig,
PipelineHConfig,
PipelineVConfig
PipelineVConfig,
CirclePointConfig
]

View File

@@ -1,6 +1,7 @@
export enum ChatCategoryEnum {
BORDER = 'Borders',
DECORATE = 'Decorates',
FlowChart = 'FlowChart',
THREE = 'Three',
MORE = 'Mores'
}
@@ -8,6 +9,7 @@ export enum ChatCategoryEnum {
export enum ChatCategoryEnumName {
BORDER = '边框',
DECORATE = '装饰',
FlowChart = '流程',
THREE = '三维',
MORE = '更多'
}
}

View File

@@ -1,6 +1,7 @@
import Borders from './Borders'
import Decorates from './Decorates'
import FlowChart from './FlowChart'
import Three from './Three'
import Mores from './Mores'
export const DecorateList = [...Borders, ...Decorates, ...Three, ...Mores]
export const DecorateList = [...Borders, ...Decorates,...FlowChart, ...Three, ...Mores]

View File

@@ -8,8 +8,8 @@
<n-input-number v-model:value="optionData.size" size="small" :min="0" placeholder="尺寸"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="旋转">
<setting-item name="旋转">
<setting-item-box name="快捷旋转">
<setting-item name="也可使用通用的【变换】来旋转">
<n-select v-model:value="optionData.rotate" size="small" :options="rotateMode"></n-select>
</setting-item>
</setting-item-box>
@@ -35,15 +35,15 @@ const rotateMode = [
label: '0°'
},
{
value: 1,
value: 90,
label: '90°'
},
{
value: 2,
value: 180,
label: '180°'
},
{
value: 3,
value: 270,
label: '270°'
}
]

View File

@@ -10,5 +10,5 @@ export const InputsInputConfig: ConfigType = {
categoryName: ChatCategoryEnumName.INPUTS,
package: PackagesCategoryEnum.INFORMATIONS,
chartFrame: ChartFrameEnum.STATIC,
image: 'inputs_select.png'
image: 'inputs_input.png'
}

View File

@@ -36,7 +36,7 @@ let option = shallowReactive({ ...configOption })
// 预览更新
const vVideoRef = ref(null)
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
option = newData
option.dataset = newData
})
// 编辑更新

View File

@@ -36,6 +36,8 @@
import { computed, PropType, toRefs, watch, reactive, ref } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { icon } from '@/plugins'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartDataFetch } from '@/hooks'
const props = defineProps({
chartConfig: {
@@ -83,6 +85,13 @@ watch(
deep: true
}
)
// setdata 数据监听与更改
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,47 @@
import { PublicConfigClass } from '@/packages/public'
import { VChartBarCommonConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import { vChartOptionPrefixHandle } from '@/packages/public/vChart'
import data from './data.json'
import cloneDeep from 'lodash/cloneDeep'
import axisThemeJson from '@/settings/vchartThemes/axis.theme.json'
import { IBarOption } from '../../index.d'
export const includes = ['legends', 'tooltip']
export const option: IBarOption & { dataset?: any } = {
// 图表配置
type: 'bar',
dataset: data,
stack: true,
xField: ['year', 'type'],
yField: ['value'],
seriesField: 'type',
// 业务配置后续会被转换为图表spec)
category: VChartBarCommonConfig.category,
xAxis: {
name: 'x轴',
...axisThemeJson,
grid: {
...axisThemeJson.grid,
visible: false
}
},
yAxis: {
name: 'y轴',
...axisThemeJson,
grid: {
...axisThemeJson.grid,
style: {
...axisThemeJson.grid.style,
lineDash: [3, 3]
}
}
}
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = VChartBarCommonConfig.key
public chartConfig = cloneDeep(VChartBarCommonConfig)
// 图表配置项
public option = vChartOptionPrefixHandle(option, includes)
}

View File

@@ -0,0 +1,19 @@
<template>
<!-- vCharts 全局设置 -->
<VChartGlobalSetting :optionData="optionData"></VChartGlobalSetting>
<Axis :axis="optionData.xAxis"></Axis>
<Axis :axis="optionData.yAxis"></Axis>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { VChartGlobalSetting, Axis } from '@/components/Pages/VChartItemSetting'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,25 @@
{
"values": [
{ "type": "Autocracies", "year": "1930", "value": 129 },
{ "type": "Autocracies", "year": "1940", "value": 133 },
{ "type": "Autocracies", "year": "1950", "value": 130 },
{ "type": "Autocracies", "year": "1960", "value": 126 },
{ "type": "Autocracies", "year": "1970", "value": 117 },
{ "type": "Autocracies", "year": "1980", "value": 114 },
{ "type": "Autocracies", "year": "1990", "value": 111 },
{ "type": "Autocracies", "year": "2000", "value": 89 },
{ "type": "Autocracies", "year": "2010", "value": 80 },
{ "type": "Autocracies", "year": "2018", "value": 80 },
{ "type": "Democracies", "year": "1930", "value": 22 },
{ "type": "Democracies", "year": "1940", "value": 13 },
{ "type": "Democracies", "year": "1950", "value": 25 },
{ "type": "Democracies", "year": "1960", "value": 29 },
{ "type": "Democracies", "year": "1970", "value": 38 },
{ "type": "Democracies", "year": "1980", "value": 41 },
{ "type": "Democracies", "year": "1990", "value": 57 },
{ "type": "Democracies", "year": "2000", "value": 87 },
{ "type": "Democracies", "year": "2010", "value": 98 },
{ "type": "Democracies", "year": "2018", "value": 99 }
]
}

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const VChartBarCommonConfig: ConfigType = {
key: 'VChartBarCommon',
chartKey: 'VVChartBarCommon',
conKey: 'VCVChartBarCommon',
title: 'VChart柱状图',
category: ChatCategoryEnum.BAR,
categoryName: ChatCategoryEnumName.BAR,
package: PackagesCategoryEnum.VCHART,
chartFrame: ChartFrameEnum.VCHART,
image: 'vchart_bar_x.png'
}

View File

@@ -0,0 +1,22 @@
<template>
<GoVChart ref="vChartRef" :option="chartConfig.option"> </GoVChart>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { GoVChart } from '@/components/GoVChart'
import { useChartDataFetch } from '@/hooks'
import config from './config'
const props = defineProps({
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -0,0 +1,47 @@
import { PublicConfigClass } from '@/packages/public'
import { VChartBarStackConfig } from './index'
import { CreateComponentType } from '@/packages/index.d'
import { vChartOptionPrefixHandle } from '@/packages/public/vChart'
import data from './data.json'
import cloneDeep from 'lodash/cloneDeep'
import axisThemeJson from '@/settings/vchartThemes/axis.theme.json'
import { IBarOption } from '../../index.d'
export const includes = ['legends', 'tooltip']
export const option: IBarOption & { dataset?: any } = {
// 图表配置
type: 'bar',
dataset: data,
xField: ['type'],
yField: ['value'],
seriesField: 'year',
stack: true,
// 业务配置后续会被转换为图表spec)
category: VChartBarStackConfig.category,
xAxis: {
name: 'x轴',
...axisThemeJson,
grid: {
...axisThemeJson.grid,
visible: false
}
},
yAxis: {
name: 'y轴',
...axisThemeJson,
grid: {
...axisThemeJson.grid,
style: {
...axisThemeJson.grid.style,
lineDash: [3, 3]
}
}
}
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = VChartBarStackConfig.key
public chartConfig = cloneDeep(VChartBarStackConfig)
// 图表配置项
public option = vChartOptionPrefixHandle(option, includes)
}

View File

@@ -0,0 +1,19 @@
<template>
<!-- vCharts 全局设置 -->
<VChartGlobalSetting :optionData="optionData"></VChartGlobalSetting>
<Axis :axis="optionData.xAxis"></Axis>
<Axis :axis="optionData.yAxis"></Axis>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { VChartGlobalSetting, Axis } from '@/components/Pages/VChartItemSetting'
import { vChartGlobalThemeJsonType } from '@/settings/vchartThemes/index'
defineProps({
optionData: {
type: Object as PropType<vChartGlobalThemeJsonType>,
required: true
}
})
</script>

View File

@@ -0,0 +1,16 @@
{
"values": [
{ "type": "Nail polish", "year": "Africa", "value": 590 },
{ "type": "Nail polish", "year": "EU", "value": 450 },
{ "type": "Nail polish", "year": "China", "value": 474 },
{ "type": "Nail polish", "year": "USA", "value": 459 },
{ "type": "Eyebrow pencil", "year": "Africa", "value": 746 },
{ "type": "Eyebrow pencil", "year": "EU", "value": 176 },
{ "type": "Eyebrow pencil", "year": "China", "value": 210 },
{ "type": "Eyebrow pencil", "year": "USA", "value": 775 },
{ "type": "Rouge", "year": "Africa", "value": 896 },
{ "type": "Rouge", "year": "EU", "value": 784 },
{ "type": "Rouge", "year": "China", "value": 866 },
{ "type": "Rouge", "year": "USA", "value": 899 }
]
}

View File

@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const VChartBarStackConfig: ConfigType = {
key: 'VChartBarStack',
chartKey: 'VVChartBarStack',
conKey: 'VCVChartBarStack',
title: 'VChart柱状图',
category: ChatCategoryEnum.BAR,
categoryName: ChatCategoryEnumName.BAR,
package: PackagesCategoryEnum.VCHART,
chartFrame: ChartFrameEnum.VCHART,
image: 'vchart_bar_x_stack.png'
}

View File

@@ -0,0 +1,22 @@
<template>
<GoVChart ref="vChartRef" :option="chartConfig.option"> </GoVChart>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { GoVChart } from '@/components/GoVChart'
import { useChartDataFetch } from '@/hooks'
import config from './config'
const props = defineProps({
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData
})
</script>

View File

@@ -0,0 +1,4 @@
import { VChartBarCommonConfig } from './VChartBarCommon/index'
import { VChartBarStackConfig } from './VChartBarStack/index'
export default [VChartBarCommonConfig, VChartBarStackConfig]

View File

@@ -0,0 +1,26 @@
import { IBarChartSpec } from '@visactor/vchart'
import { ICartesianAxisCommonSpec } from '@visactor/vchart/esm/component/axis'
export enum ChatCategoryEnum {
BAR = 'Bars',
}
export enum ChatCategoryEnumName {
BAR = '柱状图',
}
export interface IBarOption extends Omit<IBarChartSpec, 'axes'> {
category: ChatCategoryEnum.BAR
type: 'bar'
xAxis?: {
name: string
} & ICartesianAxisCommonSpec
yAxis?: {
name: string
} & ICartesianAxisCommonSpec
}
// todo
// export type IOption = IBarOption | ILineOption ....
export type IOption = IBarOption

View File

@@ -0,0 +1,3 @@
import Bars from './Bars'
export const VChartList = [...Bars]

View File

@@ -1,10 +1,13 @@
import { BaseEvent, EventLife, InteractEvents, InteractEventOn, InteractActionsType } from '@/enums/eventEnum'
import type { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import type { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
import type { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/VChart/index.d'
export enum ChartFrameEnum {
// 支持 dataset 的 echarts 框架
ECHARTS = 'echarts',
// VChart 框架
VCHART = 'VChart',
// UI 组件框架
NAIVE_UI = 'naiveUI',
// 自定义带数据组件
@@ -24,11 +27,11 @@ export type ConfigType = {
// 标题
title: string
// 分类
category: string
category: ChatCategoryEnum
// 分类名称
categoryName: string
categoryName: ChatCategoryEnumName
// 所属包
package: string
package: PackagesCategoryEnum
// 归类
chartFrame?: ChartFrameEnum
// 预览图
@@ -173,6 +176,7 @@ export type PickCreateComponentType<T extends keyof CreateComponentType> = Pick<
// 包分类枚举
export enum PackagesCategoryEnum {
CHARTS = 'Charts',
VCHART = 'VChart',
TABLES = 'Tables',
INFORMATIONS = 'Informations',
PHOTOS = 'Photos',
@@ -183,6 +187,7 @@ export enum PackagesCategoryEnum {
// 包分类名称
export enum PackagesCategoryName {
CHARTS = '图表',
VCHART = 'VChart',
TABLES = '列表',
INFORMATIONS = '信息',
PHOTOS = '图片',
@@ -199,6 +204,7 @@ export enum FetchComFlagType {
// 图表包类型
export type PackagesType = {
[PackagesCategoryEnum.CHARTS]: ConfigType[]
[PackagesCategoryEnum.VCHART]: ConfigType[]
[PackagesCategoryEnum.INFORMATIONS]: ConfigType[]
[PackagesCategoryEnum.TABLES]: ConfigType[]
[PackagesCategoryEnum.PHOTOS]: ConfigType[]

View File

@@ -1,4 +1,5 @@
import { ChartList } from '@/packages/components/Charts/index'
import { VChartList } from '@/packages/components/VChart/index'
import { DecorateList } from '@/packages/components/Decorates/index'
import { InformationList } from '@/packages/components/Informations/index'
import { TableList } from '@/packages/components/Tables/index'
@@ -19,6 +20,7 @@ const imagesModules: Record<string, { default: string }> = import.meta.glob('../
// * 所有图表
export let packagesList: PackagesType = {
[PackagesCategoryEnum.CHARTS]: ChartList,
[PackagesCategoryEnum.VCHART]: VChartList,
[PackagesCategoryEnum.INFORMATIONS]: InformationList,
[PackagesCategoryEnum.TABLES]: TableList,
[PackagesCategoryEnum.DECORATES]: DecorateList,

View File

@@ -20,7 +20,7 @@ export const mergeTheme = <T, U>(option: T, themeSetting: U, includes: string[])
* @param option
* @return option
*/
export const echartOptionProfixHandle = (option: any, includes: string[]) => {
export const echartOptionProfixHandle = (option: any, includes: string[] = []) => {
option['backgroundColor'] = 'rgba(0,0,0,0)'
return mergeTheme(option, globalThemeJson, includes)
}
@@ -40,9 +40,11 @@ export const setData = (option: any, data: EchartsDataType) => {
* @param instance
* @param data
*/
export const setOption = <T extends typeof VChart | undefined, D>(instance: T, data: D) => {
export const setOption = <T extends typeof VChart | undefined, D>(instance: T, data: D, notMerge = true) => {
if (!instance) return
const option = instance.getOption()
option.dataset = null
instance.setOption(data)
instance.setOption(data, {
notMerge: notMerge
})
}

View File

@@ -0,0 +1,24 @@
import merge from 'lodash/merge'
import pick from 'lodash/pick'
import { vChartGlobalThemeJson } from '@/settings/vchartThemes/index'
/**
* * 合并 color 和全局配置项
* @param option 配置
* @param themeSetting 设置
* @param excludes 排除元素
* @returns object
*/
export const mergeTheme = <T, U>(option: T, themeSetting: U, includes: string[]) => {
return (option = merge({}, pick(themeSetting, includes), option))
}
/**
* * vCharts option 统一前置处理
* @param option
* @return option
*/
export const vChartOptionPrefixHandle = (option: any, includes: string[] = []) => {
option['background'] = 'rgba(0,0,0,0)'
return mergeTheme(option, vChartGlobalThemeJson, includes)
}

View File

@@ -58,6 +58,7 @@ import {
Search as SearchIcon,
ChevronUpOutline as ChevronUpOutlineIcon,
ChevronDownOutline as ChevronDownOutlineIcon,
ChevronForward as ChevronForwardIcon,
Pulse as PulseIcon,
Folder as FolderIcon,
FolderOpen as FolderOpenIcon,
@@ -101,7 +102,8 @@ import {
Carbon3DSoftware as Carbon3DSoftwareIcon,
Filter as FilterIcon,
FilterEdit as FilterEditIcon,
Laptop as LaptopIcon
Laptop as LaptopIcon,
ChartPie as ChartPieIcon
} from '@vicons/carbon'
const ionicons5 = {
@@ -244,7 +246,9 @@ const ionicons5 = {
// 分析
AnalyticsIcon,
// 飞机
AirPlaneOutlineIcon
AirPlaneOutlineIcon,
// 向右
ChevronForwardIcon
}
const carbon = {
@@ -297,7 +301,9 @@ const carbon = {
FilterIcon,
FilterEditIcon,
// 图层
LaptopIcon
LaptopIcon,
// 柱状图
ChartPieIcon
}
// https://www.xicons.org/#/ 还有很多

View File

@@ -0,0 +1,37 @@
{
"visible": true,
"label": {
"visible": true,
"style": {
"fontSize": 12,
"fill": "#B9B8CE",
"fontFamily": "SimSun",
"fontWeight": "normal",
"angle": 0
}
},
"title": {
"visible": true,
"style": {
"text": "",
"fontSize": 12,
"fill": "#B9B8CE",
"fontFamily": "SimSun",
"fontWeight": "normal"
}
},
"domainLine": {
"visible": false,
"style": {
"lineWidth": 1,
"stroke": "#D5D7E2"
}
},
"grid": {
"visible": true,
"style": {
"lineWidth": 1,
"stroke": "#FFFFFF24"
}
}
}

View File

@@ -0,0 +1,78 @@
{
"legends": [
{
"visible": true,
"position": "middle",
"orient": "bottom",
"item": {
"visible": true,
"align": "left",
"shape": "circle",
"label": {
"style": {
"fontSize": 16,
"fill": "#B9B8CE",
"fontFamily": "SimSun",
"fontWeight": "normal"
}
}
}
}
],
"tooltip": {
"visible": true,
"style": {
"panel": {
"padding": {
"top": 5,
"bottom": 10,
"left": 10,
"right": 10
},
"backgroundColor": "rgba(8, 28, 48, 0.95)",
"border": {
"color": "#CFCFCF",
"width": 0,
"radius": 2
},
"shadow": {
"x": 0,
"y": 4,
"blur": 12,
"spread": 0,
"color": "rgba(0, 0, 0, 0.2)"
}
},
"titleLabel": {
"fontSize": 14,
"fill": "#FFF",
"fontWeight": "bold",
"fontFamily": "SimSun",
"align": "left",
"lineHeight": 18
},
"keyLabel": {
"fontSize": 12,
"fill": "rgba(255,255,255,0.65)",
"fontWeight": "normal",
"fontFamily": "SimSun",
"align": "left",
"lineHeight": 18
},
"valueLabel": {
"fontSize": 12,
"fill": "#FFF",
"fontWeight": "normal",
"fontFamily": "SimSun",
"align": "right",
"lineHeight": 18
},
"shape": {
"size": 10,
"spacing": 10,
"shapeLineWidth": 0
},
"spaceRow": 10
}
}
}

View File

@@ -0,0 +1,16 @@
import themeJson from './global.theme.json'
type ThemeJsonType = typeof themeJson
export type FontType = {
fontSize: number
fontFamily: string
fontWeight: string
fill: string
[T: string]: any
}
export interface vChartGlobalThemeJsonType extends Partial<ThemeJsonType> {
dataset?: any
[T: string]: any
}
export const vChartGlobalThemeJson = { ...themeJson, dataset: null }

View File

@@ -57,6 +57,7 @@ export enum EditCanvasConfigEnum {
CHART_THEME_COLOR = 'chartThemeColor',
CHART_CUSTOM_THEME_COLOR_INFO = 'chartCustomThemeColorInfo',
CHART_THEME_SETTING = 'chartThemeSetting',
VCHART_THEME_NAME = 'vChartThemeName',
BACKGROUND = 'background',
BACKGROUND_IMAGE = 'backgroundImage',
SELECT_COLOR = 'selectColor',
@@ -100,7 +101,9 @@ export interface EditCanvasConfigType {
// 图表全局配置
[EditCanvasConfigEnum.CHART_THEME_SETTING]: GlobalThemeJsonType
// 图表主题颜色
[EditCanvasConfigEnum.SELECT_COLOR]: boolean
[EditCanvasConfigEnum.SELECT_COLOR]: boolean,
// vChart 主题
[EditCanvasConfigEnum.VCHART_THEME_NAME]: string
// 预览展示方式
[EditCanvasConfigEnum.PREVIEW_SCALE_TYPE]: PreviewScaleEnum
}

View File

@@ -114,6 +114,8 @@ export const useChartEditStore = defineStore({
chartCustomThemeColorInfo: undefined,
// 全局配置
chartThemeSetting: globalThemeJson,
// vChart 主题
vChartThemeName: 'vScreenVolcanoBlue',
// 适配方式
previewScaleType: previewScaleType
},
@@ -172,6 +174,27 @@ export const useChartEditStore = defineStore({
[ChartEditStoreEnum.REQUEST_GLOBAL_CONFIG]: this.getRequestGlobalConfig
}
},
// * 获取针对 componentList 顺序排过序的 selectId
getSelectIdSortList(ids?: string[]): string[] {
if (!this.getTargetChart.selectId.length && !ids) return []
const sortArr: string[] = []
this.getComponentList.forEach((item: CreateComponentType | CreateComponentGroupType) => {
if (ids) {
ids.forEach((id: string) => {
if (item.id === id) {
sortArr.push(id)
}
})
} else {
this.getTargetChart.selectId.forEach((id: string) => {
if (item.id === id) {
sortArr.push(id)
}
})
}
})
return sortArr
},
// * 设置 editCanvas 数据项
setEditCanvas<T extends keyof EditCanvasType, K extends EditCanvasType[T]>(key: T, value: K) {
this.editCanvas[key] = value
@@ -715,18 +738,12 @@ export const useChartEditStore = defineStore({
// * 创建分组
setGroup(id?: string | string[], isHistory = true) {
try {
const selectIds = this.idPreFormat(id) || this.getTargetChart.selectId
let selectIds = this.idPreFormat(id) || this.getTargetChart.selectId
selectIds = this.getSelectIdSortList(selectIds)
if (selectIds.length < 2) return
loadingStart()
const groupClass = new PublicGroupConfigClass()
// 记录整体坐标
const groupAttr = {
l: this.getEditCanvasConfig.width,
t: this.getEditCanvasConfig.height,
r: 0,
b: 0
}
const targetList: CreateComponentType[] = []
const historyList: CreateComponentType[] = []
@@ -749,21 +766,35 @@ export const useChartEditStore = defineStore({
newSelectIds.push(id)
}
})
newSelectIds.forEach((id: string) => {
// 记录整体坐标
const groupAttr = {
l: 0,
t: 0,
r: 0,
b: 0
}
newSelectIds.forEach((id: string, index: number) => {
// 获取目标数据并从 list 中移除 (成组后不可再次成组, 断言处理)
const item = this.componentList.splice(this.fetchTargetIndex(id), 1)[0] as CreateComponentType
const { x, y, w, h } = item.attr
const { l, t, r, b } = groupAttr
// 左
groupAttr.l = l > x ? x : l
// 上
groupAttr.t = t > y ? y : t
// 宽
groupAttr.r = r < x + w ? x + w : r
//
groupAttr.b = b < y + h ? y + h : b
if (index === 0) {
groupAttr.l = x
groupAttr.t = y
groupAttr.r = x + w
groupAttr.b = y + h
} else {
const { l, t, r, b } = groupAttr
//
groupAttr.l = l > x ? x : l
// 上
groupAttr.t = t > y ? y : t
// 宽
groupAttr.r = r < x + w ? x + w : r
// 高
groupAttr.b = b < y + h ? y + h : b
}
targetList.unshift(item)
targetList.push(item)
historyList.push(toRaw(item))
})
@@ -789,7 +820,7 @@ export const useChartEditStore = defineStore({
loadingFinish()
} catch (error) {
console.log(error)
window['$message'].error('创建分组失败,请联系管理员')
window['$message'].error('创建分组失败,请联系管理员')
loadingFinish()
}
},
@@ -809,7 +840,7 @@ export const useChartEditStore = defineStore({
if (isHistory) chartHistoryStore.createUnGroupHistory(cloneDeep([targetGroup]))
// 分离组件并还原位置属性
targetGroup.groupList.reverse().forEach(item => {
targetGroup.groupList.forEach(item => {
item.attr.x = item.attr.x + targetGroup.attr.x
item.attr.y = item.attr.y + targetGroup.attr.y
if (!callBack) {
@@ -834,7 +865,7 @@ export const useChartEditStore = defineStore({
loadingFinish()
} catch (error) {
console.log(error)
window['$message'].error('解除分组失败,请联系管理员')
window['$message'].error('解除分组失败,请联系管理员')
loadingFinish()
}
},

View File

@@ -24,8 +24,8 @@ export function isArray(p: any): p is [] {
return Array.isArray(p)
}
export const toNumber = (number: number | string, toFixedNumber = 2) => {
return isString(number) ? parseFloat(parseFloat(number).toFixed(2)) : number
export const toNumber = (number: number | string, toFixedNumber: number = 2) => {
return isString(number) ? parseFloat(parseFloat(number).toFixed(toFixedNumber)) : number
}
export const toString = (str: any) => {

View File

@@ -25,7 +25,7 @@ export const isDev = () => {
* @param { Number } randomLength
*/
export const getUUID = (randomLength = 10) => {
return Number(Math.random().toString().substring(2, randomLength) + Date.now()).toString(36)
return 'id_' + Number(Math.random().toString().substring(2, randomLength) + Date.now()).toString(36)
}
/**
@@ -324,13 +324,17 @@ export const evalFn = (fn: string) => {
* @param data
*/
export const JSONParse = (data: string) => {
if (data.trim() === '') return
return JSON.parse(data, (k, v) => {
// 过滤函数字符串
if (excludeParseEventKeyList.includes(k)) return v
// 过滤函数值表达式
if (typeof v === 'string') {
const someValue = excludeParseEventValueList.some(excludeValue => v.indexOf(excludeValue) > -1)
if (someValue) return v
// // 过滤函数字符串
// if (excludeParseEventKeyList.includes(k)) return v
// // 过滤函数值表达式
// if (typeof v === 'string') {
// const someValue = excludeParseEventValueList.some(excludeValue => v.indexOf(excludeValue) > -1)
// if (someValue) return v
// }
if (k !== 'formatter') {
return v
}
// 还原函数值
if (typeof v === 'string' && v.indexOf && (v.indexOf('function') > -1 || v.indexOf('=>') > -1)) {

View File

@@ -12,7 +12,7 @@
:key="item.title"
draggable
@dragstart="!item.disabled && dragStartHandle($event, item)"
@dragend="!item.disabled && dragendHandle"
@dragend="!item.disabled && dragendHandle()"
@dblclick="dblclickHandle(item)"
@click="clickHandle(item)"
>

View File

@@ -7,7 +7,7 @@ import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
// 图标
const { AirPlaneOutlineIcon, ImageIcon, BarChartIcon } = icon.ionicons5
const { TableSplitIcon, RoadmapIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon
const { TableSplitIcon, RoadmapIcon, ChartPieIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon
// 图表
export type MenuOptionsType = {
@@ -22,6 +22,10 @@ const packagesListObj = {
icon: renderIcon(RoadmapIcon),
label: PackagesCategoryName.CHARTS
},
[PackagesCategoryEnum.VCHART]: {
icon: renderIcon(ChartPieIcon),
label: PackagesCategoryName.VCHART
},
[PackagesCategoryEnum.INFORMATIONS]: {
icon: renderIcon(SpellCheckIcon),
label: PackagesCategoryName.INFORMATIONS

View File

@@ -0,0 +1,245 @@
<template>
<n-grid :x-gap="8" :y-gap="8" :cols="2">
<n-gi v-for="item in list" :key="item.value">
<div
class="theme-item"
:class="{ active: item.value === vChartThemeName }"
size="small"
@click="selectThemeHandle(item)"
>
<n-ellipsis class="go-mr-1" style="text-align: left">{{ item.name }} </n-ellipsis>
<n-space :wrap="false" :wrap-item="false" :size="2">
<span
class="theme-color-item"
v-for="colorItem in item.colors"
:key="colorItem"
:style="{ backgroundColor: colorItem }"
></span>
</n-space>
</div>
</n-gi>
</n-grid>
<div class="go-my-4">行业模板</div>
<n-grid :x-gap="8" :y-gap="8" :cols="2">
<n-gi v-for="item in industryList" :key="item.value">
<div
class="theme-item"
:class="{ active: item.value === vChartThemeName }"
size="small"
@click="selectThemeHandle(item)"
>
<n-ellipsis class="go-mr-2" style="text-align: left">{{ item.name }} </n-ellipsis>
<n-space :wrap="false" :wrap-item="false" :size="2">
<span
class="theme-color-item"
v-for="colorItem in item.colors"
:key="colorItem"
:style="{ backgroundColor: colorItem }"
></span>
</n-space>
</div>
</n-gi>
</n-grid>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useVCharts } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useDesignStore } from '@/store/modules/designStore/designStore'
const chartEditStore = useChartEditStore()
const designStore = useDesignStore()
const vCharts = useVCharts()
const themeMap = vCharts.getThemeMap()
const vChartThemeName = computed(() => {
return chartEditStore.getEditCanvasConfig.vChartThemeName
})
// 颜色
const themeColor = computed(() => {
return designStore.getAppTheme
})
const list = ref<
Array<{
name: string
value: keyof typeof themeMap
colors: string[]
}>
>([
{
name: '火山蓝(默认)',
value: 'vScreenVolcanoBlue',
colors: ['#2D64DD', '#284588', '#58B4B6']
},
{
name: '党建红',
value: 'vScreenPartyRed',
colors: ['#d3d3d4', '#d68a46', '#d74f3c']
},
{
name: '清新蜡笔',
value: 'vScreenClean',
colors: ['#94AF60', '#7696B8', '#d6837a']
},
{
name: '郊外',
value: 'vScreenOutskirts',
colors: ['#A7C4E6', '#e1bf99', '#c0bcbb']
},
{
name: '汽车蓝橙',
value: 'vScreenBlueOrange',
colors: ['#acd5fa', '#cc896b', '#5ea4dd']
},
{
name: '金融黄',
value: 'vScreenFinanceYellow',
colors: ['#d7d7d7', '#f09761', '#f7d177']
},
{
name: '文旅青',
value: 'vScreenWenLvCyan',
colors: ['#63c6ba', '#dcb974', '#a34440']
},
{
name: '电力绿',
value: 'vScreenElectricGreen',
colors: ['#75faf2', '#ee813e', '#f4ce7f']
},
{
name: '电商紫',
value: 'vScreenECommercePurple',
colors: ['#6d4cf6', '#ed7266', '#5f83f7']
},
{
name: '红蓝',
value: 'vScreenRedBlue',
colors: ['#2e6cf6', '#bc4741', '#c1e4fb']
}
])
// 行业色版列表
const industryList = ref<
Array<{
name: string
value: keyof typeof themeMap
colors: string[]
}>
>([
{
name: '明亮(适用白背景)',
value: 'light',
colors: ['#3063f6', '#5dc3f9', '#f1f2f5']
},
{
name: '暗黑(适用黑背景)',
value: 'dark',
colors: ['#3063f6', '#5dc3f9', '#414348']
},
{
name: '亮-金融行业',
value: 'veODesignLightFinance',
colors: ['#dbba95', '#314b5e', '#f1f2f5']
},
{
name: '暗-金融行业',
value: 'veODesignDarkFinance',
colors: ['#dbba95', '#314b5e', '#414348']
},
{
name: '亮-政府行业',
value: 'veODesignLightGovernment',
colors: ['#c0403a', '#f6c552', '#f1f2f5']
},
{
name: '暗-政府行业',
value: 'veODesignDarkGovernment',
colors: ['#c0403a', '#f6c552', '#414348']
},
{
name: '亮-消费行业',
value: 'veODesignLightConsumer',
colors: ['#3f36ab', '#eb4854', '#f1f2f5']
},
{
name: '暗-消费行业',
value: 'veODesignDarkConsumer',
colors: ['#3f36ab', '#eb4854', '#414348']
},
{
name: '亮-汽车行业',
value: 'veODesignLightAutomobile',
colors: ['#1515d1', '#abb6cd', '#f1f2f5']
},
{
name: '暗-汽车行业',
value: 'veODesignDarkAutomobile',
colors: ['#1515d1', '#abb6cd', '#414348']
},
{
name: '亮-文旅行业',
value: 'veODesignLightCulturalTourism',
colors: ['#77b897', '#3c5a4b', '#f1f2f5']
},
{
name: '暗-文旅行业',
value: 'veODesignDarkCulturalTourism',
colors: ['#77b897', '#3c5a4b', '#414348']
},
{
name: '亮-医疗行业',
value: 'veODesignLightMedical',
colors: ['#76d0d1', '#314787', '#f1f2f5']
},
{
name: '暗-医疗行业',
value: 'veODesignDarkMedical',
colors: ['#76d0d1', '#314787', '#414348']
},
{
name: '亮-新能源行业',
value: 'veODesignLightNewEnergy',
colors: ['#64d886', '#1f3b76', '#f1f2f5']
},
{
name: '暗-新能源行业',
value: 'veODesignDarkNewEnergy',
colors: ['#64d886', '#1f3b76', '#414348']
}
])
const selectThemeHandle = (item: { name: string; value: keyof typeof themeMap; colors: string[] }) => {
vCharts.setTheme(item.value)
chartEditStore.setEditCanvasConfig(EditCanvasConfigEnum.VCHART_THEME_NAME, item.value)
}
</script>
<style lang="scss" scoped>
$radius: 6px;
$itemRadius: 2px;
.theme-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 34px;
padding: 0 8px;
overflow: hidden;
cursor: pointer;
font-size: 13px;
border-radius: $radius;
@include fetch-bg-color('background-color4-shallow');
&.active {
color: v-bind('themeColor');
}
.theme-color-item {
display: inline-block;
width: 16px;
height: 16px;
border-radius: $itemRadius;
}
}
</style>

View File

@@ -150,6 +150,7 @@ const switchSelectColorLoading = ref(false)
const selectColorValue = ref(0)
const ChartThemeColor = loadAsyncComponent(() => import('./components/ChartThemeColor/index.vue'))
const VChartThemeColor = loadAsyncComponent(() => import('./components/VChartThemeColor/index.vue'))
// 默认应用类型
const selectColorOptions = [
@@ -166,10 +167,16 @@ const selectColorOptions = [
const globalTabList = [
{
key: 'ChartTheme',
title: '主题颜色',
title: '默认主题',
icon: ColorPaletteIcon,
render: ChartThemeColor
}
},
{
key: 'VChartTheme',
title: 'VChart主题',
icon: ColorPaletteIcon,
render: VChartThemeColor
},
]
const previewTypeList = [

View File

@@ -1,6 +1,7 @@
<template>
<n-timeline class="go-chart-configurations-timeline">
<n-timeline-item v-show="isCharts && dimensionsAndSource" type="info" :title="TimelineTitleEnum.MAPPING">
<!-- 处理 echarts 的数据映射 -->
<n-timeline-item v-if="isCharts && dimensionsAndSource" type="info" :title="TimelineTitleEnum.MAPPING">
<n-table striped>
<thead>
<tr>
@@ -25,6 +26,47 @@
</tbody>
</n-table>
</n-timeline-item>
<!-- 处理 vcharts 的数据映射 -->
<n-timeline-item v-if="isVChart" type="info" :title="TimelineTitleEnum.MAPPING">
<n-table striped>
<thead>
<tr>
<th v-for="item in vchartTableTitle" :key="item">{{ item }}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in fieldList" :key="item.field">
<td>
<n-ellipsis style="width: 70px; max-width: 240px">
{{ item.field }}
</n-ellipsis>
</td>
<td v-if="isArray(item.mapping)">
<n-space :size="4" vertical>
<n-input
v-for="(mappingItem, index) in item.mapping"
:key="index"
v-model:value="item.mapping[index]"
type="tiny"
size="small"
placeholder="输入字段"
@change="() => (item.result = matchingHandle(item.mapping[index]))"
/>
</n-space>
</td>
<td v-else>
<n-input v-model:value="item.mapping" type="text" size="small" placeholder="小" />
</td>
<!-- <td>
<n-space style="width: 70px" :size="4">
<n-badge dot :type="item.result === 1 ? 'success' : 'error'"></n-badge>
<n-text>匹配{{ item.result === 1 ? '成功' : '失败' }}</n-text>
</n-space>
</td> -->
</tr>
</tbody>
</n-table>
</n-timeline-item>
<n-timeline-item v-show="filterShow" color="#97846c" :title="TimelineTitleEnum.FILTER">
<n-space :size="18" vertical>
<n-text depth="3">过滤器默认处理接口返回值的data字段</n-text>
@@ -66,7 +108,7 @@
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<n-text depth="3">点击下载查看完整数据</n-text>
<span>点击下载查看完整数据</span>
</n-tooltip>
</div>
</n-space>
@@ -87,11 +129,10 @@ import { DataResultEnum, TimelineTitleEnum } from '../../index.d'
import { ChartDataMonacoEditor } from '../ChartDataMonacoEditor'
import { useFile } from '../../hooks/useFile.hooks'
import { useTargetData } from '../../../hooks/useTargetData.hook'
import isObject from 'lodash/isObject'
import { toString, isArray } from '@/utils'
const { targetData } = useTargetData()
const props = defineProps({
defineProps({
show: {
type: Boolean,
required: false
@@ -104,6 +145,7 @@ const props = defineProps({
// 表格标题
const tableTitle = ['字段', '映射', '状态']
const vchartTableTitle = ['字段', '接口映射字段']
const { HelpOutlineIcon } = icon.ionicons5
const { DocumentAddIcon, DocumentDownloadIcon } = icon.carbon
@@ -113,6 +155,15 @@ const dimensions = ref()
const dimensionsAndSource = ref()
const noData = ref(false)
// 映射列表, 注意内部的mapping是响应式的上方需要修改
const fieldList = ref<
Array<{
field: string
mapping: string[]
result: DataResultEnum
}>
>([])
const { uploadFileListRef, customRequest, beforeUpload, download } = useFile(targetData)
// 是否展示过滤器
@@ -124,6 +175,10 @@ const filterShow = computed(() => {
const isCharts = computed(() => {
return targetData.value.chartConfig.chartFrame === ChartFrameEnum.ECHARTS
})
// 是支持 vchart 的图表类型
const isVChart = computed(() => {
return targetData.value.chartConfig.chartFrame === ChartFrameEnum.VCHART
})
// 处理映射列表状态结果
const matchingHandle = (mapping: string) => {
@@ -162,6 +217,29 @@ const dimensionsAndSourceHandle = () => {
}
}
// 处理 vchart 映射列表
const initFieldListHandle = () => {
if (targetData.value?.option) {
fieldList.value = []
// 所有名称,找到其中中 Field 结尾 的 key 和值
for (const key in targetData.value.option) {
if (key.endsWith('Field')) {
const value = targetData.value.option[key]
targetData.value.option[key] = value
const item = {
field: key,
mapping: value,
result: DataResultEnum.SUCCESS
}
if (item.mapping === undefined) {
item.result = DataResultEnum.FAILURE
}
fieldList.value.push(item)
}
}
}
}
watch(
() => targetData.value?.option?.dataset,
(
@@ -170,6 +248,7 @@ watch(
dimensions: any
} | null
) => {
noData.value = false
if (newData && targetData?.value?.chartConfig?.chartFrame === ChartFrameEnum.ECHARTS) {
// 只有 DataSet 数据才有对应的格式
source.value = newData
@@ -177,9 +256,13 @@ watch(
dimensions.value = newData.dimensions
dimensionsAndSource.value = dimensionsAndSourceHandle()
}
} else if (newData && targetData?.value?.chartConfig?.chartFrame === ChartFrameEnum.VCHART) {
source.value = newData
initFieldListHandle()
} else if (newData !== undefined && newData !== null) {
dimensionsAndSource.value = null
source.value = newData
fieldList.value = []
} else {
noData.value = true
source.value = '此组件无数据源'

View File

@@ -1,5 +1,11 @@
<template>
<n-modal class="go-chart-data-pond-control" v-model:show="modelShowRef" :mask-closable="false">
<n-modal
class="go-chart-data-pond-control"
v-model:show="modelShowRef"
:mask-closable="false"
:close-on-esc="true"
:onEsc="onEsc"
>
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 900px; height: 650px">
<template #header></template>
<template #header-extra> </template>
@@ -29,7 +35,10 @@
</n-icon>
</template>
</n-button>
<n-button type="primary" @click="closeHandle">保存 & 发送请求</n-button>
<div>
<n-button class="go-mr-3" @click="closeHandle">取消</n-button>
<n-button type="primary" @click="closeAndSendHandle">保存 & 发送请求</n-button>
</div>
</n-space>
</template>
</n-card>
@@ -82,9 +91,12 @@ const pondData = computed(() => {
return data[0]
})
watch(() => props.modelShow, (newValue) => {
modelShowRef.value = newValue
})
watch(
() => props.modelShow,
newValue => {
modelShowRef.value = newValue
}
)
watch(
() => pondData.value,
@@ -189,7 +201,7 @@ const deletePond = (targetData: RequestDataPondItemType) => {
}
// 关闭
const closeHandle = () => {
const closeAndSendHandle = () => {
// 将所选内容赋值给对象
if (pondData.value) {
targetData.value.request = {
@@ -200,6 +212,14 @@ const closeHandle = () => {
emit('update:modelShow', false)
emit('sendHandle')
}
const closeHandle = () => {
emit('update:modelShow', false)
}
const onEsc = () => {
closeHandle()
}
</script>
<style lang="scss" scoped>

View File

@@ -10,7 +10,11 @@
>
<!-- 源地址 -->
<setting-item name="前置 URL">
<n-input v-model:value.trim="requestOriginUrl" :disabled="editDisabled" placeholder="例http://127.0.0.1/"></n-input>
<n-input
v-model:value.trim="requestOriginUrl"
:disabled="editDisabled"
placeholder="例http://127.0.0.1/"
></n-input>
</setting-item>
<setting-item name="更新间隔,为 0 只会初始化">
<n-input-group>

View File

@@ -82,7 +82,8 @@ import {
wordCloudUrl,
treemapUrl,
threeEarth01Url,
sankeyUrl
sankeyUrl,
vchartBarDataUrl
} from '@/api/mock'
const props = defineProps({
@@ -150,7 +151,10 @@ const apiList = [
},
{
value: `【关系图】${graphUrl}`
}
},
{
value: `【vchart 柱状图】${vchartBarDataUrl}`
},
]
</script>

View File

@@ -1,5 +1,11 @@
<template>
<n-modal class="go-chart-data-request" v-model:show="modelShowRef" :mask-closable="false" :closeOnEsc="false">
<n-modal
class="go-chart-data-request"
v-model:show="modelShowRef"
:mask-closable="false"
:closeOnEsc="true"
:onEsc="onEsc"
>
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
<template #header></template>
<template #header-extra> </template>
@@ -21,7 +27,10 @@
{{ requestContentTypeObj[requestContentType] }}
</n-tag>
</div>
<n-button type="primary" @click="closeAndSendHandle"> {{ saveBtnText || '保存 & 发送请求' }}</n-button>
<div>
<n-button class="go-mr-3" @click="closeHandle">取消</n-button>
<n-button type="primary" @click="closeAndSendHandle"> {{ saveBtnText || '保存 & 发送请求' }}</n-button>
</div>
</n-space>
</template>
</n-card>
@@ -70,6 +79,10 @@ const closeAndSendHandle = () => {
emit('update:modelShow', false)
emit('sendHandle')
}
const onEsc = () => {
closeHandle()
}
</script>
<style lang="scss" scoped>

View File

@@ -1,5 +1,11 @@
<template>
<n-modal class="go-chart-data-request" v-model:show="modelShowRef" :mask-closable="false" :closeOnEsc="false">
<n-modal
class="go-chart-data-request"
v-model:show="modelShowRef"
:mask-closable="false"
:closeOnEsc="true"
:onEsc="onEsc"
>
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
<template #header></template>
<template #header-extra> </template>
@@ -17,7 +23,7 @@
<template #action>
<n-space justify="space-between">
<n-space v-if="targetDataRequest">
<n-tag :bordered="false" type="primary">名称</n-tag>
<n-tag :bordered="false" type="primary">名称</n-tag>
<n-input
v-model:value="targetDataRequest.dataPondName"
ref="inputInstRef"
@@ -60,10 +66,12 @@ const pondName = ref()
const inputInstRef = ref()
const modelShowRef = ref(false)
watch(() => props.modelShow, (newValue) => {
modelShowRef.value = newValue
})
watch(
() => props.modelShow,
newValue => {
modelShowRef.value = newValue
}
)
const closeHandle = () => {
emit('update:modelShow', false)
@@ -85,6 +93,10 @@ const closeAndSendHandle = () => {
}
})
}
const onEsc = () => {
closeHandle()
}
</script>
<style lang="scss" scoped>

View File

@@ -148,6 +148,7 @@ $max-width: 670px;
min-width: $min-width;
min-width: $max-width;
padding: 0 10px;
padding-left: 20px;
display: flex;
align-items: center;
justify-content: space-between;

Some files were not shown because too many files have changed in this diff Show More