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

<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>