闫增涛
2024-12-16 1fa2461678d7f93b88f5666c4898e5b6c72080f3
个人中心,班级、课程模块
12个文件已修改
44个文件已添加
5475 ■■■■■ 已修改文件
app.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assets/js/middleGround/api/edu.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assets/js/middleGround/api/identity.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/detail/index.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.js 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.wxml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.wxss 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/examination.js 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/examination.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/examination.wxml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/questionOptions/index.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/questionOptions/index.wxml 173 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageBookService/pages/bookServices/examination/questionSchedule/index.wxml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/baseClass/index.js 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/baseClass/index.json 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/baseClass/index.wxml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/baseClass/index.wxss 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/questionDom/index.js 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/questionDom/index.json 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/questionDom/index.wxml 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/questionDom/index.wxss 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/studentManage/index.js 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/studentManage/index.json 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/studentManage/index.wxml 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/studentManage/index.wxss 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/teaching/index.js 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/teaching/index.json 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/teaching/index.wxml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/components/teaching/index.wxss 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/detail/index.js 457 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/detail/index.json 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/detail/index.wxml 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/detail/index.wxss 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.js 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.json 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.skeleton.wxml 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.skeleton.wxss 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.wxml 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/course/index.wxss 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/studentClass/index.js 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/studentClass/index.json 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/studentClass/index.wxml 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/studentClass/index.wxss 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/detail/index.js 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/detail/index.json 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/detail/index.wxml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/detail/index.wxss 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/index.js 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/index.json 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/index.wxml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
packageCourse/pages/teachClass/index.wxss 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/personalCenter/index.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/personalCenter/index.wxss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
static/images/personal/class.png 补丁 | 查看 | 原始文档 | blame | 历史
static/images/personal/course.png 补丁 | 查看 | 原始文档 | blame | 历史
app.json
@@ -82,6 +82,18 @@
        "pages/bookServices/linkPage/index",
        "pages/psychologyAnswer/psychologyAnswer"
      ]
    },
    {
      "root": "packageCourse",
      "name": "packageCourse",
      "pages": [
        "pages/course/index",
        "pages/course/detail/index",
        "pages/teachClass/index",
        "components/baseClass/index",
        "pages/studentClass/index",
        "pages/teachClass/detail/index"
      ]
    }
  ],
  "tabBar": {
assets/js/middleGround/api/edu.js
@@ -161,6 +161,14 @@
      data,
    })
  },
    // 获取topic信息
    getClassTopic(data) {
      return request({
        url: '/edu/api/ApiGetClassTopic',
        method: 'post',
        data
      })
    },
}
export default eduApi
assets/js/middleGround/api/identity.js
@@ -199,7 +199,24 @@
      method: "post",
      data,
    });
  }
  },
  // 通过refcode加入班级/组
  joinGroupByRefCode(data) {
    return request({
      url: '/identity/api/ApiJoinGroupByRefCode',
      method: 'post',
      data
    })
  },
  // 获取加入组的列表信息
  joinedGroupByList(data) {
    return request({
      url: '/identity/api/ApiGetJoinedGroupByList',
      method: 'post',
      data
    })
  },
};
export default identityApi;
packageBookService/pages/bookServices/detail/index.js
@@ -1168,8 +1168,7 @@
              data = res.datas.cmsDatas[0].datas.filter((item) => item.type == 'questionBankFolder' && (item.childrenFolderCount > 0 || item.childrenCount > 0) && (item.name !== '填空题' &&
                  item.name !== '判断题' &&
                  item.name !== '多选题' &&
                  item.name !== '简答题')) &&
                item.refCode != 'jsek_interaction'
                item.name !== '简答题') && item.name !== '单选题')
              that.setData({
                jslx: false,
              });
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.js
New file
@@ -0,0 +1,95 @@
// components/popup/index.js
const app = getApp()
Component({
  /**
   * 组件的属性列表
   */
  properties: {
  },
  ready() {
    var that = this;
    // 动态获取屏幕高度
    wx.getSystemInfo({
      success: (result) => {
        that.setData({
          height: result.windowHeight
        });
      },
    })
  },
  properties: {
    visible: {
      type: Boolean
    },
  },
  /**
   * 组件的初始数据
   */
  data: {
    inputStyle: 'border: 2rpx solid rgba(220,220,220,1);border-radius: 12rpx; padding: 0 0 0 16rpx;',
    visible: false, //打开弹窗的对应下标
    height: '', //屏幕高度
    inputvalue: '',
    joinGroup: '',
    noTip: false,
  },
  /**
   * 组件的方法列表
   */
  methods: {
    //关闭弹窗
    closePopup() {
      this.setData({
        visible: false,
        inputvalue: '',
      })
    },
    // 输入框改变
    inputChange(e) {
      this.setData({
        inputvalue: e.detail.value
      })
    },
    // 单选框改变
    onChangeRadio(e) {
      console.log(e)
      this.setData({
        joinGroup: e.currentTarget.dataset.value
      })
    },
    // 确定
    confirmSuggest() {
      if (this.data.joinGroup) {
        if (this.data.joinGroup == 1) {
          if (!this.data.inputvalue.length) {
            wx.showToast({
              icon: "error",
              title: '请填写联系方式',
            })
          } else {
            // this.joinClass()
            var myEventDetail = {
              value: this.data.inputvalue
            } // detail对象,提供给事件监听函数
            var myEventOption = {
              bubbles: true,
              composed: true
            } // 触发事件的选项
            this.triggerEvent('joinClass', myEventDetail, myEventOption)
          }
        } else {
          this.closePopup()
        }
      } else {
        wx.showToast({
          icon: 'error',
          title: '请选择加入班级',
        })
      }
    },
  }
})
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.json
New file
@@ -0,0 +1,12 @@
{
  "component": true,
  "usingComponents": {
    "t-input": "tdesign-miniprogram/input/input",
    "t-textarea": "tdesign-miniprogram/textarea/textarea",
    "t-button": "tdesign-miniprogram/button/button",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-rate": "tdesign-miniprogram/rate/rate",
    "t-radio": "tdesign-miniprogram/radio/radio",
    "t-checkbox": "tdesign-miniprogram/checkbox/checkbox"
  }
}
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.wxml
New file
@@ -0,0 +1,46 @@
<!-- 中间弹窗 -->
<view class="popup-box" wx:if="{{visible=='1'}}" bindtap="closePopup"></view>
<view class="info-center" style="top:{{height*0.1}}px" wx:if="{{visible=='1'}}">
  <view class="row-info">
    <view class="popup-content">
      <view class="warning-txt">
        检测到您未加入班级,如您不加入班级,提交的答案将不能提交到班级老师处:
      </view>
      <t-radio
        allow-uncheck
        icon="dot"
        label="加入班级"
        value="1"
        data-value="1"
        checked="{{joinGroup == 1}}"
        bind:change="onChangeRadio"
      />
      <view class="phone-input">
        <t-input
          value="{{inputvalue}}"
          placeholder="请输入班级邀请码"
          bind:change="inputChange"
          type="number"
          maxlength="{{11}}"
          class="phone"
          style="{{inputStyle}}"
        />
      </view>
      <t-radio
        allow-uncheck
        icon="dot"
        label="不加入班级"
        value="0"
        data-value="2"
        checked="{{joinGroup == 2}}"
        bind:change="onChangeRadio"
      />
      <t-checkbox label="不再提示加入班级" icon="rectangle" value="{{noTip}}" />
    </view>
    <view class="submit-btn">
      <t-button theme="primary" size="large" block bind:tap="confirmSuggest"
        >确认</t-button
      >
    </view>
  </view></view
>
packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.wxss
New file
@@ -0,0 +1,181 @@
/* packageBookService/pages/bookServices/examination/components/joinGroupDialog/index.wxss */
/* components/popup/index.wxss */
/* 蒙层 */
.popup-box {
  position: absolute;
  z-index: 99;
  top: 0;
  background-color: rgba(0, 0, 0, 0.5);
  width: 100%;
  height: 100%;
}
/* 上 */
.info-top {
  position: fixed;
  z-index: 999;
  width: 100%;
  top: 0;
  background-color: white;
  border-bottom-left-radius: 5rpx;
  border-bottom-right-radius: 5rpx;
}
/* 中 */
.info-center {
  position: relative;
  /* background-image: linear-gradient(to bottom, #FFF8E5, #fffcf5); */
  background-color: #fff;
  position: fixed;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10rpx;
  width: 90%;
  margin-left: 5%;
  margin-right: 5%;
}
/* 下 */
.info-bottom {
  position: fixed;
  z-index: 999;
  width: 100%;
  bottom: 0;
  background-color: white;
  border-top-left-radius: 10rpx;
  border-top-right-radius: 10rpx;
}
/* 左 */
.info-left {
  position: fixed;
  z-index: 999;
  width: 50%;
  height: 100%;
  top: 0;
  background-color: white;
  border-top-right-radius: 10rpx;
  border-bottom-right-radius: 10rpx;
}
/* 右 */
.info-right {
  position: fixed;
  z-index: 999;
  width: 50%;
  height: 100%;
  right: 0;
  top: 0;
  background-color: white;
  border-top-left-radius: 10rpx;
  border-bottom-left-radius: 10rpx;
}
/* 自定义内容(根据自己需求更改,可删除) */
button {
  margin: 15rpx 0;
}
.row-info {
  width: 100%;
  padding: 10rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 15rpx;
  font-size: 32rpx;
}
.phone-input view {
  padding: 10rpx 0;
}
.line {
  width: 100rpx;
  height: 1rpx;
}
.left-line {
  background-image: linear-gradient(to left, orange, white);
}
.right-line {
  background-image: linear-gradient(to right, orange, white);
}
.row-author {
  font-size: 24rpx;
  color: gray;
}
.row-btn {
  display: flex;
  flex-direction: row;
  align-items: center;
  border-top: 1rpx dashed #f1f1f1;
}
.row-btn view {
  flex: 1;
  text-align: center;
  margin: 20rpx 10%;
  padding: 12rpx 0;
  font-size: 32rpx;
  border-radius: 10rpx;
}
.left-btn {
  background-color: #f1f1f1;
  color: #33ccff;
}
.right-btn {
  background-color: #33ccff;
  color: white;
}
.fixed {
  position: fixed;
  bottom: 0;
  width: 50%;
}
.popup-content {
  width: 100%;
  height: 680rpx;
  display: flex;
  flex-direction: column;
}
.phone {
  --td-input-border-radius: 20rpx;
  border-radius: 20rpx;
}
.phone-input {
  width: 100%;
  margin-bottom: 16rpx;
}
.submit-btn {
  width: 200rpx;
  /* padding: 40rpx; */
  --td-button-border-radius: 20rpx;
  --td-button-primary-bg-color: #ff6c00;
  --td-button-primary-border-color: #ff6c00;
  --td-button-primary-active-bg-color: #ff984d;
  --td-button-primary-active-border-color: #ff984d;
}
.warning-txt {
  color: #d8001b;
  font-size: 28rpx;
}
/* .t-radio__border {
  position: absolute;
  height: 0;
  background: unset;
} */
packageBookService/pages/bookServices/examination/examination.js
@@ -55,6 +55,9 @@
    showDialog: false, // 未提交退出拦截弹窗
    showId: '',
    isShowDialog: false, // 测试报告弹窗是否显示
    joinGroup: false, // 是否加入班级
    visible: false, // 加入班级弹窗是否打开
    classList: [] // 班级列表
  },
  /**
@@ -554,7 +557,8 @@
    if (
      this.data.answerType == "option" ||
      this.data.answerType == "errorQuestion" ||
      this.data.answerType == "mock"
      this.data.answerType == "mock" ||
      this.data.answerType == 'interaction'
    ) {
      // 先遍历所有题目,将未批改的题目批改
      const qustionList = this.data.questionDataList;
@@ -617,6 +621,9 @@
      this.setData({
        isShowDialog: true
      })
    } else if (this.data.answerType == 'interaction') {
      this.toggleCountdown();
      this.handleClassQuestion()
    }
    this.setData({
      loading: false,
@@ -639,6 +646,13 @@
      // 测试答题
      await this.getCollectIdList(); // 获取收藏id列表
      await this.getErrorList(); // 获取错题id列表
    } else if (this.data.answerType == "interaction") {
      this.setData({
        countdownTime: 2 * 60 * 60 * 1000,
      });
      this.getCurrentClassList()
      this.getinteractionInfo()
      // 教学互动
    } else if (this.data.answerType == "collectQuestion") {
      this.setData({
        submitStatus: true,
@@ -846,7 +860,6 @@
      let query = {
        path: "*",
        queryType: "*",
        productId: this.data.bookId,
        cmsPath: pathitem.productLinkPath,
        itemFields: {
@@ -880,9 +893,6 @@
          oldList = oldData.find(
            (item) => item.path == pathitem.productLinkPath
          ).infoList;
          this.setData({
            submitStatus: true,
          });
        }
        res.datas.cmsDatas[0].datas.forEach((item, index) => {
          if (this.data.storeInfo || this.data.jslx) {
@@ -1079,15 +1089,16 @@
    // 有题目再开始倒计时
    if (this.data.questionDataList.length) {
      this.startCountdown();
    } else {
      this.setData({
        noData: true
      })
    }
    this.setData({
      loading: false,
      showId: this.data.questionDataList[0].id
    });
      })
    } else {
      this.setData({
        noData: true,
        loading: false,
      })
    }
  },
  // 批改题目 (练习,我的错题,我的收藏,,组卷)
  handleQuestion(index) {
@@ -2232,7 +2243,6 @@
  // 富文本处理
  formatRichText(html) {
    console.log(html);
    let newContent = html.replace(/<img[^>]*>/gi, function (match, capture) {
      match = match
        .replace(/style="[^"]+"/gi, "")
@@ -2266,4 +2276,206 @@
    );
    return newContent;
  },
  // 教学互动模式相关
  // 获取班级列表
  getCurrentClassList() {
    const query = {
      start: 0,
      size: 999,
      filterList: [],
    }
    app.MG.identity.joinedGroupByList(query).then((res) => {
      res.datas.forEach(async item => {
        item.topicId = null
        const data = await this.getTopicInfo(item.id)
        if (data) {
          item.topicId = data.id
        }
      })
      if (res.datas.length) {
        this.setData({
          joinGroup: true,
          classList: res.datas,
        })
      } else {
        // 未加入班级,唤起加入班级弹窗
        this.setData({
          joinGroup: false,
          visible: true
        })
      }
    })
  },
  // 加入班级
  joinClass(e) {
    const data = {
      refCode: e.detail.value
    }
    app.MG.identity.joinGroupByRefCode(data).then((res) => {
      if (res == '组不存在') {
        wx.showToast({
          icon: 'error',
          title: '无效的班级',
        })
      } else if (res == '组成员数量已最大,不能加入') {
        wx.showToast({
          icon: 'error',
          title: '班级成员数量已最大,不能加入',
        })
      } else if (res == '已经申请过加入此组') {
        wx.showToast({
          icon: 'error',
          title: '已经申请过加入此班级',
        })
      } else {
        wx.showToast({
          icon: "success",
          title: '已成功,等待审核中...',
        })
        this.setData({
          visible: false
        })
        this.getCurrentClassList()
      }
    })
  },
  // 获取班级topic
  async getTopicInfo(classId) {
    let query = {
      classId,
      refCodes: ["TeachInteraction"]
    }
    const res = await app.MG.edu.getClassTopic(query)
    return res[0] ?? null
  },
  // 获取旧教学互动答题数据
  getinteractionInfo() {
    app.MG.identity
      .getUserKey({
        domain: "interactionData",
        keys: [this.data.productLinkPath],
      })
      .then((res) => {
        if (res.length) {
          let value = JSON.parse(res[0].value);
          this.setData({
            submitStatus: value.submitStatus,
            currentIndex: value.currentIndex,
          });
          // 携带答题记录 获取题目
          this.getQuestionList(value.dataList);
        } else {
          this.getQuestionList(); // 获取题库题目
          this.setData({
            showDialog: true
          })
        }
      });
  },
  // 提交教学互动答题数据
  setinteractionInfo(submitStatus) {
    const list = []
    this.data.cardList.forEach(item => {
      list.push({
        catalogName: item.catalogName,
        path: item.path,
        infoList: item.infoList.map((citem) => ({
          id: citem.id,
          userAnswer: citem.userAnswer
        }))
      })
    })
    console.log('提交数据', list);
    app.MG.identity
      .setUserKey({
        setKeyRequests: [{
          domain: "interactionData",
          key: this.data.productLinkPath,
          value: JSON.stringify({
            submitStatus,
            currentIndex: this.data.currentIndex,
            dataList: list
          }),
        }, ],
      })
      .then((res) => {});
  },
  // 处理教学互动答题
  handleClassQuestion() {
    debugger
    const flag = this.data.questionDataList.some(item => !item.userAnswer)
    if (flag) {
      // 没做完,保存即可
      const isAnswer = this.data.questionDataList.some(item => item.userAnswer)
      if (isAnswer) this.setinteractionInfo(false)
    } else {
      // 做完了,提交到message
      this.setinteractionInfo(true)
      if (this.data.classList.length) {
        this.data.classList.forEach(item => {
          this.newTopicMessage(item.topicId)
        })
      }
    }
    this.setData({
      submitStatus: true
    })
  },
  // 新建topicMessage
  newTopicMessage(topicId) {
    let content = []
    for (let cindex = 0; cindex < this.data.questionDataList.length; cindex++) {
      const citem = this.data.questionDataList[cindex];
      content.push({
        cmsItemId: citem.id,
        answer: citem.userAnswer,
        state: citem.questionType == 'shortAnswer' ? 'none' : citem.isRight,
        type: citem.questionType
      })
    }
    const data = {
      description: '',
      icon: '',
      state: 'Normal',
      topicIdOrRefCode: String(topicId),
      name: this.data.answerTitle,
      content: JSON.stringify({
        bookId: this.data.bookId,
        userName: JSON.parse(wx.getStorageSync(app.config.userInfoKey)).name,
        path: this.data.productLinkPath,
        content
      }),
      type: 'Normal',
      cmsTypeRefCode: '',
      newDataListRequest: []
    }
    app.MG.ugc.newTopicMessage(data).then((res) => {
      if (res) {
        this.setData({
          isShowDialog: true
        })
      }
    })
  },
  // 新建子topicMessage
  // newTopicSubMessage(parentId,topicId) {
  //   const data = {
  //     description: '',
  //     icon: '',
  //     parentId,
  //     state: 'Normal',
  //     topicIdOrRefCode: String(topicId),
  //     name: this.data.answerTitle,
  //     content: '',
  //     type: 'Normal',
  //     cmsTypeRefCode: '',
  //     newDataListRequest: []
  //   }
  //   MG.ugc.newTopicMessage(data).then((res) => {
  //     if (res) {
  //     }
  //   })
  // },
});
packageBookService/pages/bookServices/examination/examination.json
@@ -6,7 +6,8 @@
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-image": "tdesign-miniprogram/image/image",
    "t-button": "tdesign-miniprogram/button/button",
    "t-loading": "tdesign-miniprogram/loading/loading"
    "t-loading": "tdesign-miniprogram/loading/loading",
    "join-dialog": "./components/joinGroupDialog/index"
  },
  "disableScroll": true
}
packageBookService/pages/bookServices/examination/examination.wxml
@@ -78,3 +78,8 @@
  overlay="{{false}}"
  bind:beforeleave="beforeleave"
></page-container>
<!-- 加入班级弹窗 -->
<join-dialog visible="{{visible}}" bind:joinClass="joinClass">
</join-dialog>
packageBookService/pages/bookServices/examination/questionOptions/index.js
@@ -258,7 +258,8 @@
        // wx.navigateBack();
      } else if (
        this.properties.answerType == "option" ||
        this.properties.answerType == "mock"
        this.properties.answerType == "mock" ||
        this.properties.answerType == "interaction"
      ) {
        if (this.properties.submitStatus) return wx.navigateBack();
        this.properties.questionDataList.forEach((item) => {
packageBookService/pages/bookServices/examination/questionOptions/index.wxml
@@ -1,48 +1,105 @@
<!--pages/bookServices/examination/questionOptions/index.wxml-->
<view class="page-bottom" style="color: {{isNight ? '#fff' : '#000'}}; background-color:{{isNight ? '#202020' : '#fff'}}">
  <view class="li-option" bind:tap="setCollect" style="color: {{isNight ? '#fff' : '#000'}};">
    <image src="{{ questionDataList[currentIndex].isCollect ? '/static/images/bookService/detail/collecting.png' : isNight ? '/static/images/bookService/examination/wodeshoucang-w.png' : '/static/images/bookService/examination/collect.png'}}" />
<view
  class="page-bottom"
  style="color: {{isNight ? '#fff' : '#000'}}; background-color:{{isNight ? '#202020' : '#fff'}}"
>
  <view
    class="li-option"
    bind:tap="setCollect"
    style="color: {{isNight ? '#fff' : '#000'}};"
    wx:if="{{answerType !== 'interaction'}}"
  >
    <image
      src="{{ questionDataList[currentIndex].isCollect ? '/static/images/bookService/detail/collecting.png' : isNight ? '/static/images/bookService/examination/wodeshoucang-w.png' : '/static/images/bookService/examination/collect.png'}}"
    />
    收藏
  </view>
  <view class="li-option" bind:tap="handlePopup" style="color: {{isNight ? '#fff' : '#000'}};">
    <image src="{{isNight ? '/static/images/bookService/examination/datika-w.png' : '/static/images/bookService/examination/questionCard.png'}}" />
  <view
    class="li-option"
    bind:tap="handlePopup"
    style="color: {{isNight ? '#fff' : '#000'}};"
  >
    <image
      src="{{isNight ? '/static/images/bookService/examination/datika-w.png' : '/static/images/bookService/examination/questionCard.png'}}"
    />
    答题卡
  </view>
  <view class="li-option" bind:tap="setUpBtn" style="color: {{isNight ? '#fff' : '#000'}};">
    <image src="{{isNight ? '/static/images/bookService/examination/setting-w.png' : '/static/images/bookService/examination/setting.png'}}" />
  <view
    class="li-option"
    bind:tap="setUpBtn"
    style="color: {{isNight ? '#fff' : '#000'}};"
  >
    <image
      src="{{isNight ? '/static/images/bookService/examination/setting-w.png' : '/static/images/bookService/examination/setting.png'}}"
    />
    设置
  </view>
  <view class="li-option" bind:tap="resterBtn" wx:if="{{answerType == 'option' || (answerType == 'mock' && !submitStatus)}}" style="color: {{isNight ? '#fff' : '#000'}};">
    <image src="{{isNight ? '/static/images/bookService/examination/chongzuo-w.png' : '/static/images/bookService/examination/reset.png'}}" />
  <view
    class="li-option"
    bind:tap="resterBtn"
    wx:if="{{answerType == 'option' || (answerType == 'mock' && !submitStatus)}}"
    style="color: {{isNight ? '#fff' : '#000'}};"
  >
    <image
      src="{{isNight ? '/static/images/bookService/examination/chongzuo-w.png' : '/static/images/bookService/examination/reset.png'}}"
    />
    重做
  </view>
  <view class="bottom-submit">
    <t-button theme="primary" size="large" style="{{btnStyle}}" bind:tap="submitBtn">{{(answerType == 'option' || answerType == 'mock') && !submitStatus ?
      '提交' : '退出'}}</t-button>
    <t-button
      theme="primary"
      size="large"
      style="{{btnStyle}}"
      bind:tap="submitBtn"
      >{{(answerType == 'option' || answerType == 'mock' || answerType ==
      'interaction') && !submitStatus ? '提交' : '退出'}}</t-button
    >
  </view>
</view>
<!-- 答题卡 -->
<t-popup visible="{{questionCardState}}" bind:visible-change="onVisibleChange" placement="bottom">
<t-popup
  visible="{{questionCardState}}"
  bind:visible-change="onVisibleChange"
  placement="bottom"
>
  <view class="popup-block">
    <view class="popup-header">
      <view class="popup-title">答题卡</view>
    </view>
    <view class="question-explain">
      <view class="explain-answered" wx:if="{{answerType == 'collectQuestion' || answerType == 'errorQuestion' || (answerType == 'option' && !submitStatus) || (answerType == 'mock' && !submitStatus)}}">
      <view
        class="explain-answered"
        wx:if="{{answerType == 'collectQuestion' || answerType == 'errorQuestion' || (answerType == 'option' && !submitStatus) || (answerType == 'mock' && !submitStatus)}}"
      >
        <text class="answered explain-color-box"></text>
        <text>已答</text>
      </view>
      <view class="explain-un-answered" wx:if="{{answerType == 'collectQuestion' || answerType == 'errorQuestion' || (answerType == 'option' && !submitStatus) || (answerType == 'mock' && !submitStatus)}}">
      <view
        class="explain-un-answered"
        wx:if="{{answerType == 'collectQuestion' || answerType == 'errorQuestion' || (answerType == 'option' && !submitStatus) || (answerType == 'mock' && !submitStatus)}}"
      >
        <text class="un-answered explain-color-box"></text>
        <text>未答</text>
      </view>
      <view class="error-box" wx:if="{{((answerType == 'option' || answerType == 'mock') && submitStatus) || answerType == 'collectQuestion' || answerType == 'errorQuestion'}}">
        <text class="explain-color-box" style="background-color: #ee1818"></text>
      <view
        class="error-box"
        wx:if="{{((answerType == 'option' || answerType == 'mock') && submitStatus) || answerType == 'collectQuestion' || answerType == 'errorQuestion'}}"
      >
        <text
          class="explain-color-box"
          style="background-color: #ee1818"
        ></text>
        <text>错误</text>
      </view>
      <view class="correct-box" wx:if="{{((answerType == 'option' || answerType == 'mock') && submitStatus) || answerType == 'collectQuestion' || answerType == 'errorQuestion'}}">
        <text class="explain-color-box" style="background-color: #1fbc1f"></text>
      <view
        class="correct-box"
        wx:if="{{((answerType == 'option' || answerType == 'mock') && submitStatus) || answerType == 'collectQuestion' || answerType == 'errorQuestion'}}"
      >
        <text
          class="explain-color-box"
          style="background-color: #1fbc1f"
        ></text>
        <text>正确</text>
      </view>
    </view>
@@ -54,7 +111,16 @@
          <text class="title-text">{{item.catalogName}}</text>
        </view>
        <view class="question-list">
          <view bind:tap="goQuestion" data-id="{{citem.id}}" wx:for="{{item.infoList}}" wx:for-item="citem" wx:for-index="cindex" wx:key="cindex" style="border: {{showId == citem.id ? '1px solid #ff6c00' : ''}}" class="question-box {{ citem.isUserAnswer ? 'answered' : 'un-answered' }} {{ ( ((answerType == 'option' || answerType == 'mock') && submitStatus || answerType == 'collectQuestion' || answerType == 'errorQuestion')  && citem.questionType !== 'shortAnswer' )  ? citem.isRight && citem.isComplete ? 'correct-box-color' : citem.isComplete && !citem.isRight ?  'error-box-color' : '' :''}} ">
          <view
            bind:tap="goQuestion"
            data-id="{{citem.id}}"
            wx:for="{{item.infoList}}"
            wx:for-item="citem"
            wx:for-index="cindex"
            wx:key="cindex"
            style="border: {{showId == citem.id ? '1px solid #ff6c00' : ''}}"
            class="question-box {{ citem.isUserAnswer ? 'answered' : 'un-answered' }} {{ ( ((answerType == 'option' || answerType == 'mock') && submitStatus || answerType == 'collectQuestion' || answerType == 'errorQuestion')  && citem.questionType !== 'shortAnswer' )  ? citem.isRight && citem.isComplete ? 'correct-box-color' : citem.isComplete && !citem.isRight ?  'error-box-color' : '' :''}} "
          >
            {{citem.number}}
          </view>
        </view>
@@ -64,7 +130,11 @@
</t-popup>
<!-- 设置 -->
<t-popup visible="{{setUpPopup}}" bind:visible-change="onSetUpChange" placement="bottom">
<t-popup
  visible="{{setUpPopup}}"
  bind:visible-change="onSetUpChange"
  placement="bottom"
>
  <view class="popup-block set-up-popup">
    <view class="popup-header">
      <view class="popup-title">设置</view>
@@ -81,27 +151,48 @@
          step="{{7}}"
          bind:change="onChangeSlider"
        /> -->
        <slider value="{{sliderValue}}" min="{{28}}" max="{{48}}" step="{{7}}" activeColor="#ff6c00" bind:change="onChangeSlider" />
        <slider
          value="{{sliderValue}}"
          min="{{28}}"
          max="{{48}}"
          step="{{7}}"
          activeColor="#ff6c00"
          bind:change="onChangeSlider"
        />
      </view>
      <text>A+</text>
    </view>
    <!-- 模式 -->
    <view>
      <t-radio-group class="test-radio" t-class="horizontal-box" value="{{radioItem}}" bind:change="onRadioChange" style="margin: 0px">
      <t-radio-group
        class="test-radio"
        t-class="horizontal-box"
        value="{{radioItem}}"
        bind:change="onRadioChange"
        style="margin: 0px"
      >
        <view class="card {{radioItem == 'daytime' ? 'card--active' : ''}}">
          <t-radio value="daytime" icon="none" borderless style="height: 100%">
            <view class="radio-content" slot="content">
              <image src="{{ radioItem == 'daytime' ? '/static/images/bookService/examination/rijian-click.png' : '/static/images/bookService/examination/rijian.png'}}" />
              <text style="color: {{radioItem == 'daytime' ? '#fff':''}};">日间模式</text>
              <image
                src="{{ radioItem == 'daytime' ? '/static/images/bookService/examination/rijian-click.png' : '/static/images/bookService/examination/rijian.png'}}"
              />
              <text style="color: {{radioItem == 'daytime' ? '#fff':''}};"
                >日间模式</text
              >
            </view>
          </t-radio>
        </view>
        <view class="card {{radioItem == 'night' ? 'card--active' : ''}}">
          <t-radio value="night" icon="none" borderless style="height: 100%">
            <view class="radio-content" slot="content">
              <image src="{{ radioItem == 'night' ? '/static/images/bookService/examination/yejian-click.png' : '/static/images/bookService/examination/yejian.png'}}" />
              <text style="color: {{radioItem == 'night' ? '#fff':''}};">夜间模式</text>
              <image
                src="{{ radioItem == 'night' ? '/static/images/bookService/examination/yejian-click.png' : '/static/images/bookService/examination/yejian.png'}}"
              />
              <text style="color: {{radioItem == 'night' ? '#fff':''}};"
                >夜间模式</text
              >
            </view>
          </t-radio>
        </view>
@@ -112,7 +203,13 @@
<!-- 测试报告 -->
<t-dialog class="test-report" visible="{{testReportState}}" confirm-btn="{{null}}" title="测试报告" bind:close="closeTestReportDialog">
<t-dialog
  class="test-report"
  visible="{{testReportState}}"
  confirm-btn="{{null}}"
  title="测试报告"
  bind:close="closeTestReportDialog"
>
  <view slot="content" class="test-report">
    <view class="report-content-top">
      <view class="report-li">
@@ -125,24 +222,34 @@
      </view>
      <view class="report-li">
        <view class="report-li-left">其中客观题:</view>
        <view class="report-li-right">{{subjectiveTotal}}道,分值{{subjectiveGrade}}分</view>
        <view class="report-li-right"
          >{{subjectiveTotal}}道,分值{{subjectiveGrade}}分</view
        >
      </view>
      <view class="report-li">
        <view class="report-li-left">答对:</view>
        <view class="report-li-right"><text class="correct-color">{{correctNum}}</text> 道</view>
        <view class="report-li-right"
          ><text class="correct-color">{{correctNum}}</text> 道</view
        >
      </view>
      <view class="report-li">
        <view class="report-li-left">答错:</view>
        <view class="report-li-right"><text class="error-color">{{subjectiveTotal - correctNum}}</text>
          道</view>
        <view class="report-li-right"
          ><text class="error-color">{{subjectiveTotal - correctNum}}</text>
          道</view
        >
      </view>
      <view class="report-li">
        <view class="report-li-left">客观题得分:</view>
        <view class="report-li-right"><text class="score-color">{{subjectiveNum}}</text> 分</view>
        <view class="report-li-right"
          ><text class="score-color">{{subjectiveNum}}</text> 分</view
        >
      </view>
    </view>
    <view class="report-content-bottom">
      <t-button theme="primary" bind:tap="viewAnswer" style="width: 560rpx">查看答案与解析</t-button>
      <t-button theme="primary" bind:tap="viewAnswer" style="width: 560rpx"
        >查看答案与解析</t-button
      >
    </view>
  </view>
</t-dialog>
packageBookService/pages/bookServices/examination/questionSchedule/index.wxml
@@ -1,6 +1,6 @@
<!--pages/bookServices/examination/questionSchedule/questionSchedule.wxml-->
<view
  wx:if="{{((answerType == 'option' || answerType == 'mock') && !submitStatus) || answerType == 'collectQuestion' || answerType =='errorQuestion'}}"
  wx:if="{{((answerType == 'option' || answerType == 'mock' || answerType == 'interaction') && !submitStatus) || answerType == 'collectQuestion' || answerType =='errorQuestion'}}"
  class="schedule"
  id="schedule"
  style="background-color:{{isNight ? '#1a1a1a' : '#fff'}}"
@@ -10,16 +10,14 @@
    <view
      class="question-schedule"
      style="color: {{isNight ? '#fff' : '#000'}};"
      >{{(answerType == 'option' || answerType == 'mock') ? '答题进度' :
      answerType == 'collectQuestion' ? '收藏数量' : '错题数量' }}<text
        class="parimary-color question-num"
        >{{ready}}</text
      >
      >{{(answerType == 'option' || answerType == 'mock' || answerType ==
      'interaction') ? '答题进度' : answerType == 'collectQuestion' ? '收藏数量'
      : '错题数量' }}<text class="parimary-color question-num">{{ready}}</text>
      <text>/{{questionList.length}}</text>
    </view>
    <view
      class="remainder"
      wx:if="{{!submitStatus && (answerType == 'option' || answerType == 'mock')}}"
      wx:if="{{!submitStatus && (answerType == 'option' || answerType == 'mock' || answerType == 'interaction')}}"
      style="color:  {{isNight ? '#fff' : '#000'}};"
    >
      <view class="remainder-text">剩余时间 </view>
packageCourse/components/baseClass/index.js
New file
@@ -0,0 +1,164 @@
// packageCourse/components/baseClass/index.js
const app = getApp()
import moment from "moment"
import {
  getPublicImage
} from '../../../assets/js/middleGround/tool'
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    classId: {
      type: Number,
      default: 0
    },
    bookRefCode: {
      type: String,
      default: ''
    }
  },
  /**
   * 组件的初始数据
   */
  data: {
    noticeList: [],
    detailInfo: null,
    messageInfo: null,
    homeworkCount: 0,
    userData: null,
    bookData: null,
    defaultCmsPath: ''
  },
  ready() {
    const data = wx.getStorageSync('website-front-userInfo')
    if (data) {
      this.setData({
        userData: JSON.parse(data),
        defaultCmsPath: this.properties.bookRefCode
      })
    }
    this.getData()
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // 获取班级
    getData() {
      app.MG.edu
        .getCourseClass({
          ClassIdOrRefCode: String(this.properties.classId)
        })
        .then((res) => {
          if (res) {
            res.bookName = res.linkProductDto.product.name
            res.bookId = res.linkProductDto.product.id
            res.bookIcon = getPublicImage(res.linkProductDto.product.icon, 100)
            res.classTime =
              moment(res.beginDate).format('YYYY.MM.DD') +
              '--' +
              moment(res.endDate).format('YYYY.MM.DD')
          }
          this.setData({
            detailInfo: res
          })
          this.getTopicInfo()
          this.getBookDetail(res.bookId)
        })
    },
    // 获取教材详情
    getBookDetail(shopId) {
      let query = {
        path: this.data.defaultCmsPath,
        queryType: '*',
        productId: String(shopId),
        storeInfo: this.data.defaultCmsPath,
        coverSize: {
          height: 300,
          width: 210
        },
        fields: {
          seriesName: [],
          author: [],
          isbn: [],
          publicationDate: []
        }
      }
      app.MG.store.getProductDetail(query).then(async (res) => {
        if (res?.datas) {
          if (!res.datas.author) {
            res.datas.author = '-'
          }
          this.setData({
            bookData: res.datas
          })
        }
      })
    },
    // 获取topic
    getTopicInfo() {
      const pramas = {
        classId: this.properties.classId,
        refCodes: [app.config.refCodes.message]
      }
      app.MG.edu.getClassTopic(pramas).then((res) => {
        const data = res.find((item) => item.refCode == app.config.refCodes.message)
        this.setData({
          messageInfo: data
        })
        if (data.id) {
          wx.setStorageSync('messageId', data.id)
          this.getNotice()
        }
      })
    },
    // 获取班级通知
    getNotice() {
      const data = {
        start: 0,
        size: 3,
        appRefCode: app.config.appRefCode,
        topicIdOrRefCode: String(this.data.messageInfo.id),
        sort: {
          type: 'Desc',
          field: 'CreateDate',
          subSorts: []
        }
      }
      app.MG.ugc.getTopicMessageList(data).then((res) => {
        const list = res.datas.map((item) => {
          return {
            ...item,
            createDate: moment(item.createDate).format('YYYY-MM-DD')
          }
        })
        this.setData({
          noticeList: list
        })
      })
    },
    //复制
    copyCode() {
      wx.setClipboardData({
        data: this.data.detailInfo.refCode,
        success(res) {
          wx.hideToast()
          wx.showToast({
            title: '邀请码已复制',
            duration: 1000,
            icon: 'none',
          })
        }
      })
    }
  }
})
packageCourse/components/baseClass/index.json
New file
@@ -0,0 +1,9 @@
{
  "component": true,
  "navigationBarTitleText": "基本信息",
  "usingComponents": {
    "t-avatar": "tdesign-miniprogram/avatar/avatar",
    "t-button": "tdesign-miniprogram/button/button",
    "empty": "/components/empty/index"
  }
}
packageCourse/components/baseClass/index.wxml
New file
@@ -0,0 +1,52 @@
<!--packageCourse/components/baseClass/index.wxml-->
<view class="baseContent">
  <view class="pubCss">
    <view class="title">班级概览</view>
    <view class="statics">
      <view class="classCout">
        <text>班级人数</text>
        <text style="color: #ff6d00">{{ detailInfo.memberCount }} / {{ detailInfo.maxUserCount }}</text>
      </view>
      <view class="classCout">
        <text>作业次数</text>
        <text style="color: #ff6d00">{{homeworkCount}}</text>
      </view>
    </view>
  </view>
  <view class="pubCss">
    <view class="roleInfo">
      <text class="title" wx:if="{{userData.role == 'Teacher'}}">班级助教</text>
      <text class="title" wx:else>班级学生</text>
      <t-button style="margin: 0;" size="large" bindtap="copyCode" size="extra-small" variant="outline">邀请码</t-button>
    </view>
    <view class="userInfo">
      <t-avatar wx:if="{{userData.icon}}" class="avatar-example" size="small" image="{{userData.icon}}" />
      <text class="user">{{userData.name}}</text>
    </view>
  </view>
  <view class="pubCss">
    <view class="title">课程教材</view>
    <view class="bookData">
      <view class="bookText">
        <text>{{detailInfo.bookName}}</text>
        <text>作者:{{bookData.author}}</text>
        <text>ISBN:{{bookData.isbn}}</text>
      </view>
      <image wx:if="{{detailInfo.bookIcon}}" class="bookIcon" src="{{detailInfo.bookIcon}}" mode="" />
      <image wx:else class="bookIcon" src="/static/images/default-book-img.png" mode="" />
    </view>
  </view>
  <view class="pubCss">
    <view class="title">开课时间</view>
    <view class="courseTime">{{detailInfo.classTime}}</view>
  </view>
  <view class="pubCss">
    <view class="title">班级通知</view>
    <view class="notice" wx:if="{{noticeList.length > 0}}">
      <view class="notice-title" wx:for="{{noticeList}}" wx:key="index">{{item.name}}</view>
    </view>
    <view class="noData" wx:else>
      <empty />
    </view>
  </view>
</view>
packageCourse/components/baseClass/index.wxss
New file
@@ -0,0 +1,109 @@
/* packageCourse/components/baseClass/index.wxss */
.baseContent {
  width: 100%;
  height: 100%;
  font-size: 28rpx;
  overflow: auto;
}
.title {
  font-size: 28rpx;
  font-weight: 700;
  padding: 20rpx;
  box-sizing: border-box;
  background-color: #f8f8f8;
  margin-bottom: 10rpx;
}
.statics {
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
}
.statics .classCout {
  width: 100px;
  height: 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
  border: 1rpx solid #999;
  border-radius: 10rpx;
}
.pubCss {
  width: 100%;
  padding: 20rpx 0;
  box-sizing: border-box;
}
.roleInfo {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  background-color: #f8f8f8;
}
.userInfo {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 20rpx 30rpx;
  box-sizing: border-box;
}
.user {
  margin-left: 20rpx;
}
.bookInfo {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
}
.bookData {
  width: 100%;
  height: 200rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 30rpx;
  box-sizing: border-box;
}
.bookText {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: flex-start;
  font-size: 26rpx;
  overflow: hidden;
}
.bookIcon {
  width: 180rpx;
  height: 230rpx;
}
.courseTime {
  padding: 20rpx 30rpx;
  box-sizing: border-box;
}
.notice {
  padding: 20rpx 30rpx;
  box-sizing: border-box;
  font-size: 26rpx;
}
.noData {
  display: flex;
  justify-content: center;
}
packageCourse/components/questionDom/index.js
New file
@@ -0,0 +1,46 @@
// packageCourse/components/questionDom/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    questionList: {
      type: Array,
      default: []
    },
    noCheckbox: {
      type: Boolean,
      default: false
    },
    isDelete: {
      type: Boolean,
      default: false
    },
    isPreview: {
      type: Boolean,
      default: false
    },
    isJudge: {
      type: Boolean,
      default: false
    },
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  ready() {
    console.log(this.properties.questionList, 'tweuywteuwyteu')
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})
packageCourse/components/questionDom/index.json
New file
@@ -0,0 +1,11 @@
{
  "component": true,
  "usingComponents": {
    "t-checkbox": "tdesign-miniprogram/checkbox/checkbox",
    "t-checkbox-group": "tdesign-miniprogram/checkbox-group/checkbox-group",
    "t-input": "tdesign-miniprogram/input/input",
    "t-textarea": "tdesign-miniprogram/textarea/textarea",
    "t-radio": "tdesign-miniprogram/radio/radio",
    "t-radio-group": "tdesign-miniprogram/radio-group/radio-group"
  }
}
packageCourse/components/questionDom/index.wxml
New file
@@ -0,0 +1,54 @@
<!--packageCourse/components/questionDom/index.wxml-->
<view class="questionDom">
  <view class="list-item" wx:for="{{questionList}}" wx:for-item="pitem" wx:key="pindex">
    <view class="list-item-title">
      {{ pitem.name }}
      <text wx:if="isPreview">(共{{ pitem.data.length }}题<text wx:if="{{pitem.totalScore>0}}">,总分:{{ pitem.totalScore }} 分</text>)</text>
    </view>
    <view class="list-item-box" wx:for="{{pitem.data}}" wx:for-item="item" wx:key="index">
      <view class="list-item-box-title-box">
        <t-checkbox wx:if="{{noCheckbox}}" block="{{false}}" value="{{item.isCheck}}">
          <view Slot='label'>
            <rich-text nodes="{{item.questionStem.stemTxt}}"></rich-text>
          </view>
        </t-checkbox>
        <view class="list-item-box-title" wx:else>
          <p class="questionT">
            <text>{{index+1}}、</text>
            <rich-text nodes="{{item.questionStem.stemTxt}}"></rich-text>
          </p>
        </view>
      </view>
      <view class="questionData">
        <view class="shortAnswer" wx:if="{{item.questionType == 'shortAnswer'}}">
          <t-textarea disabled placeholder="{{item.userAnswer}}" />
        </view>
        <view class="discuss" wx:if="{{item.questionType == 'discuss'}}">
          <t-textarea disabled placeholder="{{item.userAnswer}}" />
        </view>
        <view class="discuss" wx:if="{{item.questionType == 'completion'}}">
          <t-textarea disabled placeholder="{{item.userAnswer}}" />
        </view>
        <view class="judge" wx:if="{{item.questionType == 'judge'}}">
          <t-radio-group>
            <t-radio block="{{false}}" wx:for="{{item.questionOption}}" wx:for-item="{{ritem}}" wx:index="{{ritem.index}}" label="{{ritem.value}}" value="{{ritem.value}}" />
          </t-radio-group>
        </view>
        <view class="singleChoice" wx:if="{{item.questionType == 'singleChoice'}}">
          <t-radio-group>
            <t-radio block="{{false}}" wx:for="{{item.questionOption}}" wx:for-item="{{ritem}}" wx:index="{{ritem.index}}" label="{{ritem.value}}" value="{{ritem.value}}">
              <text>{{ritem.value}}</text>. <text>{{ritem.txt}}</text>
            </t-radio>
          </t-radio-group>
        </view>
        <view class="multipleChoice" wx:if="{{item.questionType == 'multipleChoice'}}">
          <t-checkbox-group>
            <t-checkbox block="{{false}}" wx:for="{{item.questionOption}}" wx:for-item="{{mitem}}" wx:index="{{mitem.index}}" label="{{mitem.value}}" value="{{mitem.value}}">
              <text>{{mitem.value}}</text>. <text>{{mitem.txt}}</text>
            </t-checkbox>
          </t-checkbox-group>
        </view>
      </view>
    </view>
  </view>
</view>
packageCourse/components/questionDom/index.wxss
New file
@@ -0,0 +1,29 @@
/* packageCourse/components/questionDom/index.wxss */
.questionDom {
  padding: 10rpx 20rpx;
  box-sizing: border-box;
}
.list-item-title {
  margin-bottom: 20rpx;
}
.list-item-box-title-box {
  margin-bottom: 20rpx;
}
.questionT {
  display: flex;
  align-items: flex-start;
}
.shortAnswer,
.discuss {
  padding: 20rpx;
  line-height: 22px;
  border: 1px solid #eee;
  border-radius: 5px;
  font-size: 28rpx;
  box-sizing: border-box;
}
packageCourse/components/studentManage/index.js
New file
@@ -0,0 +1,257 @@
// packageCourse/components/studentManage/index.js
const app = getApp();
import moment from "moment"
import {
  getPublicImage
} from '../../../assets/js/middleGround/tool'
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    classId: {
      type: Number,
      default: 0
    },
  },
  /**
   * 组件的初始数据
   */
  data: {
    userList: [],
    page: 1,
    limit: 10,
    total: 0,
    searchKey: '',
    currentIdentity: null,
    showConfirm: false,
    radioVal: 'RefCode',
    roleList: [{
      name: '学生',
      value: 'RefCode'
    }, {
      name: '助教',
      value: 'Teacher'
    }],
    // 加载参数
    bottomLoading: false,
    isMoreData: false,
    // 返回顶部
    isBackTop: false,
    setScrollValue: 0,
    skeletonLoding: true
  },
  ready() {
    this.getStudentList()
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // 获取学生列表
    getStudentList(isReachBottom) {
      const data = {
        start: (this.data.page - 1) * this.data.limit,
        size: this.data.limit,
        searchList: this.data.searchKey ? [{
          keywords: this.data.searchKey,
          field: 'Name',
          compareType: 'Contains'
        }] : [],
        groupId: this.properties.classId
      }
      app.MG.identity.getGroupUserList(data).then((res) => {
        const {
          datas,
          totalSize
        } = res
        if (datas.length > 0) {
          let list = datas.map((item, index) => {
            if (item.linkType == 'Creator') {
              const userInfo = item.appUser?.infoList?.find((citem) => citem.type == 'teacherInfo')
              item.appUser.name = userInfo.name
              item.appUser.icon = userInfo.icon
              if (userInfo?.data) {
                const iconData = JSON.parse(userInfo.data)
                item.appUser.icon = getPublicImage(iconData?.relevantCertificates[0]?.md5, 100) ?? ''
              }
            }
            if (item.linkType == 'RefCode' || item.linkType == 'Teacher') {
              let userInfo = null
              const wechatData = item.appUser?.infoList?.find((citem) => citem.type == 'WeChat')
              const defaultData = item.appUser?.infoList?.find((citem) => citem.type == 'Default')
              userInfo = defaultData
              if (wechatData?.name) {
                userInfo = wechatData
              }
              item.appUser.name = userInfo.name
              item.appUser.icon = userInfo.icon
            }
            return {
              ...item,
              index: index + 1,
              createDate: moment(item.createDate).format('YYYY-MM-DD')
            }
          })
          //触底加载新数据并保留老数据
          if (isReachBottom) {
            list = [...this.data.courseList, ...list] //将新数据加入老数据中
          }
          this.setData({
            userList: list,
            total: res.totalSize,
            skeletonLoding: false,
            bottomLoading: false
          })
        }
      })
    },
    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh() {
      if (this._freshing) return
      this.setData({
        page: 1,
        bottomLoading: false,
        isMoreData: false
      })
      setTimeout(() => {
        this._freshing = true;
        this.setData({
          triggered: false,
        })
        this.getStudentList();
        this._freshing = false
      }, 500);
    },
    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom() {
      if (this.data.pageCourse.total > this.data.courseList.length) {
        this.setData({
          page: this.data.pageCourse.page + 1,
          bottomLoading: true,
          isMoreData: false
        })
      } else {
        this.setData({
          bottomLoading: false,
          isMoreData: true
        })
        return false;
      }
      this.getStudentList(true);
    },
    // 监听滚动距离
    onPageScroll(e) {
      if (e && e.scrollTop >= 1000) {
        this.setData({
          isBackTop: true
        })
      } else {
        this.setData({
          isBackTop: false
        })
      }
    },
    selectIdentity(e) {
      const {
        item
      } = e.currentTarget.dataset
      this.setData({
        showConfirm: true,
        currentIdentity: item
      })
    },
    onChangeVal(e) {
      this.setData({
        radioVal: e.detail.value
      });
    },
    // 移除学生
    removeStudent(e) {
      const {
        item
      } = e.currentTarget.dataset
      const data = {
        groupId: this.properties.classId,
        appUserIds: [item.appUser.id]
      }
      app.MG.identity.removeAppUserFromGroup(data).then((res) => {
        if (res) {
          wx.showToast({
            title: '已移除',
            duration: 1000,
            icon: 'none',
          })
          this.getStudentList()
          this.newNotice(item, 'remove')
        }
      })
    },
    cancle() {
      this.setData({
        showConfirm: false,
        radioVal: 'RefCode'
      })
    },
    // 更新状态
    updateStateNormal() {
      const data = {
        groupId: this.properties.classId,
        requests: [{
          linkId: this.data.currentIdentity.linkId,
          linkType: this.data.radioVal,
          state: 'Normal',
          groupState: 'Normal'
        }]
      }
      app.MG.identity.updateAppUserGroupLink(data).then((res) => {
        if (res) {
          wx.showToast({
            title: '已通过',
            duration: 1000,
            icon: 'success',
          })
          this.cancle()
          this.getStudentList()
          this.newNotice(this.data.currentIdentity, 'success')
        }
      })
    },
  },
  // 新建通知
  newNotice(item, type) {
    const messageId = wx.getStorageSync('messageId')
    const str = type == 'success' ? item.appUser.name + '--加入班级' : item.appUser.name + '--已被移除班级'
    if (!messageId) {
      return false
    }
    const data = {
      description: '',
      icon: '',
      state: 'Normal',
      topicIdOrRefCode: String(messageId),
      name: str,
      content: "",
      type: 'Normal',
      cmsTypeRefCode: '',
      newDataListRequest: []
    }
    app.MG.ugc
      .newTopicMessage(data)
      .then()
      .catch((err) => {
        console.log(err)
      })
  }
})
packageCourse/components/studentManage/index.json
New file
@@ -0,0 +1,18 @@
{
  "component": true,
  "navigationBarTitleText": "学生管理",
  "usingComponents": {
    "t-search": "tdesign-miniprogram/search/search",
    "t-input": "tdesign-miniprogram/input/input",
    "empty": "/components/empty/index",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "t-back-top": "tdesign-miniprogram/back-top/back-top",
    "t-avatar": "tdesign-miniprogram/avatar/avatar",
    "t-button": "tdesign-miniprogram/button/button",
    "t-loading": "tdesign-miniprogram/loading/loading",
    "t-popup": "tdesign-miniprogram/popup/popup",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-radio": "tdesign-miniprogram/radio/radio",
    "t-radio-group": "tdesign-miniprogram/radio-group/radio-group"
  }
}
packageCourse/components/studentManage/index.wxml
New file
@@ -0,0 +1,74 @@
<!--packageCourse/pages/course/index.wxml-->
<view class="contentBox" wx:if="{{!skeletonLoding}}">
  <view class="header">
    <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索名称" clearable />
  </view>
  <view class="contentList" wx:if="{{userList.length > 0}}">
    <scroll-view class="scroll" class="content" bind:scroll="onPageScroll" model:scroll-top="{{setScrollValue}}" scroll-y refresher-enabled="{{true}}" lower-threshold="{{80}}" refresher-threshold="{{80}}" refresher-default-style="none" refresher-triggered="{{triggered}}" bindrefresherpulling="{{refresh.onPulling}}" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">
      <view slot="refresher" class="refresh-container">
        <view class="loading">
          <t-loading theme="circular" size="40rpx" text="正在刷新..." class="wrapper" />
        </view>
      </view>
      <view class="userList" wx:for="{{userList}}" wx:key="index">
        <view class="userInfo">
          <t-avatar class="avatar-example" size="small" image="{{item.appUser.icon}}" />
          <text class="user">{{item.appUser.name}}</text>
        </view>
        <view>
          <t-button bindtap="selectIdentity" data-item="{{item}}" style="margin: 0;color: green;" size="extra-small" wx:if="{{item.state != 'Normal' && item.linkType != 'Creator'}}" variant="text">通过</t-button>
          <t-button bindtap="removeStudent" data-item="{{item}}" style="margin: 0;color: red;" size="extra-small" wx:if="{{item.linkType != 'Creator' }}" variant="text">移除</t-button>
          <text style="color: #ff6d00;" wx:if="{{item.linkType == 'Creator'}}">创建人</text>
        </view>
      </view>
      <view class="bottom-loading" wx:if="{{bottomLoading}}">
        <t-loading theme="circular" size="40rpx" text="加载中..." class="wrapper" />
      </view>
      <view class="bottom-loading" style="color: #ccc;font-size: 28rpx;" wx:if="{{isMoreData}}">
        <text>没有更多了</text>
      </view>
    </scroll-view>
  </view>
  <view class="content" wx:if="{{userList.length == 0}}">
    <empty />
  </view>
</view>
<t-popup visible="{{showConfirm}}" usingCustomNavbar bind:visible-change="onVisibleChange" placement="center">
  <view class="block--select">
    <view class="block-select-title">设置用户角色</view>
    <view class="block-select-radio">
      <t-radio-group class="groupRadio" value="{{radioVal}}" borderless t-class="box" bind:change='onChangeVal'>
        <view wx:for="{{roleList}}" wx:key="index">
          <t-radio class="radioItem" style="font-size: 30rpx;" block="{{false}}" label="{{item.name}}" value="{{item.value}}" />
        </view>
      </t-radio-group>
    </view>
    <view class="block-select-confirm">
      <t-button style="padding: 0 25px; margin:0 15px;" size="small" bindtap="cancle" variant="outline">取消</t-button>
      <t-button style="padding: 0 25px; margin:0;" size="small" bindtap="updateStateNormal">确认</t-button>
    </view>
  </view>
</t-popup>
<!-- 骨架屏 -->
<view wx:if="{{skeletonLoding}}">
  <view class="group" wx:for="{{10}}" wx:key="index">
    <t-skeleton class="group-avatar" rowCol="{{[{ size: '96rpx', type: 'circle' }]}}" loading></t-skeleton>
    <t-skeleton class="group-content" rowCol="{{[{ width: '50%' }, { width: '100%' }]}}" loading></t-skeleton>
  </view>
</view>
<wxs module="refresh">
  module.exports = {
    onPulling: function (evt, instance) {
      var p = Math.min(evt.detail.dy / 80, 1)
      var view = instance.selectComponent('.refresh-container')
      view.setStyle({
        opacity: p,
        transform: "scale(" + p + ")"
      })
    }
  }
</wxs>
packageCourse/components/studentManage/index.wxss
New file
@@ -0,0 +1,132 @@
/* packageCourse/pages/course/index.wxss */
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
.contentBox {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom));
  background-color: #eee;
  font-size: 28rpx;
}
.refresh-container {
  display: block;
  width: 100vw;
}
.loading {
  display: block;
  width: 100%;
  height: 180rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.bottom-loading {
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100rpx;
  padding-bottom: env(safe-area-inset-bottom);
}
.header {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
  border-top: 1rpx solid #eee;
  border-bottom: 1rpx solid #eee;
  background-color: #fff;
}
.header .t-search__input-box {
  height: 70rpx !important;
  font-size: 28rpx;
}
.contentList {
  width: 100%;
  height: calc(100% - 55px);
  background-color: #fff;
}
.content {
  height: 100%;
}
.userList {
  padding: 20rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-sizing: border-box;
  border-bottom: 1rpx solid #eee;
}
.userInfo {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.user {
  margin-left: 20rpx;
}
.block--select {
  width: 85vw;
  height: 180px;
  border-radius: 10rpx;
}
.block-select-title {
  font-size: 30rpx;
  padding: 20rpx;
  box-sizing: border-box;
  border-bottom: 1px solid #eee;
}
.block-select-radio .groupRadio {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 70rpx 20rpx;
  box-sizing: border-box;
}
.block-select-radio .radioItem {
  margin: 0 20rpx;
}
.block-select-confirm {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 0 20rpx;
}
/* gujia */
.group {
  display: flex;
  align-items: center;
  margin-top: 32rpx;
  margin-bottom: 30rpx;
}
.group-avatar {
  margin-right: 24rpx;
}
.group-content {
  width: 566rpx;
}
packageCourse/components/teaching/index.js
New file
@@ -0,0 +1,296 @@
// packageCourse/components/teaching/index.js
const app = getApp()
import moment from 'moment'
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    classId: {
      type: Number,
      default: 0
    },
    bookId: {
      type: Number,
      default: 0
    },
    rootCmsItemId: {
      type: Number,
      default: 0
    },
  },
  /**
   * 组件的初始数据
   */
  data: {
    searchKey: '',
    cmsDataList: [],
    teachInteractionInfo: null,
    dataList: [],
    loading: true
  },
  ready() {
    this.getData()
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // 搜索
    changeHandle(e) {
      const {
        value
      } = e.detail;
      let cmsList = this.data.cmsDataList
      this.setData({
        cmsDataList: []
      })
      if (value) {
        const data = cmsList.filter(item => item.name.indexOf(value) > -1);
        this.setData({
          cmsDataList: data
        })
      }
      if (value == '') {
        this.setData({
          cmsDataList: [],
          loading: true
        })
        this.getCmsList()
      }
    },
    // 获取班级
    getData() {
      app.MG.edu
        .getCourseClass({
          ClassIdOrRefCode: String(this.properties.classId)
        })
        .then((res) => {
          if (res) {
            this.setData({
              currentClass: res
            })
          }
          this.getTopicInfo()
        })
    },
    // 获取教学互动
    getCmsList() {
      app.MG.store
        .getProductDetail({
          path: '*',
          queryType: '*',
          productId: this.properties.bookId,
          cmsPath: this.properties.rootCmsItemId
        })
        .then((res) => {
          try {
            const data = res.datas.cmsDatas[0]?.datas.find((item) => item.refCode == 'questionBank')
            app.MG.store
              .getProductDetail({
                path: '*',
                queryType: '*',
                productId: this.properties.bookId,
                cmsPath: data.productLinkPath
              })
              .then((res) => {
                const dataTeach = res.datas.cmsDatas[0]?.datas.find(
                  (item) => item.refCode == 'jsek_interaction'
                )
                app.MG.store
                  .getProductDetail({
                    path: '*',
                    queryType: '*',
                    productId: this.properties.bookId,
                    cmsPath: dataTeach.productLinkPath
                  })
                  .then((res) => {
                    let datas = res.datas.cmsDatas[0] ? res.datas.cmsDatas[0].datas : []
                    const dataRes = []
                    if (datas?.length > 0) {
                      datas.forEach(async (item) => {
                        const cmsRes = await app.MG.store
                          .getProductDetail({
                            path: '*',
                            queryType: '*',
                            productId: this.properties.bookId,
                            cmsPath: item.productLinkPath
                          })
                        if (
                          cmsRes.datas.cmsDatas[0].datas &&
                          cmsRes.datas.cmsDatas[0].datas.length > 0
                        ) {
                          cmsRes.datas.cmsDatas[0].datas.forEach((item, i) => {
                            item.subList = []
                            item.updateDate = '-'
                            if (this.data.dataList.length > 0) {
                              this.data.dataList.forEach((mitem) => {
                                if (mitem.name == item.name) {
                                  item.updateDate = moment(mitem.updateDate).format(
                                    'YYYY-MM-DD HH:mm:ss'
                                  )
                                  item.subList.push(mitem)
                                }
                              })
                            }
                            dataRes.push(item)
                          })
                        }
                        const resList = dataRes.sort((a, b) => b.subList.length - a.subList.length)
                        this.setData({
                          cmsDataList: resList,
                          loading: false
                        })
                      })
                    }
                  })
              })
          } catch (error) {
            this.setData({
              cmsDataList: [],
              loading: false
            })
          }
        })
    },
    // 获取Messsagetopic
    getTopicInfo() {
      const pramas = {
        classId: this.properties.classId,
        refCodes: [app.config.refCodes.teachInteraction]
      }
      app.MG.edu.getClassTopic(pramas).then((res) => {
        const list = res
        const data = list.find(
          (item) => item.refCode == app.config.refCodes.teachInteraction
        )
        if (data.id) {
          this.setData({
            teachInteractionInfo: data
          })
          this.getMessage()
        }
      })
    },
    // 获取当前MessageList
    getMessage() {
      const data = {
        start: 0,
        size: 999,
        appRefCode: app.config.appRefCode,
        topicIdOrRefCode: String(this.data.teachInteractionInfo.id),
        sort: {
          type: 'Desc',
          field: 'CreateDate'
        },
      }
      app.MG.ugc.getTopicMessageList(data).then((res) => {
        const data = res.datas.map((item) => {
          item.question = []
          item.bookId = null
          item.path = ''
          try {
            const obj = JSON.parse(item.content)
            if (obj.bookId) {
              item.question = obj.content
              item.bookId = obj.bookId
              item.path = obj.path
              item.userName = obj.userName
            }
          } catch (error) {
            console.log(item)
          }
          return {
            ...item
          }
        })
        this.setData({
          dataList: data
        })
        this.getCmsList()
      })
    },
    // 处理数据结构
    chageData(arr, zrr) {
      let newData = []
      // 题库题目类型
      const questionTypeList = [{
          name: '单选题',
          value: 'singleChoice',
          data: []
        },
        {
          name: '多选题',
          value: 'multipleChoice',
          data: []
        },
        {
          name: '判断题',
          value: 'judge',
          data: []
        },
        {
          name: '简答题',
          value: 'shortAnswer',
          data: []
        },
        {
          name: '论述题',
          value: 'discuss',
          data: []
        },
        {
          name: '填空题',
          value: 'completion',
          data: []
        },
        {
          name: '连线题',
          value: 'matching',
          data: []
        },
        {
          name: '分类题',
          value: 'classification',
          data: []
        }
      ]
      for (let i = 0; i < arr.length; i++) {
        const item = arr[i]
        item.questionTypeList = questionTypeList
        for (let j = 0; j < zrr.length; j++) {
          const ele = zrr[j]
          const qusObj = item.question.find((citem) => citem.cmsItemId == ele.id)
          if (qusObj?.cmsItemId) {
            ele.userAnswer = qusObj.answer
            const index = findIndexByValue(questionTypeList, ele.questionType)
            if (index > -1) {
              item.questionTypeList[index].data.push(ele)
            }
          }
        }
        item.questionTypeList = item.questionTypeList.filter((item) => item.data.length > 0)
        newData.push(item)
      }
      return newData.filter((item) => item.questionTypeList.length > 0)
    },
    findIndexByValue(res, type) {
      for (let i = 0; i < res.length; i++) {
        if (res[i].value == type) {
          return i
        }
      }
      return -1 // 如果未找到,则返回 -1
    },
    toDetail(e) {
      const {
        item
      } = e.currentTarget.dataset
      wx.navigateTo({
        url: '/packageCourse/pages/teachClass/detail/index?questionName=' + item.name + '&teachInteractionId=' + this.data.teachInteractionInfo.id + '&bookId=' + this.properties.bookId + '&questionId=' + item.id,
      })
    }
  }
})
packageCourse/components/teaching/index.json
New file
@@ -0,0 +1,8 @@
{
  "component": true,
  "usingComponents": {
    "t-search": "tdesign-miniprogram/search/search",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "empty": "/components/empty/index"
  }
}
packageCourse/components/teaching/index.wxml
New file
@@ -0,0 +1,33 @@
<!--packageCourse/components/teaching/index.wxml-->
<view class="teachBoxContent">
  <view class="header">
    <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索名称" clearable />
  </view>
  <view class="count">共计 <text style="color: #ff6d00;">{{cmsDataList.length}}</text> 套题</view>
  <view class="teachContent" wx:if="{{cmsDataList.length > 0 && !loading}}">
    <view class="list" wx:for="{{cmsDataList}}" wx:key="index" bindtap="toDetail" data-item="{{item}}">
      <view class="questionName">{{item.name}}</view>
      <view class="questionInfo">
        <view class="questionCount">
          <text style="color: #ff6d00">{{ item.subList.length }}</text>
          <text> /</text>
          <text style="color: #67c23a"> {{ currentClass.memberCount }}</text>
          <text style="margin-left: 20rpx;color: #999;font-size:24rpx">(已答/全部)</text>
        </view>
        <view class="questionDate">
          {{item.updateDate}}
        </view>
      </view>
    </view>
  </view>
  <view class="she" wx:if="{{loading}}">
    <view wx:for="{{10}}" wx:for-item="themeItem" wx:key="index">
      <view class="demo-section__content">
        <t-skeleton theme="text"></t-skeleton>
      </view>
    </view>
  </view>
  <view class="she" wx:if="{{cmsDataList.length == 0 && !loading}}">
    <empty />
  </view>
</view>
packageCourse/components/teaching/index.wxss
New file
@@ -0,0 +1,50 @@
/* packageCourse/components/teaching/index.wxss */
.teachBoxContent {
  width: 100%;
  height: 100%;
  font-size: 28rpx;
}
.header {
  width: 100%;
  padding: 0 20rpx;
  box-sizing: border-box;
}
.teachContent {
  width: 100%;
  height: calc(100% - 82px);
  padding: 20rpx;
  box-sizing: border-box;
  overflow: auto;
}
.teachBoxContent .count {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
  border-bottom: 1rpx solid #eee;
}
.teachContent .questionInfo {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.teachContent .questionName {
  margin-bottom: 10rpx;
}
.teachContent .list {
  width: 100%;
  margin-bottom: 20rpx;
  border-bottom: 1px solid #eee;
  padding: 20rpx 0;
  box-sizing: border-box;
}
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
packageCourse/pages/course/detail/index.js
New file
@@ -0,0 +1,457 @@
// packageCourse/pages/course/detail/index.js
const app = getApp()
import moment from 'moment'
Page({
  data: {
    detail: null,
    bookData: null,
    courseId: null,
    classCount: 0,
    tabVal: 'base',
    visible: false,
    visibleStart: false,
    visibleEnd: false,
    startTime: '',
    endTime: '',
    className: '',
    count: null,
    classList: [],
    searchKey: '',
    defaultCmsPath: '',
    pageClass: {
      page: 1,
      limit: 10,
      total: 0
    },
    courseLoading: true,
    classLoading: false,
    // 加载参数
    bottomLoading: false,
    isMoreData: false,
    // 返回顶部
    isBackTop: false,
    setScrollValue: 0,
    skeletonLoding: false
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    if (options.courseId) {
      this.setData({
        courseId: options.courseId,
        defaultCmsPath: options.bookRefCode != '' ? 'jsek_digitalTextbooks' : 'defaultGoodsStore3'
      })
    }
    wx.setNavigationBarTitle({
      title: '基本信息',
    })
    this.getData()
  },
  // 获取课程信息
  getData() {
    app.MG.edu
      .getCourseById({
        courseId: this.data.courseId
      })
      .then((res) => {
        setTimeout(() => {
          this.setData({
            detail: res,
            courseLoading: false
          })
          const shopId = res.linkProduct?.id
          this.getBookDetail(shopId)
          this.getClassTotal()
        }, 1000);
      })
  },
  // 获取班级数量
  getClassTotal() {
    app.MG.edu
      .getCourseClassList({
        courseId: this.data.courseId,
      })
      .then((res) => {
        this.setData({
          classCount: res.totalSize
        })
      })
  },
  // 获取教材详情
  getBookDetail(shopId) {
    let query = {
      path: this.data.defaultCmsPath,
      queryType: '*',
      productId: String(shopId),
      storeInfo: this.data.defaultCmsPath,
      coverSize: {
        height: 300,
        width: 210
      },
      fields: {
        seriesName: [],
        author: [],
        isbn: [],
        publicationDate: []
      }
    }
    app.MG.store.getProductDetail(query).then(async (res) => {
      if (res?.datas) {
        if (!res.datas.author) {
          res.datas.author = '-'
        }
        this.setData({
          bookData: res.datas
        })
      }
    })
  },
  // tab切换
  tabActive(e) {
    const {
      str
    } = e.currentTarget.dataset
    this.setData({
      tabVal: str,
      courseLoading: true,
      classLoading: true
    })
    if (str == 'base') {
      wx.setNavigationBarTitle({
        title: '基本信息',
      })
      this.getData()
    }
    if (str == 'class') {
      wx.setNavigationBarTitle({
        title: '班级管理',
      })
      this.getClassList()
    }
  },
  // 打开新建班级
  newClass() {
    this.setData({
      visible: true,
    })
  },
  onCourseNameInput(e) {
    this.setData({
      className: e.detail.value
    })
  },
  onCourseDescInput(e) {
    this.setData({
      count: e.detail.value
    })
  },
  // 打开选择日期
  openDateStart() {
    this.setData({
      visibleStart: true
    })
  },
  openDateEnd() {
    this.setData({
      visibleEnd: true
    })
  },
  // 选择日期
  handleConfirmStart(e) {
    const {
      value
    } = e.detail;
    const dateStr = moment(value).format('YYYY-MM-DD')
    this.setData({
      startTime: dateStr,
    });
  },
  handleConfirmEnd(e) {
    const {
      value
    } = e.detail;
    const dateStr = moment(value).format('YYYY-MM-DD')
    this.setData({
      endTime: dateStr,
    });
  },
  // 关闭申请
  cancle() {
    this.setData({
      visible: false
    })
  },
  // 申请提交班级
  submitClass() {
    if (!this.data.className) {
      wx.showToast({
        title: '请填写课程名称',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    if (!this.data.count) {
      wx.showToast({
        title: '请选择关联教材',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    if (!this.data.startTime || !this.data.endTime) {
      wx.showToast({
        title: '请选择班级有效期',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    app.MG.edu
      .newCourseClass({
        courseId: this.data.courseId,
        name: this.data.className,
        description: '',
        icon: '',
        type: 'class',
        beginDate: this.data.startTime,
        endDate: this.data.endTime,
        config: '',
        price: 0,
        maxUserCount: this.data.count
      })
      .then((res) => {
        if (res) {
          wx.showToast({
            title: '已申请开班',
            duration: 1000,
            icon: 'success',
          })
          this.setData({
            visible: false
          })
          this.getClassList()
        }
      })
      .catch((err) => {
        wx.showToast({
          title: '申请开班出错',
          duration: 1000,
          icon: 'err',
        })
        this.setData({
          visible: false
        })
      })
  },
  // 搜索班级
  changeHandle(e) {
    const {
      value
    } = e.detail;
    this.setData({
      searchKey: value,
      'pageClass.page': 1
    });
    this.getClassList()
  },
  // 获取班级列表
  getClassList(isReachBottom) {
    app.MG.edu
      .getCourseClassList({
        courseId: this.data.courseId,
        start: (this.data.pageClass.page - 1) * this.data.pageClass.limit,
        size: this.data.pageClass.limit,
        sort: {
          type: 'Desc',
          field: 'CreateDate'
        },
        filterList: [],
        searchList: this.data.searchKey ? [{
          keywords: this.data.searchKey,
          field: 'Name',
          compareType: 'Contains'
        }] : []
      })
      .then((res) => {
        let list = res.datas.map((item) => {
          return {
            ...item,
            name: item.name,
            id: item.id,
            icon: item.icon ? getPublicImage(item.icon, 80) : '',
            introduction: item.description,
            reason: item.applyReturnMsg ? JSON.parse(item.applyReturnMsg).reason : '',
            beginDate: moment(item.beginDate).format('YYYY-MM-DD') ?? '-',
            endDate: moment(item.endDate).format('YYYY-MM-DD') ?? '-'
          }
        })
        //触底加载新数据并保留老数据
        if (isReachBottom) {
          list = [...this.data.classList, ...list] //将新数据加入老数据中
        }
        this.setData({
          classList: list,
          'pageClass.total': res.totalSize,
          skeletonLoding: false,
          classLoading: false,
          bottomLoading: false
        })
      })
  },
  // 删除课程
  delClass(e) {
    const {
      delId
    } = e.currentTarget.dataset
    const data = {
      ids: [delId]
    }
    app.MG.edu
      .delCourseClass(data)
      .then((res) => {
        if (res) {
          wx.showToast({
            title: '已删除',
            duration: 1000,
            icon: 'success',
          })
          this.getClassList()
        }
      })
      .catch((err) => {
        wx.showToast({
          title: '删除失败',
          duration: 1000,
          icon: 'error',
        })
        console.log(err)
      })
  },
  // 班级详情
  todetail(e) {
    const {
      classId,
      item
    } = e.currentTarget.dataset
    if (item.applyState == 'WaitAudit' || item.applyState == 'Reject') {
      wx.showToast({
        title: item.applyState == 'WaitAudit' ? '审核中' : '未通过',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    wx.navigateTo({
      url: '/packageCourse/pages/teachClass/index?classId=' + classId + '&courseId=' + this.data.courseId + '&author=' + this.data.bookData.author + '&isbn=' + this.data.bookData.isbn + '&bookId=' + this.data.bookData.id + '&rootCmsItemId=' + this.data.bookData.rootCmsItemId + "&bookRefCode=" + this.data.defaultCmsPath,
    })
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    if (this._freshing) return
    this.setData({
      'pageClass.page': 1,
      bottomLoading: false,
      isMoreData: false
    })
    setTimeout(() => {
      this._freshing = true;
      this.setData({
        triggered: false,
      })
      this.getClassList();
      this._freshing = false
    }, 500);
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
    if (this.data.pageClass.total > this.data.classList.length) {
      this.setData({
        'pageClass.page': this.data.pageClass.page + 1,
        bottomLoading: true,
        isMoreData: false
      })
    } else {
      this.setData({
        bottomLoading: false,
        isMoreData: true
      })
      return false;
    }
    this.getClassList(true);
  },
  // 监听滚动距离
  onPageScroll(e) {
    if (e && e.scrollTop >= 1000) {
      this.setData({
        isBackTop: true
      })
    } else {
      this.setData({
        isBackTop: false
      })
    }
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {
  }
})
packageCourse/pages/course/detail/index.json
New file
@@ -0,0 +1,15 @@
{
  "usingComponents": {
    "t-button": "tdesign-miniprogram/button/button",
    "t-popup": "tdesign-miniprogram/popup/popup",
    "t-input": "tdesign-miniprogram/input/input",
    "t-search": "tdesign-miniprogram/search/search",
    "t-tag": "tdesign-miniprogram/tag/tag",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-calendar": "tdesign-miniprogram/calendar/calendar",
    "t-back-top": "tdesign-miniprogram/back-top/back-top",
    "empty": "/components/empty/index",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "t-loading": "tdesign-miniprogram/loading/loading"
  }
}
packageCourse/pages/course/detail/index.wxml
New file
@@ -0,0 +1,139 @@
<!--packageCourse/pages/course/detail/index.wxml-->
<view class="contentTabBox">
  <view class="showInfo">
    <!-- 基本信息 -->
    <view class="baseInfoBox" wx:if="{{tabVal == 'base'}}">
      <view wx:if="{{!courseLoading}}">
        <view class="ID-State">
          <text class="ID">ID: {{detail.id}}</text>
          <t-tag class="margin-16" size='large' wx:if="{{detail.applyState == 'WaitAudit'}}" variant="light" theme="warning">审核中</t-tag>
          <t-tag class="margin-16" size='large' wx:if="{{detail.applyState == 'Reject'}}" variant="light" theme="danger">未通过</t-tag>
          <t-tag class="margin-16" size='large' wx:if="{{detail.applyState == 'Normal'}}" variant="light" theme="success">使用中</t-tag>
        </view>
        <view class="statics">
          <view class="classCout">
            <text>班级总量</text>
            <text style="color: #ff6d00">{{classCount}}</text>
          </view>
        </view>
        <view class="courseInfo">
          <view class="intro">
            <view class="descTitle">课程简介</view>
            <view class="descCon">{{detail.description}}</view>
          </view>
          <view class="bookInfo">
            <view class="title">课程教材</view>
            <view class="bookData">
              <view class="bookText">
                <text>{{bookData.name}}</text>
                <text>作者:{{bookData.author}}</text>
                <text>ISBN:{{bookData.isbn}}</text>
              </view>
              <image wx:if="{{bookData.icon}}" class="bookIcon" src="{{bookData.icon}}" mode="" />
              <image wx:else class="bookIcon" src="/static/images/default-book-img.png" mode="" />
            </view>
          </view>
        </view>
      </view>
      <view class="block" wx:if="{{courseLoading}}">
        <view wx:for="{{4}}" wx:for-item="themeItem" wx:key="index">
          <view class="demo-section__content">
            <t-skeleton animation="gradient" theme="text"></t-skeleton>
            <t-skeleton animation="gradient" theme="paragraph"></t-skeleton>
          </view>
        </view>
      </view>
    </view>
    <!-- 班级管理 -->
    <view class="classBox" wx:else>
      <view class="header">
        <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索班级名称" clearable />
        <t-button bindtap="newClass" size="small" style="margin-left: 15rpx;">开班</t-button>
      </view>
      <view class="contentList" wx:if="{{classList.length > 0 && !classLoading}}">
        <scroll-view class="scroll" class="content" bind:scroll="onPageScroll" model:scroll-top="{{setScrollValue}}" scroll-y refresher-enabled="{{true}}" lower-threshold="{{80}}" refresher-threshold="{{80}}" refresher-default-style="none" refresher-triggered="{{triggered}}" bindrefresherpulling="{{refresh.onPulling}}" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">
          <view slot="refresher" class="refresh-container">
            <view class="loading">
              <t-loading theme="circular" size="40rpx" text="正在刷新..." class="wrapper" />
            </view>
          </view>
          <view class="listClass" wx:for="{{classList}}" wx:key="index" data-class-id="{{item.id}}" data-item="{{item}}" bindtap="todetail">
            <view class="titleBox">
              <text class="className">{{item.name}}</text>
              <text class="classId" style="color: #999; font-size: 12px;"> ID: {{item.id}}</text>
            </view>
            <view class="infoBox">
              <text class="classTag" wx:if="{{item.applyState == 'WaitAudit'}}" style="color: #ef9f29">状态: 审核中</text>
              <text class="classTag" wx:if="{{item.applyState == 'Normal'}}" style="color: #1dbd11">状态: 进行中</text>
              <text class="classTag" wx:if="{{item.applyState == 'Reject'}}" style="color: red">状态: 未通过</text>
              <t-icon bindtap="delClass" data-del-id="{{item.id}}" class="delete" wx:if="{{item.applyState == 'Reject'}}" name="delete" color="red" size='18px' />
              <text class="classTag" wx:if="{{item.applyState == 'Reject'}}" style="color: red">拒绝原因:{{ item.reason != '' ? item.reason : '-' }}</text>
              <text class="classTag">班级人数: {{ item.memberCount }} / {{ item.maxUserCount }}</text>
              <text class="classTag">有效期:{{ item.beginDate }} - {{ item.endDate }}</text>
            </view>
          </view>
          <view class="bottom-loading" wx:if="{{bottomLoading}}">
            <t-loading theme="circular" size="40rpx" text="加载中..." class="wrapper" />
          </view>
          <view class="bottom-loading" style="color: #ccc;font-size: 28rpx;" wx:if="{{isMoreData}}">
            <text>没有更多了</text>
          </view>
        </scroll-view>
      </view>
      <view class="content" wx:if="{{classList.length == 0 && !classLoading}}">
        <empty />
      </view>
      <view class="block" wx:if="{{classLoading}}">
        <view wx:for="{{4}}" wx:for-item="themeItem" wx:key="index">
          <view class="demo-section__content">
            <t-skeleton animation="gradient" theme="text"></t-skeleton>
            <t-skeleton animation="gradient" theme="paragraph"></t-skeleton>
          </view>
        </view>
      </view>
      <t-popup visible="{{visible}}" usingCustomNavbar placement="bottom">
        <view class="block-title">申请开班</view>
        <view class="block">
          <view class="classInfo">
            <view class="c-name">
              <text>班级名称</text>
              <t-input value="{{className}}" bindchange="onCourseNameInput" borderless clearable placeholder="请输入班级名称" />
            </view>
            <view class="c-desc">
              <text>班级人数</text>
              <t-input value="{{count}}" bindchange="onCourseDescInput" borderless placeholder="请输入班级人数" />
            </view>
            <view class="c-date">
              <text>班级有效期</text>
              <view class="dateBox">
                <view>
                  <t-input value="{{startTime}}" readonly borderless bindtap="openDateStart" placeholder="请选择开始时间" />
                  <t-calendar visible="{{visibleStart}}" bind:confirm="handleConfirmStart" />
                </view>
                <view>~</view>
                <view>
                  <t-input value="{{endTime}}" readonly borderless bindtap="openDateEnd" placeholder="请选择结束时间" />
                  <t-calendar visible="{{visibleEnd}}" bind:confirm="handleConfirmEnd" />
                </view>
              </view>
            </view>
          </view>
          <view class="classSubmit">
            <t-button style="padding: 0 25px; margin:0 15px;" size="small" bindtap="cancle" variant="outline">取消</t-button>
            <t-button style="padding: 0 25px; margin:0;" size="small" bindtap="submitClass">确认</t-button>
          </view>
        </view>
      </t-popup>
    </view>
  </view>
  <view class="tabbar-bottom">
    <view class="{{tabVal == 'base'?'tab-pubItem activeTab':'tab-pubItem'}}" data-str="base" bindtap="tabActive">
      <t-icon class="tabIcon" name="home-filled" size="24px" />
      <text class="text">基本信息</text>
    </view>
    <view class="{{tabVal == 'class'?'tab-pubItem activeTab':'tab-pubItem'}}" data-str="class" bindtap="tabActive">
      <t-icon class="tabIcon" name="file-setting-filled" size="24px" />
      <text class="text">班级管理</text>
    </view>
  </view>
</view>
packageCourse/pages/course/detail/index.wxss
New file
@@ -0,0 +1,281 @@
/* packageCourse/pages/course/detail/index.wxss */
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
.contentTabBox {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom));
  font-size: 28rpx;
  position: relative;
}
.showInfo {
  width: 100%;
  height: calc(100% - 60px);
  overflow: hidden;
}
.refresh-container {
  display: block;
  width: 100vw;
}
.loading {
  display: block;
  width: 100%;
  height: 180rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.bottom-loading {
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100rpx;
  padding-bottom: env(safe-area-inset-bottom);
}
.baseInfoBox {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  padding: 10rpx 20rpx;
  box-sizing: border-box;
}
.classBox {
  height: 100%;
}
.contentList {
  width: 100%;
  height: calc(100% - 55px);
  padding: 5px 20rpx;
  box-sizing: border-box;
}
.content {
  height: 100%;
}
.listClass {
  border: 1rpx solid #efefef;
  margin-bottom: 20rpx;
  border-radius: 10rpx;
}
.listClass .titleBox {
  width: 100%;
  height: 60rpx;
  display: flex;
  padding: 5rpx 20rpx;
  justify-content: space-between;
  align-items: center;
  background-color: #f8f8f8;
  font-size: 30rpx;
  box-sizing: border-box;
}
.listClass .infoBox {
  display: flex;
  flex-direction: column;
  padding: 20rpx;
  box-sizing: border-box;
  position: relative;
  font-size: 24rpx;
}
.classTag {
  margin-bottom: 20rpx;
}
.className {
  width: 250px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-right: 20rpx;
}
.delete {
  position: absolute;
  right: 20rpx;
}
.ID-State {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 40px;
  margin-bottom: 20rpx;
}
.ID {
  font-weight: 700;
}
.statics {
  width: 100%;
  margin-bottom: 40rpx;
}
.statics .classCout {
  width: 100px;
  height: 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
  border: 1rpx solid #666;
  border-radius: 10rpx;
}
/* .intro {
  min-height: 500px;
} */
.intro .descTitle,
.bookInfo .title {
  font-size: 28rpx;
  font-weight: 700;
  padding: 20rpx 0;
  border-bottom: 1rpx solid #eee;
}
.intro .descCon {
  padding: 20rpx 0;
  line-height: 44rpx;
  word-break: break-all;
}
.bookData {
  width: 100%;
  height: 200rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx 0;
}
.bookText {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: flex-start;
}
.bookIcon {
  width: 180rpx;
  height: 230rpx;
}
.tabbar-bottom {
  width: 100%;
  height: 60px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  border-top: 1rpx solid #eee;
  position: absolute;
  bottom: 0;
  left: 0;
}
.tab-pubItem {
  color: #999;
}
.tabbar-bottom .tabIcon {
  margin-bottom: 10rpx;
}
.tabbar-bottom .text {
  font-size: 24rpx;
}
.activeTab {
  color: #ff6d00;
}
.contentList {
  width: 100%;
  height: calc(100% - 55px);
}
.header {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
  border-top: 1rpx solid #eee;
  border-bottom: 1rpx solid #eee;
  background-color: #fff;
}
.header .t-search__input-box {
  height: 70rpx !important;
  font-size: 28rpx;
}
.classInfo {
  width: 100%;
  border: 1rpx solid #eee;
  padding: 35rpx;
  box-sizing: border-box;
  border-radius: 10rpx;
  margin-bottom: 20rpx;
}
.classInfo .t-input,
.classInfo .t-textarea {
  padding: 10rpx 20rpx !important;
  border: 1px solid #eee;
  border-radius: 5px;
  margin: 10px 0;
}
.classInfo .t-input__wrap .t-input__content,
.classInfo .t-input__placeholder,
.classInfo .t-textarea__placeholder,
.classInfo .t-textarea__wrapper-inner {
  font-size: 28rpx !important;
}
.classInfo .dateBox {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.classSubmit {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-bottom: 20rpx;
  padding: 0 20px;
  box-sizing: border-box;
}
.classBox .t-overlay {
  top: 55px !important
}
.block-title {
  padding: 30rpx 20rpx;
  font-size: 30rpx;
  color: #ff6d00;
}
packageCourse/pages/course/index.js
New file
@@ -0,0 +1,378 @@
// packageCourse/pages/course/index.js
import {
  getPublicImage
} from '../../../assets/js/middleGround/tool'
const app = getApp()
Page({
  /**
   * 页面的初始数据
   */
  data: {
    visible: false,
    visibleCart: false,
    searchKey: '',
    courseList: [],
    courseName: '',
    courseDesc: '',
    cartList: [],
    radioVal: 0,
    selectName: '',
    selectedBook: null,
    pageCourse: {
      page: 1,
      limit: 10,
      total: 0,
      loading: false
    },
    pageBook: {
      page: 1,
      limit: 10,
      total: 999,
      loading: false
    },
    // 加载参数
    bottomLoading: false,
    isMoreData: false,
    // 返回顶部
    isBackTop: false,
    setScrollValue: 0,
    skeletonLoding: true
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    this.getData()
  },
  // 课程详情
  todetail(e) {
    const {
      courseId,
      item
    } = e.currentTarget.dataset
    const refCode = item.linkProduct.refCode ? item.linkProduct.refCode : ''
    if (item.applyState == 'WaitAudit' || item.applyState == 'Reject') {
      wx.showToast({
        title: item.applyState == 'WaitAudit' ? '审核中' : '未通过',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    wx.navigateTo({
      url: '/packageCourse/pages/course/detail/index?courseId=' + courseId + "&bookRefCode=" + refCode,
    })
  },
  // 搜索课程
  changeHandle(e) {
    const {
      value
    } = e.detail;
    this.setData({
      searchKey: value,
      'pageCourse.page': 1
    });
    this.getData()
  },
  // 搜索教材
  changeHandleBook(e) {
    const {
      value
    } = e.detail;
    this.setData({
      selectName: value,
      'pageBook.page': 1
    });
    this.getTextBook()
  },
  // 获取课程
  getData(isReachBottom) {
    app.MG.edu
      .getAppCourseList({
        start: (this.data.pageCourse.page - 1) * this.data.pageCourse.limit,
        size: this.data.pageCourse.limit,
        sort: {
          type: 'Desc',
          field: 'CreateDate'
        },
        filterList: [],
        searchList: this.data.searchKey ? [{
          keywords: this.data.searchKey,
          field: 'Name',
          compareType: 'Contains'
        }] : []
      })
      .then((res) => {
        let list = res.datas.map((item) => {
          return {
            ...item,
            name: item.name,
            id: item.id,
            icon: item.icon && item.icon != 'default' ? getPublicImage(item.icon, 80) : '',
            introduction: item.description,
            reason: item.applyReturnMsg ? JSON.parse(item.applyReturnMsg).reason : '-'
          }
        })
        //触底加载新数据并保留老数据
        if (isReachBottom) {
          list = [...this.data.courseList, ...list] //将新数据加入老数据中
        }
        this.setData({
          courseList: list,
          'pageCourse.total': res.totalSize,
          skeletonLoding: false,
          bottomLoading: false
        })
      })
  },
  // 获取已购买的教材列表
  getTextBook() {
    const searchData = [{
        keywords: 'jsek_digitalTextbooks',
        field: 'ProductType'
      },
      {
        keywords: 'jsek_mediaBook',
        field: 'ProductType'
      },
      {
        keywords: this.data.selectName,
        field: 'ProductName'
      }
    ]
    const data = {
      Size: this.data.pageBook.limit,
      Start: (this.data.pageBook.page - 1) * this.data.pageBook.limit,
      sort: {
        type: 'Desc',
        field: 'CreateDate'
      },
      searchList: searchData
    }
    app.MG.store
      .getPurchasedProductList(data)
      .then((res) => {
        const list = res.datas.map((item) => {
          return {
            ...item,
            icon: item.product.icon ? getPublicImage(item.product.icon, 80) : ''
          }
        })
        this.setData({
          cartList: list,
        })
      })
      .catch((err) => {
        this.setData({
          cartList: [],
          'pageBook.loading': false
        })
        console.log(err)
      })
  },
  // 新建课程
  newCourse() {
    this.setData({
      visible: true,
      'pageBook.loading': true
    })
    this.getTextBook()
  },
  // 选择课程
  selectedBook() {
    this.setData({
      visibleCart: true
    })
  },
  // 关闭
  cancle() {
    this.setData({
      visible: false
    })
  },
  radioCancle() {
    this.setData({
      visibleCart: false
    })
  },
  // 单选选择课本
  onChange(e) {
    this.setData({
      radioVal: e.detail.value
    });
  },
  // 单选课本提交
  radioSubmit() {
    const data = this.data.cartList[this.data.radioVal]
    if (!data.icon) {
      data.product.icon = 'default'
    }
    this.setData({
      selectedBook: data,
      visibleCart: false
    });
  },
  onCourseNameInput(e) {
    this.setData({
      courseName: e.detail.value
    })
  },
  onCourseDescInput(e) {
    this.setData({
      courseDesc: e.detail.value
    })
  },
  // 新建课程
  submitCourse() {
    if (!this.data.courseName) {
      wx.showToast({
        title: '请填写课程名称',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    if (!this.data.selectedBook) {
      wx.showToast({
        title: '请选择关联教材',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    app.MG.edu
      .applyNewCourse({
        name: this.data.courseName,
        description: this.data.courseDesc,
        content: '',
        icon: this.data.selectedBook.product.icon,
        type: 'course',
        config: '',
        applyData: JSON.stringify({
          textBookId: this.data.selectedBook.product.id,
          textBookName: this.data.selectedBook.product.name
        }),
        linkProductId: this.data.selectedBook.product.id,
        maxClassCount: 999,
        payPrice: 0
      })
      .then((res) => {
        if (res) {
          this.setData({
            visible: false
          })
          this.getData()
        }
      })
  },
  // 返回顶部
  onToTop() {
    this.setData({
      setScrollValue: 0
    })
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    if (this._freshing) return
    this.setData({
      'pageCourse.page': 1,
      bottomLoading: false,
      isMoreData: false
    })
    setTimeout(() => {
      this._freshing = true;
      this.setData({
        triggered: false,
      })
      this.getData();
      this._freshing = false
    }, 500);
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
    if (this.data.pageCourse.total > this.data.courseList.length) {
      this.setData({
        'pageCourse.page': this.data.pageCourse.page + 1,
        bottomLoading: true,
        isMoreData: false
      })
    } else {
      this.setData({
        bottomLoading: false,
        isMoreData: true
      })
      return false;
    }
    this.getData(true);
  },
  // 监听滚动距离
  onPageScroll(e) {
    if (e && e.scrollTop >= 1000) {
      this.setData({
        isBackTop: true
      })
    } else {
      this.setData({
        isBackTop: false
      })
    }
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {
  }
})
packageCourse/pages/course/index.json
New file
@@ -0,0 +1,18 @@
{
  "navigationBarTitleText": "我的课程",
  "usingComponents": {
    "t-search": "tdesign-miniprogram/search/search",
    "t-button": "tdesign-miniprogram/button/button",
    "t-popup": "tdesign-miniprogram/popup/popup",
    "t-input": "tdesign-miniprogram/input/input",
    "t-textarea": "tdesign-miniprogram/textarea/textarea",
    "empty": "/components/empty/index",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "t-radio": "tdesign-miniprogram/radio/radio",
    "t-radio-group": "tdesign-miniprogram/radio-group/radio-group",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-back-top": "tdesign-miniprogram/back-top/back-top",
    "t-tag": "tdesign-miniprogram/tag/tag",
    "t-loading": "tdesign-miniprogram/loading/loading"
  }
}
packageCourse/pages/course/index.skeleton.wxml
New file
@@ -0,0 +1,106 @@
<!--
此文件为开发者工具生成,生成时间: 2024/11/29下午6:56:02
使用方法:
在 D:\小程序\jsek-app\jsek-applet\packageCourse\pages\course\index.wxml 引入模板
```
<import src="index.skeleton.wxml"/>
<template is="skeleton" wx:if="{{loading}}" />
```
在 D:\小程序\jsek-app\jsek-applet\packageCourse\pages\course\index.wxss 中引入样式
```
@import "./index.skeleton.wxss";
```
更多详细信息可以参考文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
-->
<template name="skeleton">
  <view class="sk-container">
    <view class="contentBox">
      <view class="header">
        <view class=" t-search search--t-search " style="flex:1;">
          <view class="t-search__input-box search--t-search__input-box t-not-focused search--t-not-focused t-search__input-box-- search--t-search__input-box-- t-search__input-box--square search--t-search__input-box--square ">
            <view class="t-icon icon--t-icon search--t-icon  " style="true">
              <label class="t-icon-search icon--t-icon-search t-icon-base icon--t-icon-base sk-pseudo sk-pseudo-circle"></label>
            </view>
            <view class="t-input__keyword search--t-input__keyword sk-image" cursor="0" maxlength="-1" name="input" placeholder="搜索课程名称" placeholder-class="input-placeholder t-search__placeholder t-search__placeholder--normal" placeholder-style="true" selection-end="-1" selection-start="-1" type="text" value="true"></view>
          </view>
        </view>
        <button app-parameter="true" data-custom="null" form-type="true" hover-class="t-button--hover" hover-start-time="20" hover-stay-time="70" lang="true" open-type="true" send-message-img="截图" send-message-path="当前分享路径" send-message-title="当前标题" session-from="true" style="margin-left: 7px;" class="t-button button--t-button t-button--base button--t-button--base t-button--primary button--t-button--primary t-button--rectangle button--t-button--rectangle t-button--size-small button--t-button--size-small sk-button sk-pseudo sk-pseudo-circle">
          <view class="t-button__content button--t-button__content sk-transparent sk-text-28-1250-12 sk-text" style="background-position-x: 50%;">新建</view>
        </button>
      </view>
      <view class="contentList">
        <scroll-view scroll-y="true" class="content" lower-threshold="80" scroll-top="0" refresher-default-style="none" refresher-enabled="true" refresher-threshold="80">
          <view class="listCourse">
            <image class="courseIcon sk-image" mode="aspectFit"></image>
            <view class="courseInfo">
              <view class="title">
                <text class="sk-transparent sk-text-14-2857-454 sk-text">2121</text>
              </view>
              <view class="courseId">
                <text class="sk-transparent sk-text-14-2857-247 sk-text">ID: 31</text>
                <view class="t-tag tag--t-tag t-tag--warning tag--t-tag--warning t-tag--light tag--t-tag--light t-tag--medium tag--t-tag--medium t-tag--square tag--t-tag--square margin-16 " style="true">
                  <view class="t-tag__icon tag--t-tag__icon"></view>
                  <view class="t-tag__text tag--t-tag__text sk-transparent sk-text-25-0000-591 sk-text">审核中</view>
                </view>
              </view>
              <view class="desc sk-transparent sk-text-20-0000-310 sk-text">描述:本课程专为职场新手和希望提升职场英语水平的学员设计,旨在帮助您掌握在现代商业环境中必备的英语技能。
              </view>
            </view>
          </view>
          <view class="listCourse">
            <image class="courseIcon sk-image" mode="aspectFit"></image>
            <view class="courseInfo">
              <view class="title">
                <text class="sk-transparent sk-text-14-2857-789 sk-text">333</text>
              </view>
              <view class="courseId">
                <text class="sk-transparent sk-text-14-2857-930 sk-text">ID: 30</text>
                <view class="t-tag tag--t-tag t-tag--warning tag--t-tag--warning t-tag--light tag--t-tag--light t-tag--medium tag--t-tag--medium t-tag--square tag--t-tag--square margin-16 " style="true">
                  <view class="t-tag__icon tag--t-tag__icon"></view>
                  <view class="t-tag__text tag--t-tag__text sk-transparent sk-text-25-0000-24 sk-text">审核中</view>
                </view>
              </view>
              <view class="desc sk-transparent sk-text-20-0000-641 sk-text">描述:本课程专为职场新手和希望提升职场英语水平的学员设计,旨在帮助您掌握在现代商业环境中必备的英语技能。
              </view>
            </view>
          </view>
          <view class="listCourse">
            <image class="courseIcon sk-image" mode="aspectFit"></image>
            <view class="courseInfo">
              <view class="title">
                <text class="sk-transparent sk-text-14-2857-70 sk-text">123</text>
              </view>
              <view class="courseId">
                <text class="sk-transparent sk-text-14-2857-970 sk-text">ID: 29</text>
                <view class="t-tag tag--t-tag t-tag--warning tag--t-tag--warning t-tag--light tag--t-tag--light t-tag--medium tag--t-tag--medium t-tag--square tag--t-tag--square margin-16 " style="true">
                  <view class="t-tag__icon tag--t-tag__icon"></view>
                  <view class="t-tag__text tag--t-tag__text sk-transparent sk-text-25-0000-807 sk-text">审核中</view>
                </view>
              </view>
              <view class="desc sk-transparent sk-text-20-0000-381 sk-text">描述:本课程专为职场新手和希望提升职场英语水平的学员设计,旨在帮助您掌握在现代商业环境中必备的英语技能。</view>
            </view>
          </view>
          <view class="listCourse">
            <image class="courseIcon sk-image" mode="aspectFit"></image>
            <view class="courseInfo">
              <view class="title">
                <text class="sk-transparent sk-text-14-2857-709 sk-text">测试课程</text>
              </view>
              <view class="courseId">
                <text class="sk-transparent sk-text-14-2857-927 sk-text">ID: 28</text>
                <view class="t-tag tag--t-tag t-tag--warning tag--t-tag--warning t-tag--light tag--t-tag--light t-tag--medium tag--t-tag--medium t-tag--square tag--t-tag--square margin-16 " style="true">
                  <view class="t-tag__icon tag--t-tag__icon"></view>
                  <view class="t-tag__text tag--t-tag__text sk-transparent sk-text-25-0000-600 sk-text">审核中</view>
                </view>
              </view>
              <view class="desc sk-transparent sk-text-20-0000-251 sk-text">描述:大部分图标在 1.8.0 版本中新增,如果发现引入组件库后,部分图标无法使用,请检查安装的组件库。</view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>
packageCourse/pages/course/index.skeleton.wxss
New file
@@ -0,0 +1,140 @@
/*
此文件为开发者工具生成,生成时间: 2024/11/29下午6:56:02
在 D:\小程序\jsek-app\jsek-applet\packageCourse\pages\course\index.wxss 中引入样式
```
@import "./index.skeleton.wxss";
```
更多详细信息可以参考文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
*/
.sk-transparent {
    color: transparent !important;
  }
.sk-text-28-1250-12 {
    background-image: linear-gradient(transparent 28.1250%, #EEEEEE 0%, #EEEEEE 71.8750%, transparent 0%) !important;
    background-size: 100% 64.0000rpx;
    position: relative !important;
  }
.sk-text {
    background-origin: content-box !important;
    background-clip: content-box !important;
    background-color: transparent !important;
    color: transparent !important;
    background-repeat: repeat-y !important;
  }
.sk-text-20-0000-985 {
    background-image: linear-gradient(transparent 20.0000%, #EEEEEE 0%, #EEEEEE 80.0000%, transparent 0%) !important;
    background-size: 100% 40.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-454 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 42.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-247 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 36.4000rpx;
    position: relative !important;
  }
.sk-text-25-0000-591 {
    background-image: linear-gradient(transparent 25.0000%, #EEEEEE 0%, #EEEEEE 75.0000%, transparent 0%) !important;
    background-size: 100% 48.0000rpx;
    position: relative !important;
  }
.sk-text-20-0000-310 {
    background-image: linear-gradient(transparent 20.0000%, #EEEEEE 0%, #EEEEEE 80.0000%, transparent 0%) !important;
    background-size: 100% 40.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-789 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 42.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-930 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 36.4000rpx;
    position: relative !important;
  }
.sk-text-25-0000-24 {
    background-image: linear-gradient(transparent 25.0000%, #EEEEEE 0%, #EEEEEE 75.0000%, transparent 0%) !important;
    background-size: 100% 48.0000rpx;
    position: relative !important;
  }
.sk-text-20-0000-641 {
    background-image: linear-gradient(transparent 20.0000%, #EEEEEE 0%, #EEEEEE 80.0000%, transparent 0%) !important;
    background-size: 100% 40.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-70 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 42.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-970 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 36.4000rpx;
    position: relative !important;
  }
.sk-text-25-0000-807 {
    background-image: linear-gradient(transparent 25.0000%, #EEEEEE 0%, #EEEEEE 75.0000%, transparent 0%) !important;
    background-size: 100% 48.0000rpx;
    position: relative !important;
  }
.sk-text-20-0000-381 {
    background-image: linear-gradient(transparent 20.0000%, #EEEEEE 0%, #EEEEEE 80.0000%, transparent 0%) !important;
    background-size: 100% 40.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-709 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 42.0000rpx;
    position: relative !important;
  }
.sk-text-14-2857-927 {
    background-image: linear-gradient(transparent 14.2857%, #EEEEEE 0%, #EEEEEE 85.7143%, transparent 0%) !important;
    background-size: 100% 36.4000rpx;
    position: relative !important;
  }
.sk-text-25-0000-600 {
    background-image: linear-gradient(transparent 25.0000%, #EEEEEE 0%, #EEEEEE 75.0000%, transparent 0%) !important;
    background-size: 100% 48.0000rpx;
    position: relative !important;
  }
.sk-text-20-0000-251 {
    background-image: linear-gradient(transparent 20.0000%, #EEEEEE 0%, #EEEEEE 80.0000%, transparent 0%) !important;
    background-size: 100% 40.0000rpx;
    position: relative !important;
  }
.sk-button {
    color: #EFEFEF !important;
    background: #EFEFEF !important;
    border: none !important;
    box-shadow: none !important;
  }
.sk-image {
    background: #EFEFEF !important;
  }
.sk-pseudo::before, .sk-pseudo::after {
      background: #EFEFEF !important;
      background-image: none !important;
      color: transparent !important;
      border-color: transparent !important;
    }
.sk-pseudo-rect::before, .sk-pseudo-rect::after {
      border-radius: 0 !important;
    }
.sk-pseudo-circle::before, .sk-pseudo-circle::after {
      border-radius: 50% !important;
    }
.sk-container {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background-color: transparent;
  }
packageCourse/pages/course/index.wxml
New file
@@ -0,0 +1,137 @@
<!--packageCourse/pages/course/index.wxml-->
<view class="contentBox" wx:if="{{!skeletonLoding}}">
  <view class="header">
    <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索课程名称" clearable />
    <t-button bindtap="newCourse" size="small" style="margin-left: 15rpx;">新建</t-button>
  </view>
  <view class="contentList" wx:if="{{courseList.length > 0}}">
    <scroll-view class="scroll" class="content" bind:scroll="onPageScroll" model:scroll-top="{{setScrollValue}}" scroll-y refresher-enabled="{{true}}" lower-threshold="{{80}}" refresher-threshold="{{80}}" refresher-default-style="none" refresher-triggered="{{triggered}}" bindrefresherpulling="{{refresh.onPulling}}" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">
      <view slot="refresher" class="refresh-container">
        <view class="loading">
          <t-loading theme="circular" size="40rpx" text="正在刷新..." class="wrapper" />
        </view>
      </view>
      <view class="listCourse" wx:for="{{courseList}}" wx:key="index" data-item="{{item}}" data-course-id="{{item.id}}" bindtap="todetail">
        <image wx:if="{{item.icon}}" class="courseIcon" src="{{item.icon}}" mode="aspectFit" />
        <image wx:else class="courseIcon" src="/static/images/default-book-img.png" mode="aspectFit" />
        <view class="courseInfo">
          <view class="title">
            <text>{{item.name}}</text>
            <!-- <t-icon wx:if="{{item.applyState == 'Reject'}}" name="delete" color="red" size='18px' /> -->
          </view>
          <view class="courseId">
            <text>ID: {{item.id}}</text>
            <t-tag class="margin-16" wx:if="{{item.applyState == 'WaitAudit'}}" variant="light" theme="warning">审核中</t-tag>
            <t-tag class="margin-16" wx:if="{{item.applyState == 'Reject'}}" variant="light" theme="danger">未通过</t-tag>
            <t-tag class="margin-16" wx:if="{{item.applyState == 'Normal'}}" variant="light" theme="success">使用中</t-tag>
          </view>
          <view wx:if="{{item.applyState == 'Reject'}}" class="reason">拒绝原因: {{item.reason}}</view>
          <view class="desc">描述:{{item.description}}</view>
        </view>
      </view>
      <view class="bottom-loading" wx:if="{{bottomLoading}}">
        <t-loading theme="circular" size="40rpx" text="加载中..." class="wrapper" />
      </view>
      <view class="bottom-loading" style="color: #ccc;font-size: 28rpx;" wx:if="{{isMoreData}}">
        <text>没有更多了</text>
      </view>
    </scroll-view>
  </view>
  <view class="content" wx:if="{{courseList.length == 0}}">
    <empty />
  </view>
  <t-popup visible="{{visible}}" style="top: 55px;" usingCustomNavbar placement="bottom">
    <view class="block">
      <view class="courseData">
        <view class="c-name">
          <text>课程名称</text>
          <t-input value="{{courseName}}" bindchange="onCourseNameInput" borderless clearable placeholder="请输入文字" />
        </view>
        <view class="c-desc">
          <text>课程描述</text>
          <t-textarea value="{{courseDesc}}" bindchange="onCourseDescInput" bordered maxlength="50" indicator autosize placeholder="请输入文字" />
        </view>
      </view>
      <view class="courseBook">
        <view class="selectBox">
          <text>选择教材</text>
          <t-button style="margin: 0;" bindtap="selectedBook" icon="add" content="选择教材" size="extra-small"></t-button>
          <t-popup visible="{{visibleCart}}" style="top: 0;" usingCustomNavbar show-overlay="{{true}}" placement="right">
            <view class="cartTilte">
              <text class="cartText">选择教材</text>
              <t-search value="{{selectName}}" bind:clear="changeHandleBook" bind:submit="changeHandleBook" placeholder="搜索教材名称" clearable />
            </view>
            <view class="cartBox">
              <view class="cartList" wx:if="{{cartList.length > 0}}">
                <t-radio-group t-class="theme-card" value="{{radioVal}}" allow-uncheck bind:change="onChange">
                  <view wx:for="{{cartList}}" wx:key="index" class="card {{radioVal == index ? 'card--active' : ''}}">
                    <t-icon wx:if="{{radioVal == index}}" name="check" t-class="card__icon" />
                    <t-radio value="{{index}}" label="{{item.product.name}}" icon="none" borderless>
                      <view class="radioCon" slot='content'>
                        <image wx:if="{{item.icon}}" class="radioIcon" src="{{item.icon}}" mode="widthFix" />
                        <image wx:else class="radioIcon" src="/static/images/default-book-img.png" mode="widthFix" />
                      </view>
                    </t-radio>
                  </view>
                </t-radio-group>
              </view>
              <view wx:else>
                <empty />
              </view>
            </view>
            <view class="radioSubmit">
              <t-button style="margin: 0;" size="extra-small" bindtap="radioCancle" variant="outline">关闭</t-button>
              <t-button style="margin: 0 16px;" size="extra-small" bindtap="radioSubmit">确认</t-button>
            </view>
          </t-popup>
        </view>
        <view class="selectedBook">
          <view class="selectTitle">已选教材</view>
          <view class="selectItem" wx:if="{{selectedBook}}">
            <image wx:if="{{item.icon}}" class="selectedIcon" src="{{selectedBook.icon}}" mode="heightFix" />
            <image wx:else class="selectedIcon" src="/static/images/default-book-img.png" mode="heightFix" />
            <view class="selectedItem">
              <text class="name">{{selectedBook.product.name}}</text>
            </view>
          </view>
          <view class="selectItemNot" wx:else>
            <image class="not-icon" src="/static/images/empty.png" mode="heightFix"></image>
            <view class="text">暂无数据</view>
          </view>
        </view>
      </view>
      <view class="courseSubmit">
        <t-button style="margin: 0 15px;" size="small" bindtap="cancle" variant="outline">取消</t-button>
        <t-button style="margin: 0;" size="small" bindtap="submitCourse">确认</t-button>
      </view>
    </view>
    <view class="block" wx:if="{{pageBook.loading}}">
      <view wx:for="{{3}}" wx:for-item="themeItem" wx:key="index">
        <view class="demo-section__content">
          <t-skeleton animation="gradient" theme="text"></t-skeleton>
          <t-skeleton animation="gradient" theme="paragraph"></t-skeleton>
        </view>
      </view>
    </view>
    <t-toast id="t-toast" />
  </t-popup>
</view>
<!-- 骨架屏 -->
<import src="./index.skeleton.wxml" />
<template is="skeleton" wx:if="{{skeletonLoding}}"></template>
<wxs module="refresh">
  module.exports = {
    onPulling: function (evt, instance) {
      var p = Math.min(evt.detail.dy / 80, 1)
      var view = instance.selectComponent('.refresh-container')
      view.setStyle({
        opacity: p,
        transform: "scale(" + p + ")"
      })
    }
  }
</wxs>
packageCourse/pages/course/index.wxss
New file
@@ -0,0 +1,335 @@
/* packageCourse/pages/course/index.wxss */
@import "./index.skeleton.wxss";
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
.contentBox {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom));
  background-color: #eee;
  font-size: 28rpx;
}
.refresh-container {
  display: block;
  width: 100vw;
}
.loading {
  display: block;
  width: 100%;
  height: 180rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.bottom-loading {
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100rpx;
  padding-bottom: env(safe-area-inset-bottom);
}
.header {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
  border-top: 1rpx solid #eee;
  border-bottom: 1rpx solid #eee;
  background-color: #fff;
}
.header .t-search__input-box {
  height: 70rpx !important;
  font-size: 28rpx;
}
.contentList {
  width: 100%;
  height: calc(100% - 55px);
  background-color: #fff;
}
.content {
  height: 100%;
}
.listCourse {
  padding: 20rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-sizing: border-box;
  height: 300rpx;
  border-bottom: 1rpx solid #eee;
}
.courseIcon {
  width: 200rpx;
  height: 280rpx;
  margin-right: 20rpx;
}
.courseInfo {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  height: 100%;
}
.courseInfo .title {
  font-size: 30rpx;
  margin-bottom: 20rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.courseInfo .courseId {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 26rpx;
  margin-bottom: 20rpx;
}
.courseInfo .reason {
  font-size: 24rpx;
  color: red;
}
.courseInfo .desc {
  font-size: 26rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  color: #999;
  line-height: 40rpx;
  font-size: 24rpx;
  width: 245px;
  word-break: break-all;
}
.block {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom) - 60px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 40rpx 20rpx;
  box-sizing: border-box;
  overflow: auto;
}
.courseData,
.courseBook {
  width: 100%;
  border: 1rpx solid #f8f8f8;
  padding: 35rpx;
  box-sizing: border-box;
  border-radius: 10rpx;
  margin-bottom: 20rpx;
}
.courseData .t-input,
.courseData .t-textarea {
  padding: 10rpx 20rpx !important;
  border: 1px solid #f8f8f8;
  border-radius: 5px;
  margin: 10px 0;
}
.courseData .t-input__wrap .t-input__content,
.courseData .t-input__placeholder,
.courseData .t-textarea__placeholder,
.courseData .t-textarea__wrapper-inner {
  font-size: 28rpx !important;
}
.selectBox {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10rpx 20rpx;
  border: 1px solid #f8f8f8;
  border-radius: 5px;
  margin: 10px 0;
  box-sizing: border-box;
}
.selectedBook {
  padding: 10rpx 20rpx;
  border: 1px solid #f8f8f8;
  border-radius: 5px;
  margin: 10px 0;
  box-sizing: border-box;
}
.selectedBook .selectTitle {
  border-bottom: 1px solid #f8f8f8;
  padding: 10rpx 0;
  box-sizing: border-box;
}
.selectedBook .selectItem {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 20rpx 10rpx;
  box-sizing: border-box;
}
.selectItemNot {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
  color: #666;
}
.selectItemNot .not-icon {
  height: 150rpx;
  margin-bottom: 10rpx;
}
.selectItemNot .text {
  font-size: 24rpx;
}
.selectedItem .name {
  font-size: 26rpx;
  margin-left: 20rpx;
}
.selectedIcon {
  height: 200rpx;
}
.cartBox {
  width: 85vw;
  height: calc(100vh - env(safe-area-inset-bottom) - 80px);
  border: 1px solid #f8f8f8;
  overflow: auto;
  padding: 0 20rpx;
  box-sizing: border-box;
}
.cartBox .card--active {
  border-color: #ff6d00 !important;
}
.cartBox .card--active::after {
  content: '';
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  border-width: 28px 28px 28px 0;
  border-style: solid;
  border-color: var(--td-brand-color, #ff6d00) transparent transparent transparent;
}
.radioSubmit {
  height: 40px;
  border-bottom: 1rpx solid #f8f8f8;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.cartTilte {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #f8f8f8;
  border-bottom: 0;
  height: 40px;
  padding: 20rpx;
  box-sizing: border-box;
}
.cartText {
  margin-right: 20px;
}
.cartTilte .t-search__input-box {
  width: 330rpx !important;
  height: 50rpx !important;
  padding: 15rpx 10rpx;
}
.cartTilte .t-search__input-box .t-input__keyword {
  font-size: 28rpx !important;
}
.cartTilte .t-search__input-box .t-icon {
  font-size: 36rpx !important;
}
.card {
  position: relative;
  margin: 32rpx;
  border-radius: 12rpx;
  overflow: hidden;
  box-sizing: border-box;
  border: 3rpx solid var(--td-bg-color-container, #eee);
}
.card--active {
  border-color: var(--td-brand-color, #0052d9);
}
.card--active::after {
  content: '';
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  border-width: 28px 28px 28px 0;
  border-style: solid;
  border-color: var(--td-brand-color, #0052d9) transparent transparent transparent;
}
.card__icon {
  color: var(--td-bg-color-container, #fff);
  position: absolute;
  left: 1.5px;
  top: 1.5px;
  z-index: 1;
}
.courseBook .t-radio {
  font-size: 26rpx !important;
}
.radioIcon {
  width: 180rpx;
  height: 210rpx;
}
.courseSubmit {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
packageCourse/pages/studentClass/index.js
New file
@@ -0,0 +1,296 @@
// packageCourse/pages/studentClass/index.js
import {
  getPublicImage
} from '../../../assets/js/middleGround/tool'
import moment from 'moment'
const app = getApp()
Page({
  /**
   * 页面的初始数据
   */
  data: {
    searchKey: '',
    visible: false,
    refCode: '',
    classDetail: null,
    classList: [],
    page: 1,
    pageSize: 10,
    totalSize: 0,
    // 加载参数
    bottomLoading: false,
    isMoreData: false,
    // 返回顶部
    isBackTop: false,
    setScrollValue: 0,
    skeletonLoding: true
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    this.getCurrentClassList()
  },
  onCourseNameInput(e) {
    this.setData({
      refCode: e.detail.value
    })
  },
  // 通过code查询班级
  getClassDetail() {
    if (this.data.refCode == '') {
      wx.showToast({
        title: '请输入邀请码',
        duration: 1000,
        icon: "none",
      })
      return false
    }
    const data = {
      classIdOrRefCode: this.data.refCode
    }
    app.MG.edu
      .getCourseClass(data)
      .then(res => {
        if (res) {
          res.classTime = moment(res.beginDate).format('YYYY-MM-DD') + '--' + moment(res.endDate).format('YYYY-MM-DD')
          this.setData({
            classDetail: res
          })
        }
      })
      .catch(err => {
        console.log(err)
      })
  },
  // 打开新建班级
  newClass() {
    this.setData({
      visible: true,
    })
  },
  // 关闭申请
  cancle() {
    this.setData({
      visible: false
    })
  },
  // 申请提交班级
  submitClass() {
    if (!this.data.refCode) {
      wx.showToast({
        title: '请输入邀请码',
        duration: 1000,
        icon: "none",
      })
      return false
    }
    const data = {
      refCode: this.data.refCode
    }
    app.MG.identity.joinGroupByRefCode(data).then(res => {
      if (res == '组不存在') {
        wx.showToast({
          title: '班级不存在',
          duration: 1000,
          icon: "none",
        })
      }
      if (res == '组成员数量已最大,不能加入') {
        wx.showToast({
          title: '已满员',
          duration: 1000,
          icon: "none",
        })
      }
      if (res == '已经申请过加入此组') {
        wx.showToast({
          title: '已申请',
          duration: 1000,
          icon: "none",
        })
      }
      this.getCurrentClassList()
    })
  },
  // 获取当前班级列表
  getCurrentClassList(isReachBottom) {
    const data = {
      start: (this.data.page - 1) * this.data.pageSize,
      size: this.data.pageSize,
      sort: {
        type: 'Desc',
        field: 'CreateDate',
        subSorts: []
      },
      filterList: [],
      searchList: this.data.searchKey ? [{
        keywords: this.data.searchKey,
        field: 'Name',
        compareType: 'Contains'
      }] : []
    }
    app.MG.identity.joinedGroupByList(data).then(res => {
      const {
        datas,
        totalSize
      } = res
      if (datas) {
        let list = res.datas.map(item => {
          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)
          }
        }) //触底加载新数据并保留老数据
        if (isReachBottom) {
          list = [...this.data.classList, ...list] //将新数据加入老数据中
        }
        this.setData({
          classList: list,
          totalSize: totalSize,
          visible: false,
          skeletonLoding: false,
          bottomLoading: false
        })
      }
    })
  },
  // 获取教材详情
  async getBookDetail(shopId) {
    let query = {
      path: 'jsek_digitalTextbooks',
      queryType: '*',
      productId: String(shopId),
      storeInfo: 'jsek_digitalTextbooks',
      coverSize: {
        height: 300,
        width: 210
      },
      fields: {
        author: [],
        isbn: []
      }
    }
    const res = await app.MG.store.getProductDetail(query)
    return res.datas ?? null
  },
  // 班级详情
  async todetail(e) {
    const {
      classId,
      item
    } = e.currentTarget.dataset
    const bookData = await this.getBookDetail(item.linkProductDto?.product?.id)
    if (item.userState == 'WaitValid' || item.userState == 'Reject') {
      wx.showToast({
        title: item.userState == 'WaitValid' ? '审核中' : '未通过',
        duration: 1000,
        icon: 'none',
      })
      return false
    }
    wx.navigateTo({
      url: '/packageCourse/pages/teachClass/index?classId=' + classId + '&bookId=' + bookData.id + '&author=' + bookData.author + '&isbn=' + bookData.isbn + '&rootCmsItemId=' + bookData.rootCmsItemId,
    })
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
    if (this._freshing) return
    this.setData({
      page: 1,
      bottomLoading: false,
      isMoreData: false
    })
    setTimeout(() => {
      this._freshing = true;
      this.setData({
        triggered: false,
      })
      this.getCurrentClassList();
      this._freshing = false
    }, 500);
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
    if (this.data.totalSize > this.data.classList.length) {
      this.setData({
        page: this.data.page + 1,
        bottomLoading: true,
        isMoreData: false
      })
    } else {
      this.setData({
        bottomLoading: false,
        isMoreData: true
      })
      return false;
    }
    this.getCurrentClassList(true);
  },
  // 监听滚动距离
  onPageScroll(e) {
    if (e && e.scrollTop >= 1000) {
      this.setData({
        isBackTop: true
      })
    } else {
      this.setData({
        isBackTop: false
      })
    }
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {
  }
})
packageCourse/pages/studentClass/index.json
New file
@@ -0,0 +1,16 @@
{
  "navigationBarTitleText": "我的班级",
  "usingComponents": {
    "t-button": "tdesign-miniprogram/button/button",
    "t-popup": "tdesign-miniprogram/popup/popup",
    "t-input": "tdesign-miniprogram/input/input",
    "t-search": "tdesign-miniprogram/search/search",
    "t-tag": "tdesign-miniprogram/tag/tag",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-calendar": "tdesign-miniprogram/calendar/calendar",
    "t-back-top": "tdesign-miniprogram/back-top/back-top",
    "empty": "/components/empty/index",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "t-loading": "tdesign-miniprogram/loading/loading"
  }
}
packageCourse/pages/studentClass/index.wxml
New file
@@ -0,0 +1,95 @@
<view class="classBox">
  <view class="header">
    <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索班级名称" clearable />
    <t-button bindtap="newClass" size="small" style="margin-left: 15rpx;">加入班级</t-button>
  </view>
  <view class="contentList" wx:if="{{classList.length > 0 && !classLoading}}">
    <scroll-view class="scroll" class="content" bind:scroll="onPageScroll" model:scroll-top="{{setScrollValue}}" scroll-y refresher-enabled="{{true}}" lower-threshold="{{80}}" refresher-threshold="{{80}}" refresher-default-style="none" refresher-triggered="{{triggered}}" bindrefresherpulling="{{refresh.onPulling}}" bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">
      <view slot="refresher" class="refresh-container">
        <view class="loading">
          <t-loading theme="circular" size="40rpx" text="正在刷新..." class="wrapper" />
        </view>
      </view>
      <view class="listItem">
        <view class="listClass" wx:for="{{classList}}" wx:key="index" data-class-id="{{item.id}}" data-item="{{item}}" bindtap="todetail">
          <view class="titleBox">
            <text class="className">{{item.name}}</text>
            <text class="classId" style="color: #999; font-size: 12px;"> ID: {{item.id}}</text>
          </view>
          <view class="infoBox">
            <text class="classTag" wx:if="{{item.userState == 'WaitValid'}}" style="color: #ef9f29">状态 : 审核中</text>
            <text class="classTag" wx:if="{{item.userState == 'Normal'}}" style="color: #1dbd11">状态 : 进行中</text>
            <text class="classTag" wx:if="{{item.userState == 'Reject'}}" style="color: red">状态 : 未通过</text>
            <t-icon bindtap="delClass" data-del-id="{{item.id}}" class="delete" wx:if="{{item.userState == 'Reject'}}" name="delete" color="red" size='18px' />
            <text class="classTag" wx:if="{{item.userState == 'Reject'}}" style="color: red">拒绝原因:{{ item.reason != '' ? item.reason : '-' }}</text>
            <text class="classTag">班级人数 : {{ item.memberCount }} / {{ item.maxUserCount }}</text>
            <text class="classTag">有效期 :{{ item.classTime }}</text>
          </view>
        </view>
      </view>
      <view class="bottom-loading" wx:if="{{bottomLoading}}">
        <t-loading theme="circular" size="40rpx" text="加载中..." class="wrapper" />
      </view>
      <view class="bottom-loading" style="color: #ccc;font-size: 28rpx;" wx:if="{{isMoreData}}">
        <text>没有更多了</text>
      </view>
    </scroll-view>
  </view>
  <view class="content" wx:if="{{classList.length == 0 && !classLoading}}">
    <empty />
  </view>
  <view class="block" wx:if="{{classLoading}}">
    <view wx:for="{{4}}" wx:for-item="themeItem" wx:key="index">
      <view class="demo-section__content">
        <t-skeleton animation="gradient" theme="text"></t-skeleton>
        <t-skeleton animation="gradient" theme="paragraph"></t-skeleton>
      </view>
    </view>
  </view>
  <t-popup visible="{{visible}}" usingCustomNavbar placement="bottom">
    <view class="block-title">申请入班</view>
    <view class="block">
      <view class="classInfo">
        <view class="c-name">
          <text>邀请码: </text>
          <view class="c-name-option">
            <t-input value="{{refCode}}" bindchange="onCourseNameInput" borderless clearable placeholder="请输入邀请码" />
            <t-button bindtap="getClassDetail" size="small" style="margin-left: 15rpx;">查看班级</t-button>
          </view>
        </view>
        <view class="c-desc">
          <view class="c-desc-title">班级信息:</view>
          <view class="classData" wx:if="{{classDetail}}">
            <view>班级名称:{{ classDetail.name }}</view>
            <view>班级人数:{{ classDetail.memberCount }} / {{ classDetail.maxUserCount }}</view>
            <view>开课时间:{{classDetail.classTime}}</view>
          </view>
          <view class="classData" wx:else>
            <view>班级名称:-</view>
            <view>班级人数:-</view>
            <view>开课时间:-</view>
          </view>
        </view>
      </view>
      <view class="classSubmit">
        <t-button style="padding: 0 25px; margin:0 15px;" size="small" bindtap="cancle" variant="outline">取消</t-button>
        <t-button style="padding: 0 25px; margin:0;" size="small" bindtap="submitClass">加入</t-button>
      </view>
    </view>
  </t-popup>
</view>
<wxs module="refresh">
  module.exports = {
    onPulling: function (evt, instance) {
      var p = Math.min(evt.detail.dy / 80, 1)
      var view = instance.selectComponent('.refresh-container')
      view.setStyle({
        opacity: p,
        transform: "scale(" + p + ")"
      })
    }
  }
</wxs>
packageCourse/pages/studentClass/index.wxss
New file
@@ -0,0 +1,223 @@
/* packageCourse/pages/course/detail/index.wxss */
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
.contentTabBox {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom));
  font-size: 28rpx;
  position: relative;
}
.refresh-container {
  display: block;
  width: 100vw;
}
.loading {
  display: block;
  width: 100%;
  height: 180rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.bottom-loading {
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100rpx;
  padding-bottom: env(safe-area-inset-bottom);
}
.classBox {
  height: 100%;
}
.contentList {
  width: 100%;
  height: calc(100% - 55px);
}
.content {
  height: 100%;
}
.listItem {
  width: 100%;
  height: 100%;
  padding: 5px 20rpx;
  box-sizing: border-box;
}
.listClass {
  border: 1rpx solid #efefef;
  margin-bottom: 20rpx;
  border-radius: 10rpx;
}
.listClass .titleBox {
  width: 100%;
  height: 60rpx;
  display: flex;
  padding: 5rpx 20rpx;
  justify-content: space-between;
  align-items: center;
  background-color: #f8f8f8;
  font-size: 30rpx;
  box-sizing: border-box;
}
.listClass .infoBox {
  display: flex;
  flex-direction: column;
  padding: 20rpx;
  box-sizing: border-box;
  position: relative;
  font-size: 24rpx;
}
.classTag {
  margin-bottom: 20rpx;
}
.className {
  width: 250px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-right: 20rpx;
}
.delete {
  position: absolute;
  right: 20rpx;
}
.tabbar-bottom {
  width: 100%;
  height: 60px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  border-top: 1rpx solid #eee;
  position: absolute;
  bottom: 0;
  left: 0;
}
.tab-pubItem {
  color: #999;
}
.tabbar-bottom .tabIcon {
  margin-bottom: 10rpx;
}
.tabbar-bottom .text {
  font-size: 24rpx;
}
.activeTab {
  color: #ff6d00;
}
.header {
  width: 100%;
  height: 55px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx;
  box-sizing: border-box;
  border-top: 1rpx solid #eee;
  border-bottom: 1rpx solid #eee;
  background-color: #fff;
}
.header .t-search__input-box {
  height: 70rpx !important;
  font-size: 28rpx;
}
.classInfo {
  width: 100%;
  border: 1rpx solid #eee;
  padding: 35rpx;
  box-sizing: border-box;
  border-radius: 10rpx;
  margin-bottom: 20rpx;
  font-size: 26rpx;
}
.classInfo .t-input,
.classInfo .t-textarea {
  padding: 10rpx 20rpx !important;
  border: 1px solid #eee;
  border-radius: 5px;
  margin: 10px 0;
}
.classInfo .t-input__wrap .t-input__content,
.classInfo .t-input__placeholder,
.classInfo .t-textarea__placeholder,
.classInfo .t-textarea__wrapper-inner {
  font-size: 26rpx !important;
}
.classInfo .dateBox {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.classSubmit {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-bottom: 20rpx;
  padding: 0 20px;
  box-sizing: border-box;
}
.c-name-option {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.c-desc-title {
  margin-bottom: 10rpx;
}
.classData {
  width: 100%;
  height: 180rpx;
  border: 1px solid #eee;
  padding: 20rpx;
  box-sizing: border-box;
  border-radius: 10rpx;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: flex-start;
  color: #666666;
}
.classBox .t-overlay {
  top: 55px !important
}
.block-title {
  padding: 30rpx 20rpx;
  font-size: 30rpx;
  color: #ff6d00;
}
packageCourse/pages/teachClass/detail/index.js
New file
@@ -0,0 +1,309 @@
// packageCourse/components/teaching/detail/index.js.js
const app = getApp()
import moment from 'moment'
Page({
  /**
   * 页面的初始数据
   */
  data: {
    teachInteractionId: null,
    dialogLLoading: false,
    dialogList: [],
    visible: false,
    bookId: null,
    questionId: null,
    dataList: [],
    searchKey: ''
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    if (options) {
      this.setData({
        teachInteractionId: options.teachInteractionId,
        questionName: options.questionName,
        bookId: options.bookId,
        questionId: options.questionId
      })
    }
    wx.setNavigationBarTitle({
      title: '互动学生列表',
    })
    this.getMessage()
  },
  openLook(e) {
    const {
      item
    } = e.currentTarget.dataset
    this.setData({
      visible: true
    })
    this.getQuestions(item)
  },
  close() {
    this.setData({
      visible: false
    })
  },
  // 搜索
  changeHandle(e) {
    const {
      value
    } = e.detail;
    let cmsList = this.data.dataList
    this.setData({
      dataList: []
    })
    if (value) {
      const data = cmsList.filter(item => item.userName.indexOf(value) > -1);
      this.setData({
        dataList: data
      })
    }
    if (value == '') {
      this.setData({
        dataList: [],
        loading: true
      })
      this.getMessage()
    }
  },
  // 获取当前话题
  getMessage() {
    const data = {
      start: 0,
      size: 999,
      appRefCode: app.config.appRefCode,
      topicIdOrRefCode: String(this.data.teachInteractionId),
      sort: {
        type: 'Desc',
        field: 'CreateDate'
      },
      searchList: [{
        keywords: this.data.questionName,
        field: 'Name',
        compareType: 'Contains'
      }]
    }
    app.MG.ugc.getTopicMessageList(data).then((res) => {
      const list = res.datas.map((item, i) => {
        item.question = []
        item.bookId = null
        item.path = ''
        item.index = i + 1
        try {
          const obj = JSON.parse(item.content)
          if (obj.bookId) {
            item.question = obj.content
            item.bookId = obj.bookId
            item.path = obj.path
            item.userName = obj.userName ?? '-'
          }
        } catch (error) {
          console.log(item)
        }
        return {
          ...item,
          questionTime: moment(item.updateDate).format('YYYY-MM-DD HH:mm:ss')
        }
      })
      this.setData({
        dataList: list,
        loading: false
      })
    })
  },
  // 获取题目列表
  getQuestions(item) {
    app.MG.store
      .getProductDetail({
        path: '*',
        queryType: '*',
        productId: this.data.bookId,
        cmsPath: item.path,
        itemFields: {
          Embedded_QuestionBank_AnalysisCon: [],
          Embedded_QuestionBank_Answer: [],
          Embedded_QuestionBank_Difficulty: [],
          Embedded_QuestionBank_KnowledgePoint: [],
          Embedded_QuestionBank_Option: [],
          Embedded_QuestionBank_OptionStyle: [],
          Embedded_QuestionBank_QuestionType: [],
          Embedded_QuestionBank_Score: [],
          Embedded_QuestionBank_Stem: [],
          Embedded_QuestionBank_StemStyle: []
        }
      })
      .then((res) => {
        try {
          let list = []
          list = res.datas.cmsDatas[0]?.datas.map((item) => {
            try {
              if (item.Embedded_QuestionBank_Stem) {
                item.questionStem = JSON.parse(item.Embedded_QuestionBank_Stem)
              }
              if (
                item.Embedded_QuestionBank_Option &&
                item.Embedded_QuestionBank_Option.indexOf('[') > -1
              ) {
                item.questionOption = JSON.parse(item.Embedded_QuestionBank_Option)
              }
              if (
                item.Embedded_QuestionBank_Answer &&
                item.Embedded_QuestionBank_Answer.indexOf('[') > -1
              ) {
                item.Embedded_QuestionBank_Answer = JSON.parse(item.Embedded_QuestionBank_Answer)
              }
              return {
                ...item,
                questionType: item.Embedded_QuestionBank_QuestionType,
                questionAnalysisCon: item.Embedded_QuestionBank_AnalysisCon,
                questionAnswer: item.Embedded_QuestionBank_Answer,
                customAnswer: null
              }
            } catch (error) {
              console.log(item)
            }
          })
          const data = this.chageData([item], list)
          this.setData({
            dialogList: data
          })
        } catch (error) {
          this.setData({
            dialogList: []
          })
        }
      })
  },
  // 处理数据结构
  chageData(arr, zrr) {
    let newData = []
    // 题库题目类型
    const questionTypeList = [{
        name: '单选题',
        value: 'singleChoice',
        data: []
      },
      {
        name: '多选题',
        value: 'multipleChoice',
        data: []
      },
      {
        name: '判断题',
        value: 'judge',
        data: []
      },
      {
        name: '简答题',
        value: 'shortAnswer',
        data: []
      },
      {
        name: '论述题',
        value: 'discuss',
        data: []
      },
      {
        name: '填空题',
        value: 'completion',
        data: []
      },
      {
        name: '连线题',
        value: 'matching',
        data: []
      },
      {
        name: '分类题',
        value: 'classification',
        data: []
      }
    ]
    for (let i = 0; i < arr.length; i++) {
      const item = arr[i]
      item.questionTypeList = questionTypeList
      for (let j = 0; j < zrr.length; j++) {
        const ele = zrr[j]
        const qusObj = item.question.find((citem) => citem.cmsItemId == ele.id)
        if (qusObj?.cmsItemId) {
          ele.userAnswer = qusObj.answer
          const index = this.findIndexByValue(questionTypeList, ele.questionType)
          if (index > -1) {
            item.questionTypeList[index].data.push(ele)
          }
        }
      }
      item.questionTypeList = item.questionTypeList.filter((item) => item.data.length > 0)
      newData.push(item)
    }
    return newData.filter((item) => item.questionTypeList.length > 0)
  },
  findIndexByValue(res, type) {
    for (let i = 0; i < res.length; i++) {
      if (res[i].value == type) {
        return i
      }
    }
    return -1 // 如果未找到,则返回 -1
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {
  }
})
packageCourse/pages/teachClass/detail/index.json
New file
@@ -0,0 +1,11 @@
{
  "component": true,
  "usingComponents": {
    "t-search": "tdesign-miniprogram/search/search",
    "t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
    "empty": "/components/empty/index",
    "t-icon": "tdesign-miniprogram/icon/icon",
    "t-popup": "tdesign-miniprogram/popup/popup",
    "questionDom": "/packageCourse/components/questionDom/index"
  }
}
packageCourse/pages/teachClass/detail/index.wxml
New file
@@ -0,0 +1,42 @@
<!--packageCourse/components/teaching/index.wxml-->
<view class="teachBox">
  <view class="header">
    <t-search style="flex:1" value="{{searchKey}}" bind:clear="changeHandle" bind:submit="changeHandle" placeholder="搜索用户名称" clearable />
  </view>
  <view class="count">共计 <text style="color: #ff6d00;">{{dataList.length}}</text> 人答题</view>
  <view class="teachContent" wx:if="{{dataList.length > 0 && !loading}}">
    <view class="list" wx:for="{{dataList}}" wx:key="index" bindtap="openLook" data-item="{{item}}">
      <view class="questionName">用户名:<text style="color: #ff6d00;">{{item.userName}}</text></view>
      <view class="questionInfo">
        <view class="questionCount">
          <text style="color: #67c23a"> 已答完</text>
        </view>
        <view class="questionDate">
          {{item.questionTime}}
        </view>
      </view>
    </view>
  </view>
  <view class="she" wx:if="{{loading}}">
    <view wx:for="{{10}}" wx:for-item="themeItem" wx:key="index">
      <view class="demo-section__content">
        <t-skeleton theme="text"></t-skeleton>
      </view>
    </view>
  </view>
  <view class="she" wx:if="{{dataList.length == 0 && !loading}}">
    <empty />
  </view>
  <t-popup visible="{{visible}}" usingCustomNavbar placement="bottom">
    <view class="block-question-title">
      <text class="block-question-title-text">答题浏览</text>
      <t-icon size="22px" bindtap="close" color="#999" name="close"></t-icon>
    </view>
    <view class="block-question">
      <view class="block-question-list" wx:for="{{dialogList}}" wx:key="index">
        <view class="block-question-user">答题人:{{ item.userNme }}</view>
        <questionDom wx:if="{{item.questionTypeList.length > 0}}" questionList="{{item.questionTypeList}}" noCheckbox="{{false}}" is-preview="{{true}}" />
      </view>
    </view>
  </t-popup>
</view>
packageCourse/pages/teachClass/detail/index.wxss
New file
@@ -0,0 +1,92 @@
/* packageCourse/components/teaching/index.wxss */
.teachBox {
  width: 100%;
  height: 100%;
  font-size: 28rpx;
}
.teachBox .t-overlay {
  top: 85rpx !important
}
.header {
  width: 100%;
  padding: 0 20rpx;
  box-sizing: border-box;
}
.teachContent {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
}
.teachBox .count {
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
  border-bottom: 1rpx solid #eee;
}
.teachContent .questionInfo {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.teachContent .questionName {
  margin-bottom: 10rpx;
}
.teachContent .list {
  width: 100%;
  margin-bottom: 20rpx;
  border-bottom: 1px solid #eee;
  padding: 20rpx 0;
  box-sizing: border-box;
}
.block-question-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 40px;
  padding: 0 20rpx;
  font-size: 30rpx;
  border-bottom: 1rpx solid #eee;
}
.block-question {
  height: calc(100vh - 80px - 40px - 40px);
  padding: 20rpx;
  box-sizing: border-box;
  overflow-y: auto;
}
.block-question-title-text {
  color: #ff6d00;
}
.block-question-user {
  margin-bottom: 20rpx;
  color: #ff6d00;
}
.demo-section__content {
  margin-top: 32rpx;
  margin-bottom: 48rpx;
}
.block-question .t-textarea {
  padding: 0 !important;
}
.block-question .t-textarea__wrapper-inner {
  font-size: 26rpx !important;
}
.block-question .t-input__placeholder,
.block-question .t-textarea__placeholder {
  font-size: 26rpx !important;
}
packageCourse/pages/teachClass/index.js
New file
@@ -0,0 +1,118 @@
// packageCourse/pages/class/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tabVal: 'base',
    classId: null,
    bookId: null,
    rootCmsItemId: null,
    courseData: null,
    userData: null,
    defaultCmsPath: ''
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    if (options) {
      this.setData({
        classId: options.classId,
        bookId: options.bookId,
        rootCmsItemId: options.rootCmsItemId,
        courseData: {
          courseId: options.courseId,
          author: options.author,
          isbn: options.isbn
        },
        defaultCmsPath: options.bookRefCode
      })
    }
    const data = wx.getStorageSync('website-front-userInfo')
    if (data) {
      this.setData({
        userData: JSON.parse(data)
      })
    }
    wx.setNavigationBarTitle({
      title: '基本信息',
    })
  },
  // tab切换
  tabActive(e) {
    const {
      str
    } = e.currentTarget.dataset
    this.setData({
      tabVal: str,
    })
    if (str == 'base') {
      wx.setNavigationBarTitle({
        title: '基本信息',
      })
    }
    if (str == 'studentManage') {
      wx.setNavigationBarTitle({
        title: '学生管理',
      })
    }
    if (str == 'teach') {
      wx.setNavigationBarTitle({
        title: '教学互动',
      })
    }
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {
  },
  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {
  }
})
packageCourse/pages/teachClass/index.json
New file
@@ -0,0 +1,8 @@
{
  "usingComponents": {
    "t-icon": "tdesign-miniprogram/icon/icon",
    "base": "/packageCourse/components/baseClass/index",
    "studentManage": "/packageCourse/components/studentManage/index",
    "Teach": "/packageCourse/components/teaching"
  }
}
packageCourse/pages/teachClass/index.wxml
New file
@@ -0,0 +1,31 @@
<!--packageCourse/pages/class/index.wxml-->
<view class="contentTabBox">
  <view class="showInfo">
    <!-- 基本信息 -->
    <view class="baseInfoBox" wx:if="{{tabVal == 'base'}}">
      <base classId="{{classId}}" courseData="{{courseData}}" bookRefCode="{{defaultCmsPath}}" />
    </view>
    <!-- 学生管理 -->
    <view class="classBox" wx:if="{{tabVal == 'studentManage' && userData.role == 'Teacher'}}">
      <studentManage classId="{{classId}}" bookRefCode="{{defaultCmsPath}}" />
    </view>
    <!-- 教学互动 -->
    <view class="teachBox" wx:if="{{tabVal == 'teach'}}">
      <Teach classId="{{classId}}" bookId="{{bookId}}" rootCmsItemId="{{rootCmsItemId}}" />
    </view>
  </view>
  <view class="tabbar-bottom">
    <view class="{{tabVal == 'base'?'tab-pubItem activeTab':'tab-pubItem'}}" data-str="base" bindtap="tabActive">
      <t-icon class="tabIcon" name="home-filled" size="24px" />
      <text class="text">基本信息</text>
    </view>
    <view wx:if="{{ userData.role == 'Teacher'}}" class="{{tabVal == 'studentManage'?'tab-pubItem activeTab':'tab-pubItem'}}" data-str="studentManage" bindtap="tabActive">
      <t-icon class="tabIcon" name="file-setting-filled" size="24px" />
      <text class="text">学生管理</text>
    </view>
    <view class="{{tabVal == 'teach'?'tab-pubItem activeTab':'tab-pubItem'}}" data-str="teach" bindtap="tabActive">
      <t-icon class="tabIcon" name="system-setting" size="24px" />
      <text class="text">教学互动</text>
    </view>
  </view>
</view>
packageCourse/pages/teachClass/index.wxss
New file
@@ -0,0 +1,56 @@
/* packageCourse/pages/class/index.wxss */
.contentTabBox {
  width: 100%;
  height: calc(100vh - env(safe-area-inset-bottom));
  font-size: 28rpx;
  position: relative;
}
.showInfo {
  width: 100%;
  height: calc(100% - 60px);
  overflow: hidden;
}
.baseInfoBox,
.classBox,
.teachBox {
  width: 100%;
  height: 100%;
}
.contentTabBox .t-overlay {
  top: 55px !important
}
.contentTabBox .t-radio__icon--checked {
  color: #ff6d00 !important;
}
.tabbar-bottom {
  width: 100%;
  height: 60px;
  display: flex;
  justify-content: space-around;
  align-items: center;
  border-top: 1rpx solid #eee;
  position: absolute;
  bottom: 0;
  left: 0;
}
.tab-pubItem {
  color: #999;
}
.activeTab {
  color: #ff6d00;
}
.tabbar-bottom .tabIcon {
  margin-bottom: 10rpx;
}
.tabbar-bottom .text {
  font-size: 24rpx;
}
pages/personalCenter/index.js
@@ -53,6 +53,18 @@
    // url: '',
    url: '/packagePersonal/pages/publishBooks/index',
    type: 'publishBooks',
  },
  {
    title: '我的课程',
    icon: '/static/images/personal/course.png',
    url: '/packageCourse/pages/course/index',
    type: 'course',
  },
  {
    title: '我的班级',
    icon: '/static/images/personal/class.png',
    url: '/packageCourse/pages/studentClass/index',
    type: 'class',
  }
];
const moreMenu = [{
pages/personalCenter/index.wxss
@@ -105,7 +105,9 @@
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  justify-content: flex-start;
  padding: 15rpx 25rpx;
  box-sizing: border-box;
}
.domainList1 {
@@ -125,15 +127,15 @@
  width: 160rpx;
  min-width: 160rpx;
  flex-shrink: 0;
  padding: 40rpx 0;
  padding: 20rpx 0;
}
.listItem:nth-child(5),
/* .listItem:nth-child(5),
.listItem:nth-child(6),
.listItem:nth-child(7),
.listItem:nth-child(8) {
  padding-top: 10rpx !important;
}
} */
.listItem .icon {
  width: 48rpx;
static/images/personal/class.png
static/images/personal/course.png