| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <template>
- <div class="course-list-container">
- <h2 class="page-title">课程管理</h2>
-
- <el-card shadow="never" class="toolbar-card">
- <el-button type="primary" icon="Plus" @click="$router.push('/course/create')">新增课程</el-button>
- <el-button icon="Refresh" @click="loadList">刷新</el-button>
- </el-card>
-
- <el-card shadow="never" class="table-card">
- <el-table v-loading="loading" :data="list" stripe>
- <template #empty>
- <div class="custom-empty-state">
- <el-icon class="empty-icon"><Reading /></el-icon>
- <div class="empty-text">暂无课程数据</div>
- <div class="empty-hint">点击"新增课程"按钮开始创建</div>
- </div>
- </template>
- <el-table-column type="index" label="序号" width="60" />
- <el-table-column label="封面" width="100">
- <template #default="{ row }">
- <div v-if="row.coverImage || row.cover_image || row.cover" class="cover-wrapper">
- <el-image
- :src="row.coverImage || row.cover_image || row.cover"
- fit="cover"
- class="course-cover"
- :preview-src-list="[row.coverImage || row.cover_image || row.cover]"
- preview-teleported
- hide-on-click-modal
- >
- <template #error>
- <div class="image-slot">
- <el-icon><Picture /></el-icon>
- </div>
- </template>
- </el-image>
- </div>
- <div v-else class="image-slot">
- <el-icon><Picture /></el-icon>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="name" label="课程名称" min-width="200">
- <template #default="{ row }">
- <span class="data-highlight text-ellipsis-line" :title="row.name">
- {{ row.name }}
- </span>
- </template>
- </el-table-column>
- <el-table-column prop="categoryName" label="分类" width="120">
- <template #default="{ row }">
- <span class="data-meta">{{ row.categoryName || row.category_name || '-' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="instructor" label="讲师" width="120">
- <template #default="{ row }">
- <span class="data-meta">{{ row.instructor || row.teacher_name || row.teacherName || '-' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="price" label="价格" width="110" align="right">
- <template #default="{ row }">
- <span v-if="row.price > 0" class="amount-text">¥{{ row.price }}</span>
- <el-tag v-else class="status-success" size="small" effect="light">免费</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="duration" label="时长(分钟)" width="110" align="center">
- <template #default="{ row }">
- <span class="data-meta">{{ row.duration || '-' }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="participants" label="报名人数" width="100" align="center">
- <template #default="{ row }">
- <span class="number-emphasis">{{ row.participants || 0 }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="rating" label="评分" width="80" align="center">
- <template #default="{ row }">
- <span class="number-emphasis" style="color: var(--warning-color);">
- {{ row.rating || '-' }}
- </span>
- </template>
- </el-table-column>
- <el-table-column prop="isRecommended" label="推荐" width="90" align="center">
- <template #default="{ row }">
- <el-tag
- :class="row.isRecommended === 1 ? 'hot-tag' : 'status-pending'"
- size="small"
- effect="light"
- >
- {{ row.isRecommended === 1 ? '推荐' : '普通' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="状态" width="90" align="center">
- <template #default="{ row }">
- <el-switch
- v-model="row.status"
- :active-value="1"
- :inactive-value="0"
- active-color="var(--success-color)"
- inactive-color="var(--border-dark)"
- @change="handleStatusChange(row)"
- />
- </template>
- </el-table-column>
- <el-table-column label="操作" width="220" fixed="right">
- <template #default="{ row }">
- <el-button type="primary" size="small" link @click="$router.push(`/course/detail/${row.id}`)">详情</el-button>
- <el-button type="primary" size="small" link @click="$router.push(`/course/edit/${row.id}`)">编辑</el-button>
- <el-button type="danger" size="small" link @click="handleDelete(row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <div class="pagination-container">
- <el-pagination
- v-model:current-page="currentPage"
- v-model:page-size="pageSize"
- :total="total"
- layout="total, sizes, prev, pager, next"
- @size-change="loadList"
- @current-change="loadList"
- />
- </div>
- </el-card>
- </div>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { Picture, Reading } from '@element-plus/icons-vue'
- import request from '@/utils/request'
- import { API_ENDPOINTS } from '@/config/api'
- const loading = ref(false)
- const currentPage = ref(1)
- const pageSize = ref(10)
- const total = ref(0)
- const list = ref([])
- const loadList = async () => {
- loading.value = true
- try {
- const response = await request.get(API_ENDPOINTS.COURSE_LIST, {
- params: { page: currentPage.value, pageSize: pageSize.value }
- })
- if (response.code === 200) {
- // 后端返回格式: {list: [], total: xx}
- list.value = response.data.list || []
- total.value = response.data.total || 0
- }
- } catch (error) {
- console.error('加载课程列表失败:', error)
- ElMessage.error('加载课程列表失败')
- } finally {
- loading.value = false
- }
- }
- const handleStatusChange = async (row) => {
- if (!row || !row.id) {
- ElMessage.error('课程ID不存在,无法更新状态')
- return
- }
-
- try {
- const response = await request.put(`${API_ENDPOINTS.COURSE_UPDATE}/${row.id}`, {
- status: row.status
- })
- if (response.code === 200) {
- ElMessage.success('状态更新成功')
- } else {
- row.status = row.status === 1 ? 0 : 1
- ElMessage.error(response.message || '状态更新失败')
- }
- } catch (error) {
- console.error('状态更新失败:', error)
- row.status = row.status === 1 ? 0 : 1
- ElMessage.error('状态更新失败')
- }
- }
- const handleDelete = async (row) => {
- try {
- await ElMessageBox.confirm('确定要删除这个课程吗?', '提示', { type: 'warning' })
- const response = await request.delete(`${API_ENDPOINTS.COURSE_DELETE}/${row.id}`)
- if (response.code === 200) {
- ElMessage.success('删除成功')
- loadList()
- }
- } catch (error) {
- if (error !== 'cancel') console.error('删除失败:', error)
- }
- }
- onMounted(() => loadList())
- </script>
- <style scoped>
- @import '@/assets/list-common.css';
- .course-list-container {
- padding: 0;
- }
- .cover-wrapper {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .course-cover {
- width: 80px;
- height: 60px;
- border-radius: var(--radius-base);
- cursor: pointer;
- border: 2px solid var(--border-color);
- transition: all var(--transition-base);
- box-shadow: var(--shadow-sm);
- }
- .course-cover:hover {
- border-color: var(--primary-color);
- transform: scale(1.05);
- box-shadow: var(--shadow-md);
- }
- .image-slot {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 80px;
- height: 60px;
- background-color: var(--bg-tertiary);
- color: var(--text-tertiary);
- font-size: 24px;
- border-radius: var(--radius-base);
- border: 1px solid var(--border-color);
- }
- </style>
|