Commit 478db8527d890916b2b522bb250e44ae31f25ae6
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
| 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 | 4 | enum Api { |
| 5 | 5 | GET_DICT = '/dict_item', |
| 6 | - UPLOAD = '/oss/upload' | |
| 6 | + UPLOAD = '/oss/upload', | |
| 7 | + DOWNLOAD = '/oss/download_file/' | |
| 7 | 8 | } |
| 8 | 9 | |
| 9 | 10 | export const getDictItemByCode = (value: string) => { |
| ... | ... | @@ -21,3 +22,7 @@ export const upload = (file: FormData) => { |
| 21 | 22 | params: file |
| 22 | 23 | }) |
| 23 | 24 | } |
| 25 | + | |
| 26 | +export const downloadFile = (fileName: string) => { | |
| 27 | + return defHttp.get({ url: `${Api.DOWNLOAD}${fileName}` }) | |
| 28 | +} | ... | ... |
571 KB
| ... | ... | @@ -27,12 +27,21 @@ export class Basic { |
| 27 | 27 | |
| 28 | 28 | this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000) |
| 29 | 29 | this.camera.position.set(0, 30, -250) |
| 30 | - | |
| 30 | + /** | |
| 31 | + * 这里升级版本有冲突,升级版本可以使用这里的代码,官方还未修复 | |
| 32 | + * 修复官方Threejs生成缩略图无效问题,少加一个Threejs配置 | |
| 33 | + * 修改后的代码在注释之间,并标注好源代码和修改后代码,方便回溯 | |
| 34 | + * 源代码 属于新增代码,源代码无 | |
| 35 | + * 修改后代码 preserveDrawingBuffer: true //缩略图生效需开启 | |
| 36 | + * | |
| 37 | + */ | |
| 31 | 38 | this.renderer = new THREE.WebGLRenderer({ |
| 32 | 39 | // canvas: this.dom, |
| 33 | 40 | alpha: true, // 透明 |
| 34 | - antialias: true // 抗锯齿 | |
| 41 | + antialias: true, // 抗锯齿 | |
| 42 | + preserveDrawingBuffer: true//缩略图生效需开启 | |
| 35 | 43 | }) |
| 44 | + //ft | |
| 36 | 45 | this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比 |
| 37 | 46 | this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高 |
| 38 | 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 | 2 | import { Title2Config } from './Title2/index' |
| 3 | 3 | import { Title3Config } from './Title3/index' |
| 4 | 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> | ... | ... |
| 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 | 3 | import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d' |
| 4 | 4 | import { ClockConfig } from '@/packages/components/external/Decorates/Mores/Icon' |
| 5 | 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 | 10 | export function useInjectLib(packagesList: EPackagesType) { |
| 8 | 11 | |
| ... | ... | @@ -10,6 +13,9 @@ export function useInjectLib(packagesList: EPackagesType) { |
| 10 | 13 | |
| 11 | 14 | addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig) |
| 12 | 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 | /** | ... | ... |