detail.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. <template>
  2. <view>
  3. <view style="position: fixed;width: 100%;z-index: 999;background: #FFFFFF;top: 0;" id="top">
  4. <view class="video_box" v-if="!startStatus">
  5. <image :src="$method.splitImgHost(detail.coverUrl)" style="width: 100%;height: 460rpx;"></image>
  6. <image v-if="false" class="video_play" src="/static/play.png" @click="startVideo"></image>
  7. </view>
  8. <view v-else class="video_box" style="width: 100%;height: 460rpx;">
  9. <polyv-player
  10. id="playerVideo"
  11. playerId="playerVideo"
  12. height="460rpx"
  13. :vid="vid"
  14. :showSettingBtn="true"
  15. :enablePlayGesture="true"
  16. @statechange="onStateChange"
  17. :autoplay="autoplay"
  18. :isAllowSeek="isAllowSeek"
  19. :playbackRate="playbackRate"
  20. ></polyv-player>
  21. </view>
  22. <view>
  23. <u-row>
  24. <u-col span="10">
  25. <view class="video_t1">{{ detail.courseName }}</view>
  26. </u-col>
  27. <u-col span="2">
  28. <view class="video_t1_t" @click="openPhoto">
  29. <image src="/static/icon/jy_icon.png" style="width: 40rpx;height: 40rpx;"></image>
  30. 讲义
  31. </view>
  32. </u-col>
  33. </u-row>
  34. </view>
  35. <u-line color="#D6D6DB" />
  36. <view style="display: flex;justify-content: center;">
  37. <view style="width: 280px;">
  38. <u-tabs :list="list" font-size="24" bar-width="80" :current="current" @change="change" active-color="#007AFF"></u-tabs>
  39. </view>
  40. </view>
  41. <u-line color="#D6D6DB" />
  42. </view>
  43. <view class="box">
  44. <!--目录 -->
  45. <view v-show="current == 0">
  46. <view class="menuBox" v-for="(item, index) in reMenuList">
  47. <!--模块 -->
  48. <view v-if="item.type == 1"><courseModule :gradeId="gradeId" :isRebuild="true" :isBuy="true" :menuItem="item" :levelId="item.menuId"></courseModule></view>
  49. <!--章 -->
  50. <view v-if="item.type == 2"><courseChapter :gradeId="gradeId" :isRebuild="true" :isBuy="true" :menuItem="item" :levelId="'0-'+item.menuId"></courseChapter></view>
  51. <!--节 -->
  52. <view v-if="item.type == 3"><courseSection :gradeId="gradeId" :isRebuild="true" :isBuy="true" :menuItem="item" :levelId="'0-0-'+item.menuId"></courseSection></view>
  53. </view>
  54. </view>
  55. <!--目录 -->
  56. <view v-show="current == 1">
  57. <view class="menuBox" v-for="(item, index) in menuList">
  58. <!--模块 -->
  59. <view v-if="item.type == 1"><courseModule :isBuy="true" :menuItem="item" :levelId="item.menuId"></courseModule></view>
  60. <!--章 -->
  61. <view v-if="item.type == 2"><courseChapter :isBuy="true" :menuItem="item" :levelId="'0-'+item.menuId"></courseChapter></view>
  62. <!--节 -->
  63. <view v-if="item.type == 3"><courseSection :isBuy="true" :menuItem="item" :levelId="'0-0-'+item.menuId"></courseSection></view>
  64. </view>
  65. </view>
  66. <!--笔记 -->
  67. <view v-show="current == 2">
  68. <view class="inputBottom">
  69. <view style="width: 10%;"><image src="/static/icon/note3.png" style="width: 39rpx;height: 39rpx;margin:0 29rpx;"></image></view>
  70. <view style="width: 73%;height: 88rpx;margin-bottom: 15rpx;">
  71. <u-input height="78" fixed="true" :placeholder="placeholder" type="textarea" :custom-style="inputStyle" v-model="noteValue" />
  72. </view>
  73. <view style="color: #007AFF;font-size: 30rpx;font-weight: bold;width: 15%;text-align: center;" @click="postNote">提交</view>
  74. </view>
  75. <view v-for="(item, index) in noteList" >
  76. <view class="dateBox">{{$method.timestampToTime(item.dateNote)}}</view>
  77. <view class="noteBox" v-for="(item1, index1) in item.userNotes">
  78. <view>
  79. <view class="tBox" v-if="playSectionId==item1.sectionId">
  80. <image src="/static/icon/note1.png" style="width: 39rpx;height: 39rpx;margin:0 29rpx;"></image>
  81. <view class="title leftPadding">{{item1.dateNote}}</view>
  82. </view>
  83. <view class="tBox2">
  84. <view class="title" style="width: 39rpx;height: 39rpx;margin:0 29rpx;">{{$method.secondToDate(item1.noteSecond)}}</view>
  85. <view class="t2Content leftPadding">fdgdfgd</view>
  86. </view>
  87. <view class="tBox" v-if="playSectionId!=item1.sectionId">
  88. <image src="/static/icon/note2.png" style="width: 39rpx;height: 39rpx;margin:0 29rpx;"></image>
  89. <view class="title leftPadding">{{item1.sectionName}}</view>
  90. </view>
  91. <view style="margin-left: 97rpx;" class="t2Content leftPadding">{{item1.noteText}}</view>
  92. </view>
  93. </view>
  94. </view>
  95. </view>
  96. <!--答疑 -->
  97. <view v-show="current == 3">
  98. <view class="inputBottom">
  99. <view style="width: 73%;height: 88rpx;margin-bottom: 15rpx;margin-left: 10% ;">
  100. <u-input height="78" fixed="true" :placeholder="placeholder" type="textarea" :custom-style="inputStyle" v-model="ctxValue" />
  101. </view>
  102. <view style="color: #007AFF;font-size: 30rpx;font-weight: bold;width: 15%;text-align: center;" @click="postContent">提交</view>
  103. </view>
  104. <view v-for="(item, index) in answerList" style="background-color: #FFFFFF;margin-bottom: 20rpx;">
  105. <view class="chat_box" @click.stop="clearCtx">
  106. <view style="display: flex;" >
  107. <view><image :src="$method.splitImgHost(item.avatar)" style="width: 64rpx;height: 64rpx;"></image></view>
  108. <view style="margin-left: 15rpx;">
  109. <view class="chat1">{{item.realname}}</view>
  110. <view class="chat2">{{$method.timestampToTime(item.createTime)}}</view>
  111. <view class="chat3">
  112. <text v-if="item.assignUserId>0">回复</text>
  113. <text v-if="item.assignUserId>0" style="color: #007AFF;">@{{item.assignRealname}}</text>
  114. {{item.answerText}}</view>
  115. </view>
  116. </view>
  117. <view class="btnReply" @click.stop="replyContent(item)" v-if="item.userId!=userInfo.userId">回复</view>
  118. <view v-else class="btnDel" @click.stop="delContent(item)">删除</view>
  119. </view>
  120. <u-line color="#D6D6DB" />
  121. </view>
  122. <view v-if="answerList.length==0" style="text-align: center;">
  123. 暂无记录
  124. </view>
  125. </view>
  126. </view>
  127. <!-- 播放前拍照start -->
  128. <u-popup v-model="photoPopup" mode="bottom" border-radius="32" :mask-close-able="false">
  129. <view class="photoBox">
  130. <view class="photoTop">
  131. <view class="sqzz" v-if="true">
  132. <u-icon name="close" color="#333333" size="30" @click="closePhoto"></u-icon>
  133. </view>
  134. <view class="centersq">
  135. 请正视手机屏幕
  136. </view>
  137. <view class="sqzz">
  138. </view>
  139. </view>
  140. <view class="photoCenter">
  141. <camera device-position="front" flash="off" @error="error" style="width: 100%; height: 100%" v-if="photoPopup"></camera>
  142. <view class="custom">
  143. <image src="@/pages2/static/zhezhao.png" mode=""></image>
  144. </view>
  145. </view>
  146. <view class="btnResult" @click="takePhoto">
  147. 拍照
  148. </view>
  149. </view>
  150. </u-popup>
  151. <!-- 播放前拍照end -->
  152. </view>
  153. </template>
  154. <script>
  155. import eventHub from '@/common/eventHub.js';
  156. import courseModule from '@/components/course/courseModule.vue';
  157. import courseChapter from '@/components/course/courseChapter.vue';
  158. import courseSection from '@/components/course/courseSection.vue';
  159. import { mapGetters } from 'vuex';
  160. export default {
  161. components: {
  162. courseModule,
  163. courseChapter,
  164. courseSection
  165. },
  166. data() {
  167. return {
  168. startStatus: false,
  169. detail: {},
  170. courseId: 0,
  171. placeholder: '您可以在这里输入笔记内容\n还可以点击左侧图标为笔记加上时间标记',
  172. inputStyle: {
  173. background: 'rgba(244, 244, 244, 0.98)',
  174. borderRadius: '24rpx',
  175. padding: '8rpx',
  176. marginBottom: '10rpx'
  177. },
  178. playbackRate: [0.5, 0.8, 1.0],
  179. list: [
  180. {
  181. name: '重修目录'
  182. },
  183. {
  184. name: '目录'
  185. },
  186. {
  187. name: '笔记'
  188. },
  189. {
  190. name: '答疑'
  191. }
  192. ],
  193. menuList: [
  194. ],
  195. current: 1,
  196. vid:'',
  197. goodsId:0,
  198. goodsData:{},
  199. photoPopup:false,
  200. goodsPlayConfig:null,
  201. autoplay:false,
  202. isAllowSeek:'no',
  203. playbackRate: [1.0],
  204. timer:null,
  205. goodsPhotographConfig:null,
  206. intervalTimeList:[],// 间隔拍照时长
  207. intervalTimeIndex:0 ,//当前处于哪个时间段拍照
  208. playTime:0 ,//页面播放时长,不含暂停
  209. currentTime:0,
  210. avatarUrl:'',
  211. ossAvatarUrl:'',
  212. studyDuration:0, // 当前视频时长
  213. gradeId:0,
  214. chapterId:0,
  215. moduleId:0,
  216. reMenuList: [],
  217. answerList: [],
  218. assignUserId:0,
  219. placeholder:'您可以在这里输入答疑内容',
  220. ctxValue:'',
  221. noteList:[],
  222. noteValue:''
  223. };
  224. },
  225. onUnload() {},
  226. computed: { ...mapGetters(['userInfo','playSectionId']) },
  227. onLoad(option) {
  228. this.courseId = option.id;
  229. this.goodsId = uni.getStorageSync('courseGoodsId');
  230. this.courseDetail();
  231. this.getGoodsDetail()
  232. this.getAnswerList()
  233. },
  234. onShow() {},
  235. onUnload() {
  236. if(this.playSectionId>0){
  237. //退出提交记录
  238. this.ossAvatarUrl = ""
  239. this.postStudyRecord()
  240. //清除正在播放的节ID
  241. this.$store.commit('setPlaySectionId', {playSectionId :0});
  242. }
  243. },
  244. mounted() {
  245. eventHub.$on('getSection', item => {
  246. if(this.timer){
  247. clearInterval(this.timer);
  248. }
  249. if(this.vid){
  250. //切换视频
  251. var polyvPlayerContext = this.selectComponent('#playerVideo');
  252. polyvPlayerContext.changeVid(item.recordingUrl)
  253. }else{
  254. this.vid = item.recordingUrl
  255. }
  256. this.startStatus = true
  257. this.startTime = 0
  258. });
  259. eventHub.$on('levelId', item => {
  260. let arr = item.split('-')
  261. //点击节获取的各层级ID
  262. this.moduleId = arr[0]
  263. this.chapterId = arr[1]
  264. console.log(item,99)
  265. });
  266. },
  267. methods: {
  268. postNote() {
  269. let self = this;
  270. if(!(this.playSectionId>0)){
  271. this.$u.toast('目前无播放视频');
  272. retun
  273. }
  274. if(!this.noteValue){
  275. this.$u.toast('请输入内容');
  276. retun
  277. }
  278. var polyvPlayerContext = this.selectComponent('#playerVideo');
  279. let noteDate = this.$method.getZeroTime()
  280. let noteSecond= polyvPlayerContext.getCurrentTime()
  281. if(!noteSecond){
  282. this.$u.toast('视频暂未开始');
  283. retun
  284. }
  285. let data = {
  286. gradeId:this.gradeId,
  287. goodsId:this.goodsId,
  288. sectionId:this.playSectionId,
  289. courseId: this.courseId,
  290. noteText:this.noteValue,
  291. noteDate:noteDate,
  292. noteSecond:noteSecond}
  293. console.log(data,66)
  294. this.$api.postNote(data).then(res => {
  295. if (res.data.code == 200) {
  296. this.$u.toast('发布成功');
  297. self.getNoteList()
  298. this.noteValue = ''
  299. }
  300. });
  301. },
  302. getNoteList() {
  303. let self = this;
  304. this.$api.noteList({ courseId: this.courseId,gradeId:this.gradeId,goodsId:this.goodsId }).then(res => {
  305. if (res.data.code == 200) {
  306. self.noteList = res.data.rows;
  307. }
  308. });
  309. },
  310. delAnswer(answerId) {
  311. let self = this;
  312. let data = { answerId: answerId,status:-1 }
  313. this.$api.delAnswer(data).then(res => {
  314. if (res.data.code == 200) {
  315. self.getAnswerList()
  316. }
  317. });
  318. },
  319. clearCtx(){
  320. console.log(4234)
  321. this.placeholder ='您可以在这里输入答疑内容'
  322. this.ctxValue = ''
  323. this.assignUserId=0
  324. },
  325. replyContent(item){
  326. this.assignUserId = item.userId
  327. this.placeholder = '@'+item.realname
  328. },
  329. delContent(item){
  330. this.delAnswer(item.answerId)
  331. },
  332. postAnswer() {
  333. let self = this;
  334. let data = { courseId: this.courseId,answerText:this.ctxValue }
  335. if(this.assignUserId>0){
  336. data.assignUserId = this.assignUserId
  337. }
  338. this.$api.postAnswer(data).then(res => {
  339. if (res.data.code == 200) {
  340. this.$u.toast('发布成功');
  341. self.getAnswerList()
  342. this.placeholder ='您可以在这里输入答疑内容'
  343. this.ctxValue = ''
  344. this.assignUserId=0
  345. }
  346. });
  347. },
  348. postContent(){
  349. if(!this.ctxValue){
  350. this.$u.toast('请输入内容');
  351. }
  352. this.postAnswer()
  353. },
  354. postStudyRecord(status=0) {
  355. let self = this;
  356. let data = {
  357. photo:self.ossAvatarUrl,
  358. sectionId:parseInt(self.playSectionId),
  359. goodsId:parseInt(self.goodsId),
  360. courseId:parseInt(self.courseId),
  361. studyDuration:parseInt(self.studyDuration),
  362. gradeId:parseInt(self.gradeId),
  363. chapterId:parseInt(self.chapterId),
  364. moduleId:parseInt(self.moduleId)
  365. }
  366. if(status>0){
  367. data.status = status
  368. }
  369. console.log("提交接口",data)
  370. this.$api.studyRecord(data).then(res => {
  371. console.log(res)
  372. });
  373. },
  374. uploadFile(options, int) {
  375. var self = this;
  376. return new Promise((resolve, reject) => {
  377. var data = {
  378. imageStatus: int
  379. };
  380. self.$api.aliyunpolicy(data).then(res => {
  381. if(res.data.code!=200){
  382. self.$method.showToast('签名错误'+JSON.stringify(res.data))
  383. return
  384. }
  385. var ossToken = res.data.data.resultContent;
  386. if(ossToken.host==null||ossToken.host==undefined){
  387. self.$method.showToast('上传路径报错'+JSON.stringify(res.data))
  388. return
  389. }
  390. uni.uploadFile({
  391. url: ossToken.host,
  392. name: 'file',
  393. filePath: options,
  394. fileType: 'image',
  395. header: {
  396. AuthorizationToken: 'WX ' + uni.getStorageSync('token')
  397. },
  398. formData: {
  399. key: ossToken.dir,
  400. OSSAccessKeyId: ossToken.accessid,
  401. policy: ossToken.policy,
  402. Signature: ossToken.signature,
  403. callback: ossToken.callback,
  404. success_action_status: 200
  405. },
  406. success: result => {
  407. if (result.statusCode === 200) {
  408. self.ossAvatarUrl = ossToken.dir;
  409. resolve();
  410. } else {
  411. uni.showToast({
  412. title: '上传失败',
  413. icon: 'none'
  414. });
  415. return;
  416. }
  417. },
  418. fail: error => {
  419. uni.showToast({
  420. title: '上传接口报错'+error,
  421. icon: 'none'
  422. });
  423. return;
  424. }
  425. });
  426. });
  427. });
  428. },
  429. imageInfos(){
  430. var self = this
  431. return new Promise((resolve, reject) => {
  432. uni.getImageInfo({
  433. src: self.avatarUrl,
  434. success: async res => {
  435. let canvasWidth = res.width; //图片原始长宽
  436. let canvasHeight = res.height;
  437. if (canvasWidth > 1000 || canvasHeight > 1000) {
  438. uni.compressImage({
  439. src: self.avatarUrl,
  440. quality: 75,
  441. width: '50%',
  442. height: '50%',
  443. success: async rest => {
  444. const waitUpload = await self.uploadFile(rest.tempFilePath, 0);
  445. resolve()
  446. }
  447. });
  448. } else {
  449. console.log('无需压缩');
  450. const waitUpload = await self.uploadFile(self.avatarUrl, 0);
  451. resolve()
  452. }
  453. }
  454. });
  455. });
  456. },
  457. timeEvent() {
  458. let self = this
  459. var polyvPlayerContext = this.selectComponent('#playerVideo');
  460. if (polyvPlayerContext != null) {
  461. let PlayCurrentTime = polyvPlayerContext.getCurrentTime();
  462. this.studyDuration = PlayCurrentTime
  463. if(this.currentTime<PlayCurrentTime){
  464. this.playTime+=(PlayCurrentTime-this.currentTime)
  465. this.currentTime = PlayCurrentTime
  466. }else{
  467. this.currentTime = PlayCurrentTime
  468. }
  469. //判断是否需要拍照
  470. if(this.intervalTimeList.length>this.intervalTimeIndex){
  471. let photoTime = Number(this.intervalTimeList[this.intervalTimeIndex]) * 60 //获取拍照秒数
  472. if(photoTime<this.playTime){
  473. //启动拍照
  474. //暂停
  475. polyvPlayerContext.exitFullScreen()
  476. polyvPlayerContext.pause()
  477. this.openPhoto();
  478. this.intervalTimeIndex++
  479. }
  480. }
  481. }
  482. },
  483. onStateChange(newstate, oldstate) {
  484. if (newstate.detail.newstate == 'playing') {
  485. //开始播放
  486. if(this.timer){
  487. clearInterval(this.timer);
  488. }
  489. this.timer = setInterval(this.timeEvent, 1500);//定时器
  490. }
  491. if (newstate.detail.newstate == 'pause') {
  492. //暂停提交记录
  493. /* this.ossAvatarUrl = ""
  494. this.postStudyRecord() */
  495. }
  496. if (newstate.detail.newstate == 'ended') {
  497. this.ossAvatarUrl = ""
  498. this.postStudyRecord(1)
  499. }
  500. },
  501. //拍照
  502. openPhoto(){
  503. this.photoPopup = true
  504. },
  505. async submit(){
  506. const waitYS = await this.imageInfos();
  507. this.postStudyRecord()//提交记录
  508. //恢复播放
  509. var polyvPlayerContext = this.selectComponent('#playerVideo');
  510. if (polyvPlayerContext != null) {
  511. polyvPlayerContext.play();
  512. }
  513. console.log(this.ossAvatarUrl,"拍照完成456")
  514. },
  515. //确认拍照
  516. takePhoto() {
  517. var self = this
  518. const ctx = uni.createCameraContext();
  519. ctx.takePhoto({
  520. quality: 'high',
  521. success: res => {
  522. console.log(res.tempImagePath)
  523. self.avatarUrl = res.tempImagePath
  524. self.submit()
  525. self.photoPopup = false
  526. },
  527. fail: err => {
  528. console.log(err)
  529. }
  530. });
  531. },
  532. //拍照报错
  533. error(e) {
  534. console.log(e.detail);
  535. },
  536. //关闭相机
  537. closePhoto(){
  538. this.photoPopup = false
  539. },
  540. getGoodsDetail(){
  541. let self = this
  542. this.$api.goodsDetail(this.goodsId).then(res => {
  543. self.goodsData = res.data.data;
  544. self.gradeId = self.goodsData.gradeId
  545. self.getMenuList();
  546. self.getReMenuList() //获取重修目录
  547. self.getNoteList()
  548. if(self.goodsData.goodsPlayConfig){
  549. self.goodsPlayConfig = JSON.parse(self.goodsData.goodsPlayConfig);
  550. if(self.goodsPlayConfig.autoPlay>0){
  551. self.autoplay = true
  552. }
  553. if(self.goodsPlayConfig.drag>0){
  554. self.isAllowSeek = "yes"
  555. }
  556. if(self.goodsPlayConfig.speed>0){
  557. self.playbackRate = [0.5,0.8,1.0,1.25,1.5,2.0]
  558. }
  559. }
  560. if(self.goodsData.goodsPhotographConfig){
  561. self.goodsPhotographConfig = JSON.parse(self.goodsData.goodsPhotographConfig);
  562. if(self.goodsPhotographConfig.intervalTime){
  563. self.intervalTimeList = self.goodsPhotographConfig.intervalTime.split(',')
  564. }
  565. }
  566. })
  567. },
  568. startVideo() {
  569. this.startStatus = true;
  570. },
  571. getAnswerList() {
  572. let self = this;
  573. this.$api.answerList({ courseId: this.courseId }).then(res => {
  574. if (res.data.code == 200) {
  575. self.answerList = res.data.rows;
  576. }
  577. });
  578. },
  579. getReMenuList() {
  580. let self = this;
  581. this.$api.reMenuList({ courseId: this.courseId,rebuild:1,gradeId:this.gradeId }).then(res => {
  582. if (res.data.code == 200) {
  583. for (let i = 0; i < res.data.rows.length; i++) {
  584. let item = res.data.rows[i];
  585. item.down = true;
  586. item.id = item.menuId;
  587. item.name = item.menuName;
  588. }
  589. self.reMenuList = res.data.rows;
  590. }
  591. });
  592. },
  593. getMenuList() {
  594. let self = this;
  595. this.$api.menuList({ courseId: this.courseId }).then(res => {
  596. if (res.data.code == 200) {
  597. for (let i = 0; i < res.data.rows.length; i++) {
  598. let item = res.data.rows[i];
  599. item.down = true;
  600. item.id = item.menuId;
  601. item.name = item.menuName;
  602. }
  603. self.menuList = res.data.rows;
  604. }
  605. });
  606. },
  607. courseDetail() {
  608. let self = this;
  609. this.$api.courseDetail(this.courseId).then(res => {
  610. if (res.data.code == 200) {
  611. self.detail = res.data.data;
  612. self.gradeId = self.detail.gradeId
  613. }
  614. });
  615. },
  616. open(item) {
  617. item.showChildren = !item.showChildren;
  618. },
  619. change(index) {
  620. this.current = index;
  621. }
  622. }
  623. };
  624. </script>
  625. <style lang="scss" scope>
  626. .btnReply{
  627. width: 80rpx;
  628. height: 40rpx;
  629. background: #E3F0FF;
  630. border-radius: 16rpx;
  631. text-align: center;
  632. color: #007AFF;
  633. }
  634. .btnDel{
  635. width: 80rpx;
  636. height: 40rpx;
  637. background: #FFEDF0;
  638. border-radius: 16rpx;
  639. text-align: center;
  640. color: #FF2D55;
  641. }
  642. .btnReply{
  643. width: 80rpx;
  644. height: 40rpx;
  645. background: #E3F0FF;
  646. border-radius: 16rpx;
  647. font-size: 24rpx;
  648. }
  649. .photoBox{
  650. .photoTop{
  651. height: 74upx;
  652. display: flex;
  653. align-items: center;
  654. justify-content: space-between;
  655. padding: 0upx 38upx;
  656. .sqzz{
  657. width: 28upx;
  658. height: 28upx;
  659. display: flex;
  660. align-items: center;
  661. justify-content: center;
  662. }
  663. .centersq{
  664. color: #333;
  665. font-size: 30upx;
  666. font-weight: 500;
  667. }
  668. }
  669. .photoCenter{
  670. width: 750upx;
  671. height: 979upx;
  672. position: relative;
  673. .custom{
  674. width: 750upx;
  675. height: 979upx;
  676. position: absolute;
  677. top: 0;
  678. left: 0;
  679. image{
  680. width: 100%;
  681. height: 100%;
  682. }
  683. }
  684. }
  685. .btnResult{
  686. height: 100upx;
  687. width: 100%;
  688. background-color: #07c160;
  689. text-align: center;
  690. line-height: 100upx;
  691. color: #fff;
  692. font-size: 32upx;
  693. font-weight: bold;
  694. }
  695. }
  696. .chat_box {
  697. display: flex;
  698. padding: 20rpx;
  699. justify-content: space-between;
  700. }
  701. .chat3 {
  702. font-size: 30rpx;
  703. font-family: PingFang SC;
  704. font-weight: 500;
  705. color: #666666;
  706. margin-top: 10rpx;
  707. }
  708. .chat2 {
  709. font-size: 20rpx;
  710. font-family: PingFang SC;
  711. font-weight: 500;
  712. color: #999999;
  713. margin-top: 10rpx;
  714. }
  715. .chat1 {
  716. font-size: 24rpx;
  717. font-family: PingFang SC;
  718. font-weight: 500;
  719. color: #333333;
  720. }
  721. .leftPadding {
  722. margin-left: 8rpx;
  723. }
  724. .t2Content {
  725. font-size: 30rpx;
  726. font-family: PingFang SC;
  727. font-weight: bold;
  728. color: #333333;
  729. line-height: 48rpx;
  730. }
  731. .tBox2 {
  732. display: flex;
  733. padding-top: 10rpx;
  734. }
  735. .tBox {
  736. display: flex;
  737. align-items: center;
  738. padding-top: 10rpx;
  739. }
  740. .title {
  741. font-size: 24rpx;
  742. color: #999999;
  743. }
  744. page {
  745. padding-top: 10px;
  746. padding-top: constant(safe-area-inset-top);
  747. padding-top: env(safe-area-inset-top);
  748. }
  749. .inputBottom {
  750. position: fixed;
  751. left: 0;
  752. bottom: 0;
  753. background: #ffffff;
  754. height: 98rpx;
  755. display: flex;
  756. align-items: center;
  757. width: 100%;
  758. }
  759. .noteBox {
  760. width: 100%;
  761. background: #ffffff;
  762. padding: 10rpx;
  763. border-radius: 16rpx;
  764. }
  765. .dateBox {
  766. width: 216rpx;
  767. height: 48rpx;
  768. background: #ffffff;
  769. border-radius: 24rpx;
  770. font-size: 24rpx;
  771. color: #666666;
  772. text-align: center;
  773. line-height: 48rpx;
  774. margin: 20rpx 0;
  775. }
  776. .t_content1 {
  777. color: #007aff;
  778. margin-left: 10rpx;
  779. }
  780. .tag1 {
  781. border: 2rpx solid #007aff;
  782. border-radius: 8rpx;
  783. font-size: 20rpx;
  784. color: #007aff;
  785. padding: 5rpx;
  786. }
  787. .b_title {
  788. color: #333333;
  789. font-size: 30rpx;
  790. font-weight: bold;
  791. }
  792. page {
  793. background: #eaeef1;
  794. }
  795. .menuBox {
  796. width: 100%;
  797. background: #ffffff;
  798. border-radius: 16rpx;
  799. padding: 20rpx;
  800. margin-bottom: 20rpx;
  801. }
  802. .btnspric {
  803. border-top: 1rpx solid #eee;
  804. display: flex;
  805. align-items: center;
  806. justify-content: space-between;
  807. height: 108rpx;
  808. padding-left: 43rpx;
  809. padding-right: 32rpx;
  810. }
  811. .btnspric > .lefprL {
  812. font-size: 36rpx;
  813. color: #0c141f;
  814. font-weight: bold;
  815. }
  816. .btnspric > .lefprR {
  817. padding: 0rpx 24rpx;
  818. height: 60rpx;
  819. line-height: 60rpx;
  820. text-align: center;
  821. color: #fff;
  822. background: #32467b;
  823. border-radius: 24rpx;
  824. box-shadow: 0rpx 0rpx 16rpx 4rpx rgba(145, 156, 178, 0.1);
  825. }
  826. .yhj,
  827. .hdyhj {
  828. padding: 24rpx 29rpx 24rpx 34rpx;
  829. }
  830. .yhj {
  831. border-bottom: 16rpx solid #f9f9f9;
  832. }
  833. .yhjtit {
  834. font-size: 30rpx;
  835. color: #0c141f;
  836. font-weight: 500;
  837. margin-bottom: 14rpx;
  838. }
  839. .yhjList {
  840. display: flex;
  841. align-items: center;
  842. justify-content: space-between;
  843. margin-bottom: 14rpx;
  844. }
  845. .yhjList > .yhjLefts {
  846. display: flex;
  847. align-items: center;
  848. }
  849. .yhjLefts > .yhl {
  850. color: #32467b;
  851. font-size: 30rpx;
  852. margin-right: 31rpx;
  853. }
  854. .yhjLefts > .yhbq {
  855. font-size: 24rpx;
  856. color: #ff9500;
  857. border-radius: 18rpx;
  858. background-color: rgba(255, 149, 0, 0.2);
  859. border: 2rpx solid #ff9500;
  860. height: 38rpx;
  861. line-height: 38rpx;
  862. padding: 0rpx 16rpx;
  863. }
  864. .ts {
  865. font-size: 24rpx;
  866. color: #999;
  867. margin: 14rpx 0rpx;
  868. padding-right: 29rpx;
  869. padding-left: 34rpx;
  870. }
  871. .yh {
  872. padding-top: 20rpx;
  873. }
  874. .yh > .yhtitle {
  875. display: flex;
  876. align-items: center;
  877. justify-content: space-between;
  878. padding-right: 29rpx;
  879. padding-left: 34rpx;
  880. }
  881. .priceBxs {
  882. display: flex;
  883. align-items: center;
  884. }
  885. .priceBxs > .pricleft {
  886. border-radius: 24rpx;
  887. border: 1rpx solid #e91313;
  888. background-color: rgba(233, 19, 19, 0.1);
  889. padding: 0rpx 18rpx;
  890. height: 49rpx;
  891. line-height: 49rpx;
  892. text-align: center;
  893. font-size: 30rpx;
  894. font-weight: 500;
  895. color: #e91313;
  896. margin-right: 13rpx;
  897. }
  898. .topBox {
  899. padding: 32rpx 32rpx 24rpx;
  900. border-bottom: 1rpx solid #eeeeee;
  901. }
  902. .topBox > .boldFonstType {
  903. font-weight: 500;
  904. font-size: 30rpx;
  905. margin: 16rpx 0rpx 23rpx;
  906. }
  907. .topBox > .firstTopL {
  908. display: flex;
  909. align-items: center;
  910. }
  911. .topBox > .firstTopL > .imageBs {
  912. width: 331rpx;
  913. height: 160rpx;
  914. border-radius: 6rpx;
  915. overflow: hidden;
  916. margin-right: 8rpx;
  917. box-shadow: 0rpx 6rpx 6rpx 0rpx rgba(47, 67, 121, 0.08);
  918. }
  919. .topBox > .firstTopL > .imageBs > image {
  920. width: 100%;
  921. height: 100%;
  922. }
  923. .topBox > .firstTopL > .textBs {
  924. font-size: 30rpx;
  925. font-weight: bold;
  926. color: #0c141f;
  927. }
  928. .content {
  929. padding: 24rpx;
  930. text-align: left;
  931. }
  932. .catalogBox {
  933. display: flex;
  934. align-items: center;
  935. flex-wrap: nowrap;
  936. overflow-x: auto;
  937. padding-left: 38rpx;
  938. max-height: 305rpx;
  939. overflow-y: auto;
  940. transition: all 0.4s;
  941. }
  942. .catalogBox > .catalogA {
  943. min-width: 200rpx;
  944. height: 48rpx;
  945. line-height: 48rpx;
  946. // text-align: center;
  947. border: 2rpx solid transparent;
  948. white-space: nowrap;
  949. text-overflow: ellipsis;
  950. overflow: hidden;
  951. word-break: break-all;
  952. border-radius: 10rpx;
  953. background: rgba(22, 119, 255, 0.05);
  954. padding-left: 19rpx;
  955. box-sizing: border-box;
  956. padding-right: 15rpx;
  957. margin-right: 16rpx;
  958. margin-bottom: 20rpx;
  959. margin-top: 15rpx;
  960. font-size: 24rpx;
  961. color: #666;
  962. }
  963. .catalogBox > .activesq {
  964. border-color: #1677ff;
  965. }
  966. .changeCatalogBox {
  967. display: block;
  968. }
  969. .catalogBox::-webkit-scrollbar {
  970. display: none; /* Chrome Safari */
  971. }
  972. .box {
  973. position: relative;
  974. top: 650rpx;
  975. padding-bottom: 88rpx;
  976. margin: 20rpx;
  977. }
  978. .price_t2 {
  979. font-size: 18rpx;
  980. font-family: PingFang SC;
  981. font-weight: 500;
  982. text-decoration: line-through;
  983. color: #999999;
  984. }
  985. .price_t1 {
  986. font-size: 33rpx;
  987. font-family: PingFang SC;
  988. font-weight: bold;
  989. color: #e91313;
  990. }
  991. .sc_t {
  992. font-size: 22rpx;
  993. color: #000000;
  994. }
  995. .sc {
  996. width: 29rpx;
  997. height: 29rpx;
  998. }
  999. .buy {
  1000. width: 138rpx;
  1001. height: 48rpx;
  1002. line-height: 48rpx;
  1003. background: #32467b;
  1004. border-radius: 10rpx;
  1005. color: #ffffff;
  1006. font-size: 28rpx;
  1007. text-align: center;
  1008. vertical-align: middle;
  1009. position: absolute;
  1010. right: 30rpx;
  1011. }
  1012. .video_body {
  1013. padding-bottom: 96rpx;
  1014. }
  1015. .footer_tab {
  1016. position: fixed;
  1017. bottom: 0;
  1018. height: 96rpx;
  1019. width: 100%;
  1020. background-color: #ffffff;
  1021. }
  1022. .tj_box {
  1023. width: 50%;
  1024. display: inline-block;
  1025. text-align: center;
  1026. margin: 10rpx 0;
  1027. }
  1028. .teacher_t {
  1029. font-size: 24rpx;
  1030. font-family: PingFang SC;
  1031. font-weight: 400;
  1032. color: #666666;
  1033. line-height: 36rpx;
  1034. margin-left: 15rpx;
  1035. }
  1036. .teacher_img {
  1037. width: 87rpx;
  1038. height: 129rpx;
  1039. }
  1040. .t2 {
  1041. font-size: 24rpx;
  1042. font-family: PingFang SC;
  1043. color: #666666;
  1044. line-height: 36rpx;
  1045. margin: 15rpx;
  1046. }
  1047. .r_t2 {
  1048. width: 201rpx;
  1049. height: 49rpx;
  1050. background: rgba(22, 119, 255, 0.05);
  1051. border: 1rpx solid #32467b;
  1052. border-radius: 16rpx;
  1053. color: #666666;
  1054. font-size: 23rpx;
  1055. text-align: center;
  1056. display: flex;
  1057. align-items: center;
  1058. padding: 5rpx;
  1059. }
  1060. .scroll_box {
  1061. width: 100%;
  1062. height: 60rpx;
  1063. background: #ffffff;
  1064. box-shadow: 0rpx 0rpx 16rpx 4rpx rgba(145, 156, 178, 0.1);
  1065. white-space: nowrap;
  1066. overflow: hidden;
  1067. margin: 15rpx 0;
  1068. }
  1069. .r_sliper {
  1070. padding: 0 20rpx;
  1071. }
  1072. .top_line {
  1073. width: 6rpx;
  1074. height: 22rpx;
  1075. background: #32467b;
  1076. margin-right: 10rpx;
  1077. }
  1078. .video_t2 {
  1079. font-size: 24rpx;
  1080. font-family: PingFang SC;
  1081. font-weight: 500;
  1082. color: #666666;
  1083. }
  1084. .video_t1 {
  1085. height: 80rpx;
  1086. color: #333333;
  1087. line-height: 80rpx;
  1088. font-size: 30rpx;
  1089. font-family: PingFang SC;
  1090. font-weight: bold;
  1091. color: #333333;
  1092. }
  1093. .video_t1_t {
  1094. display: flex;
  1095. flex-direction: column;
  1096. height: 80rpx;
  1097. color: #333333;
  1098. text-align: center;
  1099. align-items: center;
  1100. border-left: solid 1px #d6d6db;
  1101. }
  1102. .video_play {
  1103. position: absolute;
  1104. width: 95rpx;
  1105. height: 95rpx;
  1106. top: 0;
  1107. left: 0;
  1108. right: 0;
  1109. bottom: 0;
  1110. margin: auto;
  1111. }
  1112. .video_box {
  1113. position: relative;
  1114. }
  1115. .rotoct {
  1116. transform: rotate(90deg);
  1117. }
  1118. </style>