课程管理的增删改查以及批量删除以及富文本加密以及名称查询
This commit is contained in:
535
src/view/course/courseManagement.vue
Normal file
535
src/view/course/courseManagement.vue
Normal file
@ -0,0 +1,535 @@
|
||||
<template>
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<a-form layout="inline">
|
||||
<a-space>
|
||||
<a-form-item label="课程名称">
|
||||
<a-input-search
|
||||
style="width: 300px"
|
||||
placeholder="请输入课程名称"
|
||||
enter-button
|
||||
@search="handleCourseSearch"
|
||||
v-model:value="searchCourseName"
|
||||
class="custom-search"
|
||||
/>
|
||||
</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: 1500, 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 === '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 total = ref(0);
|
||||
const selectedRowKeys = ref<number[]>([]); // 存储选中的行ID
|
||||
|
||||
const searchCourseName = ref(""); // 改为项目名称搜索参数
|
||||
const searchParams = ref({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
sortField: "id",
|
||||
sortOrder: "ascend",
|
||||
userRole: null,
|
||||
name: "",
|
||||
type:""
|
||||
});
|
||||
|
||||
// 行选择配置
|
||||
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: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 75,
|
||||
align: 'center'
|
||||
}
|
||||
];
|
||||
|
||||
interface ProjectRecord {
|
||||
// 这里根据实际数据结构定义属性
|
||||
superHostList?: string[];
|
||||
// 其他属性...
|
||||
}
|
||||
// 项目名称搜索方法
|
||||
const handleCourseSearch = async () => {
|
||||
// 将搜索参数同步到分页查询参数
|
||||
searchParams.value.name = searchCourseName.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,
|
||||
projectName: searchParams.value.projectName
|
||||
},
|
||||
{ headers: { Authorization: storedToken } }
|
||||
);
|
||||
console.log(res)
|
||||
if (res.code === 1 && res.data && Array.isArray(res.data.records)) {
|
||||
tableData.value = res.data.records.map((item: ProjectRecord) => ({
|
||||
...item,
|
||||
superUserList: item.superHostList? item.superHostList.join(', ') : '无'
|
||||
}));
|
||||
// 同步总条数到分页组件
|
||||
total.value = res.data.total;
|
||||
pagination.value.total = res.data.total; // 新增此行
|
||||
pagination.value.current = searchParams.value.current; // 同步当前页
|
||||
} 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, _: any, sorter: any) => {
|
||||
// 处理排序参数
|
||||
let sortField = "id"; // 默认排序字段
|
||||
let sortOrder = "ascend"; // 默认排序方式
|
||||
if (sorter.field) {
|
||||
sortField = sorter.field;
|
||||
sortOrder = sorter.order;
|
||||
}
|
||||
searchParams.value = {
|
||||
...searchParams.value,
|
||||
current: pag.current,
|
||||
pageSize: pag.pageSize,
|
||||
sortField: sortField, // 设置排序字段
|
||||
sortOrder: sortOrder
|
||||
};
|
||||
|
||||
// 同步到分页组件
|
||||
pagination.value = {
|
||||
...pagination.value,
|
||||
current: pag.current,
|
||||
pageSize: pag.pageSize
|
||||
};
|
||||
|
||||
getCourseList();
|
||||
};
|
||||
|
||||
// ID查询方法
|
||||
interface Project {
|
||||
id: number;
|
||||
projectName: string;
|
||||
projectImage: string;
|
||||
projectSettlementCycle: number;
|
||||
maxPromoterCount: number;
|
||||
projectStatus: string;
|
||||
projectDescription: string;
|
||||
settlementDesc: string;
|
||||
// 其他可能存在的属性根据实际情况补充
|
||||
}
|
||||
|
||||
const tableData = ref<Project[]>([]);
|
||||
|
||||
|
||||
// 删除项目 - 添加确认弹窗
|
||||
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,
|
||||
'AfterScript': 'required-script'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (res.code === 1) {
|
||||
message.success('删除成功');
|
||||
await getCourseList();
|
||||
} else {
|
||||
message.error(res.message || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
message.error('删除操作失败');
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
// 用户点击取消,不做操作
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除确认弹窗
|
||||
const showDeleteConfirm = () => {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
message.warning('请至少选择一门课程');
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: '确认批量删除',
|
||||
content: `确定要删除选中的 ${selectedRowKeys.value.length} 门课程吗?删除后数据将无法恢复!`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
await deleteBatchCourses();
|
||||
},
|
||||
onCancel() {
|
||||
// 用户点击取消,不做操作
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除方法
|
||||
const deleteBatchCourses = async () => {
|
||||
try {
|
||||
const storedToken = localStorage.getItem('token');
|
||||
const res: any = await myAxios.post(
|
||||
"/course/delBatch",
|
||||
{ ids: selectedRowKeys.value },
|
||||
{
|
||||
headers: {
|
||||
Authorization: storedToken,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 = "";
|
||||
selectedRowKeys.value = [];
|
||||
searchParams.value = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
sortField: "id",
|
||||
sortOrder: "ascend",
|
||||
userRole: null,
|
||||
name: "",
|
||||
type:""
|
||||
};
|
||||
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>
|
||||
/* 分割线样式 */
|
||||
:deep(.ant-divider-vertical) {
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
height: 1.2em;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
:deep(.ant-descriptions-item-label) {
|
||||
font-weight: 600;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.info-card :deep(.ant-card-head) {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
/* 角色颜色映射 */
|
||||
:root {
|
||||
--role-user: #87d068;
|
||||
--role-admin: #f50;
|
||||
--role-boss: #722ed1;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 批量删除按钮样式 */
|
||||
.danger {
|
||||
background-color: #ff4d4f !important;
|
||||
border-color: #ff4d4f !important;
|
||||
}
|
||||
|
||||
.danger:hover,
|
||||
.danger:focus {
|
||||
background-color: #ff7875 !important;
|
||||
border-color: #ff7875 !important;
|
||||
}
|
||||
|
||||
/*橙色按钮*/
|
||||
.custom-button {
|
||||
background-color: #ffa940;
|
||||
border-color: #ffa940;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.custom-button:hover,
|
||||
.custom-button:focus {
|
||||
background-color: #ffa940;
|
||||
border-color: #ffa940;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 保持原有的其他样式不变 */
|
||||
.search-box {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.custom-search :deep(.ant-input-search-button) {
|
||||
background-color: #ffa940;
|
||||
border-color: #ffa940;
|
||||
}
|
||||
|
||||
.custom-search :deep(.ant-input-search-button:hover),
|
||||
.custom-search :deep(.ant-input-search-button:focus) {
|
||||
background-color: #fa8c16;
|
||||
border-color: #fa8c16;
|
||||
}
|
||||
|
||||
/* 保持输入框原有样式不变 */
|
||||
.custom-search :deep(.ant-input) {
|
||||
border-right-color: #ffa940;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user