Merge branch 'dev'

This commit is contained in:
奔跑的面条 2023-05-28 17:20:36 +08:00
commit 6afbe59828
62 changed files with 2652 additions and 1103 deletions

View File

@ -129,7 +129,7 @@ Cloud IDE 代码在线预览地址:[https://idegitee.com/dromara/go-view](http
| 名称 | 版本 | 名称 | 版本 |
| ------------------- | ----- | ----------- | ------ |
| 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 |
| 详见 `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 |
已完成图表:

View File

@ -33,6 +33,7 @@
"highlight.js": "^11.5.0",
"html2canvas": "^1.4.1",
"keymaster": "^1.6.2",
"mitt": "^3.0.0",
"monaco-editor": "^0.33.0",
"naive-ui": "2.34.3",
"pinia": "^2.0.13",
@ -49,14 +50,16 @@
"devDependencies": {
"@commitlint/cli": "^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/three": "^0.144.0",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"@vicons/carbon": "^0.12.0",
"@vicons/ionicons5": "~0.11.0",
"@vitejs/plugin-vue": "^1.10.2",
"@vitejs/plugin-vue-jsx": "^1.3.9",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/compiler-sfc": "^3.2.31",
"@vueuse/core": "^7.7.1",
"commitlint": "^17.0.2",
@ -75,7 +78,7 @@
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"typescript": "4.6.3",
"vite": "4.2.1",
"vite": "4.3.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-importer": "^0.2.5",
"vite-plugin-mock": "^2.9.6",

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

View File

@ -257,9 +257,36 @@
<n-switch v-model:value="legend.show" size="small"></n-switch>
</template>
<setting-item-box name="图例文字">
<setting-item>
<setting-item name="颜色">
<n-color-picker size="small" v-model:value="legend.textStyle.color"></n-color-picker>
</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>
</collapse-item>
@ -309,9 +336,9 @@
</template>
<script setup lang="ts">
import { PropType, computed } from 'vue'
import { PropType, computed, watch } from 'vue'
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 { icon } from '@/plugins'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@ -360,4 +387,14 @@ const grid = computed(() => {
const visualMap = computed(() => {
return props.optionData.visualMap
})
// legend colortype = scroll
watch(() => legend.value && legend.value.textStyle.color, (newVal) => {
if (legend.value && newVal) {
legend.value.pageTextStyle.color = newVal
}
}, {
immediate: true,
deep: true,
})
</script>

View File

@ -1,13 +1,13 @@
<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 back" :data-back="backTextFromData"></div>
</div>
</template>
<script lang="ts" setup>
import { ref, PropType, watch } from 'vue'
import { FlipType } from './index'
import { ref, PropType, watch, nextTick } from 'vue'
import { FlipType } from './index'
const props = defineProps({
flipType: {
@ -43,6 +43,10 @@ const props = defineProps({
backColor: {
type: String,
default: '#000000'
},
borderWidth: {
type: Number,
default: 2
}
})
@ -50,19 +54,27 @@ const isFlipping = ref(false)
const frontTextFromData = 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
frontTextFromData.value = front
// true
isFlipping.value = true
//
setTimeout(() => {
timeoutID = setTimeout(() => {
isFlipping.value = false // false
frontTextFromData.value = back
}, props.duration)
@ -86,6 +98,7 @@ $radius: v-bind('`${props.radius}px`');
$width: v-bind('`${props.width}px`');
$height: v-bind('`${props.height}px`');
$perspective: v-bind('`${props.height * 2}px`');
$borderWidth: v-bind('`${props.borderWidth * 2}px`');
$speed: v-bind('`${props.duration / 1000}s`');
$shadowColor: #000000;
$lineColor: #4a9ef8;
@ -125,13 +138,12 @@ $lineColor: #4a9ef8;
}
// #endregion
.go-Flipper {
.go-flipper {
display: inline-block;
position: relative;
width: $width;
height: $height;
line-height: $height;
border: solid 1px $backColor;
border-radius: $radius;
background: $frontColor;
font-size: $width;
@ -139,6 +151,17 @@ $lineColor: #4a9ef8;
box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); //
text-align: center;
// 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:after {
@ -186,11 +209,13 @@ $lineColor: #4a9ef8;
&.down.go .front:before {
transform-origin: 50% 100%;
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;
}
&.down.go .back:after {
animation: backFlipDown $speed ease-in-out both;
box-shadow: 0 2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
/*向上翻*/
&.up .front:after {
@ -208,11 +233,13 @@ $lineColor: #4a9ef8;
&.up.go .front:after {
transform-origin: 50% 0;
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;
}
&.up.go .back:before {
animation: backFlipUp $speed ease-in-out both;
box-shadow: 0 -2px $borderWidth 0 $frontColor;
backface-visibility: hidden;
}
}
</style>

View File

@ -12,5 +12,7 @@ export enum StorageEnum {
// 工作台布局配置
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'
}

View File

@ -1,3 +1,4 @@
export * from './axis'
export * from './line'
export * from './label'
export * from './label'
export * from './legend'

View File

@ -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'
}
]
}

View File

@ -18,7 +18,14 @@ export const PieTypeObject = {
[PieTypeEnum.ROSE]: 'rose'
}
// 其它配置
const otherConfig = {
// 轮播动画
isCarousel: false,
}
const option = {
...otherConfig,
type: 'ring',
tooltip: {
show: true,

View File

@ -1,88 +1,99 @@
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem name="饼图配置" :expanded="true">
<SettingItemBox name="类型">
<SettingItem>
<n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标签">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch>
<n-text>展示标签</n-text>
</n-space>
</SettingItem>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch>
<n-text>引导线</n-text>
</n-space>
</setting-item>
<SettingItem name="位置">
<n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" />
</SettingItem>
<setting-item name="展示类型">
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</setting-item>
</SettingItemBox>
<setting-item-box name="圆角">
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
<n-text>圆角大小</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
<n-text>线条宽度</n-text>
</n-space>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { PieTypeObject, PieTypeEnum } from './config'
import { labelConfig } from '@/packages/chartConfiguration/echarts'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
}
})
const fontWeightOptions = [
{
label: PieTypeEnum.NORMAL,
value: PieTypeObject[PieTypeEnum.NORMAL]
},
{
label: PieTypeEnum.RING,
value: PieTypeObject[PieTypeEnum.RING]
},
{
label: PieTypeEnum.ROSE,
value: PieTypeObject[PieTypeEnum.ROSE]
}
]
const labelFormatterOptions = [
{ label: '数据名', value: '{b}' },
{ label: '百分比', value: '{d}' },
{ label: '列名:百分比', value: '{b}:{d}%' }
]
</script>
<template>
<!-- Echarts 全局设置 -->
<global-setting :optionData="optionData"></global-setting>
<CollapseItem name="饼图配置" :expanded="true">
<SettingItemBox name="类型">
<SettingItem>
<n-select v-model:value="optionData.type" size="small" :options="fontWeightOptions" />
</SettingItem>
</SettingItemBox>
<SettingItemBox name="动画" :alone="true">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
<n-text>开启<n-text :depth="3">将自动隐藏图例</n-text></n-text>
</n-space>
</SettingItem>
<SettingItem>
<n-text :depth="3">无鼠标点击图例场景时可强行打开图例</n-text>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标签">
<SettingItem>
<n-space>
<n-switch v-model:value="optionData.series[0].label.show" size="small"></n-switch>
<n-text>展示标签</n-text>
</n-space>
</SettingItem>
<setting-item>
<n-space>
<n-switch v-model:value="optionData.series[0].labelLine.show" size="small"></n-switch>
<n-text>引导线</n-text>
</n-space>
</setting-item>
<SettingItem name="位置">
<n-select v-model:value="optionData.series[0].label.position" size="small" :options="labelConfig.position" />
</SettingItem>
<setting-item name="展示类型">
<n-select v-model:value="optionData.series[0].label.formatter" size="small" :options="labelFormatterOptions" />
</setting-item>
</SettingItemBox>
<setting-item-box name="圆角">
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderRadius"
size="small"
:min="0"
></n-input-number>
<n-text>圆角大小</n-text>
</n-space>
</setting-item>
<setting-item>
<n-space>
<n-input-number
v-model:value="optionData.series[0].itemStyle.borderWidth"
size="small"
:min="0"
></n-input-number>
<n-text>线条宽度</n-text>
</n-space>
</setting-item>
</setting-item-box>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, watch } from 'vue'
import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import { PieTypeObject, PieTypeEnum } from './config'
import { labelConfig } from '@/packages/chartConfiguration/echarts'
const props = defineProps({
optionData: {
type: Object as PropType<GlobalThemeJsonType>,
required: true
}
})
const fontWeightOptions = [
{
label: PieTypeEnum.NORMAL,
value: PieTypeObject[PieTypeEnum.NORMAL]
},
{
label: PieTypeEnum.RING,
value: PieTypeObject[PieTypeEnum.RING]
},
{
label: PieTypeEnum.ROSE,
value: PieTypeObject[PieTypeEnum.ROSE]
}
]
const labelFormatterOptions = [
{ label: '数据名', value: '{b}' },
{ label: '百分比', value: '{d}' },
{ label: '列名:百分比', value: '{b}:{d}%' }
]
</script>

View File

@ -1,64 +1,146 @@
<template>
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="option" :manual-update="isPreview()" autoresize></v-chart>
</template>
<script setup lang="ts">
import { computed, PropType, reactive, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent])
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
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 }
)
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
</script>
<template>
<v-chart
ref="vChartRef"
autoresize
:init-options="initOptions"
:theme="themeColor"
:option="option"
:manual-update="isPreview()"
@mouseover="handleHighlight"
@mouseout="handleDownplay"
></v-chart>
</template>
<script setup lang="ts">
import { computed, PropType, onMounted, watch } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { isPreview } from '@/utils'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import dataJson from './data.json'
const props = defineProps({
themeSetting: {
type: Object,
required: true
},
themeColor: {
type: Object,
required: true
},
chartConfig: {
type: Object as PropType<config>,
required: true
}
})
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
let seriesDataNum = -1
let seriesDataMaxLength = 0
let intervalInstance: any = null
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent])
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
//
const handleSeriesData = () => {
if (seriesDataNum > -1) {
vChartRef.value?.dispatchAction({
type: 'downplay',
dataIndex: seriesDataNum
})
}
seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
vChartRef.value?.dispatchAction({
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>

View File

@ -16,6 +16,7 @@ export interface OptionType {
flipperGap: number
flipperType: FlipType
flipperSpeed: number
flipperBorderWidth: number
}
export const option: OptionType = {
@ -28,7 +29,8 @@ export const option: OptionType = {
flipperRadius: 5,
flipperGap: 10,
flipperType: 'down',
flipperSpeed: 450
flipperSpeed: 450,
flipperBorderWidth: 0,
}
export default class Config extends PublicConfigClass implements CreateComponentType {

View File

@ -16,12 +16,16 @@
<setting-item name="高度">
<n-input-number v-model:value="optionData.flipperHeight" size="small" :min="1"></n-input-number>
</setting-item>
<setting-item name="间隔">
<n-input-number v-model:value="optionData.flipperGap" size="small" :min="0"></n-input-number>
<setting-item name="边框">
<n-input-number v-model:value="optionData.flipperBorderWidth" size="small" :min="0" :max="10"></n-input-number>
</setting-item>
<setting-item name="圆角">
<n-input-number v-model:value="optionData.flipperRadius" size="small" :min="0"></n-input-number>
</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="背景色">
<n-color-picker
size="small"

View File

@ -9,6 +9,7 @@
:radius="flipperRadius"
:flip-type="flipperType"
:duration="flipperSpeed"
:border-width="flipperBorderWidth"
v-for="(item, index) in flipperData"
:key="index"
class="go-d-block"
@ -42,7 +43,8 @@ const {
flipperRadius,
flipperGap,
flipperType,
flipperSpeed
flipperSpeed,
flipperBorderWidth
} = toRefs(props.chartConfig.option as OptionType)
const flipperData = ref<string[] | number[]>([])

View File

@ -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)
}

View File

@ -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>

View File

@ -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'
}

View File

@ -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>

View File

@ -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)
}

View File

@ -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>

View File

@ -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'
}

View File

@ -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>

View File

@ -3,5 +3,7 @@ import { TimeCommonConfig } from './TimeCommon/index'
import { ClockConfig } from './Clock/index'
import { CountDownConfig } from './CountDown/index'
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]

View File

@ -1,4 +1,3 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import {
ArcCurve,

View File

@ -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

View File

@ -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)
}

View File

@ -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>

View File

@ -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'
}

View File

@ -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>

View File

@ -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

View File

@ -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

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 = '默认'
}

View File

@ -0,0 +1,5 @@
import MaterialLine from './MaterialLine'
import Common from './Common'
import Weather from './Weather'
export const IconList = [...MaterialLine, ...Weather, ...Common]

View File

@ -12,7 +12,9 @@ export const option = {
[COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE,
// 下拉展示
isPanel: 0,
dataset: dayjs().valueOf()
dataset: dayjs().valueOf(),
differValue: 0
}
export default class Config extends PublicConfigClass implements CreateComponentType {

View File

@ -1,31 +1,36 @@
<template>
<collapse-item name="展示方式" :expanded="true">
<setting-item-box name="选择方式">
<n-select
v-model:value="optionData.isPanel"
size="small"
:options="panelOptions"
/>
<n-select v-model:value="optionData.isPanel" size="small" :options="panelOptions" />
</setting-item-box>
</collapse-item>
<collapse-item name="时间配置" :expanded="true">
<setting-item-box name="基础">
<setting-item name="类型">
<n-select
v-model:value="optionData.componentInteractEventKey"
size="small"
:options="datePickerTypeOptions"
/>
<n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions" />
</setting-item>
</setting-item-box>
<setting-item-box name="默认值" :alone="true">
<n-date-picker
size="small"
v-model:value="optionData.dataset"
:type="optionData.componentInteractEventKey"
/>
<n-date-picker size="small" v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" />
</setting-item-box>
<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>
</collapse-item>
</template>

View File

@ -39,9 +39,9 @@ const onChange = (v: number | number[]) => {
props.chartConfig,
useChartEditStore,
{
[ComponentInteractParamsEnum.DATE_START]: v[0] | dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_END]: v[1] | dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_RANGE]: `${v[0]}-${v[1]}`
[ComponentInteractParamsEnum.DATE_START]: v[0] || dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_END]: v[1] || dayjs().valueOf(),
[ComponentInteractParamsEnum.DATE_RANGE]: `${v[0] || dayjs().valueOf()}-${v[1] || dayjs().valueOf()}`
},
InteractEventOn.CHANGE
)
@ -50,13 +50,12 @@ const onChange = (v: number | number[]) => {
useChartInteract(
props.chartConfig,
useChartEditStore,
{ [ComponentInteractParamsEnum.DATE]: v },
{ [ComponentInteractParamsEnum.DATE]: v || dayjs().valueOf() },
InteractEventOn.CHANGE
)
}
}
//
watch(
() => props.chartConfig.option.dataset,
(newData: number | number[]) => {
@ -68,6 +67,25 @@ watch(
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>
<style lang="scss" scoped>

View File

@ -7,7 +7,8 @@
:fallback-src="requireErrorImg()"
:width="w"
:height="h"
></n-image>
lazy
/>
</div>
</template>

View File

@ -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]

View File

@ -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

View File

@ -0,0 +1,9 @@
export enum ChatCategoryEnum {
PRIVATE = 'Private',
SHARE = 'Share'
}
export enum ChatCategoryEnumName {
PRIVATE = '私有图',
SHARE = '共享图'
}

View File

@ -0,0 +1,4 @@
import Private from './Private'
import Share from './Share'
export const PhotoList = [...Private, ...Share]

View File

@ -15,15 +15,34 @@ export enum ChartFrameEnum {
// 组件配置
export type ConfigType = {
// 组件 key
key: string
// 画布组件 key
chartKey: string
// 右侧设置面板组件 key
conKey: string
// 标题
title: string
// 分类
category: string
// 分类名称
categoryName: string
// 所属包
package: string
// 归类
chartFrame?: ChartFrameEnum
// 预览图
image: string
// 从指定路径创建创建该组件
redirectComponent?: string
// 组件预设的 dataset 值(图片/图标)
dataset?: any
// 禁用 拖拽或双击生成组件
disabled?: boolean
// 图标
icon?: string
// 事件
configEvents?: { [T: string]: Function }
}
// 数据请求
@ -120,7 +139,7 @@ export interface PublicConfigType {
}
filter?: string
status: StatusType
interactActions?: InteractActionsType[],
interactActions?: InteractActionsType[]
events: {
baseEvent: {
[K in BaseEvent]?: string
@ -140,6 +159,7 @@ export interface CreateComponentType extends PublicConfigType, requestConfig {
key: string
chartConfig: ConfigType
option: GlobalThemeJsonType
groupList?: Array<CreateComponentType>
}
// 组件成组实例类
@ -155,6 +175,8 @@ export enum PackagesCategoryEnum {
CHARTS = 'Charts',
TABLES = 'Tables',
INFORMATIONS = 'Informations',
PHOTOS = 'Photos',
ICONS = 'Icons',
DECORATES = 'Decorates'
}
@ -163,6 +185,8 @@ export enum PackagesCategoryName {
CHARTS = '图表',
TABLES = '列表',
INFORMATIONS = '信息',
PHOTOS = '图片',
ICONS = '图标',
DECORATES = '小组件'
}
@ -177,5 +201,7 @@ export type PackagesType = {
[PackagesCategoryEnum.CHARTS]: ConfigType[]
[PackagesCategoryEnum.INFORMATIONS]: ConfigType[]
[PackagesCategoryEnum.TABLES]: ConfigType[]
[PackagesCategoryEnum.PHOTOS]: ConfigType[]
[PackagesCategoryEnum.ICONS]: ConfigType[]
[PackagesCategoryEnum.DECORATES]: ConfigType[]
}

View File

@ -1,87 +1,99 @@
import { ChartList } from '@/packages/components/Charts/index'
import { DecorateList } from '@/packages/components/Decorates/index'
import { InformationList } from '@/packages/components/Informations/index'
import { TableList } from '@/packages/components/Tables/index'
import { PackagesCategoryEnum, PackagesType, ConfigType, FetchComFlagType } from '@/packages/index.d'
const configModules: Record<string, { default: string }> = import.meta.glob('./components/**/config.vue', {
eager: true
})
const indexModules: Record<string, { default: string }> = import.meta.glob('./components/**/index.vue', {
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,
[PackagesCategoryEnum.TABLES]: TableList,
[PackagesCategoryEnum.DECORATES]: DecorateList
}
/**
* *
* @param targetData
*/
export const createComponent = async (targetData: ConfigType) => {
const { category, key } = targetData
const chart = await import(`./components/${targetData.package}/${category}/${key}/config.ts`)
return new chart.default()
}
/**
* *
* @param {string} chartName
* @param {FetchComFlagType} flag 0, 1
*/
const fetchComponent = (chartName: string, flag: FetchComFlagType) => {
const module = flag === FetchComFlagType.VIEW ? indexModules : configModules
for (const key in module) {
const urlSplit = key.split('/')
if (urlSplit[urlSplit.length - 2] === chartName) {
return module[key]
}
}
}
/**
* *
* @param {ConfigType} dropData
*/
export const fetchChartComponent = (dropData: ConfigType) => {
const { key } = dropData
return fetchComponent(key, FetchComFlagType.VIEW)?.default
}
/**
* *
* @param {ConfigType} dropData
*/
export const fetchConfigComponent = (dropData: ConfigType) => {
const { key } = dropData
return fetchComponent(key, FetchComFlagType.CONFIG)?.default
}
/**
* *
* @param {ConfigType} targetData
*/
export const fetchImages = async (targetData?: ConfigType) => {
if (!targetData) 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 ''
}
import { ChartList } from '@/packages/components/Charts/index'
import { DecorateList } from '@/packages/components/Decorates/index'
import { InformationList } from '@/packages/components/Informations/index'
import { TableList } from '@/packages/components/Tables/index'
import { PhotoList } from '@/packages/components/Photos/index'
import { IconList } from '@/packages/components/Icons/index'
import { PackagesCategoryEnum, PackagesType, ConfigType, FetchComFlagType } from '@/packages/index.d'
const configModules: Record<string, { default: string }> = import.meta.glob('./components/**/config.vue', {
eager: true
})
const indexModules: Record<string, { default: string }> = import.meta.glob('./components/**/index.vue', {
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,
[PackagesCategoryEnum.TABLES]: TableList,
[PackagesCategoryEnum.DECORATES]: DecorateList,
[PackagesCategoryEnum.PHOTOS]: PhotoList,
[PackagesCategoryEnum.ICONS]: IconList
}
/**
* *
* @param targetData
*/
export const createComponent = async (targetData: ConfigType) => {
const { redirectComponent, category, key } = targetData
// redirectComponent 是给图片组件库和图标组件库使用的
if (redirectComponent) {
const [packageName, categoryName, keyName] = redirectComponent.split('/')
const redirectChart = await import(`./components/${packageName}/${categoryName}/${keyName}/config.ts`)
return new redirectChart.default()
}
const chart = await import(`./components/${targetData.package}/${category}/${key}/config.ts`)
return new chart.default()
}
/**
* *
* @param {string} chartName
* @param {FetchComFlagType} flag 0, 1
*/
const fetchComponent = (chartName: string, flag: FetchComFlagType) => {
const module = flag === FetchComFlagType.VIEW ? indexModules : configModules
for (const key in module) {
const urlSplit = key.split('/')
if (urlSplit[urlSplit.length - 2] === chartName) {
return module[key]
}
}
}
/**
* *
* @param {ConfigType} dropData
*/
export const fetchChartComponent = (dropData: ConfigType) => {
const { key } = dropData
return fetchComponent(key, FetchComFlagType.VIEW)?.default
}
/**
* *
* @param {ConfigType} dropData
*/
export const fetchConfigComponent = (dropData: ConfigType) => {
const { key } = dropData
return fetchComponent(key, FetchComFlagType.CONFIG)?.default
}
/**
* *
* @param {ConfigType} targetData
*/
export const fetchImages = async (targetData?: ConfigType) => {
if (!targetData) return ''
// 正则判断图片是否为 url是则直接返回该 url
if (/^(http|https):\/\/([\w.]+\/?)\S*/.test(targetData.image)) return targetData.image
// 新数据动态处理
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 ''
}

View File

@ -61,13 +61,14 @@ import {
Pulse as PulseIcon,
Folder as FolderIcon,
FolderOpen as FolderOpenIcon,
Image as ImageIcon,
ImageOutline as ImageIcon,
Images as ImagesIcon,
List as ListIcon,
EyeOutline as EyeOutlineIcon,
EyeOffOutline as EyeOffOutlineIcon,
Albums as AlbumsIcon,
Analytics as AnalyticsIcon
Analytics as AnalyticsIcon,
AirplaneOutline as AirPlaneOutlineIcon
} from '@vicons/ionicons5'
import {
@ -238,10 +239,12 @@ const ionicons5 = {
// 眼睛
EyeOutlineIcon,
EyeOffOutlineIcon,
// 图表列表
// 图表列表
AlbumsIcon,
// 分析
AnalyticsIcon
AnalyticsIcon,
// 飞机
AirPlaneOutlineIcon
}
const carbon = {

View File

@ -86,8 +86,18 @@
},
"legend": {
"show": true,
"top": "5%",
"type": "scroll",
"x": "center",
"y": "top",
"icon": "circle",
"orient": "horizontal",
"textStyle": {
"color": "#B9B8CE",
"fontSize": 18
},
"itemHeight": 15,
"itemWidth": 15,
"pageTextStyle": {
"color": "#B9B8CE"
}
},

View File

@ -2212,9 +2212,9 @@
"pinyin": "tengluozi"
},
{
"CMYK": [57, 62, 16, 2],
"RGB": [128, 109, 158],
"hex": "#806d9e",
"CMYK": [34, 39, 0, 24],
"RGB": [128, 118, 195],
"hex": "#8076c3",
"name": "槿紫",
"pinyin": "jinzi"
},

View File

@ -1,8 +1,9 @@
import { PackagesType, ConfigType } from '@/packages/index.d'
export { ConfigType }
export { PackagesType }
export interface PackagesStoreType {
packagesList: PackagesType
}
import { PackagesType, ConfigType } from '@/packages/index.d'
export { ConfigType }
export { PackagesType }
export interface PackagesStoreType {
packagesList: PackagesType,
newPhoto?: ConfigType
}

View File

@ -1,16 +1,32 @@
import { defineStore } from 'pinia'
import { PackagesStoreType, PackagesType } from './packagesStore.d'
import { packagesList } from '@/packages/index'
// 组件 pakages
export const usePackagesStore = defineStore({
id: 'usePackagesStore',
state: (): PackagesStoreType => ({
packagesList: Object.freeze(packagesList)
}),
getters: {
getPackagesList(): PackagesType {
return this.packagesList
}
}
})
import { defineStore } from 'pinia'
import { ConfigType, PackagesStoreType, PackagesType } from './packagesStore.d'
import { packagesList } from '@/packages/index'
import { StorageEnum } from '@/enums/storageEnum'
import { getLocalStorage, setLocalStorage } from '@/utils'
// 组件 packages
export const usePackagesStore = defineStore({
id: 'usePackagesStore',
state: (): PackagesStoreType => ({
packagesList: Object.freeze(packagesList),
newPhoto: undefined
}),
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)
}
}
})

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
* @param data
@ -329,12 +334,12 @@ export const JSONParse = (data: string) => {
}
// 还原函数值
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) {
const baseLeftIndex = v.indexOf('(')
if (baseLeftIndex > -1) {
const newFn = `function ${v.substring(baseLeftIndex)}`
return eval(`(function(){return ${newFn}})()`)
return evalFn(`(function(){return ${newFn}})()`)
}
}
return v

View File

@ -9,11 +9,12 @@
<div
class="item-box"
v-for="(item, index) in menuOptions"
:key="index"
:key="item.title"
draggable
@dragstart="dragStartHandle($event, item)"
@dragend="dragendHandle"
@dragstart="!item.disabled && dragStartHandle($event, item)"
@dragend="!item.disabled && dragendHandle"
@dblclick="dblclickHandle(item)"
@click="clickHandle(item)"
>
<div class="list-header">
<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-text>
</div>
<div class="list-center go-flex-center go-transition">
<chart-glob-image class="list-img" :chartConfig="item"></chart-glob-image>
<div class="list-center go-flex-center go-transition" draggable="true">
<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 class="list-bottom">
<n-text class="list-bottom-text" depth="3">
<n-ellipsis style="max-width: 90%">{{ item.title }}</n-ellipsis>
</n-text>
</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>
@ -42,17 +57,23 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { ChartModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
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 { 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 { Icon } from '@iconify/vue'
import { icon } from '@/plugins'
import omit from 'lodash/omit'
const chartEditStore = useChartEditStore()
const { TrashIcon } = icon.ionicons5
defineProps({
const emit = defineEmits(['deletePhoto'])
const props = defineProps({
menuOptions: {
type: Array as PropType<ConfigType[]>,
default: () => []
@ -62,6 +83,11 @@ defineProps({
const chartLayoutStore = useChartLayoutStore()
const contentChartsItemBoxRef = ref()
//
const isShowTools = (item: ConfigType) => {
return !item.disabled && item.package === PackagesCategoryEnum.PHOTOS && item.category === ChatCategoryEnum.PRIVATE
}
//
const chartMode: Ref<ChartModeEnum> = computed(() => {
return chartLayoutStore.getChartType
@ -69,6 +95,7 @@ const chartMode: Ref<ChartModeEnum> = computed(() => {
//
const dragStartHandle = (e: DragEvent, item: ConfigType) => {
if (item.disabled) return
//
componentInstall(item.chartKey, fetchChartComponent(item))
componentInstall(item.conKey, fetchConfigComponent(item))
@ -85,6 +112,7 @@ const dragendHandle = () => {
//
const dblclickHandle = async (item: ConfigType) => {
if (item.disabled) return
try {
loadingStart()
//
@ -92,6 +120,11 @@ const dblclickHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(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)
//
@ -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(
() => chartMode.value,
(newValue: ChartModeEnum) => {
@ -135,6 +185,7 @@ $halfCenterHeight: 50px;
gap: 9px;
transition: all 0.7s linear;
.item-box {
position: relative;
margin: 0;
width: $itemWidth;
overflow: hidden;
@ -145,7 +196,10 @@ $halfCenterHeight: 50px;
&:hover {
@include hover-border-color('background-color4');
.list-img {
transform: scale(1.1);
transform: scale(1.08);
}
.list-tools {
opacity: 1;
}
}
.list-header {
@ -157,6 +211,7 @@ $halfCenterHeight: 50px;
&-text {
font-size: 12px;
margin-left: 8px;
user-select: none;
}
}
.list-center {
@ -165,7 +220,7 @@ $halfCenterHeight: 50px;
overflow: hidden;
.list-img {
height: 100px;
width: 140px;
max-width: 140px;
border-radius: 6px;
@extend .go-transition;
}
@ -177,6 +232,33 @@ $halfCenterHeight: 50px;
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 {
.item-box {
@ -196,6 +278,9 @@ $halfCenterHeight: 50px;
.item-box {
width: $halfItemWidth;
max-width: $maxItemWidth;
.list-img {
max-width: 76px;
}
}
.list-center {
height: $halfCenterHeight;

View File

@ -11,8 +11,8 @@
@update:value="clickItemHandle"
></n-menu>
<div class="chart-content-list">
<n-scrollbar>
<charts-item-box :menuOptions="packages.selectOptions"></charts-item-box>
<n-scrollbar trigger="none">
<charts-item-box :menuOptions="packages.selectOptions" @deletePhoto="deleteHandle"></charts-item-box>
</n-scrollbar>
</div>
</div>
@ -23,8 +23,11 @@ import { ref, watch, computed, reactive } from 'vue'
import { ConfigType } from '@/packages/index.d'
import { useSettingStore } from '@/store/modules/settingStore/settingStore'
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 packagesStore = usePackagesStore()
const props = defineProps({
selectOptions: {
@ -61,7 +64,7 @@ let packages = reactive<{
saveSelectOptions: {}
})
const selectValue = ref<string>()
const selectValue = ref<string>('all')
//
const setSelectOptions = (categorys: any) => {
@ -79,7 +82,6 @@ watch(
if (!newData) return
newData.list.forEach((e: ConfigType) => {
const value: ConfigType[] = (packages.categorys as any)[e.category]
// @ts-ignore
packages.categorys[e.category] = value && value.length ? [...value, e] : [e]
packages.categoryNames[e.category] = e.categoryName
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) => {
packages.selectOptions = packages.categorys[key]
@ -119,6 +137,7 @@ $menuWidth: 65px;
@include fetch-bg-color('background-color2-shallow');
}
.chart-content-list {
width: 200px;
flex: 1;
display: flex;
flex-direction: column;

View File

@ -37,7 +37,8 @@
:title="item.title"
@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>
</div>
</n-scrollbar>
@ -70,7 +71,7 @@ import { ref, onUnmounted } from 'vue'
import { icon } from '@/plugins'
import { createComponent } from '@/packages'
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 { ChartModeEnum, ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
@ -78,6 +79,7 @@ import { isString, addEventListener, removeEventListener } from '@/utils'
import { fetchConfigComponent, fetchChartComponent } from '@/packages/index'
import { componentInstall, loadingStart, loadingFinish, loadingError } from '@/utils'
import { ChartGlobImage } from '@/components/Pages/ChartGlobImage'
import { Icon } from '@iconify/vue'
const props = defineProps({
menuOptions: {
@ -129,7 +131,9 @@ const searchHandle = (key: string | null) => {
}
loading.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(() => {
loading.value = undefined
}, 500)
@ -146,6 +150,7 @@ const listenerCloseHandle = (e: Event) => {
//
const selectChartHandle = async (item: ConfigType) => {
if (item.disabled) return
try {
loadingStart()
//
@ -153,6 +158,11 @@ const selectChartHandle = async (item: ConfigType) => {
componentInstall(item.conKey, fetchConfigComponent(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)
//
@ -221,10 +231,16 @@ $searchWidth: 176px;
font-size: 12px;
}
&-img {
height: 28px;
height: 20px;
max-width: 30px;
margin-right: 5px;
border-radius: 5px;
}
& > svg {
min-width: 20px;
min-height: 20px;
margin-right: 5px;
}
&:hover {
&::before {
content: '';

View File

@ -1,92 +1,95 @@
import { shallowReactive, ref } from 'vue'
import { icon } from '@/plugins'
import { renderLang, renderIcon } from '@/utils'
import { themeColor, setItem, getCharts } from './useLayout.hook'
import { PackagesCategoryEnum, PackagesCategoryName, PackagesType } from '@/packages/index.d'
// 图表
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
// 图标
const { BarChartIcon } = icon.ionicons5
const {
TableSplitIcon,
RoadmapIcon,
SpellCheckIcon,
GraphicalDataFlowIcon,
} = icon.carbon
// 图表
export type MenuOptionsType = {
key: string
icon: ReturnType<typeof renderIcon>
label: ReturnType<typeof renderLang>
list: PackagesType
}
const { getPackagesList } = usePackagesStore()
const menuOptions: MenuOptionsType[] = []
const packagesListObj = {
[PackagesCategoryEnum.CHARTS]: {
icon: renderIcon(RoadmapIcon),
label: PackagesCategoryName.CHARTS,
},
[PackagesCategoryEnum.INFORMATIONS]: {
icon: renderIcon(SpellCheckIcon),
label: PackagesCategoryName.INFORMATIONS,
},
[PackagesCategoryEnum.TABLES]: {
icon: renderIcon(TableSplitIcon),
label: PackagesCategoryName.TABLES,
},
[PackagesCategoryEnum.DECORATES]: {
icon: renderIcon(GraphicalDataFlowIcon),
label: PackagesCategoryName.DECORATES,
},
}
// 处理列表
const handlePackagesList = () => {
for (const val in getPackagesList) {
menuOptions.push({
key: val,
// @ts-ignore
icon: packagesListObj[val].icon,
// @ts-ignore
label: packagesListObj[val].label,
// @ts-ignore
list: getPackagesList[val],
})
}
}
handlePackagesList()
// 记录选中值
let beforeSelect: string = menuOptions[0]['key']
const selectValue = ref<string>(menuOptions[0]['key'])
// 选中的对象值
const selectOptions = ref(menuOptions[0])
// 点击 item
const clickItemHandle = (key: string, item: any) => {
selectOptions.value = item
// 处理折叠
if (beforeSelect === key) {
setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false)
} else {
setItem(ChartLayoutStoreEnum.CHARTS, true, false)
}
beforeSelect = key
}
export {
getCharts,
BarChartIcon,
themeColor,
selectOptions,
selectValue,
clickItemHandle,
menuOptions,
}
import { ref, watch, computed } from 'vue'
import { icon } from '@/plugins'
import { renderLang, renderIcon } from '@/utils'
import { themeColor, setItem, getCharts } from './useLayout.hook'
import { PackagesCategoryEnum, PackagesCategoryName, ConfigType } from '@/packages/index.d'
import { usePackagesStore } from '@/store/modules/packagesStore/packagesStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
// 图标
const { AirPlaneOutlineIcon, ImageIcon, BarChartIcon } = icon.ionicons5
const { TableSplitIcon, RoadmapIcon, SpellCheckIcon, GraphicalDataFlowIcon } = icon.carbon
// 图表
export type MenuOptionsType = {
key: string
icon: ReturnType<typeof renderIcon>
label: ReturnType<typeof renderLang>
list: ConfigType[]
}
const packagesListObj = {
[PackagesCategoryEnum.CHARTS]: {
icon: renderIcon(RoadmapIcon),
label: PackagesCategoryName.CHARTS
},
[PackagesCategoryEnum.INFORMATIONS]: {
icon: renderIcon(SpellCheckIcon),
label: PackagesCategoryName.INFORMATIONS
},
[PackagesCategoryEnum.TABLES]: {
icon: renderIcon(TableSplitIcon),
label: PackagesCategoryName.TABLES
},
[PackagesCategoryEnum.DECORATES]: {
icon: renderIcon(GraphicalDataFlowIcon),
label: PackagesCategoryName.DECORATES
},
[PackagesCategoryEnum.PHOTOS]: {
icon: renderIcon(ImageIcon),
label: PackagesCategoryName.PHOTOS
},
[PackagesCategoryEnum.ICONS]: {
icon: renderIcon(AirPlaneOutlineIcon),
label: PackagesCategoryName.ICONS
}
}
export const useAsideHook = () => {
const packagesStore = usePackagesStore()
const menuOptions: MenuOptionsType[] = []
// 处理列表
const handlePackagesList = () => {
for (const val in packagesStore.getPackagesList) {
menuOptions.push({
key: val,
// @ts-ignore
icon: packagesListObj[val].icon,
// @ts-ignore
label: packagesListObj[val].label,
// @ts-ignore
list: packagesStore.getPackagesList[val]
})
}
}
handlePackagesList()
// 记录选中值
let beforeSelect: string = menuOptions[0]['key']
const selectValue = ref<string>(menuOptions[0]['key'])
// 选中的对象值
const selectOptions = ref(menuOptions[0])
// 点击 item
const clickItemHandle = (key: string, item: any) => {
selectOptions.value = item
// 处理折叠
if (beforeSelect === key) {
setItem(ChartLayoutStoreEnum.CHARTS, !getCharts.value, false)
} else {
setItem(ChartLayoutStoreEnum.CHARTS, true, false)
}
beforeSelect = key
}
return {
getCharts,
BarChartIcon,
themeColor,
selectOptions,
selectValue,
clickItemHandle,
menuOptions
}
}

View File

@ -1,123 +1,105 @@
<template>
<!-- 左侧所有组件的展示列表 -->
<content-box
class="go-content-charts"
:class="{ scoped: !getCharts }"
title="组件"
:depth="1"
:backIcon="false"
>
<template #icon>
<n-icon size="14" :depth="2">
<bar-chart-icon></bar-chart-icon>
</n-icon>
</template>
<template #top-right>
<charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search>
</template>
<!-- 图表 -->
<aside>
<div class="menu-width-box">
<n-menu
class="menu-width"
v-model:value="selectValue"
:options="menuOptions"
:icon-size="16"
:indent="18"
@update:value="clickItemHandle"
></n-menu>
<div class="menu-component-box">
<go-skeleton
:load="!selectOptions"
round
text
:repeat="2"
style="width: 90%"
></go-skeleton>
<charts-option-content
v-if="selectOptions"
:selectOptions="selectOptions"
:key="selectValue"
></charts-option-content>
</div>
</div>
</aside>
</content-box>
</template>
<script setup lang="ts">
import { ContentBox } from '../ContentBox/index'
import { ChartsOptionContent } from './components/ChartsOptionContent'
import { ChartsSearch } from './components/ChartsSearch'
import {
getCharts,
BarChartIcon,
themeColor,
selectOptions,
selectValue,
clickItemHandle,
menuOptions,
} from './hooks/useAside.hook'
</script>
<style lang="scss" scoped>
/* 整体宽度 */
$width: 330px;
/* 列表的宽度 */
$widthScoped: 65px;
/* 此高度与 ContentBox 组件关联 */
$topHeight: 40px;
@include go(content-charts) {
width: $width;
@extend .go-transition;
&.scoped,
.menu-width {
width: $widthScoped;
}
.menu-width-box {
display: flex;
height: calc(100vh - #{$--header-height} - #{$topHeight});
.menu-width {
flex-shrink: 0;
@include fetch-bg-color('background-color2');
}
.menu-component-box {
flex-shrink: 0;
width: $width - $widthScoped;
overflow: hidden;
}
}
@include deep() {
.menu-width {
.n-menu-item {
height: auto !important;
&.n-menu-item--selected {
&::after {
content: '';
position: absolute;
left: 2px;
top: 0;
height: 100%;
width: 3px;
background-color: v-bind('themeColor');
border-top-right-radius: 3px;
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>
<template>
<!-- 左侧所有组件的展示列表 -->
<content-box class="go-content-charts" :class="{ scoped: !getCharts }" title="组件" :depth="1" :backIcon="false">
<template #icon>
<n-icon size="14" :depth="2">
<bar-chart-icon></bar-chart-icon>
</n-icon>
</template>
<template #top-right>
<charts-search v-show="getCharts" :menuOptions="menuOptions"></charts-search>
</template>
<!-- 图表 -->
<aside>
<div class="menu-width-box">
<n-menu
class="menu-width"
v-model:value="selectValue"
:options="menuOptions"
:icon-size="16"
:indent="18"
@update:value="clickItemHandle"
></n-menu>
<div class="menu-component-box">
<go-skeleton :load="!selectOptions" round text :repeat="2" style="width: 90%"></go-skeleton>
<charts-option-content
v-if="selectOptions"
:selectOptions="selectOptions"
:key="selectValue"
></charts-option-content>
</div>
</div>
</aside>
</content-box>
</template>
<script setup lang="ts">
import { ContentBox } from '../ContentBox/index'
import { ChartsOptionContent } from './components/ChartsOptionContent'
import { ChartsSearch } from './components/ChartsSearch'
import { useAsideHook } from './hooks/useAside.hook'
const { getCharts, BarChartIcon, themeColor, selectOptions, selectValue, clickItemHandle, menuOptions } = useAsideHook()
</script>
<style lang="scss" scoped>
/* 整体宽度 */
$width: 330px;
/* 列表的宽度 */
$widthScoped: 65px;
/* 此高度与 ContentBox 组件关联 */
$topHeight: 40px;
@include go(content-charts) {
width: $width;
@extend .go-transition;
&.scoped,
.menu-width {
width: $widthScoped;
}
.menu-width-box {
display: flex;
height: calc(100vh - #{$--header-height} - #{$topHeight});
.menu-width {
flex-shrink: 0;
@include fetch-bg-color('background-color2');
}
.menu-component-box {
flex-shrink: 0;
width: $width - $widthScoped;
overflow: hidden;
}
}
@include deep() {
.menu-width {
.n-menu-item {
height: auto !important;
&.n-menu-item--selected {
&::after {
content: '';
position: absolute;
left: 2px;
top: 0;
height: 100%;
width: 3px;
background-color: v-bind('themeColor');
border-top-right-radius: 3px;
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>

View File

@ -58,7 +58,7 @@
<help-outline-icon></help-outline-icon>
</n-icon>
</template>
<n-text>不支持静态组件分组</n-text>
<n-text>不支持静态组件</n-text>
</n-tooltip>
</template>
<n-select
@ -169,7 +169,27 @@ const fnDimensionsAndSource = (interactOn: InteractEventOn | undefined) => {
//
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
//

View File

@ -29,9 +29,15 @@ export const dragHandle = async (e: DragEvent) => {
// 修改状态
chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_CREATE, false)
const dropData: Exclude<ConfigType, ['image']> = JSONParse(drayDataString)
if (dropData.disabled) return
// 创建新图表组件
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)
chartEditStore.addComponentList(newComponent, false, true)

View File

@ -1,159 +1,159 @@
<template>
<div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }">
<div class="go-flex-center item-content">
<n-image
class="list-img"
object-fit="contain"
preview-disabled
:src="imageInfo"
:fallback-src="requireErrorImg()"
></n-image>
<n-ellipsis style="margin-right: auto">
<span class="list-text">
{{ props.componentData.chartConfig.title }}
</span>
</n-ellipsis>
<layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status>
</div>
<div :class="{ 'select-modal': select }"></div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, ref, watch } from 'vue'
import { requireErrorImg } from '@/utils'
import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { fetchImages } from '@/packages'
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { LayersStatus } from '../LayersStatus/index'
const props = defineProps({
componentData: {
type: Object as PropType<CreateComponentType | CreateComponentGroupType>,
required: true
},
isGroup: {
type: Boolean,
default: false
},
layerMode: {
type: String as PropType<LayerModeEnum>,
default: LayerModeEnum.THUMBNAIL
}
})
//
const designStore = useDesignStore()
const chartEditStore = useChartEditStore()
const imageInfo = ref('')
//
const fetchImageUrl = async () => {
imageInfo.value = await fetchImages(props.componentData.chartConfig)
}
fetchImageUrl()
//
const themeColor = computed(() => {
return designStore.getAppTheme
})
//
const select = computed(() => {
const id = props.componentData.id
return chartEditStore.getTargetChart.selectId.find((e: string) => e === id)
})
//
const hover = computed(() => {
return props.componentData.id === chartEditStore.getTargetChart.hoverId
})
// /
const status = computed(() => {
return props.componentData.status
})
//
const selectText = computed(() => {
return props.layerMode === LayerModeEnum.TEXT
})
</script>
<style lang="scss" scoped>
$centerHeight: 52px;
$centerMiniHeight: 28px;
$textSize: 10px;
@include go(content-layers-list-item) {
position: relative;
height: $centerHeight;
width: 90%;
margin: 5px 5%;
margin-bottom: 5px;
border-radius: 5px;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0);
@extend .go-transition-quick;
&.hover,
&:hover {
@include fetch-bg-color('background-color4');
}
&:hover {
@include deep() {
.icon-item {
opacity: 1;
}
}
}
.select-modal,
.item-content {
position: absolute;
top: 0;
left: 0;
}
.item-content {
z-index: 1;
padding: 6px 5px;
justify-content: start !important;
width: calc(100% - 10px);
height: calc(100% - 10px);
}
.select-modal {
width: 100%;
height: 100%;
opacity: 0.3;
background-color: v-bind('themeColor');
}
.list-img {
flex-shrink: 0;
height: $centerHeight;
border-radius: 5px;
overflow: hidden;
border: none !important;
padding: 2px;
@include hover-border-color('hover-border-color');
}
.list-text {
padding-left: 6px;
font-size: $textSize;
}
/* 选中样式 */
&.select {
border: 1px solid v-bind('themeColor');
background-color: rgba(0, 0, 0, 0);
}
// mini
&.list-mini {
height: $centerMiniHeight;
}
}
</style>
<template>
<div class="go-content-layers-list-item" :class="{ hover, select, 'list-mini': selectText }">
<div class="go-flex-center item-content">
<n-image
class="list-img"
object-fit="contain"
preview-disabled
:src="imageInfo"
:fallback-src="requireErrorImg()"
></n-image>
<n-ellipsis style="margin-right: auto">
<span class="list-text">
{{ props.componentData.chartConfig.title }}
</span>
</n-ellipsis>
<layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status>
</div>
<div :class="{ 'select-modal': select }"></div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, ref } from 'vue'
import { requireErrorImg } from '@/utils'
import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { LayerModeEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { fetchImages } from '@/packages'
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { LayersStatus } from '../LayersStatus/index'
const props = defineProps({
componentData: {
type: Object as PropType<CreateComponentType | CreateComponentGroupType>,
required: true
},
isGroup: {
type: Boolean,
default: false
},
layerMode: {
type: String as PropType<LayerModeEnum>,
default: LayerModeEnum.THUMBNAIL
}
})
//
const designStore = useDesignStore()
const chartEditStore = useChartEditStore()
const imageInfo = ref('')
//
const fetchImageUrl = async () => {
imageInfo.value = await fetchImages(props.componentData.chartConfig)
}
fetchImageUrl()
//
const themeColor = computed(() => {
return designStore.getAppTheme
})
//
const select = computed(() => {
const id = props.componentData.id
return chartEditStore.getTargetChart.selectId.find((e: string) => e === id)
})
//
const hover = computed(() => {
return props.componentData.id === chartEditStore.getTargetChart.hoverId
})
// /
const status = computed(() => {
return props.componentData.status
})
//
const selectText = computed(() => {
return props.layerMode === LayerModeEnum.TEXT
})
</script>
<style lang="scss" scoped>
$centerHeight: 52px;
$centerMiniHeight: 28px;
$textSize: 10px;
@include go(content-layers-list-item) {
position: relative;
height: $centerHeight;
width: 90%;
margin: 5px 5%;
margin-bottom: 5px;
border-radius: 5px;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0);
@extend .go-transition-quick;
&.hover,
&:hover {
@include fetch-bg-color('background-color4');
}
&:hover {
@include deep() {
.icon-item {
opacity: 1;
}
}
}
.select-modal,
.item-content {
position: absolute;
top: 0;
left: 0;
}
.item-content {
z-index: 1;
padding: 6px 5px;
justify-content: start !important;
width: calc(100% - 10px);
height: calc(100% - 10px);
}
.select-modal {
width: 100%;
height: 100%;
opacity: 0.3;
background-color: v-bind('themeColor');
}
.list-img {
flex-shrink: 0;
height: $centerHeight;
border-radius: 5px;
overflow: hidden;
border: none !important;
padding: 2px;
@include hover-border-color('hover-border-color');
}
.list-text {
padding-left: 6px;
font-size: $textSize;
}
/* 选中样式 */
&.select {
border: 1px solid v-bind('themeColor');
background-color: rgba(0, 0, 0, 0);
}
// mini
&.list-mini {
height: $centerMiniHeight;
}
}
</style>

View File

@ -132,6 +132,11 @@ export const useSync = () => {
) => {
// 补充 class 上的方法
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 (changeId) {
callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() }))
@ -156,7 +161,7 @@ export const useSync = () => {
// 组件
if (key === ChartEditStoreEnum.COMPONENT_LIST) {
let loadIndex = 0
const listLength = projectData[key].length;
const listLength = projectData[key].length
for (const comItem of projectData[key]) {
// 设置加载数量
let percentage = parseInt((parseFloat(`${++loadIndex / listLength}`) * 100).toString())