修改会议管理页面
This commit is contained in:
@@ -45,6 +45,13 @@
|
|||||||
<el-table-column prop="start_time" label="开始时间" width="180" sortable></el-table-column>
|
<el-table-column prop="start_time" label="开始时间" width="180" sortable></el-table-column>
|
||||||
<el-table-column prop="end_time" label="结束时间" width="180" sortable></el-table-column>
|
<el-table-column prop="end_time" label="结束时间" width="180" sortable></el-table-column>
|
||||||
<el-table-column prop="update_time" label="更新时间" width="180" sortable></el-table-column>
|
<el-table-column prop="update_time" label="更新时间" width="180" sortable></el-table-column>
|
||||||
|
<el-table-column label="演讲人员" width="180" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" type="primary" :icon="User" @click="handleManageSpeaker(scope.row)">
|
||||||
|
显示管理演讲人员
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="200" fixed="right">
|
<el-table-column label="操作" width="200" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@@ -61,6 +68,78 @@
|
|||||||
<div class="content-full text-gray-700 leading-relaxed whitespace-pre-wrap" v-html="currentMeeting.intro || '当前会议暂无简介'">
|
<div class="content-full text-gray-700 leading-relaxed whitespace-pre-wrap" v-html="currentMeeting.intro || '当前会议暂无简介'">
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 演讲人员管理弹窗 -->
|
||||||
|
<el-dialog v-model="speakerDialogVisible" title="演讲人员管理" :width="`800px`" :before-close="handleCloseSpeakerDialog">
|
||||||
|
<div class="mb-4 flex justify-end">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="handleAddSpeaker">新增演讲嘉宾</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 演讲嘉宾列表 -->
|
||||||
|
<el-table :data="speakerList" style="width: 100%" row-key="id" border>
|
||||||
|
<el-table-column prop="name" label="嘉宾姓名" min-width="120"></el-table-column>
|
||||||
|
<el-table-column prop="title" label="嘉宾头衔" min-width="150" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column label="嘉宾头像" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-image style="width: 40px; height: 40px; border-radius: 50%;" :src="scope.row.avatar || ''" fit="cover">
|
||||||
|
<template #error>
|
||||||
|
<div class="flex items-center justify-center w-full h-full bg-gray-100 text-gray-500 text-xs">无头像</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sort" label="排序" width="80" align="center"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="140" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="mini" type="primary" :icon="Edit" @click="handleEditSpeaker(scope.row)">编辑</el-button>
|
||||||
|
<el-button size="mini" type="danger" :icon="Delete" @click="handleDeleteSpeaker(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 新增/编辑演讲嘉宾表单 -->
|
||||||
|
<el-drawer v-model="speakerDrawerVisible" :title="currentSpeaker.id ? '编辑演讲嘉宾' : '新增演讲嘉宾'" direction="rtl" size="50%" destroy-on-close>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label required">嘉宾姓名</label>
|
||||||
|
<el-input v-model="currentSpeaker.name" placeholder="请输入嘉宾姓名" clearable :disabled="isSpeakerSubmitting" maxlength="100" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label">嘉宾头衔</label>
|
||||||
|
<el-input v-model="currentSpeaker.title" placeholder="请输入嘉宾头衔(可选)" clearable :disabled="isSpeakerSubmitting" maxlength="200" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label">嘉宾头像</label>
|
||||||
|
<el-upload action="http://localhost:8080/api/upload/image" name="image" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :on-error="handleAvatarError" :disabled="isSpeakerSubmitting">
|
||||||
|
<img v-if="currentSpeaker.avatar" :src="currentSpeaker.avatar" class="avatar-preview" alt="头像"/>
|
||||||
|
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||||
|
</el-upload>
|
||||||
|
<p class="text-xs text-gray-500 mt-2">支持JPG/PNG格式,大小不超过5MB(可选)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label">嘉宾简介</label>
|
||||||
|
<el-input v-model="currentSpeaker.intro" type="textarea" :rows="5" placeholder="请输入嘉宾简介(可选)" clearable :disabled="isSpeakerSubmitting" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label">排序(数字越小越靠前)</label>
|
||||||
|
<el-input v-model.number="currentSpeaker.sort" placeholder="请输入排序号" clearable :disabled="isSpeakerSubmitting" type="number" min="0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div style="flex: auto">
|
||||||
|
<el-button @click="speakerDrawerVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submitSpeaker" :loading="isSpeakerSubmitting">
|
||||||
|
{{ currentSpeaker.id ? '更新嘉宾' : '创建嘉宾' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 分页组件 -->
|
<!-- 分页组件 -->
|
||||||
@@ -102,12 +181,10 @@
|
|||||||
<div class="form-group time-group grid grid-cols-2 gap-4">
|
<div class="form-group time-group grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="form-label required">开始时间</label>
|
<label class="form-label required">开始时间</label>
|
||||||
<!-- 修复:添加 timezone="GMT+8" 明确指定东八区 -->
|
|
||||||
<el-date-picker v-model="form.start_time" type="datetime" placeholder="选择会议开始时间" :disabled="isSubmitting" value-format="YYYY-MM-DD HH:mm:ss" timezone="GMT+8" />
|
<el-date-picker v-model="form.start_time" type="datetime" placeholder="选择会议开始时间" :disabled="isSubmitting" value-format="YYYY-MM-DD HH:mm:ss" timezone="GMT+8" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="form-label required">结束时间</label>
|
<label class="form-label required">结束时间</label>
|
||||||
<!-- 修复:添加 timezone="GMT+8" 明确指定东八区 -->
|
|
||||||
<el-date-picker v-model="form.end_time" type="datetime" placeholder="选择会议结束时间" :disabled="isSubmitting" value-format="YYYY-MM-DD HH:mm:ss" timezone="GMT+8" />
|
<el-date-picker v-model="form.end_time" type="datetime" placeholder="选择会议结束时间" :disabled="isSubmitting" value-format="YYYY-MM-DD HH:mm:ss" timezone="GMT+8" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,14 +211,14 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, watch, nextTick } from 'vue';
|
import { ref, onMounted, watch, nextTick } from 'vue';
|
||||||
import { ElMessage, ElMessageBox, ElDialog, ElConfigProvider, ElDrawer, ElInput, ElUpload, ElDatePicker } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { Edit, Delete, Plus } from '@element-plus/icons-vue';
|
import { Edit, Delete, Plus, User } from '@element-plus/icons-vue';
|
||||||
import Quill from 'quill';
|
import Quill from 'quill';
|
||||||
import 'quill/dist/quill.snow.css';
|
import 'quill/dist/quill.snow.css';
|
||||||
import type { UploadProps } from 'element-plus';
|
import type { UploadProps } from 'element-plus';
|
||||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
|
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
|
||||||
|
|
||||||
// --- 类型定义(对齐meeting表结构)---
|
// --- 类型定义 ---
|
||||||
interface Meeting {
|
interface Meeting {
|
||||||
id: number;
|
id: number;
|
||||||
theme: string;
|
theme: string;
|
||||||
@@ -156,6 +233,17 @@ interface Meeting {
|
|||||||
is_delete: number;
|
is_delete: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 演讲嘉宾类型(对齐meeting_speaker表结构)
|
||||||
|
interface MeetingSpeaker {
|
||||||
|
id: number | null;
|
||||||
|
meeting_id: number;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
avatar: string;
|
||||||
|
intro: string;
|
||||||
|
sort: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface ListMeetingReq {
|
interface ListMeetingReq {
|
||||||
page: number;
|
page: number;
|
||||||
page_size: number;
|
page_size: number;
|
||||||
@@ -179,33 +267,32 @@ const currentMeeting = ref<Meeting>({
|
|||||||
create_time: '', update_time: '', is_delete: 0
|
create_time: '', update_time: '', is_delete: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- 获取会议列表(对齐GET /api/meetings 分页接口)---
|
// --- 获取会议列表 ---
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
// 构造请求体(POST请求通过body传递分页参数)
|
|
||||||
const reqData = {
|
const reqData = {
|
||||||
page: currentPage.value, // 对应分页页码
|
page: currentPage.value,
|
||||||
page_size: pageSize.value // 对应每页条数
|
page_size: pageSize.value
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/meetings/list`, {
|
const response = await fetch(`${API_BASE_URL}/meetings/list`, {
|
||||||
method: 'POST', // 改为POST方法
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json' // 明确JSON格式
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(reqData) // 发送JSON格式的请求体
|
body: JSON.stringify(reqData)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) throw new Error(`请求失败!状态码:${response.status}`);
|
if (!response.ok) throw new Error(`请求失败!状态码:${response.status}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
tableData.value = data.meetings || []; // 假设响应体中会议列表字段为meetings
|
tableData.value = data.meetings || [];
|
||||||
total.value = data.total || 0; // 假设响应体中总条数字段为total
|
total.value = data.total || 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ERROR] 获取会议列表失败:', error);
|
console.error('[ERROR] 获取会议列表失败:', error);
|
||||||
ElMessage.error('获取会议列表失败,请检查接口或网络!');
|
ElMessage.error('获取会议列表失败,请检查接口或网络!');
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false; // 无论成功失败,都关闭加载状态
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -269,7 +356,7 @@ const handleCurrentChange = (val: number) => {
|
|||||||
const drawerVisible = ref(false);
|
const drawerVisible = ref(false);
|
||||||
const isSubmitting = ref(false);
|
const isSubmitting = ref(false);
|
||||||
|
|
||||||
// --- 表单默认值(对齐meeting表字段)---
|
// --- 表单默认值 ---
|
||||||
const defaultFormState = () => ({
|
const defaultFormState = () => ({
|
||||||
id: null as number | null,
|
id: null as number | null,
|
||||||
theme: '',
|
theme: '',
|
||||||
@@ -278,11 +365,11 @@ const defaultFormState = () => ({
|
|||||||
schedule_image_url: '',
|
schedule_image_url: '',
|
||||||
start_time: '',
|
start_time: '',
|
||||||
end_time: '',
|
end_time: '',
|
||||||
intro: '', // 对应富文本内容
|
intro: '',
|
||||||
});
|
});
|
||||||
const form = ref(defaultFormState());
|
const form = ref(defaultFormState());
|
||||||
|
|
||||||
// --- 富文本编辑器(用于会议简介)---
|
// --- 富文本编辑器 ---
|
||||||
const editorRef = ref<HTMLDivElement | null>(null);
|
const editorRef = ref<HTMLDivElement | null>(null);
|
||||||
let quillInstance: Quill | null = null;
|
let quillInstance: Quill | null = null;
|
||||||
|
|
||||||
@@ -305,12 +392,10 @@ const initQuillEditor = () => {
|
|||||||
placeholder: '请输入会议简介(支持Markdown格式)...'
|
placeholder: '请输入会议简介(支持Markdown格式)...'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 编辑时回显内容
|
|
||||||
if (form.value.intro) {
|
if (form.value.intro) {
|
||||||
quillInstance.root.innerHTML = form.value.intro;
|
quillInstance.root.innerHTML = form.value.intro;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听内容变化
|
|
||||||
quillInstance.on('text-change', (_, __, source) => {
|
quillInstance.on('text-change', (_, __, source) => {
|
||||||
if (source === 'user') {
|
if (source === 'user') {
|
||||||
form.value.intro = quillInstance?.root.innerHTML || '';
|
form.value.intro = quillInstance?.root.innerHTML || '';
|
||||||
@@ -326,7 +411,6 @@ watch(drawerVisible, (visible) => {
|
|||||||
initQuillEditor();
|
initQuillEditor();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 关闭时重置编辑器和表单
|
|
||||||
quillInstance = null;
|
quillInstance = null;
|
||||||
form.value = defaultFormState();
|
form.value = defaultFormState();
|
||||||
}
|
}
|
||||||
@@ -357,9 +441,8 @@ const handleDrawerClose = () => {
|
|||||||
drawerVisible.value = false;
|
drawerVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 提交会议(新增/更新)---
|
// --- 提交会议 ---
|
||||||
const submitMeeting = async () => {
|
const submitMeeting = async () => {
|
||||||
// 表单校验
|
|
||||||
if (!form.value.theme.trim()) {
|
if (!form.value.theme.trim()) {
|
||||||
ElMessage.warning('请输入会议主题');
|
ElMessage.warning('请输入会议主题');
|
||||||
return;
|
return;
|
||||||
@@ -372,7 +455,7 @@ const submitMeeting = async () => {
|
|||||||
ElMessage.warning('请选择会议结束时间');
|
ElMessage.warning('请选择会议结束时间');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 修复:使用日期对象直接比较,避免字符串格式问题
|
|
||||||
const startTime = new Date(form.value.start_time);
|
const startTime = new Date(form.value.start_time);
|
||||||
const endTime = new Date(form.value.end_time);
|
const endTime = new Date(form.value.end_time);
|
||||||
if (endTime < startTime) {
|
if (endTime < startTime) {
|
||||||
@@ -395,7 +478,6 @@ const submitMeeting = async () => {
|
|||||||
let url = `${API_BASE_URL}/meetings`;
|
let url = `${API_BASE_URL}/meetings`;
|
||||||
let method = 'POST';
|
let method = 'POST';
|
||||||
|
|
||||||
// 编辑模式:添加ID参数
|
|
||||||
if (form.value.id) {
|
if (form.value.id) {
|
||||||
submitData['id'] = form.value.id;
|
submitData['id'] = form.value.id;
|
||||||
method = 'PUT';
|
method = 'PUT';
|
||||||
@@ -482,7 +564,7 @@ const handleScheduleError = () => {
|
|||||||
ElMessage.error('日程图上传失败');
|
ElMessage.error('日程图上传失败');
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 编辑器图片上传(简介中插入图片)---
|
// --- 编辑器图片上传 ---
|
||||||
function handleEditorImageUpload() {
|
function handleEditorImageUpload() {
|
||||||
const fileInput = document.createElement('input');
|
const fileInput = document.createElement('input');
|
||||||
fileInput.type = 'file';
|
fileInput.type = 'file';
|
||||||
@@ -572,10 +654,174 @@ function base64ToBlob(base64: string): Blob | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// 演讲人员管理相关逻辑
|
||||||
|
// =================================================================
|
||||||
|
// --- 演讲人员管理相关状态 ---
|
||||||
|
const speakerDialogVisible = ref(false); // 演讲人员管理弹窗
|
||||||
|
const speakerDrawerVisible = ref(false); // 新增/编辑演讲嘉宾抽屉
|
||||||
|
const isSpeakerSubmitting = ref(false); // 演讲嘉宾提交加载状态
|
||||||
|
const currentMeetingId = ref(0); // 当前操作的会议ID
|
||||||
|
const speakerList = ref<MeetingSpeaker[]>([]); // 演讲嘉宾列表
|
||||||
|
const currentSpeaker = ref<MeetingSpeaker>({
|
||||||
|
id: null,
|
||||||
|
meeting_id: 0,
|
||||||
|
name: '',
|
||||||
|
title: '',
|
||||||
|
avatar: '',
|
||||||
|
intro: '',
|
||||||
|
sort: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- 打开演讲人员管理弹窗 ---
|
||||||
|
const handleManageSpeaker = async (row: Meeting) => {
|
||||||
|
currentMeetingId.value = row.id;
|
||||||
|
speakerDialogVisible.value = true;
|
||||||
|
await fetchSpeakerList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 获取演讲嘉宾列表 ---
|
||||||
|
const fetchSpeakerList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/meetings/${currentMeetingId.value}/speakers`, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('获取演讲嘉宾列表失败');
|
||||||
|
const data = await response.json();
|
||||||
|
speakerList.value = data.speakers || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ERROR] 获取演讲嘉宾列表失败:', error);
|
||||||
|
ElMessage.error('获取演讲嘉宾列表失败,请重试!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 关闭演讲人员管理弹窗 ---
|
||||||
|
const handleCloseSpeakerDialog = () => {
|
||||||
|
speakerDialogVisible.value = false;
|
||||||
|
speakerList.value = [];
|
||||||
|
currentMeetingId.value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 新增演讲嘉宾 ---
|
||||||
|
const handleAddSpeaker = () => {
|
||||||
|
currentSpeaker.value = {
|
||||||
|
id: null,
|
||||||
|
meeting_id: currentMeetingId.value,
|
||||||
|
name: '',
|
||||||
|
title: '',
|
||||||
|
avatar: '',
|
||||||
|
intro: '',
|
||||||
|
sort: 0
|
||||||
|
};
|
||||||
|
speakerDrawerVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 编辑演讲嘉宾 ---
|
||||||
|
const handleEditSpeaker = (row: MeetingSpeaker) => {
|
||||||
|
currentSpeaker.value = { ...row };
|
||||||
|
speakerDrawerVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 删除演讲嘉宾 ---
|
||||||
|
const handleDeleteSpeaker = (row: MeetingSpeaker) => {
|
||||||
|
ElMessageBox.confirm(`确定要删除嘉宾《${row.name}》吗?此操作无法撤销!`, '警告', {
|
||||||
|
confirmButtonText: '确定删除',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/speakers/${row.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('删除失败');
|
||||||
|
ElMessage.success('删除成功!');
|
||||||
|
fetchSpeakerList();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ERROR] 删除演讲嘉宾失败:', error);
|
||||||
|
ElMessage.error(`删除失败: ${(error as Error).message}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage.info('已取消删除');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 提交演讲嘉宾 ---
|
||||||
|
const submitSpeaker = async () => {
|
||||||
|
if (!currentSpeaker.value.name.trim()) {
|
||||||
|
ElMessage.warning('请输入嘉宾姓名');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSpeakerSubmitting.value = true;
|
||||||
|
const submitData = { ...currentSpeaker.value };
|
||||||
|
|
||||||
|
try {
|
||||||
|
let url = `${API_BASE_URL}/speakers`;
|
||||||
|
let method = 'POST';
|
||||||
|
|
||||||
|
if (submitData.id) {
|
||||||
|
method = 'PUT';
|
||||||
|
url = `${url}/${submitData.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(submitData),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errData = await response.json().catch(() => null);
|
||||||
|
throw new Error(errData?.message || (submitData.id ? '更新失败' : '创建失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.success(submitData.id ? '嘉宾更新成功!' : '嘉宾创建成功!');
|
||||||
|
speakerDrawerVisible.value = false;
|
||||||
|
fetchSpeakerList();
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
ElMessage.error(`${submitData.id ? '更新' : '创建'}失败: ${err.message}`);
|
||||||
|
} finally {
|
||||||
|
isSpeakerSubmitting.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- 嘉宾头像上传相关 ---
|
||||||
|
const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
|
||||||
|
const ossUrl = response.data?.url;
|
||||||
|
if (ossUrl) {
|
||||||
|
currentSpeaker.value.avatar = ossUrl;
|
||||||
|
ElMessage.success('头像上传成功');
|
||||||
|
} else {
|
||||||
|
ElMessage.error('头像上传失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||||
|
const isImage = rawFile.type.startsWith('image/');
|
||||||
|
if (!isImage) {
|
||||||
|
ElMessage.error('请上传图片文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isLt5M = rawFile.size / 1024 / 1024 < 5;
|
||||||
|
if (!isLt5M) {
|
||||||
|
ElMessage.error('图片大小不能超过 5MB!');
|
||||||
|
}
|
||||||
|
return isImage && isLt5M;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAvatarError = () => {
|
||||||
|
ElMessage.error('头像上传失败');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 基础样式复用,调整适配会议组件 */
|
/* 基础样式复用 */
|
||||||
.el-table .el-table__cell { vertical-align: middle; }
|
.el-table .el-table__cell { vertical-align: middle; }
|
||||||
.el-table__header-wrapper th { background-color: #fafafa !important; font-weight: 600; color: #333; }
|
.el-table__header-wrapper th { background-color: #fafafa !important; font-weight: 600; color: #333; }
|
||||||
.action-buttons .el-button { margin-right: 8px; }
|
.action-buttons .el-button { margin-right: 8px; }
|
||||||
@@ -612,4 +858,9 @@ function base64ToBlob(base64: string): Blob | null {
|
|||||||
|
|
||||||
/* 日期选择器 */
|
/* 日期选择器 */
|
||||||
.el-date-picker { width: 100%; }
|
.el-date-picker { width: 100%; }
|
||||||
</style>
|
|
||||||
|
/* 演讲人员管理样式 */
|
||||||
|
.avatar-uploader-icon { font-size: 24px; color: #8c939d; width: 80px; height: 80px; text-align: center; line-height: 80px; border: 1px dashed #dcdfe6; border-radius: 6px; }
|
||||||
|
.avatar-preview { width: 80px; height: 80px; display: block; object-fit: cover; border-radius: 6px; }
|
||||||
|
.el-dialog__body .el-table { margin-bottom: 16px; }
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user