2026-02-25 16:47:55 +08:00
|
|
|
import xss from "xss"
|
|
|
|
|
<template>
|
|
|
|
|
<j-modal
|
|
|
|
|
:title="title"
|
|
|
|
|
:width="modelStyle.width"
|
|
|
|
|
:visible="visible"
|
|
|
|
|
:bodyStyle ="bodyStyle"
|
|
|
|
|
:switchFullscreen="switchFullscreen"
|
|
|
|
|
@cancel="handleCancel"
|
|
|
|
|
>
|
|
|
|
|
<template slot="footer">
|
|
|
|
|
<a-button key="back" @click="handleCancel">关闭</a-button>
|
|
|
|
|
<!-- <a-button type="primary" @click="toHandle">去处理</a-button>-->
|
|
|
|
|
</template>
|
|
|
|
|
<a-card class="daily-article" :loading="loading">
|
|
|
|
|
<a-card-meta
|
|
|
|
|
:title="record.spare"
|
|
|
|
|
:description="'发布人:'+record.sender + ' 发布时间: ' + record.sendTime">
|
|
|
|
|
</a-card-meta>
|
2026-03-30 11:03:47 +08:00
|
|
|
<a-row type="flex" align="middle">
|
|
|
|
|
<a-col>
|
|
|
|
|
<a-button type="primary" @click="speakText">播报语音</a-button>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col>
|
|
|
|
|
<a-button style="margin-left: 8px" @click="stopSpeech">停止朗读</a-button>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
2026-02-25 16:47:55 +08:00
|
|
|
<a-divider />
|
|
|
|
|
<span v-html="record.msgContent" class="article-content"></span>
|
|
|
|
|
</a-card>
|
|
|
|
|
</j-modal>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import {getUserList} from '@/api/api'
|
|
|
|
|
import xss from 'xss'
|
|
|
|
|
export default {
|
|
|
|
|
name: "SysAnnouncementModal",
|
|
|
|
|
components: {
|
|
|
|
|
},
|
|
|
|
|
data () {
|
|
|
|
|
return {
|
|
|
|
|
title:"通知消息",
|
2026-03-30 11:03:47 +08:00
|
|
|
textToSpeak: '',
|
2026-02-25 16:47:55 +08:00
|
|
|
record: {},
|
2026-03-30 11:03:47 +08:00
|
|
|
timer333: null,
|
|
|
|
|
hasUnsavedChanges: false,
|
2026-02-25 16:47:55 +08:00
|
|
|
labelCol: {
|
|
|
|
|
xs: { span: 24 },
|
|
|
|
|
sm: { span: 5 },
|
|
|
|
|
},
|
|
|
|
|
wrapperCol: {
|
|
|
|
|
xs: { span: 24 },
|
|
|
|
|
sm: { span: 16 },
|
|
|
|
|
},
|
|
|
|
|
visible: false,
|
|
|
|
|
switchFullscreen: true,
|
|
|
|
|
loading: false,
|
|
|
|
|
bodyStyle:{
|
|
|
|
|
padding: "0",
|
|
|
|
|
height:(window.innerHeight*0.8)+"px",
|
|
|
|
|
"overflow-y":"auto",
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
modelStyle:{
|
|
|
|
|
width: '60%',
|
|
|
|
|
style: { top: '20px' },
|
|
|
|
|
fullScreen: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created () {
|
2026-03-30 11:03:47 +08:00
|
|
|
console.log("组件创建");
|
|
|
|
|
// this.startAutoSpeech();todo
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
console.log("组件挂载完成");
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
visible(newVal) {
|
|
|
|
|
if (newVal) {
|
|
|
|
|
// this.startAutoSpeech(); todo
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-25 16:47:55 +08:00
|
|
|
},
|
|
|
|
|
methods: {
|
2026-03-30 11:03:47 +08:00
|
|
|
speakText() {
|
|
|
|
|
console.log("开始语音播报");
|
|
|
|
|
if ('speechSynthesis' in window) {
|
|
|
|
|
const utterance = new SpeechSynthesisUtterance(this.textToSpeak);
|
|
|
|
|
utterance.lang = 'zh-CN';
|
|
|
|
|
utterance.rate = 1;
|
|
|
|
|
utterance.pitch = 1;
|
|
|
|
|
utterance.volume = 1;
|
|
|
|
|
// window.speechSynthesis.speak(utterance);
|
|
|
|
|
const result = window.speechSynthesis.speak(utterance);
|
|
|
|
|
} else {
|
|
|
|
|
alert('您的浏览器不支持语音合成');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
stopSpeech() {
|
|
|
|
|
window.speechSynthesis.cancel();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
startAutoSpeech(intervalTime = 1800) {
|
|
|
|
|
console.log("启动自动语音播报");
|
|
|
|
|
if (this.timer333) clearInterval(this.timer333);
|
|
|
|
|
this.timer333 = setInterval(() => {
|
|
|
|
|
this.speakText();
|
|
|
|
|
}, intervalTime);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
detail(record) {
|
|
|
|
|
console.log("查看详情",record)
|
|
|
|
|
if (record.sender) {
|
|
|
|
|
getUserList({ "username": record.sender }).then((res) => {
|
|
|
|
|
if (res.success && res.result.records.length > 0) {
|
|
|
|
|
record.sender = res.result.records[0].realname;
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
2026-03-30 11:03:47 +08:00
|
|
|
});
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
2026-03-30 11:03:47 +08:00
|
|
|
|
2026-02-25 16:47:55 +08:00
|
|
|
this.visible = true;
|
2026-03-30 11:03:47 +08:00
|
|
|
|
|
|
|
|
if (record.msgContent) {
|
|
|
|
|
record.msgContent = xss(record.msgContent);
|
|
|
|
|
this.textToSpeak = record.msgContent;
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
2026-03-30 11:03:47 +08:00
|
|
|
|
2026-02-25 16:47:55 +08:00
|
|
|
this.record = record;
|
|
|
|
|
},
|
2026-03-30 11:03:47 +08:00
|
|
|
|
|
|
|
|
handleCancel() {
|
|
|
|
|
this.stopSpeech();
|
|
|
|
|
if (this.timer333) {
|
|
|
|
|
console.log('清除定时器');
|
|
|
|
|
clearInterval(this.timer333);
|
|
|
|
|
this.timer333 = null;
|
|
|
|
|
}
|
2026-02-25 16:47:55 +08:00
|
|
|
this.visible = false;
|
|
|
|
|
},
|
2026-03-30 11:03:47 +08:00
|
|
|
|
2026-02-25 16:47:55 +08:00
|
|
|
handleClickToggleFullScreen() {
|
2026-03-30 11:03:47 +08:00
|
|
|
let mode = !this.modelStyle.fullScreen;
|
2026-02-25 16:47:55 +08:00
|
|
|
if (mode) {
|
2026-03-30 11:03:47 +08:00
|
|
|
this.modelStyle.width = '100%';
|
|
|
|
|
this.modelStyle.style.top = '20px';
|
2026-02-25 16:47:55 +08:00
|
|
|
} else {
|
2026-03-30 11:03:47 +08:00
|
|
|
this.modelStyle.width = '60%';
|
|
|
|
|
this.modelStyle.style.top = '50px';
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
2026-03-30 11:03:47 +08:00
|
|
|
this.modelStyle.fullScreen = mode;
|
2026-02-25 16:47:55 +08:00
|
|
|
},
|
2026-03-30 11:03:47 +08:00
|
|
|
|
|
|
|
|
toHandle() {
|
|
|
|
|
if (this.record.openType === 'url') {
|
2026-02-25 16:47:55 +08:00
|
|
|
this.visible = false;
|
2026-03-30 11:03:47 +08:00
|
|
|
this.$router.push({ path: this.record.openPage });
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
2026-03-30 11:03:47 +08:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
if (this.timer333) {
|
|
|
|
|
clearInterval(this.timer333);
|
|
|
|
|
this.timer333 = null;
|
|
|
|
|
}
|
2026-02-25 16:47:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="less">
|
|
|
|
|
.announcementCustomModal{
|
|
|
|
|
.ant-modal-header {
|
|
|
|
|
border: none;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
position: absolute;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
right: 56px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
.ant-modal-title{
|
|
|
|
|
.custom-btn{
|
|
|
|
|
width: 56px;
|
|
|
|
|
height: 56px;
|
|
|
|
|
border: none;
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.daily-article{
|
|
|
|
|
border-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<style scoped lang="less">
|
|
|
|
|
.daily-article {
|
|
|
|
|
.article-button {
|
|
|
|
|
font-size: 1.2rem !important;
|
|
|
|
|
}
|
|
|
|
|
.ant-card-body {
|
|
|
|
|
padding: 18px !important;
|
|
|
|
|
}
|
|
|
|
|
.ant-card-head {
|
|
|
|
|
padding: 0 1rem;
|
|
|
|
|
}
|
|
|
|
|
.ant-card-meta {
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
.article-content {
|
|
|
|
|
p {
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
text-overflow: initial;
|
|
|
|
|
white-space: normal;
|
|
|
|
|
font-size: .9rem !important;
|
|
|
|
|
margin-bottom: .8rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|