初始化
This commit is contained in:
250
frontend/packages/js/mixins/chartContextMenu.js
Normal file
250
frontend/packages/js/mixins/chartContextMenu.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
53
frontend/packages/js/mixins/chartSettingMixins.js
Normal file
53
frontend/packages/js/mixins/chartSettingMixins.js
Normal 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 }
|
||||
440
frontend/packages/js/mixins/commonMixins.js
Normal file
440
frontend/packages/js/mixins/commonMixins.js
Normal 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();
|
||||
},
|
||||
}
|
||||
160
frontend/packages/js/mixins/dataVMixins.js
Normal file
160
frontend/packages/js/mixins/dataVMixins.js
Normal 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 }
|
||||
152
frontend/packages/js/mixins/datasetMixin.js
Normal file
152
frontend/packages/js/mixins/datasetMixin.js
Normal 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 }
|
||||
85
frontend/packages/js/mixins/linkageMixins.js
Normal file
85
frontend/packages/js/mixins/linkageMixins.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
145
frontend/packages/js/mixins/multipleSelectMixin.js
Normal file
145
frontend/packages/js/mixins/multipleSelectMixin.js
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
frontend/packages/js/mixins/page.js
Normal file
136
frontend/packages/js/mixins/page.js
Normal 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 }
|
||||
13
frontend/packages/js/mixins/paramsMixins.js
Normal file
13
frontend/packages/js/mixins/paramsMixins.js
Normal 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
|
||||
})
|
||||
}
|
||||
}
|
||||
28
frontend/packages/js/mixins/refreshBorder.js
Normal file
28
frontend/packages/js/mixins/refreshBorder.js
Normal 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 }
|
||||
49
frontend/packages/js/mixins/refreshComponent.js
Normal file
49
frontend/packages/js/mixins/refreshComponent.js
Normal 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 }
|
||||
Reference in New Issue
Block a user