初始化

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,60 @@
import Vue from 'vue'
// 大屏详情
export function getScreenInfo (code) {
return Vue.prototype.$dataRoomAxios.get(`/bigScreen/design/info/code/${code}`)
}
// 保存更新大屏
export function saveScreen(data) {
data.chartList.forEach((item) => {
if (item.type == 'customComponent') {
const a = JSON.parse(item.option)
if (a.data) {
a.data = []
}
item.option=JSON.stringify(a)
item.setting=item.setting.map((x) => {
const {field,value,...obj}=x
return {field,value}
})
}
})
return Vue.prototype.$dataRoomAxios.post('/bigScreen/design/update', data)
}
// 根据数据集获取数据集详情
export function getDataSetDetails (id) {
return Vue.prototype.$dataRoomAxios.get('/dataset/datasetInfo/' + id)
}
// 根据数据集id获取数据
export function getDataByDataSetId (dataSetId) {
return Vue.prototype.$dataRoomAxios.post('/dataset/execute', {
dataSetId,
params: []
})
}
// 得到图表详情
export function getChatInfo (params) {
return Vue.prototype.$dataRoomAxios.post('/bigScreen/chart/data/list', params)
}
// 得到图表的更新数据
export function getUpdateChartInfo (params) {
return Vue.prototype.$dataRoomAxios.post('/bigScreen/chart/data/chart', params)
}
// 业务组件列表
export function getBizComponentPage (params) {
return Vue.prototype.$dataRoomAxios.get('/bigScreen/bizComponent/page', params)
}
// 根据code获得业务组件的信息
export function getBizComponentInfo (code) {
return Vue.prototype.$dataRoomAxios.get(`/bigScreen/bizComponent/info/${code}`)
}
// 更新业务组件
export function updateBizComponent (params) {
return Vue.prototype.$dataRoomAxios.post('/bigScreen/bizComponent/update', params)
}

View File

@@ -0,0 +1,68 @@
/*
* @description: 基础的bigScreen展示组件
* @Date: 2023-03-13 10:04:59
* @Author: xing.heng
* @LastEditors: wujian
* @LastEditTime: 2023-06-01 15:55:48
*/
// import _ from 'lodash'
import cloneDeep from "lodash/cloneDeep";
import getComponentConfig from "data-room-ui/js/utils/getComponentConfig";
// 批量引入配置文件
import { setModules, dataModules } from "data-room-ui/js/utils/configImport";
const typeList = [
"texts",
"numbers",
"linkChart",
"horizontalLine", // 横线
"verticalLine", // 竖线
"picture",
"timeCountDown",
"currentTime",
"customHtml",
"iframeChart",
"digitalFlop",
"tables",
"screenScrollRanking",
"screenScrollBoard",
"video",
"input",
"button",
"marquee",
"chartTab",
"themeSwitcher",
"themeSelect",
"select",
"timePicker",
"dateTimePicker",
"indicatorCard",
"indicatorCard2",
"indexCard",
"indexCard2",
];
let basicConfigList = [];
basicConfigList = typeList.map((type) => {
console.log('getComponentConfig(type): ', getComponentConfig(type));
return getComponentConfig(type);
});
basicConfigList = basicConfigList.map((item) => {
return basicComponentsConfig(item);
});
// 生成基本配置
export function basicComponentsConfig(item) {
return {
...item,
border: {
type: "",
titleHeight: 60,
fontSize: 30,
isTitle: true,
padding: [0, 0, 0, 0],
},
option: cloneDeep(setModules[item.type]),
...cloneDeep(dataModules[item.type]),
};
}
export default basicConfigList;

View File

@@ -0,0 +1,40 @@
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import getComponentConfig from 'data-room-ui/js/utils/getBorderComponentsConfig'
// 批量引入配置文件
import { setModules, dataModules } from 'data-room-ui/js/utils/configImport'
const typeLIst = [
'border1',
'border2',
'border3',
'border4',
'border5',
'border6',
'border7',
'border8',
'border9',
'border10',
'border11',
'border12',
'border13',
'border14',
'border15'
]
let basicConfigList = []
basicConfigList = typeLIst.map((type) => {
// 装饰组件的className保持一致
return getComponentConfig(type, 'ScreenBorder')
})
basicConfigList = basicConfigList.map((item) => {
return basicComponentsConfig(item)
})
// 生成基本配置
export function basicComponentsConfig (item) {
// let type = `${upperFirst(item.type)}`
return {
...item,
option: cloneDeep(setModules[item.type]),
...cloneDeep(dataModules[item.type])
}
}
export default basicConfigList

View File

@@ -0,0 +1,94 @@
/*
* @description: 大屏组件通用属性
* @Date: 2023-03-13 10:04:59
* @Author: xing.heng
* @LastEditors: wujian
* @LastEditTime: 2023-06-01 10:23:13
*/
import getComponentConfig from 'data-room-ui/js/utils/getComponentConfig'
import linkageConfig from 'data-room-ui/js/config/linkageConfig'
// 关于设置组件在右侧面板可以展示哪些属性配置
export const displayOption = {
serverPagination: {
// 服务端分页
enable: false
},
pageSize: {
// 分页长度
enable: false
},
metricField: {
// 指标
label: '指标',
enable: true,
multiple: true // 是否多选
},
dimensionField: {
// 维度
label: '维度', // 维度/查询字段
enable: true,
multiple: true // 是否多选
},
dimensionList: {
// 维度(只有多折线图会存在两个维度)
label: '维度', // 维度/查询字段
enable: false,
multiple: true // 是否多选
},
seriesField: {
// 数据细分
enable: false,
required: true // 必填
},
dataAllocation: {
// 是否存在数据配置
enable: true
},
params: {
// 参数配置
enable: true
},
dataSourceType: {
// 数据源(数据集或者其他方式:静态数据)
enable: true
}
}
export default function (customConfig) {
return {
...getComponentConfig(customConfig.type),
z: 0, // z轴图层支持
locked: false, // 是否锁定组件
group: '', // 组合组件, 相同group的组件会被组合在一起
code: null,
showTitle: true,
...customConfig.root,
dataSource: {
className:
'com.gccloud.dataroom.core.module.chart.components.datasource.DataSetDataSource',
dataSourceKey: '', // 数据源,选择不同数据库
source: 'dataset',
businessKey: '', // 数据集标识
dimensionField: '', // 维度
metricField: '', // 指标
seriesField: '', // 分类字段
dimensionFieldList: [], // 唯独列表
metricFieldList: [], // 指标列表
seriesFieldList: [], // 分类列表
serverPagination: false, // 服务端分页
pageSize: 10,
params: {},
dataSetType: '1', // 数据集类型,
formCode: '',
...customConfig.dataSource // 非通用数据配置
},
customize: {
...customConfig.customize
}, // 自定义设置
...linkageConfig, // 数据联动配置
filterList: [],
dataFlag: false // 判断数据为模拟数据还是真实数据
}
}

View File

@@ -0,0 +1,30 @@
// import _ from 'lodash'
import cloneDeep from "lodash/cloneDeep";
import getComponentConfig from "data-room-ui/js/utils/getConfigurationComponentsConfig";
// 批量引入配置文件
import { setModules, dataModules } from "data-room-ui/js/utils/configImport";
const typeLIst = [
"horizontalLine2",
"verticalLine2",
"warning",
];
let basicConfigList = [];
basicConfigList = typeLIst.map((type) => {
return getComponentConfig(type, "ScreenConfiguration"); // 组态组件的className保持一致
});
basicConfigList = basicConfigList.map((item) => {
return basicComponentsConfig(item);
});
// 生成基本配置
export function basicComponentsConfig(item) {
// let type = `lcdp${upperFirst(item.type)}`
return {
...item,
option: cloneDeep(setModules[item.type]),
...cloneDeep(dataModules[item.type]),
};
}
export default basicConfigList;

View File

@@ -0,0 +1,38 @@
// import _ from 'lodash'
import cloneDeep from "lodash/cloneDeep";
import getComponentConfig from "data-room-ui/js/utils/getDecorationComponentsConfig";
// 批量引入配置文件
import { setModules, dataModules } from "data-room-ui/js/utils/configImport";
const typeLIst = [
"decoration1",
"decoration3",
"decoration2",
"decoration2Reverse",
"decoration4",
"decoration4Reverse",
"decoration5",
"decoration6",
"decoration8",
"decoration8Reverse",
"decoration9",
"decoration10",
"decoration11",
];
let basicConfigList = [];
basicConfigList = typeLIst.map((type) => {
// 装饰组件的className保持一致
return getComponentConfig(type, "ScreenDecoration");
});
basicConfigList = basicConfigList.map((item) => {
return basicComponentsConfig(item);
});
// 生成基本配置
export function basicComponentsConfig(item) {
// let type = `lcdp${upperFirst(item.type)}`
return {
...item,
option: cloneDeep(setModules[item.type]),
...cloneDeep(dataModules[item.type]),
};
}
export default basicConfigList;

View File

@@ -0,0 +1,13 @@
/*
* @description: 抛出组件的配置
* @Date: 2023-03-13 10:04:59
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-03-13 11:26:13
*/
import commonConfig, { displayOption } from './commonConfig'
export {
commonConfig, displayOption // commonConfig个函数传入参数type动态生成配置,displayOption是决定组件具有哪些属性配置
}

View File

@@ -0,0 +1,24 @@
/*
* @description: 通用的联动参数
* @Date: 2023-01-10 09:58:10
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-04-18 17:19:29
*/
export default {
inParams: [
// {
// // 组件用于入参的参数列表
// name: '', // 参数名
// code: '' // 参数值
// }
],
// 查询表单联动
linkage: {
action: {
type: 'js',
script: '' // 联动执行的逻辑
},
components: []
}
}

View File

@@ -0,0 +1,3 @@
import Icon from 'packages/Svgs/export'
const svgList = Icon.getSvgList()
export default svgList

View File

@@ -0,0 +1,130 @@
/**
* 聚合函数
* @type {string[]}
*/
export const aggregateList = ['COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'COUNT_DISTINCT']
/**
* 过滤条件
* @type {string[]}
*/
export const operatorList = [
{
label: '等于',
value: '='
},
{
label: '不等于',
value: '!='
},
{
label: '大于',
value: '>'
},
{
label: '小于',
value: '<'
},
{
label: '大于等于',
value: '>='
},
{
label: '小于等于',
value: '<='
},
{
label: '包含',
value: 'IN'
},
{
label: '不包含',
value: 'NOT IN'
},
{
label: '相似',
value: 'LIKE'
},
{
label: '为空',
value: 'IS NULL'
},
{
label: '不为空',
value: 'IS NOT NULL'
}
]
/**
* 分页条数
* @type {number[]}
*/
export const rowLimits = [10, 50, 100, 250, 500, 1000, 5000, 10000]
/**
* 最近类型的时间范围
* @type {[{label: string, value: string}]}
*/
export const lastTimeRangeType = [
{
label: '最近一天',
value: 'lastDay'
},
{
label: '最近一周',
value: 'lastWeek'
},
{
label: '最近一月',
value: 'lastMonth'
},
{
label: '最近一季度',
value: 'lastQuarter'
},
{
label: '最近一年',
value: 'lastYear'
}
]
/**
* 周期时间范围
* @type {[{label: string, value: string}]}
*/
export const previousTimeRangeType = [
{
label: '上一周',
value: 'previousWeek'
},
{
label: '上一月',
value: 'previousMonth'
},
{
label: '上一年',
value: 'previousYear'
}
]
/**
* 时间粒度
* @type {[{label: string, value: string}]}
*/
export const timeGrain = [
{
label: '原始值',
value: 'original'
},
{
label: '秒',
value: 'second'
},
{
label: '分钟',
value: 'minute'
},
{
label: '小时',
value: 'hour'
}, { label: '天', value: 'day' }, { label: '周', value: 'week' }, { label: '月', value: 'month' }, { label: '季度', value: 'quarter' }, { label: '年', value: 'year' }
]

View File

@@ -0,0 +1,250 @@
import { mapMutations, mapState } from 'vuex'
import cloneDeep from 'lodash/cloneDeep'
import { toJpeg, toPng } from 'html-to-image'
import isEmpty from 'lodash/isEmpty'
import { randomString } from 'data-room-ui/js/utils'
import Contextmenu from 'vue-contextmenujs'
import Vue from 'vue'
Vue.use(Contextmenu)
export default {
computed: {
...mapState({
activeCode: state => state.bigScreen.activeCode,
activeCodes: state => state.bigScreen.activeCodes,
hoverCode: state => state.bigScreen.hoverCode,
activeItemConfig: state => state.bigScreen.activeItemConfig,
chartList: state => state.bigScreen.pageInfo.chartList,
presetLine: state => state.bigScreen.presetLine
})
},
data () {
return {}
},
mounted () {
},
methods: {
...mapMutations('bigScreen', ['changeHoverCode', 'changeActiveCode', 'changeChartConfig', 'addItem', 'delItem', 'resetPresetLine', 'changeLayout', 'changeZIndex', 'changeLocked', 'saveTimeLine', 'copyCharts', 'pasteCharts', 'clearActiveCodes']), // 改变hover的组件
changeHover (code) {
this.changeHoverCode(code)
}, // 改变激活的组件
changeActive (code) {
this.changeActiveCode(code)
}, // 打开右侧面板
openRightPanel (config) {
this.changeActiveCode(config.code)
this.$emit('openRightPanel', config)
}, // 查看数据
dataView (config) {
this.changeActiveCode(config.code)
this.$emit('openDataViewDialog', config)
}, // 复制组件
copyItem (config) {
const newConfig = cloneDeep(config)
newConfig.code = randomString(8)
newConfig.title = newConfig.title + '_副本'
// 区分是从左侧添加还是复制的组件
newConfig.isCopy = true
newConfig.x = config.x + 20
newConfig.y = config.y + 20
if (config.group) {
newConfig.group = 'copy_' + config.group
}
this.addItem(newConfig)
}, // 删除单个组件
deleteItem (config) {
this.$confirm('确定删除该组件吗?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', customClass: 'bs-el-message-box'
}).then(() => {
this.delItem(config.code)
})
}, // 批量删除组合元素
deleteGroupItem (config) {
this.$confirm('确定批量删除选中的组件吗?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', customClass: 'bs-el-message-box'
}).then(() => {
// 找到和本组件group相同的组件 删除
const codes = this.chartList.filter(_chart => _chart.group === config.group && config.group).map(_chart => _chart.code)
if (!isEmpty(codes)) {
this.delItem(codes)
} else {
this.delItem(config.code)
}
})
}, // 获取组件的坐标字符串,取整 100 100
getPoint ({ x, y }) {
return `(${Math.round(x)}, ${Math.round(y)})`
}, // 组合/取消组合图表
groupChart (chart) {
if (!chart.group || chart.group === 'tempGroup') {
// 添加组合
// eslint-disable-next-line no-unused-expressions
this.activeCodes?.forEach(code => {
const config = this.chartList.find(item => item.code === code)
this.changeChartConfig({
...config, group: `group_${chart.code}`
})
})
this.saveTimeLine('组合图表')
} else {
// 取消组合
this.clearActiveCodes()
// 找到和本组件group相同的组件 取消group
this.chartList.forEach(_chart => {
if (_chart.group === chart.group) {
this.changeChartConfig({
..._chart, group: ''
})
}
})
this.saveTimeLine('取消组合图表')
}
}, // 生成图片
generateImage (chart) {
let componentDom = document.querySelector(`#${chart.code} .render-item-wrap`)
if (this.isPreview) {
componentDom = document.querySelector(`#${chart.code}`)
}
toPng(componentDom)
.then((dataUrl) => {
const link = document.createElement('a')
link.download = `${chart.title}.png`
link.href = dataUrl
link.click()
link.addEventListener('click', () => {
link.remove()
})
}).catch((error) => {
if (error.type === 'error') {
// 判断的error.currentTarget是img标签如果是的就弹出消息说是图片跨域
if (error.currentTarget.tagName.toLowerCase() === 'img') {
// 确认框
this.$confirm('图片资源跨域导致使用toDataURL API生成图片失败请将图片上传到资源库然后在组件中使用资源库中的图片资源确保没有跨域问题。', '提示', {
confirmButtonText: '确定',
showCancelButton: false,
type: 'warning',
customClass: 'bs-el-message-box'
}).then(() => { }).catch(() => { })
}
} else {
this.$message.warning('出现未知错误,请重试')
}
})
}, // 右键菜单
onContextmenu (event, chart) {
const isHidden = !chart?.option?.displayOption?.dataAllocation?.enable
event.preventDefault()
if (this.isPreview) {
this.$contextmenu({
items: [{
label: '查看数据',
icon: 'el-icon-view',
hidden: isHidden,
onClick: () => {
this.dataView(chart)
}
},
{
label: '生成图片',
icon: 'el-icon-download',
hidden: isHidden,
onClick: () => {
this.generateImage(chart)
}
}],
event, // 鼠标事件信息
customClass: 'bs-context-menu-class', // 自定义菜单 class
zIndex: 999, // 菜单样式 z-index
minWidth: 150 // 主菜单最小宽度
})
} else {
this.$contextmenu({
items: [{
label: '配置',
icon: 'el-icon-setting',
onClick: () => {
this.openRightPanel(chart)
}
}, {
label: '删除',
icon: 'el-icon-delete',
onClick: () => {
this.deleteItem(chart)
}
}, {
label: '批量删除',
icon: 'el-icon-delete',
onClick: () => {
this.deleteGroupItem(chart)
}
}, {
label: '复制',
icon: 'el-icon-copy-document',
onClick: () => {
this.copyItem(chart)
}
}, {
label: '组合复制',
icon: 'el-icon-copy-document',
onClick: () => {
this.copyCharts()
this.pasteCharts()
}
}, {
label: '置于顶层',
icon: 'el-icon-arrow-up',
onClick: () => {
let chartList = cloneDeep(this.chartList)
// 将当前图表置底
chartList = chartList.filter(item => item.code !== chart.code)
chartList.unshift(chart)
this.changeLayout(chartList)
this.changeZIndex(chartList)
}
}, {
label: '置于底层',
icon: 'el-icon-arrow-down',
onClick: () => {
let chartList = cloneDeep(this.chartList)
// 将当前图表置顶
chartList = chartList.filter(item => item.code !== chart.code)
chartList.push(chart)
this.changeLayout(chartList)
this.changeZIndex(chartList)
}
}, {
label: chart.locked ? '解锁' : '锁定',
icon: chart.locked ? 'el-icon-unlock' : 'el-icon-lock',
onClick: () => {
this.changeLocked(chart)
}
}, {
label: (chart.group && chart.group !== 'tempGroup') ? '取消组合' : '组合',
icon: (chart.group && chart.group !== 'tempGroup') ? 'iconfont-bigscreen icon-quxiaoguanlian' : 'iconfont-bigscreen icon-zuhe',
onClick: () => {
this.groupChart(chart)
}
}, {
label: '查看数据',
icon: 'el-icon-view',
hidden: isHidden,
onClick: () => {
this.dataView(chart)
}
}, {
label: '生成图片',
icon: 'el-icon-download',
onClick: () => {
this.generateImage(chart)
}
}],
event, // 鼠标事件信息
customClass: 'bs-context-menu-class', // 自定义菜单 class
zIndex: 999, // 菜单样式 z-index
minWidth: 150 // 主菜单最小宽度
})
}
return false
}
}
}

View File

@@ -0,0 +1,53 @@
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import ColorSelect from 'data-room-ui/ColorMultipleSelect/index.vue'
const chartSettingMixins = {
components: {
ColorSelect
},
data () {
return {
customRules: {},
colorScheme: [],
colors: []
}
},
computed: {
},
watch: {
},
mounted () {
this.initColor()
},
methods: {
initColor () {
const colorSetting = this.config?.setting?.find(item => item.type === 'colorSelect')
if (colorSetting && colorSetting.value && colorSetting.value.length) {
this.colorScheme = cloneDeep(colorSetting.value)
this.colors = cloneDeep(colorSetting.value)
}
},
// 清空颜色
delColor () {
this.colors = []
this.config.setting.forEach((set) => {
if (set && set.type === 'colorSelect' && set.value && set.value.length) {
set.value = []
}
})
},
addColor () {
this.colors.push('')
},
updateColorScheme (colors) {
this.colors = [...colors]
this.config.setting.forEach((set) => {
if (set && set.type === 'colorSelect') {
set.value = [...colors]
}
})
}
}
}
export { chartSettingMixins }

View File

@@ -0,0 +1,440 @@
/*
* @description: bigScreen公共方法
* @Date: 2023-03-24 17:10:43
* @Author: xing.heng
*/
// import _ from 'lodash'
import { mapMutations, mapState } from 'vuex'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import { getChatInfo, getUpdateChartInfo } from '../api/bigScreenApi'
import axiosFormatting from '../../js/utils/httpParamsFormatting'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
import cloneDeep from 'lodash/cloneDeep'
import mqtt from 'mqtt'
import MqttClient from 'data-room-ui/js/utils/mqttClient'
export default {
data() {
return {
filterList: [],
treeParentId: 0,
dataLoading: false
}
},
watch: {
'config.expression': { // 表达式发生变化
handler(val) {
this.getDataByExpression(this.config)
}
},
// 标题发生变化时需要及时更新表达式中的数据集库的字段名
'config.title': {
handler(val, oldVal) {
this.updateDataset({ code: this.config.code, title: val, data: [], oldTitle: oldVal, isChangeTitle: true })
this.updateComputedDatas({ code: this.config.code, title: val, data: [], oldTitle: oldVal, isChangeTitle: true })
}
},
currentDataset: { // 关联的数据发生变化
handler(val, old) {
if (val && Object.keys(val).length && JSON.stringify(val) !== JSON.stringify(old)) {
this.getDataByExpression(this.config)
}
},
deep: true,
immediate: true
},
currentComputedDatas: { // 关联的数据发生变化
handler(val, old) {
if (val && Object.keys(val).length && JSON.stringify(val) !== JSON.stringify(old)) {
this.getDataByExpression(this.config)
}
},
deep: true,
immediate: true
}
},
computed: {
...mapState({
pageCode: state => state.bigScreen.pageInfo.code,
customTheme: state => state.bigScreen.pageInfo.pageConfig.customTheme,
activeCode: state => state.bigScreen.activeCode
// dataset: state => state.bigScreen.dataset
}),
// 所有组件的数据集合
dataset() {
return this.$store.state.bigScreen.dataset
},
// 所有组件的数据集合
computedDatas() {
return this.$store.state.bigScreen.computedDatas
},
// 跟当前组件计算表达式关联的组件的数据集合
currentDataset() {
const newDataset = {}
this.config.expressionCodes?.forEach(item => {
const code = item.split('_')[1]
for (const key in this.dataset) {
const objCode = key.split('_')[1]
if (objCode === code) {
newDataset[code] = this.dataset[key]
}
}
})
return newDataset
},
// 跟当前组件计算表达式关联的组件的数据集合
currentComputedDatas() {
const newDataset = {}
this.config.expressionCodes?.forEach(item => {
const code = item.split('_')[1]
for (const key in this.computedDatas) {
const objCode = key.split('_')[1]
if (objCode === code) {
newDataset[code] = this.computedDatas[key]
}
}
})
return newDataset
},
isPreview() {
return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
}
},
mounted() {
if (!['tables', 'flyMap', 'map'].includes(this.config.type)) {
this.chartInit()
}
this.watchCacheData()
},
methods: {
...mapMutations({
changeChartConfig: 'bigScreen/changeChartConfig',
changeActiveItemConfig: 'bigScreen/changeActiveItemConfig',
updateDataset: 'bigScreen/updateDataset',
updateComputedDatas: 'bigScreen/updateComputedDatas'
}),
/**
* 初始化组件
*/
chartInit() {
let config = this.config
console.log('this.config: ', this.config);
// key和code相等说明是一进来刷新调用list接口
if (this.isPreview) {
// 改变样式
config = this.changeStyle(config) ? this.changeStyle(config) : config
// 改变数据
config = this.changeDataByCode(config)
} else {
// 否则说明是更新这里的更新只指更新数据改变样式时是直接调取changeStyle方法因为更新数据会改变key,调用chart接口
// eslint-disable-next-line no-unused-vars
config = this.changeData(config)
}
},
/**
* 初始化组件时获取后端返回的数据, 返回数据和当前组件的配置_list
* @param settingConfig 设置时的配置。不传则为当前组件的配置
* @returns {Promise<unknown>}
*/
changeDataByCode(config) {
let currentPage = 1
let size = 10
if (config?.option?.pagination) {
currentPage = config.option.pagination.currentPage
size = config.option.pagination.pageSize
}
return new Promise((resolve, reject) => {
config.loading = true
getChatInfo({
// innerChartCode: this.pageCode ? config.code : undefined,
chartCode: config.code,
current: currentPage,
pageCode: this.pageCode,
size: size,
type: config.type
}).then(async (res) => {
config.loading = false
let _res = cloneDeep(res)
//--------------------MQTT数据集执行前置条件------------------------------------------------------------
if (res.data && res.data.datasetType && res.data.datasetType === 'mqtt') {
res.executionByFrontend = true
}
//--------------------MQTT数据集执行前置条件------------------------------------------------------------
// 如果是http数据集的前端代理则需要调封装的axios请求
if (res.executionByFrontend) {
if (res.data.datasetType === 'mqtt') {
// 创建 MQTT 客户端实例
this.mqttClient = new MqttClient(res.data.url, {
clientId: "SW-VIEWS" + new Date().getTime(),
username: res.data.username,
password: res.data.password
});
// 连接到 MQTT broker
this.mqttClient.connect();
// 订阅指定主题
let produceTopic = res.data.topic;
this.mqttClient.subscribe(produceTopic, (topic, data) => {
// console.log(`收到主题 ${topic} 的消息`);
// if (topic === produceTopic) {
// // 处理收到的消息
// let JsonData = JSON.parse(data.toString());
// _res = this.httpDataFormatting(res, JsonData.data);
// config = this.dataFormatting(config, _res);
// // 每次数据更新后,重新渲染图表
// this.chart.changeData(config.option.data);
// }
if (topic === produceTopic) {
// 处理收到的消息
let JsonData = JSON.parse(data.toString());
if (res.data.responseScript) {
// eslint-disable-next-line no-new-func
const getResp = new Function('resp', res.data.responseScript)
let resData=getResp(JsonData)
let lastRes = res && Array.isArray(resData) ? resData : [{ ...resData }]
_res = this.httpDataFormatting(res, lastRes);
}else{
_res = this.httpDataFormatting(res, JsonData);
}
config = this.dataFormatting(config, _res);
// 每次数据更新后,重新渲染图表
// this.chart.changeData(config.option.data);
if (this.chart && this.chart.changeData) {
try {
this.chart.changeData(config.option.data);
} catch (error) {
console.log("Error changing data:", error);
}
}
}
});
}
if (res.data.datasetType === 'http') {
_res = await axiosFormatting(res.data)
_res = this.httpDataFormatting(res, _res)
}
if (res.data.datasetType === 'js') {
try {
const scriptAfterReplacement = res.data.script.replace(/\${(.*?)}/g, (match, p) => {
const value = this.config.dataSource?.params[p]
if (value === null || value === undefined || value === '') {
return "''"
} else if (!isNaN(value)) {
return value || p
} else {
return `'${value}' || '${p}'`
}
})
// eslint-disable-next-line no-new-func
const scriptMethod = new Function(scriptAfterReplacement)
_res.data = scriptMethod()
} catch (error) {
console.info('JS数据集脚本执行失败', error)
}
}
}
// 将后端返回的数据保存
if (_res.success) {
this.updateDataset({ code: config.code, title: config.title, data: _res?.data })
}
config = this.dataFormatting(config, _res)
this.changeChartConfig(config)
}).catch((err) => {
console.info(err)
}).finally(() => {
resolve(config)
})
})
},
/**
* @description: 更新chart
* @param {Object} config
* @param {Array} filterList
*/
changeData(config, filterList) {
const list = config?.paramsList?.map((item) => {
if (item.value === '${level}') {
return { ...item, value: config.customize.level }
} else if (item.value === '${name}') {
return { ...item, value: config.customize.scope }
} else {
return item
}
})
const params = {
chart: {
...config,
paramsList: list ? [...list] : [],
option: undefined
},
current: 1,
pageCode: this.pageCode,
type: config.type,
filterList: filterList || this.filterList
}
return new Promise((resolve, reject) => {
config.loading = true
getUpdateChartInfo(params).then(async (res) => {
config.loading = false
let _res = cloneDeep(res)
//--------------------MQTT数据集执行前置条件------------------------------------------------------------
if (res.data && res.data.datasetType && res.data.datasetType === 'mqtt') {
res.executionByFrontend = true
}
//--------------------MQTT数据集执行前置条件------------------------------------------------------------
// 如果是http数据集的前端代理则需要调封装的axios请求
if (res.executionByFrontend) {
if (res.data.datasetType === 'mqtt') {
// 创建 MQTT 客户端实例
this.mqttClient = new MqttClient(res.data.url, {
clientId: "SW-VIEWS" + new Date().getTime(),
username: res.data.username,
password: res.data.password
});
// 连接到 MQTT broker
this.mqttClient.connect();
// 订阅指定主题
let produceTopic = res.data.topic;
this.mqttClient.subscribe(produceTopic, (topic, data) => {
if (topic === produceTopic) {
// 处理收到的消息
let JsonData = JSON.parse(data.toString());
if (res.data.responseScript) {
// eslint-disable-next-line no-new-func
const getResp = new Function('resp', res.data.responseScript)
let resData=getResp(JsonData)
let lastRes = res && Array.isArray(resData) ? resData : [{ ...resData }]
_res = this.httpDataFormatting(res, lastRes);
}else{
_res = this.httpDataFormatting(res, JsonData);
}
config = this.dataFormatting(config, _res);
// 每次数据更新后,重新渲染图表
// this.chart.changeData(config.option.data);
if (this.chart && this.chart.changeData) {
try {
this.chart.changeData(config.option.data);
} catch (error) {
console.log("Error changing data:", error);
}
}
}
});
}
if (res.data.datasetType === 'http') {
_res = await axiosFormatting(res.data)
_res = this.httpDataFormatting(res, _res)
}
if (res.data.datasetType === 'js') {
try {
params.filterList.forEach(item => {
this.config.dataSource.params[item.column] = item.value
})
const scriptAfterReplacement = res.data.script.replace(/\${(.*?)}/g, (match, p) => {
const value = this.config.dataSource?.params[p]
if (value === null || value === undefined || value === '') {
return "''"
} else if (!isNaN(value)) {
return value || p
} else {
return `'${value}' || '${p}'`
}
})
// eslint-disable-next-line no-new-func
const scriptMethod = new Function(scriptAfterReplacement)
_res.data = scriptMethod()
} catch (error) {
console.info('JS数据集脚本执行失败', error)
}
}
}
// 将后端返回的数据保存
if (_res.success) {
this.updateDataset({ code: config.code, title: config.title, data: _res?.data })
}
config = this.dataFormatting(config, _res)
if (this.chart) {
// 单指标组件和多指标组件的changeData传参不同
if (['Liquid', 'Gauge', 'RingProgress', 'Progress'].includes(config.chartType)) {
this.chart.changeData(config.option.percent)
} else {
this.chart.changeData(config.option.data)
}
} if (this.config.type === 'candlestick' && this.charts) {
this.updateChartData(config, _res)
} else if (this.charts) {
// 地图组件的被联动更新
this.changeMapData(config.option.data)
}
}).catch(err => {
console.info(err)
}).finally(() => {
if (config) {
config.loading = false
}
resolve(config)
})
})
},
// 更新图表数据
updateChartData() {
},
// http前台代理需要对返回的数据进行重新组装
httpDataFormatting(chartRes, httpRes) {
let result = {}
result = {
columnData: chartRes.columnData,
data: httpRes,
success: chartRes.success
}
return result
},
dataFormatting(config, data) {
// 覆盖
},
newChart(option) {
// 覆盖
},
// 通过表达式计算获取组件的值
getDataByExpression(config) {
// 覆盖
},
changeStyle(config) {
config = { ...this.config, ...config }
// 样式改变时更新主题配置
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
this.changeChartConfig(config)
if (config.code === this.activeCode) {
this.changeActiveItemConfig(config)
}
},
// 缓存组件数据监听
watchCacheData() {
EventBus.$on('cacheDataInit', (data, dataSetId) => {
// 如果是缓存数据集
// 且当前组件的businessKey和缓存的dataSetId相等时更新组件
if (
this.config.dataSource.dataSetType === '2' &&
this.config.dataSource.businessKey === dataSetId
) {
const config = this.dataFormatting(this.config, data)
config.key = new Date().getTime()
this.changeChartConfig(config)
this.newChart(config.option)
}
})
}
},
beforeDestroy() {
this.mqttClient.disconnect();
},
}

View File

@@ -0,0 +1,160 @@
import { mapMutations, mapState } from 'vuex'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
import cloneDeep from 'lodash/cloneDeep'
const dataVMixins = {
props: {
// 卡片的属性
config: {
type: Object,
default: () => ({})
}
},
data () {
return {
updateKey: 0,
borderBgId: null
}
},
computed: {
...mapState({
customTheme: state => state.bigScreen.pageInfo.pageConfig.customTheme,
activeCode: state => state.bigScreen.activeCode
}),
code () {
return this.config.code
},
color () {
return this.config.customize.borderMainColor ||
this.config.customize.borderSecondaryColor
? [
this.config.customize.borderMainColor,
this.config.customize.borderSecondaryColor
]
: null
},
backgroundColor () {
return this.config.customize.backgroundColor
? this.config.customize.backgroundColor
: 'transparent'
},
colorType () {
return this.config.customize.colorType
},
Data () {
return JSON.parse(JSON.stringify(this.config))
}
},
watch: {
config: {
handler: function (val) {
if (val && val.customize && val.customize.colorType) {
this.changeBorderStyle(this.config)
if (val.customize.colorType === 'single') {
this.borderBgId = null
this.$nextTick(() => {
this.updateKey = new Date().getTime()
})
} else {
this.borderBgId = `borderBg${this.config.code}`
}
}
},
deep: true
},
Data: {
handler (newVal, oldVal) {
this.$nextTick(() => {
if ((newVal.w !== oldVal.w) || (newVal.h !== oldVal.h)) {
this.$nextTick(() => {
this.updateKey = new Date().getTime()
})
}
})
},
deep: true
}
},
mounted () {
this.changeBorderStyle(this.config)
},
methods: {
...mapMutations({
changeChartConfig: 'bigScreen/changeChartConfig',
changeActiveItemConfig: 'bigScreen/changeActiveItemConfig'
}),
changeStyle (config) {
config = { ...this.config, ...config }
// 样式改变时更新主题配置
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
this.changeChartConfig(config)
if (config.code === this.activeCode) {
this.changeActiveItemConfig(config)
}
this.changeBorderStyle(config)
},
changeBorderStyle (config) {
this.borderBgId = `borderBg${config.code}`
if (document.querySelector(`#dataV${config.code}`)) {
const borderElement = document.querySelector(`#dataV${config.code}`).querySelector('.border') || document.querySelector(`#dataV${config.code}`)?.querySelector('.dv-border-svg-container')
if (borderElement) {
let isBorder7 = false
borderElement.childNodes.forEach(e => {
if (e._prevClass === 'dv-bb7-line-width-2') {
isBorder7 = true
}
})
borderElement.style.opacity = (config.customize.opacity / 100)
if (this.colorType === 'gradient') {
if (!isBorder7) {
let gradientDirection = ''
switch (config.customize.gradientDirection) {
case 'to right':
gradientDirection = 'x1="0%" y1="0%" x2="100%" y2="0%"'
break
case 'to left':
gradientDirection = 'x1="100%" y1="0%" x2="0%" y2="0%"'
break
case 'to bottom':
gradientDirection = 'x1="0%" y1="0%" x2="0%" y2="100%"'
break
case 'to top':
gradientDirection = 'x1="0%" y1="100%" x2="0%" y2="0%"'
break
case 'to bottom right':
gradientDirection = 'x1="0%" y1="0%" x2="100%" y2="100%"'
break
case 'to bottom left':
gradientDirection = 'x1="100%" y1="0%" x2="0%" y2="100%"'
break
case 'to top right':
gradientDirection = 'x1="0%" y1="100%" x2="100%" y2="0%"'
break
case 'to top left':
gradientDirection = 'x1="100%" y1="100%" x2="0%" y2="0%"'
break
default:
gradientDirection = 'x1="0%" y1="0%" x2="100%" y2="0%"'
break
}
// 在目标元素内的第一个位置插入 <defs> 和其中的内容
borderElement.insertAdjacentHTML(
'afterbegin',
`<defs>
<linearGradient id="${this.borderBgId}" ${gradientDirection}>
<stop offset="0%" stop-color="${config.customize.gradientColor0}" />
<stop offset="100%" stop-color="${config.customize.gradientColor1}" />
</linearGradient>
</defs>`
)
} else {
borderElement.style.background = `linear-gradient(${config.customize.gradientDirection},${config.customize.gradientColor0}, ${config.customize.gradientColor1})`
isBorder7 = false
}
}
}
}
}
}
}
export { dataVMixins }

View File

@@ -0,0 +1,152 @@
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
const datasetMixins = {
props: {
isEdit: {
type: Boolean,
default: false
},
datasetId: {
type: String,
default: null
},
datasetName: {
type: String,
default: ''
},
typeId: {
type: String,
default: ''
},
appCode: {
type: String,
default: ''
}
},
data () {
return {
dataForm: {},
dataPreviewList: [],
structurePreviewList: [],
structurePreviewListCopy: [],
typeName: '',
categoryData: [],
current: 1,
size: 10,
totalCount: 0,
fieldDescVisible: false,
fieldsetVisible: false,
tableLoading: false,
saveLoading: false,
saveText: '',
typeSelect: [
{ value: 'String' },
{ value: 'Integer' },
{ value: 'Double' },
{ value: 'Long' },
{ value: 'Date' }
]
}
},
methods: {
/**
* 使用字段名填充字段描述
*/
fieldDescFill () {
this.structurePreviewList.forEach(field => {
if (field.fieldDesc === '' || !field.hasOwnProperty('fieldDesc')) {
field.fieldDesc = field.fieldName
}
})
this.save('form')
this.fieldDescVisible = false
},
/**
* 打开字段描述编辑弹窗
*/
fieldDescEdit () {
this.fieldDescVisible = false
this.fieldsetVisible = true
},
/**
* 跳过字段描述编辑直接保存
*/
toSave () {
this.save('form', true)
this.fieldDescVisible = false
},
/**
* 取消编辑字段
*/
cancelField () {
this.structurePreviewListCopy = cloneDeep(this.structurePreviewList)
this.fieldsetVisible = false
},
/**
* 保存字段设置
*/
setField () {
this.structurePreviewList = cloneDeep(this.structurePreviewListCopy)
this.fieldsetVisible = false
},
/**
* 清空分类选择
*/
clearType () {
this.typeName = ''
this.dataForm.typeId = ''
},
/**
* 分类展开高亮
* @param $event
*/
setCurrentNode ($event) {
if ($event) {
const key = this.dataForm.typeId || null
this.$refs.categorySelectTree.setCurrentKey(key)
}
},
/**
* 分类选择
* @param value
*/
selectParentCategory (value) {
this.dataForm.typeId = value.id
this.typeName = value.name
this.$refs.selectParentName.blur()
},
goBack () {
this.$emit('back')
},
// 每页大小改变触发
sizeChangeHandle (value) {
this.size = value
this.current = 1
this.datasetTest(false)
},
// 当前页数改变
currentChangeHandle (value) {
this.current = value
this.datasetTest(false)
const tableBodyWrapperEl = document.querySelector('.el-table__body-wrapper') || {}
this.$nextTick(() => {
if (tableBodyWrapperEl) {
// 表格滚动到顶部
tableBodyWrapperEl.scrollTop = 0
}
})
},
// 表头添加提示
renderHeader (h, { column, index }) {
const labelLong = column.label.length // 表头label长度
const size = 14 // 根据需要定义标尺,直接使用字体大小确定就行,也可以根据需要定义
column.minWidth = labelLong * size < 120 ? 120 : labelLong * size // 根据label长度计算该表头最终宽度
return h('span', { class: 'cell-content', style: { width: '100%' } }, [column.label])
},
openNewWindow (url) {
window.open(url, '_blank')
}
}
}
export { datasetMixins }

View File

@@ -0,0 +1,85 @@
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import uniqBy from 'lodash/uniqBy'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import { mapMutations } from 'vuex'
// import { getUpdateChartInfo } from '../api/bigScreenApi'
export default {
data () {
return {
filterList: [],
treeParentId: 0,
dataLoading: false
}
},
methods: {
...mapMutations('bigScreen', {
changeChartKey: 'changeChartKey'
}),
/**
* bigScreen数据联动时根据入参的值进行数据处理
* @param filterList 过滤条件
* @param isInner 是否是组件内部的数据改变
*/
dataInit (filterList, isInner = false) {
if (Array.isArray(filterList) && filterList.length) {
this.filterList = filterList
}
filterList = this.combineFilterList(isInner).filter(
field => ![undefined, ''].includes(field.value)
)
// this.dataLinkageHandle(this.config, this.pageInfo.code, filterList)
this.changeData(this.config, filterList)
},
/**
* 联动数据
* @param {*} formData
* */
linkage (formData) {
EventBus.$emit('dataInit', formData, this.config.linkage.components)
},
/**
* 绑定数据
* @param {*} formData
* */
// binding (formData) {
// EventBus.$emit('dataInit', formData, this.config.binding.components)
// },
/**
* 处理外部联动数据和内部联动数据,合并,如果内部搜索区的参数在外部联动,则赋值上
* @param {Boolean} isInner 是否是内部组件
*/
combineFilterList (isInner = false) {
let filterList = isInner ? [] : cloneDeep(this.filterList)
// 如果内部组件的搜索条件不存在则直接返回全局的filterList
if (!this.$refs?.searchForm?.form) {
return filterList
}
// 对比如果filterList的column和内部参数innerFilterList一致则赋值内部参数
const form = this.$refs.searchForm.form
const innerFilteKeyMap = Object.keys(form)
// eslint-disable-next-line no-unused-expressions
filterList?.map(filterItem => {
if (innerFilteKeyMap.includes(filterItem.column)) {
this.formData[filterItem.column] = filterItem.value
this.$refs.searchForm.form[filterItem.column] = filterItem.value
}
})
// 处理内部参数 filterList
const innerFilterList = this.config?.fields
?.map(field => {
return {
column: field.name,
operator: field.queryRule || 'like',
value: this.formData[field.name]
}
})
.filter(field => ![undefined, ''].includes(field.value))
// 合并去重
filterList = [...filterList, ...innerFilterList]
filterList = uniqBy(filterList, 'column')
return filterList
}
}
}

View File

@@ -0,0 +1,145 @@
/**
* 选中多个组件进行组合
*/
import { mapMutations, mapState } from 'vuex'
let isMac = false
if (window && window.navigator && window.navigator.userAgent) {
isMac = window.navigator.userAgent.indexOf('Mac') > -1
}
export default {
computed: {
...mapState({
activeCodes: (state) => state.bigScreen.activeCodes,
activeChart: (state) => state.bigScreen.activeItemConfig
})
},
mounted () {
// 监听shift键的按下和抬起
document.addEventListener('keydown', this.keydown)
document.addEventListener('keyup', this.keyup)
},
beforeDestroy () {
document.removeEventListener('keydown', this.keydown)
document.removeEventListener('keyup', this.keyup)
},
methods: {
...mapMutations('bigScreen', {
changeCtrlOrCommandDown: 'changeCtrlOrCommandDown',
changeActivePos: 'changeActivePos',
deleteItem: 'delItem',
undoTimeLine: 'undoTimeLine',
copyCharts: 'copyCharts',
pasteCharts: 'pasteCharts'
}),
keydown (event) {
// 获取当前获得焦点的元素
const activeElement = document.activeElement
// 判断当前获得焦点的元素是否是一个输入元素
const isInputFocused = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement
if (!isInputFocused) {
// 当前页面没有输入聚焦
if (event.keyCode === 37) {
// 关闭默认事件
event.preventDefault()
// 左箭头键被按下
this.changeActivePos({ diffX: -1, diffY: 0 })
} else if (event.keyCode === 38) {
// 关闭默认事件
event.preventDefault()
// 上箭头键被按下
this.changeActivePos({ diffX: 0, diffY: -1 })
} else if (event.keyCode === 39) {
// 关闭默认事件
event.preventDefault()
// 右箭头键被按下
this.changeActivePos({ diffX: 1, diffY: 0 })
} else if (event.keyCode === 40) {
// 关闭默认事件
event.preventDefault()
// 下箭头键被按下
this.changeActivePos({ diffX: 0, diffY: 1 })
}
}
// ctrl/command + s保存
if ((event.ctrlKey || event.metaKey) && event.keyCode === 83) {
// 关闭默认事件
event.preventDefault()
this.$refs.PageTopSetting.save('saveLoading')
}
// ctrl/command + z撤销
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && event.keyCode === 90) {
event.preventDefault()
this.undoTimeLine(true)
}
// ctrl/command + shift + z 反撤销
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.keyCode === 90) {
event.preventDefault()
this.undoTimeLine(false)
}
if (isMac) {
// 是否按下了command键
if (event.metaKey) {
this.changeCtrlOrCommandDown(true)
}
} else {
// 是否按下了ctrl键
if (event.ctrlKey) {
this.changeCtrlOrCommandDown(true)
}
}
},
keyup (event) {
// 判断mac系统还是windows系统
if (isMac) {
// 是否按下了command键
if (!event.metaKey) {
this.changeCtrlOrCommandDown(false)
}
} else {
// 是否按下了ctrl键
if (!event.ctrlKey) {
this.changeCtrlOrCommandDown(false)
}
}
},
designKeydown (event) {
// 删除键被按下且鼠标没有在输入框中
if (
(event.keyCode === 8 || event.keyCode === 46) &&
!event.target.classList.contains('el-input__inner')
) {
// 关闭默认事件
event.preventDefault()
// 如果没有选中任何组件的话则不弹出删除提示框
if (!this.activeCodes.length) {
return
}
this.$confirm('确定删除该组件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
customClass: 'bs-el-message-box'
}).then(() => {
// 批量删除
if (Array.isArray(this.activeCodes) && this.activeCodes.length > 0) {
this.deleteItem(this.activeCodes)
} else {
// 单个删除
this.deleteItem(this.activeChart)
}
}).catch(() => {})
}
// ctrl/command + c复制
if ((event.ctrlKey || event.metaKey) && event.keyCode === 67) {
this.copyCharts()
}
if ((event.ctrlKey || event.metaKey) && event.keyCode === 86) {
// 粘贴
this.pasteCharts()
}
}
}
}

View File

@@ -0,0 +1,136 @@
// 分页,分页几乎每个列表页面都会存在,代码的重合度很高,所以提出来了
const pageMixins = {
data () {
return {
// 当前页
current: 1,
// 每页大小
size: 10,
// 总页数
totalPage: 0,
// 总数据条数
totalCount: 0,
prevText: '',
nextText: '',
// 排序相关
sortForm: {
/**
* key: 字段名称
* value: descending 、ascending、''
*/
sortFieldMap: {},
// 定义排序字段的排序,如果不支持多个字段同时排序,可以传入一个即可
sortFieldOrderList: [],
// 缓存列和类名信息,方便后面清空排序字段信息
columnCacheMap: {}
},
// 允许多字段排序
enableMultiFieldOrder: false,
sortFieldHeaderTipMap: {}
}
},
watch: {
'sortForm.sortFieldMap': {
handler (sortFieldMap, oldV) {
for (const columnName in sortFieldMap) {
const order = sortFieldMap[columnName]
if (!order) {
this.$set(this.sortFieldHeaderTipMap, columnName, '点击升序')
} else if (order === 'ascending') {
this.$set(this.sortFieldHeaderTipMap, columnName, '点击降序')
} else if (order === 'descending') {
this.$set(this.sortFieldHeaderTipMap, columnName, '取消排序')
} else {
this.$set(this.sortFieldHeaderTipMap, columnName, '点击升序')
}
}
},
deep: true
}
},
methods: {
/**
* 初始化排序信息
* @param sortFieldOrderList 排序字段的优先级
* @param defaultSortFieldMap 默认排序字段信息 descending 降序ascending升序
*/
initSortField (sortFieldOrderList = [], defaultSortFieldMap = {}) {
if (defaultSortFieldMap) {
for (const field in defaultSortFieldMap) {
const order = defaultSortFieldMap[field]
this.$set(this.sortForm.sortFieldMap, field, order)
}
}
for (let i = 0; i < sortFieldOrderList.length; i++) {
const field = sortFieldOrderList[i]
const order = this.sortForm.sortFieldMap[field]
if (!order) {
// 解决未设置默认排序值字段提示为空
this.$set(this.sortForm.sortFieldMap, field, '')
}
}
this.sortForm.sortFieldOrderList = sortFieldOrderList
},
// 排序状态记录并激活,否则和页面上的排序对不上
sortStyle ({ row, column, rowIndex, columnIndex }) {
const sortColumnOrder = this.sortForm.sortFieldMap[column.property]
if (sortColumnOrder) {
column.order = sortColumnOrder
}
this.sortForm.columnCacheMap[column.property] = column
},
// 对应表格的 @sort-change 事件,当用户改变了排序的状态时触发
reSort (column) {
if (!this.enableMultiFieldOrder) {
// 不允许多个字段同时排序,清空之前的排序信息
for (const field in this.sortForm.columnCacheMap) {
const column = this.sortForm.columnCacheMap[field]
column.order = ''
}
for (const field in this.sortForm.sortFieldMap) {
this.sortForm.sortFieldMap[field] = ''
}
}
this.$set(this.sortForm.sortFieldMap, column.prop, column.order)
this.reSearch()
},
reSearch () {
this.current = 1
this.getDataList()
},
getDataList () {
console.error('你应该重写getDataList方法')
},
// 每页大小改变触发
sizeChangeHandle (val) {
this.size = val
this.current = 1
this.getDataList()
},
// 当前页数改变
currentChangeHandle (val) {
this.current = val
this.getDataList()
const bsScrollbarEl = document.querySelector('.bs-scrollbar') || {}
const tableBodyWrapperEl = document.querySelector('.el-table__body-wrapper') || {}
this.$nextTick(() => {
if (bsScrollbarEl) {
// 类名bs-scrollbar的元素滚动到顶部
bsScrollbarEl.scrollTop = 0
}
if (tableBodyWrapperEl) {
// 表格滚动到顶部
tableBodyWrapperEl.scrollTop = 0
}
})
},
getSortForm () {
return {
sortFieldMap: this.sortForm.sortFieldMap,
sortFieldOrderList: this.sortForm.sortFieldOrderList
}
}
}
}
export { pageMixins }

View File

@@ -0,0 +1,13 @@
/*
* @description: 封装组件内部的非props外部参数
*/
import { mapState } from 'vuex'
export default {
computed: {
...mapState('bigScreen', {
pageInfo: state => state.pageInfo,
customTheme: state => state.pageInfo.pageConfig.customTheme,
themeJson: state => state.pageInfo.pageConfig?.themeJson
})
}
}

View File

@@ -0,0 +1,28 @@
const refreshBorder = {
data () {
return {
updateKey: 0
}
},
computed: {
Data () {
return JSON.parse(JSON.stringify(this.config))
}
},
watch: {
Data: {
handler (newVal, oldVal) {
this.$nextTick(() => {
if ((newVal.w !== oldVal.w) || (newVal.h !== oldVal.h)) {
this.updateKey = new Date().getTime()
}
})
},
deep: true
}
},
methods: {
}
}
export { refreshBorder }

View File

@@ -0,0 +1,49 @@
import { mapMutations, mapState } from 'vuex'
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
import cloneDeep from "lodash/cloneDeep";
const refreshComponentMixin = {
data () {
return {
updateKey: 0
}
},
computed: {
...mapState({
customTheme: state => state.bigScreen.pageInfo.pageConfig.customTheme,
activeCode: state => state.bigScreen.activeCode
}),
Data () {
return JSON.parse(JSON.stringify(this.config))
}
},
watch: {
Data: {
handler (newVal, oldVal) {
this.$nextTick(() => {
if ((newVal.w !== oldVal.w) || (newVal.h !== oldVal.h)) {
this.updateKey = new Date().getTime()
}
})
},
deep: true
}
},
methods: {
...mapMutations({
changeChartConfig: 'bigScreen/changeChartConfig',
changeActiveItemConfig: 'bigScreen/changeActiveItemConfig'
}),
// 修改样式
changeStyle (config) {
config = { ...this.config, ...config }
// 样式改变时更新主题配置
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
this.changeChartConfig(config)
if (config.code === this.activeCode) {
this.changeActiveItemConfig(config)
}
}
}
}
export { refreshComponentMixin }

View File

@@ -0,0 +1,266 @@
// 组件配置转化
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import { setModules, dataModules } from 'data-room-ui/js/utils/configImport'
import { getScreenInfo, getDataSetDetails, getDataByDataSetId } from '../api/bigScreenApi'
import plotSettings from 'data-room-ui/G2Plots/settings'
import echartSettings from 'data-room-ui/Echarts/settings'
import { stringToFunction } from '../utils/evalFunctions'
import { EventBus } from '../utils/eventBus'
import plotList from 'data-room-ui/G2Plots/plotList'
import { settingToTheme, themeToSetting } from 'data-room-ui/js/utils/themeFormatting'
export default {
// 初始化页面数据
initLayout ({ commit, dispatch }, code) {
return new Promise(resolve => {
getScreenInfo(code).then(data => {
// 配置兼容
const pageInfo = handleResData(data)
// 兼容边框配置
pageInfo.chartList.forEach((chart) => {
// 对数据源的方式进行兼容
if (chart.dataSource && ['texts', 'numbers'].includes(chart.type)) {
if (chart.dataSource.source === 'dataset' && (!chart.dataSource.businessKey)) {
chart.dataSource.source = 'static'
}
} else if (!chart.dataSource.source) {
chart.dataSource.source = 'dataset'
}
if (!chart.border) {
chart.border = { type: '', titleHeight: 60, fontSize: 16, isTitle: true, padding: [0, 0, 0, 0] }
}
if (!chart.border.padding) {
chart.border.padding = [0, 0, 0, 0]
}
const plotSettingsIterator = false
const echartSettingsIterator = false
if (chart.type === 'customComponent') {
// 为本地G2组件配置添加迭代器
if (!plotSettingsIterator) {
plotSettings[Symbol.iterator] = function * () {
const keys = Object.keys(plotSettings)
for (const k of keys) {
yield [k, plotSettings[k]]
}
}
}
for (const [key, localPlotSetting] of plotSettings) {
if (chart.name == localPlotSetting.name) {
// 本地配置项
const localSettings = JSON.parse(JSON.stringify(localPlotSetting.setting))
chart.setting = localSettings.map((localSet) => {
// 在远程组件配置中找到 与 本地组件的配置项 相同的项索引
const index = chart.setting.findIndex(remoteSet => remoteSet.field == localSet.field)
if (index !== -1) {
// 使用远程的值替换本地值
localSet.field = chart.setting[index].field
localSet.value = chart.setting[index].value
}
return localSet
})
}
}
} else if (chart.type === 'echartsComponent') {
// 为本地echarts组件配置添加迭代器
if (!echartSettingsIterator) {
echartSettings[Symbol.iterator] = function * () {
const keys = Object.keys(echartSettings)
for (const k of keys) {
yield [k, echartSettings[k]]
}
}
}
for (const [key, localPlotSetting] of echartSettings) {
if (chart.name == localPlotSetting.name) {
// 本地配置项
const localSettings = JSON.parse(JSON.stringify(localPlotSetting.setting))
chart.setting = localSettings.map((localSet) => {
// 在远程组件配置中找到 与 本地组件的配置项 相同的项索引
const index = chart.setting.findIndex(remoteSet => remoteSet.field == localSet.field)
if (index !== -1) {
// 使用远程的值替换本地值
localSet.field = chart.setting[index].field
localSet.value = chart.setting[index].value
}
return localSet
})
}
}
}
})
// 改变页面数据
commit('changePageInfo', pageInfo)
commit('changeZIndex', pageInfo.chartList)
// 初始化缓存数据集数据
// eslint-disable-next-line no-unused-expressions
pageInfo.pageConfig.cacheDataSets?.map((cacheDataSet) => {
dispatch('getCacheDataSetData', { dataSetId: cacheDataSet.dataSetId })
dispatch('getCacheDataFields', { dataSetId: cacheDataSet.dataSetId })
})
// 页面加载成功
resolve(true)
commit('saveTimeLine', '初始化')
})
})
},
// 初始化缓存数据集数据
getCacheDataSetData ({ commit, dispatch }, { dataSetId }) {
getDataByDataSetId(dataSetId).then(res => {
const data = res.data
commit('changeCacheDataSetData', { dataSetId, data })
// 推送数据到各个组件
emitDataToChart(dataSetId, data)
})
},
// 初始化缓存数据集字段
getCacheDataFields ({ commit, dispatch }, { dataSetId }) {
getDataSetDetails(dataSetId).then(data => {
commit('changeCacheDataFields', { dataSetId, data })
commit('changeCacheDataParams', { dataSetId, data })
})
}
}
// 处理后端返回的数据
export function handleResData (data) {
let pageInfo = {}
if (data.pageConfig) {
pageInfo = {
...data,
pageConfig: {
...data.pageConfig,
lightBgColor: data.pageConfig.lightBgColor || '#f5f7fa'
}
}
} else {
pageInfo = {
...data,
pageConfig: {
w: 1920,
h: 1080,
bgColor: '#151a26', // 背景色
lightBgColor: '#f5f7fa',
lightBg: '',
bg: '', // 背景图
customTheme: 'dark',
opacity: 100
}
}
}
// 如果pageConfig中的cacheDataSets为null赋值[]
pageInfo.pageConfig.cacheDataSets = pageInfo.pageConfig.cacheDataSets || []
pageInfo.pageConfig.refreshConfig = pageInfo.pageConfig.refreshConfig || []
let originalConfig = {}
pageInfo.chartList.forEach((chart) => {
if (!['customComponent', 'remoteComponent', 'echartsComponent'].includes(chart.type)) {
originalConfig = { option: { ...setModules[chart.type] }, ...dataModules[chart.type] }
// 如果没有版本号,或者版本号修改了则需要进行旧数据兼容
if ((!chart.version) || chart.version !== originalConfig.version) {
chart = compatibility(chart, originalConfig)
} else {
chart.option = cloneDeep(setModules[chart.type])
}
} else {
originalConfig = plotList?.find(plot => plot.name === chart.name)
chart.option = stringToFunction(chart.option)
// 如果是自定义组件,且没配数据集,就给前端的模拟数据
if (!chart?.dataSource?.businessKey) {
chart.option.data = plotList?.find(plot => plot.name === chart.name)?.option?.data || chart?.option?.data
}
// 如果没有版本号,或者版本号修改了则需要进行旧数据兼容
if ((!chart.version) || (originalConfig && chart.version !== originalConfig?.version)) {
// TODO 远程组件需要重新写处理函数
if (chart.type === 'customComponent') {
chart = compatibility(chart, originalConfig)
}
}
}
// 初始化时应该判断是否存在theme配置没有的话添加默认的两套主题这是为了兼容旧组件
if (!chart.theme) {
chart.theme = settingToTheme(chart, 'dark')
chart.theme = settingToTheme(chart, 'light')
}
chart.key = chart.code
})
// 主题兼容
// pageInfo.chartList = themeToSetting(pageInfo.chartList, pageInfo.pageConfig.customTheme)
// 存储修改后的配置
localStorage.setItem('pageInfo', JSON.stringify(pageInfo))
return pageInfo
}
// 组件属性兼容
function compatibility (config, originalConfig) {
const newConfig = config
newConfig.version = originalConfig?.version || '2023071001'
newConfig.optionHandler = originalConfig.optionHandler || ''
newConfig.dataSource = objCompare(newConfig?.dataSource, originalConfig?.dataSource)
newConfig.customize = objCompare(newConfig?.customize, originalConfig?.customize)
newConfig.option = {
...objCompare(newConfig.option, originalConfig?.option),
displayOption: originalConfig?.option?.displayOption
}
newConfig.setting = arrCompare(newConfig?.setting, originalConfig?.setting)
return newConfig
}
// 对象比较
function objCompare (obj1, obj2) {
const keys1 = obj1 ? Object.keys(obj1) : []
const keys2 = obj2 ? Object.keys(obj2) : []
// 交集
const intersection = keys1?.filter(function (v) {
return keys2.indexOf(v) > -1
}) || []
// 差集
const differenceSet = keys2?.filter(function (v) {
return keys1.indexOf(v) === -1
}) || []
const obj = {}
for (const item of intersection) {
obj[item] = obj1[item]
}
for (const item of differenceSet) {
obj[item] = obj2[item]
}
return obj
}
// 数组比较
function arrCompare (list1, list2) {
const fieldList = list1?.map(item => item.field) || []
const list = list2?.map(item => {
let value = null
// 如果存在交集
if (fieldList.includes(item.field)) {
// 保留旧数据的value
value = (list1.filter(j => {
return j.field === item.field
}))[0].value
} else {
// 不存在交集,直接覆盖
value = item.value
}
return {
...item,
value
}
}) || []
return list
}
// 推送数据到各个组件
function emitDataToChart (dataSetId, data) {
if (data && data.length) {
EventBus.$emit('cacheDataInit',
{
success: true,
data: data
},
dataSetId
)
}
}

View File

@@ -0,0 +1,3 @@
export default {
}

View File

@@ -0,0 +1,12 @@
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
export default {
namespaced: true,
state,
mutations,
actions,
getters
}

View File

@@ -0,0 +1,3 @@
// 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。
// 这样可以使 linter 之类的工具发挥作用,
// 同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

View File

@@ -0,0 +1,512 @@
/*
* @description: vuex mutations 事件
* @Date: 2023-03-13 10:04:59
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-06-08 15:24:01
*/
import Vue from 'vue'
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
import uniq from 'lodash/uniq'
import { defaultData } from './state'
import moment from 'moment'
import { randomString } from 'data-room-ui/js/utils'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import CloneDeep from 'lodash-es/cloneDeep'
export default {
// 改变页面基本信息,后端请求的页面信息存储到此处
changePageInfo (state, pageInfo) {
state.pageInfo = pageInfo
},
// 改变组件列表
changeLayout (state, layout) {
state.pageInfo.chartList = layout
},
//
changeIframeDialog (state, dialogVisible) {
state.iframeDialog = dialogVisible
},
// 改变当前选择组件id
changeActiveCode (state, code) {
state.activeCode = code
state.hoverCode = code
let activeItem = {}
// let activeItem = cloneDeep(state.pageInfo.chartList?.find(
// item => item.code === code
// ))
for (const item of state.pageInfo.chartList) {
// 检查当前项的 code 是否与 currentCode 匹配
if (item.code === code) {
activeItem = item
break // 找到匹配的项后,退出循环
}
// 如果当前项的 type 为 'chartTab',则进一步检查其 tabList
if (item.type === 'chartTab') {
for (const tabItem of item.customize.tabList) {
// 检查 tabList 中的每一项的 code 是否与 currentCode 匹配
if (tabItem.chartCode === code) {
activeItem = tabItem.chart
break // 找到匹配的项后,退出循环
}
}
}
}
changeGroup(code, state)
state.activeItemConfig = cloneDeep(activeItem)
},
changeActiveCodes (state, codes) {
// 传入codes将codes中的组件的group改为tempGroup不传入或者传入空数组将所有组件的group改为'',即取消组合
if (codes.length !== 0) {
state.activeCodes = codes
state.pageInfo.chartList = state.pageInfo.chartList?.map(chart => {
return {
...chart,
group: (codes.includes(chart.code) && !chart.group) ? 'tempGroup' : chart.group
}
})
} else {
state.activeCodes = codes
state.pageInfo.chartList = state.pageInfo.chartList?.map(chart => {
return {
...chart,
// 确保取消高亮状态时不会使得原本设置过的组合被取消
group: chart.group === 'tempGroup' ? '' : chart.group
}
})
}
},
// 情况页面选中的组件
clearActiveCodes (state) {
state.activeCodes = []
},
// 改变当前hover组件id
changeHoverCode (state, code) {
state.hoverCode = code
},
// 改变当前选中组件id
changePageLoading (state, booleanValue) {
// 改变loading状态
state.pageLoading = booleanValue
},
// 改变当前组件的加载状态
changeChartLoading (state, itemConfig) {
// 改变loading状态
state.pageInfo.chartList.forEach((chart, index) => {
if (chart.code === itemConfig.code) {
chart.loading = itemConfig.loading
}
})
},
// 改变当前组件配置
changeChartConfig (state, itemConfig) {
// 如果存在parentCode的组件则是tab中的组件
if (itemConfig?.parentCode) {
state.pageInfo.chartList.forEach((chart, index) => {
if (chart.code === itemConfig.parentCode) {
chart.customize.tabList.forEach((tabItem, i) => {
if (tabItem.chartCode === itemConfig.code) {
Vue.set(state.pageInfo.chartList[index].customize.tabList[i], 'chart', {
...state.pageInfo.chartList[index].customize.tabList[i].chart,
...itemConfig
})
}
})
}
})
} else {
// 如果是一般的组件
let index = null
index = state.pageInfo.chartList.findIndex(
item => item.code === itemConfig.code
)
Vue.set(state.pageInfo.chartList, index, {
...state.pageInfo.chartList[index],
...itemConfig
})
// 对比之前的config和当前的itemConfig的xywh如果有变化就改变卡尺对齐线
const oldConfig = state.pageInfo.chartList[index]
if (
oldConfig.x !== itemConfig.x ||
oldConfig.y !== itemConfig.y ||
oldConfig.w !== itemConfig.w ||
oldConfig.h !== itemConfig.h
) {
// 改变当前组件的卡尺对齐线
changePresetLine(state, itemConfig)
}
}
},
setPresetLine (state, { x, y, w, h }) {
state.presetLine = [
{ type: 'h', site: y || 0 },
{ type: 'v', site: x || 0 }
]
},
changeActiveItemConfig (state, config) {
state.activeItemConfig = cloneDeep(config)
},
// 新增一个组件
addItem (state, itemConfig) {
// 放到第一项
state.pageInfo.chartList.unshift(itemConfig)
changeZIndexFuc(state, state.pageInfo.chartList)
saveTimeLineFunc(state, '新增组件' + itemConfig?.title)
},
// 删除组件/批量删除组件
delItem (state, codes) {
if (Array.isArray(codes)) {
const delCharts = state.pageInfo.chartList.filter(chart => codes.includes(chart.code))
// 如果删除的组件中有跑马灯,需要删除将跑马灯组件的音频实例销毁
delCharts.some(item => { item.type === 'marquee' && EventBus.$emit('deleteComponent', item.code) })
state.pageInfo.chartList = state.pageInfo.chartList.filter(chart => !codes.includes(chart.code))
} else {
// 如果删除的组件是跑马灯,需要删除将跑马灯组件的音频实例销毁
const delChart = state.pageInfo.chartList.find(chart => codes === chart.code)
if (delChart && delChart.type === 'marquee') {
EventBus.$emit('deleteComponent', codes)
}
state.pageInfo.chartList = state.pageInfo.chartList.filter(chart => codes !== chart.code)
// 删除组件时,将该组件的缓存数据库中的数据也删除
deldataset(state, 'dataset', codes)
deldataset(state, 'computedDatas', codes)
}
// 存储删除后的状态
saveTimeLineFunc(state, '删除组件')
if (state.pageInfo.chartList.findIndex(item => item.code === state.activeCode) == -1) {
state.activeItemConfig = null
state.activeCode = null
EventBus.$emit('closeRightPanel')
}
// 发送事件,关闭配置面板
},
changePageConfig (state, pageConfig) {
Vue.set(state.pageInfo, 'pageConfig', cloneDeep(pageConfig))
state.updateKey = new Date().getTime()
},
changeActiveItem (state, activeItem) {
state.activeItem = cloneDeep(activeItem)
state.activeId = activeItem.code
// state.settingJson = cloneDeep(activeItem.settingConfig) || {}
},
// 改变当前组件的xywh
changeActiveItemWH (state, chart) {
if (chart.code === state.activeItemConfig.code) {
state.activeItemConfig = {
...state.activeItemConfig,
...chart
}
}
},
// 清空卡尺对齐线
resetPresetLine (state) {
state.presetLine = []
},
// 改变组件的层级
changeZIndex (state, list) {
changeZIndexFuc(state, list)
},
// 改变锁定状态
changeLocked (state, config) {
// 如果是多选,则改变框选中的所有组件的锁定状态
if (state.activeCodes && state.activeCodes.length > 1) {
state.pageInfo.chartList = state.pageInfo.chartList?.map(chart => {
return {
...chart,
locked: state.activeCodes.includes(chart.code) ? !config.locked : chart.locked
}
})
saveTimeLineFunc(state, config.locked ? '解锁选中组件' : '锁定选中组件')
} else {
// 如果不是多选,则只改变当前一个
const index = state.pageInfo.chartList.findIndex(
item => item.code === config.code
)
Vue.set(state.pageInfo.chartList[index], 'locked', !config.locked)
saveTimeLineFunc(state, !config.locked ? `解锁${config?.title}` : `锁定${config?.title}`)
}
},
// 改变网格显示状态
changeGridShow (state, isShow) {
state.hasGrid = isShow
},
// 改变组件的key
changeChartKey (state, code) {
const index = state.pageInfo.chartList.findIndex(
item => item.code === code
)
if (index < 0) {
return
}
const config = state.pageInfo.chartList[index]
Vue.set(config, 'key', config.code + new Date().getTime())
},
// 改变缓存数据集中的字段列表
changeCacheDataFields (state, { dataSetId, data }) {
// 将 state.pageInfo.pageConfig.cacheDataSets 中的 dataSetId 对应fields字段数据替换为 data
const index = state.pageInfo.pageConfig.cacheDataSets.findIndex(cacheData => cacheData.dataSetId === dataSetId)
if (index < 0) {
return
}
Vue.set(state.pageInfo.pageConfig.cacheDataSets[index], 'fields', data?.fields || [])
},
// 改变缓存数据集中的数据参数
changeCacheDataParams (state, { dataSetId, data }) {
// 将 state.pageInfo.pageConfig.cacheDataSets 中的 dataSetId 对应fields字段数据替换为 data
const index = state.pageInfo.pageConfig.cacheDataSets.findIndex(cacheData => cacheData.dataSetId === dataSetId)
if (index < 0) {
return
}
Vue.set(state.pageInfo.pageConfig.cacheDataSets[index], 'params', data?.params || [])
},
// 改变缓存数据集中的数据
changeCacheDataSetData (state, { dataSetId, data }) {
const index = state.pageInfo.pageConfig.cacheDataSets.findIndex(cacheData => cacheData.dataSetId === dataSetId)
if (index < 0) {
return
}
state.pageInfo.pageConfig.cacheDataSets[index].data = data || []
},
// 改变shift是否被按下
changeCtrlOrCommandDown (state, isDown) {
state.shiftKeyDown = isDown
},
// 初始化store中的数据防止污染
resetStoreData (state) {
for (const stateKey in state) {
state[stateKey] = cloneDeep(defaultData[stateKey])
}
},
changeZoom (state, zoom) {
state.zoom = zoom
},
changeFitZoom (state, zoom) {
state.fitZoom = zoom
},
changeActivePos (state, { diffX, diffY }) {
const activeCodes = state.activeCodes
activeCodes?.forEach(code => {
const chart = state.pageInfo.chartList.find(item => item.code === code)
if (chart) {
chart.x += diffX
chart.y += diffY
}
const index = state.pageInfo.chartList.findIndex(
item => item.code === chart.code
)
if (index < 0) {
return
}
Vue.set(state.pageInfo.chartList, index, {
...state.pageInfo.chartList[index],
...chart
})
changePresetLine(state, chart)
})
},
// 保存当前状态
saveTimeLine (state, title) {
const date = new Date()
const time = moment(date).format('HH:mm:ss')
// title默认获取当前时间时分秒
if (!title) {
const date = new Date()
title = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
}
saveTimeLineFunc(state, title, time)
},
// 撤回/反撤回当前事件线 undo和redo放到一个函数中用isUndo区分
undoTimeLine (state, isUndo = true) {
let currentStore = {}
// 撤回
if (isUndo) {
if (state.timelineStore.length > 0 && state.currentTimeLine > 1) {
// 时间线往前推一个
state.currentTimeLine = state.currentTimeLine - 1
currentStore = state.timelineStore[state.currentTimeLine - 1]
if (currentStore?.chartList) {
state.pageInfo.chartList = cloneDeep(currentStore?.chartList)
state.activeItemConfig = cloneDeep(currentStore?.chartList?.find(item => item.code === state.activeCode) || {})
EventBus.$emit('operationRollback', true)
}
}
}
// 反撤回 redo
if (!isUndo) {
if (state.currentTimeLine < state.timelineStore.length) {
// 时间线往后推一个
state.currentTimeLine = state.currentTimeLine + 1
currentStore = state.timelineStore[state.currentTimeLine - 1]
state.pageInfo.chartList = cloneDeep(currentStore?.chartList || [])
}
}
state.pageInfo.chartList = state.pageInfo.chartList.map(chart => {
return {
...chart,
key: chart.code + new Date().getTime()
}
})
},
clearTimeline (state) {
// 最后一个状态
const lastStore = state.timelineStore[state.timelineStore.length - 1]
// 将最后一个状态作为初始状态,否则下次拖拽后无法回到之前
state.timelineStore = [
{
...lastStore,
timelineTitle: '初始状态',
updateTime: moment(new Date()).format('HH:mm:ss')
}
]
state.currentTimeLine = 1
},
// 回退到指定时间线
rollbackTimeline (state, index) {
state.pageInfo.chartList = cloneDeep(state.timelineStore[index]?.chartList || [])
state.currentTimeLine = index + 1
},
// 复制组件
copyCharts (state) {
state.copyChartCodes = cloneDeep(state.activeCodes)
},
// 粘贴组件
pasteCharts (state) {
const copyChartCodes = state.copyChartCodes
const chartList = state.pageInfo.chartList
// 将选中的组件复制一份, code加上 随机后缀, key 也加上随机后缀, x, y 各增加50
const additionCode = randomString(5)
const copyCharts = copyChartCodes.map(code => {
const chart = chartList.find(item => item.code === code)
const copyChart = cloneDeep(chart)
copyChart.code = `${copyChart.code}_${additionCode}`
copyChart.key = `${copyChart.key}_${additionCode}`
copyChart.group = (copyChart.group && copyChart.group !== 'tempGroup') ? `${copyChart.group}_${additionCode}` : ''
copyChart.x += 50
copyChart.y += 50
return copyChart
})
// 将复制的组件添加到chartList中
state.pageInfo.chartList = [...copyCharts, ...state.pageInfo.chartList]
},
// 更新数据集库中的内容
updateDataset (state, res) {
// 如果只是更新了组件的标题
if (res.isChangeTitle) {
if (state.dataset.hasOwnProperty(res.oldTitle + '_' + res.code)) {
const _dataset = CloneDeep(state.dataset)
_dataset[res.title + '_' + res.code] = _dataset[res.oldTitle + '_' + res.code]
delete _dataset[res.oldTitle + '_' + res.code]
state.dataset = CloneDeep(_dataset)
}
} else {
Vue.set(state.dataset, res.title + '_' + res.code, res.data)
}
},
// 更新数据集库中的内容
updateComputedDatas (state, res) {
// 如果只是更新了组件的标题
if (res.isChangeTitle) {
if ((!res.isExpression) && state.computedDatas.hasOwnProperty(res.oldTitle + '_' + res.code)) {
const _computedDatas = CloneDeep(state.computedDatas)
_computedDatas[res.title + '_' + res.code] = _computedDatas[res.oldTitle + '_' + res.code]
delete _computedDatas[res.oldTitle + '_' + res.code]
state.computedDatas = CloneDeep(_computedDatas)
}
} else {
Vue.set(state.computedDatas, res.title + '_' + res.code, res.data)
}
},
// 清空数据集库
emptyDataset (state) {
state.dataset = {}
},
// 清空数据集库
emptyComputedDatas (state) {
state.computedDatas = {}
},
// 修改磁吸状态
snapChange (state, snap) {
state.snapTolerance = snap
}
}
function deldataset (state, type, codes) {
const datasets = state[type]
for (const code of codes) {
for (const key in datasets) {
if (key.endsWith(code)) {
delete state[type][key]
break // 找到匹配的属性后,退出内层循环
}
}
}
}
function changeZIndexFuc (state, list) {
const len = list?.length - 1 || 0
list.forEach((item, i) => {
const index = state.pageInfo.chartList.findIndex(
_item => _item.code === item.code
)
Vue.set(state.pageInfo.chartList[len - index], 'z', i)
})
}
// 改变当前组件的卡尺对齐线
function changePresetLine (state, { x, y, w, h }) {
state.presetLine = [
{ type: 'h', site: y || 0 },
{ type: 'v', site: x || 0 }
]
}
function changeGroup (code, state) {
if (code) {
// 找到和此组件group相同的组件并添加到activeCodes中
const group = state.pageInfo.chartList?.find(item => item.code === code)?.group
if (group) {
state.activeCodes = state.pageInfo.chartList?.filter(chart => chart.group === group && chart.group).map(item => item.code)
}
if (state.shiftKeyDown) {
state.activeCodes = uniq([...state.activeCodes, code])
// eslint-disable-next-line no-unused-expressions
state.pageInfo.chartList?.forEach(chart => {
if (state.activeCodes.includes(chart.code)) {
chart.group = 'tempGroup'
}
})
} else {
if (!group) {
state.activeCodes = [code]
}
}
} else {
state.activeCodes = []
state.pageInfo.chartList = state.pageInfo.chartList?.map(chart => ({
...chart,
group: chart.group === 'tempGroup' ? '' : chart.group
}))
}
}
function saveTimeLineFunc (state, title, time) {
// 最多保存10个状态
const MAX_TIME_LINE = 10
const stateCopy = cloneDeep(state.pageInfo)
const date = new Date()
time = time || moment(date).format('HH:mm:ss')
stateCopy.timelineTitle = title
stateCopy.updateTime = time
if (!Array.isArray(state.timelineStore)) {
state.timelineStore = []
}
if (!Number.isInteger(state.currentTimeLine)) {
state.currentTimeLine = 0
}
if (state.timelineStore.length >= MAX_TIME_LINE) {
// 去掉最早的一个
state.timelineStore.shift()
}
state.timelineStore?.push(stateCopy)
state.currentTimeLine = state.timelineStore?.length
}

View File

@@ -0,0 +1,83 @@
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
export const defaultData = {
// 大屏信息
pageInfo: {
className:
'com.gccloud.dataroom.core.module.manage.dto.DataRoomPageDTO',
id: '',
name: '测试bigScreen',
code: '',
icon: '',
iconColor: '#007aff',
orderNum: 0,
remark: '',
type: 'bigScreen',
style: '',
parentCode: '0',
// 大屏页面的配置
pageConfig: {
w: 1920,
h: 1080,
bgColor: '#151a26', // 背景色
bg: '', // 背景图
lightBgColor: '#f5f7fa',
lightBg: '',
opacity: 100,
customTheme: 'dark',
themeJson: {}, // 自定义主题配置
// 缓存的数据集 { name: '', dataSetId: '' }
cacheDataSets: [],
refreshConfig: [],
// 自适应模式 无(none) 、自动(auto)、宽度铺满(fitWidth)、高度铺满(fitHeight)和 双向铺满cover 5 种自适应模式
fitMode: 'none'
},
// 图表的集合
chartList: []
},
// 当前选中的组件code
activeCode: null,
// 当前鼠标悬浮的组件code
hoverCode: null,
// 页面初始化加载状态
pageLoading: true,
// 当前选中的组件的配置
activeItemConfig: null,
// 当前选中的组件的对齐线
presetLine: [],
// 强制更新键
updateKey: null,
// 是否显示网格
hasGrid: false,
// 多选的组件code数据
activeCodes: [],
// 复制到粘贴板上的组件编码
copyChartCodes: [],
// false 表示 shift 键没有被按下, true表示 shift 键被按下
shiftKeyDown: false,
// 缩放
zoom: 100,
// 自适应下的缩放比例
fitZoom: 100,
iframeDialog: false,
// 页面上所有组件的数据集的数据信息
dataset: {},
// 页面上所有组件的数据集的数据信息
computedDatas: {},
// 是否开启磁吸
snapTolerance: 3
}
export default () => ({
// 存储的大屏timeline信息
timelineStore: [],
// 当前的timeline 的index
currentTimeLine: 0,
// 具体信息
...cloneDeep(defaultData)
})

View File

@@ -0,0 +1,91 @@
/*!
* 标签管理
*/
import Vue from 'vue'
/**
* 获取标签列表
* @returns {*}
*/
const getLabelList = () => Vue.prototype.$dataRoomAxios.get('/label/getLabelList')
/**
* 获取标签
* @param data
* @returns {*}
*/
const labelList = (data) => Vue.prototype.$dataRoomAxios.get('/label/list', data)
/**
* 获取标签分类
* @returns {*}
*/
const getLabelType = () => Vue.prototype.$dataRoomAxios.get('/label/getLabelType')
/**
* 根据种类移除标签
* @param data
* @returns {*}
*/
const removeLabelByType = (data) => Vue.prototype.$dataRoomAxios.post('/label/removeLabelByType', data)
/**
* 移除标签
* @param id
* @returns {*}
*/
const removeLabel = (id = '-1') => Vue.prototype.$dataRoomAxios.get(`/label/removeLabel/${id}`)
/**
* 检查重复标签
* @param data
* @returns {*}
*/
const checkRepeatLabel = (data) => Vue.prototype.$dataRoomAxios.post('/label/checkRepeat', data)
/**
* 新增/修改标签
* @param data
* @returns {*}
*/
const addOrUpdateLabel = (data) => Vue.prototype.$dataRoomAxios.post('/label/addOrUpdateLabel', data)
/**
* 获取标签详情
* @param id
* @returns {*}
*/
const getLabelDetail = (id = '-1') => Vue.prototype.$dataRoomAxios.get(`/label/getLabelDetail/${id}`)
/**
* 修改标签种类
* @param data
* @returns {*}
*/
const updateLabelType = (data) => Vue.prototype.$dataRoomAxios.post('/label/updateLabelType', data)
/**
* 根据标签id获取数据集id列表
* @param id
*/
const getDataSetIdListByLabelId = (id = '-1') => Vue.prototype.$dataRoomAxios.get(`/label/queryDataSetIdList/${id}`)
/**
* 根据数据集id获取标签列表
* @param id
*/
const getLabelListByDatasetId = (id = '-1') => Vue.prototype.$dataRoomAxios.get(`/label/queryDataSetLabelList/${id}`)
export {
getLabelList,
labelList,
getLabelType,
removeLabelByType,
removeLabel,
checkRepeatLabel,
addOrUpdateLabel,
getLabelDetail,
updateLabelType,
getDataSetIdListByLabelId,
getLabelListByDatasetId
}

View File

@@ -0,0 +1,11 @@
// 颜色选择器的预设颜色
export const predefineColors = [
'#6b74e4',
'#4391f4',
'#38bbe5',
'#69d6fd',
'#36c6a0',
'#007aff',
'#ffffff',
'#0b1833'
]

View File

@@ -0,0 +1,91 @@
/*
* @description: 大屏组件通用属性
* @Date: 2023-03-13 10:04:59
* @Author: xing.heng
* @LastEditors: wujian
* @LastEditTime: 2023-06-01 14:23:23
*/
import getComponentConfig from 'packages/js/utils/getComponentConfig'
import linkageConfig from 'packages/js/config/linkageConfig'
// 关于设置组件在右侧面板可以展示哪些属性配置
export const displayOption = {
serverPagination: {
// 服务端分页
enable: false
},
pageSize: {
// 分页长度
enable: false
},
metricField: {
// 指标
label: '指标',
enable: true,
multiple: true // 是否多选
},
dimensionField: {
// 维度
label: '维度', // 维度/查询字段
enable: true,
multiple: true // 是否多选
},
dimensionList: {
// 维度(只有多折线图会存在两个维度)
label: '维度', // 维度/查询字段
enable: false,
multiple: true // 是否多选
},
seriesField: {
// 数据细分
enable: false,
required: true // 必填
},
dataAllocation: {
// 是否存在数据配置
enable: true
},
params: {
// 参数配置
enable: true
},
dataSourceType: {
// 数据源(数据集或者其他方式:静态数据)
enable: true
}
}
export default function (customConfig) {
return {
...getComponentConfig(customConfig.type),
z: 0, // z轴图层支持
locked: false, // 是否锁定组件
group: '', // 组合组件, 相同group的组件会被组合在一起
code: null,
showTitle: true,
...customConfig.root,
dataSource: {
className:
'com.gccloud.dataroom.core.module.chart.components.datasource.DataSetDataSource',
dataSourceKey: '', // 数据源,选择不同数据库
businessKey: '', // 数据集标识
dimensionField: '', // 维度
metricField: '', // 指标
seriesField: '', // 分类字段
dimensionFieldList: [], // 唯独列表
metricFieldList: [], // 指标列表
seriesFieldList: [], // 分类列表
serverPagination: false, // 服务端分页
pageSize: 10,
params: {},
dataSetType: '', // 数据集类型,
formCode: '',
...customConfig.dataSource // 非通用数据配置
},
customize: {
...customConfig.customize
}, // 自定义设置
...linkageConfig, // 数据联动配置
filterList: [],
dataFlag: false // 判断数据为模拟数据还是真实数据
}
}

View File

@@ -0,0 +1,42 @@
/* eslint-disable no-useless-escape */
/*
* @description: 批量导入pc端大屏组件
* @Date: 2023-03-13 10:04:58
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-05-17 12:40:25
*/
const modules = {};
// 组件名称替换
const replaceName = {};
// 排除的组件
const excludeCommponents = [];
function importComponents(files) {
files.keys().forEach((key) => {
// 正则,取到./和/之间的字符串
const reg = new RegExp("(.\\/)(.*)(\\/)");
let moduleName = key.match(reg)[0].replace(/(\.\/)|(\/)|(src)/g, "");
// 替换组件名称
if (replaceName[moduleName]) {
moduleName = replaceName[moduleName];
}
if (!excludeCommponents.includes(moduleName)) {
modules[moduleName] = files(key).default;
}
});
}
importComponents(
require.context("data-room-ui/BasicComponents", true, /\index.vue$/)
);
importComponents(require.context("data-room-ui/Borders", true, /\index.vue$/));
importComponents(
require.context("data-room-ui/Decorations", true, /\index.vue$/)
);
importComponents(
require.context("data-room-ui/BorderComponents", true, /\index.vue$/)
);
importComponents(
require.context("data-room-ui/Configuration", true, /\index.vue$/)
);
export default modules;

View File

@@ -0,0 +1,100 @@
export function showSize (base64url) {
let str = base64url.replace('data:image/png;base64,', '')
// 找到等号,把等号也去掉
const equalIndex = str.indexOf('=')
if (str.indexOf('=') > 0) {
str = str.substring(0, equalIndex)
}
// 原来的字符流大小,单位为字节
const strLength = base64url.length
// 计算后得到的文件流大小,单位为字节
const fileLength = parseInt(strLength - (strLength / 8) * 2)
// 由字节转换为kb
let size = ''
size = (fileLength / 1024).toFixed(2)
const sizeStr = size + '' // 转成字符串
const index = sizeStr.indexOf('.') // 获取小数点处的索引
const dou = sizeStr.substr(index + 1, 2) // 获取小数点后两位的值
if (dou === '00') {
// 判断后两位是否为00如果是则删除00
return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
}
return Number(size)
}
export function dataURLtoFile (dataurl, filename) {
// 将base64转换为文件dataurl为base64字符串filename为文件名必须带后缀名如.jpg,.png
const arr = dataurl.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}
export function dataURLtoBlob (dataurl) {
const arr = dataurl.split(',')
const _arr = arr[1].substring(0, arr[1].length - 2)
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(_arr)
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {
type: mime
})
}
export function translateBlobToBase64 (blob, callback) {
const reader = new FileReader()
reader.onload = function (e) {
callback(e.target)
}
reader.readAsDataURL(blob)
// 读取后result属性中将包含一个data:URL格式的Base64字符串用来表示所读取的文件
}
// 压缩方法
export function compressImage (base64, { width: w = 1280, height: h = 720, size = 200, quality = 1 }) {
return new Promise((resolve, reject) => {
const newImage = new Image()
newImage.src = base64
newImage.setAttribute('crossOrigin', 'Anonymous')
let imgWidth, imgHeight
newImage.onload = function () {
imgWidth = 1280
imgHeight = 720
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
if (Math.max(imgWidth, imgHeight) > w) {
if (imgWidth > imgHeight) {
canvas.width = w
canvas.height = w * imgHeight / imgWidth
} else {
canvas.height = w
canvas.width = w * imgWidth / imgHeight
}
} else {
canvas.width = imgWidth
canvas.height = imgHeight
quality = 1
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
let compressedBase64 = canvas.toDataURL('image/jpeg', quality) // 压缩语句
if (showSize(compressedBase64) > Number(size)) {
compressedBase64 = canvas.toDataURL('image/jpeg', quality - 0.2 > 0.4 ? quality - 0.2 : 0.4)
}
resolve(compressedBase64)
}
newImage.onerror = function () {
reject(new Error('Failed to load image'))
}
})
}

View File

@@ -0,0 +1,39 @@
/*
* @description: 批量导入所有组件的配置config
* @Date: 2023-03-13 10:04:58
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-05-18 10:39:42
*/
const setModules = {}; // 设置模块
const dataModules = {}; // 数据模块
function importComponentSettingConfig(files) {
files
.keys()
.filter((key) => {
return key.match(/settingConfig/);
})
.forEach((key) => {
const reg = new RegExp("(.\\/)(.*)(\\/)");
let moduleName = key.match(reg)[0].replace(/(\.\/)|(\/)/g, "");
moduleName = moduleName.replace(
moduleName[0],
moduleName[0].toLowerCase()
);
setModules[moduleName] = files(key).settingConfig;
dataModules[moduleName] = files(key).dataConfig;
});
}
importComponentSettingConfig(
require.context("data-room-ui/BasicComponents", true, /\.js$/)
);
importComponentSettingConfig(
require.context("data-room-ui/Borders", true, /\.js$/)
);
importComponentSettingConfig(
require.context("data-room-ui/Decorations", true, /\.js$/)
);
importComponentSettingConfig(
require.context("data-room-ui/Configuration", true, /\.js$/)
);
export { setModules, dataModules };

View File

@@ -0,0 +1,116 @@
/*!
* 数据源管理
*/
import Vue from 'vue'
/**
* 修改数据源
* @param params
* @param flag
* @returns {*}
*/
const add = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/datasource/add', params, flag)
/**
* 删除数据源前查询是否使用
* @param params
* @param flag
* @returns {*}
*/
const dataSourceCheck = (id='-1', flag = false) => Vue.prototype.$dataRoomAxios.post(`/datasource/deleteCheck/${id}`, {}, flag)
/**
* 修改数据源
* @param params
* @param flag
* @returns {*}
*/
const update = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/datasource/update', params, flag)
/**
* 数据源名称校验
* @param params
* @param flag
* @returns {*}
*/
const checkRepeat = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/datasource/checkRepeat', params, flag)
/**
* 数据源连接测试
* @param params
* @param flag
* @returns {*}
*/
const sourceLinkTest = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/datasource/testConnect', params, flag)
/**
* 获取数据源列表
* @param params
* @param flag
* @returns {*}
*/
const datasourcePage = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/datasource/page', params, flag)
/**
* 获取数据源列表
* @param params
* @param flag
* @returns {*}
*/
const datasourceList = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/datasource/list', params, flag)
/**
* 删除数据源
* @param id
* @param flag
* @returns {*}
*/
const sourceRemove = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.post(`/datasource/delete/${id}`, {}, flag)
/**
* 获取数据源下表列表
* @param id
* @param flag
* @returns {*}
*/
const getSourceTable = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.get(`/datasource/getTableList/${id}`, {}, flag)
/**
* 获取数据源下视图列表
* @param id
* @param flag
* @returns {*}
*/
const getSourceView = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.get(`/datasource/getViewList/${id}`, {}, flag)
/**
* 获取数据源下表字段列表
* @param sourceId
* @param tableName
* @param flag
* @returns {Promise<*>}
*/
const getTableFieldList = (sourceId = '-1', tableName = '', flag = false) => Vue.prototype.$dataRoomAxios.get(`/datasource/getFieldList/table/${sourceId}/${tableName}`, {}, flag)
/**
* 获取数据源下视图字段列表
* @param sourceId
* @param viewName
* @param flag
* @returns {Promise<*>}
*/
const getViewFieldList = (sourceId = '-1', viewName = '', flag = false) => Vue.prototype.$dataRoomAxios.get(`/datasource/getFieldList/view/${sourceId}/${viewName}`, {}, flag)
export {
add,
update,
checkRepeat,
sourceLinkTest,
datasourcePage,
datasourceList,
sourceRemove,
getSourceTable,
getSourceView,
getTableFieldList,
getViewFieldList,
dataSourceCheck
}

View File

@@ -0,0 +1,145 @@
/*!
* 数据集管理
*/
import Vue from 'vue'
/**
* 数据集分页查询
* @param params
* @param flag
* @returns {*}
*/
const datasetPage = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/dataset/page', params, flag)
/**
* 删除数据源前查询是否使用
* @param params
* @param flag
* @returns {*}
*/
const datasetCheck = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.post(`/dataset/deleteCheck/${id}`, {}, flag)
/**
* 验证节点是否可删除
* @param params
* @param flag
* @returns {*}
*/
const categoryDele = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.get(`/dataset/getCountByType/${id}`, {}, flag)
/**
* 数据集列表查询
* @param params
* @param flag
* @returns {*}
*/
const datasetList = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/dataset/list', params, flag)
/**
* 数据集名称校验
* @param params
* @param flag
* @returns {*}
*/
const nameCheckRepeat = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/dataset/checkRepeat', params, flag)
/**
* 数据集新增
* @param params
* @param flag
* @returns {*}
*/
const datasetAdd = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/dataset/add', params, flag)
/**
* 数据集修改
* @param params
* @param flag
* @returns {*}
*/
const datasetUpdate = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/dataset/update', params, flag)
/**
* 删除数据集
* @param id
* @param flag
* @returns {*}
*/
const datasetRemove = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.post(`/dataset/delete/${id}`, {}, flag)
/**
* 数据集执行
* @param params
* @param flag
* @returns {*}
*/
const datasetExecuteTest = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/dataset/execute/test', params, flag)
/**
* 获取数据集详情
* @param id
* @param flag
* @returns {*}
*/
const getDataset = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.get(`/dataset/info/${id}`, {}, flag)
/**
* 获取数据集分类
* @param r_dataset
* @param flag
* @returns {*}
*/
const getCategoryTree = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/category/queryTreeList', params, flag)
/**
* 新增分类树节点
* @param params
* @param flag
* @returns {*}
*/
const categoryAdd = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/category/add', params, flag)
/**
* 编辑分类树节点
* @param params
* @param flag
* @returns {*}
*/
const categoryUpdate = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/category/update', params, flag)
/**
* 删除分类树节点
* @param id
* @param flag
* @returns {*}
*/
const categoryRemove = (id = '-1', flag = false) => Vue.prototype.$dataRoomAxios.post(`/category/delete/${id}`, {}, flag)
/**
* 分类名称校验
* @param params
* @param flag
*/
const categoryNameRepeat = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/category/checkRepeat', params, flag)
export {
datasetPage,
datasetList,
datasetAdd,
datasetUpdate,
datasetRemove,
nameCheckRepeat,
datasetExecuteTest,
getDataset,
categoryDele,
getCategoryTree,
categoryAdd,
categoryUpdate,
categoryRemove,
datasetCheck,
categoryNameRepeat
}

View File

@@ -0,0 +1,29 @@
function stringifyObjectFunctions (obj) {
// 遍历对象属性
for (const key in obj) {
const value = obj[key]
// 如果属性值是函数类型,将函数转换为字符串
if (typeof value === 'function') {
obj[key] = `(${value.toString()})`
// 如果属性值是对象类型,则递归进行转换
} else if (typeof value === 'object' && value !== null) {
stringifyObjectFunctions(value)
}
}
return JSON.stringify(obj)
}
function stringToFunction (str) {
return JSON.parse(str, (key, value) => {
if (typeof value === 'string' && (value.includes('=>') || value.includes('function'))) {
// eslint-disable-next-line no-eval
return eval(`(${value})`)
}
return value
})
}
export {
stringifyObjectFunctions,
stringToFunction
}

View File

@@ -0,0 +1,34 @@
import Vue from 'vue';
// 创建全局唯一的 EventBus 实例
export const EventBus = new Vue();
// 创建唯一的事件处理函数
function dataInitHandler(_this, formData, bindComponents) {
bindComponents?.forEach(com => {
const maps = com.maps;
const filterList = maps?.map(param => ({
column: param.targetField,
operator: param.queryRule,
value: formData[param.sourceField],
}));
_this.$nextTick(() => {
if (_this.$refs[com.componentKey] && _this.$refs[com.componentKey].dataInit) {
_this.$refs[com.componentKey].dataInit(filterList);
}
});
});
}
// 注册事件监听器
export function dataInit(_this) {
EventBus.$on('dataInit', (formData, bindComponents) => {
// 在回调中调用处理函数并传递组件实例
dataInitHandler(_this, formData, bindComponents);
});
}
export function destroyedEvent() {
EventBus.$off('dataInit', dataInitHandler);
}

View File

@@ -0,0 +1,21 @@
function getFileUrl(url){
// 如果是空的直接返回
if (!url) {
return url
}
// 如果是http开头的直接返回
if (/^http/.test(url)) {
return url
}
// 如果没有以/开头的加上/
if (!/^\//.test(url)) {
url = `/${url}`
}
// return `${window.BS_CONFIG?.httpConfigs?.fileUrlPrefix}${url}`
return `${process.env.VUE_APP_BASE_API}/static${url}`
}
export {
getFileUrl
}

View File

@@ -0,0 +1,22 @@
const fontList = [
{
label: '默认',
value: ''
},
{
label: '时钟加粗倾斜',
value: 'ds-digitalbold_italic'
},
{
label: '时钟加粗正常',
value: 'ds-digitalbold'
},
{
label: '时钟倾斜',
value: 'ds-digitalitalic'
},
{
label: '时钟正常',
value: 'ds-digitalnormal'
}]
export default fontList

View File

@@ -0,0 +1,223 @@
/*
* @description: 得到边框组件配置
* @Date: 2023-03-16 10:49:11
*/
export default function getComponentConfig (type, classNameType) {
const className =
'com.gccloud.dataroom.core.module.chart.components.ScreenBorderChart'
switch (type) {
case 'border1':
return {
name: '边框一',
title: '边框一',
icon: null,
img: require('data-room-ui/Borders/images/border-1.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border2':
return {
name: '边框二',
title: '边框二',
icon: null,
img: require('data-room-ui/Borders/images/border-2.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border3':
return {
name: '边框三',
title: '边框三',
icon: null,
img: require('data-room-ui/Borders/images/border-3.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border4':
return {
name: '边框四',
title: '边框四',
icon: null,
img: require('data-room-ui/Borders/images/border-4.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border5':
return {
name: '边框五',
title: '边框五',
icon: null,
img: require('data-room-ui/Borders/images/border-5.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border6':
return {
name: '边框六',
title: '边框六',
icon: null,
img: require('data-room-ui/Borders/images/border-6.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border7':
return {
name: '边框七',
title: '边框七',
icon: null,
img: require('data-room-ui/Borders/images/border-7.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border8':
return {
name: '边框八',
title: '边框八',
icon: null,
img: require('data-room-ui/Borders/images/border-8.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border9':
return {
name: '边框九',
title: '边框九',
icon: null,
img: require('data-room-ui/Borders/images/border-9.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border10':
return {
name: '边框十',
title: '边框十',
icon: null,
img: require('data-room-ui/Borders/images/border-10.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border11':
return {
name: '边框十一',
title: '边框十一',
icon: null,
img: require('data-room-ui/Borders/images/border-11.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border12':
return {
name: '边框十二',
title: '边框十二',
icon: null,
img: require('data-room-ui/Borders/images/border-12.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border13':
return {
name: '边框十三',
title: '边框十三',
icon: null,
img: require('data-room-ui/Borders/images/border-13.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border14':
return {
name: '边框十四',
title: '边框十四',
icon: null,
img: require('data-room-ui/Borders/images/border-14.png'),
component: null,
className,
w: 450,
h: 320,
x: 0,
y: 0,
type
}
case 'border15':
return {
name: '边框十五',
title: '边框十五',
icon: null,
img: require('data-room-ui/Borders/images/border-15.png'),
component: null,
className,
w: 450,
h: 450,
x: 0,
y: 0,
type
}
default:
return {}
}
}

View File

@@ -0,0 +1,369 @@
import Icon from "data-room-ui/assets/images/bigScreenIcon/export";
export default function getComponentConfig(type) {
switch (type) {
case "texts":
return {
name: "文本",
title: "文本",
icon: Icon.getNameList()[0],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenTextChart",
w: 200,
h: 60,
x: 0,
y: 0,
type,
dataHandler: {}, // 数据自定义处理js脚本
};
case "numbers":
return {
name: "数字",
title: "数字",
icon: Icon.getNameList()[28],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenNumbersChart",
w: 200,
h: 60,
x: 0,
y: 0,
type,
};
case "linkChart":
return {
name: "超链接",
title: "超链接",
icon: Icon.getNameList()[15],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenLinkChart",
w: 200,
h: 60,
x: 0,
y: 0,
type,
};
case "horizontalLine":
return {
name: "水平线",
title: "水平线",
icon: Icon.getNameList()[24],
component: null,
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenBorderChart",
w: 300,
h: 40,
x: 0,
y: 0,
type,
};
case "verticalLine":
return {
name: "垂直线",
title: "垂直线",
icon: Icon.getNameList()[25],
component: null,
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenBorderChart",
w: 40,
h: 300,
x: 0,
y: 0,
type,
};
case "picture":
return {
name: "图片",
title: "图片",
icon: Icon.getNameList()[1],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenPictureChart",
// w: 280,
// h: 200,
// x: 0,
// y: 0,
type,
};
case "screenScrollBoard":
return {
name: "轮播表",
title: "轮播表",
icon: Icon.getNameList()[2],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenScrollBoardChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "screenScrollRanking":
return {
name: "排名表",
title: "排名表",
icon: Icon.getNameList()[3],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenScrollRankingChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "tables":
return {
name: "表格",
title: "表格",
icon: Icon.getNameList()[4],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenTablesChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "currentTime":
return {
name: "当前时间",
title: "当前时间",
icon: Icon.getNameList()[6],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenCurrentTimeChart",
w: 300,
h: 60,
x: 0,
y: 0,
type,
};
case "timeCountDown":
return {
name: "倒计时",
title: "倒计时",
icon: Icon.getNameList()[7],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenTimeCountDownChart",
w: 300,
h: 60,
x: 0,
y: 0,
type,
};
case "iframeChart":
return {
name: "外链",
title: "外链",
icon: Icon.getNameList()[8],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenIframeChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "digitalFlop":
return {
name: "翻牌器",
title: "翻牌器",
icon: null,
img: require("data-room-ui/BasicComponents/DigitalFlop/images/fanpaiqi.png"),
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenDigitalFlopChart",
w: 800,
h: 150,
x: 0,
y: 0,
type,
};
case "customHtml":
return {
name: "自定义HTML",
title: "自定义HTML",
icon: Icon.getNameList()[29],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenCustomHtmlChart",
w: 600,
h: 150,
x: 0,
y: 0,
type,
};
case "video":
return {
name: "播放器",
title: "播放器",
icon: Icon.getNameList()[12],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenVideoChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "input":
return {
name: "输入框",
title: "输入框",
icon: Icon.getNameList()[13],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenInputChart",
w: 450,
h: 60,
x: 0,
y: 0,
type,
};
case "button":
return {
name: "按钮",
title: "按钮",
icon: Icon.getNameList()[14],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenButtonChart",
w: 80,
h: 40,
x: 0,
y: 0,
type,
};
case "marquee":
return {
name: "跑马灯",
title: "跑马灯",
icon: Icon.getNameList()[16],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenMarqueeChart",
w: 250,
h: 150,
x: 0,
y: 0,
type,
};
case "chartTab":
return {
name: "图表Tab页",
title: "图表Tab页",
icon: Icon.getNameList()[19],
className:
"com.gccloud.dataroom.core.module.chart.components.ChartTabChart",
w: 600,
h: 400,
x: 0,
y: 0,
type,
};
case "themeSelect":
return {
name: "主题切换",
title: "主题切换",
icon: Icon.getNameList()[20],
className:
"com.gccloud.dataroom.core.module.chart.components.ThemeSelectChart",
w: 200,
h: 100,
x: 0,
y: 0,
type,
};
case "select":
return {
name: "选择器",
title: "选择器",
icon: Icon.getNameList()[21],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenSelectChart",
w: 450,
h: 60,
x: 0,
y: 0,
type,
};
case "timePicker":
return {
name: "时间选择器",
title: "时间选择器",
icon: Icon.getNameList()[22],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenTimePickerChart",
w: 200,
h: 60,
x: 0,
y: 0,
type,
};
case "dateTimePicker":
return {
name: "日期时间选择器",
title: "日期时间选择器",
icon: Icon.getNameList()[23],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenDateTimePickerChart",
w: 500,
h: 60,
x: 0,
y: 0,
type,
};
case "indicatorCard":
return {
name: "指标卡一",
title: "指标卡一",
icon: Icon.getNameList()[30],
// img: require('data-room-ui/assets/images/cardImg/card.png'),
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenIndicatorCardChart",
w: 300,
h: 114,
x: 0,
y: 0,
type,
};
case "indicatorCard2":
return {
name: "指标卡二",
title: "指标卡二",
icon: Icon.getNameList()[31],
// img: require('data-room-ui/assets/images/cardImg/card2.png'),
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenIndicatorCardChart",
w: 300,
h: 114,
x: 0,
y: 0,
type,
};
case "indexCard":
return {
name: "指标卡三",
title: "指标卡三",
icon: Icon.getNameList()[32],
// img: require('data-room-ui/assets/images/cardImg/indicard.png'),
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenIndexCardChart",
w: 300,
h: 114,
x: 0,
y: 0,
type,
};
case "indexCard2":
return {
name: "指标卡四",
title: "指标卡四",
icon: Icon.getNameList()[33],
// img: require('data-room-ui/assets/images/cardImg/indcard2.png'),
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenIndexCardChart",
w: 300,
h: 114,
x: 0,
y: 0,
type,
};
default:
return {};
}
}

View File

@@ -0,0 +1,57 @@
/*
* @description: 得到装饰组件配置
*/
import Icon from "data-room-ui/assets/images/bigScreenIcon/export";
export default function getComponentConfig(type, classNameType) {
const className =
"com.gccloud.dataroom.core.module.chart.components.ScreenConfigurationChart";
switch (type) {
case "horizontalLine2":
return {
name: "水平线2",
title: "水平线2",
// icon: Icon.getNameList()[36],
img: require("data-room-ui/Configuration/images/水平线.png"),
component: null,
className,
w: 300,
h: 20,
x: 0,
y: 0,
type,
};
case "verticalLine2":
return {
name: "垂直线2",
title: "垂直线2",
// icon: Icon.getNameList()[36],
img: require("data-room-ui/Configuration/images/垂直线.png"),
component: null,
className,
w: 20,
h: 300,
x: 0,
y: 0,
type,
};
case "warning":
return {
name: "告警灯",
title: "告警灯",
icon: Icon.getNameList()[37],
className:
"com.gccloud.dataroom.core.module.chart.components.ScreenTextChart",
w: 200,
h: 200,
x: 0,
y: 0,
type,
dataHandler: {}, // 数据自定义处理js脚本
};
default:
return {};
}
}

View File

@@ -0,0 +1,180 @@
/*
* @description: 得到装饰组件配置
*/
export default function getComponentConfig (type, classNameType) {
const className =
'com.gccloud.dataroom.core.module.chart.components.ScreenDecorationChart'
switch (type) {
case 'decoration1':
return {
name: '装饰一',
title: '装饰一',
img: require('data-room-ui/Decorations/images/01.png'),
component: null,
className,
w: 350,
h: 30,
x: 0,
y: 0,
type
}
case 'decoration2':
return {
name: '装饰三',
title: '装饰三',
img: require('data-room-ui/Decorations/images/03.png'),
component: null,
className,
w: 350,
h: 20,
x: 0,
y: 0,
type
}
case 'decoration2Reverse':
return {
name: '装饰三(旋转)',
title: '装饰三(旋转)',
img: require('data-room-ui/Decorations/images/03_reverse.png'),
component: null,
className,
w: 20,
h: 350,
x: 0,
y: 0,
type
}
case 'decoration3':
return {
name: '装饰二',
title: '装饰二',
img: require('data-room-ui/Decorations/images/02.png'),
component: null,
className,
w: 350,
h: 30,
x: 0,
y: 0,
type
}
case 'decoration4':
return {
name: '装饰四',
title: '装饰四',
img: require('data-room-ui/Decorations/images/04.png'),
component: null,
className,
w: 30,
h: 320,
x: 0,
y: 0,
type
}
case 'decoration4Reverse':
return {
name: '装饰四(旋转)',
title: '装饰四(旋转)',
img: require('data-room-ui/Decorations/images/04_reverse.png'),
component: null,
className,
w: 320,
h: 30,
x: 0,
y: 0,
type
}
case 'decoration5':
return {
name: '装饰五',
title: '装饰五',
img: require('data-room-ui/Decorations/images/05.png'),
component: null,
className,
w: 350,
h: 50,
x: 0,
y: 0,
type
}
case 'decoration6':
return {
name: '装饰六',
title: '装饰六',
img: require('data-room-ui/Decorations/images/06.png'),
component: null,
className,
w: 350,
h: 30,
x: 0,
y: 0,
type
}
case 'decoration8':
return {
name: '装饰七',
title: '装饰七',
img: require('data-room-ui/Decorations/images/07.png'),
component: null,
className,
w: 350,
h: 50,
x: 0,
y: 0,
type
}
case 'decoration8Reverse':
return {
name: '装饰七(旋转)',
title: '装饰七(旋转)',
img: require('data-room-ui/Decorations/images/07_reverse.png'),
component: null,
className,
w: 350,
h: 50,
x: 0,
y: 0,
type
}
case 'decoration9':
return {
name: '装饰八',
title: '装饰八',
img: require('data-room-ui/Decorations/images/08.png'),
component: null,
className,
w: 150,
h: 150,
x: 0,
y: 0,
type
}
case 'decoration10':
return {
name: '装饰九',
title: '装饰九',
img: require('data-room-ui/Decorations/images/09.png'),
component: null,
className,
w: 350,
h: 50,
x: 0,
y: 0,
type
}
case 'decoration11':
return {
name: '装饰十',
title: '装饰十',
img: require('data-room-ui/Decorations/images/10.png'),
component: null,
className,
w: 350,
h: 50,
x: 0,
y: 0,
type
}
default:
return {}
}
}

View File

@@ -0,0 +1,231 @@
import axios from 'axios'
import qs from 'qs'
// import _ from 'lodash'
import merge from 'lodash/merge'
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth'
/**
* 统一进行异常输出
* 如果异常只是弹框显示即可,可使用该实例
*/
const httpConfig = {
timeout: 1000 * 30,
withCredentials: true,
baseURL: process.env.VUE_APP_BASE_API,
headers: {
'Content-Type': 'application/json; charset=utf-8',
"AuthToken" : 'Bearer ' + getToken()
}
}
const httpCustom = axios.create(httpConfig)
/**
*对于出现异常时还需要做其他操作,可使用该实例
*/
const http = axios.create(httpConfig)
/**
* 封装的异常对象
* @param message
* @param code
* @constructor
*
*/
function EipException (message, code) {
this.msg = message
this.code = code
}
/**
* 请求拦截
*/
http.interceptors.request.use(config => {
return {
...config,
...merge(httpConfig, window.BS_CONFIG?.httpConfigs)
}
}, error => {
return Promise.reject(error)
})
/**
* 自定义请求拦截
*/
httpCustom.interceptors.request.use(config => {
return config
}, error => {
return Promise.reject(error)
})
/**
* 响应拦截
*/
http.interceptors.response.use(response => {
const res = response.data
// 异常拦截
// eslint-disable-next-line no-empty
if (res && res.code === 401) {
} else if (res && res.code !== 200) {
// return Promise.reject(response.data.msg)
Message({
message: response.data.msg,
type: 'error'
})
throw new EipException(response.data.msg, response.data.code)
} else {
return res
}
}, error => {
if (error.message && error.message === 'Network Error') {
Message({
message: error.message,
type: 'error'
})
return Promise.reject(error)
// eslint-disable-next-line no-empty
} else {
}
Message({
message: '服务异常',
type: 'error'
})
return Promise.reject(error)
})
/**
* 响应拦截
*/
httpCustom.interceptors.response.use(response => {
const res = response.data
return res
}, error => {
if (error.message && error.message === 'Network Error') {
return Promise.reject(error)
// eslint-disable-next-line no-empty
} else {
}
Message({
message: '服务异常',
type: 'error'
})
return Promise.reject(error)
})
/**
* get请求
* @param url
* @param params
* @returns {Promise<any>}
*/
export function get (url, params = {}, customHandlerException = false) {
// if (!url.startsWith('http')) {
// url = window.BS_CONFIG?.httpConfigs?.baseURL + url
// }
// 如果是ie浏览器要添加个时间戳解决浏览器缓存问题
if (!!window.ActiveXObject || 'ActiveXObject' in window) {
params._t = new Date().getTime()
}
const axiosInstance = customHandlerException ? httpCustom : http
return new Promise((resolve, reject) => {
axiosInstance.get(url, {
params: params,
paramsSerializer: params => {
return qs.stringify(params, { indices: false })
}
}).then(response => {
if (customHandlerException) {
resolve(response)
} else {
resolve(response.data)
}
}).catch(err => {
reject(err)
})
})
}
/**
* Post 请求
* @param url
* @param params
* @returns {Promise<any>}
*/
export function post (url, data = {}, customHandlerException = false) {
// if (!url.startsWith('http')) {
// url = window.BS_CONFIG?.httpConfigs?.baseURL + url
// }
const axiosInstance = customHandlerException ? httpCustom : http
data = JSON.stringify(data)
return new Promise((resolve, reject) => {
axiosInstance.post(url, data).then(response => {
if (customHandlerException) {
resolve(response)
} else {
resolve(response.data)
}
}).catch(err => {
reject(err)
})
})
}
/**
* download 请求
* @returns {Promise<any>}
*/
export function download (url, headers = {}, params = {}, body = {}) {
// if (!url.startsWith('http')) {
// url = window.BS_CONFIG?.httpConfigs?.baseURL + url
// }
// 如果是ie浏览器要添加个时间戳解决浏览器缓存问题
if (!!window.ActiveXObject || 'ActiveXObject' in window) {
params._t = new Date().getTime()
}
return new Promise((resolve, reject) => {
axios({
method: 'post',
url: httpConfig.baseURL+ url,
headers: httpConfig.headers,
params: params,
data: body,
withCredentials: false,
responseType: 'arraybuffer'
}).then(res => {
// IE10,11采用自带下载文件流方法
if ((!!window.ActiveXObject || 'ActiveXObject' in window) && window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(new Blob([res.data]), res.headers.filename)
return
}
const fileUrl = window.URL.createObjectURL(new Blob([res.data]))
// 创建超链接
const fileLink = document.createElement('a')
fileLink.href = fileUrl
// 设置下载文件名
let responseFileName = res.headers.filename
// 解决中文乱码
responseFileName = window.decodeURI(responseFileName)
fileLink.setAttribute('download', responseFileName)
document.body.appendChild(fileLink)
// 模拟人工点击下载超链接
fileLink.click()
// 释放资源
document.body.removeChild(fileLink)
window.URL.revokeObjectURL(fileUrl)
}).catch(e => {
const status = e?.response?.status
if (status === 404) {
Message({
message: '文件不存在或已被删除',
type: 'error'
})
return
}
Message({
message: '服务异常',
type: 'error'
})
console.error('服务异常')
})
})
}

View File

@@ -0,0 +1,143 @@
import axios from 'axios'
// import { Loading, Message } from 'element-ui'
// import _ from 'lodash'
import cloneDeep from 'lodash/cloneDeep'
export default function axiosFormatting (customConfig) {
const newCustomConfig = replaceParams(customConfig)
// 将请求头和请求参数的值转化为对象形式
const httpConfig = {
timeout: 1000 * 30,
baseURL: '',
headers: { 'Content-Type': 'application/json', ...newCustomConfig.headers }
}
// let loadingInstance = null // 加载全局的loading
const instance = axios.create(httpConfig)
/** 添加请求拦截器 **/
instance.interceptors.request.use(config => {
/**
* 在这里:可以根据业务需求可以在发送请求之前做些什么。
* config.headers['token'] = sessionStorage.getItem('token') || ''
*/
// 执行请求脚本
// https://mock.presstime.cn/mock/64bf8a00ce1b0ea640809069/test_copy_copy_copy/httpData?token=123&ss=ss
const req = { ...config, url: {} }
eval(newCustomConfig.requestScript)
for (const key in req.url) {
newCustomConfig.url = replaceUrlParam(newCustomConfig.url, key, req.url[key])
}
config = { ...config, ...req, url: newCustomConfig.url }
return config
}, error => {
// 对请求错误做些什么
return Promise.reject(error)
})
/** 添加响应拦截器 **/
instance.interceptors.response.use(response => {
const resp = response.data
// console.log('resp: ', resp);
// 执行响应脚本
if (newCustomConfig.responseScript) {
// eslint-disable-next-line no-new-func
const getResp = new Function('resp', newCustomConfig.responseScript)
const res = getResp(resp)
return Promise.resolve(res)
} else {
return Promise.resolve(resp)
}
})
const body = newCustomConfig?.body.replace(/: ,/g, ':undefined,').replace(/, }/g, ',undefined}')
/** 发送请求 **/
return new Promise((resolve, reject) => {
instance({
method: newCustomConfig.method,
url: newCustomConfig.url,
params: newCustomConfig.params,
// params 序列化操作
paramsSerializer: (params) => {
return Object.keys(params)
.map(key => {
if (Array.isArray(params[key])) {
return params[key].map(value => `${key}=${value}`).join('&')
} else {
return `${key}=${params[key]}`
}
})
.join('&')
},
data: newCustomConfig.method === 'post' ? body : undefined
}).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
// 动态替换url后面参数的值
function replaceUrlParam (url, paramName, paramValue) {
const regex = new RegExp(`([?&])${paramName}=.*?(&|$)`, 'i')
const separator = url.indexOf('?') !== -1 ? '&' : '?'
if (url.match(regex)) {
return url.replace(regex, `$1${paramName}=${paramValue}$2`)
} else {
return `${url}${separator}${paramName}=${paramValue}`
}
}
// 将参数的值替换掉其他配置中对应属性的值
function replaceParams (customConfig) {
const newConfig = cloneDeep(customConfig)
newConfig.url = evalStrFunc(newConfig.paramsList, newConfig.url)
newConfig.headers = evalArrFunc(newConfig.paramsList, newConfig.headers)
newConfig.params = evalArrFunc(newConfig.paramsList, newConfig.params)
newConfig.body = evalStrFunc(newConfig.paramsList, newConfig.body)
return newConfig
}
function evalStrFunc (paramsList, string) {
// 取name作为变量名, value作为变量值 { name: '站三', token: '123'}
const params = paramsList.reduce((acc, cur) => {
acc[cur.name] = cur.value
return acc
}, {})
// 将url中 ${xxx} 替换成 ${params.xxx}
const str = string.replace(/\$\{(\w+)\}/g, (match, p1) => {
return '${params.' + p1 + '}'
})
const transformStr = ''
// 将字符串中的${}替换为变量, 使用eval执行
eval('transformStr = `' + str + '`')
return transformStr
}
function evalArrFunc (paramsList, arr) {
// 取name作为变量名, value作为变量值 { name: '站三', token: '123'}
const params = paramsList.reduce((acc, cur) => {
acc[cur.name] = cur.value
return acc
}, {})
// 取name作为变量名, value作为变量值 { _name: '${name}', _token: '${token}'}
const paramsListObj = arr.reduce((acc, cur) => {
if (acc[cur.key]) {
if (Array.isArray(acc[cur.key])) {
acc[cur.key].push(cur.value)
} else {
acc[cur.key] = [acc[cur.key], cur.value]
}
} else {
acc[cur.key] = cur.value
}
return acc
}, {})
// 转成字符串
const paramsListStr = JSON.stringify(paramsListObj)
// 将url中 ${xxx} 替换成 ${params.xxx}
const str = paramsListStr.replace(/\$\{(\w+)\}/g, (match, p1) => {
return '${params.' + p1 + '}'
})
const transformStr = ''
// 将字符串中的${}替换为变量, 使用eval执行
eval('transformStr = `' + str + '`')
const obj = JSON.parse(transformStr)
return obj
}

View File

@@ -0,0 +1,57 @@
// import _ from 'lodash'
import upperFirst from 'lodash/upperFirst'
export const randomString = e => {
e = e || 32
const t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'
const a = t.length
let n = ''
for (let i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a))
return n
}
export const resolveComponentType = type => {
return `${upperFirst(type)}`
}
export function deepCompare (obj1, obj2, excludeKeys = []) {
// eslint-disable-next-line eqeqeq
if (obj1 == obj2) {
return false
}
// 如果两个对象中的一个是基本类型,则它们不相等
if (typeof obj1 !== 'object' || obj1 === null ||
typeof obj2 !== 'object' || obj2 === null) {
return true
}
// 如果两个对象的类型不相同,则它们不相等
if (obj1.constructor !== obj2.constructor) {
return true
}
// 递归地比较对象的属性
for (const prop in obj1) {
// 如果prop是要排除的key则跳过
if (excludeKeys.includes(prop)) {
continue
}
if (obj1.hasOwnProperty(prop)) {
if (!obj2.hasOwnProperty(prop)) {
return true
}
if (deepCompare(obj1[prop], obj2[prop])) {
return true
}
}
}
// 检查 obj2 中是否有 obj1 没有的属性
for (const prop in obj2) {
if (obj2.hasOwnProperty(prop) && !obj1.hasOwnProperty(prop)) {
return true
}
}
// 如果上面的检查都通过,则对象是相等的
return false
}

View File

@@ -0,0 +1,23 @@
// 自定义序列化方法解决JSON.stringify方法忽略函数属性的问题
export function customSerialize(obj) {
// 将对象属性和函数转换为字符串形式
const serializedObj = JSON.stringify(obj, function (key, value) {
if (typeof value === "function") {
return value.toString(); // 将函数转换为字符串
}
return value; // 保持其他属性不变
});
return serializedObj;
}
// 自定义反序列化方法
export function customDeserialize(serializedObj) {
const parsedObject = JSON.parse(serializedObj, function (key, value) {
if (typeof value === "string" && value.indexOf("function") === 0) {
// 将字符串还原为函数
return new Function("return " + value)();
}
return value; // 保持其他属性不变
});
return parsedObject;
}

View File

@@ -0,0 +1,86 @@
/*!
* 地图数据管理
*/
import Vue from 'vue'
/**
* 获取地图列表
* @param params
* @param flag
* @returns {*}
*/
const mapList = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/bigScreen/map/list', params, flag)
/**
* 新增地图
* @param params
* @param flag
* @returns {*}
*/
const mapAdd = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/add', params, flag)
/**
* 更新地图
* @param params
* @param flag
* @returns {*}
*/
const mapUpdate = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/update', params, flag)
/**
* 删除地图
* @param id
*/
const mapDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/delete/${id}`)
/**
* 级联删除地图
* @param id
*/
const mapCascadeDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/cascadingDelete/${id}`)
/**
* 根据父编码解析父级json中的子级
* @param code
*/
const getMapChildFromGeoJson = (code = '-1') => Vue.prototype.$dataRoomAxios.get(`/bigScreen/map/getMapChildFromGeoJson/${code}`)
/**
* 上传地图json
* @param params
* @param flag
*/
const uploadGeoJson = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/upload', params, flag)
/**
* 编码重复校验
* @param params
* @param flag
*/
const repeatCheck = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/repeat/code', params, flag)
/**
* 名称重复校验
* @param params
* @param flag
*/
const nameRepeatCheck = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/repeat/name', params, flag)
/**
* 根据父编码解析父级json中的子级
* @param id
*/
const mapInfo = (id = '-1') => Vue.prototype.$dataRoomAxios.get(`/bigScreen/map/info/${id}`)
export {
mapList,
mapAdd,
mapUpdate,
mapDelete,
mapCascadeDelete,
getMapChildFromGeoJson,
uploadGeoJson,
repeatCheck,
nameRepeatCheck,
mapInfo
}

View File

@@ -0,0 +1,68 @@
// mqttClient.js
import mqtt from 'mqtt';
class MqttClient {
constructor(brokerUrl, options) {
this.brokerUrl = brokerUrl || '';
this.clientId = options.clientId || "SW-VIEWS" + new Date().getTime();
this.username = options.username || 'admin';
this.password = options.password || '123456';
this.client = null;
}
// 连接 MQTT broker
connect() {
this.client = mqtt.connect(this.brokerUrl, {
clientId: this.clientId,
username: this.username,
password: this.password
});
this.client.on('connect', () => {
console.log('mqtt 已经连接成功');
});
this.client.on('reconnect', () => {
console.log("mqtt reconnect");
});
this.client.on('offline', () => {
console.log("mqtt offline");
});
this.client.on('error', (error) => {
console.log("mqtt error");
console.log(error);
});
}
// 订阅某个主题
subscribe(topic, callback) {
if (this.client) {
this.client.subscribe(topic, (err) => {
if (!err) {
console.log(`mqtt 订阅主题 ${topic} 成功`);
} else {
console.log(`mqtt 订阅主题 ${topic} 失败`);
}
});
this.client.on('message', (topic, message) => {
console.log(`mqtt 收到来自 ${topic} 的消息`);
callback(topic, message);
});
} else {
console.error('MQTT client is not connected');
}
}
// 断开连接
disconnect() {
if (this.client) {
this.client.end();
console.log('mqtt 连接已断开');
}
}
}
export default MqttClient;

View File

@@ -0,0 +1,198 @@
/**
* 对象属性合并,与 Object.assign 语法不同
* @param target
* @param source
* @returns {{}}
*/
function configDeepMerge (target, source) {
const merged = {}
for (const each in source) {
if (target.hasOwnProperty(each) && source.hasOwnProperty(each)) {
if (
typeof target[each] === 'object' &&
typeof source[each] === 'object'
) {
merged[each] = configDeepMerge(target[each], source[each])
} else {
merged[each] = source[each]
}
} else if (source.hasOwnProperty(each)) {
merged[each] = source[each]
}
}
for (const eachTarget in target) {
if (!(eachTarget in source) && target.hasOwnProperty(eachTarget)) {
merged[eachTarget] = target[eachTarget]
}
}
return merged
}
// 自动注册路由
function registerRouters (config, router) {
// 没有router对象不注册路由
if (!router) {
return
}
const routers = [
// 页面管理
{
path: config?.routers?.pageManagementUrl || '/management',
redirect: config?.routers?.pageListUrl || '/big-screen-list',
component: () => import('data-room-ui/Layout/BigScreenHomeLayout'),
children: [
{
path: config?.routers?.pageListUrl || '/big-screen-list',
name: 'BigScreenList',
component: () =>
require.ensure([], () => require('data-room-ui/BigScreenMag')),
meta: {
title: '大屏管理'
}
},
{
path: config?.routers?.templateListUrl || '/big-screen-template',
name: 'BigScreenTemplate',
component: () =>
require.ensure([], () => require('data-room-ui/BigScreenTempMag')),
meta: {
title: '模版管理'
}
},
{
path: config?.routers?.dataSourceUrl || '/big-screen-dataSource',
component: () => import('data-room-ui/DataSourceManagement'),
meta: {
title: '数据源管理'
}
},
{
path: config?.routers?.dataSetUrl || '/big-screen-dataSet',
component: () => import('data-room-ui/DataSetManagement'),
meta: {
title: '数据集管理'
}
},
{
path: config?.routers?.mapData || '/big-screen-map-data',
component: () => import('data-room-ui/MapDataManagement'),
meta: {
title: '地图数据管理'
}
},
{
path: config?.routers?.SourceUrl || '/big-screen-source',
component: () => import('data-room-ui/SourceManagement'),
meta: {
title: '资源库'
}
},
{
path: config?.routers?.componentUrl || '/big-screen-components',
component: () => import('data-room-ui/BigScreenComponentMag'),
meta: {
title: '资源管理'
}
}
]
},
{
path: config?.routers?.designUrl || '/big-screen/design',
name: 'BigScreenDesign',
component: () =>
require.ensure([], () => require('data-room-ui/BigScreenDesign'))
},
{
path: config?.routers?.previewUrl || '/big-screen/preview',
name: 'BigScreenPreview',
component: () =>
require.ensure([], () => require('data-room-ui/BigScreenRun'))
},
{
path: '/dataRoom-redirect',
name: 'Redirect',
component: () => import('data-room-ui/Layout/Redirect/index.vue')
},
{
path: config?.routers?.bizComponentDesignUrl || '/big-screen-biz-component-design',
component: () => import('data-room-ui/BizComponent'),
meta: {
title: '业务组件'
}
},
{
path: config?.routers?.bizComponentPreviewUrl || '/big-screen-biz-component-preview',
component: () => import('data-room-ui/BizComponent/Preview.vue'),
meta: {
title: '业务组件预览'
}
}
]
// 如果router有addRoutes方法
if (router?.addRoutes) {
router?.addRoutes(routers)
} else {
// eslint-disable-next-line no-unused-expressions
routers?.forEach((route) => {
// eslint-disable-next-line no-unused-expressions
router?.addRoute(route)
})
}
}
// 注册配置
function registerTheme (config) {
const defaultTheme = {
'--db-background-appmain': '#f0f2f5',
'--bs-el-color-primary': '#409EFF', // elment-ui主题色激活
'--bs-background-1': '#151a26', // 整体背景色
'--bs-background-2': '#232832', // 布局背景色
'--bs-el-background-1': '#151A26', // 组件背景色,输入框...
'--bs-el-background-2': '#35393F', // 组件背景色,按钮、分页、加载...
'--bs-el-background-3': '#303640', // 组件背景色表格头部、下拉框hover...
'--bs-el-title': '#ffffff', // 标题字体颜色
'--bs-el-text': '#ffffff', // 一般字体颜色
'--bs-el-border': 'transparent', // 边框颜色
'--bs-el-color-primary-active': '64, 158, 255'
}
const customTheme= {
'--db-background-appmain': '#151a26',
'--db-background-header': '#007aff', // 头部颜色
'--db-background-leftPanel': '#eef2f7', // 左侧组件栏背景色
'--db-background-1': '#fff', // 整体背景色
'--db-background-2': '#fff', // 布局背景色
'--db-background-3': '#f6f7fb', // 列表背景色
'--db-el-background-1': '#fff', // 组件背景色,输入框...
'--db-el-background-2': '#F5F7FA', // 组件背景色,按钮、分页、加载...
'--db-el-background-3': '#F5F7FA', // 组件背景色表格头部、下拉框hover...
'--db-el-title': '#76838f', // 标题字体颜色
'--db-el-text': '#36474f', // 一般字体颜色
'--db-el-color-primary': '#409EFF', // elment-ui主题色激活
'--db-el-border': 'transparent' // 边框颜色
}
const mergedTheme = { ...defaultTheme, ...config?.customTheme }
const style = document.createElement('style')
style.type = 'text/css'
let themeStr = ''
for (const key in mergedTheme) {
themeStr += `${key}:${mergedTheme[key]};`
}
// 给body添加class bs-body-theme-wrap
document.body.classList.add('bs-body-theme-wrap')
style.innerHTML = `.bs-body-theme-wrap {${themeStr}}`
document.getElementsByTagName('head')[0].appendChild(style)
}
// 注册配置
export default function (config, router) {
window.BS_CONFIG = {}
window.BS_CONFIG = configDeepMerge(window.BS_CONFIG, config)
if (!config?.httpConfigs?.fileUrlPrefix) {
// 如果没有配置文件访问前缀使用baseURL加上/static作为文件前缀
window.BS_CONFIG.httpConfigs.fileUrlPrefix = window.BS_CONFIG.httpConfigs.baseURL + '/static'
}
// 注册路由
registerRouters(config, router)
// 注册自定义主题
registerTheme(config)
}

View File

@@ -0,0 +1,42 @@
/*
* @description: 批量导入所有右侧配置组件
* @Date: 2023-03-13 10:04:58
* @Author: xing.heng
* @LastEditors: xing.heng
* @LastEditTime: 2023-05-17 13:18:36
*/
const modules = {};
const replaceName = {};
// 排除的组件
const excludeCommponents = []; // 有的话就添加进去
function importComponentSetting(files) {
files
.keys()
.filter((key) => {
return key.match(/setting/);
})
.forEach((key) => {
// 正则,取到./和/之间的字符串
const reg = new RegExp("(.\\/)(.*)(\\/)");
let moduleName = key.match(reg)[0].replace(/(\.\/)|(\/)/g, "");
// 替换组件名称
if (replaceName[moduleName]) {
moduleName = replaceName[moduleName];
}
// 去掉排除的组件
if (!excludeCommponents.includes(moduleName)) {
modules[moduleName] = files(key).default;
}
});
}
importComponentSetting(
require.context("data-room-ui/BasicComponents", true, /\.vue$/)
);
importComponentSetting(require.context("data-room-ui/Borders", true, /\.vue$/));
importComponentSetting(
require.context("data-room-ui/Decorations", true, /\.vue$/)
);
importComponentSetting(
require.context("data-room-ui/Configuration", true, /\.vue$/)
);
export default modules;

View File

@@ -0,0 +1,64 @@
// 设置表格高度
const doResize = async (el, binding, vnode) => {
// 获取表格Dom对象
const {
componentInstance: $table
} = await vnode
// 获取调用传递过来的数据
const {
value
} = binding
if (!$table.height) {
throw new Error("使用v-table指令,el-table需设置height,例如: height='0'")
}
// 获取距底部距离(用于展示页码等信息)
const bottomOffset = value || 100
if (!$table) return
// 计算列表高度并设置
let height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
height = height + 32 // 针对没有底部适配
$table.layout.setHeight(height)
$table.doLayout()
}
// 节流函数
const throttle = (fn) => {
let flag = null
return function () {
if (!flag) {
flag = setTimeout(() => {
fn()
flag = null
}
, 500)
}
}
}
export default {
// 初始化设置
bind (el, binding, vnode) {
// 设置resize监听方法
el.resizeListener = async () => {
await doResize(el, binding, vnode)
}
// 绑定监听方法
window.addEventListener('resize', throttle(el.resizeListener))
},
// 绑定默认高度
async inserted (el, binding, vnode) {
await doResize(el, binding, vnode)
},
// 更新数据时延时绑定高度
async componentUpdated (el, binding, vnode) {
await setTimeout(() => {
doResize(el, binding, vnode)
}, 500)
},
// 销毁时设置
unbind (el) {
// 移除resize监听
window.removeEventListener('resize', throttle(el.resizeListener))
}
}

View File

@@ -0,0 +1,121 @@
/**
* @Description: 主题设置格式化
* @author liu.shiyi
* @date 2023/8/17 14:47
*/
// 将组件中的setting里面的主题设置颜色存放到theme里面
export function settingToTheme (config, type) {
// 考虑与上一次保存过的主题进行合并
// 排除掉主题非暗黑非明亮的情况
if (['dark', 'light'].includes(type)) {
const theme = { dark: { ...config?.theme?.dark }, light: { ...config?.theme?.light } }
// 根据组件的type来判断主题的转化方式
// 如果是g2组件或者远程组件
if (['customComponent', 'remoteComponent', 'echartsComponent'].includes(config.type)) {
config.setting.forEach((setItem) => {
if (['gradual', 'colorPicker', 'colorSelect'].includes(setItem.type)) {
theme[type][setItem.field] = setItem.value
}
})
} else {
// 如果是普通组件
if (config.customize && Object.keys(config.customize).length) {
// customize属性存在层级
for (const item in config.customize) {
const value = config.customize[item]
// 如果包含二级属性
if (value && typeof value === 'object' && Object.keys(value).length) {
// 对于二级属性
for (const subKey in value) {
if (subKey.includes('color') || subKey.includes('Color') || subKey.includes('BGC')) {
theme[type][`${item}_${subKey}`] = value[subKey]
}
}
} else {
// 如果只有一级属性
if (item.includes('color') || item.includes('Color') || item.includes('BGC')) {
theme[type][item] = config.customize[item]
}
}
}
}
}
return theme
} else {
return config?.theme || { dark: {}, light: {} }
}
}
// 将保存的theme主题设置颜色存放到chartList
export function themeToSetting (chartList, type) {
let modifiedChartList = chartList
let finalChartList = chartList
// 排除掉主题非暗黑非明亮的情况
if (['dark', 'light'].includes(type)) {
modifiedChartList = chartList.map((item) => {
// 使用 map 方法遍历 chartList 数组并执行操作chartThemeToSetting
return chartThemeToSetting(item, type)
})
finalChartList = modifiedChartList.map((item) => {
// 如果当前项的 type 为 'chartTab',遍历其 tabList 数组并执行操作chartThemeToSetting
if (item.type === 'chartTab' && Array.isArray(item.customize.tabList) && item.customize.tabList.length) {
const modifiedChildren = item.customize.tabList.map((child) => {
return { ...child, chart: chartThemeToSetting(child.chart, type) }
})
return { ...item, customize: { ...item.customize, tabList: modifiedChildren } }
}
return item
})
}
return finalChartList
}
// 对单个组件进行主题设置从theme到Setting
function chartThemeToSetting (chart, type) {
if (chart.option && chart.option.theme) {
chart.option.theme = type === 'dark' ? 'transparent' : 'light'
}
if (chart.theme && chart.theme[type]) {
// 如果是g2组件或者远程组件
if (['customComponent', 'remoteComponent', 'echartsComponent'].includes(chart.type)) {
for (const item of chart.setting) {
// 检查 obj 中是否存在与 item.field 相对应的属性
if (Object.prototype.hasOwnProperty.call(chart.theme[type], item.field)) {
// 更新 setting 中对应项的 value 值为 theme 中的属性值
item.value = chart.theme[type][item.field]
}
}
} else {
// 如果是普通组件
if (chart.customize && Object.keys(chart.customize).length) {
for (const key in chart.theme[type]) {
const value = chart.theme[type][key]
// 如果对应的是二级属性
if (key.includes('_')) {
const [propertyName, subPropertyName] = key.split('_')
if (!chart.customize[propertyName]) {
chart.customize[propertyName] = {}
} else {
chart.customize[propertyName][subPropertyName] = value
}
} else {
// 对应的一级属性
if (Object.prototype.hasOwnProperty.call(chart.customize, key)) {
// 更新 customize 中对应项的值为 theme 中的属性值
chart.customize[key] = chart.theme[type][key]
}
}
}
for (const item in chart.customize) {
// 检查 obj 中是否存在与 customize 相对应的属性
if (Object.prototype.hasOwnProperty.call(chart.theme[type], item)) {
// 更新 customize 中对应项的值为 theme 中的属性值
chart.customize[item] = chart.theme[type][item]
}
}
}
}
}
return chart
}

View File

@@ -0,0 +1,13 @@
export default function updateTheme (data) {
const querySelectorName = data === false ? false : !data ? '.el-button--primary' : data
if (querySelectorName) {
window.requestAnimationFrame(() => {
const primaryButton = document.querySelector(querySelectorName)
if (primaryButton) {
const backgroundColor = window.getComputedStyle(primaryButton).getPropertyValue('background-color')
const element = document.querySelector('.bs-body-theme-wrap')
element.style.setProperty('--bs-el-color-primary', backgroundColor)
}
})
}
}

View File

@@ -0,0 +1,8 @@
// 是否是火狐浏览器
export const isFirefox = () => {
const userAgent = navigator.userAgent
if (userAgent.indexOf('Firefox') > -1) {
return true
}
return false
}

View File

@@ -0,0 +1,35 @@
/**
* @description 文字转语音方法
* @public
* @param { text, rate, lang, volume, pitch } object
* @param text 要合成的文字内容,字符串
* @param rate 读取文字的语速 0.1~10 正常1
* @param lang 读取文字时的语言
* @param volume 读取时声音的音量 0~1 正常1
* @param pitch 读取时声音的音高 0~2 正常1
* @returns SpeechSynthesisUtterance
*/
export default function speak ({ text, speechRate, lang, volume, pitch }, endEvent, startEvent) {
if (!window.SpeechSynthesisUtterance) {
console.warn('当前浏览器不支持文字转语音服务')
return
}
if (!text) {
return
}
const speechUtterance = new SpeechSynthesisUtterance()
speechUtterance.text = text
speechUtterance.rate = speechRate || 1
speechUtterance.lang = lang || 'zh-CN'
speechUtterance.volume = volume || 1
speechUtterance.pitch = pitch || 1
speechUtterance.onend = function () {
endEvent && endEvent()
}
speechUtterance.onstart = function () {
startEvent && startEvent()
}
speechSynthesis.speak(speechUtterance)
}