Commit 478db8527d890916b2b522bb250e44ae31f25ae6

Authored by xp.Huang
2 parents 50f08037 215fd4ce

Merge branch 'ft' into 'main_dev'

feat(external/Composes): 大屏新增三维模型上传和自定义轮播图功能和修复Threejs无法生成缩略图问题

See merge request yunteng/thingskit-view!63
Showing 25 changed files with 1360 additions and 6 deletions
@@ -45,6 +45,7 @@ @@ -45,6 +45,7 @@
45 "three": "^0.145.0", 45 "three": "^0.145.0",
46 "video.js": "^7.20.3", 46 "video.js": "^7.20.3",
47 "vue": "^3.2.31", 47 "vue": "^3.2.31",
  48 + "vue-3d-loader": "^2.1.7",
48 "vue-demi": "^0.13.1", 49 "vue-demi": "^0.13.1",
49 "vue-i18n": "^9.2.2", 50 "vue-i18n": "^9.2.2",
50 "vue-router": "4.0.12", 51 "vue-router": "4.0.12",
1 -import { defHttp } from "@/utils/external/http/axios";  
2 -import { DictItem, UploadResponse } from "./model"; 1 +import { defHttp } from '@/utils/external/http/axios'
  2 +import { DictItem, UploadResponse } from './model'
3 3
4 enum Api { 4 enum Api {
5 GET_DICT = '/dict_item', 5 GET_DICT = '/dict_item',
6 - UPLOAD = '/oss/upload' 6 + UPLOAD = '/oss/upload',
  7 + DOWNLOAD = '/oss/download_file/'
7 } 8 }
8 9
9 export const getDictItemByCode = (value: string) => { 10 export const getDictItemByCode = (value: string) => {
@@ -21,3 +22,7 @@ export const upload = (file: FormData) => { @@ -21,3 +22,7 @@ export const upload = (file: FormData) => {
21 params: file 22 params: file
22 }) 23 })
23 } 24 }
  25 +
  26 +export const downloadFile = (fileName: string) => {
  27 + return defHttp.get({ url: `${Api.DOWNLOAD}${fileName}` })
  28 +}
@@ -27,12 +27,21 @@ export class Basic { @@ -27,12 +27,21 @@ export class Basic {
27 27
28 this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000) 28 this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000)
29 this.camera.position.set(0, 30, -250) 29 this.camera.position.set(0, 30, -250)
30 - 30 + /**
  31 + * 这里升级版本有冲突,升级版本可以使用这里的代码,官方还未修复
  32 + * 修复官方Threejs生成缩略图无效问题,少加一个Threejs配置
  33 + * 修改后的代码在注释之间,并标注好源代码和修改后代码,方便回溯
  34 + * 源代码 属于新增代码,源代码无
  35 + * 修改后代码 preserveDrawingBuffer: true //缩略图生效需开启
  36 + *
  37 + */
31 this.renderer = new THREE.WebGLRenderer({ 38 this.renderer = new THREE.WebGLRenderer({
32 // canvas: this.dom, 39 // canvas: this.dom,
33 alpha: true, // 透明 40 alpha: true, // 透明
34 - antialias: true // 抗锯齿 41 + antialias: true, // 抗锯齿
  42 + preserveDrawingBuffer: true//缩略图生效需开启
35 }) 43 })
  44 + //ft
36 this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比 45 this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比
37 this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高 46 this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高
38 this.dom.appendChild(this.renderer.domElement) // 添加到dom中 47 this.dom.appendChild(this.renderer.domElement) // 添加到dom中
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { ThreeDimensionalConfig } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +import { chartInitConfig } from '@/settings/designSetting'
  6 +
  7 +export const option = {
  8 + //vue3dLoader支持数组或字符串,暂且绑定字符串,这个插件可以加载多个模型
  9 + dataset: "",
  10 + backgroundColor: '', //场景背景色
  11 + backgroundAlpha: 0, //场景透明度
  12 + enableDamping: false, //是否启用阻尼
  13 + dampingFactor: 0.05, //阻尼值
  14 + autoPlay: true, //三维动画是否启用自动播放
  15 + /**
  16 + * 输出编码,可取值为 liner 或 sRGB。
  17 + * linear 是 LinearEncoding 线性编码,
  18 + * sRGB 即 sRGBEncoding rgb 模式编码(sRGBEncoding 能更好的还原材质颜色)
  19 + */
  20 + outputEncoding: 'liner',
  21 + clearScene: false, //是否清空场景内容
  22 + lights: [] //灯光,暂且没实现
  23 +}
  24 +
  25 +export default class Config extends PublicConfigClass implements CreateComponentType {
  26 + public key = ThreeDimensionalConfig.key
  27 + public attr = { ...chartInitConfig, zIndex: 1, w: 600, h: 500 }
  28 + public chartConfig = cloneDeep(ThreeDimensionalConfig)
  29 + public option = cloneDeep(option)
  30 +}
  1 +<template>
  2 + <collapse-item name="三维配置" :expanded="true">
  3 + <setting-item-box name="文件上传" :alone="true">
  4 + <setting-item :name="fileSizeMsg">
  5 + <n-upload
  6 + :max="1"
  7 + :customRequest="customRequest"
  8 + :onBeforeUpload="beforeUploadHandle"
  9 + :default-file-list="defaultFileList"
  10 + >
  11 + <n-button> 上传文件</n-button>
  12 + </n-upload>
  13 + </setting-item>
  14 + </setting-item-box>
  15 + <setting-item-box name="属性配置">
  16 + <setting-item name="场景色(需要这种格式HEX #000000,否则失效)">
  17 + <n-color-picker size="small" :show-alpha="false" v-model:value="optionData.backgroundColor"></n-color-picker>
  18 + </setting-item>
  19 + <SettingItem name="场景透明度">
  20 + <n-input-number :min="0" :max="1" v-model:value="optionData.backgroundAlpha" />
  21 + </SettingItem>
  22 + <SettingItem name="启用阻尼">
  23 + <n-switch v-model:value="optionData.enableDamping" size="small" />
  24 + </SettingItem>
  25 + <SettingItem v-if="optionData.enableDamping" name="阻尼值">
  26 + <n-input-number v-model:value="optionData.dampingFactor" :min="0" :max="1" size="small"></n-input-number>
  27 + </SettingItem>
  28 + <SettingItem name="启用动画">
  29 + <n-switch v-model:value="optionData.autoPlay" size="small" />
  30 + </SettingItem>
  31 + <SettingItem name="输出编码">
  32 + <n-select v-model:value="optionData.outputEncoding" size="small" :options="encodinghList"></n-select>
  33 + </SettingItem>
  34 + <SettingItem name="是否清空场景">
  35 + <n-switch @change="handleChange" v-model:value="optionData.clearScene" size="small" />
  36 + </SettingItem>
  37 + </setting-item-box>
  38 + </collapse-item>
  39 +</template>
  40 +
  41 +<script setup lang="ts">
  42 +import { PropType, h, ref, nextTick } from 'vue'
  43 +import { option } from './config'
  44 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  45 +import { NInputNumber, NSelect } from 'naive-ui'
  46 +import { uploadFile } from '@/api/external/contentSave/content'
  47 +import { UploadCustomRequestOptions } from 'naive-ui'
  48 +
  49 +const props = defineProps({
  50 + optionData: {
  51 + type: Object as PropType<typeof option>,
  52 + required: true
  53 + }
  54 +})
  55 +
  56 +const defaultFileList = ref([])
  57 +
  58 +//文件限制大小
  59 +const fileSizeConst = ref(50)
  60 +
  61 +/**
  62 + * 三维文件格式,vue-3d-loader是这个插件支持的格式,其他格式待解决
  63 + */
  64 +const threeSupportFileFormat = ['fbx', 'obj', 'gltf', 'stl', 'dae', 'glb', 'ply', 'json']
  65 +
  66 +const fileSizeMsg = ref(`文件需小于 ${fileSizeConst.value}M ,格式为${threeSupportFileFormat.join(',')}的文件`)
  67 +
  68 +const handleChange = (e: boolean) => {
  69 + if (e) props.optionData.dataset = ''
  70 +}
  71 +
  72 +const encodinghList = [
  73 + { label: 'linear', value: 'linear' },
  74 + { label: 'sRGB ', value: 'sRGB ' }
  75 +]
  76 +
  77 +
  78 +const extname = (filename: string) => {
  79 + if (!filename || typeof filename != 'string') {
  80 + return false
  81 + }
  82 + let a = filename.split('').reverse().join('')
  83 + let b = a.substring(0, a.search(/\./)).split('').reverse().join('')
  84 + return b
  85 +}
  86 +
  87 +// 上传图片前置处理
  88 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
  89 +//@ts-ignore
  90 +const beforeUploadHandle = async ({ file }) => {
  91 + defaultFileList.value = []
  92 + const type = extname(file.file.name) as string
  93 + const size = file.file.size
  94 + if (size / (1024 * 1024) > fileSizeConst.value) {
  95 + window['$message'].warning(`文件超出 ${fileSizeConst.value}M限制,请重新上传!`)
  96 + return false
  97 + }
  98 + if (!threeSupportFileFormat.includes(type)) {
  99 + window['$message'].warning('文件格式不符合,请重新上传!')
  100 + return false
  101 + }
  102 + return true
  103 +}
  104 +
  105 +// 自定义上传操作
  106 +const customRequest = (options: UploadCustomRequestOptions) => {
  107 + const { file } = options
  108 + nextTick(async () => {
  109 + if (file.file) {
  110 + const formData = new FormData()
  111 + formData.append('file', file.file)
  112 + const uploadRes = await uploadFile(formData)
  113 + if (uploadRes) {
  114 + props.optionData.dataset = uploadRes?.fileStaticUri
  115 + window['$message'].success('上传文件成功!')
  116 + }
  117 + } else {
  118 + window['$message'].error('上传文件失败,请稍后重试!')
  119 + }
  120 + })
  121 +}
  122 +</script>
  123 +
  124 +<style lang="scss" scoped></style>
  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('ThreeDimensional')
  7 +export const ThreeDimensionalConfig: 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: 'threeDimensional.png'
  17 +}
  1 +<template>
  2 + <div class="go-content-box">
  3 + <div v-if="supportWebGL">
  4 + <vue3dLoader
  5 + ref="vue3dLoaderRef"
  6 + :webGLRendererOptions="webGLRendererOptions"
  7 + :backgroundColor="backgroundColor"
  8 + :backgroundAlpha="backgroundAlpha"
  9 + :height="h"
  10 + :width="w"
  11 + :filePath="dataset"
  12 + :autoPlay="autoPlay"
  13 + :enableDamping="enableDamping"
  14 + :outputEncoding="outputEncoding"
  15 + :clearScene="clearScene"
  16 + :dampingFactor="dampingFactor"
  17 + @process="onProcess"
  18 + @load="onLoad"
  19 + @click="onClick"
  20 + />
  21 + <div v-show="show" class="process">
  22 + <span> 拼命加载中... </span>
  23 + <n-progress type="line" :color="themeColor" :percentage="process" :indicator-placement="'inside'" processing />
  24 + </div>
  25 + </div>
  26 + <div v-else>您的浏览器不支持WebGL!</div>
  27 + </div>
  28 +</template>
  29 +<script setup lang="ts">
  30 +import { PropType, toRefs, ref, onMounted, nextTick, computed } from 'vue'
  31 +import { CreateComponentType } from '@/packages/index.d'
  32 +import { vue3dLoader } from 'vue-3d-loader'
  33 +import { useDesignStore } from '@/store/modules/designStore/designStore'
  34 +
  35 +const designStore = useDesignStore()
  36 +
  37 +const props = defineProps({
  38 + chartConfig: {
  39 + type: Object as PropType<CreateComponentType>,
  40 + required: true
  41 + }
  42 +})
  43 +
  44 +// 颜色
  45 +const themeColor = computed(() => {
  46 + return designStore.getAppTheme
  47 +})
  48 +
  49 +const vue3dLoaderRef = ref(null)
  50 +
  51 +//threejs配置
  52 +const webGLRendererOptions = {
  53 + alpha: true, // 透明
  54 + antialias: true, // 抗锯齿
  55 + // fix: 预览三维图像需加上preserveDrawingBuffer: true
  56 + preserveDrawingBuffer: true //缩略图生效需开启
  57 +}
  58 +
  59 +//三维模型加载
  60 +const show = ref(false)
  61 +
  62 +const onLoad = async () => {
  63 + //加载完成
  64 + await nextTick()
  65 + show.value = false
  66 +}
  67 +
  68 +//三维模型进度条
  69 +const process = ref(0)
  70 +
  71 +const onProcess = (event: any) => {
  72 + process.value = Math.floor((event.loaded / event.total) * 100)
  73 +}
  74 +
  75 +const onClick = (event: any) => {
  76 + console.log(event)
  77 +}
  78 +
  79 +//判断浏览器是否支持WebGL
  80 +const supportWebGL = ref(true)
  81 +const detectWebGLContext = () => {
  82 + let canvas = document.createElement('canvas')
  83 + let gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
  84 + if (gl && gl instanceof WebGLRenderingContext) {
  85 + supportWebGL.value = true
  86 + } else {
  87 + supportWebGL.value = false
  88 + }
  89 +}
  90 +
  91 +onMounted(() => {
  92 + detectWebGLContext()
  93 + console.log(`实例`, vue3dLoaderRef.value)
  94 +})
  95 +
  96 +const { w, h } = toRefs(props.chartConfig.attr)
  97 +
  98 +const {
  99 + backgroundColor,
  100 + backgroundAlpha,
  101 + autoPlay,
  102 + enableDamping,
  103 + dataset,
  104 + outputEncoding,
  105 + clearScene,
  106 + dampingFactor
  107 +} = toRefs(props.chartConfig.option) as any
  108 +</script>
  109 +
  110 +<style lang="scss" scoped>
  111 +.go-content-box {
  112 + height: 100%;
  113 + position: relative;
  114 + .process {
  115 + position: absolute;
  116 + top: 50%;
  117 + left: 50%;
  118 + width: 50%;
  119 + transform: translate(-50%, -50%);
  120 + }
  121 +}
  122 +</style>
@@ -2,5 +2,6 @@ import { Title1Config } from './Title1/index' @@ -2,5 +2,6 @@ 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 { ThreeDimensionalConfig } from './ThreeDimensional/index'
5 6
6 -export default [Title1Config, Title2Config, Title3Config, CameraConfig] 7 +export default [Title1Config, Title2Config, Title3Config, CameraConfig, ThreeDimensionalConfig]
  1 +import { PublicConfigClass } from '@/packages/public'
  2 +import { CreateComponentType } from '@/packages/index.d'
  3 +import { OverrideCarouselConfig } from './index'
  4 +import cloneDeep from 'lodash/cloneDeep'
  5 +
  6 +export const option = {
  7 + // 图片资源列表
  8 + dataset: [],
  9 + // 自动播放
  10 + autoplay: true,
  11 + // 自动播放的间隔(ms)
  12 + interval: 5000,
  13 + // 每页显示的图片数量
  14 + slidesPerview: 1,
  15 + // 轮播方向
  16 + direction: 'horizontal',
  17 + // 拖曳切换
  18 + draggable: true,
  19 + // 居中显示
  20 + centeredSlides: false,
  21 + // 过渡效果
  22 + effect: 'slide',
  23 + // 是否显示指示点
  24 + showDots: true,
  25 + // 指示器样式
  26 + dotType: 'dot',
  27 + // 指示器位置
  28 + dotPlacement: 'bottom',
  29 + // 显示箭头
  30 + showArrow: false,
  31 + // 图片样式
  32 + fit: 'contain'
  33 +}
  34 +
  35 +export default class Config extends PublicConfigClass implements CreateComponentType {
  36 + public key = OverrideCarouselConfig.key
  37 + public chartConfig = cloneDeep(OverrideCarouselConfig)
  38 + public option = cloneDeep(option)
  39 +}
  1 +<template>
  2 + <collapse-item name="属性" :expanded="true">
  3 + <setting-item-box name="路径" :alone="true">
  4 + <n-upload
  5 + :max="5"
  6 + v-model:file-list="fileList"
  7 + :customRequest="customRequest"
  8 + :onBeforeUpload="beforeUploadHandle"
  9 + @change="handleUploadChange"
  10 + >
  11 + <n-button>上传图片</n-button>
  12 + </n-upload>
  13 + </setting-item-box>
  14 + <setting-item-box name="播放器">
  15 + <setting-item>
  16 + <n-space>
  17 + <n-switch v-model:value="optionData.autoplay" size="small" />
  18 + <n-text>自动播放</n-text>
  19 + </n-space>
  20 + </setting-item>
  21 + <!-- 开启自动播放时,设置间隔时间 -->
  22 + <setting-item name="间隔时间">
  23 + <n-input-number v-model:value="optionData.interval" size="small" placeholder=""></n-input-number>
  24 + </setting-item>
  25 + <setting-item name="轮播方向">
  26 + <n-select v-model:value="optionData.direction" :options="directions" placeholder="选择方向" />
  27 + </setting-item>
  28 + <setting-item name="过渡效果">
  29 + <n-select v-model:value="optionData.effect" :options="effects" placeholder="效果" />
  30 + </setting-item>
  31 + <setting-item name="每页数量">
  32 + <n-input-number v-model:value="optionData.slidesPerview" size="small" placeholder=""></n-input-number>
  33 + </setting-item>
  34 + <setting-item>
  35 + <n-space>
  36 + <n-switch v-model:value="optionData.centeredSlides" size="small" />
  37 + <n-text>居中显示</n-text>
  38 + </n-space>
  39 + </setting-item>
  40 + <setting-item name="图片样式">
  41 + <n-select v-model:value="optionData.fit" :options="fitList" placeholder="样式" />
  42 + </setting-item>
  43 + </setting-item-box>
  44 +
  45 + <setting-item-box name="指示器">
  46 + <setting-item name="样式">
  47 + <n-select v-model:value="optionData.dotType" :options="dotTypes" placeholder="选择样式" />
  48 + </setting-item>
  49 + <setting-item name="位置">
  50 + <n-select v-model:value="optionData.dotPlacement" :options="dotPlacements" placeholder="选择位置" />
  51 + </setting-item>
  52 + <setting-item>
  53 + <n-space>
  54 + <n-switch v-model:value="optionData.showDots" size="small" />
  55 + <n-text>显示</n-text>
  56 + </n-space>
  57 + </setting-item>
  58 + <setting-item>
  59 + <n-space>
  60 + <n-switch v-model:value="optionData.showArrow" size="small" />
  61 + <n-text>箭头</n-text>
  62 + </n-space>
  63 + </setting-item>
  64 + <setting-item>
  65 + <n-space>
  66 + <n-switch v-model:value="optionData.draggable" size="small" />
  67 + <n-text>拖曳切换</n-text>
  68 + </n-space>
  69 + </setting-item>
  70 + </setting-item-box>
  71 + </collapse-item>
  72 +</template>
  73 +
  74 +<script setup lang="ts">
  75 +import { PropType, ref, nextTick, watch } from 'vue'
  76 +import { option } from './config'
  77 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  78 +import { FileTypeEnum } from '@/enums/fileTypeEnum'
  79 +import { uploadFile } from '@/api/external/contentSave/content'
  80 +import { UploadCustomRequestOptions } from 'naive-ui'
  81 +import type { UploadFileInfo } from 'naive-ui'
  82 +import { backgroundImageSize } from '@/settings/designSetting'
  83 +import { fetchRouteParamsLocation } from '@/utils'
  84 +
  85 +const props = defineProps({
  86 + optionData: {
  87 + type: Object as PropType<typeof option>,
  88 + required: true
  89 + }
  90 +})
  91 +
  92 +const fileList = ref<UploadFileInfo[]>([])
  93 +
  94 +watch(
  95 + () => props.optionData,
  96 + newValue => {
  97 + console.log(newValue)
  98 + fileList.value = newValue?.dataset
  99 + },
  100 + {
  101 + immediate: true
  102 + }
  103 +)
  104 +
  105 +// 上传图片前置处理
  106 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
  107 +//@ts-ignore
  108 +const beforeUploadHandle = async ({ file }) => {
  109 + const type = file.file.type
  110 + const size = file.file.size
  111 + if (size > 1024 * 1024 * backgroundImageSize) {
  112 + window['$message'].warning(`图片超出 ${backgroundImageSize}M 限制,请重新上传!`)
  113 + return false
  114 + }
  115 + if (type !== FileTypeEnum.PNG && type !== FileTypeEnum.JPEG && type !== FileTypeEnum.GIF) {
  116 + window['$message'].warning('文件格式不符合,请重新上传!')
  117 + return false
  118 + }
  119 + return true
  120 +}
  121 +
  122 +const handleUploadChange = (data: { fileList: UploadFileInfo[] }) => {
  123 + console.log(data.fileList)
  124 + props.optionData.dataset = data.fileList as never as any
  125 +}
  126 +
  127 +// 自定义上传操作
  128 +const customRequest = (options: UploadCustomRequestOptions) => {
  129 + const { file } = options
  130 + nextTick(async () => {
  131 + if (file.file) {
  132 + // 修改名称
  133 + const newNameFile = new File([file.file], `${fetchRouteParamsLocation()}`, {
  134 + type: file.file.type
  135 + })
  136 + let uploadParams = new FormData()
  137 + uploadParams.append('file', newNameFile)
  138 + const uploadRes = await uploadFile(uploadParams)
  139 + if (uploadRes) {
  140 + fileList.value.push({
  141 + id: -Math.random() + '',
  142 + name: uploadRes?.fileName,
  143 + status: 'finished',
  144 + url: uploadRes?.fileStaticUri
  145 + })
  146 + const fileArr = fileList.value.filter((item: UploadFileInfo) => {
  147 + return item.status === 'finished'
  148 + })
  149 + fileList.value = fileArr
  150 + console.log(fileList.value)
  151 + props.optionData.dataset = fileList.value as never as any
  152 + window['$message'].success('添加图片成功!')
  153 + }
  154 + } else {
  155 + window['$message'].error('添加图片失败,请稍后重试!')
  156 + }
  157 + })
  158 +}
  159 +
  160 +// 字典
  161 +const dotTypes = [
  162 + {
  163 + label: '点',
  164 + value: 'dot'
  165 + },
  166 + {
  167 + label: '线',
  168 + value: 'line'
  169 + }
  170 +]
  171 +const directions = [
  172 + {
  173 + label: '水平方向',
  174 + value: 'horizontal'
  175 + },
  176 + {
  177 + label: '垂直方向',
  178 + value: 'vertical'
  179 + }
  180 +]
  181 +const effects = [
  182 + {
  183 + label: 'slide',
  184 + value: 'slide'
  185 + },
  186 + {
  187 + label: 'fade',
  188 + value: 'fade'
  189 + },
  190 + {
  191 + label: 'card',
  192 + value: 'card'
  193 + },
  194 + {
  195 + label: 'custom',
  196 + value: 'custom'
  197 + }
  198 +]
  199 +const dotPlacements = [
  200 + {
  201 + label: '上边',
  202 + value: 'top'
  203 + },
  204 + {
  205 + label: '下边',
  206 + value: 'bottom'
  207 + },
  208 + {
  209 + label: '左边',
  210 + value: 'left'
  211 + },
  212 + {
  213 + label: '右边',
  214 + value: 'right'
  215 + }
  216 +]
  217 +
  218 +// 适应类型
  219 +const fitList = [
  220 + {
  221 + value: 'fill',
  222 + label: 'fill'
  223 + },
  224 + {
  225 + value: 'contain',
  226 + label: 'contain'
  227 + },
  228 + {
  229 + value: 'cover',
  230 + label: 'cover'
  231 + },
  232 + {
  233 + value: 'scale-down',
  234 + label: 'scale-down'
  235 + },
  236 + {
  237 + value: 'none',
  238 + label: 'none'
  239 + }
  240 +]
  241 +</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('OverrideCarousel', true)
  6 +
  7 +export const OverrideCarouselConfig: 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: 'photo.png'
  17 +}
  1 +<template>
  2 + <div>
  3 + <n-carousel
  4 + :autoplay="autoplay"
  5 + :interval="interval"
  6 + :centered-slides="centeredSlides"
  7 + :direction="direction"
  8 + :dot-placement="dotPlacement"
  9 + :dot-type="dotType"
  10 + :draggable="draggable"
  11 + :effect="effect"
  12 + :slides-per-view="slidesPerview"
  13 + :show-arrow="showArrow"
  14 + :show-dots="showDots"
  15 + >
  16 + <n-image
  17 + v-for="(item, index) in option.dataset"
  18 + :key="index"
  19 + :object-fit="fit"
  20 + preview-disabled
  21 + :src="item.url"
  22 + :fallback-src="requireErrorImg()"
  23 + :width="w"
  24 + :height="h"
  25 + ></n-image>
  26 + </n-carousel>
  27 + </div>
  28 +</template>
  29 +<script setup lang="ts">
  30 +import { PropType, toRefs, shallowReactive, watch } from 'vue'
  31 +import { CreateComponentType } from '@/packages/index.d'
  32 +import { requireErrorImg } from '@/utils'
  33 +import { useChartDataFetch } from '@/hooks'
  34 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  35 +import { option as configOption } from './config'
  36 +
  37 +const props = defineProps({
  38 + chartConfig: {
  39 + type: Object as PropType<CreateComponentType>,
  40 + required: true
  41 + }
  42 +})
  43 +
  44 +const option: any = shallowReactive({
  45 + dataset: configOption.dataset
  46 +})
  47 +
  48 +const { w, h } = toRefs(props.chartConfig.attr)
  49 +const {
  50 + autoplay,
  51 + interval,
  52 + slidesPerview,
  53 + direction,
  54 + draggable,
  55 + centeredSlides,
  56 + effect,
  57 + dotType,
  58 + dotPlacement,
  59 + showArrow,
  60 + showDots,
  61 + fit
  62 +} = toRefs(props.chartConfig.option)
  63 +
  64 +watch(
  65 + () => props.chartConfig.option.dataset,
  66 + (newData: any) => {
  67 + option.dataset = newData
  68 + },
  69 + {
  70 + immediate: true,
  71 + deep: false
  72 + }
  73 +)
  74 +
  75 +useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
  76 + option.dataset = newData
  77 +})
  78 +</script>
@@ -58,6 +58,7 @@ const props = defineProps({ @@ -58,6 +58,7 @@ const props = defineProps({
58 const uploadFileListRef = ref() 58 const uploadFileListRef = ref()
59 59
60 // 上传图片前置处理 60 // 上传图片前置处理
  61 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
61 //@ts-ignore 62 //@ts-ignore
62 const beforeUploadHandle = async ({ file }) => { 63 const beforeUploadHandle = async ({ file }) => {
63 uploadFileListRef.value = [] 64 uploadFileListRef.value = []
  1 +import dayjs from 'dayjs'
  2 +import cloneDeep from 'lodash/cloneDeep'
  3 +import { PublicConfigClass } from '@/packages/public'
  4 +import { CreateComponentType } from '@/packages/index.d'
  5 +import { chartInitConfig } from '@/settings/designSetting'
  6 +import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum'
  7 +import { interactActions, ComponentInteractEventEnum } from './interact'
  8 +import { OverrideInputsDateConfig } from './index'
  9 +
  10 +export const option = {
  11 + // 时间组件展示类型,必须和 interactActions 中定义的数据一致
  12 + [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATE,
  13 + // 下拉展示
  14 + isPanel: 0,
  15 + dataset: dayjs().valueOf(),
  16 + selectStyleOptions: {
  17 + textColor: 'black',
  18 + textBackgroundColor: 'white',
  19 + textBackgroundColorAlpha: 1,
  20 + iconColor:'black',
  21 + //弹出下拉框
  22 + textSelectModalBackgroundColor: 'white',
  23 + textSelectModalTextColor: 'black'
  24 + }
  25 +}
  26 +
  27 +export default class Config extends PublicConfigClass implements CreateComponentType {
  28 + public key = OverrideInputsDateConfig.key
  29 + public attr = { ...chartInitConfig, w: 260, h: 32, zIndex: -1 }
  30 + public chartConfig = cloneDeep(OverrideInputsDateConfig)
  31 + public interactActions = interactActions
  32 + public option = cloneDeep(option)
  33 +}
  1 +<template>
  2 + <collapse-item name="展示方式" :expanded="true">
  3 + <setting-item-box name="选择方式">
  4 + <n-select v-model:value="optionData.isPanel" size="small" :options="panelOptions" />
  5 + </setting-item-box>
  6 + </collapse-item>
  7 +
  8 + <collapse-item name="时间配置" :expanded="true">
  9 + <setting-item-box name="基础">
  10 + <setting-item name="类型">
  11 + <n-select v-model:value="optionData.componentInteractEventKey" size="small" :options="datePickerTypeOptions" />
  12 + </setting-item>
  13 + </setting-item-box>
  14 +
  15 + <setting-item-box name="默认值" :alone="true">
  16 + <n-date-picker size="small" v-model:value="optionData.dataset" :type="optionData.componentInteractEventKey" />
  17 + </setting-item-box>
  18 + <setting-item-box name="文字颜色" :alone="true">
  19 + <SettingItem name="颜色">
  20 + <n-color-picker
  21 + size="small"
  22 + :modes="['hex']"
  23 + v-model:value="optionData.selectStyleOptions.textColor"
  24 + ></n-color-picker>
  25 + </SettingItem>
  26 + <SettingItem>
  27 + <n-button size="small" @click="optionData.selectStyleOptions.textColor = 'black'"> 恢复默认 </n-button>
  28 + </SettingItem>
  29 + </setting-item-box>
  30 + <setting-item-box name="背景颜色" :alone="true">
  31 + <SettingItem name="颜色">
  32 + <n-color-picker
  33 + size="small"
  34 + :modes="['hex']"
  35 + v-model:value="optionData.selectStyleOptions.textBackgroundColor"
  36 + ></n-color-picker>
  37 + </SettingItem>
  38 + <SettingItem>
  39 + <n-button size="small" @click="optionData.selectStyleOptions.textBackgroundColor = 'white'">
  40 + 恢复默认
  41 + </n-button>
  42 + </SettingItem>
  43 + </setting-item-box>
  44 + <setting-item-box name="透明度" :alone="true">
  45 + <SettingItem name="透明度">
  46 + <n-input-number
  47 + :min="0"
  48 + :max="1"
  49 + v-model:value="optionData.selectStyleOptions.textBackgroundColorAlpha"
  50 + clearable
  51 + />
  52 + </SettingItem>
  53 + </setting-item-box>
  54 + <setting-item-box name="弹出框背景色" :alone="true">
  55 + <SettingItem name="背景色">
  56 + <n-color-picker
  57 + size="small"
  58 + :modes="['hex']"
  59 + v-model:value="optionData.selectStyleOptions.textSelectModalBackgroundColor"
  60 + ></n-color-picker>
  61 + </SettingItem>
  62 + <SettingItem>
  63 + <n-button size="small" @click="optionData.selectStyleOptions.textSelectModalBackgroundColor = 'white'">
  64 + 恢复默认
  65 + </n-button>
  66 + </SettingItem>
  67 + </setting-item-box>
  68 + <setting-item-box name="弹出框文字颜色" :alone="true">
  69 + <SettingItem name="颜色">
  70 + <n-color-picker
  71 + size="small"
  72 + :modes="['hex']"
  73 + v-model:value="optionData.selectStyleOptions.textSelectModalTextColor"
  74 + ></n-color-picker>
  75 + </SettingItem>
  76 + <SettingItem>
  77 + <n-button size="small" @click="optionData.selectStyleOptions.textSelectModalTextColor = 'black'">
  78 + 恢复默认
  79 + </n-button>
  80 + </SettingItem>
  81 + </setting-item-box>
  82 + <setting-item-box name="图标颜色" :alone="true">
  83 + <SettingItem name="颜色">
  84 + <n-color-picker
  85 + size="small"
  86 + :modes="['hex']"
  87 + v-model:value="optionData.selectStyleOptions.iconColor"
  88 + ></n-color-picker>
  89 + </SettingItem>
  90 + <SettingItem>
  91 + <n-button size="small" @click="optionData.selectStyleOptions.iconColor = 'black'"> 恢复默认 </n-button>
  92 + </SettingItem>
  93 + </setting-item-box>
  94 + </collapse-item>
  95 +</template>
  96 +
  97 +<script lang="ts" setup>
  98 +import { PropType } from 'vue'
  99 +import { icon } from '@/plugins'
  100 +import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  101 +import { option } from './config'
  102 +import { ComponentInteractEventEnum } from './interact'
  103 +
  104 +const { HelpOutlineIcon } = icon.ionicons5
  105 +
  106 +const props = defineProps({
  107 + optionData: {
  108 + type: Object as PropType<typeof option>,
  109 + required: true
  110 + }
  111 +})
  112 +
  113 +const panelOptions = [
  114 + {
  115 + label: '下拉展示',
  116 + value: 0
  117 + },
  118 + {
  119 + label: '面板展示',
  120 + value: 1
  121 + }
  122 +]
  123 +
  124 +const datePickerTypeOptions = [
  125 + {
  126 + label: '日期',
  127 + value: ComponentInteractEventEnum.DATE
  128 + },
  129 + {
  130 + label: '日期时间',
  131 + value: ComponentInteractEventEnum.DATE_TIME
  132 + },
  133 + {
  134 + label: '日期范围',
  135 + value: ComponentInteractEventEnum.DATE_RANGE
  136 + },
  137 + {
  138 + label: '月份',
  139 + value: ComponentInteractEventEnum.MONTH
  140 + },
  141 + {
  142 + label: '月份范围',
  143 + value: ComponentInteractEventEnum.MONTH_RANGE
  144 + },
  145 + {
  146 + label: '年份',
  147 + value: ComponentInteractEventEnum.YEAR
  148 + },
  149 + {
  150 + label: '年份范围',
  151 + value: ComponentInteractEventEnum.YEAR_RANGE
  152 + },
  153 + {
  154 + label: '季度',
  155 + value: ComponentInteractEventEnum.QUARTER
  156 + },
  157 + {
  158 + label: '季度范围',
  159 + value: ComponentInteractEventEnum.QUARTER_RANGE
  160 + }
  161 +]
  162 +</script>
  1 +
  2 +import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
  3 +import { ChatCategoryEnum, ChatCategoryEnumName } from '@/packages/components/Informations/index.d'
  4 +import { useWidgetKey } from '@/packages/external/useWidgetKey'
  5 +
  6 +const { key, conKey, chartKey } = useWidgetKey('OverrideInputsDate', true)
  7 +
  8 +export const OverrideInputsDateConfig: ConfigType = {
  9 + key,
  10 + chartKey,
  11 + conKey,
  12 + title: '自定义时间选择器',
  13 + category: ChatCategoryEnum.MORE,
  14 + categoryName: ChatCategoryEnumName.MORE,
  15 + package: PackagesCategoryEnum.INFORMATIONS,
  16 + chartFrame: ChartFrameEnum.STATIC,
  17 + image: 'inputs_date.png'
  18 +}
  1 +<template>
  2 + <n-date-picker
  3 + v-model:value="option.dataset"
  4 + :panel="!!chartConfig.option.isPanel"
  5 + :type="chartConfig.option.componentInteractEventKey"
  6 + :style="`width:${w}px;`"
  7 + :to="false"
  8 + @update:value="onChange"
  9 + />
  10 +</template>
  11 +
  12 +<script setup lang="ts">
  13 +import { PropType, toRefs, ref, shallowReactive, watch } from 'vue'
  14 +import dayjs from 'dayjs'
  15 +import { CreateComponentType } from '@/packages/index.d'
  16 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  17 +import { useChartInteract } from '@/hooks'
  18 +import { InteractEventOn } from '@/enums/eventEnum'
  19 +import { ComponentInteractParamsEnum } from './interact'
  20 +
  21 +const props = defineProps({
  22 + chartConfig: {
  23 + type: Object as PropType<CreateComponentType>,
  24 + required: true
  25 + }
  26 +})
  27 +
  28 +const { w, h } = toRefs(props.chartConfig.attr)
  29 +
  30 +const {
  31 + textColor,
  32 + textBackgroundColor,
  33 + iconColor,
  34 + textBackgroundColorAlpha,
  35 + textSelectModalBackgroundColor,
  36 + textSelectModalTextColor
  37 +} = toRefs(props.chartConfig.option.selectStyleOptions) as any
  38 +
  39 +const rangeDate = ref<number | number[]>()
  40 +
  41 +const option = shallowReactive({
  42 + dataset: props.chartConfig.option.dataset
  43 +})
  44 +
  45 +// 监听事件改变
  46 +const onChange = (v: number | number[]) => {
  47 + if (v instanceof Array) {
  48 + // 存储到联动数据
  49 + useChartInteract(
  50 + props.chartConfig,
  51 + useChartEditStore,
  52 + {
  53 + [ComponentInteractParamsEnum.DATE_START]: v[0],
  54 + [ComponentInteractParamsEnum.DATE_END]: v[1],
  55 + [ComponentInteractParamsEnum.DATE_RANGE]: `${v[0]}-${v[1]}`
  56 + },
  57 + InteractEventOn.CHANGE
  58 + )
  59 + } else {
  60 + // 存储到联动数据
  61 + useChartInteract(
  62 + props.chartConfig,
  63 + useChartEditStore,
  64 + { [ComponentInteractParamsEnum.DATE]: v },
  65 + InteractEventOn.CHANGE
  66 + )
  67 + }
  68 +}
  69 +
  70 +// 手动更新
  71 +watch(
  72 + () => props.chartConfig.option.dataset,
  73 + (newData: number | number[]) => {
  74 + option.dataset = newData
  75 + // 关联目标组件首次请求带上默认内容
  76 + onChange(newData)
  77 + },
  78 + {
  79 + immediate: true
  80 + }
  81 +)
  82 +</script>
  83 +
  84 +<style lang="scss" scoped>
  85 +@include deep() {
  86 + .n-input {
  87 + height: v-bind('h + "px"');
  88 + display: flex;
  89 + align-items: center;
  90 + background: v-bind('textBackgroundColor') !important;
  91 + }
  92 + .n-input__input-el {
  93 + color: v-bind('textColor') !important;
  94 + }
  95 + .n-input__placeholder {
  96 + color: v-bind('textColor') !important;
  97 + }
  98 + .n-base-icon {
  99 + color: v-bind('iconColor') !important;
  100 + }
  101 + .n-date-panel{
  102 + background: v-bind('textSelectModalBackgroundColor') !important;;
  103 + }
  104 +}
  105 +</style>
  1 +import { InteractEventOn, InteractActionsType } from '@/enums/eventEnum'
  2 +
  3 +// 时间组件类型
  4 +export enum ComponentInteractEventEnum {
  5 + DATE = 'date',
  6 + DATE_TIME = 'datetime',
  7 + DATE_RANGE = 'daterange',
  8 + DATE_TIME_RANGE = 'datetimerange',
  9 + MONTH = 'month',
  10 + MONTH_RANGE = 'monthrange',
  11 + YEAR = 'year',
  12 + YEAR_RANGE = 'yearrange',
  13 + QUARTER = 'quarter',
  14 + QUARTER_RANGE = 'quarterrange'
  15 +}
  16 +
  17 +// 联动参数
  18 +export enum ComponentInteractParamsEnum {
  19 + DATE = 'date',
  20 + DATE_START = 'dateStart',
  21 + DATE_END = 'dateEnd',
  22 + DATE_RANGE = 'daterange'
  23 +}
  24 +
  25 +const time = [
  26 + {
  27 + value: ComponentInteractParamsEnum.DATE,
  28 + label: '日期'
  29 + }
  30 +]
  31 +
  32 +const timeRange = [
  33 + {
  34 + value: ComponentInteractParamsEnum.DATE_START,
  35 + label: '开始时间'
  36 + },
  37 + {
  38 + value: ComponentInteractParamsEnum.DATE_END,
  39 + label: '结束时间'
  40 + },
  41 + {
  42 + value: ComponentInteractParamsEnum.DATE_RANGE,
  43 + label: '日期范围'
  44 + }
  45 +]
  46 +
  47 +// 定义组件触发回调事件
  48 +export const interactActions: InteractActionsType[] = [
  49 + {
  50 + interactType: InteractEventOn.CHANGE,
  51 + interactName: '选择完成',
  52 + componentEmitEvents: {
  53 + [ComponentInteractEventEnum.DATE]: time,
  54 + [ComponentInteractEventEnum.DATE_TIME]: time,
  55 + [ComponentInteractEventEnum.DATE_RANGE]: timeRange,
  56 + [ComponentInteractEventEnum.MONTH]: time,
  57 + [ComponentInteractEventEnum.MONTH_RANGE]: timeRange,
  58 + [ComponentInteractEventEnum.QUARTER]: time,
  59 + [ComponentInteractEventEnum.QUARTER_RANGE]: timeRange,
  60 + [ComponentInteractEventEnum.YEAR]: time,
  61 + [ComponentInteractEventEnum.YEAR_RANGE]: timeRange,
  62 + }
  63 + }
  64 +]
  1 +import cloneDeep from 'lodash/cloneDeep'
  2 +import { PublicConfigClass } from '@/packages/public'
  3 +import { CreateComponentType } from '@/packages/index.d'
  4 +import { chartInitConfig } from '@/settings/designSetting'
  5 +import { COMPONENT_INTERACT_EVENT_KET } from '@/enums/eventEnum'
  6 +import { interactActions, ComponentInteractEventEnum } from './interact'
  7 +import { OverrideSelectConfig } from './index'
  8 +
  9 +export const option = {
  10 + // 时间组件展示类型,必须和 interactActions 中定义的数据一致
  11 + [COMPONENT_INTERACT_EVENT_KET]: ComponentInteractEventEnum.DATA,
  12 + // 默认值
  13 + selectValue: '1',
  14 + // 暴露配置内容给用户
  15 + dataset: [
  16 + {
  17 + label: '选项1',
  18 + value: '1'
  19 + },
  20 + {
  21 + label: '选项2',
  22 + value: '2'
  23 + },
  24 + {
  25 + label: '选项3',
  26 + value: '3'
  27 + }
  28 + ],
  29 + selectStyleOptions: {
  30 + textColor: 'black',
  31 + textBackgroundColor: 'white',
  32 + textBackgroundColorAlpha: 1,
  33 + iconColor:'grey',
  34 + //弹出下拉框
  35 + textSelectModalBackgroundColor: 'white',
  36 + textSelectModalTextColor: 'black'
  37 + }
  38 +}
  39 +
  40 +export default class Config extends PublicConfigClass implements CreateComponentType {
  41 + public key = OverrideSelectConfig.key
  42 + public attr = { ...chartInitConfig, w: 260, h: 32, zIndex: -1 }
  43 + public chartConfig = cloneDeep(OverrideSelectConfig)
  44 + public interactActions = interactActions
  45 + public option = cloneDeep(option)
  46 +}
  1 +<template>
  2 + <collapse-item name="下拉配置" :expanded="true">
  3 + <setting-item-box name="选择项" :alone="true">
  4 + <SettingItem name="选择项">
  5 + <n-select size="small" v-model:value="optionData.selectValue" :options="optionData.dataset" />
  6 + </SettingItem>
  7 + </setting-item-box>
  8 + <setting-item-box name="文字颜色" :alone="true">
  9 + <SettingItem name="颜色">
  10 + <n-color-picker
  11 + size="small"
  12 + :modes="['hex']"
  13 + v-model:value="optionData.selectStyleOptions.textColor"
  14 + ></n-color-picker>
  15 + </SettingItem>
  16 + <SettingItem>
  17 + <n-button size="small" @click="optionData.selectStyleOptions.textColor = 'black'"> 恢复默认 </n-button>
  18 + </SettingItem>
  19 + </setting-item-box>
  20 + <setting-item-box name="背景颜色" :alone="true">
  21 + <SettingItem name="颜色">
  22 + <n-color-picker
  23 + size="small"
  24 + :modes="['hex']"
  25 + v-model:value="optionData.selectStyleOptions.textBackgroundColor"
  26 + ></n-color-picker>
  27 + </SettingItem>
  28 + <SettingItem>
  29 + <n-button size="small" @click="optionData.selectStyleOptions.textBackgroundColor = 'white'">
  30 + 恢复默认
  31 + </n-button>
  32 + </SettingItem>
  33 + </setting-item-box>
  34 + <setting-item-box name="透明度" :alone="true">
  35 + <SettingItem name="透明度">
  36 + <n-input-number
  37 + :min="0"
  38 + :max="1"
  39 + v-model:value="optionData.selectStyleOptions.textBackgroundColorAlpha"
  40 + clearable
  41 + />
  42 + </SettingItem>
  43 + </setting-item-box>
  44 + <setting-item-box name="弹出框背景色" :alone="true">
  45 + <SettingItem name="背景色">
  46 + <n-color-picker
  47 + size="small"
  48 + :modes="['hex']"
  49 + v-model:value="optionData.selectStyleOptions.textSelectModalBackgroundColor"
  50 + ></n-color-picker>
  51 + </SettingItem>
  52 + <SettingItem>
  53 + <n-button size="small" @click="optionData.selectStyleOptions.textSelectModalBackgroundColor = 'white'">
  54 + 恢复默认
  55 + </n-button>
  56 + </SettingItem>
  57 + </setting-item-box>
  58 + <setting-item-box name="弹出框文字颜色" :alone="true">
  59 + <SettingItem name="颜色">
  60 + <n-color-picker
  61 + size="small"
  62 + :modes="['hex']"
  63 + v-model:value="optionData.selectStyleOptions.textSelectModalTextColor"
  64 + ></n-color-picker>
  65 + </SettingItem>
  66 + <SettingItem>
  67 + <n-button size="small" @click="optionData.selectStyleOptions.textSelectModalTextColor = 'black'">
  68 + 恢复默认
  69 + </n-button>
  70 + </SettingItem>
  71 + </setting-item-box>
  72 + <setting-item-box name="图标颜色" :alone="true">
  73 + <SettingItem name="颜色">
  74 + <n-color-picker
  75 + size="small"
  76 + :modes="['hex']"
  77 + v-model:value="optionData.selectStyleOptions.iconColor"
  78 + ></n-color-picker>
  79 + </SettingItem>
  80 + <SettingItem>
  81 + <n-button size="small" @click="optionData.selectStyleOptions.iconColor = 'black'"> 恢复默认 </n-button>
  82 + </SettingItem>
  83 + </setting-item-box>
  84 + </collapse-item>
  85 +</template>
  86 +
  87 +<script lang="ts" setup>
  88 +import { PropType } from 'vue'
  89 +import { CollapseItem, SettingItemBox } from '@/components/Pages/ChartItemSetting'
  90 +import { option } from './config'
  91 +
  92 +defineProps({
  93 + optionData: {
  94 + type: Object as PropType<typeof option>,
  95 + required: true
  96 + }
  97 +})
  98 +</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('OverrideSelect', true)
  6 +
  7 +export const OverrideSelectConfig: 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: 'inputs_select.png'
  17 +}
  1 +<template>
  2 + <n-select
  3 + v-model:value="option.value.selectValue"
  4 + :options="option.value.dataset"
  5 + :style="`width:${w}px;`"
  6 + :to="false"
  7 + @update:value="onChange"
  8 + />
  9 +</template>
  10 +
  11 +<script setup lang="ts">
  12 +import { PropType, toRefs, shallowReactive, watch } from 'vue'
  13 +import { CreateComponentType } from '@/packages/index.d'
  14 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  15 +import { useChartInteract } from '@/hooks'
  16 +import { InteractEventOn } from '@/enums/eventEnum'
  17 +import { ComponentInteractParamsEnum } from './interact'
  18 +
  19 +const props = defineProps({
  20 + chartConfig: {
  21 + type: Object as PropType<CreateComponentType>,
  22 + required: true
  23 + }
  24 +})
  25 +
  26 +const { w, h } = toRefs(props.chartConfig.attr)
  27 +
  28 +const {
  29 + textColor,
  30 + textBackgroundColor,
  31 + iconColor,
  32 + textBackgroundColorAlpha,
  33 + textSelectModalBackgroundColor,
  34 + textSelectModalTextColor
  35 +} = toRefs(props.chartConfig.option.selectStyleOptions) as any
  36 +
  37 +const option = shallowReactive({
  38 + value: {
  39 + selectValue: props.chartConfig.option.selectValue,
  40 + dataset: props.chartConfig.option.dataset
  41 + }
  42 +})
  43 +
  44 +// 监听事件改变
  45 +const onChange = (v: string) => {
  46 + // 存储到联动数据
  47 + useChartInteract(
  48 + props.chartConfig,
  49 + useChartEditStore,
  50 + { [ComponentInteractParamsEnum.DATA]: v },
  51 + InteractEventOn.CHANGE
  52 + )
  53 +}
  54 +
  55 +// 手动更新
  56 +watch(
  57 + () => props.chartConfig.option,
  58 + (newData: any) => {
  59 + option.value = newData
  60 + onChange(newData.selectValue)
  61 + },
  62 + {
  63 + immediate: true,
  64 + deep: true
  65 + }
  66 +)
  67 +</script>
  68 +
  69 +<style lang="css"></style>
  70 +
  71 +<style lang="scss" scoped>
  72 +@include deep() {
  73 + .n-base-selection-label {
  74 + height: v-bind('h + "px"');
  75 + display: flex;
  76 + align-items: center;
  77 + background: v-bind('textBackgroundColor') !important;
  78 + opacity: v-bind('textBackgroundColorAlpha') !important;
  79 + }
  80 + .n-base-selection-input__content {
  81 + color: v-bind('textColor');
  82 + }
  83 + .n-base-icon {
  84 + color: v-bind('iconColor') !important;
  85 + }
  86 + .n-select-menu {
  87 + background: v-bind('textSelectModalBackgroundColor') !important;
  88 + }
  89 + .n-base-select-option__content {
  90 + color: v-bind('textSelectModalTextColor') !important;
  91 + }
  92 +}
  93 +</style>
  1 +import { InteractEventOn, InteractActionsType } from '@/enums/eventEnum'
  2 +
  3 +// 时间组件类型
  4 +export enum ComponentInteractEventEnum {
  5 + DATA = 'data'
  6 +}
  7 +
  8 +// 联动参数
  9 +export enum ComponentInteractParamsEnum {
  10 + DATA = 'data'
  11 +}
  12 +
  13 +// 定义组件触发回调事件
  14 +export const interactActions: InteractActionsType[] = [
  15 + {
  16 + interactType: InteractEventOn.CHANGE,
  17 + interactName: '选择完成',
  18 + componentEmitEvents: {
  19 + [ComponentInteractEventEnum.DATA]: [
  20 + {
  21 + value: ComponentInteractParamsEnum.DATA,
  22 + label: '选择项'
  23 + }
  24 + ]
  25 + }
  26 + }
  27 +]
@@ -3,6 +3,9 @@ import { ComposesList } from '@/packages/components/external/Composes' @@ -3,6 +3,9 @@ import { ComposesList } from '@/packages/components/external/Composes'
3 import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d' 3 import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
4 import { ClockConfig } from '@/packages/components/external/Decorates/Mores/Icon' 4 import { ClockConfig } from '@/packages/components/external/Decorates/Mores/Icon'
5 import { OverrideImageConfig } from '@/packages/components/external/Informations/Mores/OverrideImage' 5 import { OverrideImageConfig } from '@/packages/components/external/Informations/Mores/OverrideImage'
  6 +import { OverrideCarouselConfig } from '@/packages/components/external/Informations/Mores/OverrideCarousel'
  7 +import { OverrideSelectConfig } from '@/packages/components/external/Informations/Mores/OverrideSelect'
  8 +import { OverrideInputsDateConfig } from '@/packages/components/external/Informations/Mores/OverrideInputsDate'
6 9
7 export function useInjectLib(packagesList: EPackagesType) { 10 export function useInjectLib(packagesList: EPackagesType) {
8 11
@@ -10,6 +13,9 @@ export function useInjectLib(packagesList: EPackagesType) { @@ -10,6 +13,9 @@ export function useInjectLib(packagesList: EPackagesType) {
10 13
11 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig) 14 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig)
12 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideImageConfig) 15 addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideImageConfig)
  16 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideCarouselConfig)
  17 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideSelectConfig)
  18 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.INFORMATIONS, OverrideInputsDateConfig)
13 } 19 }
14 20
15 /** 21 /**