You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

573 lines
16 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="page-top-setting-wrap">
<div class="logo-wrap item-wrap">
<img
class="menu-img"
src="../BigScreenDesign/images/app.png"
alt="返回"
@click="backManagement"
>
<span class="logo-text name-span">{{ pageInfo.name }}</span>
</div>
<div class="head-btn-group">
<el-tooltip
v-for="(mode,index) in alignList"
:key="mode.value"
popper-class="bs-el-tooltip-dark"
effect="dark"
:content="mode.label"
placement="top"
>
<CusBtn
class="align-btn"
@click="setAlign(mode.value)"
>
<icon-svg
:name="iconList[index]"
/>
</CusBtn>
</el-tooltip>
<CusBtn
:loading="saveAndPreviewLoading"
@click.native="designAssign()"
>
设计分工
</CusBtn>
<CusBtn
@click.native="showHostory"
>
历史操作
</CusBtn>
<CusBtn
:disabled="undoDisabled"
@click.native="undo(true)"
>
<i class="iconfont-bigscreen icon-jiantouqianjin icon-reverse" />
</CusBtn>
<CusBtn
:disabled="redoDisabled"
@click.native="undo(false)"
>
<i class="iconfont-bigscreen icon-jiantouqianjin" />
</CusBtn>
<CusBtn
:loading="saveAndPreviewLoading"
@click.native="createdImg()"
>
生成图片
</CusBtn>
<CusBtn
:loading="saveAndPreviewLoading"
@click.native="execRun()"
>
预览
</CusBtn>
<CusBtn
:loading="saveLoading"
@click="save('saveLoading')"
>
保存
</CusBtn>
<CusBtn @click="empty">
清空
</CusBtn>
<CusBtn @click="showPageInfo">
设置
</CusBtn>
<CusBtn @click="updateRightVisiable">
<i
class="iconfont-bigscreen"
:class="rightFold ? 'icon-zhankaicaidan' : 'icon-shouqicaidan'"
/>
</CusBtn>
</div>
<ChooseTemplateDialog
ref="ChooseTemplateDialog"
:has-create="false"
:page-info="pageInfo"
@replaceItByTemplate="replaceItByTemplate"
/>
<AssignDialog ref="AssignDialog" />
<HistoryList ref="HistoryList" />
</div>
</template>
<script>
import { toJpeg, toPng } from 'html-to-image'
import { mapMutations, mapActions, mapState } from 'vuex'
import { saveScreen } from 'data-room-ui/js/api/bigScreenApi'
import ChooseTemplateDialog from 'data-room-ui/BigScreenManagement/ChooseTemplateDialog.vue'
import _ from 'lodash'
import { stringifyObjectFunctions } from 'data-room-ui/js/utils/evalFunctions'
import AssignDialog from 'data-room-ui/BigScreenDesign/AssignDialog/index.vue'
import HistoryList from 'data-room-ui/BigScreenDesign/HistoryList/index.vue'
import CusBtn from './BtnLoading'
import icons from 'data-room-ui/assets/images/alignIcon/export'
import IconSvg from 'data-room-ui/SvgIcon'
import {
showSize,
dataURLtoBlob,
translateBlobToBase64
} from 'data-room-ui/js/utils/compressImg'
import * as imageConversion from 'image-conversion'
export default {
name: 'PageTopSetting',
components: {
IconSvg,
ChooseTemplateDialog,
AssignDialog,
CusBtn,
HistoryList
},
props: {
code: {
type: String,
default: ''
},
rightFold: {
type: Boolean,
default: false
}
},
data () {
return {
iconList: icons.getNameList(),
alignList: [
{
label: '左侧对齐',
value: 'left'
},
{
label: '居中对齐',
value: 'center'
},
{
label: '右侧对齐',
value: 'right'
},
{
label: '顶部对齐',
value: 'top'
},
{
label: '中部对齐',
value: 'middle'
},
{
label: '底部对齐',
value: 'bottom'
},
{
label: '水平均分',
value: 'levelAround'
},
{
label: '垂直均分',
value: 'verticalAround'
}
],
appInfo: '',
saveLoading: false,
createdImgLoading: false,
saveAndPreviewLoading: false
}
},
computed: {
...mapState({
pageInfo: (state) => state.bigScreen.pageInfo,
timelineStore: (state) => state.bigScreen.timelineStore,
currentTimeLine: (state) => state.bigScreen.currentTimeLine,
activeCodes: state => state.bigScreen.activeCodes
}),
pageCode () {
return this.$route.query.code || this.code
},
undoDisabled () {
return Boolean(this.currentTimeLine <= 1)
},
redoDisabled () {
return Boolean(
!this.timelineStore?.length ||
(
this.currentTimeLine &&
this.currentTimeLine === this.timelineStore?.length
)
)
}
},
methods: {
...mapActions({
initLayout: 'bigScreen/initLayout'
}),
...mapMutations({
changeActiveCode: 'bigScreen/changeActiveCode',
changeActiveItem: 'bigScreen/changeActiveItem',
changePageInfo: 'bigScreen/changePageInfo',
undoTimeLine: 'bigScreen/undoTimeLine',
saveTimeLine: 'bigScreen/saveTimeLine'
}),
setAlign (command) {
const pageInfo = _.cloneDeep(this.pageInfo)
// 获取所有选中的组件
let activeChartList = pageInfo.chartList.filter((chart) => {
return this.activeCodes.some(code => (code === chart.code))
})
// 找到选中组件内的xy最大最小值
const maxXW = Math.max.apply(Math, activeChartList.map(item => { return item.x + item.w }))
let maxX = Math.max.apply(Math, activeChartList.map(item => { return item.x }))
const minX = Math.min.apply(Math, activeChartList.map(item => { return item.x }))
const maxYH = Math.max.apply(Math, activeChartList.map(item => { return item.y + item.h }))
const maxY = Math.max.apply(Math, activeChartList.map(item => { return item.y }))
const minY = Math.min.apply(Math, activeChartList.map(item => { return item.y }))
const centerW = maxXW - minX
const centerH = maxY - minY
switch (command) {
case 'left':
activeChartList.forEach((chart) => {
chart.x = minX
})
break
case 'center':
// eslint-disable-next-line no-case-declarations
activeChartList.forEach((chart) => {
chart.x = (centerW - chart.w) / 2 + minX
})
break
case 'right':
activeChartList.forEach((chart) => {
chart.x = maxXW - chart.w
})
break
case 'top':
activeChartList.forEach((chart) => {
chart.y = minY
})
break
case 'middle':
activeChartList.forEach((chart) => {
chart.y = (centerH - chart.h) / 2 + minY
})
break
case 'bottom':
activeChartList.forEach((chart) => {
chart.y = maxYH - chart.h
})
break
case 'levelAround':
// 先让数组根据x的属性进行排序
activeChartList = activeChartList.sort(this.compare('x'))
// eslint-disable-next-line no-case-declarations
const minXW = activeChartList[0].x + activeChartList[0].w
maxX = Math.max.apply(Math, activeChartList.map(item => { return item.x }))
// 中间总的宽度
// eslint-disable-next-line no-case-declarations
let totalW = 0
for (let i = 1; i < activeChartList.length - 1; i++) {
totalW = totalW + activeChartList[i].w
}
// 中间剩余的空格
// eslint-disable-next-line no-case-declarations
const padding = (maxX - minXW - totalW) / (activeChartList.length - 1)
// eslint-disable-next-line no-case-declarations
let useW = 0
for (let i = 1; i < activeChartList.length - 1; i++) {
activeChartList[i].x = minXW + padding * i + useW
useW = useW + activeChartList[i].w
}
break
case 'verticalAround':
// 先让数组根据y的属性进行排序
activeChartList = activeChartList.sort(this.compare('y'))
// eslint-disable-next-line no-case-declarations
const minYH = activeChartList[0].y + activeChartList[0].h
// eslint-disable-next-line no-case-declarations
let totalH = 0
for (let i = 1; i < activeChartList.length - 1; i++) {
totalH = totalH + activeChartList[i].h
}
// eslint-disable-next-line no-case-declarations
const paddingBottom = (maxY - minYH - totalH) / (activeChartList.length - 1)
// eslint-disable-next-line no-case-declarations
let useH = 0
for (let i = 1; i < activeChartList.length - 1; i++) {
activeChartList[i].y = minYH + paddingBottom * i + useH
useH = useH + activeChartList[i].h
}
break
}
pageInfo.chartList = [...pageInfo.chartList, ...activeChartList]
pageInfo.chartList = _.uniqBy(pageInfo.chartList, 'code')
this.changePageInfo(pageInfo)
},
compare (property) {
return function (obj1, obj2) {
const value1 = obj1[property]
const value2 = obj2[property]
return value1 - value2 // 升序
}
},
backManagement () {
this.$router.push({ path: this.pageInfo.type === 'component' ? (window.BS_CONFIG?.routers?.componentUrl || '/big-screen-components') : (window.BS_CONFIG?.routers?.pageManagementUrl || '/home') })
const data = { componentsManagementType: 'component' }
this.$router.app.$options.globalData = data // 将数据存储在全局变量中
},
undo (isUndo) {
this.undoTimeLine(isUndo)
},
// 清空
empty () {
this.changeActiveCode('')
this.$emit('empty')
},
// 预览
async execRun () {
this.save('saveAndPreviewLoading').then((res) => {
this.preview()
})
},
// 预览
preview () {
const { href } = this.$router.resolve({
path: window.BS_CONFIG?.routers?.previewUrl || '/big-screen/preview',
query: {
code: this.pageCode
}
})
window.open(href, '_blank')
},
// 保存
save (loadingType = 'saveLoading', hasPageTemplateId = false) {
const pageInfo = _.cloneDeep(this.handleSaveData())
// 保存页面
this[loadingType] = true
return new Promise((resolve, reject) => {
if (!hasPageTemplateId) {
delete pageInfo.pageTemplateId
}
const node = document.querySelector('.render-theme-wrap')
toJpeg(node, { quality: 0.2 })
.then((dataUrl) => {
const that = this
if (showSize(dataUrl) > 200) {
const url = dataURLtoBlob(dataUrl)
// 压缩到500KB,这里的500就是要压缩的大小,可自定义
imageConversion
.compressAccurately(url, {
size: 200, // 图片大小压缩到100kb
width: 1280, // 宽度压缩到1280
height: 720 // 高度压缩到720
})
.then((res) => {
translateBlobToBase64(res, function (e) {
pageInfo.coverPicture = e.result
saveScreen(pageInfo)
.then((res) => {
that.$message.success('保存成功')
resolve(res)
})
.finally(() => {
that[loadingType] = false
})
})
})
} else {
pageInfo.coverPicture = dataUrl
saveScreen(pageInfo)
.then((res) => {
this.$message.success('保存成功')
resolve(res)
})
.finally(() => {
this[loadingType] = false
})
}
})
.catch(() => {
this[loadingType] = false
})
})
},
goBack (path) {
this.$router.push({
path: `/${path}`
})
},
// 得到模板列表
getTemplateList (type) {
this.$nextTick(() => {
this.$refs.ChooseTemplateDialog.init(undefined, type)
})
},
// 选择模版后覆盖配置
selectTemplate (template) {
this.pageInfo.pageTemplateId = template.id
this.save('saveLoading', true).then(() => {
this.initLayout(this.pageCode)
})
},
replaceItByTemplate (config) {
this.changePageInfo(config)
},
// 处理保存数据
handleSaveData () {
const pageInfo = _.cloneDeep(this.pageInfo)
const chartList = _.cloneDeep(this.pageInfo.chartList)
pageInfo.pageConfig.cacheDataSets =
pageInfo.pageConfig.cacheDataSets?.map((cache) => ({
name: cache.name,
dataSetId: cache.dataSetId
})) || []
const newChartList = chartList?.map((chart) => {
// 如果是自定义组件需要将option转换为json字符串因为其中可能有函数
if (['customComponent', 'remoteComponent'].includes(chart.type)) {
chart.option.data = []
chart.option = stringifyObjectFunctions(chart.option)
}
return chart
})
return _.cloneDeep({
...this.pageInfo,
chartList: newChartList
})
},
updateRightVisiable () {
this.$emit('updateRightVisiable', !this.rightFold)
},
showPageInfo () {
this.$emit('showPageInfo')
},
designAssign () {
this.$refs.AssignDialog.init()
},
showHostory () {
this.$refs.HistoryList.init()
},
createdImg () {
this.saveAndPreviewLoading = true
const node = document.querySelector('.render-theme-wrap')
toPng(node)
.then((dataUrl) => {
const link = document.createElement('a')
link.download = `${this.pageInfo.name}.png`
link.href = dataUrl
link.click()
link.addEventListener('click', () => {
link.remove()
})
this.saveAndPreviewLoading = false
})
.catch(() => {
this.$message.warning('出现未知错误,请重试')
this.saveAndPreviewLoading = false
})
}
}
}
</script>
<style lang="scss" scoped>
@import '../BigScreenDesign/fonts/iconfont.css';
.default-layout-box {
display: flex;
flex-wrap: wrap;
.default-layout-item {
cursor: pointer;
width: 42%;
margin: 9px;
display: flex;
flex-direction: column;
align-items: center;
.component-name {
font-size: 12px;
}
.sampleImg {
margin: 0 auto;
width: 102px;
height: 73px;
display: block;
}
.img_dispaly {
margin: 0 auto;
text-align: center;
width: 100px;
height: 70px;
line-height: 70px;
background-color: #d7d7d7;
color: #999;
}
.demonstration {
text-align: center;
}
}
.default-layout-item:hover {
cursor: pointer;
}
::v-deep .el-radio__label {
display: none;
}
}
.page-top-setting-wrap {
height: 40px;
background-color: var(--bs-background-2);
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
color: #ffffff;
padding: 0 5px;
.app-name {
cursor: pointer;
}
.head-btn-group {
display: flex;
margin-left: 50px;
i {
font-size: 14px;
}
.icon-reverse {
transform: rotate(180deg);
}
}
.item-wrap {
display: flex;
align-items: center;
.menu-img {
width: 18px;
height: 18px;
margin-left: 9px;
margin-right: 15px;
cursor: pointer;
}
.logo-text {
user-select: none;
margin-left: 9px;
font-size: 14px;
color: #ffffff;
}
.name-span {
max-width: 300px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
</style>