424 lines
13 KiB
Vue
424 lines
13 KiB
Vue
<template>
|
||
<div class="board-container">
|
||
<div class="widget-left-container">
|
||
<div class="widget-left-header">
|
||
<span>{{ dataBoard.boardName }}</span>
|
||
</div>
|
||
<div class="widget-left-wrapper">
|
||
<ul class="list-group">
|
||
<li v-for="(item, index) in dataChartList" :key="item.id" class="list-group-item">
|
||
<div class="list-group-item-text">{{ item.chartName }}</div>
|
||
<div class="list-group-item-button"><el-button icon="el-icon-plus" type="text" size="mini" :disabled="item.disabled" @click="handleAddChart(item)" /></div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="widget-center-container">
|
||
<div class="widget-center-header">
|
||
<div class="widget-center-header-collapse" @click="drawer = true"><i class="el-icon-info" /></div>
|
||
<div class="widget-center-header-button">
|
||
<el-button icon="el-icon-view" type="text" @click="handlePreview">
|
||
预览
|
||
</el-button>
|
||
<el-button icon="el-icon-delete" type="text" @click="handleReset">
|
||
重置
|
||
</el-button>
|
||
<el-button icon="el-icon-plus" type="text" @click="handleSubmit">
|
||
保存
|
||
</el-button>
|
||
<el-button icon="el-icon-close" type="text" @click="handleCancel">
|
||
取消
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="widget-center-wrapper">
|
||
<grid-layout
|
||
:layout.sync="layout"
|
||
:col-num="24"
|
||
:row-height="30"
|
||
:is-draggable="true"
|
||
:is-resizable="true"
|
||
:is-mirrored="false"
|
||
:vertical-compact="true"
|
||
:use-css-transforms="true"
|
||
:pane-container="false"
|
||
:margin="[10, 10]"
|
||
style="border: 1px dashed #999; height: 100%; overflow-x: hidden; overflow-y: auto;"
|
||
>
|
||
<grid-item
|
||
v-for="item in layout"
|
||
:key="item.i"
|
||
:x="item.x"
|
||
:y="item.y"
|
||
:w="item.w"
|
||
:h="item.h"
|
||
:i="item.i"
|
||
drag-allow-from=".vue-draggable-handle"
|
||
@resized="handleResize"
|
||
>
|
||
<el-card v-loading="getChartItem(item.i).loading" class="widget-center-card" body-style="padding: 10px;">
|
||
<div slot="header" class="widget-center-card-header vue-draggable-handle">
|
||
<div>
|
||
<span>{{ getChartItem(item.i).chartName }}</span>
|
||
</div>
|
||
<div>
|
||
<i class="el-icon-delete" @click="handleDeleteChart(item.i)" />
|
||
<i class="el-icon-setting" @click="handleTimerChart(item.i)" />
|
||
</div>
|
||
</div>
|
||
<chart-panel v-if="getChartItem(item.i).visible" :key="item.i" :ref="`charts${item.i}`" :chart-schema="getChartItem(item.i).chartSchema" :chart-data="getChartItem(item.i).data" :chart-style="{height: `${item.h * 30 + 10 * (item.h - 1) - 60}px`}" />
|
||
<div v-else :style="{height: `${item.h * 30 + 10 * (item.h - 1) - 60}px`}" />
|
||
</el-card>
|
||
</grid-item>
|
||
</grid-layout>
|
||
</div>
|
||
</div>
|
||
<el-drawer
|
||
size="300px"
|
||
:visible.sync="drawer"
|
||
:with-header="false"
|
||
>
|
||
<div class="widget-board-form">
|
||
<el-form size="mini" label-position="top">
|
||
<el-form-item label="看板名称">
|
||
<el-input v-model="dataBoard.boardName" size="mini" :disabled="true" />
|
||
</el-form-item>
|
||
<el-form-item label="缩略图">
|
||
<el-upload
|
||
action="#"
|
||
accept=".png, .jpg, .jpeg"
|
||
list-type="picture-card"
|
||
:limit="1"
|
||
:auto-upload="false"
|
||
:before-upload="beforeUpload"
|
||
:on-change="handleChange"
|
||
:on-remove="handleRemove"
|
||
:file-list="fileList"
|
||
:class="{ hideUpload: hideUpload }"
|
||
>
|
||
<i slot="default" class="el-icon-plus" />
|
||
</el-upload>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getDataBoard, buildDataBoard } from '@/api/visual/databoard'
|
||
import { listDataChart, getDataChart, dataParser } from '@/api/visual/datachart'
|
||
import VueGridLayout from 'vue-grid-layout'
|
||
import ChartPanel from '../datachart/components/ChartPanel'
|
||
import { compressImg, dataURLtoBlob } from '@/utils/compressImage'
|
||
|
||
export default {
|
||
name: 'DataBoardBuild',
|
||
components: {
|
||
GridLayout: VueGridLayout.GridLayout,
|
||
GridItem: VueGridLayout.GridItem,
|
||
ChartPanel
|
||
},
|
||
data() {
|
||
return {
|
||
dataBoard: {},
|
||
dataChartList: [],
|
||
layout: [],
|
||
interval: [],
|
||
charts: [],
|
||
drawer: false,
|
||
// 文件上传
|
||
fileList: [],
|
||
hideUpload: false
|
||
}
|
||
},
|
||
created() {
|
||
this.getDataBoard(this.$route.params.id)
|
||
this.getDataChartList()
|
||
},
|
||
methods: {
|
||
getDataBoard(id) {
|
||
getDataBoard(id).then(response => {
|
||
if (response.success) {
|
||
this.dataBoard = response.data
|
||
if (this.dataBoard.boardThumbnail) {
|
||
const blob = dataURLtoBlob(this.dataBoard.boardThumbnail)
|
||
const fileUrl = URL.createObjectURL(blob)
|
||
this.fileList.push({ url: fileUrl })
|
||
this.hideUpload = true
|
||
}
|
||
this.layout = this.dataBoard.boardConfig ? JSON.parse(JSON.stringify(this.dataBoard.boardConfig.layout)) : []
|
||
this.interval = this.dataBoard.boardConfig ? JSON.parse(JSON.stringify(this.dataBoard.boardConfig.interval)) : []
|
||
const charts = this.dataBoard.charts ? JSON.parse(JSON.stringify(this.dataBoard.charts)) : []
|
||
charts.forEach((item, index, arr) => {
|
||
this.parserChart(item)
|
||
})
|
||
this.charts = charts
|
||
}
|
||
})
|
||
},
|
||
parserChart(chart) {
|
||
this.$set(chart, 'loading', true)
|
||
if (chart.chartConfig) {
|
||
dataParser(JSON.parse(chart.chartConfig)).then(response => {
|
||
if (response.success) {
|
||
this.$set(chart, 'data', response.data.data)
|
||
this.$set(chart, 'chartSchema', JSON.parse(chart.chartConfig))
|
||
this.$set(chart, 'loading', false)
|
||
this.$set(chart, 'visible', true)
|
||
}
|
||
})
|
||
} else {
|
||
this.$set(chart, 'loading', false)
|
||
}
|
||
},
|
||
getChartItem(id) {
|
||
return this.charts.find(chart => chart.id === id)
|
||
},
|
||
getDataChartList() {
|
||
listDataChart().then(response => {
|
||
if (response.success) {
|
||
this.dataChartList = response.data
|
||
}
|
||
})
|
||
},
|
||
handleAddChart(chart) {
|
||
const index = this.layout.findIndex(item => item.i === chart.id)
|
||
if (index !== -1) {
|
||
this.$set(chart, 'disabled', true)
|
||
return
|
||
}
|
||
getDataChart(chart.id).then(response => {
|
||
if (response.success) {
|
||
const obj = {
|
||
x: 0,
|
||
y: 0,
|
||
w: 12,
|
||
h: 9,
|
||
i: chart.id
|
||
}
|
||
this.layout.push(obj)
|
||
const data = response.data
|
||
this.parserChart(data)
|
||
this.charts.push(data)
|
||
this.$set(chart, 'disabled', true)
|
||
}
|
||
})
|
||
},
|
||
handleDeleteChart(id) {
|
||
this.layout.splice(this.layout.findIndex(item => item.i === id), 1)
|
||
this.charts.splice(this.charts.findIndex(item => item.id === id), 1)
|
||
this.$set(this.dataChartList.find(item => item.id === id), 'disabled', false)
|
||
},
|
||
handleTimerChart(id) {
|
||
this.$prompt('请输入定时时间间隔,输入0不定时(单位毫秒,1000 毫秒 = 1 秒)', '提示', {
|
||
showClose: false,
|
||
closeOnClickModal: false,
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
inputPattern: /^(0|\+?[1-9][0-9]*)$/,
|
||
inputErrorMessage: '格式不正确',
|
||
inputValue: '5000'
|
||
}).then(({ value }) => {
|
||
const timer = this.interval.find(item => item.id === id)
|
||
if (timer) {
|
||
this.$set(timer, 'milliseconds', value)
|
||
} else {
|
||
this.interval.push({ id: id, milliseconds: value })
|
||
}
|
||
}).catch(() => {})
|
||
},
|
||
handlePreview() {
|
||
const route = this.$router.resolve({ path: `/visual/board/view/${this.dataBoard.id}` })
|
||
window.open(route.href, '_blank')
|
||
},
|
||
handleReset() {
|
||
this.layout = this.dataBoard.boardConfig ? JSON.parse(JSON.stringify(this.dataBoard.boardConfig.layout)) : []
|
||
this.interval = this.dataBoard.boardConfig ? JSON.parse(JSON.stringify(this.dataBoard.boardConfig.interval)) : []
|
||
const charts = this.dataBoard.charts ? JSON.parse(JSON.stringify(this.dataBoard.charts)) : []
|
||
charts.forEach((item, index, arr) => {
|
||
this.parserChart(item)
|
||
})
|
||
this.charts = charts
|
||
this.dataChartList.forEach((item, index, arr) => {
|
||
if (charts.findIndex(chart => chart.id === item.id) === -1) {
|
||
this.$set(item, 'disabled', false)
|
||
} else {
|
||
this.$set(item, 'disabled', true)
|
||
}
|
||
})
|
||
},
|
||
handleSubmit() {
|
||
const data = {
|
||
id: this.dataBoard.id,
|
||
boardThumbnail: this.dataBoard.boardThumbnail,
|
||
boardConfig: {
|
||
layout: this.layout,
|
||
interval: this.interval
|
||
}
|
||
}
|
||
buildDataBoard(data).then(response => {
|
||
if (response.success) {
|
||
this.$message.success('保存成功')
|
||
}
|
||
})
|
||
},
|
||
handleCancel() {
|
||
window.location.href = 'about:blank'
|
||
window.close()
|
||
},
|
||
handleResize(i, newH, newW, newHPx, newWPx) {
|
||
if (this.charts.find(chart => chart.id === i).visible) {
|
||
this.$refs[`charts${i}`][0].$children[0].$emit('resized')
|
||
}
|
||
},
|
||
beforeUpload(file) {
|
||
const isLt2M = file.size / 1024 / 1024 < 2
|
||
if (!isLt2M) {
|
||
this.$message.error('上传图片大小不能超过 2MB')
|
||
}
|
||
return isLt2M
|
||
},
|
||
handleChange(file, fileList) {
|
||
this.hideUpload = fileList.length >= 1
|
||
const config = {
|
||
width: 250, // 压缩后图片的宽
|
||
height: 150, // 压缩后图片的高
|
||
quality: 0.8 // 压缩后图片的清晰度,取值0-1,值越小,所绘制出的图像越模糊
|
||
}
|
||
compressImg(file.raw, config).then(result => {
|
||
// result 为压缩后二进制文件
|
||
this.$set(this.dataBoard, 'boardThumbnail', result)
|
||
})
|
||
},
|
||
handleRemove(file, fileList) {
|
||
this.hideUpload = fileList.length >= 1
|
||
this.$set(this.dataBoard, 'boardThumbnail', 'x')
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.board-container {
|
||
height: 100vh;
|
||
width: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex: 1;
|
||
flex-basis: auto;
|
||
box-sizing: border-box;
|
||
min-width: 0;
|
||
::-webkit-scrollbar {
|
||
width: 3px;
|
||
height: 5px;
|
||
}
|
||
::-webkit-scrollbar-track-piece {
|
||
background: #d3dce6;
|
||
}
|
||
::-webkit-scrollbar-thumb {
|
||
background: #99a9bf;
|
||
border-radius: 10px;
|
||
}
|
||
.widget-left-container {
|
||
width: 250px;
|
||
box-sizing: border-box;
|
||
.widget-left-header {
|
||
height: 40px;
|
||
line-height: 40px;
|
||
padding: 0 20px;
|
||
}
|
||
.widget-left-wrapper {
|
||
height: calc(100% - 40px);
|
||
padding: 20px;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
border-top: 1px solid #e4e7ed;
|
||
.list-group {
|
||
padding: 0;
|
||
margin: 0;
|
||
.list-group-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
height: 30px;
|
||
line-height: 30px;
|
||
font-size: 14px;
|
||
.list-group-item-text {
|
||
width: 130px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.list-group-item-button {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.widget-center-container {
|
||
width: calc(100% - 250px);
|
||
flex: 1;
|
||
flex-basis: auto;
|
||
box-sizing: border-box;
|
||
border-left: 1px solid #e4e7ed;
|
||
border-right: 1px solid #e4e7ed;
|
||
.widget-center-header {
|
||
height: 40px;
|
||
line-height: 40px;
|
||
.widget-center-header-collapse {
|
||
float: right;
|
||
padding-right: 20px;
|
||
}
|
||
.widget-center-header-button {
|
||
float: right;
|
||
text-align: right;
|
||
padding-right: 20px;
|
||
}
|
||
}
|
||
.widget-center-wrapper {
|
||
height: calc(100% - 40px);
|
||
padding: 10px;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
border-top: 1px solid #e4e7ed;
|
||
.widget-center-card {
|
||
::v-deep .el-card__header {
|
||
padding: 0;
|
||
.widget-center-card-header {
|
||
font-size: 14px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
height: 30px;
|
||
padding: 0 10px;
|
||
line-height: 30px;
|
||
i {
|
||
margin-right: 10px;
|
||
color: #409EFF;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.widget-board-form {
|
||
padding: 20px;
|
||
.hideUpload ::v-deep {
|
||
.el-upload--picture-card {
|
||
display: none;
|
||
}
|
||
}
|
||
::v-deep {
|
||
.el-upload-list__item {
|
||
width: 250px;
|
||
height: 150px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|