Commit a19d97cb02b201ca37963561e0cae8d9984c0343
Merge branch 'dev_by_ft' into 'main_dev'
feat(src/views/chart): 新增图标,可配置自定义内容弹窗和优化多个摄像头,可以自动切换也可以手动切换 See merge request yunteng/thingskit-view!177
Showing
8 changed files
with
331 additions
and
14 deletions
1 | +<template> | ||
2 | + <div class="smallcircle2"></div> | ||
3 | +</template> | ||
4 | + | ||
5 | +<script setup lang="ts"></script> | ||
6 | + | ||
7 | +<style lang="scss" scoped> | ||
8 | +.smallcircle2 { | ||
9 | + display: block; | ||
10 | + width: 12px; | ||
11 | + height: 12px; | ||
12 | + border-radius: 50%; | ||
13 | + opacity: 0.4; | ||
14 | + background: radial-gradient(circle at center, red 0, blue, #2277b2 100%); | ||
15 | +} | ||
16 | +</style> |
@@ -16,8 +16,34 @@ export interface IconOptions { | @@ -16,8 +16,34 @@ export interface IconOptions { | ||
16 | pointColor?: string | 16 | pointColor?: string |
17 | dynamicEffect?: IconDynamicEffectEnum[] | 17 | dynamicEffect?: IconDynamicEffectEnum[] |
18 | backgroundColor?: string | 18 | backgroundColor?: string |
19 | + popupConfig: PopupConfig | ||
20 | + dataset: bodyItem[] | ||
19 | } | 21 | } |
20 | 22 | ||
23 | +interface bodyItem { | ||
24 | + label: string | ||
25 | + value: string | ||
26 | +} | ||
27 | + | ||
28 | +interface PopupConfig { | ||
29 | + borderWidth: number | ||
30 | + borderHeight: number | ||
31 | + borderColor: string | ||
32 | + boxShadowColor: string | ||
33 | + linearLeftColor: string | ||
34 | + linearRightColor: string | ||
35 | + arrowColor: string | ||
36 | + lineColor: string | ||
37 | + fontColor: string | ||
38 | + fontSize: number | ||
39 | + fontWeight: number | ||
40 | + fontContent: string | ||
41 | + labelColor: string | ||
42 | + valueColor: string | ||
43 | + placement: string | ||
44 | +} | ||
45 | + | ||
46 | + | ||
21 | export const option: IconOptions = { | 47 | export const option: IconOptions = { |
22 | icon: 'turangshuifen', | 48 | icon: 'turangshuifen', |
23 | iconColor: '#1C9276', | 49 | iconColor: '#1C9276', |
@@ -28,7 +54,30 @@ export const option: IconOptions = { | @@ -28,7 +54,30 @@ export const option: IconOptions = { | ||
28 | pointSize: 4, | 54 | pointSize: 4, |
29 | pointColor: '#9ADED7', | 55 | pointColor: '#9ADED7', |
30 | dynamicEffect: [], | 56 | dynamicEffect: [], |
31 | - backgroundColor: '#082B2A' | 57 | + backgroundColor: '#082B2A', |
58 | + dataset: [ | ||
59 | + { label: '设备:', value: 'xxxxxxxxx设备' }, | ||
60 | + { label: '产品:', value: 'xxxxxxxxx产品' }, | ||
61 | + { label: '位置:', value: 'xxxxxxxxx位置' }, | ||
62 | + { label: '创建时间:', value: '2024-01-01' } | ||
63 | + ], | ||
64 | + popupConfig: { | ||
65 | + borderWidth: 380, | ||
66 | + borderHeight: 200, | ||
67 | + borderColor: 'red', | ||
68 | + boxShadowColor: '#356A82FF', | ||
69 | + linearLeftColor: '#385391FF', | ||
70 | + linearRightColor: '#385391FF', | ||
71 | + arrowColor: '#427FB4', | ||
72 | + lineColor: '#427FB4', | ||
73 | + fontColor: '#ffffff', | ||
74 | + fontSize: 16, | ||
75 | + fontWeight: 500, | ||
76 | + fontContent: '我是标题', | ||
77 | + labelColor: '#3d6e9d', | ||
78 | + valueColor: '#ffffff', | ||
79 | + placement: 'top' | ||
80 | + } | ||
32 | } | 81 | } |
33 | 82 | ||
34 | export default class Config extends PublicConfigClass implements CreateComponentType { | 83 | export default class Config extends PublicConfigClass implements CreateComponentType { |
@@ -42,6 +42,95 @@ | @@ -42,6 +42,95 @@ | ||
42 | </SettingItem> | 42 | </SettingItem> |
43 | </SettingItemBox> | 43 | </SettingItemBox> |
44 | </CollapseItem> | 44 | </CollapseItem> |
45 | + <CollapseItem name="弹窗配置" expanded> | ||
46 | + <SettingItemBox name="宽高"> | ||
47 | + <SettingItem name="宽"> | ||
48 | + <NInputNumber | ||
49 | + v-model:value="optionData.popupConfig.borderWidth" | ||
50 | + size="small" | ||
51 | + :step="10" | ||
52 | + :min="0" | ||
53 | + ></NInputNumber> | ||
54 | + </SettingItem> | ||
55 | + <SettingItem name="高"> | ||
56 | + <NInputNumber | ||
57 | + v-model:value="optionData.popupConfig.borderHeight" | ||
58 | + size="small" | ||
59 | + :step="10" | ||
60 | + :min="0" | ||
61 | + ></NInputNumber> | ||
62 | + </SettingItem> | ||
63 | + </SettingItemBox> | ||
64 | + <SettingItemBox name="位置"> | ||
65 | + <SettingItem name=""> | ||
66 | + <NSelect v-model:value="optionData.popupConfig.placement" size="small" :options="placementOptions"> </NSelect> | ||
67 | + </SettingItem> | ||
68 | + </SettingItemBox> | ||
69 | + <SettingItemBox name="颜色"> | ||
70 | + <SettingItem name="边框阴影"> | ||
71 | + <NColorPicker | ||
72 | + size="small" | ||
73 | + :modes="['hex']" | ||
74 | + :actions="['clear']" | ||
75 | + v-model:value="optionData.popupConfig.boxShadowColor" | ||
76 | + > | ||
77 | + </NColorPicker> | ||
78 | + </SettingItem> | ||
79 | + <SettingItem name="背景线性渐变左"> | ||
80 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.linearLeftColor"> </NColorPicker> | ||
81 | + </SettingItem> | ||
82 | + <SettingItem name="背景线性渐变右"> | ||
83 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.linearRightColor"> </NColorPicker> | ||
84 | + </SettingItem> | ||
85 | + <SettingItem name="箭头颜色"> | ||
86 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.arrowColor"> </NColorPicker> | ||
87 | + </SettingItem> | ||
88 | + <SettingItem name="线条颜色"> | ||
89 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.lineColor"> </NColorPicker> | ||
90 | + </SettingItem> | ||
91 | + <SettingItem name="标题内容"> | ||
92 | + <NInput v-model:value="optionData.popupConfig.fontContent"> </NInput> | ||
93 | + </SettingItem> | ||
94 | + <SettingItem name="标题颜色"> | ||
95 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.fontColor"> </NColorPicker> | ||
96 | + </SettingItem> | ||
97 | + <SettingItem name="标题加粗"> | ||
98 | + <NInputNumber | ||
99 | + v-model:value="optionData.popupConfig.fontWeight" | ||
100 | + size="small" | ||
101 | + :step="100" | ||
102 | + :min="0" | ||
103 | + ></NInputNumber> | ||
104 | + </SettingItem> | ||
105 | + <SettingItem name="内容键文本颜色"> | ||
106 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.labelColor"> </NColorPicker> | ||
107 | + </SettingItem> | ||
108 | + <SettingItem name="内容值文本颜色"> | ||
109 | + <NColorPicker size="small" v-model:value="optionData.popupConfig.valueColor"> </NColorPicker> | ||
110 | + </SettingItem> | ||
111 | + </SettingItemBox> | ||
112 | + <template v-for="(item, index) in optionData.dataset" :key="index"> | ||
113 | + <setting-item-box name="内容键" :alone="true"> | ||
114 | + <setting-item> | ||
115 | + <NInput v-model:value="item.label"> </NInput> | ||
116 | + </setting-item> | ||
117 | + </setting-item-box> | ||
118 | + <setting-item-box name="内容值" :alone="true"> | ||
119 | + <setting-item> | ||
120 | + <NInput v-model:value="item.value"> </NInput> | ||
121 | + </setting-item> | ||
122 | + </setting-item-box> | ||
123 | + <n-button size="small" @click="optionData.dataset.splice(index, 1)"> - </n-button> | ||
124 | + </template> | ||
125 | + <n-button | ||
126 | + style="margin-left: 10px" | ||
127 | + v-if="optionData.dataset.length < 8" | ||
128 | + size="small" | ||
129 | + @click="optionData.dataset.push({ label: '', value: '' })" | ||
130 | + > | ||
131 | + + | ||
132 | + </n-button> | ||
133 | + </CollapseItem> | ||
45 | </template> | 134 | </template> |
46 | 135 | ||
47 | <script setup lang="ts"> | 136 | <script setup lang="ts"> |
@@ -79,4 +168,24 @@ const getDynamicEffectOptions = computed<SelectOption[]>(() => { | @@ -79,4 +168,24 @@ const getDynamicEffectOptions = computed<SelectOption[]>(() => { | ||
79 | ] | 168 | ] |
80 | 169 | ||
81 | }) | 170 | }) |
171 | + | ||
172 | +//native-ui 组件popover内置弹窗位置,不需要通过枚举方式 | ||
173 | +const placementOptions: SelectOption[] = [ | ||
174 | + { | ||
175 | + label: '顶部', | ||
176 | + value: 'top' | ||
177 | + }, | ||
178 | + { | ||
179 | + label: '右边', | ||
180 | + value: 'right' | ||
181 | + }, | ||
182 | + { | ||
183 | + label: '左边', | ||
184 | + value: 'left' | ||
185 | + }, | ||
186 | + { | ||
187 | + label: '底部', | ||
188 | + value: 'bottom' | ||
189 | + } | ||
190 | +] | ||
82 | </script> | 191 | </script> |
@@ -8,7 +8,7 @@ export const PickIconConfig: ConfigType = { | @@ -8,7 +8,7 @@ export const PickIconConfig: 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: PackagesCategoryEnum.DECORATES, | 14 | package: PackagesCategoryEnum.DECORATES, |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | -import { computed, PropType } from 'vue' | 2 | +import { computed, PropType, toRefs } from 'vue' |
3 | import { CreateComponentType } from '@/packages/index.d' | 3 | import { CreateComponentType } from '@/packages/index.d' |
4 | import { option } from './config' | 4 | import { option } from './config' |
5 | import SvgBorder from './SvgBorder.vue'; | 5 | import SvgBorder from './SvgBorder.vue'; |
@@ -17,14 +17,144 @@ const size = computed(() => { | @@ -17,14 +17,144 @@ const size = computed(() => { | ||
17 | return Math.min(w, h) / 2 | 17 | return Math.min(w, h) / 2 |
18 | }) | 18 | }) |
19 | 19 | ||
20 | - | 20 | +const { popupConfig, dataset } = toRefs(props.chartConfig.option) |
21 | </script> | 21 | </script> |
22 | 22 | ||
23 | <template> | 23 | <template> |
24 | - <section> | ||
25 | - <SvgBorder :option="chartConfig.option"> | ||
26 | - <SvgIcon :style="{ color: chartConfig.option.iconColor }" :size="size" :name="chartConfig.option.icon!" | ||
27 | - prefix="iconfont" /> | ||
28 | - </SvgBorder> | 24 | + <section> |
25 | + <n-popover :placement="popupConfig.placement" trigger="click" raw> | ||
26 | + <template #trigger> | ||
27 | + <SvgBorder :option="chartConfig.option"> | ||
28 | + <SvgIcon | ||
29 | + :style="{ color: chartConfig.option.iconColor }" | ||
30 | + :size="size" | ||
31 | + :name="chartConfig.option.icon!" | ||
32 | + prefix="iconfont" | ||
33 | + /> | ||
34 | + </SvgBorder> | ||
35 | + </template> | ||
36 | + <div | ||
37 | + style="transform-origin: inherit" | ||
38 | + :style="`width:${popupConfig.borderWidth}px; | ||
39 | + height: ${popupConfig.borderHeight}px; | ||
40 | + box-shadow: 0 0 10px 10px ${popupConfig.boxShadowColor}; | ||
41 | + background:linear-gradient(to right, ${popupConfig.linearLeftColor} , ${popupConfig.linearRightColor}); | ||
42 | + `" | ||
43 | + > | ||
44 | + <div class="popup-body"> | ||
45 | + <div class="popup-arrow-right" :style="`border-color:${popupConfig.arrowColor}`"></div> | ||
46 | + <div class="popup-header-content"> | ||
47 | + <p | ||
48 | + class="header-title" | ||
49 | + :style="`color:${popupConfig.fontColor};font-size:${popupConfig.fontSize}px;font-weight:${popupConfig.fontWeight}`" | ||
50 | + > | ||
51 | + {{ popupConfig.fontContent }} | ||
52 | + </p> | ||
53 | + <div class="header-content-graphical"> | ||
54 | + <div class="graphical-circle-left"><Circle /></div> | ||
55 | + <div class="graphical-line"> | ||
56 | + <div class="line-left" :style="`background-color:${popupConfig.lineColor}`"></div> | ||
57 | + <div class="line-center" :style="`background-color:${popupConfig.lineColor}`"></div> | ||
58 | + <div class="line-right" :style="`background-color:${popupConfig.lineColor}`"></div> | ||
59 | + </div> | ||
60 | + <div class="graphical-circle-right"><Circle /></div> | ||
61 | + </div> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + <div class="content-body"> | ||
65 | + <div class="body" v-for="(item, index) in dataset" :key="index"> | ||
66 | + <span :style="`color:${popupConfig.labelColor};`">{{ item.label }}</span> | ||
67 | + <span :style="`color:${popupConfig.valueColor};`">{{ item.value }}</span> | ||
68 | + </div> | ||
69 | + </div> | ||
70 | + </div> | ||
71 | + </n-popover> | ||
29 | </section> | 72 | </section> |
30 | </template> | 73 | </template> |
74 | + | ||
75 | +<style lang="scss" scoped> | ||
76 | +@mixin base-text-ellipsis() { | ||
77 | + overflow: hidden; | ||
78 | + white-space: nowrap; | ||
79 | + text-overflow: ellipsis; | ||
80 | + -o-text-overflow: ellipsis; | ||
81 | +} | ||
82 | +.popup-body { | ||
83 | + display: flex; | ||
84 | + justify-content: center; | ||
85 | + flex-direction: column; | ||
86 | + .popup-arrow-right { | ||
87 | + position: absolute; | ||
88 | + top: 15px; | ||
89 | + left: 10px; | ||
90 | + border-right: 1px solid; | ||
91 | + border-bottom: 1px solid; | ||
92 | + width: 7px; | ||
93 | + height: 7px; | ||
94 | + transform: rotate(-45deg); | ||
95 | + -o-transform: rotate(-45deg); | ||
96 | + -webkit-transform: rotate(-45deg); | ||
97 | + -moz-transform: rotate(-45deg); | ||
98 | + -ms-transform: rotate(-45deg); | ||
99 | + } | ||
100 | + .popup-header-content { | ||
101 | + margin: 23px; | ||
102 | + display: flex; | ||
103 | + justify-content: center; | ||
104 | + flex-direction: column; | ||
105 | + .header-title { | ||
106 | + @include base-text-ellipsis; | ||
107 | + } | ||
108 | + .header-content-graphical { | ||
109 | + display: flex; | ||
110 | + align-items: center; | ||
111 | + .graphical-circle-left { | ||
112 | + position: relative; | ||
113 | + left: -2px; | ||
114 | + } | ||
115 | + .graphical-line { | ||
116 | + display: flex; | ||
117 | + .line-left { | ||
118 | + width: 115px; | ||
119 | + height: 2px; | ||
120 | + position: relative; | ||
121 | + } | ||
122 | + .line-center { | ||
123 | + width: 35px; | ||
124 | + height: 2px; | ||
125 | + transform: rotateZ(145deg); | ||
126 | + position: absolute; | ||
127 | + top: 44px; | ||
128 | + left: 146px; | ||
129 | + } | ||
130 | + .line-right { | ||
131 | + width: 50px; | ||
132 | + height: 2px; | ||
133 | + position: absolute; | ||
134 | + top: 34px; | ||
135 | + left: 178px; | ||
136 | + } | ||
137 | + } | ||
138 | + .graphical-circle-right { | ||
139 | + position: absolute; | ||
140 | + top: 29px; | ||
141 | + left: 231px; | ||
142 | + } | ||
143 | + } | ||
144 | + } | ||
145 | +} | ||
146 | +.content-body { | ||
147 | + display: flex; | ||
148 | + align-items: center; | ||
149 | + flex-wrap: wrap; | ||
150 | + margin: -10px 0; | ||
151 | + .body { | ||
152 | + display: flex; | ||
153 | + align-items: center; | ||
154 | + justify-content: flex-start; | ||
155 | + gap: 27px; | ||
156 | + margin-left: 15px; | ||
157 | + margin-top: 1px; | ||
158 | + } | ||
159 | +} | ||
160 | +</style> |
@@ -9,6 +9,7 @@ export enum sourceTypeEnum { | @@ -9,6 +9,7 @@ export enum sourceTypeEnum { | ||
9 | } | 9 | } |
10 | 10 | ||
11 | export const option = { | 11 | export const option = { |
12 | + autoSwitch: false, | ||
12 | dataset: [ | 13 | dataset: [ |
13 | { | 14 | { |
14 | byIdUrl: '', //通过接口获取的url | 15 | byIdUrl: '', //通过接口获取的url |
@@ -18,7 +19,7 @@ export const option = { | @@ -18,7 +19,7 @@ export const option = { | ||
18 | } | 19 | } |
19 | ] as any, | 20 | ] as any, |
20 | // 自动播放的间隔(ms) | 21 | // 自动播放的间隔(ms) |
21 | - interval: 5000, | 22 | + interval: 2000, |
22 | autoplay: true, | 23 | autoplay: true, |
23 | effect: 'slide', | 24 | effect: 'slide', |
24 | displayMode: 'singleGrid' | 25 | displayMode: 'singleGrid' |
1 | <template> | 1 | <template> |
2 | <CollapseItem name="播放器配置" :expanded="true"> | 2 | <CollapseItem name="播放器配置" :expanded="true"> |
3 | + <setting-item-box name="开启切换" :alone="true"> | ||
4 | + <setting-item> | ||
5 | + <n-switch v-model:value="optionData.autoSwitch" size="small"></n-switch> | ||
6 | + </setting-item> | ||
7 | + </setting-item-box> | ||
8 | + <setting-item-box name="间隔时长" :alone="true"> | ||
9 | + <setting-item> | ||
10 | + <n-input-number v-model:value="optionData.interval" size="small"></n-input-number> | ||
11 | + </setting-item> | ||
12 | + </setting-item-box> | ||
3 | <template v-for="(item, index) in optionData.dataset" :key="index"> | 13 | <template v-for="(item, index) in optionData.dataset" :key="index"> |
4 | <setting-item-box name="源类型" :alone="true"> | 14 | <setting-item-box name="源类型" :alone="true"> |
5 | <setting-item> | 15 | <setting-item> |
@@ -37,6 +37,8 @@ const isShowSvg = ref(false) | @@ -37,6 +37,8 @@ const isShowSvg = ref(false) | ||
37 | 37 | ||
38 | const { w, h } = toRefs(props.chartConfig.attr) | 38 | const { w, h } = toRefs(props.chartConfig.attr) |
39 | 39 | ||
40 | +const { autoSwitch, interval } = toRefs(props.chartConfig.option) | ||
41 | + | ||
40 | const option = shallowReactive({ | 42 | const option = shallowReactive({ |
41 | dataset: configOption.dataset | 43 | dataset: configOption.dataset |
42 | }) | 44 | }) |
@@ -45,8 +47,6 @@ const cameraRef = ref<InstanceType<typeof CameraItem>>() | @@ -45,8 +47,6 @@ const cameraRef = ref<InstanceType<typeof CameraItem>>() | ||
45 | 47 | ||
46 | let initial = ref(0) | 48 | let initial = ref(0) |
47 | 49 | ||
48 | -let interval = ref(2500) | ||
49 | - | ||
50 | const computedFunc = (initial: number, source: any) => { | 50 | const computedFunc = (initial: number, source: any) => { |
51 | if (initial < 0) initial = 0 | 51 | if (initial < 0) initial = 0 |
52 | if (Array.isArray(source)) { | 52 | if (Array.isArray(source)) { |
@@ -130,10 +130,12 @@ let root = ref(null) | @@ -130,10 +130,12 @@ let root = ref(null) | ||
130 | 130 | ||
131 | onMounted(() => { | 131 | onMounted(() => { |
132 | clearInterval(timer) | 132 | clearInterval(timer) |
133 | - autoPlay() | ||
134 | const box: any = root.value | 133 | const box: any = root.value |
135 | box.onmouseenter = () => clearInterval(timer) | 134 | box.onmouseenter = () => clearInterval(timer) |
136 | - box.onmouseleave = () => autoPlay() | 135 | + if (autoSwitch.value) { |
136 | + autoPlay() | ||
137 | + box.onmouseleave = () => autoPlay() | ||
138 | + } | ||
137 | }) | 139 | }) |
138 | 140 | ||
139 | // 点击左右按钮切换图片 | 141 | // 点击左右按钮切换图片 |