初始化

This commit is contained in:
砂糖
2025-11-08 10:38:36 +08:00
commit 3beeec7296
1626 changed files with 198488 additions and 0 deletions

View File

@@ -0,0 +1,441 @@
<template>
<!-- 添加一个类似鼠标hover事件 -->
<div class="marquee-box">
<div class="scroll-area">
<audio
:ref="`audioPlayer${config.code}`"
muted
autoplay
crossorigin="anonymous"
/>
<!-- 设置margin使内容 有从无到有的出现效果 -->
<div
class="marquee-container"
@mouseenter.stop="mouseenter"
@mouseleave.stop="mouseleave"
>
<div class="icon">
<icon-svg
v-if="config.customize.icon.name && config.customize.icon.position === 'left'"
:name="config.customize.icon.name"
:style="{ color: config.customize.icon.color, width: config.customize.fontSize + 'px',height: config.customize.fontSize + 'px' }"
/>
</div>
<svg class="svg-container">
<defs>
<linearGradient
:id="'backgroundGradient-' + config.code"
:x1="0"
:y1="['to top right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
:x2="['to right', 'to bottom right', 'to top right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
:y2="['to bottom', 'to bottom right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
>
<stop
offset="0%"
:stop-color="config.customize.backgroundColorType === 'pure' ? config.customize.backgroundColor : config.customize.bgGradientColor0"
/>
<stop
offset="100%"
:stop-color="config.customize.backgroundColorType === 'pure' ? config.customize.backgroundColor : config.customize.bgGradientColor1"
/>
</linearGradient>
<linearGradient
:id="'textGradient-' + config.code"
:x1="0"
:y1="['to top right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
:x2="['to right', 'to bottom right', 'to top right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
:y2="['to bottom', 'to bottom right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
>
<stop
offset="0%"
:stop-color="config.customize.textColorType === 'pure' ? config.customize.textColor : config.customize.textGradientColor0"
/>
<stop
offset="100%"
:stop-color="config.customize.textColorType === 'pure' ? config.customize.textColor : config.customize.textGradientColor1"
/>
</linearGradient>
</defs>
<rect
v-if="config.customize.backgroundColorType !== 'transparent'"
width="100%"
height="100%"
:fill="`url(#backgroundGradient-${config.code})`"
/>
<text
:x="10"
:y="config.customize.fontSize"
:style="{ fontSize: config.customize.fontSize + 'px', fontWeight: config.customize.fontWeight }"
:fill="`url(#textGradient-${config.code})`"
>
<animate
v-if="isAnimate"
:attributeName="attributeName[config.customize.direction]"
:from="from[config.customize.direction]"
:to="to[config.customize.direction]"
:dur="config.customize.dur + 's'"
repeatCount="indefinite"
/>
{{ config.customize.title }}
</text>
</svg>
<div class="icon">
<icon-svg
v-if="config.customize.icon.name && config.customize.icon.position === 'right'"
:name="config.customize.icon.name"
:style="{ color: config.customize.icon.color, width: config.customize.fontSize + 'px',height: config.customize.fontSize + 'px' }"
/>
</div>
</div>
</div>
<div
v-show="config.customize.voiceBroadcast && showVoiceSwitch"
class="voice-switch"
:style="{fontSize:config.customize.fontSize + 'px',right:config.customize.fontSize + 5 + 'px',}"
@mouseenter.stop="mouseenter"
>
<i
:class="voiceSwitchValue ? 'el-icon-microphone' : 'el-icon-turn-off-microphone'"
@click="voiceSwitch"
/>
</div>
</div>
</template>
<script>
import Speech from 'speak-tts'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import commonMixins from 'data-room-ui/js/mixins/commonMixins'
import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
import cloneDeep from 'lodash/cloneDeep'
import IconSvg from 'data-room-ui/SvgIcon'
import { get } from 'sortablejs'
export default {
name: 'Marquee',
props: {
// 卡片的属性
config: {
type: Object,
default: () => ({})
}
},
components: {
IconSvg
},
data () {
return {
showVoiceSwitch: false,
visibilityState: false,
voiceSwitchValue: true,
customClass: {},
attributeName: {
right: 'x',
left: 'x',
top: 'y',
bottom: 'y'
},
// 动画开始
from: {
left: '-100%',
right: '100%',
top: '-100%',
bottom: '100%'
},
// 动画结束
to: {
left: '100%',
right: '-100%',
top: '100%',
bottom: '-100%'
},
isAnimate: true,
// 组件内部数据
innerData: null,
// 音频播放
audio: null,
// 音频地址
isPlayAudio: null,
// 语音播报
speech: null,
isInit: false,
firstSpeech: true,
numberBroadcasts: 0
}
},
computed: {
// speechText
speechText () {
return this.config.customize.title || ''
},
audioSrc: {
get () {
return this.config?.option?.data?.[this.config?.dataSource?.metricField] || ''
},
set (val) {
this.config.option.data[this.config.dataSource.metricField] = val
}
}
},
watch: {
speechText (val) {
if (!this.isPreview && this.config.customize.voiceBroadcast && !this.isInit && !this.firstSpeech) {
this.speechBroadcast(val)
} else {
if (this.speech) {
this.speech = null
}
}
},
deep: true,
audioSrc (val) {
if (this.config.customize.voiceBroadcast) {
if (this.audio) {
this.audio.src = val
this.audio.play()
}
} else {
if (this.aduio) {
this.aduio.pause()
this.aduio = null
}
}
}
},
mixins: [paramsMixins, commonMixins, linkageMixins],
mounted () {
this.chartInit()
EventBus.$on('stopMarquee', () => {
this.isAnimate = false
})
// 图片生成完成后,再开启动画
EventBus.$on('startMarquee', () => {
this.isAnimate = true
})
// 如果删除了组件
EventBus.$on('deleteComponent', (codes) => {
if (codes.includes(this.config.code)) {
if (this.audio) {
this.audio.pause()
this.audio = null
}
if (this.speech) {
this.speech = null
}
}
})
this.speech = null
this.isInit = true
// 如果是预览模式的话,则弹出对话框,当前大屏存在语音播报,是否开启语音播报
if (this.isPreview && this.config.customize.voiceBroadcast) {
this.$confirm('当前大屏存在语音播报,是否开启语音播报?若开启请点击确认或者回车', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'bs-el-message-box'
}).then(() => {
if (this.audioSrc) {
this.audio.play()
} else {
this.speech = null
this.speechBroadcast(this.config.customize.title)
this.isInit = false
}
}).catch(() => { })
}
document.addEventListener('visibilitychange', this.handleVisibilityChange)
},
beforeDestroy () {
EventBus.$off('stopMarquee')
EventBus.$off('startMarquee')
EventBus.$off('deleteComponent')
},
methods: {
dataFormatting (config, data) {
// 数据返回成功则赋值
if (data.success) {
data = data.data
// 获取到后端返回的数据,有则赋值
if (config.dataHandler) {
try {
// 此处函数处理data
eval(config.dataHandler)
} catch (e) {
console.info(e)
}
}
config.option.data = data
config.customize.title = config.option.data[config.dataSource.dimensionField] || config.customize.title
this.innerData = config
// 语音播报
} else {
// 数据返回失败则赋前端的模拟数据
config.option.data = []
}
// 清除上一个visibilitychange监听重新开始监听
if (this.voiceSwitchValue && !this.visibilityState && this.isInit) {
this.voiceBroadcast(config)
}
return config
},
// 语音播报
voiceBroadcast (config) {
const innerData = this.innerData || config
if (innerData) {
if (config.customize.voiceBroadcast) {
if (innerData?.dataSource?.businessKey && innerData?.option?.data[this.innerData.dataSource.metricField]) {
// 如果aduio存在先销毁这个实例或者替换它的URL
if (this.aduio) {
this.aduio.pause()
this.aduio = null
}
// 获取音频元素
this.audio = this.$refs[`audioPlayer${config.code}`]
this.audio.src = innerData.option.data[this.innerData.dataSource.metricField]
this.audio.play()
} else if (config.customize.title) {
// 页面初始化不执行
if (!this.isInit) {
this.speechBroadcast(config.customize.title)
}
}
} else {
if (this.aduio) {
this.aduio.pause()
this.aduio = null
}
}
}
},
// 语音播报
speechBroadcast (text) {
this.numberBroadcasts = 0
this.speech = new Speech()
this.speech.setLanguage('zh-CN')
this.speech.pitch = 1
this.speech.init()
if (this.speech.hasBrowserSupport()) {
if (this.numberBroadcasts < 1) {
this.speech.speak({ text: text })
this.numberBroadcasts += 1
}
} else {
this.$message({
message: '您的浏览器不支持语音播报',
type: 'warning'
})
}
},
changeStyle (config) {
config = { ...this.config, ...config }
if (config.customize.voiceBroadcast && this.isInit && !config?.option?.data?.[this.config?.dataSource?.metricField]) {
this.isInit = false
this.speechBroadcast(config.customize.title)
this.$nextTick(() => {
this.firstSpeech = false
})
}
// 样式改变时更新主题配置
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
this.changeChartConfig(config)
if (config.code === this.activeCode) {
this.changeActiveItemConfig(config)
}
},
// 监听页面是否可见
handleVisibilityChange () {
if (document.visibilityState === 'hidden') {
this.visibilityState = true
if (this.audio) {
this.audio.pause()
}
if (this.speech) {
this.speech = null
}
} else {
this.visibilityState = false
if (this.audio) {
this.audio.play()
}
if (this.speech) {
this.speech.resume()
}
}
},
voiceSwitch () {
this.voiceSwitchValue = !this.voiceSwitchValue
if (this.voiceSwitchValue) {
if (this.audio) {
try {
this.audio.play()
} catch (e) {
console.info(e)
}
}
if (this.speech) {
this.speech.resume()
}
} else {
if (this.audio) {
try {
this.audio.pause()
} catch (e) {
console.info(e)
}
}
if (this.speech) {
this.speech.pause()
}
}
},
mouseenter () {
this.showVoiceSwitch = true
},
mouseleave () {
this.showVoiceSwitch = false
}
}
}
</script>
<style lang="scss" scoped>
.marquee-box {
width: 100%;
height: 100%;
user-select: none;
white-space: nowrap;
overflow: hidden;
position: relative;
.scroll-area {
width: 100%;
height: 100%;
.marquee-container {
width: 100%;
height: 100%;
display: flex;
.svg-container {
width: 100%;
height: 100%;
}
}
}
.icon {
position: relative;
top: 0;
// 清除浮动
}
}
.voice-switch{
position: absolute;
cursor: pointer;
bottom: 5px;
color: #fff;
}
</style>

View File

@@ -0,0 +1,479 @@
<!--
* @description: 标题属性设置面板
* @Date: 2022-08-17 16:53:28
* @Author: shiyi
-->
<template>
<div>
<el-form
ref="form"
label-width="100px"
label-position="left"
:model="config"
:rules="rules"
class="bs-el-form"
>
<SettingTitle>标题</SettingTitle>
<div class="bs-setting-wrap">
<el-form-item
label="标题"
label-width="100px"
prop="title"
>
<el-input
v-model="config.customize.title"
placeholder="请输入标题"
clearable
/>
</el-form-item>
</div>
<SettingTitle>位置</SettingTitle>
<div class="lc-field-body">
<PosWhSetting :config="config" />
</div>
<SettingTitle v-if="config.border">边框</SettingTitle>
<div class="lc-field-body">
<BorderSetting
v-if="config.border"
label-width="100px"
:config="config.border"
:bigTitle='config.title'
/>
</div>
<SettingTitle>旋转</SettingTitle>
<div class="lc-field-body">
<RotateSetting
:config="config"
/>
</div>
<SettingTitle>基础</SettingTitle>
<div class="lc-field-body">
<el-form-item
label="字体大小"
label-width="100px"
>
<el-input
v-model="config.customize.fontSize"
placeholder="请输入标题字体大小"
clearable
>
<template slot="append">
px
</template>
</el-input>
</el-form-item>
<el-form-item
label="字体权重"
label-width="100px"
>
<el-input-number
v-model="config.customize.fontWeight"
class="bs-el-input-number"
placeholder="请输入标题字体权重"
:min="0"
:step="100"
/>
</el-form-item>
<!-- 走马灯走向 -->
<el-form-item
label="滚动方向"
label-width="100px"
>
<el-select
v-model="config.customize.direction"
class="bs-el-select"
popper-class="bs-el-select"
filterable
clearable
>
<el-option
v-for="direction in directionList"
:key="direction.value"
:label="direction.label"
:value="direction.value"
/>
</el-select>
</el-form-item>
<!-- 滚动速度 -->
<el-form-item
label="滚动间隔"
label-width="100px"
>
<el-input-number
v-model="config.customize.dur"
class="bs-el-input-number"
placeholder="请输入滚动间隔"
:min="1"
:step="1"
>
<template slot="append">
s
</template>
</el-input-number>
</el-form-item>
<!-- 图标选择 -->
<el-form-item
label="图标选择"
label-width="100px"
>
<!-- <IconPicker v-model="config.customize.icon.name" /> -->
<el-select
v-model="config.customize.icon.name"
class="bs-el-select"
popper-class="bs-el-select"
filterable
clearable
>
<el-option
v-for="(icbroadcaston,index) in broadcastList"
:key="index"
:label="icbroadcaston.label"
:value="icbroadcaston.value"
>
<icon-svg :name="icbroadcaston.value" />
<span style="float: right; color: #8492a6; font-size: 13px">{{ icbroadcaston.label }}</span>
</el-option>
</el-select>
</el-form-item>
<!-- 图标位置 -->
<el-form-item
v-if="config.customize.icon.name"
label="图标位置"
label-width="100px"
>
<el-select
v-model="config.customize.icon.position"
class="bs-el-select"
popper-class="bs-el-select"
filterable
clearable
>
<el-option
v-for="iconPosition in iconPositionOptions"
:key="iconPosition.value"
:label="iconPosition.label"
:value="iconPosition.value"
/>
</el-select>
</el-form-item>
<!-- 图标颜色 -->
<el-form-item
v-if="config.customize.icon.name"
label="图标颜色"
label-width="100px"
>
<ColorPicker
v-model="config.customize.icon.color"
placeholder="请选择图标颜色"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item label="文字颜色类型">
<el-select
v-model="config.customize.textColorType"
popper-class="bs-el-select"
class="bs-el-select"
filterable
clearable
>
<el-option
label="纯色"
value="pure"
/>
<el-option
label="渐变色"
value="gradient"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="config.customize.textColorType === 'pure'"
label="文字颜色"
>
<ColorPicker
v-model="config.customize.textColor"
placeholder="请选择文字颜色"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item
v-if="config.customize.textColorType === 'gradient'"
label="文字渐变色方向"
>
<el-select
v-model="config.customize.textGradientDirection"
popper-class="bs-el-select"
class="bs-el-select"
filterable
clearable
>
<el-option
v-for="direction in gradientDirection"
:key="direction.value"
:label="direction.label"
:value="direction.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="config.customize.textColorType === 'gradient'"
label="文字渐变开始色"
>
<ColorPicker
v-model="config.customize.textGradientColor0"
placeholder="请选择渐变色开始色值"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item
v-if="config.customize.textColorType === 'gradient'"
label="文字渐变结束色"
>
<ColorPicker
v-model="config.customize.textGradientColor1"
placeholder="请选择渐变色结束色值"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item label="背景色类型">
<el-select
v-model="config.customize.backgroundColorType"
popper-class="bs-el-select"
class="bs-el-select"
filterable
clearable
>
<el-option
label="透明"
value="transparent"
/>
<el-option
label="纯色"
value="pure"
/>
<el-option
label="渐变色"
value="gradient"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="config.customize.backgroundColorType === 'pure'"
label="背景色"
>
<ColorPicker
v-model="config.customize.backgroundColor"
placeholder="请选择背景色"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item
v-if="config.customize.backgroundColorType === 'gradient'"
label="背景渐变色方向"
>
<el-select
v-model="config.customize.bgGradientDirection"
popper-class="bs-el-select"
class="bs-el-select"
filterable
clearable
>
<el-option
v-for="direction in gradientDirection"
:key="direction.value"
:label="direction.label"
:value="direction.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="config.customize.backgroundColorType === 'gradient'"
label="背景渐变开始色"
>
<ColorPicker
v-model="config.customize.bgGradientColor0"
placeholder="请选择渐变色开始色值"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
<el-form-item
v-if="config.customize.backgroundColorType === 'gradient'"
label="背景渐变结束色"
>
<ColorPicker
v-model="config.customize.bgGradientColor1"
placeholder="请选择渐变色结束色值"
:predefine-colors="predefineThemeColors"
/>
</el-form-item>
</div>
<SettingTitle>其他</SettingTitle>
<div class="lc-field-body">
<!-- 是否开启语音播报 -->
<el-form-item
label="语音播报"
label-width="100px"
>
<el-switch
v-model="config.customize.voiceBroadcast"
:active-value="true"
:inactive-value="false"
class="bs-el-switch"
/>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import SettingTitle from 'data-room-ui/SettingTitle/index.vue'
import ColorPicker from 'data-room-ui/ColorPicker/index.vue'
import BorderSetting from 'data-room-ui/BigScreenDesign/RightSetting/BorderSetting.vue'
import PosWhSetting from 'data-room-ui/BigScreenDesign/RightSetting/PosWhSetting.vue'
import RotateSetting from 'data-room-ui/BigScreenDesign/RightSetting/RotateSetting.vue'
import IconSvg from 'data-room-ui/SvgIcon'
import {predefineColors} from "data-room-ui/js/utils/colorList";
export default {
name: 'MarqueeSetting',
components: {
PosWhSetting,
ColorPicker,
SettingTitle,
RotateSetting,
IconSvg,
BorderSetting
},
data () {
return {
predefineThemeColors: predefineColors,
directionList: [
{
value: 'right',
label: '从右到左'
},
{
value: 'left',
label: '从左到右'
},
{
value: 'top',
label: '从上到下'
},
{
value: 'bottom',
label: '从下到上'
}
],
gradientDirection: [
{
label: '从左到右',
value: 'to right'
},
{
label: '从上到下',
value: 'to bottom'
},
{
label: '从左上到右下',
value: 'to bottom right'
},
{
label: '从左下到右上',
value: 'to top right'
}
],
iconPositionOptions: [
{
label: '左侧',
value: 'left'
},
{
label: '右侧',
value: 'right'
}
],
broadcastList: [
{
label: '广播1',
value: 'broadcast-1'
},
{
label: '广播2',
value: 'broadcast-2'
},
{
label: '广播3',
value: 'broadcast-3'
},
{
label: '广播4',
value: 'broadcast-4'
},
{
label: '广播5',
value: 'broadcast-5'
},
{
label: '广播6',
value: 'broadcast-6'
},
{
label: '广播7',
value: 'broadcast-7'
},
{
label: '广播8',
value: 'broadcast-8'
},
{
label: '广播9',
value: 'broadcast-9'
},
{
label: '广播10',
value: 'broadcast-10'
},
{
label: '广播11',
value: 'broadcast-11'
}
],
rules: {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
]
}
}
},
computed: {
config: {
get () {
return this.$store.state.bigScreen.activeItemConfig
},
set (val) {
this.$store.state.bigScreen.activeItemConfig = val
}
}
},
watch: {
},
mounted () { },
methods: {
changeStyle () { }
}
}
</script>
<style lang="scss" scoped>
@import '../../assets/style/settingWrap.scss';
@import '../../assets/style/bsTheme.scss';
.bs-setting-wrap {
padding: 12px 16px;
}
.lc-field-body {
padding: 12px 16px;
}
</style>

View File

@@ -0,0 +1,89 @@
/*
* @Descripttion:
* @Author: liu.shiyi
* @Date: 2022-10-13 11:18:03
* @LastEditTime: 2022-10-13 13:55:11
*/
import { commonConfig, displayOption } from 'data-room-ui/js/config'
export const settingConfig = {
theme: 'dark',
text: '跑马灯占位符', // text内容
// 设置面板属性的显隐
displayOption: {
...displayOption,
metricField: {
// 指标
label: '音频链接',
enable: true,
multiple: false // 是否多选
},
dimensionField: {
// 维度
label: '跑马灯内容', // 维度/查询字段
enable: true,
multiple: false // 是否多选
}
}
}
const dataHandler = 'data = data[0]'
const customConfig = {
type: 'marquee',
root: {
version: '2023071001',
// 绕x轴旋转角度
rotateX: 0,
// 绕y轴旋转角度
rotateY: 0,
// 绕z轴旋转角度
rotateZ: 0,
// 透视距离
perspective: 0,
skewX: 0,
skewY: 0
},
customize: {
title: '花有重开日,人无再少年',
fontSize: 14,
fontWeight: 700,
icon: {
name: '',
position: 'left',
color: '#fff'
},
// 文字颜色类型: 纯色、渐变
textColorType: 'pure',
// 文字颜色
textColor: '#fff',
// 文字渐变开始颜色
textGradientColor0: '#fff',
// 文字渐变结束颜色
textGradientColor1: '#fff',
// 文字渐变方向
textGradientDirection: 'to right',
// 滚动方向
direction: 'right',
// 滚动间隔
dur: '8',
// 背景色类型:纯色、渐变、透明
backgroundColorType: 'transparent',
// 背景色
backgroundColor: '#fff',
// 背景渐变色开始颜色
bgGradientColor0: '#fff',
// 背景渐变色结束颜色
bgGradientColor1: '#fff',
// 背景色渐变方向
bgGradientDirection: 'to right',
// 语音播报
voiceBroadcast: false
}
}
// 配置处理脚本
export const dataConfig = {
...commonConfig(customConfig),
dataHandler
}