From 5f00696dfb25bc90034448ceb634ed1ef256681a Mon Sep 17 00:00:00 2001
From: qiyunfeng-create <1940665526@qq.com>
Date: 星期四, 21 八月 2025 21:13:35 +0800
Subject: [PATCH] Merge branch 'master' of http://182.92.203.7:2001/r/xiehe_website

---
 src/views/personalCenter/course.vue |  668 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 665 insertions(+), 3 deletions(-)

diff --git a/src/views/personalCenter/course.vue b/src/views/personalCenter/course.vue
index 68d9d97..226347d 100644
--- a/src/views/personalCenter/course.vue
+++ b/src/views/personalCenter/course.vue
@@ -1,7 +1,669 @@
 <template>
-  <div class="myBook">
-    <div class="myBook_header">
-      <div class="myBook_header_title">鎴戠殑璇剧▼</div>
+  <div class="coursePage">
+    <div class="personalPage-title">鎴戠殑璇剧▼</div>
+    <div class="headerBox">
+      <div class="searchBox">
+        <el-input
+          v-model="searchKey"
+          clearable
+          @clear="getData"
+          placeholder="璇疯緭鍏ュ叧閿瓧"
+        >
+          <template #append>
+            <el-button type="primary" class="searchBtn" :icon="Search" @click="getData" />
+          </template>
+        </el-input>
+      </div>
+      <el-button type="primary" class="applyStartClasses" @click="applyCourse"
+        >鐢宠寮�璇�</el-button
+      >
     </div>
+    <div class="courseListBox" v-loading="pages.loading">
+      <div
+        class="courseItem"
+        v-for="(item, index) in courseList"
+        :key="index"
+        @click="gotoDetail(item)"
+      >
+        <div class="itemHeader">
+          <div class="title" :title="item.name">{{ item.name }}</div>
+        </div>
+        <div class="reasonBox">
+          <el-tooltip placement="right-end" effect="light" class="box-item">
+            <template #content>
+              <div style="width: 300px" v-html="item.reason"></div>
+            </template>
+            <span v-if="item.applyState == 'Reject'" style="color: red">
+              鎷掔粷鍘熷洜锛歿{ item.reason != "" ? item.reason : "-" }}
+            </span>
+          </el-tooltip>
+          <el-button
+            v-if="item.applyState == 'Reject'"
+            type="primary"
+            size="small"
+            style="margin-left: 20px"
+            @click="reapplyCourse(item)"
+            >閲嶆柊鐢宠</el-button
+          >
+        </div>
+
+        <div class="itemInfo">
+          <div class="imgBox autoImgBox">
+            <div
+              class="stateIcon"
+              v-if="item.applyState == 'Normal'"
+              style="background-color: #1dbd11"
+            >
+              浣跨敤涓�
+            </div>
+            <div
+              class="stateIcon"
+              v-if="item.applyState == 'WaitAudit'"
+              style="background-color: #ef9f29"
+            >
+              瀹℃牳涓�
+            </div>
+            <div
+              class="stateIcon"
+              v-if="item.applyState == 'Reject'"
+              style="background-color: red"
+            >
+              鏈�氳繃
+            </div>
+            <img v-if="item.icon" :src="item.icon" />
+            <img v-else :src="defaultImg" alt="" />
+          </div>
+          <div class="infoBox">
+            <p class="id">ID锛歿{ item.id }}</p>
+            <div class="introduction" :title="item.introduction">
+              {{ item.introduction }}
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="pagination-box" v-if="courseList.length > 0">
+      <el-pagination
+        v-model:current-page="pages.page"
+        v-model:page-size="pages.pageSize"
+        :background="false"
+        layout="total, prev, pager, next"
+        :total="pages.count"
+        @current-change="pageChange"
+      />
+    </div>
+    <div class="nullBox" v-if="!pages.loading && courseList.length == 0">
+      <el-empty />
+    </div>
+    <!-- 鐢宠寮�璇惧脊妗� -->
+    <el-dialog v-model="applyCourseDialog" width="750" align-center>
+      <template #title>{{ editData ? "閲嶆柊鐢宠" : "鐢宠寮�璇�" }}</template>
+      <el-form
+        :model="formData"
+        label-position="left"
+        ref="dialogFormRef"
+        label-width="80px"
+      >
+        <el-form-item
+          label="璇剧▼鍚嶇О"
+          prop="name"
+          :rules="[{ required: true, message: '璇疯緭鍏ヨ绋嬪悕绉�' }]"
+        >
+          <el-input v-model="formData.name" placeholder="璇疯緭鍏ヨ绋嬪悕绉�" />
+        </el-form-item>
+        <el-form-item
+          v-if="!editData"
+          label="鍏宠仈鏁欐潗"
+          prop="bookName"
+          :rules="[{ required: true, message: '璇烽�夋嫨鏁欐潗' }]"
+        >
+          <div style="display: flex">
+            <el-input v-model="formData.bookName" disabled="true" style="width: 300px" />
+            <el-button style="margin-left: 10px" @click="selectBook">閫夋嫨鏁欐潗</el-button>
+          </div>
+        </el-form-item>
+        <el-form-item label="璇剧▼浠嬬粛">
+          <el-input
+            v-model="formData.desc"
+            type="textarea"
+            autocomplete="off"
+            maxlength="300"
+            show-word-limit
+            rows="7"
+            placeholder="璇疯緭鍏ヨ绋嬩粙缁�"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="applyCourseDialog = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="submit" :loading="submitLoading"
+            >鎻愪氦</el-button
+          >
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 閫夋嫨鏁欐潗寮规 -->
+    <el-dialog v-model="selectTextBookDialog" width="1000" align-center>
+      <template #title>閫夋嫨鏁欐潗</template>
+      <div class="textBookList" v-loading="textBookPages.loading">
+        <div class="headerBox" style="padding: 10px">
+          <div class="searchBox" style="float: right">
+            <el-input
+              v-model="selectName"
+              @clear="getTextBook"
+              clearable
+              placeholder="璇疯緭鍏ュ叧閿瓧"
+            >
+              <template #append>
+                <el-button
+                  type="primary"
+                  class="searchBtn"
+                  :icon="Search"
+                  @click="getTextBook"
+                />
+              </template>
+            </el-input>
+          </div>
+        </div>
+        <div style="min-height: 370px" v-if="textBookListData.length > 0">
+          <div
+            v-for="(item, index) in textBookListData"
+            :key="index"
+            class="textBookItem"
+          >
+            <el-checkbox
+              class="checkBox"
+              v-model="item.check"
+              @change="selectChange(item)"
+            />
+            <div class="imgBox autoImgBox">
+              <img :src="item.img" />
+            </div>
+            <p>{{ item.product.name }}</p>
+          </div>
+        </div>
+        <div
+          class="nullBox"
+          v-if="!textBookPages.loading && textBookListData.length == 0"
+        >
+          <el-empty />
+        </div>
+      </div>
+      <div class="pagination-box" v-if="textBookListData.length > 0">
+        <el-pagination
+          v-model:current-page="textBookPages.page"
+          v-model:page-size="textBookPages.pageSize"
+          :background="false"
+          layout="total, prev, pager, next"
+          :total="textBookPages.count"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="selectTextBookDialog = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="selectTextBookSubmit">纭畾</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
+
+<script setup lang="ts">
+import { reactive, ref, onMounted, inject, watch } from "vue";
+import { Search } from "@element-plus/icons-vue";
+import { useRouter, useRoute } from "vue-router";
+import { ElMessage } from "element-plus";
+// eslint-disable-next-line
+import { getPublicImage } from "@/assets/js/middleGround/tool.js";
+// eslint-disable-next-line
+import defaultImg from "@/assets/images/default-book-img.png";
+
+const router: any = useRouter();
+const route: any = useRoute();
+const MG: any = inject("MG");
+const selectName = ref("");
+
+// 鐢宠寮�璇鹃槻鎶杔oading
+const submitLoading = ref(false);
+
+const courseList: any = ref([]);
+
+onMounted(() => {
+  getData();
+});
+
+const searchKey = ref("");
+const pages = reactive({
+  page: 1,
+  pageSize: 6,
+  count: 0,
+  loading: true,
+});
+
+// 鑾峰彇璇剧▼
+const getData = () => {
+  pages.loading = true;
+  MG.edu
+    .getAppCourseList({
+      size: pages.pageSize,
+      start: pages.pageSize * pages.page - pages.pageSize,
+      sort: {
+        type: "Desc",
+        field: "CreateDate",
+      },
+      filterList: [],
+      searchList: searchKey.value
+        ? [
+            {
+              keywords: searchKey.value,
+              field: "Name",
+              compareType: "Contains",
+            },
+          ]
+        : [],
+    })
+    .then((res: any) => {
+      pages.loading = false;
+      pages.count = res.totalSize;
+      courseList.value = res.datas.map((item: any) => {
+        return {
+          ...item,
+          name: item.name,
+          id: item.id,
+          icon: item.icon != "default" ? getPublicImage(item.icon, 80) : defaultImg,
+          introduction: item.description,
+          reason: item.applyReturnMsg ? JSON.parse(item.applyReturnMsg).reason : "",
+        };
+      });
+    });
+};
+
+// 鐢宠寮�璇�
+const applyCourse = () => {
+  editData.value = null;
+  formData.value = {
+    name: "",
+    bookName: "",
+    bookId: "",
+    selectData: "",
+    desc: "",
+  };
+  applyCourseDialog.value = true;
+};
+
+const editData: any = ref(null);
+const reapplyCourse = (data: any) => {
+  editData.value = data;
+  formData.value = {
+    name: data.name,
+    bookName: data.linkProduct.name,
+    bookId: data.linkProduct.id,
+    selectData: "",
+    desc: data.description,
+  };
+  applyCourseDialog.value = true;
+};
+
+// 鐢宠鏁欐潗寮规
+const applyCourseDialog = ref(false);
+const selectTextBookDialog = ref(false);
+const formData = ref({
+  name: "",
+  bookName: "",
+  bookId: "",
+  selectData: "",
+  desc: "",
+});
+
+const selectChange = (select: any) => {
+  for (let i = 0; i < textBookListData.value.length; i++) {
+    const item: any = textBookListData.value[i];
+    if (item.id == select.id) {
+      item.check = true;
+    } else {
+      item.check = false;
+    }
+  }
+};
+
+const selectTextBookSubmit = () => {
+  const selectData: any = textBookListData.value.filter((item: any) => item.check)[0];
+  if (!selectData?.product?.id) {
+    ElMessage.warning("璇烽�夋嫨寮�璇炬暀鏉愶紒");
+    return false;
+  }
+  formData.value.bookId = selectData.product.id;
+  formData.value.bookName = selectData.product.name;
+  formData.value.selectData = selectData;
+  selectTextBookDialog.value = false;
+};
+
+const submit = () => {
+  submitLoading.value = true;
+  if (editData.value) {
+    MG.edu
+      .updateCourse({
+        courseId: editData.value.id,
+        name: formData.value.name,
+        description: formData.value.desc,
+      })
+      .then((res: any) => {
+        MG.edu
+          .updateCourseApply({
+            courseId: editData.value.id,
+            applyData: JSON.stringify({
+              textBookId: formData.value.bookId,
+              textBookName: formData.value.bookName,
+            }),
+          })
+          .then((ares: any) => {
+            ElMessage.success("璇剧▼宸查噸鏂扮敵璇凤紝绛夊緟绠$悊鍛樺鏍搞��");
+            applyCourseDialog.value = false;
+            getData();
+          });
+      });
+  } else {
+    if (formData.value.name == "") {
+      ElMessage({
+        type: "warning",
+        message: "璇峰~鍐欒绋嬪悕绉�",
+      });
+      submitLoading.value = false;
+      return false;
+    }
+    if (!formData.value.bookId) {
+      ElMessage({
+        type: "warning",
+        message: "璇烽�夋嫨鍏宠仈鏁欐潗",
+      });
+      submitLoading.value = false;
+      return false;
+    }
+    MG.edu
+      .applyNewCourse({
+        name: formData.value.name,
+        description: formData.value.desc,
+        content: "",
+        icon: formData.value.selectData.product.icon ?? "default",
+        type: "course",
+        config: "",
+        applyData: JSON.stringify({
+          textBookId: formData.value.bookId,
+          textBookName: formData.value.bookName,
+        }),
+        linkProductId: formData.value.bookId,
+        maxClassCount: 999,
+        payPrice: 0,
+      })
+      .then((res: any) => {
+        if (res) {
+          ElMessage.success("璇剧▼宸茬敵璇凤紝绛夊緟绠$悊鍛樺鏍搞��");
+          applyCourseDialog.value = false;
+          getData();
+        }
+      });
+  }
+  formData.value.selectData = "";
+  setTimeout(() => {
+    submitLoading.value = false;
+  }, 1000);
+};
+
+// 鑾峰彇宸茶喘涔扮殑鏁欐潗鍒楄〃
+const textBookPages = reactive({
+  page: 1,
+  pageSize: 12,
+  count: 0,
+  loading: false,
+});
+const textBookListData = ref([]);
+
+const getTextBook = () => {
+  textBookPages.loading = true;
+  const searchData = [
+    {
+      keywords: "digitalTextbooks",
+      field: "ProductType",
+    },
+    {
+      keywords: "mediaBook",
+      field: "ProductType",
+    },
+    {
+      keywords: selectName.value,
+      field: "ProductName",
+    },
+  ];
+  const data = {
+    Size: textBookPages.pageSize,
+    Start: textBookPages.pageSize * textBookPages.page - textBookPages.pageSize,
+    sort: {
+      type: "Desc",
+      field: "CreateDate",
+    },
+    searchList: searchData,
+  };
+  MG.store
+    .getPurchasedProductList(data)
+    .then((res: any) => {
+      textBookPages.count = res.totalSize;
+      textBookListData.value = res.datas.map((item: any) => {
+        return {
+          ...item,
+          img: item.product.icon ? getPublicImage(item.product.icon, 80) : defaultImg,
+        };
+      });
+      textBookPages.loading = false;
+    })
+    .catch(() => {
+      textBookPages.loading = false;
+    });
+};
+
+// 閫夋嫨鏁欐潗
+const selectBook = () => {
+  selectTextBookDialog.value = true;
+  textBookListData.value = [];
+  getTextBook();
+};
+
+const pageChange = (val: any) => {
+  pages.page = val;
+  getData();
+};
+const handleCurrentChange = (val: any) => {
+  textBookPages.page = val;
+  getTextBook();
+};
+
+const gotoDetail = (data: any) => {
+  if (data.applyState == "Normal") {
+    router.push({
+      name: "courseDetail",
+      query: {
+        courseId: data.id,
+      },
+    });
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.coursePage {
+  .headerBox {
+    padding: 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;
+    min-height: 200px;
+
+    .courseItem {
+      float: left;
+      width: 46%;
+      height: 210px;
+      margin: 0 2% 20px;
+      border-radius: 8px;
+      border: 1px solid #efefef;
+      overflow: hidden;
+      cursor: pointer;
+
+      .itemHeader {
+        height: 40px;
+        line-height: 40px;
+        padding: 0 20px;
+        background-color: #f8f8f8;
+        .title {
+          font-weight: 600;
+        }
+
+        div {
+          width: 100%;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          word-break: break-all;
+        }
+      }
+
+      .reasonBox {
+        display: flex;
+        padding: 5px 20px;
+        cursor: default;
+        span {
+          font-size: 12px;
+          line-height: 24px;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+
+      .itemInfo {
+        padding: 10px 20px;
+        flex: 1;
+        display: flex;
+        .imgBox {
+          width: 90px;
+          height: 120px;
+          margin-right: 20px;
+        }
+        .infoBox {
+          flex: 1;
+          font-size: 14px;
+          p {
+            margin-bottom: 10px;
+          }
+          .introduction {
+            height: 88px;
+            line-height: 22px;
+            display: -webkit-box;
+            -webkit-box-orient: vertical;
+            -webkit-line-clamp: 4;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            word-break: break-all;
+          }
+        }
+        .stateIcon {
+          position: absolute;
+          right: -6px;
+          top: -4px;
+          padding: 5px 8px;
+          color: #fff;
+          border-top-left-radius: 30px;
+          border-bottom-right-radius: 30px;
+          border-top-right-radius: 30px;
+          z-index: 99;
+        }
+      }
+    }
+  }
+}
+
+.textBookList {
+  overflow: hidden;
+  margin-bottom: 20px;
+  .textBookItem {
+    float: left;
+    width: 160px;
+    height: 220px;
+    margin: 10px;
+    position: relative;
+    border: 1px solid #eee;
+    padding: 20px;
+    box-sizing: border-box;
+    .checkBox {
+      position: absolute;
+      right: 0;
+      top: 0;
+      height: 14px;
+      ::v-deep {
+        .el-checkbox__inner {
+          border-color: #888;
+        }
+      }
+    }
+
+    p {
+      line-height: 1.2;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+    }
+  }
+}
+
+.autoImgBox {
+  width: 120px;
+  height: 130px;
+  margin-bottom: 10px;
+  position: relative;
+  img {
+    width: auto;
+    height: auto;
+    max-width: 100%;
+    max-height: 100%;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    margin: auto;
+  }
+}
+
+.pagination-box {
+  display: flex;
+  justify-content: center;
+  padding: 10px 0;
+}
+
+.nullBox {
+  text-align: center;
+  margin-top: 30px;
+  font-size: 20px;
+  color: #ccc;
+}
+</style>

--
Gitblit v1.9.1