Merge branch 'dev'

This commit is contained in:
奔跑的面条
2023-05-28 17:20:36 +08:00
62 changed files with 2652 additions and 1103 deletions
+2 -2
View File
@@ -129,7 +129,7 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
| 名称 | 版本 | 名称 | 版本 | | 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ | | ------------------- | ----- | ----------- | ------ |
| Vue | 3.2.x | TypeScript4 | 4.6.x | | Vue | 3.2.x | TypeScript4 | 4.6.x |
| Vite | 2.9.x | NaiveUI | 2.27.x | | Vite | 4.2.x | NaiveUI | 2.34.x |
| ECharts | 5.3.x | Pinia | 2.0.x | | ECharts | 5.3.x | Pinia | 2.0.x |
| 详见 `package.json` | 😁 | 🥰 | 🤗 | | 详见 `package.json` | 😁 | 🥰 | 🤗 |
@@ -137,7 +137,7 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
| 名称 | 版本 | 名称 | 版本 | | 名称 | 版本 | 名称 | 版本 |
| ---- | ------- | ------- | ----- | | ---- | ------- | ------- | ----- |
| node | 16.14.x | npm | 8.5.x | | node | 16.16.x | npm | 8.5.x |
| pnpm | 7.1.x | windows | 11 | | pnpm | 7.1.x | windows | 11 |
已完成图表: 已完成图表:
+6 -3
View File
@@ -33,6 +33,7 @@
"highlight.js": "^11.5.0", "highlight.js": "^11.5.0",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"keymaster": "^1.6.2", "keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0", "monaco-editor": "^0.33.0",
"naive-ui": "2.34.3", "naive-ui": "2.34.3",
"pinia": "^2.0.13", "pinia": "^2.0.13",
@@ -49,14 +50,16 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.0.2", "@commitlint/cli": "^17.0.2",
"@commitlint/config-conventional": "^17.0.2", "@commitlint/config-conventional": "^17.0.2",
"@iconify/types": "^2.0.0",
"@iconify/vue": "^4.1.1",
"@types/node": "^16.11.26", "@types/node": "^16.11.26",
"@types/three": "^0.144.0", "@types/three": "^0.144.0",
"@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^5.18.0",
"@vicons/carbon": "^0.12.0", "@vicons/carbon": "^0.12.0",
"@vicons/ionicons5": "~0.11.0", "@vicons/ionicons5": "~0.11.0",
"@vitejs/plugin-vue": "^1.10.2", "@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.9", "@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/compiler-sfc": "^3.2.31", "@vue/compiler-sfc": "^3.2.31",
"@vueuse/core": "^7.7.1", "@vueuse/core": "^7.7.1",
"commitlint": "^17.0.2", "commitlint": "^17.0.2",
@@ -75,7 +78,7 @@
"sass": "^1.49.11", "sass": "^1.49.11",
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"typescript": "4.6.3", "typescript": "4.6.3",
"vite": "4.2.1", "vite": "4.3.6",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-importer": "^0.2.5", "vite-plugin-importer": "^0.2.5",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
+533 -385
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -257,9 +257,36 @@
<n-switch v-model:value="legend.show" size="small"></n-switch> <n-switch v-model:value="legend.show" size="small"></n-switch>
</template> </template>
<setting-item-box name="图例文字"> <setting-item-box name="图例文字">
<setting-item> <setting-item name="颜色">
<n-color-picker size="small" v-model:value="legend.textStyle.color"></n-color-picker> <n-color-picker size="small" v-model:value="legend.textStyle.color"></n-color-picker>
</setting-item> </setting-item>
<setting-item name="大小">
<n-input-number v-model:value="legend.textStyle.fontSize" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box>
<setting-item-box name="图例位置">
<setting-item name="x轴">
<n-select v-model:value="legend.x" size="small" :options="legendConfig.lengendX" />
</setting-item>
<setting-item name="y轴">
<n-select v-model:value="legend.y" size="small" :options="legendConfig.lengendY" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例信息">
<setting-item name="方向">
<n-select v-model:value="legend.orient" size="small" :options="legendConfig.orient" />
</setting-item>
<setting-item name="形状">
<n-select v-model:value="legend.icon" size="small" :options="legendConfig.shape" />
</setting-item>
</setting-item-box>
<setting-item-box name="图例大小">
<setting-item name="宽">
<n-input-number v-model:value="legend.itemWidth" :min="1" size="small"></n-input-number>
</setting-item>
<setting-item name="高">
<n-input-number v-model:value="legend.itemHeight" :min="1" size="small"></n-input-number>
</setting-item>
</setting-item-box> </setting-item-box>
</collapse-item> </collapse-item>
@@ -309,9 +336,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType, computed } from 'vue' import { PropType, computed, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index' import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { axisConfig } from '@/packages/chartConfiguration/echarts/index' import { axisConfig, legendConfig } from '@/packages/chartConfiguration/echarts/index'
import { CollapseItem, SettingItemBox, SettingItem, GlobalSettingPosition } from '@/components/Pages/ChartItemSetting' import { CollapseItem, SettingItemBox, SettingItem, GlobalSettingPosition } from '@/components/Pages/ChartItemSetting'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -360,4 +387,14 @@ const grid = computed(() => {
const visualMap = computed(() => { const visualMap = computed(() => {
return props.optionData.visualMap return props.optionData.visualMap
}) })
// 监听legend color颜色改变type = scroll的颜色
watch(() => legend.value && legend.value.textStyle.color, (newVal) => {
if (legend.value && newVal) {
legend.value.pageTextStyle.color = newVal
}
}, {
immediate: true,
deep: true,
})
</script> </script>
+38 -11
View File
@@ -1,13 +1,13 @@
<template> <template>
<div class="go-Flipper" :class="[flipType, { go: isFlipping }]"> <div class="go-flipper" :class="[flipType, { go: isFlipping }]">
<div class="digital front" :data-front="frontTextFromData"></div> <div class="digital front" :data-front="frontTextFromData"></div>
<div class="digital back" :data-back="backTextFromData"></div> <div class="digital back" :data-back="backTextFromData"></div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, PropType, watch } from 'vue' import { ref, PropType, watch, nextTick } from 'vue'
import { FlipType } from './index' import { FlipType } from './index'
const props = defineProps({ const props = defineProps({
flipType: { flipType: {
@@ -43,6 +43,10 @@ const props = defineProps({
backColor: { backColor: {
type: String, type: String,
default: '#000000' default: '#000000'
},
borderWidth: {
type: Number,
default: 2
} }
}) })
@@ -50,19 +54,27 @@ const isFlipping = ref(false)
const frontTextFromData = ref(props.count || 0) const frontTextFromData = ref(props.count || 0)
const backTextFromData = ref(props.count || 0) const backTextFromData = ref(props.count || 0)
let timeoutID: any = 0
// 翻牌 // 翻牌
const flip = (front: string | number, back: string | number) => { const flip = async (front: string | number, back: string | number) => {
// 如果处于翻转中,则不执行 // 如果处于翻转中,则不执行
if (isFlipping.value) return if (isFlipping.value) {
isFlipping.value = false // 立即结束此次动画
clearTimeout(timeoutID) // 清除上一个计时器任务
await nextTick()
await flip(front, back) // 开始最后一次翻牌任务
return
}
// 设置翻盘前后数据 // 设置翻盘前后数据
backTextFromData.value = back backTextFromData.value = back
frontTextFromData.value = front frontTextFromData.value = front
// 设置翻转状态为true // 设置翻转状态为true
isFlipping.value = true isFlipping.value = true
// 翻牌结束的行为 // 翻牌结束的行为
setTimeout(() => { timeoutID = setTimeout(() => {
isFlipping.value = false // 设置翻转状态为false isFlipping.value = false // 设置翻转状态为false
frontTextFromData.value = back frontTextFromData.value = back
}, props.duration) }, props.duration)
@@ -86,6 +98,7 @@ $radius: v-bind('`${props.radius}px`');
$width: v-bind('`${props.width}px`'); $width: v-bind('`${props.width}px`');
$height: v-bind('`${props.height}px`'); $height: v-bind('`${props.height}px`');
$perspective: v-bind('`${props.height * 2}px`'); $perspective: v-bind('`${props.height * 2}px`');
$borderWidth: v-bind('`${props.borderWidth * 2}px`');
$speed: v-bind('`${props.duration / 1000}s`'); $speed: v-bind('`${props.duration / 1000}s`');
$shadowColor: #000000; $shadowColor: #000000;
$lineColor: #4a9ef8; $lineColor: #4a9ef8;
@@ -125,13 +138,12 @@ $lineColor: #4a9ef8;
} }
// #endregion // #endregion
.go-Flipper { .go-flipper {
display: inline-block; display: inline-block;
position: relative; position: relative;
width: $width; width: $width;
height: $height; height: $height;
line-height: $height; line-height: $height;
border: solid 1px $backColor;
border-radius: $radius; border-radius: $radius;
background: $frontColor; background: $frontColor;
font-size: $width; font-size: $width;
@@ -139,6 +151,17 @@ $lineColor: #4a9ef8;
box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); // 阴影部分 box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); // 阴影部分
text-align: center; text-align: center;
// font-family: 'Helvetica Neue'; // font-family: 'Helvetica Neue';
&::after {
content: '';
position: absolute;
z-index: 10;
left: 0;
top: 0;
right: 0;
bottom: 0;
box-shadow: inset 0 0 $borderWidth 0 $frontColor; // 内测阴影部分
border-radius: $radius;
}
.digital:before, .digital:before,
.digital:after { .digital:after {
@@ -186,11 +209,13 @@ $lineColor: #4a9ef8;
&.down.go .front:before { &.down.go .front:before {
transform-origin: 50% 100%; transform-origin: 50% 100%;
animation: frontFlipDown $speed ease-in-out both; animation: frontFlipDown $speed ease-in-out both;
box-shadow: 0 -2px 6px rgba($color: $lineColor, $alpha: 0.3); box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden; backface-visibility: hidden;
} }
&.down.go .back:after { &.down.go .back:after {
animation: backFlipDown $speed ease-in-out both; animation: backFlipDown $speed ease-in-out both;
box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
} }
/*向上翻*/ /*向上翻*/
&.up .front:after { &.up .front:after {
@@ -208,11 +233,13 @@ $lineColor: #4a9ef8;
&.up.go .front:after { &.up.go .front:after {
transform-origin: 50% 0; transform-origin: 50% 0;
animation: frontFlipUp $speed ease-in-out both; animation: frontFlipUp $speed ease-in-out both;
box-shadow: 0 2px 6px rgba($color: $lineColor, $alpha: 0.3); box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden; backface-visibility: hidden;
} }
&.up.go .back:before { &.up.go .back:before {
animation: backFlipUp $speed ease-in-out both; animation: backFlipUp $speed ease-in-out both;
box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
} }
} }
</style> </style>
+3 -1
View File
@@ -12,5 +12,7 @@ export enum StorageEnum {
// 工作台布局配置 // 工作台布局配置
GO_CHART_LAYOUT_STORE = 'GO_CHART_LAYOUT', GO_CHART_LAYOUT_STORE = 'GO_CHART_LAYOUT',
// 工作台需要保存的数据 // 工作台需要保存的数据
GO_CHART_STORAGE_LIST = 'GO_CHART_STORAGE_LIST' GO_CHART_STORAGE_LIST = 'GO_CHART_STORAGE_LIST',
// 用户存储的图片媒体
GO_USER_MEDIA_PHOTOS = 'GO_USER_MEDIA_PHOTOS'
} }
@@ -1,3 +1,4 @@
export * from './axis' export * from './axis'
export * from './line' export * from './line'
export * from './label' export * from './label'
export * from './legend'
@@ -0,0 +1,70 @@
export const legendConfig = {
// X轴位置
lengendX: [
{
label: '靠左',
value: 'left'
},
{
label: '居中',
value: 'center'
},
{
label: '靠右',
value: 'right'
}
],
// y轴位置
lengendY: [
{
label: '靠上',
value: 'top'
},
{
label: '居中',
value: 'center'
},
{
label: '靠下',
value: 'bottom'
}
],
// 排列方向
orient: [
{
label: '水平',
value: 'horizontal'
},
{
label: '垂直',
value: 'vertical'
}
],
// 形状
shape: [
{
label: '圆形',
value: 'circle'
},
{
label: '方形',
value: 'rect'
},
{
label: '圆角方形',
value: 'roundRect'
},
{
label: '三角形',
value: 'triangle'
},
{
label: '钢笔形',
value: 'pin'
},
{
label: '箭头形',
value: 'arrow'
}
]
}
@@ -18,7 +18,14 @@ export const PieTypeObject = {
[PieTypeEnum.ROSE]: 'rose' [PieTypeEnum.ROSE]: 'rose'
} }
// 其它配置
const otherConfig = {
// 轮播动画
isCarousel: false,
}
const option = { const option = {
...otherConfig,
type: 'ring', type: 'ring',
tooltip: { tooltip: {
show: true, show: true,
@@ -1,88 +1,99 @@
<template> <template>
<!-- Echarts 全局设置 --> <!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting> <global-setting :optionData="optionData"></global-setting>
<CollapseItem name="饼图配置" :expanded="true"> <CollapseItem name="饼图配置" :expanded="true">
<SettingItemBox name="类型"> <SettingItemBox name="类型">
<SettingItem> <SettingItem>
<n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" /> <n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" />
</SettingItem> </SettingItem>
</SettingItemBox> </SettingItemBox>
<SettingItemBox name="标签"> <SettingItemBox name="动画" :alone="true">
<SettingItem> <SettingItem>
<n-space> <n-space>
<n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch> <n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
<n-text>展示标签</n-text> <n-text>开启<n-text :depth="3">将自动隐藏图例</n-text></n-text>
</n-space> </n-space>
</SettingItem> </SettingItem>
<setting-item> <SettingItem>
<n-space> <n-text :depth="3">无鼠标点击图例场景时可强行打开图例</n-text>
<n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch> </SettingItem>
<n-text>引导线</n-text> </SettingItemBox>
</n-space> <SettingItemBox name="标签">
</setting-item> <SettingItem>
<SettingItem name="位置"> <n-space>
<n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" /> <n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch>
</SettingItem> <n-text>展示标签</n-text>
<setting-item name="展示类型"> </n-space>
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" /> </SettingItem>
</setting-item> <setting-item>
</SettingItemBox> <n-space>
<setting-item-box name="圆角"> <n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch>
<setting-item> <n-text>引导线</n-text>
<n-space> </n-space>
<n-input-number </setting-item>
v-model:value="optionData.series[0].itemStyle.borderRadius" <SettingItem name="位置">
size="small" <n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" />
:min="0" </SettingItem>
></n-input-number> <setting-item name="展示类型">
<n-text>圆角大小</n-text> <n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</n-space> </setting-item>
</setting-item> </SettingItemBox>
<setting-item> <setting-item-box name="圆角">
<n-space> <setting-item>
<n-input-number <n-space>
v-model:value="optionData.series[0].itemStyle.borderWidth" <n-input-number
size="small" v-model:value="optionData.series[0].itemStyle.borderRadius"
:min="0" size="small"
></n-input-number> :min="0"
<n-text>线条宽度</n-text> ></n-input-number>
</n-space> <n-text>圆角大小</n-text>
</setting-item> </n-space>
</setting-item-box> </setting-item>
</CollapseItem> <setting-item>
</template> <n-space>
<n-input-number
<script setup lang="ts"> v-model:value="optionData.series[0].itemStyle.borderWidth"
import { PropType, watch } from 'vue' size="small"
import { GlobalThemeJsonType } from '@/settings/chartThemes/index' :min="0"
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting' ></n-input-number>
import { PieTypeObject, PieTypeEnum } from './config' <n-text>线条宽度</n-text>
import { labelConfig } from '@/packages/chartConfiguration/echarts' </n-space>
</setting-item>
const props = defineProps({ </setting-item-box>
optionData: { </CollapseItem>
type: Object as PropType<GlobalThemeJsonType>, </template>
required: true
} <script setup lang="ts">
}) import { PropType, watch } from 'vue'
const fontWeightOptions = [ import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
{ import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
label: PieTypeEnum.NORMAL, import { PieTypeObject, PieTypeEnum } from './config'
value: PieTypeObject[PieTypeEnum.NORMAL] import { labelConfig } from '@/packages/chartConfiguration/echarts'
},
{ const props = defineProps({
label: PieTypeEnum.RING, optionData: {
value: PieTypeObject[PieTypeEnum.RING] type: Object as PropType<GlobalThemeJsonType>,
}, required: true
{ }
label: PieTypeEnum.ROSE, })
value: PieTypeObject[PieTypeEnum.ROSE] const fontWeightOptions = [
} {
] label: PieTypeEnum.NORMAL,
value: PieTypeObject[PieTypeEnum.NORMAL]
const labelFormatterOptions = [ },
{ label: '数据名', value: '{b}' }, {
{ label: '百分比', value: '{d}' }, label: PieTypeEnum.RING,
{ label: '列名:百分比', value: '{b}:{d}%' } value: PieTypeObject[PieTypeEnum.RING]
] },
</script> {
label: PieTypeEnum.ROSE,
value: PieTypeObject[PieTypeEnum.ROSE]
}
]
const labelFormatterOptions = [
{ label: '数据名', value: '{b}' },
{ label: '百分比', value: '{d}' },
{ label: '列名:百分比', value: '{b}:{d}%' }
]
</script>
@@ -1,64 +1,146 @@
<template> <template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()" autoresize></v-chart> <v-chart
</template> ref="vChartRef"
autoresize
<script setup lang="ts"> :init-options="initOptions"
import { computed, PropType, reactive, watch } from 'vue' :theme="themeColor"
import VChart from 'vue-echarts' :option="option"
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook' :manual-update="isPreview()"
import { use } from 'echarts/core' @mouseover="handleHighlight"
import { CanvasRenderer } from 'echarts/renderers' @mouseout="handleDownplay"
import { PieChart } from 'echarts/charts' ></v-chart>
import { mergeTheme } from '@/packages/public/chart' </template>
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks' <script setup lang="ts">
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { computed, PropType, onMounted, watch } from 'vue'
import { isPreview } from '@/utils' import VChart from 'vue-echarts'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
const props = defineProps({ import { CanvasRenderer } from 'echarts/renderers'
themeSetting: { import { PieChart } from 'echarts/charts'
type: Object, import { mergeTheme } from '@/packages/public/chart'
required: true import config, { includes } from './config'
}, import { useChartDataFetch } from '@/hooks'
themeColor: { import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
type: Object, import { isPreview } from '@/utils'
required: true import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
}, import dataJson from './data.json'
chartConfig: {
type: Object as PropType<config>, const props = defineProps({
required: true themeSetting: {
} type: Object,
}) required: true
},
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting) themeColor: {
type: Object,
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent]) required: true
},
const option = computed(() => { chartConfig: {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes) type: Object as PropType<config>,
}) required: true
}
watch( })
() => props.chartConfig.option.type, const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
newData => { let seriesDataNum = -1
try { let seriesDataMaxLength = 0
if (newData === 'nomal') { let intervalInstance: any = null
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = false use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent])
} else if (newData === 'ring') {
props.chartConfig.option.series[0].radius = ['40%', '65%'] const option = computed(() => {
props.chartConfig.option.series[0].roseType = false return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
} else { })
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = true // 会重新选择需要选中和展示的数据
} const handleSeriesData = () => {
} catch (error) { if (seriesDataNum > -1) {
console.log(error) vChartRef.value?.dispatchAction({
} type: 'downplay',
}, dataIndex: seriesDataNum
{ deep: false, immediate: true } })
) }
seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore) vChartRef.value?.dispatchAction({
</script> type: 'highlight',
dataIndex: seriesDataNum
})
}
// 新增轮播
const addPieInterval = (newData?: typeof dataJson, skipPre = false) => {
if (!skipPre && !Array.isArray(newData?.source)) return
if (!skipPre) seriesDataMaxLength = newData?.source.length || 0
clearInterval(intervalInstance)
intervalInstance = setInterval(() => {
handleSeriesData()
}, 1000)
}
// 取消轮播
const clearPieInterval = () => {
vChartRef.value?.dispatchAction({
type: 'downplay',
dataIndex: seriesDataNum
})
clearInterval(intervalInstance)
intervalInstance = null
}
// 处理鼠标聚焦高亮内容
const handleHighlight = () => {
clearPieInterval()
}
// 处理鼠标取消悬浮
const handleDownplay = () => {
if (props.chartConfig.option.isCarousel && !intervalInstance) {
// 恢复轮播
addPieInterval(undefined, true)
}
}
watch(
() => props.chartConfig.option.type,
newData => {
try {
if (newData === 'nomal') {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = false
} else if (newData === 'ring') {
props.chartConfig.option.series[0].radius = ['40%', '65%']
props.chartConfig.option.series[0].roseType = false
} else {
props.chartConfig.option.series[0].radius = '70%'
props.chartConfig.option.series[0].roseType = true
}
} catch (error) {
console.log(error)
}
},
{ deep: false, immediate: true }
)
watch(
() => props.chartConfig.option.isCarousel,
newData => {
if (newData) {
addPieInterval(undefined, true)
props.chartConfig.option.legend.show = false
} else {
props.chartConfig.option.legend.show = true
clearPieInterval()
}
}
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
addPieInterval(newData)
})
onMounted(() => {
seriesDataMaxLength = dataJson.source.length
if (props.chartConfig.option.isCarousel) {
addPieInterval(undefined, true)
}
})
</script>
@@ -16,6 +16,7 @@ export interface OptionType {
flipperGap: number flipperGap: number
flipperType: FlipType flipperType: FlipType
flipperSpeed: number flipperSpeed: number
flipperBorderWidth: number
} }
export const option: OptionType = { export const option: OptionType = {
@@ -28,7 +29,8 @@ export const option: OptionType = {
flipperRadius: 5, flipperRadius: 5,
flipperGap: 10, flipperGap: 10,
flipperType: 'down', flipperType: 'down',
flipperSpeed: 450 flipperSpeed: 450,
flipperBorderWidth: 0,
} }
export default class Config extends PublicConfigClass implements CreateComponentType { export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -16,12 +16,16 @@
<setting-item name="高度"> <setting-item name="高度">
<n-input-number v-model:value="optionData.flipperHeight" size="small" :min="1"></n-input-number> <n-input-number v-model:value="optionData.flipperHeight" size="small" :min="1"></n-input-number>
</setting-item> </setting-item>
<setting-item name="间隔"> <setting-item name="边框">
<n-input-number v-model:value="optionData.flipperGap" size="small" :min="0"></n-input-number> <n-input-number v-model:value="optionData.flipperBorderWidth" size="small" :min="0" :max="10"></n-input-number>
</setting-item> </setting-item>
<setting-item name="圆角"> <setting-item name="圆角">
<n-input-number v-model:value="optionData.flipperRadius" size="small" :min="0"></n-input-number> <n-input-number v-model:value="optionData.flipperRadius" size="small" :min="0"></n-input-number>
</setting-item> </setting-item>
<setting-item name="翻牌间隔">
<n-input-number v-model:value="optionData.flipperGap" size="small" :min="0"></n-input-number>
</setting-item>
<setting-item />
<setting-item name="背景色"> <setting-item name="背景色">
<n-color-picker <n-color-picker
size="small" size="small"
@@ -9,6 +9,7 @@
:radius="flipperRadius" :radius="flipperRadius"
:flip-type="flipperType" :flip-type="flipperType"
:duration="flipperSpeed" :duration="flipperSpeed"
:border-width="flipperBorderWidth"
v-for="(item, index) in flipperData" v-for="(item, index) in flipperData"
:key="index" :key="index"
class="go-d-block" class="go-d-block"
@@ -42,7 +43,8 @@ const {
flipperRadius, flipperRadius,
flipperGap, flipperGap,
flipperType, flipperType,
flipperSpeed flipperSpeed,
flipperBorderWidth
} = toRefs(props.chartConfig.option as OptionType) } = toRefs(props.chartConfig.option as OptionType)
const flipperData = ref<string[] | number[]>([]) const flipperData = ref<string[] | number[]>([])
@@ -0,0 +1,19 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { PipelineHConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
color_type: 1,
o_color: '#0a7ae2',
i_color: '#119bfa',
line_class: 'svg_ani_flow'
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = PipelineHConfig.key
public attr = { ...chartInitConfig, w: 500, h: 15, zIndex: -1 }
public chartConfig = cloneDeep(PipelineHConfig)
public option = cloneDeep(option)
}
@@ -0,0 +1,77 @@
<template>
<CollapseItem name="管道" :expanded="true">
<SettingItemBox name="默认颜色">
<SettingItem>
<n-select v-model:value="optionData.color_type" :options="colorOptions" @update:value="handleColorChange" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="管道颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.o_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="水流颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.i_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="流向">
<SettingItem>
<n-select v-model:value="optionData.line_class" :options="options" />
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, ref } 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
}
})
const options = ref([
{
value: 'svg_ani_flow',
label: '正向'
},
{
value: 'svg_ani_flow_back',
label: '反向'
},
{
value: 'svg_ani_flow_stop',
label: '停止'
}
])
const colorOptions = ref([
{
value: 1,
label: '蓝'
},
{
value: 2,
label: '黄'
}
])
// 默认颜色
const handleColorChange = (e: number) => {
switch (e) {
case 1:
props.optionData.o_color = '#0a7ae2'
props.optionData.i_color = '#119bfa'
break
case 2:
props.optionData.o_color = '#ff9d00'
props.optionData.i_color = '#f7ea37'
break
}
}
</script>
@@ -0,0 +1,13 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const PipelineHConfig: ConfigType = {
key: 'PipelineH',
chartKey: 'VPipelineH',
conKey: 'VCPipelineH',
title: '管道-横向',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.DECORATES,
image: 'Pipeline_H.png'
}
@@ -0,0 +1,141 @@
<template>
<div class="go-decorates-line">
<svg :width="w" :height="h">
<line :x1="0" :y1="h / 2" :x2="w" :y2="h / 2" :stroke="o_color" :stroke-width="h"></line>
<line :x1="0" :y1="h / 2" :x2="w" :y2="h / 2" :stroke="i_color" :stroke-width="h / 2" :class="line_class"></line>
</svg>
</div>
</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 { o_color, i_color, line_class } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
.go-decorates-line {
font-size: 0;
}
/* 正向流动效果 */
.svg_ani_flow {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
@-webkit-keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
/* 停止流动效果 */
.svg_ani_flow_stop {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_stop 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_stop 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 反向流动效果 */
.svg_ani_flow_back {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_back 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_back 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 以最大40高度填充 */
.svg_ani_fill_h40 {
animation: ani_fill_h40 5s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_fill_h40 5s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_fill_h40 {
from {
height: 0px;
}
to {
height: 40px;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
</style>
@@ -0,0 +1,19 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { PipelineVConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
color_type: 1,
o_color: '#0a7ae2',
i_color: '#119bfa',
line_class: 'svg_ani_flow'
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = PipelineVConfig.key
public attr = { ...chartInitConfig, w: 15, h: 500, zIndex: -1 }
public chartConfig = cloneDeep(PipelineVConfig)
public option = cloneDeep(option)
}
@@ -0,0 +1,77 @@
<template>
<CollapseItem name="管道" :expanded="true">
<SettingItemBox name="默认颜色">
<SettingItem>
<n-select v-model:value="optionData.color_type" :options="colorOptions" @update:value="handleColorChange" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="管道颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.o_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="水流颜色">
<SettingItem>
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.i_color"></n-color-picker>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="流向">
<SettingItem>
<n-select v-model:value="optionData.line_class" :options="options" />
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, ref } 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
}
})
const options = ref([
{
value: 'svg_ani_flow',
label: '正向'
},
{
value: 'svg_ani_flow_back',
label: '反向'
},
{
value: 'svg_ani_flow_stop',
label: '停止'
}
])
const colorOptions = ref([
{
value: 1,
label: '蓝'
},
{
value: 2,
label: '黄'
}
])
// 默认颜色
const handleColorChange = (e: number) => {
switch (e) {
case 1:
props.optionData.o_color = '#0a7ae2'
props.optionData.i_color = '#119bfa'
break
case 2:
props.optionData.o_color = '#ff9d00'
props.optionData.i_color = '#f7ea37'
break
}
}
</script>
@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const PipelineVConfig: ConfigType = {
key: 'PipelineV',
chartKey: 'VPipelineV',
conKey: 'VCPipelineV',
title: '管道-纵向',
category: ChatCategoryEnum.MORE,
categoryName: ChatCategoryEnumName.MORE,
package: PackagesCategoryEnum.DECORATES,
image: 'Pipeline_V.png'
}
@@ -0,0 +1,115 @@
<template>
<div class="go-decorates-line">
<svg :width="w" :height="h">
<line :x1="w / 2" :y1="0" :x2="w / 2" :y2="h" :stroke="o_color" :stroke-width="w"></line>
<line :x1="w / 2" :y1="0" :x2="w / 2" :y2="h" :stroke="i_color" :stroke-width="w / 2" :class="line_class"></line>
</svg>
</div>
</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 { o_color, i_color, line_class } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
.go-decorates-line {
font-size: 0;
}
/* 正向流动效果 */
.svg_ani_flow {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
@-webkit-keyframes ani_flow {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 13, 5;
}
}
/* 停止流动效果 */
.svg_ani_flow_stop {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_stop 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_stop 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_stop {
from {
stroke-dasharray: 10, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
/* 反向流动效果 */
.svg_ani_flow_back {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: ani_flow_back 10s linear infinite;
animation-fill-mode: forwards;
-webkit-animation: ani_flow_back 10s linear infinite;
-webkit-animation-fill-mode: forwards;
}
@keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
@-webkit-keyframes ani_flow_back {
from {
stroke-dasharray: 13, 5;
}
to {
stroke-dasharray: 10, 5;
}
}
</style>
@@ -3,5 +3,7 @@ import { TimeCommonConfig } from './TimeCommon/index'
import { ClockConfig } from './Clock/index' import { ClockConfig } from './Clock/index'
import { CountDownConfig } from './CountDown/index' import { CountDownConfig } from './CountDown/index'
import { FlipperNumberConfig } from './FlipperNumber' import { FlipperNumberConfig } from './FlipperNumber'
import { PipelineHConfig } from './PipelineH/index'
import { PipelineVConfig } from './PipelineV/index'
export default [NumberConfig, FlipperNumberConfig, TimeCommonConfig, CountDownConfig, ClockConfig] export default [NumberConfig, FlipperNumberConfig, TimeCommonConfig, CountDownConfig, ClockConfig, PipelineHConfig, PipelineVConfig]
@@ -1,4 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck // @ts-nocheck
import { import {
ArcCurve, ArcCurve,
@@ -0,0 +1,84 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'uim:adobe',
'uim:airplay',
'uim:align',
'uim:angle-double-down',
'uim:angle-double-left',
'uim:angle-double-right',
'uim:angle-double-up',
'uim:angle-down',
'uim:angle-left',
'uim:angle-right',
'uim:angle-up',
'uim:android-alt',
'uim:apple',
'uim:apps',
'uim:bag',
'uim:battery-bolt',
'uim:bing',
'uim:box',
'uim:briefcase',
'uim:calender',
'uim:chart',
'uim:chart-pie',
'uim:circle-layer',
'uim:clinic-medical',
'uim:clock',
'uim:comment-alt',
'uim:comment-alt-dots',
'uim:docker',
'uim:download-alt',
'uim:dribbble',
'uim:dropbox',
'uim:entry',
'uim:exclamation-circle',
'uim:exclamation-triangle',
'uim:exit',
'uim:facebook',
'uim:facebook-messenger',
'uim:facebook-messenger-alt',
'uim:google-drive',
'uim:google-play',
'uim:graph-bar',
'uim:head-side-mask',
'uim:horizontal-align-left',
'uim:hospital',
'uim:house-user',
'uim:image-v',
'uim:key-skeleton',
'uim:layer-group',
'uim:layers-alt',
'uim:link-h',
'uim:microscope',
'uim:microsoft',
'uim:object-group',
'uim:object-ungroup',
'uim:paypal',
'uim:refresh',
'uim:repeat',
'uim:right-indent-alt',
'uim:rocket',
'uim:shield-plus',
'uim:social-distancing',
'uim:telegram-alt',
'uim:user-md',
'uim:toilet-paper',
'uim:youtube'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.COMMON,
categoryName: ChatCategoryEnumName.COMMON,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('uim:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则:packageName/categoryName/componentKey
}))
export default iconList
@@ -0,0 +1,20 @@
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { IconConfig } from './index'
import cloneDeep from 'lodash/cloneDeep'
export const option = {
// 图标名称
dataset: 'uim:apple',
color: '#03A9F4',
size: 64,
rotate: 0 // 旋转角度
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = IconConfig.key
public attr = { ...chartInitConfig, w: 64, h: 64, zIndex: 1 }
public chartConfig = cloneDeep(IconConfig)
public option = cloneDeep(option)
}
@@ -0,0 +1,50 @@
<template>
<collapse-item name="属性" :expanded="true">
<setting-item-box name="样式">
<setting-item name="颜色">
<n-color-picker size="small" :modes="['hex']" v-model:value="optionData.color"></n-color-picker>
</setting-item>
<setting-item name="尺寸">
<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="旋转">
<n-select v-model:value="optionData.rotate" size="small" :options="rotateMode"></n-select>
</setting-item>
</setting-item-box>
</collapse-item>
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { option } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true
}
})
// 旋转方式
const rotateMode = [
{
value: 0,
label: '0°'
},
{
value: 1,
label: '90°'
},
{
value: 2,
label: '180°'
},
{
value: 3,
label: '270°'
}
]
</script>
@@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const IconConfig: ConfigType = {
key: 'Icon',
chartKey: 'VIcon',
conKey: 'VCIcon',
title: '图标',
category: ChatCategoryEnum.DEFAULT,
categoryName: ChatCategoryEnumName.DEFAULT,
package: PackagesCategoryEnum.ICONS,
chartFrame: ChartFrameEnum.STATIC,
image: 'icon.png'
}
@@ -0,0 +1,31 @@
<template>
<div class="go-icon-box">
<Icon :icon="((dataset || '') as string)" :color="color" :width="size" :rotate="rotate" />
</div>
</template>
<script setup lang="ts">
import { PropType, toRefs } from 'vue'
import { CreateComponentType } from '@/packages/index.d'
import { Icon } from '@iconify/vue'
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true
}
})
const { w, h } = toRefs(props.chartConfig.attr)
const { dataset, color, size, rotate } = toRefs(props.chartConfig.option)
</script>
<style lang="scss" scoped>
@include go('icon-box') {
display: flex;
align-items: center;
justify-content: center;
width: v-bind('`${w}px`');
height: v-bind('`${h}px`');
}
</style>
@@ -0,0 +1,45 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'line-md:beer-alt-filled-loop',
'line-md:beer-alt-twotone-loop',
'line-md:cloud-braces-loop',
'line-md:cloud-download-loop',
'line-md:cloud-download-outline-loop',
'line-md:cloud-upload-loop',
'line-md:cloud-upload-outline-loop',
'line-md:coffee-half-empty-twotone-loop',
'line-md:coffee-loop',
'line-md:coffee-twotone-loop',
'line-md:downloading-loop',
'line-md:github-loop',
'line-md:light-dark-loop',
'line-md:loading-alt-loop',
'line-md:loading-loop',
'line-md:loading-twotone-loop',
'line-md:moon-alt-loop',
'line-md:moon-alt-to-sunny-outline-loop-transition',
'line-md:moon-filled-loop',
'line-md:moon-filled-to-sunny-filled-loop-transition',
'line-md:star-pulsating-filled-loop',
'line-md:star-pulsating-loop',
'line-md:star-pulsating-twotone-loop',
'line-md:upload-loop',
'line-md:upload-outline-loop',
'line-md:uploading-loop'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.ML,
categoryName: ChatCategoryEnumName.ML,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('line-md:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则:packageName/categoryName/componentKey
}))
export default iconList
@@ -0,0 +1,53 @@
import { IconConfig } from '../Default/Icon/index'
import { PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
const iconNames = [
'wi:cloud',
'wi:cloudy',
'wi:cloudy-gusts',
'wi:cloudy-windy',
'wi:day-cloudy',
'wi:day-cloudy-high',
'wi:day-hail',
'wi:day-haze',
'wi:day-light-wind',
'wi:day-lightning',
'wi:day-rain',
'wi:day-rain-mix',
'wi:day-rain-wind',
'wi:day-showers',
'wi:day-sleet',
'wi:day-sleet-storm',
'wi:day-snow',
'wi:day-snow-thunderstorm',
'wi:day-snow-wind',
'wi:day-sprinkle',
'wi:day-storm-showers',
'wi:day-sunny',
'wi:day-sunny-overcast',
'wi:hail',
'wi:horizon',
'wi:horizon-alt',
'wi:hot',
'wi:lightning',
'wi:night-alt-cloudy',
'wi:night-alt-cloudy-gusts',
'wi:night-alt-cloudy-high',
'wi:night-alt-hail',
'wi:night-alt-lightning',
'wi:umbrella'
]
const iconList = iconNames.map(name => ({
...IconConfig,
category: ChatCategoryEnum.WEATHER,
categoryName: ChatCategoryEnumName.WEATHER,
package: PackagesCategoryEnum.ICONS,
image: name,
icon: name,
dataset: name,
title: name.replace('wi:', ''),
redirectComponent: `${IconConfig.package}/${IconConfig.category}/${IconConfig.key}` // 跳转组件路径规则:packageName/categoryName/componentKey
}))
export default iconList
+13
View File
@@ -0,0 +1,13 @@
export enum ChatCategoryEnum {
ML = 'MaterialLine',
COMMON = 'Common',
WEATHER = 'Weather',
DEFAULT = 'Default' // 这个仅用来表示组件分类目录,不要在 index.ts 中导入
}
export enum ChatCategoryEnumName {
ML = '动画',
COMMON = '通用',
WEATHER = '天气',
DEFAULT = '默认'
}
+5
View File
@@ -0,0 +1,5 @@
import MaterialLine from './MaterialLine'
import Common from './Common'
import Weather from './Weather'
export const IconList = [...MaterialLine, ...Weather, ...Common]
@@ -12,7 +12,9 @@ export const option = {
[COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE, [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE,
// 下拉展示 // 下拉展示
isPanel: 0, isPanel: 0,
dataset: dayjs().valueOf() dataset: dayjs().valueOf(),
differValue: 0
} }
export default class Config extends PublicConfigClass implements CreateComponentType { export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -1,31 +1,36 @@
<template> <template>
<collapse-item name="展示方式" :expanded="true"> <collapse-item name="展示方式" :expanded="true">
<setting-item-box name="选择方式"> <setting-item-box name="选择方式">
<n-select <n-select v-model:value="optionData.isPanel" size="small" :options="panelOptions" />
v-model:value="optionData.isPanel"
size="small"
:options="panelOptions"
/>
</setting-item-box> </setting-item-box>
</collapse-item> </collapse-item>
<collapse-item name="时间配置" :expanded="true"> <collapse-item name="时间配置" :expanded="true">
<setting-item-box name="基础"> <setting-item-box name="基础">
<setting-item name="类型"> <setting-item name="类型">
<n-select <n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions" />
v-model:value="optionData.componentInteractEventKey"
size="small"
:options="datePickerTypeOptions"
/>
</setting-item> </setting-item>
</setting-item-box> </setting-item-box>
<setting-item-box name="默认值" :alone="true"> <setting-item-box name="默认值" :alone="true">
<n-date-picker <n-date-picker size="small" v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" />
size="small" </setting-item-box>
v-model:value="optionData.dataset"
:type="optionData.componentInteractEventKey" <setting-item-box :alone="true">
/> <template #name>
<n-text>动态</n-text>
<n-tooltip trigger="hover">
<template #trigger>
<n-icon size="21" :depth="3">
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<n-text>动态值不为0时默认值:取当天时间相加当前值</n-text>
</n-tooltip>
</template>
<n-input-number v-model:value="optionData.differValue" class="input-num-width" size="small" :min="-40" :max="40">
<template #suffix> </template>
</n-input-number>
</setting-item-box> </setting-item-box>
</collapse-item> </collapse-item>
</template> </template>
@@ -39,9 +39,9 @@ const onChange = (v: number | number[]) => {
props.chartConfig, props.chartConfig,
useChartEditStore, useChartEditStore,
{ {
[ComponentInteractParamsEnum.DATE_START]: v[0] | dayjs().valueOf(), [ComponentInteractParamsEnum.DATE_START]: v[0] || dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_END]: v[1] | dayjs().valueOf(), [ComponentInteractParamsEnum.DATE_END]: v[1] || dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_RANGE]: `${v[0]}-${v[1]}` [ComponentInteractParamsEnum.DATE_RANGE]: `${v[0] || dayjs().valueOf()}-${v[1] || dayjs().valueOf()}`
}, },
InteractEventOn.CHANGE InteractEventOn.CHANGE
) )
@@ -50,13 +50,12 @@ const onChange = (v: number | number[]) => {
useChartInteract( useChartInteract(
props.chartConfig, props.chartConfig,
useChartEditStore, useChartEditStore,
{ [ComponentInteractParamsEnum.DATE]: v }, { [ComponentInteractParamsEnum.DATE]: v || dayjs().valueOf() },
InteractEventOn.CHANGE InteractEventOn.CHANGE
) )
} }
} }
// 手动更新
watch( watch(
() => props.chartConfig.option.dataset, () => props.chartConfig.option.dataset,
(newData: number | number[]) => { (newData: number | number[]) => {
@@ -68,6 +67,25 @@ watch(
immediate: true immediate: true
} }
) )
// 手动更新
watch(
() => props.chartConfig.option.differValue,
(newData: number) => {
if (props.chartConfig.option.differValue === 0) return
if (typeof option.dataset === 'object') {
option.dataset[0] = dayjs().add(newData, 'day').valueOf()
option.dataset[1] = dayjs().add(newData, 'day').valueOf()
} else {
option.dataset = dayjs().add(newData, 'day').valueOf()
}
// 关联目标组件首次请求带上默认内容
onChange(newData)
},
{
immediate: true
}
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -7,7 +7,8 @@
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
:width="w" :width="w"
:height="h" :height="h"
></n-image> lazy
/>
</div> </div>
</template> </template>
@@ -0,0 +1,91 @@
import { ChartFrameEnum, ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
import { setLocalStorage, getLocalStorage, goDialog } from '@/utils'
import { StorageEnum } from '@/enums/storageEnum'
import { FileTypeEnum } from '@/enums/fileTypeEnum'
import { backgroundImageSize } from '@/settings/designSetting'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
const StoreKey = StorageEnum.GO_USER_MEDIA_PHOTOS
/**
* 上传完成事件类型
*/
type UploadCompletedEventType = {
fileName: string
url: string
}
const userPhotosList: ConfigType[] = getLocalStorage(StoreKey) || []
const uploadFile = (callback: Function | null = null) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.png,.jpg,.jpeg,.gif' // 这里只允许部分图片类型
input.onchange = async () => {
if (!input.files || !input.files.length) return
const file = input.files[0]
const { name, size, type } = file
if (size > 1024 * 1024 * backgroundImageSize) {
window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`)
return false
}
if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) {
window['$message'].warning('文件格式不符合,请重新上传!')
return false
}
const reader = new FileReader()
reader.onload = () => {
const eventObj: UploadCompletedEventType = { fileName: name, url: reader.result as string }
callback && callback(eventObj)
}
reader.readAsDataURL(file)
}
input.click()
}
const addConfig = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
title: '点击上传图片',
image: 'upload.png',
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}`, // 跳转组件路径规则:packageName/categoryName/componentKey
disabled: true,
configEvents: {
// 点击上传事件
addHandle: (photoConfig: ConfigType) => {
goDialog({
message: `图片需小于 ${backgroundImageSize}M 且只暂存在浏览器中。当前图片暂存上限5M,超过不再缓存新图片,请自行对接后端接口!现编译成 base64 进行渲染,对接后端后请使用【URL地址】进行交互!`,
transformOrigin: 'center',
onPositiveCallback: () => {
uploadFile((e: UploadCompletedEventType) => {
// 和上传组件一样配置,更换标题,图片,预设数据
const packagesStore = usePackagesStore()
const newPhoto = {
...ImageConfig,
category: ChatCategoryEnum.PRIVATE,
categoryName: ChatCategoryEnumName.PRIVATE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
title: e.fileName,
image: e.url,
dataset: e.url,
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}` // 跳转组件路径规则:packageName/categoryName/componentKey
}
userPhotosList.unshift(newPhoto)
// 存储在本地数据中
setLocalStorage(StoreKey, userPhotosList)
// 插入到上传按钮前的位置
packagesStore.addPhotos(newPhoto, 1)
})
}
})
}
}
}
export default [addConfig, ...userPhotosList]
@@ -0,0 +1,23 @@
import { ChartFrameEnum, PackagesCategoryEnum } from '@/packages/index.d'
import { ImageConfig } from '@/packages/components/Informations/Mores/Image/index'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../index.d'
// 远程共享库(调接口获取图像列表)
const imageList = [
{ imageName: 'carousel1', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg' },
{ imageName: 'carousel2', imageUrl: 'https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg' }
]
const photoConfigList = imageList.map(i => ({
...ImageConfig,
category: ChatCategoryEnum.SHARE,
categoryName: ChatCategoryEnumName.SHARE,
package: PackagesCategoryEnum.PHOTOS,
chartFrame: ChartFrameEnum.STATIC,
image: i.imageUrl,
dataset: i.imageUrl,
title: i.imageName,
redirectComponent: `${ImageConfig.package}/${ImageConfig.category}/${ImageConfig.key}` // 跳转组件路径规则:packageName/categoryName/componentKey
}))
export default photoConfigList
+9
View File
@@ -0,0 +1,9 @@
export enum ChatCategoryEnum {
PRIVATE = 'Private',
SHARE = 'Share'
}
export enum ChatCategoryEnumName {
PRIVATE = '私有图',
SHARE = '共享图'
}
+4
View File
@@ -0,0 +1,4 @@
import Private from './Private'
import Share from './Share'
export const PhotoList = [...Private, ...Share]
+27 -1
View File
@@ -15,15 +15,34 @@ export enum ChartFrameEnum {
// 组件配置 // 组件配置
export type ConfigType = { export type ConfigType = {
// 组件 key
key: string key: string
// 画布组件 key
chartKey: string chartKey: string
// 右侧设置面板组件 key
conKey: string conKey: string
// 标题
title: string title: string
// 分类
category: string category: string
// 分类名称
categoryName: string categoryName: string
// 所属包
package: string package: string
// 归类
chartFrame?: ChartFrameEnum chartFrame?: ChartFrameEnum
// 预览图
image: string image: string
// 从指定路径创建创建该组件
redirectComponent?: string
// 组件预设的 dataset 值(图片/图标)
dataset?: any
// 禁用 拖拽或双击生成组件
disabled?: boolean
// 图标
icon?: string
// 事件
configEvents?: { [T: string]: Function }
} }
// 数据请求 // 数据请求
@@ -120,7 +139,7 @@ export interface PublicConfigType {
} }
filter?: string filter?: string
status: StatusType status: StatusType
interactActions?: InteractActionsType[], interactActions?: InteractActionsType[]
events: { events: {
baseEvent: { baseEvent: {
[K in BaseEvent]?: string [K in BaseEvent]?: string
@@ -140,6 +159,7 @@ export interface CreateComponentType extends PublicConfigType, requestConfig {
key: string key: string
chartConfig: ConfigType chartConfig: ConfigType
option: GlobalThemeJsonType option: GlobalThemeJsonType
groupList?: Array<CreateComponentType>
} }
// 组件成组实例类 // 组件成组实例类
@@ -155,6 +175,8 @@ export enum PackagesCategoryEnum {
CHARTS = 'Charts', CHARTS = 'Charts',
TABLES = 'Tables', TABLES = 'Tables',
INFORMATIONS = 'Informations', INFORMATIONS = 'Informations',
PHOTOS = 'Photos',
ICONS = 'Icons',
DECORATES = 'Decorates' DECORATES = 'Decorates'
} }
@@ -163,6 +185,8 @@ export enum PackagesCategoryName {
CHARTS = '图表', CHARTS = '图表',
TABLES = '列表', TABLES = '列表',
INFORMATIONS = '信息', INFORMATIONS = '信息',
PHOTOS = '图片',
ICONS = '图标',
DECORATES = '小组件' DECORATES = '小组件'
} }
@@ -177,5 +201,7 @@ export type PackagesType = {
[PackagesCategoryEnum.CHARTS]: ConfigType[] [PackagesCategoryEnum.CHARTS]: ConfigType[]
[PackagesCategoryEnum.INFORMATIONS]: ConfigType[] [PackagesCategoryEnum.INFORMATIONS]: ConfigType[]
[PackagesCategoryEnum.TABLES]: ConfigType[] [PackagesCategoryEnum.TABLES]: ConfigType[]
[PackagesCategoryEnum.PHOTOS]: ConfigType[]
[PackagesCategoryEnum.ICONS]: ConfigType[]
[PackagesCategoryEnum.DECORATES]: ConfigType[] [PackagesCategoryEnum.DECORATES]: ConfigType[]
} }
+99 -87
View File
@@ -1,87 +1,99 @@
import { ChartList } from '@/packages/components/Charts/index' import { ChartList } from '@/packages/components/Charts/index'
import { DecorateList } from '@/packages/components/Decorates/index' import { DecorateList } from '@/packages/components/Decorates/index'
import { InformationList } from '@/packages/components/Informations/index' import { InformationList } from '@/packages/components/Informations/index'
import { TableList } from '@/packages/components/Tables/index' import { TableList } from '@/packages/components/Tables/index'
import { PackagesCategoryEnum, PackagesType, ConfigType, FetchComFlagType } from '@/packages/index.d' import { PhotoList } from '@/packages/components/Photos/index'
import { IconList } from '@/packages/components/Icons/index'
const configModules: Record<string, { default: string }> = import.meta.glob('./components/**/config.vue', { import { PackagesCategoryEnum, PackagesType, ConfigType, FetchComFlagType } from '@/packages/index.d'
eager: true
}) const configModules: Record<string, { default: string }> = import.meta.glob('./components/**/config.vue', {
const indexModules: Record<string, { default: string }> = import.meta.glob('./components/**/index.vue', { eager: true
eager: true })
}) const indexModules: Record<string, { default: string }> = import.meta.glob('./components/**/index.vue', {
const imagesModules: Record<string, { default: string }> = import.meta.glob('../assets/images/chart/**', { eager: true
eager: true })
}) const imagesModules: Record<string, { default: string }> = import.meta.glob('../assets/images/chart/**', {
eager: true
// * 所有图表 })
export let packagesList: PackagesType = {
[PackagesCategoryEnum.CHARTS]: ChartList, // * 所有图表
[PackagesCategoryEnum.INFORMATIONS]: InformationList, export let packagesList: PackagesType = {
[PackagesCategoryEnum.TABLES]: TableList, [PackagesCategoryEnum.CHARTS]: ChartList,
[PackagesCategoryEnum.DECORATES]: DecorateList [PackagesCategoryEnum.INFORMATIONS]: InformationList,
} [PackagesCategoryEnum.TABLES]: TableList,
[PackagesCategoryEnum.DECORATES]: DecorateList,
/** [PackagesCategoryEnum.PHOTOS]: PhotoList,
* * 获取目标组件配置信息 [PackagesCategoryEnum.ICONS]: IconList
* @param targetData }
*/
export const createComponent = async (targetData: ConfigType) => { /**
const { category, key } = targetData * * 获取目标组件配置信息
const chart = await import(`./components/${targetData.package}/${category}/${key}/config.ts`) * @param targetData
return new chart.default() */
} export const createComponent = async (targetData: ConfigType) => {
const { redirectComponent, category, key } = targetData
/** // redirectComponent 是给图片组件库和图标组件库使用的
* * 获取组件 if (redirectComponent) {
* @param {string} chartName 名称 const [packageName, categoryName, keyName] = redirectComponent.split('/')
* @param {FetchComFlagType} flag 标识 0为展示组件, 1为配置组件 const redirectChart = await import(`./components/${packageName}/${categoryName}/${keyName}/config.ts`)
*/ return new redirectChart.default()
const fetchComponent = (chartName: string, flag: FetchComFlagType) => { }
const module = flag === FetchComFlagType.VIEW ? indexModules : configModules const chart = await import(`./components/${targetData.package}/${category}/${key}/config.ts`)
for (const key in module) { return new chart.default()
const urlSplit = key.split('/') }
if (urlSplit[urlSplit.length - 2] === chartName) {
return module[key] /**
} * * 获取组件
} * @param {string} chartName 名称
} * @param {FetchComFlagType} flag 标识 0为展示组件, 1为配置组件
*/
/** const fetchComponent = (chartName: string, flag: FetchComFlagType) => {
* * 获取展示组件 const module = flag === FetchComFlagType.VIEW ? indexModules : configModules
* @param {ConfigType} dropData 配置项 for (const key in module) {
*/ const urlSplit = key.split('/')
export const fetchChartComponent = (dropData: ConfigType) => { if (urlSplit[urlSplit.length - 2] === chartName) {
const { key } = dropData return module[key]
return fetchComponent(key, FetchComFlagType.VIEW)?.default }
} }
}
/**
* * 获取配置组件 /**
* @param {ConfigType} dropData 配置项 * * 获取展示组件
*/ * @param {ConfigType} dropData 配置项
export const fetchConfigComponent = (dropData: ConfigType) => { */
const { key } = dropData export const fetchChartComponent = (dropData: ConfigType) => {
return fetchComponent(key, FetchComFlagType.CONFIG)?.default const { key } = dropData
} return fetchComponent(key, FetchComFlagType.VIEW)?.default
}
/**
* * 获取图片内容 /**
* @param {ConfigType} targetData 配置项 * * 获取配置组件
*/ * @param {ConfigType} dropData 配置项
export const fetchImages = async (targetData?: ConfigType) => { */
if (!targetData) return '' export const fetchConfigComponent = (dropData: ConfigType) => {
// 新数据动态处理 const { key } = dropData
const { image, package: targetDataPackage } = targetData return fetchComponent(key, FetchComFlagType.CONFIG)?.default
// 兼容旧数据 }
if (image.includes('@') || image.includes('base64')) return image
/**
const imageName = image.substring(image.lastIndexOf('/') + 1) * * 获取图片内容
for (const key in imagesModules) { * @param {ConfigType} targetData 配置项
const urlSplit = key.split('/') */
if (urlSplit[urlSplit.length - 1] === imageName) { export const fetchImages = async (targetData?: ConfigType) => {
return imagesModules[key]?.default if (!targetData) return ''
} // 正则判断图片是否为 url,是则直接返回该 url
} if (/^(http|https):\/\/([\w.]+\/?)\S*/.test(targetData.image)) return targetData.image
return '' // 新数据动态处理
} const { image, package: targetDataPackage } = targetData
// 兼容旧数据
if (image.includes('@') || image.includes('base64')) return image
const imageName = image.substring(image.lastIndexOf('/') + 1)
for (const key in imagesModules) {
const urlSplit = key.split('/')
if (urlSplit[urlSplit.length - 1] === imageName) {
return imagesModules[key]?.default
}
}
return ''
}
+7 -4
View File
@@ -61,13 +61,14 @@ import {
Pulse as PulseIcon, Pulse as PulseIcon,
Folder as FolderIcon, Folder as FolderIcon,
FolderOpen as FolderOpenIcon, FolderOpen as FolderOpenIcon,
Image as ImageIcon, ImageOutline as ImageIcon,
Images as ImagesIcon, Images as ImagesIcon,
List as ListIcon, List as ListIcon,
EyeOutline as EyeOutlineIcon, EyeOutline as EyeOutlineIcon,
EyeOffOutline as EyeOffOutlineIcon, EyeOffOutline as EyeOffOutlineIcon,
Albums as AlbumsIcon, Albums as AlbumsIcon,
Analytics as AnalyticsIcon Analytics as AnalyticsIcon,
AirplaneOutline as AirPlaneOutlineIcon
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { import {
@@ -238,10 +239,12 @@ const ionicons5 = {
// 眼睛 // 眼睛
EyeOutlineIcon, EyeOutlineIcon,
EyeOffOutlineIcon, EyeOffOutlineIcon,
// 图表列表 // 图表列表
AlbumsIcon, AlbumsIcon,
// 分析 // 分析
AnalyticsIcon AnalyticsIcon,
// 飞机
AirPlaneOutlineIcon
} }
const carbon = { const carbon = {
+11 -1
View File
@@ -86,8 +86,18 @@
}, },
"legend": { "legend": {
"show": true, "show": true,
"top": "5%", "type": "scroll",
"x": "center",
"y": "top",
"icon": "circle",
"orient": "horizontal",
"textStyle": { "textStyle": {
"color": "#B9B8CE",
"fontSize": 18
},
"itemHeight": 15,
"itemWidth": 15,
"pageTextStyle": {
"color": "#B9B8CE" "color": "#B9B8CE"
} }
}, },
+3 -3
View File
@@ -2212,9 +2212,9 @@
"pinyin": "tengluozi" "pinyin": "tengluozi"
}, },
{ {
"CMYK": [57, 62, 16, 2], "CMYK": [34, 39, 0, 24],
"RGB": [128, 109, 158], "RGB": [128, 118, 195],
"hex": "#806d9e", "hex": "#8076c3",
"name": "槿紫", "name": "槿紫",
"pinyin": "jinzi" "pinyin": "jinzi"
}, },
+9 -8
View File
@@ -1,8 +1,9 @@
import { PackagesType, ConfigType } from '@/packages/index.d' import { PackagesType, ConfigType } from '@/packages/index.d'
export { ConfigType } export { ConfigType }
export { PackagesType } export { PackagesType }
export interface PackagesStoreType { export interface PackagesStoreType {
packagesList: PackagesType packagesList: PackagesType,
} newPhoto?: ConfigType
}
@@ -1,16 +1,32 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { PackagesStoreType, PackagesType } from './packagesStore.d' import { ConfigType, PackagesStoreType, PackagesType } from './packagesStore.d'
import { packagesList } from '@/packages/index' import { packagesList } from '@/packages/index'
import { StorageEnum } from '@/enums/storageEnum'
// 组件 pakages import { getLocalStorage, setLocalStorage } from '@/utils'
export const usePackagesStore = defineStore({
id: 'usePackagesStore', // 组件 packages
state: (): PackagesStoreType => ({ export const usePackagesStore = defineStore({
packagesList: Object.freeze(packagesList) id: 'usePackagesStore',
}), state: (): PackagesStoreType => ({
getters: { packagesList: Object.freeze(packagesList),
getPackagesList(): PackagesType { newPhoto: undefined
return this.packagesList }),
} getters: {
} getPackagesList(): PackagesType {
}) return this.packagesList
}
},
actions: {
addPhotos(newPhoto: ConfigType, index: number) {
this.newPhoto = newPhoto
this.packagesList.Photos.splice(index, 0, newPhoto)
},
deletePhotos(photoInfo: ConfigType, index: number) {
this.packagesList.Photos.splice(index, 1)
const StoreKey = StorageEnum.GO_USER_MEDIA_PHOTOS
const userPhotosList = getLocalStorage(StoreKey)
userPhotosList.splice(index - 1, 1)
setLocalStorage(StoreKey, userPhotosList)
}
}
})
+7 -2
View File
@@ -314,6 +314,11 @@ export const JSONStringify = <T>(data: T) => {
) )
} }
export const evalFn = (fn: string) => {
var Fun = Function // 一个变量指向Function,防止前端编译工具报错
return new Fun('return ' + fn)()
}
/** /**
* * JSON反序列化,支持函数和 undefined * * JSON反序列化,支持函数和 undefined
* @param data * @param data
@@ -329,12 +334,12 @@ export const JSONParse = (data: string) => {
} }
// 还原函数值 // 还原函数值
if (typeof v === 'string' && v.indexOf && (v.indexOf('function') > -1 || v.indexOf('=>') > -1)) { if (typeof v === 'string' && v.indexOf && (v.indexOf('function') > -1 || v.indexOf('=>') > -1)) {
return eval(`(function(){return ${v}})()`) return evalFn(`(function(){return ${v}})()`)
} else if (typeof v === 'string' && v.indexOf && v.indexOf('return ') > -1) { } else if (typeof v === 'string' && v.indexOf && v.indexOf('return ') > -1) {
const baseLeftIndex = v.indexOf('(') const baseLeftIndex = v.indexOf('(')
if (baseLeftIndex > -1) { if (baseLeftIndex > -1) {
const newFn = `function ${v.substring(baseLeftIndex)}` const newFn = `function ${v.substring(baseLeftIndex)}`
return eval(`(function(){return ${newFn}})()`) return evalFn(`(function(){return ${newFn}})()`)
} }
} }
return v return v
@@ -9,11 +9,12 @@
<div <div
class="item-box" class="item-box"
v-for="(item, index) in menuOptions" v-for="(item, index) in menuOptions"
:key="index" :key="item.title"
draggable draggable
@dragstart="dragStartHandle($event, item)" @dragstart="!item.disabled && dragStartHandle($event, item)"
@dragend="dragendHandle" @dragend="!item.disabled && dragendHandle"
@dblclick="dblclickHandle(item)" @dblclick="dblclickHandle(item)"
@click="clickHandle(item)"
> >
<div class="list-header"> <div class="list-header">
<mac-os-control-btn class="list-header-control-btn" :mini="true" :disabled="true"></mac-os-control-btn> <mac-os-control-btn class="list-header-control-btn" :mini="true" :disabled="true"></mac-os-control-btn>
@@ -21,14 +22,28 @@
<n-ellipsis>{{ item.title }}</n-ellipsis> <n-ellipsis>{{ item.title }}</n-ellipsis>
</n-text> </n-text>
</div> </div>
<div class="list-center go-flex-center go-transition"> <div class="list-center go-flex-center go-transition" draggable="true">
<chart-glob-image class="list-img" :chartConfig="item"></chart-glob-image> <Icon v-if="item.icon" class="list-img" :icon="item.icon" color="#999" width="48" />
<chart-glob-image v-else class="list-img" :chartConfig="item" />
</div> </div>
<div class="list-bottom"> <div class="list-bottom">
<n-text class="list-bottom-text" depth="3"> <n-text class="list-bottom-text" depth="3">
<n-ellipsis style="max-width: 90%">{{ item.title }}</n-ellipsis> <n-ellipsis style="max-width: 90%">{{ item.title }}</n-ellipsis>
</n-text> </n-text>
</div> </div>
<!-- 遮罩 -->
<div v-if="item.disabled" class="list-model"></div>
<!-- 工具栏 -->
<div v-if="isShowTools(item)" class="list-tools go-transition" @click="deleteHandle(item, index)">
<n-button text type="default" color="#ffffff">
<template #icon>
<n-icon>
<TrashIcon />
</n-icon>
</template>
<span>删除</span>
</n-button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -42,17 +57,23 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { ChartModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore' import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
import { componentInstall, loadingStart, loadingFinish, loadingError, JSONStringify } from '@/utils' import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { componentInstall, loadingStart, loadingFinish, loadingError, JSONStringify, goDialog } from '@/utils'
import { DragKeyEnum } from '@/enums/editPageEnum' import { DragKeyEnum } from '@/enums/editPageEnum'
import { createComponent } from '@/packages' import { createComponent } from '@/packages'
import { ConfigType, CreateComponentType } from '@/packages/index.d' import { ConfigType, CreateComponentType, PackagesCategoryEnum } from '@/packages/index.d'
import { ChatCategoryEnum } from '@/packages/components/Photos/index.d'
import { fetchConfigComponent, fetchChartComponent } from '@/packages/index' import { fetchConfigComponent, fetchChartComponent } from '@/packages/index'
import { Icon } from '@iconify/vue'
import { icon } from '@/plugins'
import omit from 'lodash/omit' import omit from 'lodash/omit'
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { TrashIcon } = icon.ionicons5
defineProps({ const emit = defineEmits(['deletePhoto'])
const props = defineProps({
menuOptions: { menuOptions: {
type: Array as PropType<ConfigType[]>, type: Array as PropType<ConfigType[]>,
default: () => [] default: () => []
@@ -62,6 +83,11 @@ defineProps({
const chartLayoutStore = useChartLayoutStore() const chartLayoutStore = useChartLayoutStore()
const contentChartsItemBoxRef = ref() const contentChartsItemBoxRef = ref()
// 判断工具栏展示
const isShowTools = (item: ConfigType) => {
return !item.disabled && item.package === PackagesCategoryEnum.PHOTOS && item.category === ChatCategoryEnum.PRIVATE
}
// 组件展示状态 // 组件展示状态
const chartMode: Ref<ChartModeEnum> = computed(() => { const chartMode: Ref<ChartModeEnum> = computed(() => {
return chartLayoutStore.getChartType return chartLayoutStore.getChartType
@@ -69,6 +95,7 @@ const chartMode: Ref<ChartModeEnum> = computed(() => {
// 拖拽处理 // 拖拽处理
const dragStartHandle = (e: DragEvent, item: ConfigType) => { const dragStartHandle = (e: DragEvent, item: ConfigType) => {
if (item.disabled) return
// 动态注册图表组件 // 动态注册图表组件
componentInstall(item.chartKey, fetchChartComponent(item)) componentInstall(item.chartKey, fetchChartComponent(item))
componentInstall(item.conKey, fetchConfigComponent(item)) componentInstall(item.conKey, fetchConfigComponent(item))
@@ -85,6 +112,7 @@ const dragendHandle = () => {
// 双击添加 // 双击添加
const dblclickHandle = async (item: ConfigType) => { const dblclickHandle = async (item: ConfigType) => {
if (item.disabled) return
try { try {
loadingStart() loadingStart()
// 动态注册图表组件 // 动态注册图表组件
@@ -92,6 +120,11 @@ const dblclickHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(item)) componentInstall(item.conKey, fetchConfigComponent(item))
// 创建新图表组件 // 创建新图表组件
let newComponent: CreateComponentType = await createComponent(item) let newComponent: CreateComponentType = await createComponent(item)
if (item.redirectComponent) {
item.dataset && (newComponent.option.dataset = item.dataset)
newComponent.chartConfig.title = item.title
newComponent.chartConfig.chartFrame = item.chartFrame
}
// 添加 // 添加
chartEditStore.addComponentList(newComponent, false, true) chartEditStore.addComponentList(newComponent, false, true)
// 选中 // 选中
@@ -103,6 +136,23 @@ const dblclickHandle = async (item: ConfigType) => {
} }
} }
// 单击事件
const clickHandle = (item: ConfigType) => {
item?.configEvents?.addHandle(item)
}
const deleteHandle = (item: ConfigType, index: number) => {
goDialog({
message: '是否删除此图片?',
transformOrigin: 'center',
onPositiveCallback: () => {
const packagesStore = usePackagesStore()
emit('deletePhoto', item, index)
packagesStore.deletePhotos(item, index)
}
})
}
watch( watch(
() => chartMode.value, () => chartMode.value,
(newValue: ChartModeEnum) => { (newValue: ChartModeEnum) => {
@@ -135,6 +185,7 @@ $halfCenterHeight: 50px;
gap: 9px; gap: 9px;
transition: all 0.7s linear; transition: all 0.7s linear;
.item-box { .item-box {
position: relative;
margin: 0; margin: 0;
width: $itemWidth; width: $itemWidth;
overflow: hidden; overflow: hidden;
@@ -145,7 +196,10 @@ $halfCenterHeight: 50px;
&:hover { &:hover {
@include hover-border-color('background-color4'); @include hover-border-color('background-color4');
.list-img { .list-img {
transform: scale(1.1); transform: scale(1.08);
}
.list-tools {
opacity: 1;
} }
} }
.list-header { .list-header {
@@ -157,6 +211,7 @@ $halfCenterHeight: 50px;
&-text { &-text {
font-size: 12px; font-size: 12px;
margin-left: 8px; margin-left: 8px;
user-select: none;
} }
} }
.list-center { .list-center {
@@ -165,7 +220,7 @@ $halfCenterHeight: 50px;
overflow: hidden; overflow: hidden;
.list-img { .list-img {
height: 100px; height: 100px;
width: 140px; max-width: 140px;
border-radius: 6px; border-radius: 6px;
@extend .go-transition; @extend .go-transition;
} }
@@ -177,6 +232,33 @@ $halfCenterHeight: 50px;
padding-left: 5px; padding-left: 5px;
} }
} }
.list-model {
z-index: 1;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0);
}
.list-tools {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
bottom: 0;
left: 0;
margin: 0 4px 2px;
height: 26px;
width: calc(100% - 8px);
opacity: 0;
border-radius: 6px;
backdrop-filter: blur(20px);
background-color: rgba(255, 255, 255, 0.15);
&:hover {
background-color: rgba(232, 128, 128, 0.7);
}
}
} }
&.single { &.single {
.item-box { .item-box {
@@ -196,6 +278,9 @@ $halfCenterHeight: 50px;
.item-box { .item-box {
width: $halfItemWidth; width: $halfItemWidth;
max-width: $maxItemWidth; max-width: $maxItemWidth;
.list-img {
max-width: 76px;
}
} }
.list-center { .list-center {
height: $halfCenterHeight; height: $halfCenterHeight;
@@ -11,8 +11,8 @@
@update:value="clickItemHandle" @update:value="clickItemHandle"
></n-menu> ></n-menu>
<div class="chart-content-list"> <div class="chart-content-list">
<n-scrollbar> <n-scrollbar trigger="none">
<charts-item-box :menuOptions="packages.selectOptions"></charts-item-box> <charts-item-box :menuOptions="packages.selectOptions" @deletePhoto="deleteHandle"></charts-item-box>
</n-scrollbar> </n-scrollbar>
</div> </div>
</div> </div>
@@ -23,8 +23,11 @@ import { ref, watch, computed, reactive } from 'vue'
import { ConfigType } from '@/packages/index.d' import { ConfigType } from '@/packages/index.d'
import { useSettingStore } from '@/store/modules/settingStore/settingStore' import { useSettingStore } from '@/store/modules/settingStore/settingStore'
import { loadAsyncComponent } from '@/utils' import { loadAsyncComponent } from '@/utils'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { PackagesCategoryEnum } from '@/packages/index.d'
const ChartsItemBox = loadAsyncComponent(() => import('../ChartsItemBox/index.vue')) const ChartsItemBox = loadAsyncComponent(() => import('../ChartsItemBox/index.vue'))
const packagesStore = usePackagesStore()
const props = defineProps({ const props = defineProps({
selectOptions: { selectOptions: {
@@ -61,7 +64,7 @@ let packages = reactive<{
saveSelectOptions: {} saveSelectOptions: {}
}) })
const selectValue = ref<string>() const selectValue = ref<string>('all')
// 设置初始列表 // 设置初始列表
const setSelectOptions = (categorys: any) => { const setSelectOptions = (categorys: any) => {
@@ -79,7 +82,6 @@ watch(
if (!newData) return if (!newData) return
newData.list.forEach((e: ConfigType) => { newData.list.forEach((e: ConfigType) => {
const value: ConfigType[] = (packages.categorys as any)[e.category] const value: ConfigType[] = (packages.categorys as any)[e.category]
// @ts-ignore
packages.categorys[e.category] = value && value.length ? [...value, e] : [e] packages.categorys[e.category] = value && value.length ? [...value, e] : [e]
packages.categoryNames[e.category] = e.categoryName packages.categoryNames[e.category] = e.categoryName
packages.categorys['all'].push(e) packages.categorys['all'].push(e)
@@ -100,6 +102,22 @@ watch(
} }
) )
watch(
() => packagesStore.newPhoto,
newPhoto => {
if (!newPhoto) return
const newPhotoCategory = newPhoto.category
packages.categorys[newPhotoCategory].splice(1, 0, newPhoto)
packages.categorys['all'].splice(1, 0, newPhoto)
}
)
// 删除图片
const deleteHandle = (item: ConfigType, index: number) => {
packages.categorys[item.category].splice(index, 1)
packages.categorys['all'].splice(index, 1)
}
// 处理点击事件 // 处理点击事件
const clickItemHandle = (key: string) => { const clickItemHandle = (key: string) => {
packages.selectOptions = packages.categorys[key] packages.selectOptions = packages.categorys[key]
@@ -119,6 +137,7 @@ $menuWidth: 65px;
@include fetch-bg-color('background-color2-shallow'); @include fetch-bg-color('background-color2-shallow');
} }
.chart-content-list { .chart-content-list {
width: 200px;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -37,7 +37,8 @@
:title="item.title" :title="item.title"
@click="selectChartHandle(item)" @click="selectChartHandle(item)"
> >
<chart-glob-image class="list-item-img" :chartConfig="item"></chart-glob-image> <Icon v-if="item.icon" class="list-img" :icon="item.icon" color="#999" width="20" />
<chart-glob-image v-else class="list-item-img" :chartConfig="item" />
<n-text class="list-item-fs" depth="2">{{ item.title }}</n-text> <n-text class="list-item-fs" depth="2">{{ item.title }}</n-text>
</div> </div>
</n-scrollbar> </n-scrollbar>
@@ -70,7 +71,7 @@ import { ref, onUnmounted } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { createComponent } from '@/packages' import { createComponent } from '@/packages'
import { ConfigType, CreateComponentType } from '@/packages/index.d' import { ConfigType, CreateComponentType } from '@/packages/index.d'
import { themeColor, MenuOptionsType } from '../../hooks/useAside.hook' import { themeColor } from '../../hooks/useLayout.hook'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore' import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
@@ -78,6 +79,7 @@ import { isString, addEventListener, removeEventListener } from '@/utils'
import { fetchConfigComponent, fetchChartComponent } from '@/packages/index' import { fetchConfigComponent, fetchChartComponent } from '@/packages/index'
import { componentInstall, loadingStart, loadingFinish, loadingError } from '@/utils' import { componentInstall, loadingStart, loadingFinish, loadingError } from '@/utils'
import { ChartGlobImage } from '@/components/Pages/ChartGlobImage' import { ChartGlobImage } from '@/components/Pages/ChartGlobImage'
import { Icon } from '@iconify/vue'
const props = defineProps({ const props = defineProps({
menuOptions: { menuOptions: {
@@ -129,7 +131,9 @@ const searchHandle = (key: string | null) => {
} }
loading.value = true loading.value = true
showPopover.value = true showPopover.value = true
searchRes.value = List.filter((e: ConfigType) => !key || e.title.toLowerCase().includes(key.toLowerCase())) searchRes.value = List.filter(
(e: ConfigType) => !e.disabled && (!key || e.title.toLowerCase().includes(key.toLowerCase()))
)
setTimeout(() => { setTimeout(() => {
loading.value = undefined loading.value = undefined
}, 500) }, 500)
@@ -146,6 +150,7 @@ const listenerCloseHandle = (e: Event) => {
// 选择处理 // 选择处理
const selectChartHandle = async (item: ConfigType) => { const selectChartHandle = async (item: ConfigType) => {
if (item.disabled) return
try { try {
loadingStart() loadingStart()
// 动态注册图表组件 // 动态注册图表组件
@@ -153,6 +158,11 @@ const selectChartHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(item)) componentInstall(item.conKey, fetchConfigComponent(item))
// 创建新图表组件 // 创建新图表组件
let newComponent: CreateComponentType = await createComponent(item) let newComponent: CreateComponentType = await createComponent(item)
if (item.redirectComponent) {
item.dataset && (newComponent.option.dataset = item.dataset)
newComponent.chartConfig.title = item.title
newComponent.chartConfig.chartFrame = item.chartFrame
}
// 添加 // 添加
chartEditStore.addComponentList(newComponent, false, true) chartEditStore.addComponentList(newComponent, false, true)
// 选中 // 选中
@@ -221,10 +231,16 @@ $searchWidth: 176px;
font-size: 12px; font-size: 12px;
} }
&-img { &-img {
height: 28px; height: 20px;
max-width: 30px;
margin-right: 5px; margin-right: 5px;
border-radius: 5px; border-radius: 5px;
} }
& > svg {
min-width: 20px;
min-height: 20px;
margin-right: 5px;
}
&:hover { &:hover {
&::before { &::before {
content: ''; content: '';
@@ -1,92 +1,95 @@
import { shallowReactive, ref } from 'vue' import { ref, watch, computed } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { renderLang, renderIcon } from '@/utils' import { renderLang, renderIcon } from '@/utils'
import { themeColor, setItem, getCharts } from './useLayout.hook' import { themeColor, setItem, getCharts } from './useLayout.hook'
import { PackagesCategoryEnum, PackagesCategoryName, PackagesType } from '@/packages/index.d' import { PackagesCategoryEnum, PackagesCategoryName, ConfigType } from '@/packages/index.d'
// 图表 import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore' import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' // 图标
// 图标 const { AirPlaneOutlineIcon, ImageIcon, BarChartIcon } = icon.ionicons5
const { BarChartIcon } = icon.ionicons5 const { TableSplitIcon, RoadmapIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon
const {
TableSplitIcon, // 图表
RoadmapIcon, export type MenuOptionsType = {
SpellCheckIcon, key: string
GraphicalDataFlowIcon, icon: ReturnType<typeof renderIcon>
} = icon.carbon label: ReturnType<typeof renderLang>
list: ConfigType[]
}
// 图表
export type MenuOptionsType = { const packagesListObj = {
key: string [PackagesCategoryEnum.CHARTS]: {
icon: ReturnType<typeof renderIcon> icon: renderIcon(RoadmapIcon),
label: ReturnType<typeof renderLang> label: PackagesCategoryName.CHARTS
list: PackagesType },
} [PackagesCategoryEnum.INFORMATIONS]: {
icon: renderIcon(SpellCheckIcon),
const { getPackagesList } = usePackagesStore() label: PackagesCategoryName.INFORMATIONS
const menuOptions: MenuOptionsType[] = [] },
[PackagesCategoryEnum.TABLES]: {
const packagesListObj = { icon: renderIcon(TableSplitIcon),
[PackagesCategoryEnum.CHARTS]: { label: PackagesCategoryName.TABLES
icon: renderIcon(RoadmapIcon), },
label: PackagesCategoryName.CHARTS, [PackagesCategoryEnum.DECORATES]: {
}, icon: renderIcon(GraphicalDataFlowIcon),
[PackagesCategoryEnum.INFORMATIONS]: { label: PackagesCategoryName.DECORATES
icon: renderIcon(SpellCheckIcon), },
label: PackagesCategoryName.INFORMATIONS, [PackagesCategoryEnum.PHOTOS]: {
}, icon: renderIcon(ImageIcon),
[PackagesCategoryEnum.TABLES]: { label: PackagesCategoryName.PHOTOS
icon: renderIcon(TableSplitIcon), },
label: PackagesCategoryName.TABLES, [PackagesCategoryEnum.ICONS]: {
}, icon: renderIcon(AirPlaneOutlineIcon),
[PackagesCategoryEnum.DECORATES]: { label: PackagesCategoryName.ICONS
icon: renderIcon(GraphicalDataFlowIcon), }
label: PackagesCategoryName.DECORATES, }
},
} export const useAsideHook = () => {
const packagesStore = usePackagesStore()
// 处理列表 const menuOptions: MenuOptionsType[] = []
const handlePackagesList = () => {
for (const val in getPackagesList) { // 处理列表
menuOptions.push({ const handlePackagesList = () => {
key: val, for (const val in packagesStore.getPackagesList) {
// @ts-ignore menuOptions.push({
icon: packagesListObj[val].icon, key: val,
// @ts-ignore // @ts-ignore
label: packagesListObj[val].label, icon: packagesListObj[val].icon,
// @ts-ignore // @ts-ignore
list: getPackagesList[val], label: packagesListObj[val].label,
}) // @ts-ignore
} list: packagesStore.getPackagesList[val]
} })
handlePackagesList() }
}
// 记录选中值 handlePackagesList()
let beforeSelect: string = menuOptions[0]['key']
const selectValue = ref<string>(menuOptions[0]['key']) // 记录选中值
let beforeSelect: string = menuOptions[0]['key']
// 选中的对象值 const selectValue = ref<string>(menuOptions[0]['key'])
const selectOptions = ref(menuOptions[0])
// 选中的对象值
// 点击 item const selectOptions = ref(menuOptions[0])
const clickItemHandle = (key: string, item: any) => {
selectOptions.value = item // 点击 item
// 处理折叠 const clickItemHandle = (key: string, item: any) => {
if (beforeSelect === key) { selectOptions.value = item
setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false) // 处理折叠
} else { if (beforeSelect === key) {
setItem(ChartLayoutStoreEnum.CHARTS, true, false) setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false)
} } else {
beforeSelect = key setItem(ChartLayoutStoreEnum.CHARTS, true, false)
} }
beforeSelect = key
export { }
getCharts,
BarChartIcon, return {
themeColor, getCharts,
selectOptions, BarChartIcon,
selectValue, themeColor,
clickItemHandle, selectOptions,
menuOptions, selectValue,
} clickItemHandle,
menuOptions
}
}
+105 -123
View File
@@ -1,123 +1,105 @@
<template> <template>
<!-- 左侧所有组件的展示列表 --> <!-- 左侧所有组件的展示列表 -->
<content-box <content-box class="go-content-charts" :class="{ scoped: !getCharts }" title="组件" :depth="1" :backIcon="false">
class="go-content-charts" <template #icon>
:class="{ scoped: !getCharts }" <n-icon size="14" :depth="2">
title="组件" <bar-chart-icon></bar-chart-icon>
:depth="1" </n-icon>
:backIcon="false" </template>
>
<template #icon> <template #top-right>
<n-icon size="14" :depth="2"> <charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search>
<bar-chart-icon></bar-chart-icon> </template>
</n-icon> <!-- 图表 -->
</template> <aside>
<div class="menu-width-box">
<template #top-right> <n-menu
<charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search> class="menu-width"
</template> v-model:value="selectValue"
<!-- 图表 --> :options="menuOptions"
<aside> :icon-size="16"
<div class="menu-width-box"> :indent="18"
<n-menu @update:value="clickItemHandle"
class="menu-width" ></n-menu>
v-model:value="selectValue" <div class="menu-component-box">
:options="menuOptions" <go-skeleton :load="!selectOptions" round text :repeat="2" style="width: 90%"></go-skeleton>
:icon-size="16" <charts-option-content
:indent="18" v-if="selectOptions"
@update:value="clickItemHandle" :selectOptions="selectOptions"
></n-menu> :key="selectValue"
<div class="menu-component-box"> ></charts-option-content>
<go-skeleton </div>
:load="!selectOptions" </div>
round </aside>
text </content-box>
:repeat="2" </template>
style="width: 90%"
></go-skeleton> <script setup lang="ts">
<charts-option-content import { ContentBox } from '../ContentBox/index'
v-if="selectOptions" import { ChartsOptionContent } from './components/ChartsOptionContent'
:selectOptions="selectOptions" import { ChartsSearch } from './components/ChartsSearch'
:key="selectValue" import { useAsideHook } from './hooks/useAside.hook'
></charts-option-content>
</div> const { getCharts, BarChartIcon, themeColor, selectOptions, selectValue, clickItemHandle, menuOptions } = useAsideHook()
</div> </script>
</aside>
</content-box> <style lang="scss" scoped>
</template> /* 整体宽度 */
$width: 330px;
<script setup lang="ts"> /* 列表的宽度 */
import { ContentBox } from '../ContentBox/index' $widthScoped: 65px;
import { ChartsOptionContent } from './components/ChartsOptionContent' /* 此高度与 ContentBox 组件关联 */
import { ChartsSearch } from './components/ChartsSearch' $topHeight: 40px;
import {
getCharts, @include go(content-charts) {
BarChartIcon, width: $width;
themeColor, @extend .go-transition;
selectOptions, &.scoped,
selectValue, .menu-width {
clickItemHandle, width: $widthScoped;
menuOptions, }
} from './hooks/useAside.hook' .menu-width-box {
</script> display: flex;
height: calc(100vh - #{$--header-height} - #{$topHeight});
<style lang="scss" scoped> .menu-width {
/* 整体宽度 */ flex-shrink: 0;
$width: 330px; @include fetch-bg-color('background-color2');
/* 列表的宽度 */ }
$widthScoped: 65px; .menu-component-box {
/* 此高度与 ContentBox 组件关联 */ flex-shrink: 0;
$topHeight: 40px; width: $width - $widthScoped;
overflow: hidden;
@include go(content-charts) { }
width: $width; }
@extend .go-transition; @include deep() {
&.scoped, .menu-width {
.menu-width { .n-menu-item {
width: $widthScoped; height: auto !important;
} &.n-menu-item--selected {
.menu-width-box { &::after {
display: flex; content: '';
height: calc(100vh - #{$--header-height} - #{$topHeight}); position: absolute;
.menu-width { left: 2px;
flex-shrink: 0; top: 0;
@include fetch-bg-color('background-color2'); height: 100%;
} width: 3px;
.menu-component-box { background-color: v-bind('themeColor');
flex-shrink: 0; border-top-right-radius: 3px;
width: $width - $widthScoped; border-bottom-right-radius: 3px;
overflow: hidden; }
} }
} .n-menu-item-content {
@include deep() { display: flex;
.menu-width { flex-direction: column;
.n-menu-item { padding: 6px 12px !important;
height: auto !important; font-size: 14px !important;
&.n-menu-item--selected { }
&::after { .n-menu-item-content__icon {
content: ''; font-size: 18px !important;
position: absolute; margin-right: 0 !important;
left: 2px; }
top: 0; }
height: 100%; }
width: 3px; }
background-color: v-bind('themeColor'); }
border-top-right-radius: 3px; </style>
border-bottom-right-radius: 3px;
}
}
.n-menu-item-content {
display: flex;
flex-direction: column;
padding: 6px 12px !important;
font-size: 14px !important;
}
.n-menu-item-content__icon {
font-size: 18px !important;
margin-right: 0 !important;
}
}
}
}
}
</style>
@@ -58,7 +58,7 @@
<help-outline-icon></help-outline-icon> <help-outline-icon></help-outline-icon>
</n-icon> </n-icon>
</template> </template>
<n-text>不支持静态组件分组</n-text> <n-text>不支持静态组件</n-text>
</n-tooltip> </n-tooltip>
</template> </template>
<n-select <n-select
@@ -169,7 +169,27 @@ const fnDimensionsAndSource = (interactOn: InteractEventOn | undefined) => {
// 绑定组件列表 // 绑定组件列表
const fnEventsOptions = (): Array<SelectOption | SelectGroupOption> => { const fnEventsOptions = (): Array<SelectOption | SelectGroupOption> => {
const filterOptionList = chartEditStore.componentList.filter(item => { // 扁平化树形数据
const fnFlattern = (
data: Array<CreateComponentType | CreateComponentGroupType>
): Array<CreateComponentType | CreateComponentGroupType> => {
return data.reduce(
(
iter: Array<CreateComponentType | CreateComponentGroupType>,
val: CreateComponentType | CreateComponentGroupType
) => {
if (val.groupList && val.groupList.length > 0) {
iter.push(val)
} else {
iter.push(val)
}
return val.groupList ? [...iter, ...fnFlattern(val.groupList)] : iter
},
[]
)
}
const filterOptionList = fnFlattern(chartEditStore.componentList).filter(item => {
// 排除自己 // 排除自己
const isNotSelf = item.id !== targetData.value.id const isNotSelf = item.id !== targetData.value.id
// 排除静态组件 // 排除静态组件
@@ -29,9 +29,15 @@ export const dragHandle = async (e: DragEvent) => {
// 修改状态 // 修改状态
chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_CREATE, false) chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_CREATE, false)
const dropData: Exclude<ConfigType, ['image']> = JSONParse(drayDataString) const dropData: Exclude<ConfigType, ['image']> = JSONParse(drayDataString)
if (dropData.disabled) return
// 创建新图表组件 // 创建新图表组件
let newComponent: CreateComponentType = await createComponent(dropData) let newComponent: CreateComponentType = await createComponent(dropData)
if (dropData.redirectComponent) {
dropData.dataset && (newComponent.option.dataset = dropData.dataset)
newComponent.chartConfig.title = dropData.title
newComponent.chartConfig.chartFrame = dropData.chartFrame
}
setComponentPosition(newComponent, e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2) setComponentPosition(newComponent, e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2)
chartEditStore.addComponentList(newComponent, false, true) chartEditStore.addComponentList(newComponent, false, true)
@@ -1,159 +1,159 @@
<template> <template>
<div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }"> <div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }">
<div class="go-flex-center item-content"> <div class="go-flex-center item-content">
<n-image <n-image
class="list-img" class="list-img"
object-fit="contain" object-fit="contain"
preview-disabled preview-disabled
:src="imageInfo" :src="imageInfo"
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
></n-image> ></n-image>
<n-ellipsis style="margin-right: auto"> <n-ellipsis style="margin-right: auto">
<span class="list-text"> <span class="list-text">
{{ props.componentData.chartConfig.title }} {{ props.componentData.chartConfig.title }}
</span> </span>
</n-ellipsis> </n-ellipsis>
<layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status> <layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status>
</div> </div>
<div :class="{ 'select-modal': select }"></div> <div :class="{ 'select-modal': select }"></div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, PropType, ref, watch } from 'vue' import { computed, PropType, ref } from 'vue'
import { requireErrorImg } from '@/utils' import { requireErrorImg } from '@/utils'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { fetchImages } from '@/packages' import { fetchImages } from '@/packages'
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { LayersStatus } from '../LayersStatus/index' import { LayersStatus } from '../LayersStatus/index'
const props = defineProps({ const props = defineProps({
componentData: { componentData: {
type: Object as PropType<CreateComponentType | CreateComponentGroupType>, type: Object as PropType<CreateComponentType | CreateComponentGroupType>,
required: true required: true
}, },
isGroup: { isGroup: {
type: Boolean, type: Boolean,
default: false default: false
}, },
layerMode: { layerMode: {
type: String as PropType<LayerModeEnum>, type: String as PropType<LayerModeEnum>,
default: LayerModeEnum.THUMBNAIL default: LayerModeEnum.THUMBNAIL
} }
}) })
// 全局颜色 // 全局颜色
const designStore = useDesignStore() const designStore = useDesignStore()
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const imageInfo = ref('') const imageInfo = ref('')
// 获取图片 // 获取图片
const fetchImageUrl = async () => { const fetchImageUrl = async () => {
imageInfo.value = await fetchImages(props.componentData.chartConfig) imageInfo.value = await fetchImages(props.componentData.chartConfig)
} }
fetchImageUrl() fetchImageUrl()
// 颜色 // 颜色
const themeColor = computed(() => { const themeColor = computed(() => {
return designStore.getAppTheme return designStore.getAppTheme
}) })
// 计算当前选中目标 // 计算当前选中目标
const select = computed(() => { const select = computed(() => {
const id = props.componentData.id const id = props.componentData.id
return chartEditStore.getTargetChart.selectId.find((e: string) => e === id) return chartEditStore.getTargetChart.selectId.find((e: string) => e === id)
}) })
// 悬浮对象 // 悬浮对象
const hover = computed(() => { const hover = computed(() => {
return props.componentData.id === chartEditStore.getTargetChart.hoverId return props.componentData.id === chartEditStore.getTargetChart.hoverId
}) })
// 组件状态 隐藏/锁定 // 组件状态 隐藏/锁定
const status = computed(() => { const status = computed(() => {
return props.componentData.status return props.componentData.status
}) })
// 是否选中文本 // 是否选中文本
const selectText = computed(() => { const selectText = computed(() => {
return props.layerMode === LayerModeEnum.TEXT return props.layerMode === LayerModeEnum.TEXT
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$centerHeight: 52px; $centerHeight: 52px;
$centerMiniHeight: 28px; $centerMiniHeight: 28px;
$textSize: 10px; $textSize: 10px;
@include go(content-layers-list-item) { @include go(content-layers-list-item) {
position: relative; position: relative;
height: $centerHeight; height: $centerHeight;
width: 90%; width: 90%;
margin: 5px 5%; margin: 5px 5%;
margin-bottom: 5px; margin-bottom: 5px;
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0); border: 1px solid rgba(0, 0, 0, 0);
@extend .go-transition-quick; @extend .go-transition-quick;
&.hover, &.hover,
&:hover { &:hover {
@include fetch-bg-color('background-color4'); @include fetch-bg-color('background-color4');
} }
&:hover { &:hover {
@include deep() { @include deep() {
.icon-item { .icon-item {
opacity: 1; opacity: 1;
} }
} }
} }
.select-modal, .select-modal,
.item-content { .item-content {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
} }
.item-content { .item-content {
z-index: 1; z-index: 1;
padding: 6px 5px; padding: 6px 5px;
justify-content: start !important; justify-content: start !important;
width: calc(100% - 10px); width: calc(100% - 10px);
height: calc(100% - 10px); height: calc(100% - 10px);
} }
.select-modal { .select-modal {
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: 0.3; opacity: 0.3;
background-color: v-bind('themeColor'); background-color: v-bind('themeColor');
} }
.list-img { .list-img {
flex-shrink: 0; flex-shrink: 0;
height: $centerHeight; height: $centerHeight;
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
border: none !important; border: none !important;
padding: 2px; padding: 2px;
@include hover-border-color('hover-border-color'); @include hover-border-color('hover-border-color');
} }
.list-text { .list-text {
padding-left: 6px; padding-left: 6px;
font-size: $textSize; font-size: $textSize;
} }
/* 选中样式 */ /* 选中样式 */
&.select { &.select {
border: 1px solid v-bind('themeColor'); border: 1px solid v-bind('themeColor');
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
} }
// mini样式 // mini样式
&.list-mini { &.list-mini {
height: $centerMiniHeight; height: $centerMiniHeight;
} }
} }
</style> </style>
+6 -1
View File
@@ -132,6 +132,11 @@ export const useSync = () => {
) => { ) => {
// 补充 class 上的方法 // 补充 class 上的方法
let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig) let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig)
if (_componentInstance.chartConfig.redirectComponent) {
_componentInstance.chartConfig.dataset && (newComponent.option.dataset = _componentInstance.chartConfig.dataset)
newComponent.chartConfig.title = _componentInstance.chartConfig.title
newComponent.chartConfig.chartFrame = _componentInstance.chartConfig.chartFrame
}
if (callBack) { if (callBack) {
if (changeId) { if (changeId) {
callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() })) callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() }))
@@ -156,7 +161,7 @@ export const useSync = () => {
// 组件 // 组件
if (key === ChartEditStoreEnum.COMPONENT_LIST) { if (key === ChartEditStoreEnum.COMPONENT_LIST) {
let loadIndex = 0 let loadIndex = 0
const listLength = projectData[key].length; const listLength = projectData[key].length
for (const comItem of projectData[key]) { for (const comItem of projectData[key]) {
// 设置加载数量 // 设置加载数量
let percentage = parseInt((parseFloat(`${++loadIndex / listLength}`) * 100).toString()) let percentage = parseInt((parseFloat(`${++loadIndex / listLength}`) * 100).toString())