Commit e60609330666793fb811e6b20d60291989bd1278

Authored by xp.Huang
2 parents ddb436ba b7cdeb86

Merge branch 'ft' into 'main_dev'

feat(packages/external): 点击按钮新增跳转提示和三维模型支持mtl材质和材质贴图上传显示和单个摄像头新增播放配置

See merge request yunteng/thingskit-view!71
... ... @@ -11,8 +11,6 @@ export const useChartInteract = (
11 11 useChartEditStore: ChartEditStoreType,
12 12 param: { [T: string]: any },
13 13 interactEventOn: string,
14   - target: string,
15   - status: boolean
16 14 ) => {
17 15 const chartEditStore = useChartEditStore()
18 16 const { interactEvents } = chartConfig.events
... ...
... ... @@ -26,6 +26,10 @@ const props = defineProps({
26 26 },
27 27 index: {
28 28 type: Number
  29 + },
  30 + autoplay: {
  31 + type: Boolean,
  32 + default: false
29 33 }
30 34 })
31 35
... ... @@ -40,7 +44,7 @@ const options: VideoJsPlayerOptions = {
40 44 language: 'zh-CN', // 设置语言
41 45 controls: true, // 是否显示控制条
42 46 preload: 'auto', // 预加载
43   - autoplay: true, // 是否自动播放
  47 + autoplay: props.autoplay, // 是否自动播放
44 48 fluid: false, // 自适应宽高
45 49 poster: props?.avatar || '',
46 50 src: props?.sourceSrc || '', // 要嵌入的视频源的源 URL
... ...
... ... @@ -9,11 +9,7 @@ export const option = {
9 9 url: ''
10 10 }
11 11 ] as any,
12   - // 自动播放的间隔(ms)
13   - interval: 5000,
14   - autoplay: true,
15   - effect: 'slide',
16   - displayMode: 'singleGrid'
  12 + autoplay: false,
17 13 }
18 14
19 15 export default class Config extends PublicConfigClass implements CreateComponentType {
... ...
... ... @@ -7,6 +7,11 @@
7 7 </n-input-group>
8 8 </setting-item>
9 9 </setting-item-box>
  10 + <setting-item-box name="自动播放">
  11 + <setting-item>
  12 + <n-switch v-model:value="optionData.autoplay" size="small" />
  13 + </setting-item>
  14 + </setting-item-box>
10 15 </CollapseItem>
11 16 </template>
12 17
... ... @@ -21,5 +26,4 @@ defineProps({
21 26 required: true
22 27 }
23 28 })
24   -
25 29 </script>
... ...
... ... @@ -10,6 +10,7 @@
10 10 :key="item + index"
11 11 :sourceSrc="item.url"
12 12 :index="index"
  13 + :autoplay="autoplay"
13 14 />
14 15 </div>
15 16 </n-gi>
... ... @@ -32,6 +33,8 @@ const props = defineProps({
32 33
33 34 const { h } = toRefs(props.chartConfig.attr)
34 35
  36 +const { autoplay } = toRefs(props.chartConfig.option)
  37 +
35 38 const responsiveComputeValue = ref(0)
36 39
37 40 const option = shallowReactive({
... ...
  1 +<template>
  2 + <setting-item :name="fileSizeMsg">
  3 + <n-upload
  4 + :max="max"
  5 + :customRequest="customRequest"
  6 + :onBeforeUpload="beforeUploadHandle"
  7 + :default-file-list="fileList"
  8 + @change="handleUploadChange"
  9 + >
  10 + <n-button> 上传文件</n-button>
  11 + </n-upload>
  12 + </setting-item>
  13 +</template>
  14 +
  15 +<script setup lang="ts">
  16 +import { ref, nextTick } from 'vue'
  17 +import { SettingItem } from '@/components/Pages/ChartItemSetting'
  18 +import { uploadFile } from '@/api/external/contentSave/content'
  19 +import type { UploadFileInfo } from 'naive-ui'
  20 +import { UploadCustomRequestOptions } from 'naive-ui'
  21 +
  22 +const props = defineProps({
  23 + max: {
  24 + type: Number,
  25 + default: 1
  26 + },
  27 + fileSizeConst: {
  28 + type: Number,
  29 + default: 50
  30 + },
  31 + singleFileType: {
  32 + type: String,
  33 + default: 'mtl'
  34 + },
  35 + threeSupportFileFormat: {
  36 + type: Array,
  37 + default: () => []
  38 + }
  39 +})
  40 +
  41 +const emits = defineEmits(['fileStaticUri'])
  42 +
  43 +const fileList = ref<UploadFileInfo[]>([])
  44 +
  45 +const fileSizeMsg = ref(`文件需小于 ${props.fileSizeConst}M;格式为${props.threeSupportFileFormat.join(',')}的文件`)
  46 +
  47 +const extname = (filename: string) => {
  48 + if (!filename || typeof filename != 'string') {
  49 + return false
  50 + }
  51 + let a = filename.split('').reverse().join('')
  52 + let b = a.substring(0, a.search(/\./)).split('').reverse().join('')
  53 + return b
  54 +}
  55 +
  56 +// 上传图片前置处理
  57 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
  58 +//@ts-ignore
  59 +const beforeUploadHandle = async ({ file }) => {
  60 + const type = extname(file.file.name) as string
  61 + const size = file.file.size
  62 + if (size / (1024 * 1024) > props.fileSizeConst) {
  63 + window['$message'].warning(`文件超出 ${props.fileSizeConst}M限制,请重新上传!`)
  64 + return false
  65 + }
  66 + if (!props.threeSupportFileFormat.includes(type)) {
  67 + window['$message'].warning('文件格式不符合,请重新上传!')
  68 + return false
  69 + }
  70 + return true
  71 +}
  72 +
  73 +const handleUploadChange = (data: { fileList: UploadFileInfo[] }) => {
  74 + fileList.value = []
  75 + emits('fileStaticUri', data.fileList as never as any)
  76 +}
  77 +
  78 +// 自定义上传操作
  79 +const customRequest = (options: UploadCustomRequestOptions) => {
  80 + const { file } = options
  81 + nextTick(async () => {
  82 + if (file.file) {
  83 + const formData = new FormData()
  84 + formData.append('file', file.file)
  85 + const uploadRes = await uploadFile(formData)
  86 + if (uploadRes) {
  87 + fileList.value.push({
  88 + id: -Math.random() + '',
  89 + name: uploadRes?.fileName,
  90 + status: 'finished',
  91 + url: uploadRes?.fileStaticUri
  92 + })
  93 + fileList.value = fileList.value.filter((item: UploadFileInfo) => {
  94 + return item.status === 'finished'
  95 + })
  96 + emits('fileStaticUri', fileList.value)
  97 + window['$message'].success('上传文件成功!')
  98 + }
  99 + } else {
  100 + window['$message'].error('上传文件失败,请稍后重试!')
  101 + }
  102 + })
  103 +}
  104 +</script>
... ...
... ... @@ -6,7 +6,7 @@ import { chartInitConfig } from '@/settings/designSetting'
6 6
7 7 export const option = {
8 8 //vue3dLoader支持数组或字符串,暂且绑定字符串,这个插件可以加载多个模型
9   - dataset: "",
  9 + dataset: '',
10 10 backgroundColor: '', //场景背景色
11 11 backgroundAlpha: 0, //场景透明度
12 12 enableDamping: false, //是否启用阻尼
... ... @@ -19,7 +19,9 @@ export const option = {
19 19 */
20 20 outputEncoding: 'liner',
21 21 clearScene: false, //是否清空场景内容
22   - lights: [] //灯光,暂且没实现
  22 + lights: [], //灯光,暂且没实现
  23 + mtlPath: [], //.mtl材质路径,比如搭配obj使用
  24 + textureImage: [] //jpg/png 材质路径
23 25 }
24 26
25 27 export default class Config extends PublicConfigClass implements CreateComponentType {
... ...
1 1 <template>
2 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>
  3 + <setting-item-box :alone="true">
  4 + <template #name>
  5 + <n-text>提示</n-text>
  6 + <n-tooltip trigger="hover">
  7 + <template #trigger>
  8 + <n-icon size="21" :depth="3">
  9 + <help-outline-icon></help-outline-icon>
  10 + </n-icon>
  11 + </template>
  12 + <span class="help-span">{{ threeFileHelpMessgae }}</span>
  13 + </n-tooltip>
  14 + </template>
  15 + <FileUpload
  16 + :max="1"
  17 + :threeSupportFileFormat="threeSupportFileFormat"
  18 + :singleFileType="singleFileTypeNotMtl"
  19 + @fileStaticUri="handleFileStaticUri"
  20 + />
  21 + </setting-item-box>
  22 + <setting-item-box name="mtl材质文件上传">
  23 + <FileUpload :max="10" :threeSupportFileFormat="supportFileMtl" @fileStaticUri="handleFileMtlStaticUri" />
  24 + </setting-item-box>
  25 + <setting-item-box name="材质贴图上传">
  26 + <FileUpload
  27 + :max="10"
  28 + :threeSupportFileFormat="supportFileTextureImage"
  29 + @fileStaticUri="handleFileTextureImageStaticUri"
  30 + />
14 31 </setting-item-box>
15 32 <setting-item-box name="属性配置">
16 33 <setting-item name="场景色(需要这种格式HEX #000000,否则失效)">
... ... @@ -39,12 +56,15 @@
39 56 </template>
40 57
41 58 <script setup lang="ts">
42   -import { PropType, h, ref, nextTick } from 'vue'
  59 +import { PropType, ref } from 'vue'
43 60 import { option } from './config'
44 61 import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
45 62 import { NInputNumber, NSelect } from 'naive-ui'
46   -import { uploadFile } from '@/api/external/contentSave/content'
47   -import { UploadCustomRequestOptions } from 'naive-ui'
  63 +import FileUpload from './components/FileUpload.vue'
  64 +import { icon } from '@/plugins'
  65 +import type { UploadFileInfo } from 'naive-ui'
  66 +
  67 +const { HelpOutlineIcon } = icon.ionicons5
48 68
49 69 const props = defineProps({
50 70 optionData: {
... ... @@ -53,17 +73,15 @@ const props = defineProps({
53 73 }
54 74 })
55 75
56   -const defaultFileList = ref([])
  76 +const singleFileTypeNotMtl = ref('')
57 77
58   -//文件限制大小
59   -const fileSizeConst = ref(50)
  78 +const threeFileHelpMessgae = ref('如果格式为obj,则上传顺序是,先上传材质贴图(png或jpg)、mtl材质文件,最后是obj')
60 79
61   -/**
62   - * 三维文件格式,vue-3d-loader是这个插件支持的格式,其他格式待解决
63   - */
64 80 const threeSupportFileFormat = ['fbx', 'obj', 'gltf', 'stl', 'dae', 'glb', 'ply', 'json']
65 81
66   -const fileSizeMsg = ref(`文件需小于 ${fileSizeConst.value}M ,格式为${threeSupportFileFormat.join(',')}的文件`)
  82 +const supportFileMtl = ['mtl']
  83 +
  84 +const supportFileTextureImage = ['jpg', 'png']
67 85
68 86 const handleChange = (e: boolean) => {
69 87 if (e) props.optionData.dataset = ''
... ... @@ -74,51 +92,24 @@ const encodinghList = [
74 92 { label: 'sRGB ', value: 'sRGB ' }
75 93 ]
76 94
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
  95 +const handleFileStaticUri = (value: UploadFileInfo[]) => {
  96 + props.optionData.dataset = value[0]?.url as string
85 97 }
86 98
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
  99 +const handleFileMtlStaticUri = (value: UploadFileInfo[]) => {
  100 + props.optionData.mtlPath = value.map(item => item?.url) as any
103 101 }
104 102
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   - })
  103 +const handleFileTextureImageStaticUri = (value: UploadFileInfo[]) => {
  104 + props.optionData.textureImage = value.map(item => item?.url) as any
121 105 }
122 106 </script>
123 107
124   -<style lang="scss" scoped></style>
  108 +<style lang="scss" scoped>
  109 +.help-span {
  110 + display: flex;
  111 + flex-wrap: wrap;
  112 + width: 8vw;
  113 + color: white;
  114 +}
  115 +</style>
... ...
... ... @@ -9,6 +9,8 @@
9 9 :height="h"
10 10 :width="w"
11 11 :filePath="dataset"
  12 + :mltPath="mtlPath"
  13 + :textureImage="textureImage"
12 14 :autoPlay="autoPlay"
13 15 :enableDamping="enableDamping"
14 16 :outputEncoding="outputEncoding"
... ... @@ -101,6 +103,8 @@ const {
101 103 autoPlay,
102 104 enableDamping,
103 105 dataset,
  106 + mtlPath,
  107 + textureImage,
104 108 outputEncoding,
105 109 clearScene,
106 110 dampingFactor
... ...
... ... @@ -19,12 +19,17 @@ export const option = {
19 19 targetButton: ''
20 20 }
21 21 ],
22   - buttonType:'primary'
  22 + buttonType: 'primary',
  23 + buttonGhost: false,
  24 + buttonDashed: false,
  25 + buttonColor: '',
  26 + buttonTextColor: 'white',
  27 + buttonTextSize: '16',
23 28 }
24 29
25 30 export default class Config extends PublicConfigClass implements CreateComponentType {
26 31 public key = ButtonConfig.key
27   - public attr = { ...chartInitConfig, w: 460, h: 32, zIndex: -1 }
  32 + public attr = { ...chartInitConfig, w: 90, h: 40, zIndex: -1 }
28 33 public chartConfig = cloneDeep(ButtonConfig)
29 34 public interactActions = interactActions
30 35 public option = cloneDeep(option)
... ...
... ... @@ -3,17 +3,53 @@
3 3 <setting-item-box name="默认值" :alone="true">
4 4 <n-select size="small" v-model:value="optionData.buttonType" :options="buttonTypeOptions" />
5 5 </setting-item-box>
6   - <setting-item-box name="跳转配置项" :alone="true">
  6 + <setting-item-box name="虚线">
  7 + <setting-item name="">
  8 + <n-switch v-model:value="optionData.buttonDashed" />
  9 + </setting-item>
  10 + </setting-item-box>
  11 + <setting-item-box name="透明">
  12 + <setting-item name="">
  13 + <n-switch v-model:value="optionData.buttonGhost" />
  14 + </setting-item>
  15 + </setting-item-box>
  16 + <setting-item-box name="颜色">
  17 + <setting-item name="">
  18 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonColor"></n-color-picker>
  19 + </setting-item>
  20 + </setting-item-box>
  21 + <setting-item-box name="文字颜色">
  22 + <setting-item name="">
  23 + <n-color-picker size="small" :modes="['hex']" v-model:value="optionData.buttonTextColor"></n-color-picker>
  24 + </setting-item>
  25 + </setting-item-box>
  26 + <setting-item-box name="文字大小">
  27 + <setting-item name="">
  28 + <n-input-number v-model:value="optionData.buttonTextSize" />
  29 + </setting-item>
  30 + </setting-item-box>
  31 + <setting-item-box :alone="true">
  32 + <template #name>
  33 + <n-text>跳转</n-text>
  34 + <n-tooltip trigger="hover">
  35 + <template #trigger>
  36 + <n-icon size="21" :depth="3">
  37 + <help-outline-icon></help-outline-icon>
  38 + </n-icon>
  39 + </template>
  40 + <span class="help-span">{{ threeFileHelpMessgae }}</span>
  41 + </n-tooltip>
  42 + </template>
7 43 <setting-item v-for="(item, index) in optionData.dataset" :key="index">
8 44 <n-input v-model:value="item.label" size="small" placeholder="按钮文字"></n-input>
9 45 <div style="height: 5px"></div>
10   - <n-input v-model:value="item.currentButton" size="small" placeholder="当前按钮"></n-input>
  46 + <n-input v-model:value="item.currentButton" size="small" placeholder="填写当前按钮id(事件里面)"></n-input>
11 47 <div style="height: 5px"></div>
12   - <n-input v-model:value="item.targetButton" size="small" placeholder="目标按钮"></n-input>
  48 + <n-input v-model:value="item.targetButton" size="small" placeholder="填写目标按钮id(事件里面)"></n-input>
13 49 <div style="height: 5px"></div>
14   - <n-input v-model:value="item.current" size="small" placeholder="当前页面"></n-input>
  50 + <n-input v-model:value="item.current" size="small" placeholder="填写当前页面id(事件里面)"></n-input>
15 51 <div style="height: 5px"></div>
16   - <n-input v-model:value="item.target" size="small" placeholder="目标页面"></n-input>
  52 + <n-input v-model:value="item.target" size="small" placeholder="填写目标页面id(事件里面)"></n-input>
17 53 <div style="height: 5px"></div>
18 54 </setting-item>
19 55 </setting-item-box>
... ... @@ -21,9 +57,10 @@
21 57 </template>
22 58
23 59 <script lang="ts" setup>
24   -import { PropType } from 'vue'
  60 +import { PropType, ref } from 'vue'
25 61 import { CollapseItem, SettingItemBox } from '@/components/Pages/ChartItemSetting'
26 62 import { option } from './config'
  63 +import { icon } from '@/plugins'
27 64
28 65 defineProps({
29 66 optionData: {
... ... @@ -32,6 +69,12 @@ defineProps({
32 69 }
33 70 })
34 71
  72 +const { HelpOutlineIcon } = icon.ionicons5
  73 +
  74 +const threeFileHelpMessgae = ref(`
  75 +在事件里面复制对应组件id,填写你要跳转的目标,相当于一个按钮就绑定了一个页面(分组),然后跳转到另一个页面,其实就是点击当前页隐藏,目标页显示
  76 +`)
  77 +
35 78 const buttonTypeOptions = [
36 79 {
37 80 label: 'default',
... ... @@ -63,3 +106,11 @@ const buttonTypeOptions = [
63 106 }
64 107 ]
65 108 </script>
  109 +<style lang="scss" scoped>
  110 +.help-span {
  111 + display: flex;
  112 + flex-wrap: wrap;
  113 + width: 8vw;
  114 + color: white;
  115 +}
  116 +</style>
... ...
... ... @@ -2,9 +2,13 @@
2 2 <n-button
3 3 :style="{ width: `${w}px`, height: `${h}px` }"
4 4 :type="buttonType"
5   - @click="onClick(option.value.dataset[0],true)"
6   - >{{ option.value.dataset[0].label }}</n-button
  5 + :dashed="buttonDashed"
  6 + :ghost="buttonGhost"
  7 + :color="buttonColor"
  8 + @click="onClick(option.value.dataset[0])"
7 9 >
  10 + <span class="button-text-color">{{ option.value.dataset[0].label }}</span>
  11 + </n-button>
8 12 </template>
9 13
10 14 <script setup lang="ts">
... ... @@ -24,24 +28,24 @@ const props = defineProps({
24 28 })
25 29
26 30 const { w, h } = toRefs(props.chartConfig.attr)
27   -const { buttonType } = toRefs(props.chartConfig.option)
  31 +const { buttonType, buttonDashed, buttonGhost, buttonColor, buttonTextColor, buttonTextSize } = toRefs(
  32 + props.chartConfig.option
  33 +)
28 34 const option = shallowReactive({
29 35 value: cloneDeep(props.chartConfig.option)
30 36 })
31 37
32   -const onClick = (v: any, s: boolean) => {
  38 +const onClick = (v: any) => {
33 39 useChartInteract(
34 40 props.chartConfig,
35 41 useChartEditStore,
36 42 { [ComponentInteractParamsEnum.DATA]: v?.current },
37 43 InteractEventOn.CHANGE,
38   - v?.target,
39   - s
40 44 )
41 45 }
42 46
43 47 onMounted(() => {
44   - onClick(option.value.dataset[0], false)
  48 + onClick(option.value.dataset[0])
45 49 })
46 50
47 51 // 手动更新
... ... @@ -49,7 +53,7 @@ watch(
49 53 () => props.chartConfig.option,
50 54 (newData: any) => {
51 55 option.value = newData
52   - // onClick(newData.tabValue, true)
  56 + // onClick(newData.tabValue)
53 57 },
54 58 {
55 59 immediate: true,
... ... @@ -57,3 +61,10 @@ watch(
57 61 }
58 62 )
59 63 </script>
  64 +
  65 +<style lang="scss" scoped>
  66 +.button-text-color {
  67 + color: v-bind('buttonTextColor');
  68 + font-size: v-bind('`${buttonTextSize}px`');
  69 +}
  70 +</style>
... ...
... ... @@ -94,7 +94,6 @@ const fileList = ref<UploadFileInfo[]>([])
94 94 watch(
95 95 () => props.optionData,
96 96 newValue => {
97   - console.log(newValue)
98 97 fileList.value = newValue?.dataset
99 98 },
100 99 {
... ... @@ -120,7 +119,6 @@ const beforeUploadHandle = async ({ file }) => {
120 119 }
121 120
122 121 const handleUploadChange = (data: { fileList: UploadFileInfo[] }) => {
123   - console.log(data.fileList)
124 122 props.optionData.dataset = data.fileList as never as any
125 123 }
126 124
... ... @@ -147,7 +145,6 @@ const customRequest = (options: UploadCustomRequestOptions) => {
147 145 return item.status === 'finished'
148 146 })
149 147 fileList.value = fileArr
150   - console.log(fileList.value)
151 148 props.optionData.dataset = fileList.value as never as any
152 149 window['$message'].success('添加图片成功!')
153 150 }
... ...