Files
qingcheng-Web/src/view/course/courseManagement.vue

514 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 搜索框 -->
<div class="search-box">
<a-form layout="inline">
<a-space>
<a-form-item label="课程名称">
<a-input-search
style="width: 200px"
placeholder="请输入课程名称"
enter-button
@search="handleCourseSearch"
v-model:value="searchCourseName"
class="custom-search"
/>
</a-form-item>
<a-form-item label="课程类别">
<a-select
style="width: 120px"
placeholder="请选择"
v-model:value="searchCourseType"
@change="handleCourseSearch"
>
<a-select-option value="">全部</a-select-option>
<a-select-option value="考公考研">考公考研</a-select-option>
<a-select-option value="自媒体">自媒体</a-select-option>
<a-select-option value="财经">财经</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="上架状态">
<a-select
style="width: 120px"
placeholder="请选择"
v-model:value="searchIsShelves"
@change="handleCourseSearch"
>
<a-select-option value="">全部</a-select-option>
<a-select-option value="true">已上架</a-select-option>
<a-select-option value="false">已下架</a-select-option>
</a-select>
</a-form-item>
<a-button class="custom-button" @click="goAddCourse">新增课程</a-button>
<a-button class="custom-button" @click="reset">重置搜索</a-button>
<a-button
class="custom-button danger"
:disabled="selectedRowKeys.length === 0"
@click="showDeleteConfirm"
>
批量删除
</a-button>
</a-space>
</a-form>
</div>
<!-- 数据-->
<a-table
:columns="columns"
:data-source="tableData"
:scroll="{ x: 1600, y: 550 }"
:loading="loading"
:pagination="pagination"
:row-selection="rowSelection"
bordered
rowKey="id"
@change="handleTableChange"
>
<template #bodyCell="{ column, record }">
<!-- 课程图片 -->
<template v-if="column.key === 'image'">
<a-avatar :src="downLoadImage+record.image" shape="square" :size="64"/>
</template>
<template v-if="column.key === 'originPrice'">
{{ record.originPrice }}
</template>
<template v-if="column.key === 'discountPrice'">
{{ record.discountPrice }}
</template>
<template v-if="column.key === 'orderCount'">
{{ record.orderCount }}
</template>
<template v-if="column.key === 'firstLevelRate'">
{{ record.firstLevelRate }}%
</template>
<template v-if="column.key === 'secondLevelRate'">
{{ record.secondLevelRate }}%
</template>
<template v-if="column.key === 'isShelves'">
<a-tag :color="record.isShelves ? 'green' : 'red'">
{{ record.isShelves ? '已上架' : '已下架' }}
</a-tag>
</template>
<!-- 操作列 -->
<template v-if="column.key === 'action'">
<a-space :size="8">
<a-button
size="small"
danger
@click="deleteCourse(record.id)"
>
删除
</a-button>
<a-button
size="small"
type="link"
@click="showDetails(record.id)"
>
详情
</a-button>
<a-button
size="small"
type="link"
@click="chapterDetails(record.id)"
>
章节
</a-button>
</a-space>
</template>
</template>
</a-table>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import myAxios from "../../api/myAxios.ts";
import { message, Modal } from "ant-design-vue";
import {downLoadImage} from "../../api/ImageUrl.ts";
import router from "../../router";
const loading = ref(false);
const selectedRowKeys = ref<number[]>([]); // 存储选中的行ID
// 搜索参数
const searchCourseName = ref("");
const searchCourseType = ref("");
const searchIsShelves = ref("");
// 分页查询参数
const searchParams = ref({
current: 1,
pageSize: 10,
sortField: "id",
sortOrder: "ascend",
name: "",
type: "",
isShelves: ""
});
// 行选择配置
const rowSelection = ref({
selectedRowKeys: selectedRowKeys,
onChange: (selectedKeys: number[]) => {
selectedRowKeys.value = selectedKeys;
}
});
// 课程表格列定义
const columns = [
{
title: '课程ID',
dataIndex: 'id',
width: 45,
key: 'id',
fixed: 'left',
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '课程图片',
dataIndex: 'image',
key: 'image',
width: 45,
fixed: 'left',
align: 'center'
},
{
title: '课程名称',
dataIndex: 'name',
width: 65,
key: 'name',
fixed: 'left',
align: 'center'
},
{
title: '课程类别',
dataIndex: 'type',
width: 55,
key: 'type',
align: 'center'
},
{
title: '课程原价',
dataIndex: 'originPrice',
width: 55,
key: 'originPrice',
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '折扣价格',
dataIndex: 'discountPrice',
key: 'discountPrice',
width: 55,
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '已下单人数',
dataIndex: 'orderCount',
key: 'orderCount',
width: 55,
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '一级佣金比例',
dataIndex: 'firstLevelRate',
key: 'firstLevelRate',
width: 65,
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '二级佣金比例',
dataIndex: 'secondLevelRate',
key: 'secondLevelRate',
width: 65,
align: 'center',
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '是否上架',
dataIndex: 'isShelves',
key: 'isShelves',
width: 65,
align: 'center'
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 75,
align: 'center'
}
];
// 课程名称搜索方法
const handleCourseSearch = async () => {
searchParams.value.name = searchCourseName.value;
searchParams.value.type = searchCourseType.value;
searchParams.value.isShelves = searchIsShelves.value;
searchParams.value.current = 1; // 重置到第一页
await getCourseList();
};
// 课程分页查询
const getCourseList = async () => {
loading.value = true;
try {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
const res: any = await myAxios.post("/course/page", searchParams.value,
{ headers: { Authorization: storedToken } }
);
if (res.code === 1 && res.data && Array.isArray(res.data.records)) {
tableData.value = res.data.records;
// 同步总条数到分页组件
pagination.value.total = res.data.total;
} else {
message.error(res.message || '请求失败');
}
} catch (error) {
console.error("请求失败:", error);
message.error('获取数据失败');
} finally {
loading.value = false;
}
};
onMounted(getCourseList);
// 分页配置
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number) => `${total}`,
pageSizeOptions: ['10', '20', '50', '100']
});
// 处理分页和排序变化
const handleTableChange = (pag: any, _filters: any, sorter: any) => {
searchParams.value.current = pag.current;
searchParams.value.pageSize = pag.pageSize;
// 处理排序
if (sorter && sorter.field) {
// 获取排序字段使用列的key而不是dataIndex
const sortField = sorter.field;
// 获取排序方向ascend/descend
const sortOrder = sorter.order ? sorter.order : '';
// 更新搜索参数中的排序字段和排序方向
searchParams.value.sortField = sortField;
searchParams.value.sortOrder = sortOrder;
} else {
// 如果没有排序信息,重置为默认排序
searchParams.value.sortField = "id";
searchParams.value.sortOrder = "ascend";
}
// 同步到分页组件
pagination.value.current = pag.current;
pagination.value.pageSize = pag.pageSize;
getCourseList();
};
// 课程接口定义
interface Course {
id: number;
name: string;
type: string;
detail: string;
promoCodeDesc: string;
image: string;
originPrice: number;
discountPrice: number;
orderCount: number;
firstLevelRate: number;
secondLevelRate: number;
isShelves: boolean;
}
const tableData = ref<Course[]>([]);
// 删除课程
const deleteCourse = (id: number) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除该课程吗?删除后数据将无法恢复!',
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
const storedToken = localStorage.getItem('token');
const res: any = await myAxios.post(
"/course/delete",
{ id },
{ headers: { Authorization: storedToken } }
);
if (res.code === 1) {
message.success('删除成功');
await getCourseList();
} else {
message.error(res.message || '删除失败');
}
} catch (error) {
console.error('删除失败:', error);
message.error('删除操作失败');
}
}
});
};
// 批量删除确认弹窗
const showDeleteConfirm = () => {
if (selectedRowKeys.value.length === 0) {
message.warning('请至少选择一门课程');
return;
}
Modal.confirm({
title: '确认批量删除',
content: `确定要删除选中的 ${selectedRowKeys.value.length} 门课程吗?删除后数据将无法恢复!`,
okText: '确认',
cancelText: '取消',
onOk: deleteBatchCourses
});
};
// 批量删除方法
const deleteBatchCourses = async () => {
try {
const storedToken = localStorage.getItem('token');
const res: any = await myAxios.post(
"/course/delBatch",
{ ids: selectedRowKeys.value },
{ headers: { Authorization: storedToken } }
);
if (res.code === 1) {
message.success(`成功删除 ${selectedRowKeys.value.length} 门课程`);
selectedRowKeys.value = []; // 清空选择
await getCourseList(); // 刷新列表
} else {
message.error(res.message || '批量删除失败');
}
} catch (error) {
console.error('批量删除失败:', error);
message.error('批量删除操作失败');
}
};
// 重置搜索条件
const reset = () => {
searchCourseName.value = "";
searchCourseType.value = "";
searchIsShelves.value = "";
selectedRowKeys.value = [];
searchParams.value = {
current: 1,
pageSize: 10,
sortField: "id",
sortOrder: "ascend",
name: "",
type: "",
isShelves: ""
};
getCourseList();
};
// 跳转到新增课程页面
const goAddCourse = () => {
router.push('/addcourse')
}
// 查看课程详情
const showDetails = (id: string) => {
router.push({
path: '/courseDetail',
query: { id: String(id) }
})
}
// 查看章节详情
const chapterDetails = (id: string) => {
router.push({
path: '/chapterDetail',
query: { id: String(id) }
})
}
</script>
<style scoped>
/* 原有样式保持不变 */
.search-box {
margin-bottom: 10px;
}
.custom-button {
background-color: #ffa940;
border-color: #ffa940;
color: #fff;
font-weight: 500;
}
.custom-button:hover,
.custom-button:focus {
background-color: #fa8c16;
border-color: #fa8c16;
color: #fff;
}
.custom-button[disabled] {
background-color: #ffa940;
border-color: #ffa940;
opacity: 0.6;
color: #fff;
}
.custom-button.ant-btn-dangerous {
background-color: #ff4d4f;
border-color: #ff4d4f;
}
.custom-button.ant-btn-dangerous:hover,
.custom-button.ant-btn-dangerous:focus {
background-color: #ff7875;
border-color: #ff7875;
}
.custom-search :deep(.ant-input-search-button) {
background-color: #ffa940;
border-color: #ffa940;
}
.danger {
background-color: #ff4d4f !important;
border-color: #ff4d4f !important;
}
</style>