Commit ac4f27b6ac5acf57dfa042d73c7a5130287cf478

Authored by xp.Huang
2 parents 0e80ddda d52f4d9f

Merge branch 'ft' into 'main_dev'

feat(packages/external): 重写部分柱状图和部分折线图,新增开启关闭动画效果和全屏效果

See merge request yunteng/thingskit-view!66
Showing 37 changed files with 1909 additions and 65 deletions
  1 +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
  2 +import { OverrideBarCommonConfig } from './index'
  3 +import { CreateComponentType } from '@/packages/index.d'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +import dataJson from './data.json'
  6 +
  7 +export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
  8 +// 其它配置项比如新增(动画)
  9 +const otherConfig = {
  10 + // 轮播动画
  11 + isCarousel: false
  12 +}
  13 +export const seriesItem = {
  14 + type: 'bar',
  15 + barWidth: 15,
  16 + label: {
  17 + show: true,
  18 + position: 'top',
  19 + color: '#fff',
  20 + fontSize: 12
  21 + },
  22 + itemStyle: {
  23 + color: null,
  24 + borderRadius: 2
  25 + }
  26 +}
  27 +export const option = {
  28 + ...otherConfig,
  29 + tooltip: {
  30 + show: true,
  31 + trigger: 'axis',
  32 + axisPointer: {
  33 + show: true,
  34 + type: 'shadow'
  35 + }
  36 + },
  37 + xAxis: {
  38 + show: true,
  39 + type: 'category'
  40 + },
  41 + yAxis: {
  42 + show: true,
  43 + type: 'value'
  44 + },
  45 + dataset: { ...dataJson },
  46 + series: [seriesItem, seriesItem]
  47 +}
  48 +
  49 +export default class Config extends PublicConfigClass implements CreateComponentType {
  50 + public key = OverrideBarCommonConfig.key
  51 + public chartConfig = cloneDeep(OverrideBarCommonConfig)
  52 + // 图表配置项
  53 + public option = echartOptionProfixHandle(option, includes)
  54 +}
  1 +<template>
  2 + <!-- Echarts 全局设置 -->
  3 + <global-setting :optionData="optionData"></global-setting>
  4 + <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`柱状图-${index + 1}`" :expanded="true">
  5 + <SettingItemBox name="图形">
  6 + <SettingItem name="宽度">
  7 + <n-input-number
  8 + v-model:value="item.barWidth"
  9 + :min="1"
  10 + :max="100"
  11 + size="small"
  12 + placeholder="自动计算"
  13 + ></n-input-number>
  14 + </SettingItem>
  15 + <SettingItem name="圆角">
  16 + <n-input-number v-model:value="item.itemStyle.borderRadius" :min="0" size="small"></n-input-number>
  17 + </SettingItem>
  18 + </SettingItemBox>
  19 + <SettingItemBox name="动画" :alone="true">
  20 + <SettingItem>
  21 + <n-space>
  22 + <n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
  23 + <n-text>开启<n-text :depth="3">(将自动隐藏图例)</n-text></n-text>
  24 + </n-space>
  25 + </SettingItem>
  26 + <SettingItem>
  27 + <n-text :depth="3">无鼠标点击图例场景时,可强行打开图例</n-text>
  28 + </SettingItem>
  29 + </SettingItemBox>
  30 + <setting-item-box name="标签">
  31 + <setting-item>
  32 + <n-space>
  33 + <n-switch v-model:value="item.label.show" size="small" />
  34 + <n-text>展示标签</n-text>
  35 + </n-space>
  36 + </setting-item>
  37 + <setting-item name="大小">
  38 + <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
  39 + </setting-item>
  40 + <setting-item name="颜色">
  41 + <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
  42 + </setting-item>
  43 + <setting-item name="位置">
  44 + <n-select
  45 + v-model:value="item.label.position"
  46 + :options="[
  47 + { label: 'top', value: 'top' },
  48 + { label: 'left', value: 'left' },
  49 + { label: 'right', value: 'right' },
  50 + { label: 'bottom', value: 'bottom' }
  51 + ]"
  52 + />
  53 + </setting-item>
  54 + </setting-item-box>
  55 + </CollapseItem>
  56 +</template>
  57 +
  58 +<script setup lang="ts">
  59 +import { PropType, computed } from 'vue'
  60 +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  61 +import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
  62 +
  63 +const props = defineProps({
  64 + optionData: {
  65 + type: Object as PropType<GlobalThemeJsonType>,
  66 + required: true
  67 + }
  68 +})
  69 +
  70 +const seriesList = computed(() => {
  71 + return props.optionData.series
  72 +})
  73 +</script>
  1 +{
  2 + "dimensions": ["product", "data1", "data2"],
  3 + "source": [
  4 + {
  5 + "product": "Mon",
  6 + "data1": 120,
  7 + "data2": 130
  8 + },
  9 + {
  10 + "product": "Tue",
  11 + "data1": 200,
  12 + "data2": 130
  13 + },
  14 + {
  15 + "product": "Wed",
  16 + "data1": 150,
  17 + "data2": 312
  18 + },
  19 + {
  20 + "product": "Thu",
  21 + "data1": 80,
  22 + "data2": 268
  23 + },
  24 + {
  25 + "product": "Fri",
  26 + "data1": 70,
  27 + "data2": 155
  28 + },
  29 + {
  30 + "product": "Sat",
  31 + "data1": 110,
  32 + "data2": 117
  33 + },
  34 + {
  35 + "product": "Sun",
  36 + "data1": 130,
  37 + "data2": 160
  38 + }
  39 + ]
  40 +}
  1 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  2 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d'
  3 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  4 +
  5 +const { key, conKey, chartKey } = useWidgetKey('OverrideBarCommon', true)
  6 +
  7 +export const OverrideBarCommonConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '柱状图动画',
  12 + category: ChatCategoryEnum.BAR,
  13 + categoryName: ChatCategoryEnumName.BAR,
  14 + package: PackagesCategoryEnum.CHARTS,
  15 + chartFrame: ChartFrameEnum.ECHARTS,
  16 + image: 'bar_x.png'
  17 +}
  1 +<template>
  2 + <v-chart
  3 + ref="vChartRef"
  4 + :init-options="initOptions"
  5 + :theme="themeColor"
  6 + :option="option"
  7 + :manual-update="isPreview()"
  8 + :update-options="{
  9 + replaceMerge: replaceMergeArr
  10 + }"
  11 + autoresize
  12 + @mouseover="handleHighlight"
  13 + @mouseout="handleDownplay"
  14 + >
  15 + </v-chart>
  16 +</template>
  17 +
  18 +<script setup lang="ts">
  19 +import { ref, nextTick, computed, watch, PropType, onMounted } from 'vue'
  20 +import VChart from 'vue-echarts'
  21 +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
  22 +import { use } from 'echarts/core'
  23 +import { CanvasRenderer } from 'echarts/renderers'
  24 +import { BarChart } from 'echarts/charts'
  25 +import config, { includes, seriesItem } from './config'
  26 +import { mergeTheme } from '@/packages/public/chart'
  27 +import { useChartDataFetch } from '@/hooks'
  28 +import { CreateComponentType } from '@/packages/index.d'
  29 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  30 +import { isPreview } from '@/utils'
  31 +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
  32 +import isObject from 'lodash/isObject'
  33 +import dataJson from './data.json'
  34 +import { isFullScreen } from '../../utls/isFullScreen'
  35 +
  36 +const props = defineProps({
  37 + themeSetting: {
  38 + type: Object,
  39 + required: true
  40 + },
  41 + themeColor: {
  42 + type: Object,
  43 + required: true
  44 + },
  45 + chartConfig: {
  46 + type: Object as PropType<config>,
  47 + required: true
  48 + }
  49 +})
  50 +
  51 +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
  52 +
  53 +use([DatasetComponent, CanvasRenderer, BarChart, GridComponent, TooltipComponent, LegendComponent])
  54 +
  55 +const replaceMergeArr = ref<string[]>()
  56 +
  57 +const option = computed(() => {
  58 + return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
  59 +})
  60 +
  61 +const toolBoxOption = {
  62 + show: true,
  63 + right: 20,
  64 + feature: {
  65 + myFullButton: {
  66 + show: true,
  67 + title: '全屏查看',
  68 + icon: 'path://M733.549304 0l116.434359 116.23452-226.402521 226.40252 57.053835 57.068109 226.459617-226.445342 120.616689 120.41685V0H733.549304zM689.513507 619.855586l-57.068108 57.068109 224.232847 224.232847-122.64362 122.843458h293.676657V729.838022l-114.007751 114.207588-224.190025-224.190024zM338.197775 404.144414l57.068109-57.068109L171.033037 122.843458 293.676657 0H0v294.161978l114.022025-114.207588 224.17575 224.190024zM347.076305 624.294851L120.616689 850.754468 0 730.323343v293.676657h294.161978l-116.420084-116.23452 226.40252-226.40252-57.068109-57.068109z',
  69 + onclick: () => {
  70 + const getEchartDom = vChartRef.value?.getDom()
  71 + const domName = document.getElementById(getEchartDom.id) as any
  72 + const htmlName = document.querySelector('html') as any
  73 + isFullScreen(domName, htmlName)
  74 + }
  75 + }
  76 + }
  77 +}
  78 +props.chartConfig.option = {
  79 + ...props.chartConfig.option,
  80 + ...{ toolbox: toolBoxOption }
  81 +}
  82 +
  83 +// dataset 无法变更条数的补丁
  84 +watch(
  85 + () => props.chartConfig.option.dataset,
  86 + (newData: { dimensions: any }, oldData) => {
  87 + try {
  88 + if (!isObject(newData) || !('dimensions' in newData)) return
  89 + if (Array.isArray(newData?.dimensions)) {
  90 + const seriesArr = []
  91 + for (let i = 0; i < newData.dimensions.length - 1; i++) {
  92 + seriesArr.push(seriesItem)
  93 + }
  94 + replaceMergeArr.value = ['series']
  95 + props.chartConfig.option.series = seriesArr
  96 + nextTick(() => {
  97 + replaceMergeArr.value = []
  98 + })
  99 + }
  100 + } catch (error) {
  101 + console.log(error)
  102 + }
  103 + },
  104 + {
  105 + deep: false
  106 + }
  107 +)
  108 +
  109 +let seriesDataNum = -1
  110 +let seriesDataMaxLength = 0
  111 +let intervalInstance: any = null
  112 +//轮播时长
  113 +const duration = 1500
  114 +
  115 +// 会重新选择需要选中和展示的数据
  116 +const handleSeriesData = () => {
  117 + if (seriesDataNum > -1) {
  118 + vChartRef.value?.dispatchAction({
  119 + type: 'downplay',
  120 + dataIndex: seriesDataNum
  121 + })
  122 + }
  123 + seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
  124 + vChartRef.value?.dispatchAction({
  125 + type: 'showTip',
  126 + seriesIndex: 0,
  127 + dataIndex: seriesDataNum
  128 + })
  129 +}
  130 +
  131 +// 新增轮播
  132 +const addPieInterval = (newData?: typeof dataJson, skipPre = false) => {
  133 + if (!skipPre && !Array.isArray(newData?.source)) return
  134 + if (!skipPre) seriesDataMaxLength = newData?.source.length || 0
  135 + clearInterval(intervalInstance)
  136 + intervalInstance = setInterval(() => {
  137 + handleSeriesData()
  138 + }, duration)
  139 +}
  140 +
  141 +// 取消轮播
  142 +const clearPieInterval = () => {
  143 + vChartRef.value?.dispatchAction({
  144 + type: 'hideTip',
  145 + seriesIndex: 0,
  146 + dataIndex: seriesDataNum
  147 + })
  148 + vChartRef.value?.dispatchAction({
  149 + type: 'downplay',
  150 + dataIndex: seriesDataNum
  151 + })
  152 + clearInterval(intervalInstance)
  153 + intervalInstance = null
  154 +}
  155 +
  156 +// 处理鼠标聚焦高亮内容
  157 +const handleHighlight = () => {
  158 + clearPieInterval()
  159 +}
  160 +
  161 +// 处理鼠标取消悬浮
  162 +const handleDownplay = () => {
  163 + if (props.chartConfig.option.isCarousel && !intervalInstance) {
  164 + // 恢复轮播
  165 + addPieInterval(undefined, true)
  166 + }
  167 +}
  168 +
  169 +watch(
  170 + () => props.chartConfig.option.isCarousel,
  171 + newData => {
  172 + if (newData) {
  173 + addPieInterval(undefined, true)
  174 + props.chartConfig.option.legend.show = false
  175 + } else {
  176 + props.chartConfig.option.legend.show = true
  177 + clearPieInterval()
  178 + }
  179 + }
  180 +)
  181 +
  182 +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
  183 + // addPieInterval(newData)
  184 +})
  185 +
  186 +onMounted(() => {
  187 + seriesDataMaxLength = dataJson.source.length
  188 + if (props.chartConfig.option.isCarousel) {
  189 + addPieInterval(undefined, true)
  190 + }
  191 +})
  192 +</script>
  1 +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
  2 +import { OverrideLineCommonConfig } from './index'
  3 +import { CreateComponentType } from '@/packages/index.d'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +import dataJson from './data.json'
  6 +
  7 +export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
  8 +export const seriesItem = {
  9 + type: 'line',
  10 + label: {
  11 + show: true,
  12 + position: 'top',
  13 + color: '#fff',
  14 + fontSize: 12
  15 + },
  16 + symbolSize: 5, //设定实心点的大小
  17 + itemStyle: {
  18 + color: null,
  19 + borderRadius: 0
  20 + },
  21 + lineStyle: {
  22 + type: 'solid',
  23 + width: 3,
  24 + color: null
  25 + }
  26 +}
  27 +// 其它配置项比如新增(动画)
  28 +const otherConfig = {
  29 + // 轮播动画
  30 + isCarousel: false,
  31 +}
  32 +export const option = {
  33 + ...otherConfig,
  34 + tooltip: {
  35 + show: true,
  36 + trigger: 'axis',
  37 + axisPointer: {
  38 + type: 'line'
  39 + }
  40 + },
  41 + xAxis: {
  42 + show: true,
  43 + type: 'category'
  44 + },
  45 + yAxis: {
  46 + show: true,
  47 + type: 'value'
  48 + },
  49 + dataset: { ...dataJson },
  50 + series: [seriesItem, seriesItem]
  51 +}
  52 +
  53 +export default class Config extends PublicConfigClass implements CreateComponentType {
  54 + public key: string = OverrideLineCommonConfig.key
  55 + public chartConfig = cloneDeep(OverrideLineCommonConfig)
  56 + // 图表配置项
  57 + public option = echartOptionProfixHandle(option, includes)
  58 +}
  1 +<template>
  2 + <!-- Echarts 全局设置 -->
  3 + <global-setting :optionData="optionData"></global-setting>
  4 + <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`折线图-${index + 1}`" :expanded="true">
  5 + <SettingItemBox name="线条">
  6 + <SettingItem name="宽度">
  7 + <n-input-number
  8 + v-model:value="item.lineStyle.width"
  9 + :min="1"
  10 + :max="100"
  11 + size="small"
  12 + placeholder="自动计算"
  13 + ></n-input-number>
  14 + </SettingItem>
  15 + <SettingItem name="类型">
  16 + <n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
  17 + </SettingItem>
  18 + </SettingItemBox>
  19 + <SettingItemBox name="动画" :alone="true">
  20 + <SettingItem>
  21 + <n-space>
  22 + <n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
  23 + <n-text>开启<n-text :depth="3">(将自动隐藏图例)</n-text></n-text>
  24 + </n-space>
  25 + </SettingItem>
  26 + <SettingItem>
  27 + <n-text :depth="3">无鼠标点击图例场景时,可强行打开图例</n-text>
  28 + </SettingItem>
  29 + </SettingItemBox>
  30 + <SettingItemBox name="实心点">
  31 + <SettingItem name="大小">
  32 + <n-input-number
  33 + v-model:value="item.symbolSize"
  34 + :min="1"
  35 + :max="100"
  36 + size="small"
  37 + placeholder="自动计算"
  38 + ></n-input-number>
  39 + </SettingItem>
  40 + </SettingItemBox>
  41 + <setting-item-box name="标签">
  42 + <setting-item>
  43 + <n-space>
  44 + <n-switch v-model:value="item.label.show" size="small" />
  45 + <n-text>展示标签</n-text>
  46 + </n-space>
  47 + </setting-item>
  48 + <setting-item name="大小">
  49 + <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
  50 + </setting-item>
  51 + <setting-item name="颜色">
  52 + <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
  53 + </setting-item>
  54 + <setting-item name="位置">
  55 + <n-select
  56 + v-model:value="item.label.position"
  57 + :options="[
  58 + { label: 'top', value: 'top' },
  59 + { label: 'left', value: 'left' },
  60 + { label: 'right', value: 'right' },
  61 + { label: 'bottom', value: 'bottom' }
  62 + ]"
  63 + />
  64 + </setting-item>
  65 + </setting-item-box>
  66 + </CollapseItem>
  67 +</template>
  68 +
  69 +<script setup lang="ts">
  70 +import { PropType, computed } from 'vue'
  71 +import { lineConf } from '@/packages/chartConfiguration/echarts/index'
  72 +import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
  73 +import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  74 +
  75 +const props = defineProps({
  76 + optionData: {
  77 + type: Object as PropType<GlobalThemeJsonType>,
  78 + required: true
  79 + }
  80 +})
  81 +
  82 +const seriesList = computed(() => {
  83 + return props.optionData.series
  84 +})
  85 +</script>
  1 +{
  2 + "dimensions": ["product", "data1", "data2"],
  3 + "source": [
  4 + {
  5 + "product": "Mon",
  6 + "data1": 120,
  7 + "data2": 130
  8 + },
  9 + {
  10 + "product": "Tue",
  11 + "data1": 200,
  12 + "data2": 130
  13 + },
  14 + {
  15 + "product": "Wed",
  16 + "data1": 150,
  17 + "data2": 312
  18 + },
  19 + {
  20 + "product": "Thu",
  21 + "data1": 80,
  22 + "data2": 268
  23 + },
  24 + {
  25 + "product": "Fri",
  26 + "data1": 70,
  27 + "data2": 155
  28 + },
  29 + {
  30 + "product": "Sat",
  31 + "data1": 110,
  32 + "data2": 117
  33 + },
  34 + {
  35 + "product": "Sun",
  36 + "data1": 130,
  37 + "data2": 160
  38 + }
  39 + ]
  40 +}
  1 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  2 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d'
  3 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  4 +
  5 +const { key, conKey, chartKey } = useWidgetKey('OverrideLineCommon', true)
  6 +
  7 +export const OverrideLineCommonConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '折线图动画',
  12 + category: ChatCategoryEnum.LINE,
  13 + categoryName: ChatCategoryEnumName.LINE,
  14 + package: PackagesCategoryEnum.CHARTS,
  15 + chartFrame: ChartFrameEnum.ECHARTS,
  16 + image: 'line.png'
  17 +}
  1 +<template>
  2 + <v-chart
  3 + ref="vChartRef"
  4 + :init-options="initOptions"
  5 + :theme="themeColor"
  6 + :option="option"
  7 + :manual-update="isPreview()"
  8 + :update-options="{
  9 + replaceMerge: replaceMergeArr
  10 + }"
  11 + autoresize
  12 + @mouseover="handleHighlight"
  13 + @mouseout="handleDownplay"
  14 + >
  15 + </v-chart>
  16 +</template>
  17 +
  18 +<script setup lang="ts">
  19 +import { PropType, computed, watch, ref, nextTick, onMounted } from 'vue'
  20 +import VChart from 'vue-echarts'
  21 +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
  22 +import { use } from 'echarts/core'
  23 +import { CanvasRenderer } from 'echarts/renderers'
  24 +import { LineChart } from 'echarts/charts'
  25 +import config, { includes, seriesItem } from './config'
  26 +import { mergeTheme } from '@/packages/public/chart'
  27 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  28 +import { useChartDataFetch } from '@/hooks'
  29 +import { isPreview } from '@/utils'
  30 +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
  31 +import isObject from 'lodash/isObject'
  32 +import dataJson from './data.json'
  33 +import { isFullScreen } from '../../utls/isFullScreen'
  34 +
  35 +const props = defineProps({
  36 + themeSetting: {
  37 + type: Object,
  38 + required: true
  39 + },
  40 + themeColor: {
  41 + type: Object,
  42 + required: true
  43 + },
  44 + chartConfig: {
  45 + type: Object as PropType<config>,
  46 + required: true
  47 + }
  48 +})
  49 +
  50 +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
  51 +
  52 +use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent])
  53 +
  54 +const replaceMergeArr = ref<string[]>()
  55 +
  56 +const option = computed(() => {
  57 + return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
  58 +})
  59 +
  60 +const toolBoxOption = {
  61 + show: true,
  62 + right: 20,
  63 + feature: {
  64 + myFullButton: {
  65 + show: true,
  66 + title: '全屏查看',
  67 + icon: 'path://M733.549304 0l116.434359 116.23452-226.402521 226.40252 57.053835 57.068109 226.459617-226.445342 120.616689 120.41685V0H733.549304zM689.513507 619.855586l-57.068108 57.068109 224.232847 224.232847-122.64362 122.843458h293.676657V729.838022l-114.007751 114.207588-224.190025-224.190024zM338.197775 404.144414l57.068109-57.068109L171.033037 122.843458 293.676657 0H0v294.161978l114.022025-114.207588 224.17575 224.190024zM347.076305 624.294851L120.616689 850.754468 0 730.323343v293.676657h294.161978l-116.420084-116.23452 226.40252-226.40252-57.068109-57.068109z',
  68 + onclick: () => {
  69 + const getEchartDom = vChartRef.value?.getDom()
  70 + const domName = document.getElementById(getEchartDom.id) as any
  71 + const htmlName = document.querySelector('html') as any
  72 + isFullScreen(domName, htmlName)
  73 + }
  74 + }
  75 + }
  76 +}
  77 +props.chartConfig.option = {
  78 + ...props.chartConfig.option,
  79 + ...{ toolbox: toolBoxOption }
  80 +}
  81 +
  82 +// dataset 无法变更条数的补丁
  83 +watch(
  84 + () => props.chartConfig.option.dataset,
  85 + (newData: { dimensions: any }, oldData) => {
  86 + try {
  87 + if (!isObject(newData) || !('dimensions' in newData)) return
  88 + if (Array.isArray(newData?.dimensions)) {
  89 + const seriesArr = []
  90 + for (let i = 0; i < newData.dimensions.length - 1; i++) {
  91 + seriesArr.push(seriesItem)
  92 + }
  93 + replaceMergeArr.value = ['series']
  94 + props.chartConfig.option.series = seriesArr
  95 + nextTick(() => {
  96 + replaceMergeArr.value = []
  97 + })
  98 + }
  99 + } catch (error) {
  100 + console.log(error)
  101 + }
  102 + },
  103 + {
  104 + deep: false
  105 + }
  106 +)
  107 +
  108 +let seriesDataNum = -1
  109 +let seriesDataMaxLength = 0
  110 +let intervalInstance: any = null
  111 +const duration = 1500
  112 +
  113 +// 会重新选择需要选中和展示的数据
  114 +const handleSeriesData = () => {
  115 + if (seriesDataNum > -1) {
  116 + vChartRef.value?.dispatchAction({
  117 + type: 'downplay',
  118 + dataIndex: seriesDataNum
  119 + })
  120 + }
  121 + seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
  122 + vChartRef.value?.dispatchAction({
  123 + type: 'showTip',
  124 + seriesIndex: 0,
  125 + dataIndex: seriesDataNum
  126 + })
  127 +}
  128 +
  129 +// 新增轮播
  130 +const addPieInterval = (newData?: typeof dataJson, skipPre = false) => {
  131 + if (!skipPre && !Array.isArray(newData?.source)) return
  132 + if (!skipPre) seriesDataMaxLength = newData?.source.length || 0
  133 + clearInterval(intervalInstance)
  134 + intervalInstance = setInterval(() => {
  135 + handleSeriesData()
  136 + }, duration)
  137 +}
  138 +
  139 +// 取消轮播
  140 +const clearPieInterval = () => {
  141 + vChartRef.value?.dispatchAction({
  142 + type: 'hideTip',
  143 + seriesIndex: 0,
  144 + dataIndex: seriesDataNum
  145 + })
  146 + vChartRef.value?.dispatchAction({
  147 + type: 'downplay',
  148 + dataIndex: seriesDataNum
  149 + })
  150 + clearInterval(intervalInstance)
  151 + intervalInstance = null
  152 +}
  153 +
  154 +// 处理鼠标聚焦高亮内容
  155 +const handleHighlight = () => {
  156 + clearPieInterval()
  157 +}
  158 +
  159 +// 处理鼠标取消悬浮
  160 +const handleDownplay = () => {
  161 + if (props.chartConfig.option.isCarousel && !intervalInstance) {
  162 + // 恢复轮播
  163 + addPieInterval(undefined, true)
  164 + }
  165 +}
  166 +
  167 +watch(
  168 + () => props.chartConfig.option.isCarousel,
  169 + newData => {
  170 + if (newData) {
  171 + addPieInterval(undefined, true)
  172 + props.chartConfig.option.legend.show = false
  173 + } else {
  174 + props.chartConfig.option.legend.show = true
  175 + clearPieInterval()
  176 + }
  177 + }
  178 +)
  179 +
  180 +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
  181 + // addPieInterval(newData)
  182 +})
  183 +
  184 +onMounted(() => {
  185 + seriesDataMaxLength = dataJson.source.length
  186 + if (props.chartConfig.option.isCarousel) {
  187 + addPieInterval(undefined, true)
  188 + }
  189 +})
  190 +</script>
  1 +import { echartOptionProfixHandle, PublicConfigClass } from '@/packages/public'
  2 +import { OverrideLineGradientsConfig } from './index'
  3 +import { CreateComponentType } from '@/packages/index.d'
  4 +import { graphic } from 'echarts/core'
  5 +import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
  6 +import cloneDeep from 'lodash/cloneDeep'
  7 +import dataJson from './data.json'
  8 +
  9 +export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
  10 +
  11 +// 其它配置项比如新增(动画)
  12 +const otherConfig = {
  13 + // 轮播动画
  14 + isCarousel: false,
  15 +}
  16 +const option = {
  17 + ...otherConfig,
  18 + tooltip: {
  19 + show: true,
  20 + trigger: 'axis',
  21 + axisPointer: {
  22 + type: 'line'
  23 + }
  24 + },
  25 + xAxis: {
  26 + show: true,
  27 + type: 'category'
  28 + },
  29 + yAxis: {
  30 + show: true,
  31 + type: 'value'
  32 + },
  33 + dataset: { ...dataJson },
  34 + series: [
  35 + {
  36 + type: 'line',
  37 + smooth: false,
  38 + symbolSize: 5, //设定实心点的大小
  39 + label: {
  40 + show: true,
  41 + position: 'top',
  42 + color: '#fff',
  43 + fontSize: 12
  44 + },
  45 + lineStyle: {
  46 + width: 3,
  47 + type: 'solid'
  48 + },
  49 + areaStyle: {
  50 + opacity: 0.8,
  51 + color: new graphic.LinearGradient(0, 0, 0, 1, [
  52 + {
  53 + offset: 0,
  54 + color: chartColorsSearch[defaultTheme][3]
  55 + },
  56 + {
  57 + offset: 1,
  58 + color: 'rgba(0,0,0,0)'
  59 + }
  60 + ])
  61 + }
  62 + },
  63 + {
  64 + type: 'line',
  65 + smooth: false,
  66 + label: {
  67 + show: true,
  68 + position: 'top',
  69 + color: '#fff',
  70 + fontSize: 12
  71 + },
  72 + lineStyle: {
  73 + width: 3,
  74 + type: 'solid'
  75 + },
  76 + areaStyle: {
  77 + opacity: 0.8,
  78 + color: new graphic.LinearGradient(0, 0, 0, 1, [
  79 + {
  80 + offset: 0,
  81 + color: chartColorsSearch[defaultTheme][4]
  82 + },
  83 + {
  84 + offset: 1,
  85 + color: 'rgba(0,0,0,0)'
  86 + }
  87 + ])
  88 + }
  89 + }
  90 + ]
  91 +}
  92 +
  93 +export default class Config extends PublicConfigClass implements CreateComponentType {
  94 + public key: string = OverrideLineGradientsConfig.key
  95 + public chartConfig = cloneDeep(OverrideLineGradientsConfig)
  96 + // 图表配置项
  97 + public option = echartOptionProfixHandle(option, includes)
  98 +}
  1 +<template>
  2 + <!-- Echarts 全局设置 -->
  3 + <global-setting :optionData="optionData"></global-setting>
  4 + <CollapseItem
  5 + v-for="(item, index) in seriesList"
  6 + :key="index"
  7 + name="单折线面积图"
  8 + :expanded="true"
  9 + >
  10 + <SettingItemBox name="线条">
  11 + <SettingItem name="宽度">
  12 + <n-input-number
  13 + v-model:value="item.lineStyle.width"
  14 + :min="1"
  15 + size="small"
  16 + placeholder="自动计算"
  17 + ></n-input-number>
  18 + </SettingItem>
  19 + <SettingItem name="类型">
  20 + <n-select
  21 + v-model:value="item.lineStyle.type"
  22 + size="small"
  23 + :options="lineConf.lineStyle.type"
  24 + ></n-select>
  25 + </SettingItem>
  26 + </SettingItemBox>
  27 + <SettingItemBox name="动画" :alone="true">
  28 + <SettingItem>
  29 + <n-space>
  30 + <n-switch v-model:value="optionData.isCarousel" size="small"></n-switch>
  31 + <n-text>开启<n-text :depth="3">(将自动隐藏图例)</n-text></n-text>
  32 + </n-space>
  33 + </SettingItem>
  34 + <SettingItem>
  35 + <n-text :depth="3">无鼠标点击图例场景时,可强行打开图例</n-text>
  36 + </SettingItem>
  37 + </SettingItemBox>
  38 + <SettingItemBox name="实心点">
  39 + <SettingItem name="大小">
  40 + <n-input-number
  41 + v-model:value="item.symbolSize"
  42 + :min="1"
  43 + :max="100"
  44 + size="small"
  45 + placeholder="自动计算"
  46 + ></n-input-number>
  47 + </SettingItem>
  48 + </SettingItemBox>
  49 + <setting-item-box name="标签">
  50 + <setting-item>
  51 + <n-space>
  52 + <n-switch v-model:value="item.label.show" size="small" />
  53 + <n-text>展示标签</n-text>
  54 + </n-space>
  55 + </setting-item>
  56 + <setting-item name="大小">
  57 + <n-input-number
  58 + v-model:value="item.label.fontSize"
  59 + size="small"
  60 + :min="1"
  61 + ></n-input-number>
  62 + </setting-item>
  63 + <setting-item name="颜色">
  64 + <n-color-picker
  65 + size="small"
  66 + :modes="['hex']"
  67 + v-model:value="item.label.color"
  68 + ></n-color-picker>
  69 + </setting-item>
  70 + <setting-item name="位置">
  71 + <n-select
  72 + v-model:value="item.label.position"
  73 + :options="[
  74 + { label: 'top', value: 'top' },
  75 + { label: 'left', value: 'left' },
  76 + { label: 'right', value: 'right' },
  77 + { label: 'bottom', value: 'bottom' },
  78 + ]"
  79 + />
  80 + </setting-item>
  81 + </setting-item-box>
  82 + </CollapseItem>
  83 +</template>
  84 +
  85 +<script setup lang="ts">
  86 +import { PropType, computed } from 'vue'
  87 +import { lineConf } from '@/packages/chartConfiguration/echarts/index'
  88 +import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
  89 +import {
  90 + GlobalSetting,
  91 + CollapseItem,
  92 + SettingItemBox,
  93 + SettingItem
  94 +} from '@/components/Pages/ChartItemSetting'
  95 +
  96 +const props = defineProps({
  97 + optionData: {
  98 + type: Object as PropType<GlobalThemeJsonType>,
  99 + required: true
  100 + },
  101 +})
  102 +
  103 +const seriesList = computed(() => {
  104 + return props.optionData.series
  105 +})
  106 +</script>
  1 +{
  2 + "dimensions": ["product", "data1", "data2"],
  3 + "source": [
  4 + {
  5 + "product": "Mon",
  6 + "data1": 120,
  7 + "data2": 130
  8 + },
  9 + {
  10 + "product": "Tue",
  11 + "data1": 200,
  12 + "data2": 130
  13 + },
  14 + {
  15 + "product": "Wed",
  16 + "data1": 150,
  17 + "data2": 312
  18 + },
  19 + {
  20 + "product": "Thu",
  21 + "data1": 80,
  22 + "data2": 268
  23 + },
  24 + {
  25 + "product": "Fri",
  26 + "data1": 70,
  27 + "data2": 155
  28 + },
  29 + {
  30 + "product": "Sat",
  31 + "data1": 110,
  32 + "data2": 117
  33 + },
  34 + {
  35 + "product": "Sun",
  36 + "data1": 130,
  37 + "data2": 160
  38 + }
  39 + ]
  40 +}
  1 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  2 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Charts/index.d'
  3 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  4 +
  5 +const { key, conKey, chartKey } = useWidgetKey('OverrideLineGradients', true)
  6 +
  7 +export const OverrideLineGradientsConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '双折线渐变面积图动画',
  12 + category: ChatCategoryEnum.LINE,
  13 + categoryName: ChatCategoryEnumName.LINE,
  14 + package: PackagesCategoryEnum.CHARTS,
  15 + chartFrame: ChartFrameEnum.ECHARTS,
  16 + image: 'line_gradient.png'
  17 +}
  1 +<template>
  2 + <v-chart
  3 + ref="vChartRef"
  4 + :init-options="initOptions"
  5 + :theme="themeColor"
  6 + :option="option.value"
  7 + :manual-update="isPreview()"
  8 + autoresize
  9 + @mouseover="handleHighlight"
  10 + @mouseout="handleDownplay"
  11 + ></v-chart>
  12 +</template>
  13 +
  14 +<script setup lang="ts">
  15 +import { reactive, watch, PropType, onMounted } from 'vue'
  16 +import VChart from 'vue-echarts'
  17 +import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
  18 +import { use, graphic } from 'echarts/core'
  19 +import { CanvasRenderer } from 'echarts/renderers'
  20 +import { LineChart } from 'echarts/charts'
  21 +import config, { includes } from './config'
  22 +import { mergeTheme } from '@/packages/public/chart'
  23 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  24 +import { chartColorsSearch, defaultTheme } from '@/settings/chartThemes/index'
  25 +import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
  26 +import { useChartDataFetch } from '@/hooks'
  27 +import { isPreview, colorGradientCustomMerge } from '@/utils'
  28 +import dataJson from './data.json'
  29 +import { isFullScreen } from '../../utls/isFullScreen'
  30 +
  31 +const props = defineProps({
  32 + themeSetting: {
  33 + type: Object,
  34 + required: true
  35 + },
  36 + themeColor: {
  37 + type: Object,
  38 + required: true
  39 + },
  40 + chartConfig: {
  41 + type: Object as PropType<config>,
  42 + required: true
  43 + }
  44 +})
  45 +
  46 +const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
  47 +
  48 +use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponent, LegendComponent])
  49 +const chartEditStore = useChartEditStore()
  50 +
  51 +const option = reactive({
  52 + value: {}
  53 +})
  54 +const toolBoxOption = {
  55 + show: true,
  56 + right: 20,
  57 + feature: {
  58 + myFullButton: {
  59 + show: true,
  60 + title: '全屏查看',
  61 + icon: 'path://M733.549304 0l116.434359 116.23452-226.402521 226.40252 57.053835 57.068109 226.459617-226.445342 120.616689 120.41685V0H733.549304zM689.513507 619.855586l-57.068108 57.068109 224.232847 224.232847-122.64362 122.843458h293.676657V729.838022l-114.007751 114.207588-224.190025-224.190024zM338.197775 404.144414l57.068109-57.068109L171.033037 122.843458 293.676657 0H0v294.161978l114.022025-114.207588 224.17575 224.190024zM347.076305 624.294851L120.616689 850.754468 0 730.323343v293.676657h294.161978l-116.420084-116.23452 226.40252-226.40252-57.068109-57.068109z',
  62 + onclick: () => {
  63 + const getEchartDom = vChartRef.value?.getDom()
  64 + const domName = document.getElementById(getEchartDom.id) as any
  65 + const htmlName = document.querySelector('html') as any
  66 + isFullScreen(domName, htmlName)
  67 + }
  68 + }
  69 + }
  70 +}
  71 +props.chartConfig.option = {
  72 + ...props.chartConfig.option,
  73 + ...{ toolbox: toolBoxOption }
  74 +}
  75 +
  76 +// 渐变色处理
  77 +watch(
  78 + () => chartEditStore.getEditCanvasConfig.chartThemeColor,
  79 + (newColor: keyof typeof chartColorsSearch) => {
  80 + try {
  81 + if (!isPreview()) {
  82 + const themeColor =
  83 + colorGradientCustomMerge(chartEditStore.getEditCanvasConfig.chartCustomThemeColorInfo)[newColor] ||
  84 + colorGradientCustomMerge(chartEditStore.getEditCanvasConfig.chartCustomThemeColorInfo)[defaultTheme]
  85 + props.chartConfig.option.series.forEach((value: any, index: number) => {
  86 + value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
  87 + {
  88 + offset: 0,
  89 + color: themeColor[3 + index]
  90 + },
  91 + {
  92 + offset: 1,
  93 + color: 'rgba(0,0,0, 0)'
  94 + }
  95 + ])
  96 + })
  97 + }
  98 + option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
  99 + props.chartConfig.option = option.value
  100 + } catch (error) {
  101 + console.log(error)
  102 + }
  103 + },
  104 + {
  105 + immediate: true
  106 + }
  107 +)
  108 +
  109 +watch(
  110 + () => props.chartConfig.option.dataset,
  111 + () => {
  112 + option.value = props.chartConfig.option
  113 + }
  114 +)
  115 +let seriesDataNum = -1
  116 +let seriesDataMaxLength = 0
  117 +let intervalInstance: any = null
  118 +const duration = 1500
  119 +
  120 +// 会重新选择需要选中和展示的数据
  121 +const handleSeriesData = () => {
  122 + if (seriesDataNum > -1) {
  123 + vChartRef.value?.dispatchAction({
  124 + type: 'downplay',
  125 + dataIndex: seriesDataNum
  126 + })
  127 + }
  128 + seriesDataNum = seriesDataNum >= seriesDataMaxLength - 1 ? 0 : seriesDataNum + 1
  129 + vChartRef.value?.dispatchAction({
  130 + type: 'showTip',
  131 + seriesIndex: 0,
  132 + dataIndex: seriesDataNum
  133 + })
  134 +}
  135 +
  136 +// 新增轮播
  137 +const addPieInterval = (newData?: typeof dataJson, skipPre = false) => {
  138 + if (!skipPre && !Array.isArray(newData?.source)) return
  139 + if (!skipPre) seriesDataMaxLength = newData?.source.length || 0
  140 + clearInterval(intervalInstance)
  141 + intervalInstance = setInterval(() => {
  142 + handleSeriesData()
  143 + }, duration)
  144 +}
  145 +
  146 +// 取消轮播
  147 +const clearPieInterval = () => {
  148 + vChartRef.value?.dispatchAction({
  149 + type: 'hideTip',
  150 + seriesIndex: 0,
  151 + dataIndex: seriesDataNum
  152 + })
  153 + vChartRef.value?.dispatchAction({
  154 + type: 'downplay',
  155 + dataIndex: seriesDataNum
  156 + })
  157 + clearInterval(intervalInstance)
  158 + intervalInstance = null
  159 +}
  160 +
  161 +// 处理鼠标聚焦高亮内容
  162 +const handleHighlight = () => {
  163 + clearPieInterval()
  164 +}
  165 +
  166 +// 处理鼠标取消悬浮
  167 +const handleDownplay = () => {
  168 + if (props.chartConfig.option.isCarousel && !intervalInstance) {
  169 + // 恢复轮播
  170 + addPieInterval(undefined, true)
  171 + }
  172 +}
  173 +
  174 +watch(
  175 + () => props.chartConfig.option.isCarousel,
  176 + newData => {
  177 + if (newData) {
  178 + addPieInterval(undefined, true)
  179 + props.chartConfig.option.legend.show = false
  180 + } else {
  181 + props.chartConfig.option.legend.show = true
  182 + clearPieInterval()
  183 + }
  184 + }
  185 +)
  186 +
  187 +const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: typeof dataJson) => {
  188 + // addPieInterval(newData)
  189 +})
  190 +
  191 +onMounted(() => {
  192 + seriesDataMaxLength = dataJson.source.length
  193 + if (props.chartConfig.option.isCarousel) {
  194 + addPieInterval(undefined, true)
  195 + }
  196 +})
  197 +</script>
  1 +export const isFullScreen = (domName: any, htmlName: any) => {
  2 + const isFullScreen = document.fullscreenElement
  3 + const currentDatkTheme = htmlName.getAttribute('data-theme')
  4 +
  5 + if (isFullScreen) {
  6 + console.log('退出全屏')
  7 + if (document.exitFullscreen) {
  8 + document.exitFullscreen()
  9 + domName.style.background = ''
  10 + }
  11 + } else {
  12 + console.log('进入全屏')
  13 + if (domName.requestFullscreen) {
  14 + domName.requestFullscreen()
  15 + domName.style.background = currentDatkTheme === 'light' ? 'white' : '#18181c'
  16 + } else if (domName.mozRequestFullScreen) {
  17 + domName.mozRequestFullScreen()
  18 + domName.style.background = currentDatkTheme === 'light' ? 'white' : '#18181c'
  19 + } else if (domName.webkitRequestFullscreen) {
  20 + domName.webkitRequestFullscreen()
  21 + domName.style.background = currentDatkTheme === 'light' ? 'white' : '#18181c'
  22 + } else if (domName.msRequestFullscreen) {
  23 + domName.msRequestFullscreen()
  24 + domName.style.background = currentDatkTheme === 'light' ? 'white' : '#18181c'
  25 + }
  26 + }
  27 +}
@@ -13,11 +13,6 @@ @@ -13,11 +13,6 @@
13 </n-button> 13 </n-button>
14 </setting-item> 14 </setting-item>
15 </setting-item-box> 15 </setting-item-box>
16 - <setting-item-box name="排布">  
17 - <setting-item>  
18 - <n-select v-model:value="optionData.displayMode" size="small" :options="displayModeOptions"></n-select>  
19 - </setting-item>  
20 - </setting-item-box>  
21 </CollapseItem> 16 </CollapseItem>
22 </template> 17 </template>
23 18
@@ -32,20 +27,4 @@ defineProps({ @@ -32,20 +27,4 @@ defineProps({
32 required: true 27 required: true
33 } 28 }
34 }) 29 })
35 -  
36 -// 旋转方式  
37 -const displayModeOptions = [  
38 - {  
39 - value: 'singleGrid',  
40 - label: '默认'  
41 - },  
42 - {  
43 - value: 'fourGrid',  
44 - label: '四宫格'  
45 - },  
46 - {  
47 - value: 'nineGrid',  
48 - label: '九宫格'  
49 - }  
50 -]  
51 </script> 30 </script>
@@ -8,7 +8,7 @@ export const CameraConfig: ConfigType = { @@ -8,7 +8,7 @@ export const CameraConfig: ConfigType = {
8 key, 8 key,
9 chartKey, 9 chartKey,
10 conKey, 10 conKey,
11 - title: '摄像头', 11 + title: '多个摄像头',
12 category: ChatCategoryEnum.MORE, 12 category: ChatCategoryEnum.MORE,
13 categoryName: ChatCategoryEnumName.MORE, 13 categoryName: ChatCategoryEnumName.MORE,
14 package: EPackagesCategoryEnum.COMPOSES, 14 package: EPackagesCategoryEnum.COMPOSES,
  1 +<template>
  2 + <video
  3 + crossOrigin="anonymous"
  4 + :id="`my-player${index}`"
  5 + ref="videoRef"
  6 + class="video-js my-video vjs-theme-city vjs-big-play-centered"
  7 + >
  8 + <source :src="sourceSrc" />
  9 + </video>
  10 +</template>
  11 +<script setup lang="ts">
  12 +import { onMounted, ref, onUnmounted, watch } from 'vue'
  13 +import videojs from 'video.js'
  14 +import type { VideoJsPlayerOptions } from 'video.js'
  15 +import 'video.js/dist/video-js.min.css'
  16 +
  17 +const props = defineProps({
  18 + sourceSrc: {
  19 + type: String
  20 + },
  21 + name: {
  22 + type: String
  23 + },
  24 + avatar: {
  25 + type: String
  26 + },
  27 + index: {
  28 + type: Number
  29 + }
  30 +})
  31 +
  32 +// video标签
  33 +const videoRef = ref<HTMLElement | null>(null)
  34 +
  35 +// video实例对象
  36 +let videoPlayer: videojs.Player | null = null
  37 +
  38 +//options配置
  39 +const options: VideoJsPlayerOptions = {
  40 + language: 'zh-CN', // 设置语言
  41 + controls: true, // 是否显示控制条
  42 + preload: 'auto', // 预加载
  43 + autoplay: true, // 是否自动播放
  44 + fluid: false, // 自适应宽高
  45 + poster: props?.avatar || '',
  46 + src: props?.sourceSrc || '', // 要嵌入的视频源的源 URL
  47 + muted: true,
  48 + userActions: {
  49 + hotkeys: true
  50 + }
  51 +}
  52 +
  53 +// 初始化videojs
  54 +const initVideo = () => {
  55 + if (videoRef.value) {
  56 + // 创建 video 实例
  57 + videoPlayer = videojs(videoRef.value, options)
  58 + }
  59 +}
  60 +
  61 +watch(
  62 + () => props.sourceSrc,
  63 + (newData: any) => {
  64 + // props.sourceSrc = newData
  65 + videoPlayer?.src(newData) as any
  66 + videoPlayer?.play()
  67 + },
  68 + {
  69 + immediate: true
  70 + }
  71 +)
  72 +
  73 +onMounted(() => {
  74 + initVideo()
  75 +})
  76 +
  77 +onUnmounted(() => {
  78 + handleVideoDispose()
  79 +})
  80 +
  81 +//播放
  82 +const handleVideoPlay = () => videoPlayer?.play()
  83 +
  84 +const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause()
  85 +//暂停
  86 +defineExpose({
  87 + handleVideoPlay,
  88 + handleVideoDispose
  89 +})
  90 +</script>
  91 +
  92 +<style lang="scss" scoped>
  93 +.my-video {
  94 + width: 100%;
  95 + height: 100%;
  96 +}
  97 +</style>
  1 +import CameraItem from './CameraItem.vue'
  2 +
  3 +export { CameraItem }
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { SingleCameraConfig } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export const option = {
  7 + dataset: [
  8 + {
  9 + url: ''
  10 + }
  11 + ] as any,
  12 + // 自动播放的间隔(ms)
  13 + interval: 5000,
  14 + autoplay: true,
  15 + effect: 'slide',
  16 + displayMode: 'singleGrid'
  17 +}
  18 +
  19 +export default class Config extends PublicConfigClass implements CreateComponentType {
  20 + public key = SingleCameraConfig.key
  21 + public chartConfig = cloneDeep(SingleCameraConfig)
  22 + public option = cloneDeep(option)
  23 +}
  1 +<template>
  2 + <CollapseItem name="播放器配置" :expanded="true">
  3 + <setting-item-box name="源地址" :alone="true">
  4 + <setting-item v-for="(item, index) in optionData.dataset" :key="index">
  5 + <n-input-group>
  6 + <n-input v-model:value="item.url" size="small" placeholder="请输入源地址"></n-input>
  7 + </n-input-group>
  8 + </setting-item>
  9 + </setting-item-box>
  10 + </CollapseItem>
  11 +</template>
  12 +
  13 +<script setup lang="ts">
  14 +import { PropType } from 'vue'
  15 +import { option } from './config'
  16 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  17 +
  18 +defineProps({
  19 + optionData: {
  20 + type: Object as PropType<typeof option>,
  21 + required: true
  22 + }
  23 +})
  24 +
  25 +</script>
  1 +import { ChartFrameEnum, ConfigType } from '@/packages/index.d'
  2 +import { EPackagesCategoryEnum } from '@/packages/components/external/types'
  3 +import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
  4 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  5 +
  6 +const { key, chartKey, conKey } = useWidgetKey('SingleCamera')
  7 +export const SingleCameraConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '单个摄像头',
  12 + category: ChatCategoryEnum.MORE,
  13 + categoryName: ChatCategoryEnumName.MORE,
  14 + package: EPackagesCategoryEnum.COMPOSES,
  15 + chartFrame: ChartFrameEnum.NAIVE_UI,
  16 + image: 'camera.png'
  17 +}
  1 +<template>
  2 + <div class="banner-box" ref="root">
  3 + <n-grid x-gap="12" :y-gap="12" :cols="computedCols">
  4 + <n-gi v-for="(item, index) in option.dataset" :key="index + item">
  5 + <div class="camera-container">
  6 + <CameraItem
  7 + ref="cameraRef"
  8 + :name="item.name"
  9 + :avatar="item.avatar"
  10 + :key="item + index"
  11 + :sourceSrc="item.url"
  12 + :index="index"
  13 + />
  14 + </div>
  15 + </n-gi>
  16 + </n-grid>
  17 + </div>
  18 +</template>
  19 +<script setup lang="ts">
  20 +import { PropType, toRefs, watch, shallowReactive, ref, computed } from 'vue'
  21 +import { CreateComponentType } from '@/packages/index.d'
  22 +import 'video.js/dist/video-js.min.css'
  23 +import { option as configOption } from './config'
  24 +import { CameraItem } from './components'
  25 +
  26 +const props = defineProps({
  27 + chartConfig: {
  28 + type: Object as PropType<CreateComponentType>,
  29 + required: true
  30 + }
  31 +})
  32 +
  33 +const { h } = toRefs(props.chartConfig.attr)
  34 +
  35 +const responsiveComputeValue = ref(0)
  36 +
  37 +const option = shallowReactive({
  38 + dataset: configOption.dataset
  39 +})
  40 +
  41 +const computedCols = computed(() => {
  42 + if (option.dataset.length <= 1) return 1
  43 + if (option.dataset.length <= 4) return 2
  44 + return 3
  45 +})
  46 +
  47 +const cameraRef = ref<InstanceType<typeof CameraItem>>()
  48 +
  49 +const responsive = (value: number) => {
  50 + responsiveComputeValue.value = value
  51 + if (option.dataset.length <= 2) responsiveComputeValue.value = value
  52 + if (option.dataset.length > 2 && option.dataset.length <= 4) responsiveComputeValue.value = value / 2.03
  53 + if (option.dataset.length > 4 && option.dataset.length <= 9) responsiveComputeValue.value = value / 3.1
  54 +}
  55 +
  56 +watch(
  57 + () => props.chartConfig.option.dataset,
  58 + newData => {
  59 + option.dataset = newData
  60 + responsive(h.value)
  61 + },
  62 + {
  63 + immediate: true,
  64 + deep: true
  65 + }
  66 +)
  67 +
  68 +watch(
  69 + () => h.value,
  70 + newData => responsive(newData),
  71 + {
  72 + immediate: true
  73 + }
  74 +)
  75 +</script>
  76 +
  77 +<style lang="scss" scoped>
  78 +.banner-box {
  79 + .camera-container {
  80 + height: v-bind('`${responsiveComputeValue}px`');
  81 + }
  82 +}
  83 +</style>
@@ -2,6 +2,7 @@ import { Title1Config } from './Title1/index' @@ -2,6 +2,7 @@ import { Title1Config } from './Title1/index'
2 import { Title2Config } from './Title2/index' 2 import { Title2Config } from './Title2/index'
3 import { Title3Config } from './Title3/index' 3 import { Title3Config } from './Title3/index'
4 import { CameraConfig } from './Camera/index' 4 import { CameraConfig } from './Camera/index'
  5 +import { SingleCameraConfig } from './SingleCamera/index'
5 import { ThreeDimensionalConfig } from './ThreeDimensional/index' 6 import { ThreeDimensionalConfig } from './ThreeDimensional/index'
6 7
7 -export default [Title1Config, Title2Config, Title3Config, CameraConfig, ThreeDimensionalConfig] 8 +export default [Title1Config, Title2Config, Title3Config, CameraConfig, SingleCameraConfig, ThreeDimensionalConfig]
@@ -23,19 +23,19 @@ export const enum areaEnum { @@ -23,19 +23,19 @@ export const enum areaEnum {
23 export const weatherTextMapImg = [ 23 export const weatherTextMapImg = [
24 { 24 {
25 text: '晴', 25 text: '晴',
26 - img: '/large-designer/src/assets/external/weather/clearDay.png' 26 + img: 'src/assets/external/weather/clearDay.png'
27 }, 27 },
28 { 28 {
29 text: '多云', 29 text: '多云',
30 - img: '/large-designer/src/assets/external/weather/cloudy.png' 30 + img: 'src/assets/external/weather/cloudy.png'
31 }, 31 },
32 { 32 {
33 text: '阴', 33 text: '阴',
34 - img: '/large-designer/src/assets/external/weather/cloudyDay.png' 34 + img: 'src/assets/external/weather/cloudyDay.png'
35 }, 35 },
36 { 36 {
37 text: '小雨', 37 text: '小雨',
38 - img: '/large-designer/src/assets/external/weather/lightRain.png' 38 + img: 'src/assets/external/weather/lightRain.png'
39 } 39 }
40 ] 40 ]
41 41
@@ -4,7 +4,7 @@ export const useUtils = () => { @@ -4,7 +4,7 @@ export const useUtils = () => {
4 const loadWeatherImg = (text: string) => { 4 const loadWeatherImg = (text: string) => {
5 return ( 5 return (
6 weatherTextMapImg.find((item: any) => item.text === text)?.img || 6 weatherTextMapImg.find((item: any) => item.text === text)?.img ||
7 - '/large-designer/src/assets/external/weather/clearDay.png' 7 + '/src/assets/external/weather/clearDay.png'
8 ) 8 )
9 } 9 }
10 10
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { OverrideTextCommonConfig } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export enum WritingModeEnum {
  7 + HORIZONTAL = '水平',
  8 + VERTICAL = '垂直'
  9 +}
  10 +
  11 +export const WritingModeObject = {
  12 + [WritingModeEnum.HORIZONTAL]: 'horizontal-tb',
  13 + [WritingModeEnum.VERTICAL]: 'vertical-rl'
  14 +}
  15 +
  16 +export enum FontWeightEnum {
  17 + NORMAL = '常规',
  18 + BOLD = '加粗'
  19 +}
  20 +
  21 +export const FontWeightObject = {
  22 + [FontWeightEnum.NORMAL]: 'normal',
  23 + [FontWeightEnum.BOLD]: 'bold'
  24 +}
  25 +
  26 +export const option = {
  27 + link: '',
  28 + linkHead: 'http://',
  29 + dataset: '我是文本',
  30 + fontSize: 20,
  31 + fontColor: '#ffffff',
  32 + paddingX: 10,
  33 + paddingY: 10,
  34 + textAlign: 'center', // 水平对齐方式
  35 + fontWeight: 'normal',
  36 +
  37 + // 边框
  38 + borderWidth: 0,
  39 + borderColor: '#ffffff',
  40 + borderRadius: 5,
  41 +
  42 + // 字间距
  43 + letterSpacing: 5,
  44 + writingMode: 'horizontal-tb',
  45 + backgroundColor: '#00000000',
  46 +
  47 + //跳转方式
  48 + linkMethod: 'default'
  49 +}
  50 +
  51 +export default class Config extends PublicConfigClass implements CreateComponentType {
  52 + public key = OverrideTextCommonConfig.key
  53 + public chartConfig = cloneDeep(OverrideTextCommonConfig)
  54 + public option = cloneDeep(option)
  55 +}
  1 +<template>
  2 + <collapse-item name="信息" :expanded="true">
  3 + <setting-item-box name="文字" :alone="true">
  4 + <setting-item>
  5 + <n-input v-model:value="optionData.dataset" size="small"></n-input>
  6 + </setting-item>
  7 + </setting-item-box>
  8 + <setting-item-box name="链接" :alone="true">
  9 + <setting-item>
  10 + <n-input-group>
  11 + <n-select
  12 + v-model:value="optionData.linkHead"
  13 + size="small"
  14 + :style="{ width: '80%' }"
  15 + :options="linkHeadOptions"
  16 + />
  17 + <n-input v-model:value="optionData.link" size="small"></n-input>
  18 + </n-input-group>
  19 + </setting-item>
  20 + </setting-item-box>
  21 + <setting-item-box name="跳转方式" :alone="true">
  22 + <setting-item>
  23 + <n-input-group>
  24 + <n-select
  25 + v-model:value="optionData.linkMethod"
  26 + size="small"
  27 + :style="{ width: '80%' }"
  28 + :options="linkMethodOptions"
  29 + />
  30 + <n-button
  31 + :disabled="!optionData.linkMethod"
  32 + secondary
  33 + size="small"
  34 + @click="handleLinkClick(optionData.linkMethod)"
  35 + >跳转</n-button
  36 + >
  37 + </n-input-group>
  38 + </setting-item>
  39 + </setting-item-box>
  40 + </collapse-item>
  41 +
  42 + <collapse-item name="样式" :expanded="true">
  43 + <setting-item-box name="文字">
  44 + <setting-item name="颜色">
  45 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.fontColor"></n-color-picker>
  46 + </setting-item>
  47 + <setting-item name="字体大小">
  48 + <n-input-number v-model:value="optionData.fontSize" size="small" placeholder="字体大小"></n-input-number>
  49 + </setting-item>
  50 + <setting-item name="字体粗细">
  51 + <n-select v-model:value="optionData.fontWeight" size="small" :options="fontWeightOptions" />
  52 + </setting-item>
  53 + <setting-item name="X轴内边距">
  54 + <n-input-number v-model:value="optionData.paddingX" size="small" placeholder="输入内边距"></n-input-number>
  55 + </setting-item>
  56 + <setting-item name="Y轴内边距">
  57 + <n-input-number v-model:value="optionData.paddingY" size="small" placeholder="输入内边距"></n-input-number>
  58 + </setting-item>
  59 +
  60 + <setting-item name="水平对齐">
  61 + <n-select v-model:value="optionData.textAlign" size="small" :options="textAlignOptions" />
  62 + </setting-item>
  63 + <setting-item name="文本方向">
  64 + <n-select v-model:value="optionData.writingMode" size="small" :options="verticalOptions" />
  65 + </setting-item>
  66 +
  67 + <setting-item name="字间距">
  68 + <n-input-number v-model:value="optionData.letterSpacing" size="small" placeholder="输入字间距"></n-input-number>
  69 + </setting-item>
  70 + </setting-item-box>
  71 +
  72 + <setting-item-box name="边框">
  73 + <setting-item name="宽度">
  74 + <n-input-number
  75 + v-model:value="optionData.borderWidth"
  76 + size="small"
  77 + :min="0"
  78 + placeholder="宽度"
  79 + ></n-input-number>
  80 + </setting-item>
  81 + <setting-item name="颜色">
  82 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.borderColor"></n-color-picker>
  83 + </setting-item>
  84 + <setting-item name="圆角">
  85 + <n-input-number
  86 + v-model:value="optionData.borderRadius"
  87 + size="small"
  88 + :min="0"
  89 + placeholder="圆角"
  90 + ></n-input-number>
  91 + </setting-item>
  92 + </setting-item-box>
  93 +
  94 + <setting-item-box name="背景" :alone="true">
  95 + <setting-item name="背景颜色">
  96 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.backgroundColor"></n-color-picker>
  97 + </setting-item>
  98 + </setting-item-box>
  99 + </collapse-item>
  100 +</template>
  101 +
  102 +<script setup lang="ts">
  103 +import { PropType } from 'vue'
  104 +import { option, WritingModeEnum, WritingModeObject, FontWeightEnum, FontWeightObject } from './config'
  105 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  106 +const props = defineProps({
  107 + optionData: {
  108 + type: Object as PropType<typeof option>,
  109 + required: true
  110 + }
  111 +})
  112 +
  113 +const textAlignOptions = [
  114 + { label: '左对齐', value: 'start' },
  115 + { label: '居中', value: 'center' },
  116 + { label: '右对齐', value: 'end' }
  117 +]
  118 +
  119 +const linkMethodOptions = [
  120 + { label: '默认', value: 'default' },
  121 + { label: '新开窗口', value: 'open' }
  122 +]
  123 +
  124 +const verticalOptions = [
  125 + {
  126 + label: WritingModeEnum.HORIZONTAL,
  127 + value: WritingModeObject[WritingModeEnum.HORIZONTAL]
  128 + },
  129 + {
  130 + label: WritingModeEnum.VERTICAL,
  131 + value: WritingModeObject[WritingModeEnum.VERTICAL]
  132 + }
  133 +]
  134 +const fontWeightOptions = [
  135 + {
  136 + label: FontWeightEnum.NORMAL,
  137 + value: FontWeightObject[FontWeightEnum.NORMAL]
  138 + },
  139 + {
  140 + label: FontWeightEnum.BOLD,
  141 + value: FontWeightObject[FontWeightEnum.BOLD]
  142 + }
  143 +]
  144 +const handleLinkClick = (target: string) => {
  145 + const hrefStr = props.optionData.linkHead + props.optionData.link
  146 + if (target === 'open') window.open(hrefStr)
  147 + else window.location.href = hrefStr
  148 +}
  149 +const linkHeadOptions = [
  150 + { label: 'http://', value: 'http://' },
  151 + { label: 'https://', value: 'https://' }
  152 +]
  153 +</script>
  1 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  2 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Informations/index.d'
  3 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  4 +
  5 +const { key, conKey, chartKey } = useWidgetKey('OverrideTextCommon', true)
  6 +
  7 +export const OverrideTextCommonConfig: ConfigType = {
  8 + key,
  9 + chartKey,
  10 + conKey,
  11 + title: '自定义文字',
  12 + category: ChatCategoryEnum.MORE,
  13 + categoryName: ChatCategoryEnumName.MORE,
  14 + package: PackagesCategoryEnum.INFORMATIONS,
  15 + chartFrame: ChartFrameEnum.COMMON,
  16 + image: 'text_static.png'
  17 +}
  1 +<template>
  2 + <div class="go-text-box">
  3 + <div class="content">
  4 + <span style="cursor: pointer" v-if="link" @click="click">{{ option.dataset }}</span>
  5 + <span v-else>{{ option.dataset }}</span>
  6 + </div>
  7 + </div>
  8 +</template>
  9 +
  10 +<script setup lang="ts">
  11 +import { PropType, toRefs, shallowReactive, watch } from 'vue'
  12 +import { CreateComponentType } from '@/packages/index.d'
  13 +import { useChartDataFetch } from '@/hooks'
  14 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  15 +import { option as configOption } from './config'
  16 +
  17 +const props = defineProps({
  18 + chartConfig: {
  19 + type: Object as PropType<CreateComponentType & typeof option>,
  20 + required: true
  21 + }
  22 +})
  23 +
  24 +const {
  25 + linkHead,
  26 + link,
  27 + fontColor,
  28 + fontSize,
  29 + letterSpacing,
  30 + paddingY,
  31 + paddingX,
  32 + textAlign,
  33 + borderWidth,
  34 + borderColor,
  35 + borderRadius,
  36 + writingMode,
  37 + backgroundColor,
  38 + fontWeight,
  39 + linkMethod
  40 +} = toRefs(props.chartConfig.option)
  41 +
  42 +const option = shallowReactive({
  43 + dataset: configOption.dataset
  44 +})
  45 +
  46 +// 手动更新
  47 +watch(
  48 + () => props.chartConfig.option.dataset,
  49 + (newData: any) => {
  50 + option.dataset = newData
  51 + },
  52 + {
  53 + immediate: true,
  54 + deep: false
  55 + }
  56 +)
  57 +
  58 +// 预览更新
  59 +useChartDataFetch(props.chartConfig, useChartEditStore, (newData: string) => {
  60 + option.dataset = newData
  61 +})
  62 +
  63 +//打开链接
  64 +const click = () => {
  65 + const hrefStr = linkHead.value + link.value
  66 + if (linkMethod.value === 'open') window.open(hrefStr)
  67 + else window.location.href = hrefStr
  68 +}
  69 +</script>
  70 +
  71 +<style lang="scss" scoped>
  72 +@include go('text-box') {
  73 + display: flex;
  74 + align-items: center;
  75 + justify-content: v-bind('textAlign');
  76 +
  77 + .content {
  78 + color: v-bind('fontColor');
  79 + padding: v-bind('`${paddingY}px ${paddingX}px`');
  80 + font-size: v-bind('fontSize + "px"');
  81 + letter-spacing: v-bind('letterSpacing + "px"');
  82 + writing-mode: v-bind('writingMode');
  83 + font-weight: v-bind('fontWeight');
  84 + border-style: solid;
  85 + border-width: v-bind('borderWidth + "px"');
  86 + border-radius: v-bind('borderRadius + "px"');
  87 + border-color: v-bind('borderColor');
  88 +
  89 + background-color: v-bind('backgroundColor');
  90 + }
  91 +}
  92 +</style>
@@ -8,6 +8,10 @@ import { OverrideCarouselConfig } from '@/packages/components/external/Informati @@ -8,6 +8,10 @@ import { OverrideCarouselConfig } from '@/packages/components/external/Informati
8 import { OverrideSelectConfig } from '@/packages/components/external/Informations/Mores/OverrideSelect' 8 import { OverrideSelectConfig } from '@/packages/components/external/Informations/Mores/OverrideSelect'
9 import { OverrideInputsDateConfig } from '@/packages/components/external/Informations/Mores/OverrideInputsDate' 9 import { OverrideInputsDateConfig } from '@/packages/components/external/Informations/Mores/OverrideInputsDate'
10 import { OverrideInputsTabConfig } from '@/packages/components/external/Informations/Mores/OverrideInputsTab' 10 import { OverrideInputsTabConfig } from '@/packages/components/external/Informations/Mores/OverrideInputsTab'
  11 +import { OverrideTextCommonConfig } from '@/packages/components/external/Informations/Mores/OverrideTextCommon'
  12 +import { OverrideBarCommonConfig } from '@/packages/components/external/Charts/Bars/OverrideBarCommon'
  13 +import { OverrideLineCommonConfig } from '@/packages/components/external/Charts/Lines/OverrideLineCommon'
  14 +import { OverrideLineGradientsConfig } from '@/packages/components/external/Charts/Lines/OverrideLineGradients'
11 15
12 export function useInjectLib(packagesList: EPackagesType) { 16 export function useInjectLib(packagesList: EPackagesType) {
13 17
@@ -20,6 +24,10 @@ export function useInjectLib(packagesList: EPackagesType) { @@ -20,6 +24,10 @@ export function useInjectLib(packagesList: EPackagesType) {
20 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideSelectConfig) 24 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideSelectConfig)
21 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideInputsDateConfig) 25 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideInputsDateConfig)
22 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideInputsTabConfig) 26 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideInputsTabConfig)
  27 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideTextCommonConfig)
  28 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideBarCommonConfig)
  29 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineCommonConfig)
  30 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.CHARTS, OverrideLineGradientsConfig)
23 } 31 }
24 32
25 /** 33 /**
@@ -69,10 +69,17 @@ export const useSocketStore = defineStore({ @@ -69,10 +69,17 @@ export const useSocketStore = defineStore({
69 }) 69 })
70 } else { 70 } else {
71 const keysRecord: Record<string, KeyBoundComponentList[]> = {} 71 const keysRecord: Record<string, KeyBoundComponentList[]> = {}
72 -  
73 - keys.forEach(key => { 72 + /**这里修改自定义tab切换传的单个属性问题
  73 + * ft
  74 + * 源代码 keys.forEach
  75 + * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys overrideKeys.forEach
  76 + */
  77 + const overrideKeys = typeof keys==="string"?[keys]: keys
  78 +
  79 + overrideKeys.forEach(key => {
74 Reflect.set(keysRecord, key, [{ componentId }]) 80 Reflect.set(keysRecord, key, [{ componentId }])
75 }) 81 })
  82 + //ft
76 Reflect.set(this.connectionPool, entityId, keysRecord) 83 Reflect.set(this.connectionPool, entityId, keysRecord)
77 } 84 }
78 return this.refreshSubscribedMessage(entityId) 85 return this.refreshSubscribedMessage(entityId)
@@ -177,15 +184,22 @@ export const useSocketStore = defineStore({ @@ -177,15 +184,22 @@ export const useSocketStore = defineStore({
177 */ 184 */
178 getNeedUpdateComponentsIdBySubscribeId(subscribeId: number, keys: string[]) { 185 getNeedUpdateComponentsIdBySubscribeId(subscribeId: number, keys: string[]) {
179 const entityId = this.subscribePool.find(item => item.subscribeId === subscribeId)?.entityId 186 const entityId = this.subscribePool.find(item => item.subscribeId === subscribeId)?.entityId
  187 + /**这里修改自定义tab切换传的单个属性问题
  188 + * ft
  189 + * 源代码 keys.map(key => keysRecord[key])
  190 + * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys overrideKeys.map(key => keysRecord[key])
  191 + */
  192 + const overrideKeys = typeof keys==="string"?[keys]: keys
180 193
181 if (entityId) { 194 if (entityId) {
182 const keysRecord = Reflect.get(this.connectionPool, entityId) 195 const keysRecord = Reflect.get(this.connectionPool, entityId)
183 - const needUpdateComponents = keys.map(key => keysRecord[key]) 196 + const needUpdateComponents = overrideKeys.map(key => keysRecord[key])
184 const ids = needUpdateComponents 197 const ids = needUpdateComponents
185 .reduce((prev, next) => [...prev, ...next], []) 198 .reduce((prev, next) => [...prev, ...next], [])
186 .map((item: KeyBoundComponentList) => item.componentId) 199 .map((item: KeyBoundComponentList) => item.componentId)
187 return [...new Set(ids)] 200 return [...new Set(ids)]
188 } 201 }
  202 + //ft
189 }, 203 },
190 204
191 /** 205 /**
@@ -198,7 +212,15 @@ export const useSocketStore = defineStore({ @@ -198,7 +212,15 @@ export const useSocketStore = defineStore({
198 const { request: { requestParams } } = targetComponent 212 const { request: { requestParams } } = targetComponent
199 const { Params } = requestParams 213 const { Params } = requestParams
200 const { keys = [] } = Params 214 const { keys = [] } = Params
201 - const targetComponentBindKeys = keys as unknown as string[] 215 + /**这里修改自定义tab切换传的单个属性问题
  216 + * ft
  217 + * 源代码 keys as unknown as string[]
  218 + * 修改代码 const overrideKeys = typeof keys==="string"?[keys]: keys overrideKeys as unknown as string[]
  219 + */
  220 + const overrideKeys = typeof keys==="string"?[keys]: keys
  221 +
  222 + const targetComponentBindKeys = overrideKeys as unknown as string[]
  223 + //ft
202 224
203 const _value = cloneDeep(value) || { data: {}, latestValues: {} } 225 const _value = cloneDeep(value) || { data: {}, latestValues: {} }
204 _value.data = targetComponentBindKeys.reduce((prev, next) => { 226 _value.data = targetComponentBindKeys.reduce((prev, next) => {
@@ -43,7 +43,22 @@ @@ -43,7 +43,22 @@
43 </n-button> 43 </n-button>
44 </div> 44 </div>
45 </template> 45 </template>
46 - <span>保存</span> 46 + <span>保存内容</span>
  47 + </n-tooltip>
  48 + <!-- 保存缩略图 -->
  49 + <n-tooltip v-if="!isCustomerUser" placement="bottom" trigger="hover">
  50 + <template #trigger>
  51 + <div class="save-btn">
  52 + <n-button size="small" type="primary" ghost @click="thumbnailSyncUpdate()">
  53 + <template #icon>
  54 + <n-icon>
  55 + <Awake />
  56 + </n-icon>
  57 + </template>
  58 + </n-button>
  59 + </div>
  60 + </template>
  61 + <span>保存缩略图</span>
47 </n-tooltip> 62 </n-tooltip>
48 </n-space> 63 </n-space>
49 </n-space> 64 </n-space>
@@ -53,7 +68,7 @@ @@ -53,7 +68,7 @@
53 import { toRefs, Ref, reactive, computed } from 'vue' 68 import { toRefs, Ref, reactive, computed } from 'vue'
54 import { renderIcon, goDialog, goHome } from '@/utils' 69 import { renderIcon, goDialog, goHome } from '@/utils'
55 import { icon } from '@/plugins' 70 import { icon } from '@/plugins'
56 -import { Save } from '@vicons/carbon' 71 +import { Save, Awake } from '@vicons/carbon'
57 import { useRemoveKeyboard } from '../../../hooks/useKeyboard.hook' 72 import { useRemoveKeyboard } from '../../../hooks/useKeyboard.hook'
58 73
59 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' 74 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
@@ -71,7 +86,7 @@ const { setItem } = useChartLayoutStore() @@ -71,7 +86,7 @@ const { setItem } = useChartLayoutStore()
71 const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore()) 86 const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore())
72 const chartEditStore = useChartEditStore() 87 const chartEditStore = useChartEditStore()
73 const chartHistoryStore = useChartHistoryStore() 88 const chartHistoryStore = useChartHistoryStore()
74 -const { dataSyncUpdate } = useSyncRemote() 89 +const { dataSyncUpdate ,thumbnailSyncUpdate } = useSyncRemote()
75 const { isCustomerUser } = useRole() 90 const { isCustomerUser } = useRole()
76 91
77 interface ItemType<T> { 92 interface ItemType<T> {
@@ -70,22 +70,47 @@ export const useSyncRemote = () => { @@ -70,22 +70,47 @@ export const useSyncRemote = () => {
70 } 70 }
71 } 71 }
72 72
73 - // 数据保存  
74 - const dataSyncUpdate = throttle(async (updateImg = true) => { 73 + /**
  74 + * 数据保存和缩略图保存逻辑拆分
  75 + */
  76 +
  77 + //dataSyncUpdate 数据保存
  78 + const dataSyncUpdate = throttle(async () => {
75 if (!fetchRouteParamsLocation()) return 79 if (!fetchRouteParamsLocation()) return
76 80
77 // 客户角色只有查看权限 81 // 客户角色只有查看权限
78 if (unref(isCustomerUser)) return 82 if (unref(isCustomerUser)) return
79 83
80 - const { dataViewId, state, organizationId, dataViewName, dataViewContent } = projectInfoStore.getProjectInfo 84 + const { dataViewId, dataViewName, dataViewContent } = projectInfoStore.getProjectInfo
81 85
82 if (dataViewId === null || dataViewId === '') { 86 if (dataViewId === null || dataViewId === '') {
83 window['$message'].error('数据初未始化成功,请刷新页面!') 87 window['$message'].error('数据初未始化成功,请刷新页面!')
84 return 88 return
85 } 89 }
86 projectInfoStore.setSaveStatus(SyncEnum.START) 90 projectInfoStore.setSaveStatus(SyncEnum.START)
87 - // 异常处理:缩略图上传失败不影响JSON的保存  
88 - try { 91 + // 保存数据
  92 + const saveContent = {
  93 + dataViewContent: {
  94 + id: dataViewContent.id,
  95 + content: JSONStringify(chartEditStore.getStorageInfo || {})
  96 + },
  97 + dataViewName,
  98 + dataViewId
  99 + }
  100 + await contentUpdateApi(saveContent as unknown as BaseUpdateContentParams)
  101 + window['$message'].success('保存成功!')
  102 + // 成功状态
  103 + setTimeout(() => {
  104 + projectInfoStore.setSaveStatus(SyncEnum.SUCCESS)
  105 + }, 1000)
  106 + return
  107 + // 失败状态
  108 + // chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
  109 + }, 3000)
  110 +
  111 + //thumbnailSyncUpdate 缩略图保存
  112 + const thumbnailSyncUpdate = throttle(async(updateImg = true)=>{
  113 + const { state, organizationId, dataViewName } = projectInfoStore.getProjectInfo
89 if (updateImg) { 114 if (updateImg) {
90 // 获取缩略图片 115 // 获取缩略图片
91 const range = document.querySelector('.go-edit-range') as HTMLElement 116 const range = document.querySelector('.go-edit-range') as HTMLElement
@@ -96,7 +121,6 @@ export const useSyncRemote = () => { @@ -96,7 +121,6 @@ export const useSyncRemote = () => {
96 useCORS: true, 121 useCORS: true,
97 logging: false 122 logging: false
98 }) 123 })
99 -  
100 // 上传预览图 124 // 上传预览图
101 const uploadParams = new FormData() 125 const uploadParams = new FormData()
102 uploadParams.append( 126 uploadParams.append(
@@ -104,7 +128,6 @@ export const useSyncRemote = () => { @@ -104,7 +128,6 @@ export const useSyncRemote = () => {
104 base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`) 128 base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`)
105 ) 129 )
106 const uploadRes = await uploadFile(uploadParams) 130 const uploadRes = await uploadFile(uploadParams)
107 -  
108 // 保存预览图 131 // 保存预览图
109 if (uploadRes) { 132 if (uploadRes) {
110 await saveDataViewList({ 133 await saveDataViewList({
@@ -114,30 +137,10 @@ export const useSyncRemote = () => { @@ -114,30 +137,10 @@ export const useSyncRemote = () => {
114 id: fetchRouteParamsLocation(), 137 id: fetchRouteParamsLocation(),
115 thumbnail: `${uploadRes.fileStaticUri}` 138 thumbnail: `${uploadRes.fileStaticUri}`
116 }) 139 })
  140 + window['$message'].success('保存缩略图成功!')
117 } 141 }
118 } 142 }
119 - } catch (e) {  
120 - console.log(e)  
121 - }  
122 - // 保存数据  
123 - const saveContent = {  
124 - dataViewContent: {  
125 - id: dataViewContent.id,  
126 - content: JSONStringify(chartEditStore.getStorageInfo || {})  
127 - },  
128 - dataViewName,  
129 - dataViewId  
130 - }  
131 - await contentUpdateApi(saveContent as unknown as BaseUpdateContentParams)  
132 - window['$message'].success('保存成功!')  
133 - // 成功状态  
134 - setTimeout(() => {  
135 - projectInfoStore.setSaveStatus(SyncEnum.SUCCESS)  
136 - }, 1000)  
137 - return  
138 - // 失败状态  
139 - // chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)  
140 - }, 3000) 143 + })
141 144
142 // * 定时处理 145 // * 定时处理
143 const intervalDataSyncUpdate = () => { 146 const intervalDataSyncUpdate = () => {
@@ -145,7 +148,6 @@ export const useSyncRemote = () => { @@ -145,7 +148,6 @@ export const useSyncRemote = () => {
145 // const syncTiming = setInterval(() => { 148 // const syncTiming = setInterval(() => {
146 // dataSyncUpdate() 149 // dataSyncUpdate()
147 // }, saveInterval * 1000) 150 // }, saveInterval * 1000)
148 -  
149 // // 销毁 151 // // 销毁
150 // onUnmounted(() => { 152 // onUnmounted(() => {
151 // clearInterval(syncTiming) 153 // clearInterval(syncTiming)
@@ -154,6 +156,7 @@ export const useSyncRemote = () => { @@ -154,6 +156,7 @@ export const useSyncRemote = () => {
154 return { 156 return {
155 dataSyncFetch, 157 dataSyncFetch,
156 dataSyncUpdate, 158 dataSyncUpdate,
  159 + thumbnailSyncUpdate,
157 intervalDataSyncUpdate 160 intervalDataSyncUpdate
158 } 161 }
159 } 162 }