新增消息类型:语音,视频

This commit is contained in:
liuwei
2025-06-12 11:37:20 +08:00
parent 0e198c7c09
commit 0e2169f295
4 changed files with 154 additions and 15 deletions

View File

@@ -23,8 +23,12 @@ message_thread_pool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="mes
shared_loop = None
loop_lock = threading.Lock()
# 允许的图片文件扩展名
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
# 允许的文件扩展名
ALLOWED_EXTENSIONS = {
'image': {'png', 'jpg', 'jpeg', 'gif'},
'voice': {'mp3', 'wav'},
'video': {'mp4'}
}
def get_or_create_loop():
@@ -69,8 +73,9 @@ def send_message_in_thread(func, *args, **kwargs):
message_thread_pool.submit(run)
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def allowed_file(filename, file_type='image'):
"""检查文件类型是否允许上传"""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS.get(file_type, set())
# 消息推送管理页面
@@ -503,7 +508,7 @@ def api_statistics():
@message_push_bp.route('/api/upload', methods=['POST'])
def upload_file():
"""处理图片上传"""
"""处理文件上传"""
if 'file' not in request.files:
return jsonify({
'success': False,
@@ -517,7 +522,14 @@ def upload_file():
'message': '没有选择文件'
})
if file and allowed_file(file.filename):
# 根据文件类型检查
file_type = 'image' # 默认类型
if file.content_type.startswith('audio/'):
file_type = 'voice'
elif file.content_type.startswith('video/'):
file_type = 'video'
if file and allowed_file(file.filename, file_type):
# 生成安全的文件名
filename = secure_filename(file.filename)
# 生成唯一文件名

View File

@@ -339,6 +339,36 @@
<img width="100%" :src="previewUrl" alt="Preview">
</el-dialog>
</el-tab-pane>
<el-tab-pane label="语音" name="voice">
<el-upload
class="upload-demo"
action="/message_push/api/upload"
{% raw %}:on-success="handleVoiceSuccess"
:before-upload="beforeVoiceUpload"
:on-preview="handleVoicePreview"
:file-list="voiceList" {% endraw %}>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传mp3/wav文件且不超过10MB</div>
</el-upload>
<el-dialog :visible.sync="voicePreviewVisible" append-to-body>
<audio controls :src="voicePreviewUrl" style="width: 100%"></audio>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="视频" name="video">
<el-upload
class="upload-demo"
action="/message_push/api/upload"
{% raw %}:on-success="handleVideoSuccess"
:before-upload="beforeVideoUpload"
:on-preview="handleVideoPreview"
:file-list="videoList" {% endraw %}>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传mp4文件且不超过20MB</div>
</el-upload>
<el-dialog :visible.sync="videoPreviewVisible" append-to-body>
<video controls :src="videoPreviewUrl" style="width: 100%"></video>
</el-dialog>
</el-tab-pane>
<el-tab-pane label="链接" name="link">
<el-form-item label="链接标题">
<el-input {% raw %}v-model="taskForm.content_link.title" {% endraw %}></el-input>
@@ -441,6 +471,8 @@ new Vue({
groups: [],
content_text: '',
content_image: '',
content_voice: '',
content_video: '',
content_link: {
title: '',
des: '',
@@ -498,6 +530,12 @@ new Vue({
previewVisible: false,
previewUrl: '',
thumbnailList: [],
voiceList: [],
voicePreviewVisible: false,
voicePreviewUrl: '',
videoList: [],
videoPreviewVisible: false,
videoPreviewUrl: '',
isEdit: false
}
},
@@ -599,6 +637,8 @@ new Vue({
groups: [],
content_text: '',
content_image: '',
content_voice: '',
content_video: '',
content_link: {
title: '',
des: '',
@@ -914,6 +954,76 @@ new Vue({
this.previewVisible = true;
},
// 语音上传相关
handleVoiceSuccess(response, file) {
if (response.success) {
this.taskForm.content_voice = response.data.url;
const fileName = file.name;
this.voiceList = [{
name: fileName,
url: `/static/uploads/${response.data.url.split('/').pop()}`
}];
} else {
this.$message.error('上传失败');
}
},
beforeVoiceUpload(file) {
const isVoice = file.type === 'audio/mp3' || file.type === 'audio/wav';
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isVoice) {
this.$message.error('只能上传mp3/wav文件!');
return false;
}
if (!isLt10M) {
this.$message.error('语音文件大小不能超过 10MB!');
return false;
}
return true;
},
handleVoicePreview(file) {
const fileName = file.url.split('/').pop();
this.voicePreviewUrl = `/static/uploads/${fileName}`;
this.voicePreviewVisible = true;
},
// 视频上传相关
handleVideoSuccess(response, file) {
if (response.success) {
this.taskForm.content_video = response.data.url;
const fileName = file.name;
this.videoList = [{
name: fileName,
url: `/static/uploads/${response.data.url.split('/').pop()}`
}];
} else {
this.$message.error('上传失败');
}
},
beforeVideoUpload(file) {
const isVideo = file.type === 'video/mp4';
const isLt20M = file.size / 1024 / 1024 < 20;
if (!isVideo) {
this.$message.error('只能上传mp4文件!');
return false;
}
if (!isLt20M) {
this.$message.error('视频文件大小不能超过 20MB!');
return false;
}
return true;
},
handleVideoPreview(file) {
const fileName = file.url.split('/').pop();
this.videoPreviewUrl = `/static/uploads/${fileName}`;
this.videoPreviewVisible = true;
},
// 工具函数
getStatusType(status) {
const typeMap = {