| | |
| | | <template> |
| | | <div class="myBook"> |
| | | <div class="myBook_header"> |
| | | <div class="myBook_header_title">我的班级</div> |
| | | <div class="coursePage"> |
| | | <div class="personalPage-title">我的班级</div> |
| | | <div class="tabs"> |
| | | <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> |
| | | <el-tab-pane |
| | | :label="'当前班级(' + calssList.length + ')'" |
| | | name="1" |
| | | ></el-tab-pane> |
| | | <!-- <el-tab-pane label="历史班级(5)" name="2"></el-tab-pane> --> |
| | | </el-tabs> |
| | | </div> |
| | | <div class="headerBox"> |
| | | <div class="searchBox"> |
| | | <el-input |
| | | v-model="searchKey" |
| | | clearable |
| | | @clear="searchList()" |
| | | placeholder="请输入关键字" |
| | | > |
| | | <template #append> |
| | | <el-button |
| | | type="primary" |
| | | class="searchBtn" |
| | | @click="searchList()" |
| | | :icon="Search" |
| | | /> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | <el-button type="primary" class="applyStartClasses" @click="openJoin()" |
| | | >加入班级</el-button |
| | | > |
| | | <el-dialog v-model="dialogVisible" title="加入班级" width="500"> |
| | | <div class="codeContent"> |
| | | <span>邀请码:</span> |
| | | <el-input style="width: 330px" v-model="codeText" placeholder="请输入邀请码" /> |
| | | <el-button type="primary" @click="getClassDetail">确认</el-button> |
| | | </div> |
| | | <div class="classInfo" v-if="classDetail?.name"> |
| | | <div class="itemCon"> |
| | | <span>班级名称:</span> |
| | | <span>{{ classDetail.name }}</span> |
| | | </div> |
| | | <div class="itemCon"> |
| | | <span>班级人数:</span> |
| | | <span>{{ classDetail.memberCount }} / {{ classDetail.maxUserCount }}</span> |
| | | </div> |
| | | <div class="itemCon"> |
| | | <span>开课时间:</span> |
| | | <span |
| | | >{{ moment(classDetail.beginDate).format("YYYY-MM-DD") }} - |
| | | {{ moment(classDetail.endDate).format("YYYY-MM-DD") }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false"> 取消 </el-button> |
| | | <el-button type="primary" @click="joinClass()"> 确认 </el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | <div class="courseListBox" v-if="calssList.length > 0 && !isLoading"> |
| | | <div class="courseItem" v-for="(item, index) in calssList" :key="index"> |
| | | <div class="itemHeader"> |
| | | <div class="title">{{ item.name }}</div> |
| | | <div class="courseId">(ID:{{ item.id }})</div> |
| | | <!-- <div class="copyIdBtn" @click="copy(item.refCode)">复制邀请码</div> --> |
| | | </div> |
| | | <div class="itemInfo" @click="goClassManage(item)"> |
| | | <div class="infoBox"> |
| | | <p> |
| | | 状态:<span v-if="item.userState == 'WaitValid'" style="color: #ef9f29"> |
| | | 审核中 </span |
| | | ><span v-if="item.userState == 'Normal'" style="color: #1dbd11"> |
| | | 进行中 </span |
| | | ><span v-if="item.userState == 'Reject'" style="color: red"> 未通过 </span> |
| | | </p> |
| | | <p>班级人数:{{ item.memberCount }} / {{ item.maxUserCount }}</p> |
| | | <p>有效期:{{ item.classTime }}</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <el-empty description="暂无数据" v-if="calssList.length == 0 && !isLoading" /> |
| | | <div style="min-height: 200px" v-if="isLoading" v-loading="isLoading"></div> |
| | | <div class="pageBox"> |
| | | <el-pagination |
| | | v-model:current-page="pages.currentPage" |
| | | :page-size="pages.pageSize" |
| | | :size="'small'" |
| | | :disabled="pages.count <= 1" |
| | | layout="total, prev, pager, next" |
| | | :total="pages.count" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { reactive, ref, onMounted, inject, watch } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { getPublicImage } from "@/assets/js/middleGround/tool.js"; |
| | | import moment from "moment"; |
| | | import useClipboard from "vue-clipboard3"; |
| | | |
| | | const { toClipboard } = useClipboard(); |
| | | const MG: any = inject("MG"); |
| | | const config: any = inject("config"); |
| | | const router = useRouter(); |
| | | const activeName = ref("1"); |
| | | |
| | | interface ClassItem { |
| | | id: string; |
| | | name: string; |
| | | memberCount: number; |
| | | maxUserCount: number; |
| | | userState: string; |
| | | beginDate: string; |
| | | endDate: string; |
| | | classTime: string; |
| | | linkProductDto: { |
| | | product: { |
| | | name: string; |
| | | icon: string; |
| | | }; |
| | | }; |
| | | } |
| | | |
| | | const calssList = ref<ClassItem[]>([]); |
| | | const searchKey = ref(""); |
| | | const codeText = ref(""); |
| | | const dialogVisible = ref(false); |
| | | const classDetail = ref(); |
| | | const isLoading = ref(true); |
| | | |
| | | let pages = reactive({ |
| | | currentPage: 1, |
| | | page: 1, |
| | | pageSize: 6, |
| | | count: 0, |
| | | loading: false, |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | getCurrentClassList(); |
| | | }); |
| | | |
| | | // 分页 |
| | | const handleSizeChange = (val: number) => { |
| | | pages.pageSize = val; |
| | | getCurrentClassList(); |
| | | }; |
| | | |
| | | const handleCurrentChange = (val: number) => { |
| | | pages.page = val; |
| | | pages.currentPage = val; |
| | | getCurrentClassList(); |
| | | }; |
| | | |
| | | const handleClick = (val: any) => { |
| | | activeName.value = val; |
| | | }; |
| | | |
| | | const openJoin = () => { |
| | | dialogVisible.value = true; |
| | | classDetail.value = null; |
| | | codeText.value = ""; |
| | | }; |
| | | |
| | | // 搜索 |
| | | const searchList = () => { |
| | | pages.page = 1; |
| | | pages.currentPage = 1; |
| | | getCurrentClassList(); |
| | | }; |
| | | |
| | | // 复制 |
| | | const copy = async (text: string) => { |
| | | try { |
| | | await toClipboard(text); |
| | | ElMessage({ |
| | | message: "复制成功", |
| | | type: "success", |
| | | }); |
| | | } catch (e) { |
| | | console.error(e); |
| | | } |
| | | }; |
| | | |
| | | // 加入班级 |
| | | const joinClass = () => { |
| | | if (!codeText.value) { |
| | | ElMessage({ |
| | | message: "无效的邀请码", |
| | | type: "error", |
| | | }); |
| | | return false; |
| | | } |
| | | const data = { refCode: codeText.value }; |
| | | MG.identity.joinGroupByRefCode(data).then((res: any) => { |
| | | if (res == "组不存在") { |
| | | ElMessage({ |
| | | message: "无效的班级", |
| | | type: "error", |
| | | }); |
| | | } |
| | | if (res == "组成员数量已最大,不能加入") { |
| | | ElMessage({ |
| | | message: "班级成员数量已最大,不能加入", |
| | | type: "error", |
| | | }); |
| | | } |
| | | if (res == "已经申请过加入此组") { |
| | | ElMessage({ |
| | | message: "已经申请过加入此班级", |
| | | type: "error", |
| | | }); |
| | | } |
| | | dialogVisible.value = false; |
| | | getCurrentClassList(); |
| | | }); |
| | | }; |
| | | |
| | | // 获取当前班级列表 |
| | | const getCurrentClassList = () => { |
| | | isLoading.value = true; |
| | | const data = { |
| | | start: (pages.page - 1) * pages.pageSize, |
| | | size: pages.pageSize, |
| | | sort: { |
| | | type: "Desc", |
| | | field: "CreateDate", |
| | | subSorts: [], |
| | | }, |
| | | filterList: [], |
| | | searchList: searchKey.value |
| | | ? [ |
| | | { |
| | | keywords: searchKey.value, |
| | | field: "Name", |
| | | compareType: "Contains", |
| | | }, |
| | | ] |
| | | : [], |
| | | }; |
| | | MG.identity.joinedGroupByList(data).then((res: any) => { |
| | | isLoading.value = false; |
| | | pages.count = res.totalSize; |
| | | if (res.datas) { |
| | | calssList.value = res.datas.map((item: any) => { |
| | | return { |
| | | ...item, |
| | | classTime: |
| | | moment(item.beginDate).format("YYYY.MM.DD") + |
| | | "--" + |
| | | moment(item.endDate).format("YYYY.MM.DD"), |
| | | bookName: item.linkProductDto.product.name, |
| | | bookIcon: getPublicImage(item.linkProductDto.product.icon, 100), |
| | | }; |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 跳转班级管理 |
| | | const goClassManage = async (item: any) => { |
| | | if (item.userState == "WaitValid") { |
| | | ElMessage({ |
| | | message: "正在审核中....", |
| | | type: "warning", |
| | | }); |
| | | return false; |
| | | } |
| | | if (item.userState == "Reject") { |
| | | ElMessage({ |
| | | message: "审核未通过", |
| | | type: "warning", |
| | | }); |
| | | return false; |
| | | } |
| | | const bookInfo = await getBookDetail(item.linkProductDto?.product); |
| | | let info = { |
| | | id: item.id, |
| | | name: item.name, |
| | | icon: bookInfo.icon, |
| | | rootCmsItemId: bookInfo.rootCmsItemId, |
| | | bookId: bookInfo.id, |
| | | author: bookInfo.author, |
| | | isbn: bookInfo.isbn, |
| | | bookRefCode: bookInfo.refCode, |
| | | }; |
| | | let page = router.resolve({ |
| | | path: "/classManage", |
| | | query: { |
| | | classInfo: JSON.stringify(info), |
| | | }, |
| | | }); |
| | | window.open(page.href, "_blank"); |
| | | // router.push({ |
| | | // path: '/classManage', |
| | | // query: { |
| | | // classInfo: JSON.stringify(info) |
| | | // } |
| | | // }) |
| | | }; |
| | | |
| | | // 获取教材详情 |
| | | const getBookDetail = async (item: any) => { |
| | | const path = item.refCode ? "jsek_digitalTextbooks" : config.goodsStore; |
| | | let query = { |
| | | path, |
| | | queryType: "*", |
| | | productId: String(item.id), |
| | | storeInfo: path, |
| | | coverSize: { |
| | | height: 300, |
| | | width: 210, |
| | | }, |
| | | fields: { |
| | | author: [], |
| | | isbn: [], |
| | | }, |
| | | }; |
| | | const res = await MG.store.getProductDetail(query); |
| | | return res.datas ?? null; |
| | | }; |
| | | |
| | | // 通过code查询班级 |
| | | const getClassDetail = () => { |
| | | if (codeText.value == "") { |
| | | ElMessage({ |
| | | message: "请输入邀请码", |
| | | type: "warning", |
| | | }); |
| | | return false; |
| | | } |
| | | const data = { |
| | | classIdOrRefCode: codeText.value, |
| | | }; |
| | | MG.edu |
| | | .getCourseClass(data) |
| | | .then((res: any) => { |
| | | classDetail.value = res; |
| | | }) |
| | | .catch((err: any) => { |
| | | console.log(err); |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .coursePage { |
| | | .tabs { |
| | | padding: 0 20px; |
| | | } |
| | | .headerBox { |
| | | padding: 5px 20px; |
| | | margin-bottom: 10px; |
| | | overflow: hidden; |
| | | .searchBox { |
| | | width: 300px; |
| | | float: left; |
| | | .searchBtn { |
| | | background-color: var(--el-color-primary); |
| | | color: #fff; |
| | | border-top-left-radius: 0; |
| | | border-bottom-left-radius: 0; |
| | | } |
| | | } |
| | | .applyStartClasses { |
| | | float: right; |
| | | } |
| | | } |
| | | .courseListBox { |
| | | overflow: hidden; |
| | | .courseItem { |
| | | float: left; |
| | | width: 46%; |
| | | margin: 0 2% 20px; |
| | | border-radius: 8px; |
| | | border: 1px solid #efefef; |
| | | overflow: hidden; |
| | | .itemHeader { |
| | | height: 40px; |
| | | line-height: 40px; |
| | | padding: 0 20px; |
| | | // color: #fff; |
| | | background-color: #f8f8f8; |
| | | .title { |
| | | font-weight: 600; |
| | | } |
| | | div { |
| | | display: inline-block; |
| | | } |
| | | .courseId { |
| | | margin-left: 6px; |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | .copyIdBtn { |
| | | float: right; |
| | | height: 20px; |
| | | line-height: 20px; |
| | | margin-top: 10px; |
| | | font-size: 12px; |
| | | background-color: #fff; |
| | | color: #3b93fe; |
| | | padding: 0 6px; |
| | | border-radius: 50px; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .itemInfo { |
| | | padding: 20px; |
| | | flex: 1; |
| | | display: flex; |
| | | cursor: pointer; |
| | | .imgBox { |
| | | width: 90px; |
| | | height: 120px; |
| | | margin-right: 20px; |
| | | } |
| | | .infoBox { |
| | | flex: 1; |
| | | font-size: 12px; |
| | | p { |
| | | margin-bottom: 10px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .codeContent { |
| | | width: 100%; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-bottom: 30px; |
| | | } |
| | | .classInfo { |
| | | padding: 30px 0; |
| | | border-top: 2px dashed #ccc; |
| | | |
| | | .itemCon { |
| | | margin-bottom: 20px; |
| | | } |
| | | } |
| | | .pageBox { |
| | | padding: 10px 0; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | } |
| | | </style> |