Browse Source

大屏界面

master
xuhuajiao 3 months ago
parent
commit
70a3204184
  1. 43
      src/api/digitalScreen/index.js
  2. BIN
      src/assets/images/screen/screen5.png
  3. 34
      src/assets/styles/digitalScreen.scss
  4. 10
      src/views/components/upload.vue
  5. 1018
      src/views/digitalScreen/index.vue
  6. 59
      src/views/digitalScreen/module/addBookDialog.vue
  7. 84
      src/views/digitalScreen/module/advSetting.vue
  8. 101
      src/views/digitalScreen/module/areaSetting.vue
  9. 41
      src/views/digitalScreen/module/bookRecommend.vue
  10. 1
      src/views/digitalScreen/module/mediaSetting.vue
  11. 205
      src/views/digitalScreen/module/wecharQrCode.vue

43
src/api/digitalScreen/index.js

@ -17,4 +17,45 @@ export function FetchEditScreenSetting(parameter) {
})
}
export default { FetchInitScreenSetting, FetchEditScreenSetting }
// 智慧大屏图书推荐
export function FetchInitScreenBookRecommend(params) {
return request({
url: 'api/screenSetting/initScreenBookRecommend' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 编辑智慧大屏图书推荐
export function FetchEditScreenBookRecommend(parameter) {
return request({
url: 'api/screenSetting/editScreenBookRecommend',
method: 'post',
data: parameter
})
}
// 智慧大屏分馆列表
export function FetchInitScreenBranch(params) {
return request({
url: 'api/screenSetting/initScreenBranch' + '?' + qs.stringify(params, { indices: false }),
method: 'get'
})
}
// 编辑智慧大屏分馆
export function FetchEditScreenBranch(parameter) {
return request({
url: 'api/screenSetting/editScreenBranch',
method: 'post',
data: parameter
})
}
export default {
FetchInitScreenSetting,
FetchEditScreenSetting,
FetchInitScreenBookRecommend,
FetchEditScreenBookRecommend,
FetchInitScreenBranch,
FetchEditScreenBranch
}

BIN
src/assets/images/screen/screen5.png

Before

Width: 1920  |  Height: 1080  |  Size: 985 KiB

After

Width: 3556  |  Height: 2000  |  Size: 292 KiB

34
src/assets/styles/digitalScreen.scss

@ -99,7 +99,7 @@
}
}
.library-form{
padding-left: 70px;
padding-left: 100px;
margin-top: 10px;
.el-input{
width: 240px !important;
@ -195,7 +195,7 @@
// }
.book-swiper-wrapper {
position: relative;
width: 100%;
// width: 100%;
// padding: 0 20px;
max-width: 1140px;
overflow: visible !important; // 避免意外截断
@ -208,14 +208,14 @@
width: 110px;
height: 160px;
flex-shrink: 0; // 禁止压缩保持固定尺寸
border: 1px solid #fff;
border: 2px solid #fff;
border-radius: 4px;
transition: all 0.2s;
margin: 0 5px;
&:hover {
border-color: #0348F3; // hover时高亮边框
box-shadow: 0 2px 8px rgba(3, 72, 243, 0.1);
}
// &:hover {
// border-color: #0348F3; // hover时高亮边框
// box-shadow: 0 2px 8px rgba(3, 72, 243, 0.1);
// }
img{
display: block;
width: 100%;
@ -242,8 +242,9 @@
color: #0348F3;
}
.icon-weixuan {
color: #A6ADB6;
color: #fff;
}
}
.book-delete {
@ -263,6 +264,17 @@
}
border-color: #0348F3;
}
&:hover {
border-color: #c9d4f0;
box-shadow: 0 2px 8px rgba(3, 72, 243, 0.1);
}
&.book-list-item--selected {
background:rgba(0,0,0,0.5);
border-color: #ED4A41;
box-shadow: 0 2px 12px rgba(3, 72, 243, 0.2);
}
}
.book-upload-btn{
width: 160px;
@ -305,6 +317,7 @@
position: absolute;
left: 0;
top: 0;
width: 120px;
height: 120px;
opacity: 0;
}
@ -326,4 +339,9 @@
& i{
font-size: 30px;
}
}
// 选中时图片轻微缩放增强反馈
.book-list-item--selected .book-img{
transform: scale(0.95);
}

10
src/views/components/upload.vue

@ -1,6 +1,6 @@
<template>
<div>
<el-form-item class="book-cover-upload" :label="labelName" prop="cover">
<el-form-item v-if="uploadType !== 'other-digital'" class="book-cover-upload" :label="labelName" prop="cover">
<el-input v-model="fileNames" placeholder="请上传" :readonly="true" />
<!-- <p :class="['input-style', form.cover === null ? 'error-box' :'']">{{ form.cover }}</p> -->
<!-- <span v-if="form.cover === null" class="error-tip">请上传图书封面</span> -->
@ -9,6 +9,11 @@
<el-button size="small" type="primary"><i class="iconfont icon-shangchuan" />上传</el-button>
</div>
</el-form-item>
<div v-else class="upload-img-input">
<input ref="fileInput" type="file" @change="changeFile($event)">
<div class="upload-libImg"><i class="iconfont icon-shangchuan2" /><span>点击上传</span></div>
</div>
</div>
</template>
@ -70,7 +75,7 @@ export default {
const res = await this.getImgPx(fileBase64)
this.imageUrl = fileBase64
this.px = res.width + 'px*' + res.height + 'px'
if (this.uploadType === 'book') {
if (this.uploadType === 'book' || this.uploadType !== 'other-digital') {
//
upload(this.baseApi + '/api/fileRelevant/uploadBookImg', this.file).then(res => {
console.log(res)
@ -146,6 +151,7 @@ export default {
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/digitalScreen.scss";
::v-deep .book-cover-upload {
display: flex;
justify-content: flex-start;

1018
src/views/digitalScreen/index.vue
File diff suppressed because it is too large
View File

59
src/views/digitalScreen/module/addBookDialog.vue

@ -3,8 +3,8 @@
<div class="setting-dialog">
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="90px">
<div style="width: 696px;">
<el-form-item label="题名" prop="title">
<el-input v-model="form.title" placeholder="请输入" />
<el-form-item label="题名" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
</el-form-item>
<el-form-item label="著者" prop="author">
<el-input v-model="form.author" placeholder="请输入" />
@ -12,11 +12,11 @@
<el-form-item label="ISBN" prop="isbn">
<el-input v-model="form.isbn" placeholder="请输入" />
</el-form-item>
<el-form-item label="分类号" prop="classNo">
<el-input v-model="form.classNo" placeholder="请输入" />
<el-form-item label="分类号" prop="callnos">
<el-input v-model="form.callnos" placeholder="请输入" />
</el-form-item>
<el-form-item label="出版年份" prop="publishYear">
<el-input v-model="form.publishYear" placeholder="请输入" />
<el-form-item label="出版年份" prop="publisherdate">
<el-input v-model="form.publisherdate" placeholder="请输入" />
</el-form-item>
<div>
<el-form-item label="出版社" prop="publisher">
@ -24,13 +24,13 @@
</el-form-item>
</div>
<div>
<el-form-item label="简介" prop="remarks">
<el-input v-model="form.remarks" type="textarea" row="4" placeholder="请输入" style="width: 586px;" />
<el-form-item label="简介" prop="explain">
<el-input v-model="form.explain" type="textarea" row="4" placeholder="请输入" style="width: 586px;" />
</el-form-item>
</div>
<UploadCover :label-name="labelName" :form="form" upload-type="book" @childCover="handleCover" />
</div>
<div v-if="form.bookCover" class="preview-cover">
<div v-if="form.imgPath" class="preview-cover">
<p>封面预览</p>
<img :src="bookCover" alt="">
</div>
@ -45,6 +45,7 @@
</template>
<script>
import { FetchEditScreenBookRecommend } from '@/api/digitalScreen/index'
import UploadCover from '@/views/components/upload.vue'
import { mapGetters } from 'vuex'
@ -55,11 +56,11 @@ export default {
return {
btnLoading: false,
addBookDialogVisible: false,
form: { id: null, title: null, author: null, isbn: null, classNo: null, publishYear: null, publisher: null, bookCover: null, bookRecNo: null, remarks: null },
form: { id: null, name: null, author: null, isbn: null, callnos: null, publisherdate: null, publisher: null, imgPath: null, explain: null },
bookCover: null,
labelName: '图书封面',
rules: {
title: [
name: [
{ required: true, message: '题名不可为空', trigger: 'blur' }
],
// author: [
@ -68,7 +69,7 @@ export default {
isbn: [
{ required: true, message: 'ISBN不可为空', trigger: 'blur' }
]
// classNo: [
// callnos: [
// { required: true, message: '', trigger: 'blur' }
// ],
// publisher: [
@ -88,10 +89,10 @@ export default {
handleCover(value) {
console.log(value)
if (value) {
this.form.bookCover = value
this.bookCover = this.baseApi + '/api/fileRelevant/getImg?imgType=2&imgId=' + value + '.jpg'
this.form.imgPath = value
this.bookCover = this.baseApi + '/api/fileRelevant/getImg?imgType=2&imgId=' + value
} else {
this.form.bookCover = null
this.form.imgPath = null
this.bookCover = null
}
},
@ -102,22 +103,18 @@ export default {
this.$refs.form.validate((valid) => {
if (valid) {
this.addBookDialogVisible = false
// const ids = []
// this.selectBookData.forEach(val => {
// ids.push(val.id)
// })
// const params = {
// 'gridId': this.selectGridVal.id,
// 'ids': ids
// }
// console.log('params', params)
// FetchManualShelving(params).then(() => {
// this.$message({ message: '', type: 'success', offset: 8 })
// this.handleCloseDialog()
// }).catch(err => {
// console.log(err)
// this.addBookDialogVisible = false
// })
console.log(this.form)
FetchEditScreenBookRecommend(this.form).then((res) => {
if (res.code !== 500) {
this.$message({ message: '推荐图书新增成功', type: 'success', offset: 8 })
} else {
this.$message({ message: res.message, type: 'error', offset: 8 })
}
this.handleClose()
}).catch(err => {
console.log(err)
this.addBookDialogVisible = false
})
} else {
console.log('error submit!!')
return false

84
src/views/digitalScreen/module/advSetting.vue

@ -4,12 +4,12 @@
<div class="config-item-main">
<span class="data-title">播放设置</span>
<el-form ref="advForm" class="advForm" :model="advForm" size="small" label-width="100px">
<el-form-item label="图片切换时长" prop="time">
<div v-if="!editStatus.advForm.time" style="display: flex; justify-content: flex-start; width: 340px; height: 32px; align-items: flex-start;">
<span class="edit-readonly" @dblclick="handleDblClick('advForm', 'time')"> {{ advForm.time || '' }} </span>
<el-form-item label="图片切换时长" prop="welcome_play">
<div v-if="!editStatus.advForm.welcome_play" style="display: flex; justify-content: flex-start; width: 340px; height: 32px; align-items: flex-start;">
<span class="edit-readonly" @dblclick="handleDblClick('advForm', 'welcome_play')"> {{ advForm.welcome_play || '' }} </span>
<span class="edit-slot"></span>
</div>
<el-input v-else ref="advFormtime" v-model="advForm.time" @blur="handleBlur('advForm', 'time')" @keyup.enter="handleBlur('advForm', 'time')"><template slot="append"></template></el-input>
<el-input v-else ref="advFormwelcome_play" v-model="advForm.welcome_play" @blur="handleBlur('advForm', 'welcome_play')" @keyup.enter="handleBlur('advForm', 'welcome_play')"><template slot="append"></template></el-input>
</el-form-item>
</el-form>
</div>
@ -30,16 +30,19 @@
v-for="(book, index) in advImgList"
:key="book.id"
class="book-list-item"
:class="{ 'book-list-item--selected': book.selected }"
>
<img :src="book.url || require('@/assets/images/cover-bg.png')" alt="图书封面">
<span style="position: absolute; top: 0; left: 0;">{{ book.id }}</span>
<div class="book-select" @click.stop="toggleBookSelect(index)">
<div style="cursor: default;" @click.stop="toggleBookSelect(index)">
<img :src="book.url || require('@/assets/images/cover-bg.png')" alt="图书封面"></img>
</div>
<!-- <span style="position: absolute; top: 0; left: 0;">{{ book.id }}</span> -->
<!-- <div class="book-select" @click.stop="toggleBookSelect(index)">
<i v-if="book.selected" class="iconfont icon-wancheng" />
<i v-else class="iconfont icon-weixuan" />
</div>
<div class="book-delete" @click.stop="deleteBook(index)">
<i class="iconfont icon-shanchu" />
</div>
</div> -->
</swiper-slide>
</swiper>
</div>
@ -60,6 +63,7 @@
</template>
<script>
import { FetchEditScreenSetting } from '@/api/digitalScreen/index'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import { mapGetters } from 'vuex'
@ -72,21 +76,27 @@ export default {
swiper,
swiperSlide
},
props: {
welcomePlay: {
type: String,
default: null
}
},
data() {
return {
advForm: {
time: null
welcome_play: null
},
editStatus: {
advForm: {
time: false
welcome_play: false
}
},
originalValues: {
advForm: {}
},
advImgList: [
{ id: 1, url: require('@/assets/images/screen/screen4.png'), selected: false },
{ id: 1, url: require('@/assets/images/screen/screen5.png'), selected: false },
{ id: 2, url: require('@/assets/images/screen/screen4.png'), selected: true },
{ id: 3, url: require('@/assets/images/screen/screen4.png'), selected: false },
{ id: 4, url: require('@/assets/images/screen/screen4.png'), selected: false },
@ -122,18 +132,21 @@ export default {
return this.advImgList.some(book => book.selected)
}
},
watch: {
welcomePlay(newVal) {
if (newVal !== this.advForm.welcome_play) {
this.advForm.welcome_play = newVal
this.originalValues.advForm.welcome_play = newVal
}
}
},
created() {
this.advForm.welcome_play = this.welcomePlay
},
mounted() {
},
methods: {
updateSwiper() {
if (this.$refs.adImgSwiper && this.$refs.adImgSwiper.swiper) {
const swiper = this.$refs.adImgSwiper.swiper
swiper.update()
swiper.slideReset()
}
},
handleDblClick(formName, field) {
this.originalValues[formName][field] = this[formName][field]
this.editStatus[formName][field] = true
@ -145,18 +158,43 @@ export default {
handleBlur(formName, field) {
this.editStatus[formName][field] = false
if (this[formName][field] !== this.originalValues[formName][field]) {
this.saveEdit(formName, field)
this.submitMapData(formName, field)
}
},
saveEdit(formName, field) {
submitMapData(formName, field) {
const value = this[formName][field]
console.log(`保存${formName}.${field}`, value)
if (!value) {
this.$message.warning(`${this.getFieldLabel(formName, field)}不能为空`)
this.$message.warning(`图片切换时长不能为空`)
this[formName][field] = this.originalValues[formName][field]
return
}
this.$message.success(`${this.getFieldLabel(formName, field)}更新成功`)
const param = {
'code': field,
'context': value,
'remarks': null
}
FetchEditScreenSetting(param)
.then((res) => {
if (res.code !== 500) {
this.$message.success('图片切换时长更新成功')
this.$emit('update:welcomePlay', value)
this.$emit('triggerInit')
} else {
this.$message.error('图片切换时长更新失败')
}
})
.catch(() => {
this.$message.error('图片切换时长更新失败')
})
},
updateSwiper() {
if (this.$refs.adImgSwiper && this.$refs.adImgSwiper.swiper) {
const swiper = this.$refs.adImgSwiper.swiper
swiper.update()
swiper.slideReset()
}
},
toggleBookSelect(index) {
this.advImgList[index].selected = !this.advImgList[index].selected

101
src/views/digitalScreen/module/areaSetting.vue

@ -4,22 +4,23 @@
<div class="config-item-main">
<span class="data-title">地图DataV</span>
<el-form ref="areaForm" class="areaForm" :model="areaForm" size="small" label-width="100px">
<el-form-item label="GeoJSON" prop="geoJSON">
<el-form-item label="GeoJSON" prop="map_data">
<span
v-if="!editStatus.areaForm.geoJSON"
v-if="!editStatus.areaForm.map_data"
class="edit-readonly"
@dblclick="handleDblClick('areaForm', 'geoJSON')"
style="padding: 4px 15px;"
@dblclick="handleDblClick('areaForm', 'map_data')"
>
{{ areaForm.geoJSON || '' }}
{{ areaForm.map_data || '' }}
</span>
<el-input
v-else
ref="areaFormgeoJSON"
v-model="areaForm.geoJSON"
ref="areaFormmap_data"
v-model="areaForm.map_data"
type="textarea"
rows="5"
@blur="handleBlur('areaForm', 'geoJSON')"
@keyup.enter="handleBlur('areaForm', 'geoJSON')"
@blur="handleBlur('areaForm', 'map_data')"
@keyup.enter="handleBlur('areaForm', 'map_data')"
/>
</el-form-item>
</el-form>
@ -50,6 +51,7 @@
</el-table-column>
</el-table>
<el-pagination
v-if="tableData.length !== 0"
style="margin: 34px 0 10px 0 !important;"
:current-page="page.page"
:total="page.total"
@ -171,19 +173,27 @@
</template>
<script>
import { FetchEditScreenSetting, FetchInitScreenBranch } from '@/api/digitalScreen/index'
import { mapGetters } from 'vuex'
export default {
name: 'AreaSetting',
components: { },
props: {
mapData: {
type: String,
default: null
}
},
data() {
return {
btnLoading: false,
areaForm: {
geoJSON: null
map_data: null
},
editStatus: {
areaForm: {
geoJSON: false
map_data: false
}
},
originalValues: {
@ -192,17 +202,10 @@ export default {
geoJsonVisible: false,
link: 'https://datav.aliyun.com/portal/school/atlas/area_selector',
copied: false,
tableData: [{
name: '总管',
libCode: 'lib001',
province: '114.133561,30.641408',
number: '3'
}, {
name: '分管1',
libCode: 'lib001',
province: '114.133561,30.641408',
number: '3'
}],
tableData: [
// { name: '', libCode: 'lib001', province: '114.133561,30.641408', number: '3' },
// { name: '1', libCode: 'lib001', province: '114.133561,30.641408', number: '3' }
],
page: {
page: 1,
size: 10,
@ -241,42 +244,70 @@ export default {
'baseApi'
])
},
watch: {
mapData(newVal) {
if (newVal !== this.areaForm.map_data) {
this.areaForm.map_data = newVal
this.originalValues.areaForm.map_data = newVal
}
}
},
created() {
this.areaForm.map_data = this.mapData
},
mounted() {
this.$emit('ready')
},
methods: {
//
initScreenBranch() {
FetchInitScreenBranch().then((res) => {
console.log('initScreenBranch', res)
}).catch(err => {
console.log(err)
})
},
handleDblClick(formName, field) {
//
this.originalValues[formName][field] = this[formName][field]
//
this.editStatus[formName][field] = true
//
this.$nextTick(() => {
const input = this.$refs[`${formName}${field}`]
if (input) input.focus()
})
},
// /
handleBlur(formName, field) {
//
this.editStatus[formName][field] = false
//
if (this[formName][field] !== this.originalValues[formName][field]) {
this.saveEdit(formName, field)
this.submitMapData(formName, field)
}
},
//
saveEdit(formName, field) {
submitMapData(formName, field) {
const value = this[formName][field]
console.log(`保存${formName}.${field}`, value)
if (!value) {
this.$message.warning(`${this.getFieldLabel(formName, field)}不能为空`)
this.$message.warning(`GeoJSON不能为空`)
this[formName][field] = this.originalValues[formName][field]
return
}
this.$message.success(`${this.getFieldLabel(formName, field)}更新成功`)
const param = {
'code': field,
'context': value,
'remarks': null
}
FetchEditScreenSetting(param)
.then((res) => {
if (res.code !== 500) {
this.$message.success('GeoJSON更新成功')
this.$emit('update:mapData', value)
this.$emit('triggerInit')
} else {
this.$message.error('GeoJSON更新失败')
}
})
.catch(() => {
this.$message.error('GeoJSON更新失败')
})
},
handleClose() {
this.geoJsonVisible = false
this.libDialogVisible = false

41
src/views/digitalScreen/module/bookRecommend.vue

@ -28,7 +28,7 @@
</div>
<!-- <div class="book-upload-btn" @click.stop="uploadBook"><i class="iconfont icon-shangchuan2" /></div> -->
</div>
<div style="display: flex; flex-direction: column; justify-content: flex-start; margin-left: 15px;">
<div style="display: flex; flex-direction: column; justify-content: flex-start;">
<el-button size="mini" type="primary" @click.stop="uploadBook"><i class="iconfont icon-shangchuan" />图书上传</el-button>
<el-button size="mini" style="margin-left: 0 !important; margin-top: 10px;" :disabled="!hasSelectedItems" @click="batchDelete">
<i class="iconfont icon-shanchu" />
@ -40,6 +40,7 @@
</template>
<script>
import { FetchInitScreenBookRecommend } from '@/api/digitalScreen/index'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import addBookDialog from './addBookDialog'
@ -57,21 +58,21 @@ export default {
data() {
return {
bookList: [
{ id: 1, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 2, url: require('@/assets/images/screen/book.jpg'), selected: true },
{ id: 3, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 4, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 5, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 6, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 7, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 8, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 9, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 10, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 11, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 12, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 13, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 14, url: require('@/assets/images/screen/book.jpg'), selected: false },
{ id: 15, url: require('@/assets/images/screen/book.jpg'), selected: false }
// { id: 1, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 2, url: require('@/assets/images/screen/book.jpg'), selected: true },
// { id: 3, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 4, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 5, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 6, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 7, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 8, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 9, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 10, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 11, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 12, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 13, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 14, url: require('@/assets/images/screen/book.jpg'), selected: false },
// { id: 15, url: require('@/assets/images/screen/book.jpg'), selected: false }
],
swiperOptions: {
slidesPerView: 'auto', //
@ -105,8 +106,16 @@ export default {
created() {
},
mounted() {
this.$emit('ready')
},
methods: {
initScreenBookRecommend() {
FetchInitScreenBookRecommend().then((res) => {
console.log('BookRecommend', res)
}).catch(err => {
console.log(err)
})
},
updateBookSwiper() {
if (this.$refs.bookSwiper && this.$refs.bookSwiper.swiper) {
const swiper = this.$refs.bookSwiper.swiper

1
src/views/digitalScreen/module/mediaSetting.vue

@ -186,6 +186,7 @@ export default {
components: { },
data() {
return {
btnLoading: false,
tableData: [
{ name: 'XXXX通知', startTime: '2025-05-01 10:05:00', endTime: '2025-05-01 10:05:00' },
{ name: 'XXXX通知', startTime: '2025-05-01 10:05:00', endTime: '2025-05-01 10:05:00' }

205
src/views/digitalScreen/module/wecharQrCode.vue

@ -0,0 +1,205 @@
<template>
<div class="config-item">
<div class="config-item-main">
<span class="data-title">二维码</span>
<div class="upload-item" style="margin-bottom: 20px;">
<p>公众号二维码</p>
<div style="display: flex; justify-content: flex-start;">
<div class="image-container">
<div v-if="isLoading" class="image-loading">
<div class="spinner" />
<span class="loading-text">加载中...</span>
</div>
<img
v-show="!isLoading || imageError"
:key="imageKey"
style="display: block; height: 120px; margin-right: 15px; transition: opacity 0.3s ease-in-out;"
:src="currentQrCodeUrl"
:class="{ 'image-loaded': !isLoading && currentQrCodeUrl }"
alt="公众号二维码"
@load="handleImageLoad"
@error="handleImageError"
>
<!-- 错误提示 -->
<div v-if="imageError" class="image-error">
<i class="iconfont icon-error" />
<span>加载失败</span>
</div>
</div>
<UploadCover upload-type="other-digital" @childCover="handleCover" />
</div>
</div>
</div>
<div class="config-remarks">双击可上传图片二维码用于总览屏媒体屏在任意分屏编辑均可生效</div>
</div>
</template>
<script>
import { FetchEditScreenSetting } from '@/api/digitalScreen/index'
import UploadCover from '@/views/components/upload.vue'
import { mapGetters } from 'vuex'
export default {
name: 'WecharQrCode',
components: { UploadCover },
props: {
qrCodeUrl: {
type: String,
default: null
}
},
data() {
return {
defaultImg: '@/assets/images/cover-bg.png',
imageKey: 0,
localQrCodeUrl: null,
isLoading: false, //
imageError: false //
}
},
computed: {
...mapGetters(['baseApi']),
currentQrCodeUrl() {
if (this.localQrCodeUrl) {
return this.localQrCodeUrl
}
if (this.qrCodeUrl) {
return this.qrCodeUrl.startsWith('http') ? this.qrCodeUrl : this.baseApi + this.qrCodeUrl
}
return this.defaultImg
}
},
watch: {
qrCodeUrl(newVal) {
this.localQrCodeUrl = newVal
this.reloadImage()
},
currentQrCodeUrl(newVal) {
if (newVal && newVal !== this.defaultImg) {
this.reloadImage()
}
}
},
created() {
this.localQrCodeUrl = this.qrCodeUrl
},
mounted() {
this.$emit('ready')
},
methods: {
handleCover(value) {
if (value) {
const imgUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=1&imgId=' + value
this.submitCover(imgUrl)
} else {
this.localQrCodeUrl = null
this.imageKey++
this.isLoading = false
this.imageError = false
}
},
submitCover(imgUrl) {
const param = {
'code': 'wechar_qr_code',
'context': imgUrl,
'remarks': null
}
FetchEditScreenSetting(param)
.then(() => {
this.$message.success('公众号二维码更新成功')
this.localQrCodeUrl = imgUrl
this.reloadImage()
this.$emit('update:qrCodeUrl', imgUrl)
this.$emit('triggerInit')
})
.catch(() => {
this.$message.error('公众号二维码更新失败')
})
},
reloadImage() {
this.isLoading = true
this.imageError = false
this.imageKey++
},
//
handleImageLoad() {
this.isLoading = false
this.imageError = false
},
//
handleImageError() {
this.isLoading = false
this.imageError = true
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/digitalScreen.scss";
.image-container {
position: relative;
min-width: 120px;
height: 120px;
// margin-right: 15px;
}
.image-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 4px;
}
.spinner {
width: 20px;
height: 20px;
border: 2px solid #eee;
border-top: 2px solid #0348f3;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 8px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 12px;
color: #666;
}
.image-loaded {
opacity: 1;
}
.image-error {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 4px;
color: #ED4A41;
font-size: 12px;
}
.image-error .icon-error {
font-size: 20px;
margin-bottom: 8px;
}
</style>
Loading…
Cancel
Save