detail.vue 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979
  1. <template>
  2. <view class="polyv_detail">
  3. <uni-nav-bar
  4. left-icon="back"
  5. :statusBar="true"
  6. fixed="true"
  7. :title="detail.courseName || '课程详情'"
  8. @clickLeft="clickLeft"
  9. ></uni-nav-bar>
  10. <view id="top">
  11. <view class="video_box" v-if="!startStatus">
  12. <image
  13. :src="$method.splitImgHost(goodsData.coverUrl)"
  14. mode="widthFix"
  15. style="width: 100%; height: 421rpx"
  16. ></image>
  17. </view>
  18. <view v-else>
  19. <my-player
  20. ref="player"
  21. :playVid="playVID"
  22. :autoplay="autoplay"
  23. :allowSeek="isAllowSeek"
  24. :playbackRate="playbackRate"
  25. :videoCurrentTime="recordObj.videoCurrentTime || 0"
  26. @playing="playing"
  27. @pause="pause"
  28. @ended="ended"
  29. @loadedmetadata="loadedmetadata"
  30. @timeupdate="timeupdate"
  31. @playerError="playerError"
  32. ></my-player>
  33. </view>
  34. <view class="course_name">
  35. <view class="course_titles">
  36. <view class="video_t1" :class="{ one: !goodsData.buyNote }">{{
  37. detail.courseName
  38. }}</view>
  39. <view class="notice_wrap" v-if="goodsData.buyNote">
  40. <view class="video_t1_t" @click="studyNotice"> 学员须知 </view>
  41. </view>
  42. <view
  43. class="toggle_course"
  44. v-if="goodsTeacher.length > 1"
  45. @click="changeCourses()"
  46. >
  47. <image
  48. class="img"
  49. src="/pages3/static/imgs/toggle.png"
  50. mode="widthFix"
  51. ></image>
  52. <view class="toggle_name">切换课程</view>
  53. <!-- courseTotal -->
  54. <view class="numbers">共{{ goodsTeacher.length }}门</view>
  55. </view>
  56. </view>
  57. </view>
  58. <u-line color="#D6D6DB" />
  59. <view>
  60. <view>
  61. <u-tabs
  62. :item-width="itemWidth()"
  63. :list="list"
  64. font-size="32"
  65. bar-width="24"
  66. :current="current"
  67. @change="change"
  68. active-color="#007AFF"
  69. ></u-tabs>
  70. </view>
  71. </view>
  72. <u-line color="#D6D6DB" />
  73. </view>
  74. <view class="box" :class="{ first_ml: current == 0 }">
  75. <scroll-view class="box_in" scroll-y="true">
  76. <!--目录 -->
  77. <view v-show="current == 0">
  78. <view
  79. class="top__header"
  80. v-if="livingItem"
  81. @click="goLive(livingItem)"
  82. >
  83. <image
  84. class="img"
  85. src="/pages3/static/imgs/live.png"
  86. mode="widthFix"
  87. ></image>
  88. <view class="note">正在直播中</view>
  89. <view class="title">{{ livingItem.sectionName }}</view>
  90. </view>
  91. <course-tree
  92. v-if="sectionItem.id || sectionItem.sectionId"
  93. :orderGoodsId="orderGoodsId"
  94. :sectionMaxNum="goodsData.sectionMaxNum"
  95. :courseId="courseId"
  96. :learningOrder="orderNum"
  97. :goodsId="goodsId"
  98. :gradeId="gradeId"
  99. :isRebuild="false"
  100. :isBuy="true"
  101. :goodsType="1"
  102. :menuAllList="menuAllList"
  103. :sectionItem="sectionItem"
  104. ></course-tree>
  105. </view>
  106. <!--讲义 -->
  107. <view v-show="current == 1">
  108. <handouts-box
  109. :handoutsId="goodsData.handoutsId"
  110. v-if="goodsData.handoutsId"
  111. ></handouts-box>
  112. </view>
  113. <!--笔记 -->
  114. <view v-if="current == 2">
  115. <note-Box
  116. :isPlayRebuild="sectionItem.rebuild"
  117. :refPlv="refPlv"
  118. @jumpNote="jumpNote"
  119. :params="params()"
  120. ></note-Box>
  121. </view>
  122. <!--答疑 -->
  123. <view v-show="current == 3">
  124. <answer-box
  125. :userId="userInfo ? userInfo.userId : 0"
  126. :params="params(['orderGoodsId', 'goodsId', 'courseId', 'gradeId'])"
  127. ></answer-box>
  128. </view>
  129. <!--目录 -->
  130. <view v-show="current == 4">
  131. <view
  132. class="menuBox"
  133. v-for="(item, index) in reMenuList"
  134. :key="index"
  135. >
  136. <!--模块 -->
  137. <view v-if="item.type == 1"
  138. ><courseModule
  139. :orderGoodsId="orderGoodsId"
  140. :sectionMaxNum="goodsData.sectionMaxNum"
  141. :courseId="courseId"
  142. :learningOrder="orderNum"
  143. :goodsId="goodsId"
  144. :gradeId="gradeId"
  145. :isRebuild="true"
  146. :isBuy="true"
  147. :menuItem="item"
  148. :levelId="item.menuId"
  149. :goodsType="1"
  150. :menuAllList="menuAllList"
  151. :sectionItem="sectionItem"
  152. ></courseModule
  153. ></view>
  154. <!--章 -->
  155. <view v-if="item.type == 2">
  156. <courseChapter
  157. :orderGoodsId="orderGoodsId"
  158. :courseId="courseId"
  159. :learningOrder="orderNum"
  160. :sectionMaxNum="goodsData.sectionMaxNum"
  161. @playEnd="sectionPlayEnd($event, index)"
  162. :gradeId="gradeId"
  163. :goodsId="goodsId"
  164. :isRebuild="true"
  165. :isBuy="true"
  166. :menuItem="item"
  167. :levelId="'0-' + item.menuId"
  168. :goodsType="1"
  169. :menuAllList="menuAllList"
  170. ></courseChapter>
  171. </view>
  172. <!--节 -->
  173. <view v-if="item.type == 3">
  174. <courseSection
  175. :orderGoodsId="orderGoodsId"
  176. :courseId="courseId"
  177. :learningOrder="orderNum"
  178. :sectionMaxNum="goodsData.sectionMaxNum"
  179. @playEnd="sectionPlayEnd($event, index)"
  180. :gradeId="gradeId"
  181. :goodsId="goodsId"
  182. :isRebuild="true"
  183. :isBuy="true"
  184. :nextMenuItem="findMenuNextSection(index)"
  185. :menuItem="item"
  186. :levelId="'0-0-' + item.menuId"
  187. :goodsType="1"
  188. :testType="3"
  189. :menuAllList="menuAllList"
  190. ></courseSection>
  191. </view>
  192. </view>
  193. </view>
  194. </scroll-view>
  195. </view>
  196. <u-popup
  197. v-model="noticeShow"
  198. class="notice_modal"
  199. mode="center"
  200. border-radius="28"
  201. width="650rpx"
  202. height="622rpx"
  203. :mask-close-able="false"
  204. @close="closeNotice"
  205. >
  206. <view class="content">
  207. <view class="title">学员须知</view>
  208. <scroll-view scroll-y="true">
  209. <view
  210. class="text"
  211. v-html="
  212. goodsData.buyNote && goodsData.buyNote.replace(/\n|\r\n/g, '<br>')
  213. "
  214. >
  215. </view>
  216. </scroll-view>
  217. <view
  218. class="had_read"
  219. :class="{ gray: CountTo >= 0 }"
  220. @click="noticeConfirm()"
  221. >
  222. <text v-if="CountTo >= 0">请阅读学员须知,30s后可关闭</text>
  223. <text v-else>我已阅读学员须知</text>
  224. <text v-if="CountTo >= 0">{{ " " + CountTo + "s" }}</text>
  225. </view>
  226. </view>
  227. </u-popup>
  228. <!-- 倒计时提交 -->
  229. <u-popup
  230. v-model="noticeShow1"
  231. class="notice_modal"
  232. mode="center"
  233. border-radius="28"
  234. width="650rpx"
  235. height="262rpx"
  236. :mask-close-able="false"
  237. >
  238. <view class="content">
  239. <view class="title">提示</view>
  240. <view class="had_read">
  241. <text v-if="CountTo1 >= 0">视频学习时长不达标,请等待</text>
  242. <text v-if="CountTo1 >= 0">{{ " " + CountTo1 + "s" }}</text>
  243. </view>
  244. </view>
  245. </u-popup>
  246. <u-modal
  247. v-model="showMark"
  248. title="提示"
  249. @confirm="markConfirm"
  250. @cancel="toBack"
  251. confirm-text="复制学习网址"
  252. :show-cancel-button="true"
  253. cancel-text="关闭"
  254. >
  255. <view class="slot-content">
  256. <view>您的学习账号已经开通,请按照步骤操作,进行学习。</view>
  257. <view>1.复制学习地址:{{ markContent }}</view>
  258. <view>2.在【浏览器中】打开复制的学习网址</view>
  259. <view>3.打开学习网址后,选择【个人用户】进行登录</view>
  260. <view>(1)账号:您个人的身份证号码</view>
  261. <view>(2)密码:身份证号码,再加111111</view>
  262. </view>
  263. </u-modal>
  264. <!-- 切换课程弹窗 -->
  265. <u-popup
  266. v-model="toggleCourseShow"
  267. mode="bottom"
  268. border-radius="40"
  269. :mask-close-able="false"
  270. >
  271. <view class="popup_box">
  272. <view class="check_head">
  273. <view class="headers">
  274. <view class="grade">切换课程</view>
  275. <u-icon
  276. name="close"
  277. color="#9C9C9C"
  278. size="40"
  279. @click="closePop()"
  280. ></u-icon>
  281. </view>
  282. <view class="coruse_num">共{{ goodsTeacher.length }}门</view>
  283. <view class="menuSel">
  284. <scroll-view class="sub_sliper" scroll-x="true">
  285. <view
  286. v-for="(item, index) in subList"
  287. :key="index"
  288. style="margin-right: 50rpx; display: inline-block"
  289. >
  290. <view
  291. class="r_t1"
  292. :class="{ nactive: subIndex == index }"
  293. @click="cMenu(item, index)"
  294. >
  295. {{ item.subjectName }}
  296. </view>
  297. </view>
  298. </scroll-view>
  299. </view>
  300. </view>
  301. <view class="check_con">
  302. <scroll-view scroll-y="true" style="height: 700rpx">
  303. <view v-for="(courseItem, gTindex) in goodsTeacher" :key="gTindex">
  304. <view
  305. v-for="(item, index) in courseItem.courseList"
  306. :key="index"
  307. v-show="
  308. item.subjectId === newActiveSubjectId || !newActiveSubjectId
  309. "
  310. >
  311. <view class="course_items" v-if="item.show && item.show == 1">
  312. <view class="course_lefts">
  313. <view class="course_title">
  314. {{ item.courseName }}
  315. </view>
  316. <view
  317. v-if="courseItem.teaList && courseItem.teaList.length > 0"
  318. class="teacher_names"
  319. >
  320. <view
  321. v-for="(tea, tindex) in courseItem.teaList"
  322. :key="tindex"
  323. class="names"
  324. >{{ tea.aliasName }}</view
  325. >
  326. </view>
  327. <view class="course_pros">
  328. 学习进度
  329. <text>
  330. {{ item.stuAllNum + item.recordNum }}/{{
  331. item.secAllNum + item.examNum
  332. }}</text
  333. >
  334. </view>
  335. </view>
  336. <view
  337. class="course_rights"
  338. @click="jump(item, gTindex, 'jump')"
  339. >
  340. <view class="cicles">
  341. <u-icon
  342. name="arrow-right"
  343. color="#498AFE"
  344. size="20"
  345. ></u-icon>
  346. </view>
  347. <view class="intoStudy">进入学习</view>
  348. </view>
  349. </view>
  350. </view>
  351. </view>
  352. </scroll-view>
  353. </view>
  354. </view>
  355. </u-popup>
  356. <!-- 摄像头 -->
  357. <Popup-camera :goodsId="goodsId" ref="camera"></Popup-camera>
  358. </view>
  359. </template>
  360. <script>
  361. import plv from "../static/polyv-sdk/index";
  362. import courseModule from "@/components/course/courseModule.vue";
  363. import courseChapter from "@/components/course/courseChapter.vue";
  364. import courseSection from "@/components/course/courseSection.vue";
  365. import handoutsBox from "@/components/course/handoutsBox.vue";
  366. import courseTree from "@/components/course/courseTree.vue";
  367. import PopupCamera from "../../components/popup/camera.vue";
  368. import myCompressImage from "@/common/compressPhoto.js";
  369. import myPlayer from "@/components/myPlayer/polyvPlayer.vue";
  370. import noteBox from "@/components/course/noteBox.vue";
  371. import answerBox from "@/components/course/answerBox.vue";
  372. import { mapGetters, mapMutations } from "vuex";
  373. import { lockAction } from "../../utils/lock";
  374. export default {
  375. components: {
  376. courseModule,
  377. courseChapter,
  378. courseSection,
  379. handoutsBox,
  380. myPlayer,
  381. noteBox,
  382. answerBox,
  383. courseTree,
  384. PopupCamera,
  385. },
  386. data() {
  387. return {
  388. markContent: "http://admin.zhujianpeixun.com/",
  389. showMark: false,
  390. hasStart: false,
  391. channelItem: null,
  392. lockTimer: null,
  393. orderGoodsId: 0,
  394. noticeShow: false,
  395. enableAutoRotation: true,
  396. seekTime: "",
  397. toastTimer: null,
  398. videoToastShow: false,
  399. showSet: false,
  400. startStatus: false,
  401. courseId: 0,
  402. menuList: [],
  403. current: 0,
  404. vid: "",
  405. goodsId: 0,
  406. goodsData: {},
  407. photoPopup: false,
  408. goodsPlayConfig: null,
  409. autoplay: false,
  410. isAllowSeek: "no",
  411. playbackRate: [1.0],
  412. timer: null,
  413. goodsPhotographConfig: null,
  414. intervalTimeList: [], // 间隔拍照时长
  415. intervalTimeIndex: 0, //当前处于哪个时间段拍照
  416. playTime: 0, //页面播放时长,不含暂停
  417. currentTime: 0,
  418. avatarUrl: "",
  419. ossAvatarUrl: "",
  420. studyDuration: 0, // 当前视频时长
  421. gradeId: 0,
  422. chapterId: 0,
  423. moduleId: 0,
  424. reMenuList: [],
  425. recordObj: {},
  426. isTaking: true, //是否正在拍照
  427. needSeek: false, //第一次播放是否需要跳转
  428. photoNum: 0,
  429. photoList: [], //拍照的时间点
  430. photoConfig: false, //是否配置好拍照次数
  431. photoIndex: 0, //当前位于拍照的区间下标 从0开始
  432. photoHistoryList: [], //已拍照历史的下标点
  433. sectionItem: {},
  434. showNotes: true,
  435. uploadLock: false, //上传图片
  436. isPlayRebuild: false, //是否正在播放重修视频needOpen
  437. isRebuild: false, //视频是否从重修目录点击
  438. clearTimer: null,
  439. livingItem: "",
  440. option: null,
  441. toggleCourseShow: false, // 切换课程弹窗
  442. courseList: [], // 课程列表
  443. subList: [],
  444. subIndex: 0,
  445. goodsTeacher: [],
  446. teacherList: [],
  447. teacherIndex: 0,
  448. newActiveSubjectId: "", //当前选中ID
  449. compareFaceData: 0, // 拍照匹配相似度
  450. prendreAutoCarme: false, // 是否发起授权相机
  451. studyTimer: null, // 学习记录定时器
  452. CountTo: 30, // 倒计时
  453. CountTo1: 0,
  454. handoutTipLength: 0,
  455. menuAllList: [],
  456. curPlayIndex: 0, // 正在播放的节的下标
  457. faceUrl: "",
  458. erJianErZao: false,
  459. jjShiGongYuan: false,
  460. pauseTime: 0,
  461. pauseTimer: null,
  462. barTimer: null,
  463. isReach: false,
  464. noticeShow1: false,
  465. refPlv: null,
  466. };
  467. },
  468. computed: {
  469. ...mapGetters([
  470. "userInfo",
  471. "playSectionId",
  472. "playChannelId",
  473. "playVID",
  474. "config",
  475. ]),
  476. playSecIsLearn() {
  477. return this.sectionItem.learning != 1;
  478. },
  479. orderNum() {
  480. return 0;
  481. return this.goodsData.goodsLearningOrder;
  482. },
  483. list() {
  484. let list = [
  485. {
  486. name: "目录",
  487. },
  488. {
  489. name: "讲义",
  490. },
  491. {
  492. name: "笔记",
  493. },
  494. {
  495. name: "答疑",
  496. },
  497. ];
  498. if (this.reMenuList.length > 0) {
  499. list.push({ name: "重修目录" });
  500. }
  501. return list;
  502. },
  503. params: function () {
  504. return (keys = ["orderGoodsId", "goodsId", "courseId"]) => {
  505. let params = {};
  506. for (const key of keys) {
  507. params[key] = this[key];
  508. }
  509. return params;
  510. };
  511. },
  512. detail() {
  513. if (!this.courseId || !this.courseList.length) {
  514. return {};
  515. }
  516. return this.courseList.find((e) => e.courseId == this.courseId);
  517. },
  518. },
  519. watch: {
  520. showSet(n) {
  521. if (n) {
  522. if (polyvPlayerContext) {
  523. // #ifdef MP-WEIXIN
  524. polyvPlayerContext.pause();
  525. // #endif
  526. // #ifdef H5
  527. polyvPlayerContext.j2s_pauseVideo(); // 暂停播放视频
  528. // #endif
  529. }
  530. }
  531. },
  532. photoPopup(n) {
  533. if (n) {
  534. if (this.prendreAutoCarme) {
  535. this.photoPopup = false;
  536. }
  537. this.showSet && (this.photoPopup = false);
  538. }
  539. },
  540. },
  541. async onLoad(option) {
  542. if (option.isOther) {
  543. this.showMark = true;
  544. return;
  545. }
  546. this.option = option;
  547. let { skipPort, id, goodsId, orderGoodsId, gradeId, informId } = option;
  548. if (skipPort) {
  549. await this.$method.skipLogin(skipPort);
  550. }
  551. if (this.$method.isGoLogin()) {
  552. return;
  553. }
  554. this.orderGoodsId = Number(orderGoodsId) || "";
  555. this.isCanLearn();
  556. !this.userInfo && this.$api.refreshUserInfo();
  557. this.courseId = Number(id) || "";
  558. this.goodsId = Number(goodsId);
  559. this.gradeId = Number(gradeId);
  560. // 公众号模板消息的数据埋点
  561. informId && this.clickOfficial(informId);
  562. this.init();
  563. this.courseCourseList();
  564. },
  565. async onShow() {
  566. // this.closePhoto();
  567. },
  568. onUnload() {
  569. console.log("onUnloadonUnloadonUnloadonUnload");
  570. this.originUnload();
  571. this.clears();
  572. clearInterval(this.lockTimer);
  573. },
  574. onHide() {
  575. this.originUnload();
  576. },
  577. mounted() {},
  578. methods: {
  579. ...mapMutations(["updateChapterOpen", "updateLiveLast"]),
  580. init() {
  581. // #ifdef MP-WEIXIN
  582. this.isAllowSeek = "no";
  583. // #endif
  584. // #ifdef H5
  585. this.isAllowSeek = "on";
  586. // #endif
  587. // 锁
  588. lockAction();
  589. this.lockTimer = setInterval(lockAction, 10000);
  590. },
  591. // 七大员是否能进入学习
  592. async qCheckIsCanLearn() {
  593. let res = await this.$api.qCheckIsCanLearn(this.orderGoodsId);
  594. if (res.data.code !== 200) {
  595. uni.showModal({
  596. showCancel: false,
  597. title: "提示",
  598. content: res.data.msg,
  599. success: (resultst) => {
  600. uni.navigateBack();
  601. },
  602. });
  603. return Promise.reject();
  604. }
  605. let res1 = await this.$api.syncSevenPublicClass({
  606. orderGoodsId: this.orderGoodsId,
  607. });
  608. if (res1.data.code !== 200) {
  609. uni.showModal({
  610. showCancel: false,
  611. title: "提示",
  612. content: "无法进入学习!",
  613. success: (resultst) => {
  614. uni.navigateBack();
  615. },
  616. });
  617. return Promise.reject();
  618. }
  619. },
  620. // 新增微信公众号模板消息点击数据
  621. clickOfficial(informId) {
  622. this.$http({
  623. url: "/data/click",
  624. method: "post",
  625. data: { informId },
  626. });
  627. },
  628. // 点击课程目录
  629. cMenu(item, index) {
  630. this.subIndex = index;
  631. this.newActiveSubjectId = item.subjectId;
  632. },
  633. courseCourseList() {
  634. this.courseList = [];
  635. this.menuList = [];
  636. this.photoConfig = false;
  637. this.$api
  638. .courseCourseList({
  639. pageNum: 1,
  640. pageSize: 200,
  641. goodsId: this.goodsId,
  642. gradeId: this.gradeId,
  643. orderGoodsId: this.orderGoodsId,
  644. })
  645. .then((res) => {
  646. if (res.data.code == 200) {
  647. this.courseList = res.data.rows;
  648. // 科目
  649. let allItem = [{ subjectId: 0, subjectName: "所有" }];
  650. let ids = [];
  651. const newArr = [];
  652. this.courseList.forEach((item) => {
  653. if (ids.indexOf(item.subjectId) == -1) {
  654. ids.push(item.subjectId);
  655. newArr.push(item);
  656. }
  657. });
  658. this.subList = [...allItem, ...newArr];
  659. if (res.data.total > 1) {
  660. this.getUserWatchLast();
  661. } else {
  662. this.originOnShow();
  663. this.originMounted();
  664. }
  665. }
  666. });
  667. },
  668. // 查询用户最后一次看的录播的信息
  669. getUserWatchLast() {
  670. this.$http({
  671. url: "/study/record/getUserWatchLast",
  672. method: "get",
  673. data: {
  674. orderGoodsId: this.orderGoodsId,
  675. },
  676. }).then((res) => {
  677. console.log("🚀 ~ file: detail.vue:859 ~ getUserWatchLast ~ res:", res);
  678. if (res.data.code == 200 && res.data.data) {
  679. this.courseId = res.data.data.courseId;
  680. }
  681. if (!this.courseId) {
  682. this.courseId = this.courseList[0].courseId;
  683. }
  684. this.originOnShow();
  685. this.originMounted();
  686. //获取商品双师资模板
  687. this.getCourseTeacher(this.courseList);
  688. });
  689. },
  690. getCourseTeacher(rows) {
  691. this.goodsTeacher = [];
  692. //获取商品双师资模板
  693. this.$api
  694. .courseTeacherList({
  695. goodsId: this.goodsId,
  696. })
  697. .then((res1) => {
  698. if (res1.data.data && res1.data.data.length > 0) {
  699. //课程老师模板
  700. let teacherTel = res1.data.data;
  701. //商品课程
  702. let courses = rows;
  703. teacherTel.forEach((tea) => {
  704. let dataList = [];
  705. let teacherList = [];
  706. courses.forEach((item) => {
  707. let data = tea.courseList.filter(
  708. (x) => x.courseId == item.courseId
  709. );
  710. if (data && data.length > 0) {
  711. dataList.push(item);
  712. teacherList = tea.courseList;
  713. }
  714. });
  715. let result = {
  716. teaList: teacherList,
  717. courseList: dataList,
  718. };
  719. this.goodsTeacher.push(result);
  720. });
  721. if (this.goodsTeacher && this.goodsTeacher.length > 0) {
  722. let courseIds = [];
  723. this.goodsTeacher.forEach((item) => {
  724. item.courseList.forEach((course) => {
  725. courseIds.push(course.courseId);
  726. });
  727. });
  728. if (courseIds.length > 0) {
  729. courses.forEach((item) => {
  730. if (!courseIds.includes(item.courseId)) {
  731. let data = {
  732. teaList: [],
  733. courseList: [],
  734. };
  735. data.courseList.push(item);
  736. this.goodsTeacher.push(data);
  737. }
  738. });
  739. }
  740. this.goodsTeacher.forEach((item) => {
  741. if (item.courseList && item.courseList.length > 0) {
  742. item.courseList[0].show = 1;
  743. }
  744. });
  745. }
  746. } else {
  747. //没有双师资模板
  748. rows.forEach((item) => {
  749. item.show = 1;
  750. let data = {
  751. teaList: [],
  752. courseList: [],
  753. };
  754. data.courseList.push(item);
  755. this.goodsTeacher.push(data);
  756. });
  757. }
  758. this.goodsTeacher.forEach((item) => {
  759. if (item.courseList.some((x) => x.courseId == this.courseId)) {
  760. this.teacherList = item.teaList;
  761. }
  762. });
  763. });
  764. },
  765. erJianErZaoPauseTip() {
  766. if (this.playSecIsLearn && this.erJianErZao) {
  767. if (this.pauseTimer) {
  768. return;
  769. }
  770. this.pauseTime = Date.now();
  771. console.log("开启定时器");
  772. this.pauseTimer = setInterval(() => {
  773. console.log("暂停时间", Date.now() - this.pauseTime, this.pauseTime);
  774. if (Date.now() - this.pauseTime > 5 * 60 * 1000) {
  775. // 5 * 60 * 1000
  776. let text = this.photoPopup ? "拍照停留" : "暂停";
  777. this.photoPopup = false;
  778. uni.showModal({
  779. title: "提示",
  780. showCancel: false,
  781. content: `检测${text}时间过长,刷新当前页面`,
  782. cancelText: "取消",
  783. confirmText: "确定",
  784. success: (res) => {
  785. if (res.confirm) {
  786. // #ifdef H5
  787. location.reload();
  788. // #endif
  789. // #ifdef MP-WEIXIN
  790. this.courseCourseList();
  791. // #endif
  792. }
  793. },
  794. });
  795. this.clearPauseTimer();
  796. }
  797. }, 5000);
  798. }
  799. },
  800. // 原来onshow里面的内容
  801. async originOnShow() {
  802. this.getReMenuList();
  803. await this.studyRecordMenuAllList();
  804. // 消息过来 定位某个节
  805. if (this.option.noteSecond) {
  806. this.jumpNote({
  807. sectionType: 1,
  808. ...this.option,
  809. });
  810. return;
  811. }
  812. this.studyRecordQueryLiveLast();
  813. },
  814. // 原来的mouted内容
  815. originMounted() {
  816. uni.$on("changeSection", (oldSectionId) => {
  817. console.log(
  818. "切换课程-originMounted->playVID:",
  819. this.playVID,
  820. oldSectionId
  821. );
  822. this.studyTimer && clearInterval(this.studyTimer); // 清除定时器
  823. this.clearPauseTimer();
  824. this.hasStart = false;
  825. this.photoConfig = false;
  826. this.photoIndex = 0;
  827. //清除直播
  828. this.$store.commit("setPlayChannelId", { playChannelId: 0 });
  829. // 防止原先初始化错误
  830. if (!this.refPlv) {
  831. this.refPlv = this.$refs.player;
  832. }
  833. this.postStudyRecord(0, oldSectionId);
  834. });
  835. uni.$on("getSection", (item) => {
  836. //清除直播
  837. this.studyTimer && clearInterval(this.studyTimer);
  838. this.hasStart = false;
  839. this.isPlayRebuild = item.rebuild;
  840. this.photoConfig = false;
  841. this.photoIndex = 0;
  842. this.sectionItem = item;
  843. this.moduleId = item.moduleId || null;
  844. this.chapterId = item.chapterId || null;
  845. this.$store.commit("setPlayChannelId", { playChannelId: 0 });
  846. this.playVideo(item);
  847. });
  848. uni.$on("levelId", (item) => {
  849. let arr = item.split("-");
  850. //点击节获取的各层级ID
  851. this.moduleId = arr[0];
  852. this.chapterId = arr[1];
  853. });
  854. uni.$on("getChannel", (item) => {
  855. //清除录播
  856. this.studyTimer && clearInterval(this.studyTimer);
  857. this.hasStart = false;
  858. this.$store.commit("setPlayVID", { playVID: null });
  859. this.moduleId = item.moduleId;
  860. this.chapterId = item.chapterId;
  861. this.$store.commit("setPlaySectionId", {
  862. playSectionId: item.sectionId || item.menuId,
  863. });
  864. this.playChannel(item);
  865. this.channelItem = item;
  866. });
  867. uni.$on("isRebuild", (item) => {
  868. this.isRebuild = item;
  869. });
  870. this.updateChapterOpen(true);
  871. },
  872. clearPauseTimer() {
  873. if (this.pauseTimer) {
  874. this.pauseTime = 0;
  875. clearInterval(this.pauseTimer);
  876. this.pauseTimer = null;
  877. }
  878. },
  879. clearBarTimer() {
  880. if (this.barTimer) {
  881. clearInterval(this.barTimer);
  882. this.barTimer = null;
  883. }
  884. },
  885. // 原来onUnload里面的内容
  886. originUnload() {
  887. if (this.playSectionId > 0 && this.hasStart) {
  888. //退出提交记录
  889. this.postStudyRecord();
  890. }
  891. //清除正在播放的节ID
  892. // this.$store.commit('setPlayObj',null)
  893. this.$store.commit("setPlaySectionId", { playSectionId: 0 });
  894. this.$store.commit("setPlayChannelId", { playChannelId: 0 });
  895. this.$store.commit("setPlayVID", { playVID: null });
  896. //移除所有的事件监听器
  897. uni.$off();
  898. this.clearTimer && clearTimeout(this.clearTimer);
  899. this.toastTimer && clearTimeout(this.toastTimer);
  900. if (this.studyTimer) {
  901. clearInterval(this.studyTimer);
  902. this.hasStart = false;
  903. }
  904. this.clearPauseTimer();
  905. this.clearBarTimer();
  906. if (this.lockTimer) {
  907. clearInterval(this.lockTimer);
  908. this.$api
  909. .lockDelLock({
  910. action: "jxjy",
  911. uuid: this.$method.getUuid(),
  912. })
  913. .then((res) => {
  914. uni.hideLoading();
  915. });
  916. }
  917. },
  918. changeCourses() {
  919. this.toggleCourseShow = true;
  920. },
  921. closePop() {
  922. this.toggleCourseShow = false;
  923. },
  924. async activeFunc(item, index) {
  925. this.teacherIndex = index;
  926. let findResult = "";
  927. this.goodsTeacher.forEach((citem, index) => {
  928. citem.courseList.forEach((e, e_index) => {
  929. if (e.courseId == item.courseId) {
  930. findResult = e;
  931. }
  932. });
  933. });
  934. this.jump(findResult, 1);
  935. },
  936. // 进入学习
  937. async jump(item, index, type) {
  938. if (this.courseId === item.courseId) {
  939. this.toggleCourseShow = false;
  940. return;
  941. }
  942. if (this.orderNum == 2 && index != 0) {
  943. let prevItem = this.courseList[index - 1];
  944. if (
  945. prevItem.stuAllNum + prevItem.recordNum <
  946. prevItem.secAllNum + prevItem.examNum
  947. ) {
  948. uni.showToast({
  949. icon: "none",
  950. title: "请按顺序学完上一课再学习这一课",
  951. });
  952. return;
  953. }
  954. }
  955. // 打回 需重修
  956. if (item.rebuild === 0) {
  957. this.$navTo.togo("/pages2/learn/details", {
  958. id: item.courseId,
  959. gradeId: item.gradeId,
  960. goodsId: this.goodsId,
  961. orderGoodsId: this.orderGoodsId,
  962. });
  963. return;
  964. }
  965. if (type) {
  966. this.teacherIndex = 0;
  967. this.goodsTeacher.forEach((citem) => {
  968. if (citem.courseList.some((x) => x.courseId == item.courseId)) {
  969. this.teacherList = citem.teaList;
  970. }
  971. });
  972. }
  973. this.sectionItem = {};
  974. this.toggleCourseShow = false;
  975. this.courseId = item.courseId;
  976. this.originOnShow();
  977. },
  978. async nextCourses(item, type) {
  979. this.vid = "";
  980. this.hasStart = true;
  981. await this.originUnload();
  982. this.courseId = item.courseId;
  983. this.gradeId = item.gradeId;
  984. this.toggleCourseShow = false;
  985. },
  986. clickLeft() {
  987. // uni.navigateBack()
  988. uni.switchTab({
  989. url: "/pages/learn/index",
  990. });
  991. },
  992. toBack(delta = 1) {
  993. uni.navigateBack({
  994. delta,
  995. });
  996. },
  997. markConfirm() {
  998. uni.setClipboardData({
  999. data: this.markContent,
  1000. success: () => {
  1001. setTimeout(this.toBack, 1000);
  1002. },
  1003. });
  1004. },
  1005. closeNotice() {
  1006. this.$api
  1007. .baseHandoutTip({
  1008. orderGoodsId: this.orderGoodsId,
  1009. })
  1010. .then((res) => {});
  1011. },
  1012. noticeConfirm() {
  1013. if (this.CountTo < 0) {
  1014. this.noticeShow = false;
  1015. if (this.handoutTipLength == 0 && this.goodsPlayConfig.autoPlay > 0) {
  1016. this.autoplay = true;
  1017. this.refPlv.resumeVideo();
  1018. }
  1019. }
  1020. },
  1021. baseHandoutTipList() {
  1022. this.$api
  1023. .baseHandoutTipList({
  1024. orderGoodsId: this.orderGoodsId,
  1025. })
  1026. .then((res) => {
  1027. this.handoutTipLength = res.data.rows.length;
  1028. if (res.data.rows.length == 0) {
  1029. this.noticeShow = true;
  1030. if (this.CountTo == 30) {
  1031. var timer = setInterval(() => {
  1032. this.CountTo--;
  1033. if (this.CountTo < 0) {
  1034. clearInterval(timer);
  1035. }
  1036. }, 1000);
  1037. }
  1038. } else {
  1039. this.CountTo = -1;
  1040. }
  1041. });
  1042. },
  1043. /**
  1044. * 获取上次观看的直播
  1045. */
  1046. studyRecordGetLastLive() {
  1047. this.$api
  1048. .studyRecordGetLastLive({
  1049. orderGoodsId: this.orderGoodsId,
  1050. courseId: this.courseId,
  1051. })
  1052. .then((res) => {
  1053. this.updateLiveLast(res.data.data);
  1054. });
  1055. },
  1056. async initPlayVideo(sectionItem) {
  1057. this.moduleId = sectionItem.moduleId;
  1058. this.chapterId = sectionItem.chapterId;
  1059. if (sectionItem.sectionType == 1) {
  1060. //录播
  1061. this.playVideo(sectionItem);
  1062. } else if (sectionItem.sectionType == 2) {
  1063. //直播
  1064. this.studyRecordGetLastLive();
  1065. } else if (sectionItem.sectionType == 3) {
  1066. //回放
  1067. this.playVideo(sectionItem);
  1068. } else if (sectionItem.doType == 2) {
  1069. uni.showModal({
  1070. title: "温馨提示",
  1071. content: "当前节视频已学完,是否进入考试?",
  1072. success: (res) => {
  1073. if (res.confirm) {
  1074. this.toQuestionBank(sectionItem);
  1075. }
  1076. },
  1077. });
  1078. return;
  1079. }
  1080. },
  1081. toQuestionBank(sectionItem) {
  1082. uni.navigateTo({
  1083. url:
  1084. "/pages2/class/questionBank?courseId=" +
  1085. this.courseId +
  1086. "&gradeId=" +
  1087. this.gradeId +
  1088. "&isFromVideo=1&id=" +
  1089. sectionItem.id +
  1090. "&goodsid=" +
  1091. this.goodsId +
  1092. "&moduleId=" +
  1093. (sectionItem.moduleId || 0) +
  1094. "&chapterId=" +
  1095. (sectionItem.chapterId || 0) +
  1096. "&orderGoodsId=" +
  1097. this.orderGoodsId +
  1098. "&type=" +
  1099. (sectionItem.type == 4 ? 1 : 3) +
  1100. "&learning=" +
  1101. sectionItem.studyStatus +
  1102. "&isBackVideo=" +
  1103. 1,
  1104. });
  1105. },
  1106. studyRecordQueryLiveLast() {
  1107. // /study/record/queryLiveLast
  1108. this.$api
  1109. .studyRecordQueryLiveLast({
  1110. gradeId: this.gradeId,
  1111. orderGoodsId: this.orderGoodsId,
  1112. courseId: this.courseId,
  1113. })
  1114. .then((res) => {
  1115. let { data } = res.data;
  1116. if (!data.sectionId) {
  1117. data = this.menuAllList[0];
  1118. }
  1119. // if (data.learning == 1 && this.orderNum == 2) {
  1120. // let next = this.menuAllList.find((e) => e.studyStatus != 1);
  1121. // next && (data = next);
  1122. // }
  1123. this.initPlayVideo(data);
  1124. });
  1125. },
  1126. /**
  1127. * 模块大节播放完毕,刷新列表
  1128. */
  1129. sectionPlayEnd(isRebuild, index) {
  1130. if (this.reMenuList.length > 0) {
  1131. //有重修目录
  1132. if (isRebuild.isRebuild) {
  1133. //从重修点击
  1134. this.$api
  1135. .reMenuList({
  1136. orderGoodsId: this.orderGoodsId,
  1137. courseId: this.courseId,
  1138. rebuild: 1,
  1139. gradeId: this.gradeId,
  1140. })
  1141. .then((res) => {
  1142. if (res.data.code == 200) {
  1143. if (res.data.rows.length) {
  1144. res.data.rows[index].name = res.data.rows[index].menuName;
  1145. this.$set(this.reMenuList, index, res.data.rows[index]);
  1146. for (let i = 0; i < res.data.rows.length; i++) {
  1147. let item = res.data.rows[i];
  1148. item.down = true;
  1149. item.id = item.menuId;
  1150. item.name = item.menuName;
  1151. }
  1152. this.reMenuList = [];
  1153. this.$nextTick(() => {
  1154. this.reMenuList = res.data.rows;
  1155. // console.log(this.reMenuList,'this.reMenuList1')
  1156. });
  1157. } else {
  1158. this.reMenuList = [];
  1159. }
  1160. }
  1161. });
  1162. this.$api
  1163. .reMenuList({
  1164. courseId: this.courseId,
  1165. gradeId: this.gradeId,
  1166. orderGoodsId: this.orderGoodsId,
  1167. })
  1168. .then((res) => {
  1169. if (res.data.code == 200) {
  1170. for (let i = 0; i < res.data.rows.length; i++) {
  1171. let item = res.data.rows[i];
  1172. item.down = true;
  1173. item.id = item.menuId;
  1174. item.name = item.menuName;
  1175. item.menuType = item.type;
  1176. }
  1177. this.menuList = [];
  1178. this.$nextTick(() => {
  1179. this.menuList = res.data.rows;
  1180. });
  1181. }
  1182. });
  1183. } else {
  1184. //从普通目录点击
  1185. this.$api
  1186. .reMenuList({
  1187. courseId: this.courseId,
  1188. gradeId: this.gradeId,
  1189. orderGoodsId: this.orderGoodsId,
  1190. })
  1191. .then((res) => {
  1192. if (res.data.code == 200) {
  1193. res.data.rows[index].name = res.data.rows[index].menuName;
  1194. res.data.rows[index].id = res.data.rows[index].menuId;
  1195. this.$set(this.menuList, index, res.data.rows[index]);
  1196. }
  1197. });
  1198. this.$api
  1199. .reMenuList({
  1200. orderGoodsId: this.orderGoodsId,
  1201. courseId: this.courseId,
  1202. rebuild: 1,
  1203. gradeId: this.gradeId,
  1204. })
  1205. .then((res) => {
  1206. if (res.data.code == 200) {
  1207. if (res.data.rows.length) {
  1208. // for (let i = 0; i < res.data.rows.length; i++) {
  1209. // let item = res.data.rows[i];
  1210. // item.down = true;
  1211. // item.id = item.menuId;
  1212. // item.name = item.menuName;
  1213. // }
  1214. // this.reMenuList = res.data.rows;
  1215. } else {
  1216. this.reMenuList = [];
  1217. }
  1218. }
  1219. });
  1220. }
  1221. } else {
  1222. console.log("--模块大节播放完毕,刷新列表-");
  1223. //没有重修目录
  1224. this.$api
  1225. .reMenuList({
  1226. courseId: this.courseId,
  1227. gradeId: this.gradeId,
  1228. orderGoodsId: this.orderGoodsId,
  1229. })
  1230. .then((res) => {
  1231. if (res.data.code == 200) {
  1232. res.data.rows[index].name = res.data.rows[index].menuName;
  1233. res.data.rows[index].id = res.data.rows[index].menuId;
  1234. this.$set(this.menuList, index, res.data.rows[index]);
  1235. }
  1236. });
  1237. }
  1238. },
  1239. goLive(item) {
  1240. let moduleId = item.moduleId || 0;
  1241. let chapterId = item.chapterId || 0;
  1242. let sectionId = item.sectionId || item.menuId;
  1243. let uuid = new Date().valueOf() + "";
  1244. // buyCourse 是否购买课程:1是 0否
  1245. let encode = encodeURIComponent(
  1246. this.config.hostLive +
  1247. "/pages/live/index?token=" +
  1248. uni.getStorageSync("token") +
  1249. "&userInfo=" +
  1250. JSON.stringify(this.userInfo) +
  1251. "&channelId=" +
  1252. item.liveUrl +
  1253. "&gradeId=" +
  1254. this.gradeId +
  1255. "&courseId=" +
  1256. this.courseId +
  1257. "&goodsId=" +
  1258. this.goodsId +
  1259. "&orderGoodsId=" +
  1260. this.orderGoodsId +
  1261. "&sectionId=" +
  1262. sectionId +
  1263. "&chapterId=" +
  1264. chapterId +
  1265. "&moduleId=" +
  1266. moduleId +
  1267. "&buyCourse=1" +
  1268. "&ident=" +
  1269. uuid
  1270. );
  1271. uni.navigateTo({
  1272. url: `../../pages/webview/index?url=` + encode,
  1273. });
  1274. },
  1275. studyRecordMenuAllList() {
  1276. // study/record/menuAllList
  1277. return this.$api
  1278. .studMenuAllList({
  1279. courseId: this.courseId,
  1280. gradeId: this.gradeId,
  1281. goodsId: this.goodsId,
  1282. orderGoodsId: this.orderGoodsId,
  1283. })
  1284. .then((res) => {
  1285. let nowTime = Number(new Date().getTime() / 1000).toFixed(0);
  1286. if (res.data.data) {
  1287. this.menuAllList = res.data.data.filter(
  1288. (e) => e.doType != 1 || (e.doType == 2 && e.studyStatus == 1)
  1289. );
  1290. this.livingItem = res.data.data.find(
  1291. (item) =>
  1292. item.liveStartTime <= nowTime && item.liveEndTime > nowTime
  1293. );
  1294. }
  1295. return Promise.resolve();
  1296. });
  1297. },
  1298. async getbaseprofiletplists() {
  1299. let {
  1300. data: { code, rows },
  1301. } = await this.$api.getbaseprofiletplists({
  1302. goodsId: this.goodsId,
  1303. orderGoodsId: this.orderGoodsId,
  1304. });
  1305. if (code === 200 && rows.length && rows[0].keyValue) {
  1306. let baseRes = await this.$api.getbaseprofiletpId(this.goodsId);
  1307. if (baseRes.data.code === 200 && baseRes.data.data) {
  1308. let {
  1309. data: { code, data },
  1310. } = await this.$api.getbaseprofiletpgetInfo({
  1311. goodsId: this.goodsId,
  1312. orderGoodsId: this.orderGoodsId,
  1313. });
  1314. if (
  1315. code === 200 &&
  1316. (!data || (data.status === 3 && data.changeStatus === 1))
  1317. ) {
  1318. uni.showModal({
  1319. content: !data
  1320. ? "请前往填写资料"
  1321. : "资料审核不通过,请前往重新填写",
  1322. cancelText: "返回",
  1323. success: (resultst) => {
  1324. if (resultst.confirm) {
  1325. this.$navTo.togo("/pages2/verify/input", {
  1326. id: this.goodsId,
  1327. orderGoodsId: this.orderGoodsId,
  1328. });
  1329. }
  1330. if (resultst.cancel) {
  1331. uni.navigateBack();
  1332. }
  1333. },
  1334. });
  1335. return Promise.reject();
  1336. } else if (data.status === 1 && JSON.parse(rows[0].keyValue2)[0]) {
  1337. let {
  1338. data: { code, data },
  1339. } = await this.$api.getbaseprofileStampgetInfo({
  1340. goodsId: this.goodsId,
  1341. orderGoodsId: this.orderGoodsId,
  1342. });
  1343. if (
  1344. code === 200 &&
  1345. (!data || (data.status === 3 && data.changeStatus === 1))
  1346. ) {
  1347. uni.showModal({
  1348. content: !data
  1349. ? "请前往填写盖章资料"
  1350. : "资料盖章审核不通过,请前往重新填写",
  1351. cancelText: "返回",
  1352. success: (resultst) => {
  1353. if (resultst.confirm) {
  1354. this.$navTo.togo("/pages2/verify/input2", {
  1355. id: this.goodsId,
  1356. orderGoodsId: this.orderGoodsId,
  1357. });
  1358. }
  1359. if (resultst.cancel) {
  1360. uni.navigateBack();
  1361. }
  1362. },
  1363. });
  1364. return Promise.reject();
  1365. }
  1366. }
  1367. }
  1368. }
  1369. return Promise.resolve();
  1370. },
  1371. async isCanLearn() {
  1372. this.option.isQ !== "" && (await this.qCheckIsCanLearn());
  1373. await this.getbaseprofiletplists();
  1374. await this.getGradeInfo();
  1375. await this.getGoodsDetail();
  1376. },
  1377. /**
  1378. * 计算tabs宽度
  1379. */
  1380. itemWidth() {
  1381. return 100 / this.list.length + "%";
  1382. },
  1383. findMenuNextSection(index) {
  1384. for (let i = index + 1; i < this.reMenuList.length; i++) {
  1385. let item = this.reMenuList[i];
  1386. if (item.type == 3) {
  1387. return item;
  1388. }
  1389. }
  1390. return {};
  1391. },
  1392. loadedmetadata(event) {
  1393. this.$refs["camera"].openCamera();
  1394. this.refPlv = this.$refs.player;
  1395. },
  1396. getPhotoLastRecord() {
  1397. // if (this.erJianErZao || !this.playSecIsLearn || this.photoNum <= 0) {
  1398. // return;
  1399. // }
  1400. let self = this;
  1401. let { chapterId, sectionId, moduleId } = this.sectionItem;
  1402. let data = {
  1403. sectionId: sectionId || 0,
  1404. goodsId: this.goodsId,
  1405. courseId: this.courseId,
  1406. gradeId: this.gradeId,
  1407. chapterId: chapterId || 0,
  1408. moduleId: moduleId || 0,
  1409. orderGoodsId: this.orderGoodsId,
  1410. };
  1411. this.$api.getPhotoLastRecord(data).then((res) => {
  1412. console.log(res.data.data, 99999999999);
  1413. if (res.data.code == 200) {
  1414. //清空历史数据
  1415. self.photoHistoryList = [];
  1416. this.photoIndex = 0;
  1417. self.photoList = [];
  1418. for (let i = 0; i < res.data.data.length; i++) {
  1419. //-2存储随机拍照数组
  1420. if (res.data.data[i].photoIndex == -2) {
  1421. self.photoList =
  1422. res.data.data[i].timeInterval &&
  1423. res.data.data[i].timeInterval.split(",");
  1424. } else {
  1425. self.photoHistoryList.push(res.data.data[i].photoIndex);
  1426. }
  1427. }
  1428. }
  1429. });
  1430. },
  1431. //postTime 只提交随机时间
  1432. postCoursePhotoRecord(postTime = false) {
  1433. return new Promise((resolve, reject) => {
  1434. let currentTime = 0;
  1435. // var polyvPlayerContext = this.selectComponent("#playerVideo");
  1436. if (polyvPlayerContext) {
  1437. // #ifdef MP-WEIXIN
  1438. currentTime = polyvPlayerContext.getCurrentTime();
  1439. // #endif
  1440. // #ifdef H5
  1441. currentTime = polyvPlayerContext.j2s_getCurrentTime();
  1442. // #endif
  1443. }
  1444. let self = this;
  1445. let photoIndex = self.photoIndex;
  1446. let data = {
  1447. photo: self.ossAvatarUrl,
  1448. sectionId: parseInt(self.playSectionId),
  1449. goodsId: parseInt(self.goodsId),
  1450. courseId: parseInt(self.courseId),
  1451. photoTime: parseInt(currentTime > 0 ? currentTime : 0),
  1452. gradeId: parseInt(self.gradeId),
  1453. photoIndex: postTime ? -2 : parseInt(photoIndex), //从0算起,-2只提交随机时间
  1454. photoNum: parseInt(self.photoNum),
  1455. chapterId: parseInt(self.chapterId),
  1456. moduleId: parseInt(self.moduleId),
  1457. timeInterval: postTime ? self.photoList.join(",") : "",
  1458. orderGoodsId: this.orderGoodsId,
  1459. };
  1460. // console.log("提交接口", data);
  1461. this.$api
  1462. .coursePhotoRecord(data)
  1463. .then((res) => {
  1464. if (res.data.code == 200) {
  1465. resolve();
  1466. } else {
  1467. reject();
  1468. }
  1469. })
  1470. .catch((err) => {
  1471. reject();
  1472. });
  1473. });
  1474. },
  1475. randomNum(minNum, maxNum) {
  1476. switch (arguments.length) {
  1477. case 1:
  1478. return parseInt(Math.random() * minNum + 1, 10);
  1479. break;
  1480. case 2:
  1481. return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
  1482. break;
  1483. default:
  1484. return 0;
  1485. break;
  1486. }
  1487. },
  1488. //配置随机拍照时间
  1489. configPhoto() {
  1490. // var polyvPlayerContext = this.selectComponent("#playerVideo");
  1491. let totalVideoTime = 0;
  1492. let duration = 0;
  1493. // #ifdef MP-WEIXIN
  1494. totalVideoTime = polyvPlayerContext.getDuration();
  1495. duration = polyvPlayerContext.getCurrentTime();
  1496. // #endif
  1497. // #ifdef H5
  1498. totalVideoTime = polyvPlayerContext.j2s_getDuration();
  1499. duration = polyvPlayerContext.j2s_getCurrentTime();
  1500. // #endif
  1501. if (!this.photoConfig) {
  1502. this.photoConfig = true;
  1503. if (this.erJianErZao) {
  1504. this.photoList = this.randomConfig(totalVideoTime, duration);
  1505. return;
  1506. }
  1507. //没有历史拍照间隔数据
  1508. if (!this.photoList || this.photoList.length == 0) {
  1509. this.photoList = this.commonConfig(
  1510. totalVideoTime,
  1511. this.jjShiGongYuan ? 46 * 60 : undefined
  1512. );
  1513. this.postCoursePhotoRecord(true); //提交随机拍照时间数组
  1514. }
  1515. //兼容已有观看历史
  1516. for (let i = 0; i < this.photoList.length - 1; i++) {
  1517. if (
  1518. this.photoList[i] < duration &&
  1519. this.photoList[i + 1] > duration
  1520. ) {
  1521. this.photoIndex = i + 1;
  1522. break;
  1523. }
  1524. if (duration > this.photoList[this.photoList.length - 1]) {
  1525. this.photoIndex = this.photoList.length - 1; //取最后一个下标
  1526. break;
  1527. }
  1528. }
  1529. }
  1530. },
  1531. // 二建随机拍摄时间
  1532. randomConfig(totalVideoTime, duration) {
  1533. this.photoHistoryList = [];
  1534. let photoList = [duration];
  1535. let pre = duration;
  1536. if (totalVideoTime > 300) {
  1537. while (pre <= totalVideoTime) {
  1538. pre += this.randomNum(780, 900);
  1539. pre <= totalVideoTime && photoList.push(pre);
  1540. }
  1541. if (totalVideoTime - 300 > photoList.slice(-1)[0]) {
  1542. photoList.push(this.randomNum(totalVideoTime - 180, totalVideoTime));
  1543. }
  1544. }
  1545. return photoList;
  1546. },
  1547. // 随机前后五分钟
  1548. commonConfig(totalVideoTime, fixS) {
  1549. console.log("🚀 ~ file: detail.vue:2407 ~ commonConfig ~ fixS:", fixS);
  1550. let photoList = [0];
  1551. // 固定间隔时间取
  1552. if (fixS) {
  1553. let num = Math.ceil(totalVideoTime / fixS);
  1554. for (let i = 1; i < num; i++) {
  1555. photoList.push(i * fixS);
  1556. }
  1557. this.photoNum = num + 1;
  1558. } else {
  1559. if (this.photoNum == 3) {
  1560. if (totalVideoTime >= 900) {
  1561. //大于15分钟
  1562. let centerTime = Math.floor(totalVideoTime / 2); //获取中间时间
  1563. let centerMinTime = centerTime - 300; //前后5分钟
  1564. let centerMaxTime = centerTime + 300;
  1565. let centerTakeTime = this.randomNum(centerMinTime, centerMaxTime);
  1566. photoList.push(centerTakeTime); //中间拍一张
  1567. let endMaxTime = totalVideoTime - 60;
  1568. let endMinTime = totalVideoTime - 300;
  1569. let endTakeTime = this.randomNum(endMinTime, endMaxTime);
  1570. photoList.push(endTakeTime); //最后拍一张
  1571. } else {
  1572. //小于15分钟
  1573. let centerTime = this.randomNum(
  1574. (1 / 3) * totalVideoTime,
  1575. (2 / 3) * totalVideoTime
  1576. );
  1577. photoList.push(centerTime);
  1578. let endTakeTime = this.randomNum(
  1579. (2 / 3) * totalVideoTime,
  1580. totalVideoTime
  1581. );
  1582. photoList.push(endTakeTime);
  1583. }
  1584. }
  1585. }
  1586. return photoList;
  1587. },
  1588. getLiveUid(channelId) {
  1589. let self = this;
  1590. return new Promise((resolve) => {
  1591. let data = {
  1592. channelId: channelId,
  1593. orderGoodsId: this.orderGoodsId,
  1594. };
  1595. self.$api.polyvSign(data).then((res) => {
  1596. resolve(res.data.data);
  1597. });
  1598. });
  1599. },
  1600. studyNotice() {
  1601. this.noticeShow = true;
  1602. },
  1603. //正常播放视频
  1604. async playVideo(item) {
  1605. this.sectionItem = item;
  1606. let { learning, videoCurrentTime, sectionId, recordingUrl } = item;
  1607. console.log(this.sectionItem, "this.sectionItem");
  1608. if (this.timer) {
  1609. clearInterval(this.timer);
  1610. }
  1611. this.recordObj = videoCurrentTime
  1612. ? { videoCurrentTime }
  1613. : await this.getRecordLast(item);
  1614. await this.getPhotoLastRecord();
  1615. // 查找拍照历史
  1616. if ((this.photoNum > 0 || this.jjShiGongYuan) && learning != 1) {
  1617. await this.getPhotoLastRecord();
  1618. }
  1619. this.$store.commit("setPlayVID", {
  1620. playVID: recordingUrl,
  1621. });
  1622. this.$store.commit("setPlaySectionId", {
  1623. playSectionId: sectionId,
  1624. });
  1625. this.startStatus = true;
  1626. if (this.refPlv) {
  1627. this.refPlv.changeVid({
  1628. vid: recordingUrl,
  1629. videoCurrentTime: this.recordObj.videoCurrentTime,
  1630. });
  1631. }
  1632. },
  1633. getRecordLast(sectionItem) {
  1634. let { chapterId, sectionId, courseId, moduleId } = sectionItem;
  1635. return new Promise((resolve) => {
  1636. let data = {
  1637. gradeId:
  1638. this.gradeId || this.gradeId == 0 ? Number(this.gradeId) : null,
  1639. goodsId:
  1640. this.goodsId || this.goodsId == 0 ? Number(this.goodsId) : null,
  1641. sectionId: sectionId || 0,
  1642. courseId: courseId || courseId == 0 ? Number(courseId) : null,
  1643. chapterId: chapterId || 0,
  1644. moduleId: moduleId || 0,
  1645. orderGoodsId: this.orderGoodsId,
  1646. };
  1647. this.$api.recordLast(data).then((res) => {
  1648. resolve(res.data.data);
  1649. });
  1650. });
  1651. },
  1652. jumpNote(item) {
  1653. this.$u.toast("即将跳到笔记位置");
  1654. if (this.playSectionId != item.sectionId) {
  1655. this.initPlayVideo({
  1656. sectionType: 1,
  1657. ...item,
  1658. videoCurrentTime: Number(item.noteSecond),
  1659. });
  1660. } else {
  1661. this.refPlv.seekVideo(item.noteSecond);
  1662. }
  1663. },
  1664. getGradeInfo() {
  1665. // 即刻 1 待定2 日期3
  1666. return this.$api.goodsGradeInfo(this.gradeId).then((res) => {
  1667. if (res.data.code == 200) {
  1668. let { data } = res.data;
  1669. if (
  1670. data.learningStatus == 2 ||
  1671. (data.learningStatus == 3 &&
  1672. Number(data.learningTimeStart) > Number(new Date() / 1000))
  1673. ) {
  1674. uni.showModal({
  1675. showCancel: false,
  1676. confirmText: "确定",
  1677. content:
  1678. "当前课程正在申请中,正式开班后方可进行学习,请耐心等候!",
  1679. success: function (resultst) {
  1680. uni.navigateBack();
  1681. },
  1682. });
  1683. return Promise.reject(123);
  1684. }
  1685. }
  1686. });
  1687. },
  1688. postStudyRecord(status = 0, sectionId = this.playSectionId) {
  1689. if (!this.refPlv) {
  1690. return;
  1691. }
  1692. let currentTime = this.refPlv.playCurrentTime();
  1693. let PlayDuration = this.refPlv.playVideoTime();
  1694. if (currentTime < 10 && !this.ossAvatarUrl) {
  1695. return;
  1696. }
  1697. if (this.playChannelId > 0) {
  1698. currentTime = 2; //直播无法获取,无论开始结束都传2秒
  1699. }
  1700. let self = this;
  1701. let data = {
  1702. fromPlat: 1, //来源平台 1小程序 2网站
  1703. photo: self.ossAvatarUrl,
  1704. sectionId: sectionId || 0,
  1705. goodsId: parseInt(self.goodsId),
  1706. courseId: parseInt(self.courseId),
  1707. orderGoodsId: this.orderGoodsId,
  1708. studyDuration: parseInt(
  1709. PlayDuration > 0 ? PlayDuration : self.studyDuration
  1710. ),
  1711. gradeId: parseInt(self.gradeId),
  1712. chapterId: this.chapterId || 0,
  1713. moduleId: this.moduleId || 0,
  1714. videoCurrentTime: parseInt(
  1715. currentTime > 0 ? currentTime : self.studyDuration
  1716. ),
  1717. erJianErZao: this.erJianErZao,
  1718. };
  1719. if (this.ossAvatarUrl) {
  1720. data.similarity = this.compareFaceData; // 相似度
  1721. }
  1722. if (status > 0) {
  1723. data.status = status;
  1724. }
  1725. console.log(data, "记录参数");
  1726. return new Promise((resolve, reject) => {
  1727. this.$api
  1728. .studyRecord(data)
  1729. .then((res) => {
  1730. console.log(res, "记录返回");
  1731. let { code, msg } = res.data;
  1732. if (code == 200) {
  1733. if (status > 0) {
  1734. this.studyRecordMenuAllList();
  1735. let moduleId = this.moduleId || 0;
  1736. let chapterId = this.chapterId || 0;
  1737. let playNextIdisRebuild = `moduleId${moduleId}chapterId${chapterId}sectionId${sectionId}isRebuild`;
  1738. let playNextId = `moduleId${moduleId}chapterId${chapterId}sectionId${sectionId}`; //拼接对应章节唯一id
  1739. uni.$emit("playNext" + playNextIdisRebuild, {
  1740. fromRebuild: this.isRebuild,
  1741. }); //通知播放结束,不来自重修目录的点击不用弹窗学习下一节
  1742. uni.$emit("playNext" + playNextId); //通知播放结束
  1743. }
  1744. self.ossAvatarUrl = "";
  1745. } else if (code == 600) {
  1746. uni.showModal({
  1747. showCancel: false,
  1748. title: "提示",
  1749. content: msg,
  1750. success: (resultst) => {
  1751. uni.navigateBack();
  1752. },
  1753. });
  1754. } else if (code == 558) {
  1755. this.CountTo1 = msg.split(",")[1];
  1756. this.noticeShow1 = true;
  1757. var timer = setInterval(() => {
  1758. this.CountTo1--;
  1759. if (this.CountTo1 < 0) {
  1760. this.noticeShow1 = false;
  1761. clearInterval(timer);
  1762. this.postStudyRecord(1);
  1763. }
  1764. }, 1000);
  1765. reject("中断执行");
  1766. } else {
  1767. this.uploadLock = false;
  1768. uni.showToast({
  1769. icon: "none",
  1770. title: res.data.msg,
  1771. duration: 2000,
  1772. });
  1773. if (this.erJianErZao && code == 559) {
  1774. this.isReach = true;
  1775. this.openPhoto();
  1776. }
  1777. if (code == 559 || code == 588) {
  1778. reject("中断执行");
  1779. }
  1780. }
  1781. resolve();
  1782. })
  1783. .catch((err) => {
  1784. this.studyRecordMenuAllList();
  1785. });
  1786. });
  1787. },
  1788. uploadFile(options, int) {
  1789. var self = this;
  1790. return new Promise((resolve, reject) => {
  1791. var data = {
  1792. imageStatus: int,
  1793. gradeId: this.gradeId,
  1794. orderGoodsId: this.orderGoodsId,
  1795. };
  1796. self.$api.aliyunpolicy(data).then((res) => {
  1797. if (res.data.code != 200) {
  1798. self.$method.showToast("签名错误" + JSON.stringify(res.data));
  1799. return;
  1800. }
  1801. var ossToken = res.data.data.resultContent;
  1802. if (ossToken.host == null || ossToken.host == undefined) {
  1803. self.$method.showToast("上传路径报错" + JSON.stringify(res.data));
  1804. return;
  1805. }
  1806. let filePath = "";
  1807. // #ifdef H5
  1808. var localData = options; //dataUrl为base64位
  1809. let base = atob(localData.substring(localData.indexOf(",") + 1)); // base是将base64编码解码,去掉data:image/png;base64部分
  1810. let length = base.length;
  1811. let url = new Uint8Array(length);
  1812. while (length--) {
  1813. url[length] = base.charCodeAt(length);
  1814. }
  1815. filePath = new File([url], "a.jpg", {
  1816. type: "image/jpg",
  1817. });
  1818. uni.uploadFile({
  1819. url: ossToken.host,
  1820. name: "file",
  1821. file: filePath,
  1822. fileType: "image",
  1823. header: {
  1824. AuthorizationToken: "WX " + uni.getStorageSync("token"),
  1825. },
  1826. formData: {
  1827. key: ossToken.dir,
  1828. OSSAccessKeyId: ossToken.accessid,
  1829. policy: ossToken.policy,
  1830. Signature: ossToken.signature,
  1831. callback: ossToken.callback,
  1832. success_action_status: 200,
  1833. },
  1834. success: (result) => {
  1835. this.$u.toast("上传成功");
  1836. self.ossAvatarUrl = ossToken.dir;
  1837. resolve(ossToken.dir);
  1838. },
  1839. fail: (error) => {
  1840. uni.showToast({
  1841. title: "上传接口报错,请重新拍照上传" + error,
  1842. icon: "none",
  1843. });
  1844. this.openPhoto();
  1845. return;
  1846. },
  1847. });
  1848. // #endif
  1849. // #ifdef MP-WEIXIN
  1850. uni.uploadFile({
  1851. url: ossToken.host,
  1852. name: "file",
  1853. filePath: options,
  1854. fileType: "image",
  1855. header: {
  1856. AuthorizationToken: "WX " + uni.getStorageSync("token"),
  1857. },
  1858. formData: {
  1859. key: ossToken.dir,
  1860. OSSAccessKeyId: ossToken.accessid,
  1861. policy: ossToken.policy,
  1862. Signature: ossToken.signature,
  1863. callback: ossToken.callback,
  1864. success_action_status: 200,
  1865. },
  1866. success: (result) => {
  1867. // if (result.statusCode === 200) {
  1868. this.$u.toast("上传成功");
  1869. self.ossAvatarUrl = ossToken.dir;
  1870. resolve(ossToken.dir);
  1871. },
  1872. fail: (error) => {
  1873. uni.showToast({
  1874. title: "上传接口报错,请重新拍照上传" + error,
  1875. icon: "none",
  1876. });
  1877. this.openPhoto();
  1878. return;
  1879. },
  1880. });
  1881. // #endif
  1882. });
  1883. });
  1884. },
  1885. imageInfos() {
  1886. var self = this;
  1887. return new Promise(async (resolve, reject) => {
  1888. let resPath = await myCompressImage(this.avatarUrl || this.faceUrl, 50);
  1889. const waitUpload = await self.uploadFile(resPath, 0);
  1890. resolve(waitUpload);
  1891. });
  1892. },
  1893. timeEvent(playTime) {
  1894. this.configPhoto();
  1895. let photoTime = 0; //获取拍照秒数
  1896. for (let i = 0; i < this.photoList.length; i++) {
  1897. photoTime = Number(this.photoList[i]); //获取拍照秒数
  1898. if (
  1899. (this.erJianErZao && !this.photoHistoryList.length) ||
  1900. (photoTime < playTime && photoTime > playTime - 8)
  1901. ) {
  1902. //3秒区间内才触发拍照,避免拉动滚动条
  1903. if (this.photoHistoryList.indexOf(i) < 0) {
  1904. //不存在拍照历史,没有重修过,没有学过,则拍照
  1905. //启动拍照
  1906. //暂停
  1907. console.log("去拍照");
  1908. this.refPlv.playPause();
  1909. this.refPlv.exitFullScreen();
  1910. this.photoIndex = i;
  1911. }
  1912. }
  1913. }
  1914. },
  1915. closeToast() {
  1916. clearTimeout(this.toastTimer);
  1917. this.videoToastShow = false;
  1918. },
  1919. // 新增用户视频学习日志
  1920. studyLog() {
  1921. this.$http({
  1922. url: "/user/study/log",
  1923. method: "post",
  1924. data: {
  1925. goodsId: this.goodsId,
  1926. courseId: this.courseId,
  1927. moduleId: this.moduleId || 0,
  1928. chapterId: this.chapterId || 0,
  1929. sectionId: this.playSectionId || 0,
  1930. fromPlat: 1, //来源平台 1小程序 2PC网站
  1931. goodsType: 1, // 商品类型 1视频2题库 3补考 4前培 5虚拟赠送题库 6直播
  1932. orderGoodsId: this.orderGoodsId,
  1933. },
  1934. }).then((res) => {});
  1935. },
  1936. timeupdate(time) {
  1937. // console.log("播放中", time);
  1938. this.clearPauseTimer();
  1939. if (this.playSecIsLearn && (this.erJianErZao || this.photoNum > 0)) {
  1940. this.isReach = false;
  1941. this.timeEvent();
  1942. }
  1943. },
  1944. playing() {
  1945. console.log("playing");
  1946. if (this.noticeShow) {
  1947. this.refPlv.playPause();
  1948. return;
  1949. }
  1950. this.$refs["camera"].openCamera();
  1951. this.studyLog();
  1952. if (!this.recordObj.videoCurrentTime) {
  1953. this.postStudyRecord(0);
  1954. }
  1955. this.studyTimer && clearInterval(this.studyTimer);
  1956. this.studyTimer = setInterval(() => {
  1957. this.postStudyRecord(0);
  1958. }, 15000);
  1959. },
  1960. openCamera() {
  1961. this.$refs["camera"].openCamera();
  1962. },
  1963. pause() {
  1964. this.erJianErZaoPauseTip();
  1965. clearInterval(this.timer);
  1966. clearInterval(this.studyTimer);
  1967. },
  1968. async ended() {
  1969. this.hasStart = false;
  1970. uni.showToast({
  1971. icon: "none",
  1972. title: "播放完毕",
  1973. });
  1974. clearInterval(this.timer);
  1975. clearInterval(this.studyTimer);
  1976. await this.postStudyRecord(1);
  1977. this.nextSection();
  1978. },
  1979. playerError() {
  1980. console.log(46546);
  1981. },
  1982. //播放下一节
  1983. nextSection() {
  1984. console.log("播放下一节");
  1985. if (!this.menuAllList.length) {
  1986. return;
  1987. }
  1988. this.curPlayIndex = this.menuAllList.findIndex((item) => {
  1989. let i_sectionId = item.sectionId || 0;
  1990. let i_chapterId = item.chapterId || 0;
  1991. let i_moduleId = item.moduleId || 0;
  1992. return (
  1993. i_sectionId == this.playSectionId &&
  1994. i_chapterId == this.chapterId &&
  1995. i_moduleId == this.moduleId
  1996. );
  1997. });
  1998. let data = this.menuAllList[this.curPlayIndex + 1];
  1999. if (!data) {
  2000. //第二个弹窗
  2001. uni.showModal({
  2002. title: "温馨提示",
  2003. content:
  2004. "恭喜您课程学习全部完成,教务会在1-3个工作日内完成学习初审,请耐心等待。",
  2005. showCancel: !this.erJianErZao,
  2006. success: (res) => {
  2007. if (res.confirm) {
  2008. uni.switchTab({
  2009. url: "/pages/learn/index",
  2010. });
  2011. }
  2012. },
  2013. });
  2014. } else {
  2015. if (data.doType == 2) {
  2016. if (data.studyStatus == 1) {
  2017. uni.showToast({
  2018. title: "试卷已合格!",
  2019. duration: 2000,
  2020. icon: "none",
  2021. });
  2022. return;
  2023. }
  2024. uni.showModal({
  2025. title: "温馨提示",
  2026. content: "当前节视频已学完,是否进入考试?",
  2027. success: (res) => {
  2028. if (res.confirm) {
  2029. this.toQuestionBank(data);
  2030. }
  2031. },
  2032. });
  2033. return;
  2034. }
  2035. uni.showModal({
  2036. title: "温馨提示",
  2037. content: "当前节视频已学完,继续学习下一节?",
  2038. success: async (res) => {
  2039. if (res.confirm) {
  2040. this.moduleId = data.moduleId;
  2041. this.chapterId = data.chapterId;
  2042. this.sectionId = data.sectionId;
  2043. if (data.sectionType == 1) {
  2044. //录播
  2045. this.$store.commit("setPlaySectionId", {
  2046. playSectionId: data.sectionId,
  2047. });
  2048. this.$store.commit("setPlayVID", {
  2049. playVID: data.recordingUrl,
  2050. });
  2051. this.hasStart = false;
  2052. this.photoConfig = false;
  2053. this.photoIndex = 0;
  2054. this.sectionItem = data;
  2055. this.playVideo(data);
  2056. } else if (data.sectionType == 2) {
  2057. //直播
  2058. this.studyRecordGetLastLive();
  2059. } else if (data.sectionType == 3) {
  2060. //回放
  2061. this.$store.commit("setPlaySectionId", {
  2062. playSectionId: data.sectionId,
  2063. });
  2064. this.$store.commit("setPlayVID", {
  2065. playVID: data.recordingUrl,
  2066. });
  2067. this.sectionItem = data;
  2068. this.playVideo(data);
  2069. }
  2070. let playNextId = `moduleId${data.moduleId}chapterId${data.chapterId}sectionId${data.sectionId}`;
  2071. this.$store.commit("updatePlayNextId", playNextId);
  2072. this.updateChapterOpen(true);
  2073. }
  2074. },
  2075. });
  2076. }
  2077. },
  2078. //拍照
  2079. openPhoto() {
  2080. if (polyvPlayerContext) {
  2081. // #ifdef MP-WEIXIN
  2082. polyvPlayerContext.exitFullScreen();
  2083. // #endif
  2084. // #ifdef H5
  2085. if (this.isFullScreen()) {
  2086. this.exitFullscreen();
  2087. }
  2088. // #endif
  2089. }
  2090. // #ifdef MP-WEIXIN
  2091. this.enableAutoRotation = false;
  2092. this.photoPopup = true;
  2093. this.isTaking = true;
  2094. uni.setKeepScreenOn({
  2095. keepScreenOn: true,
  2096. });
  2097. uni.authorize({
  2098. scope: "scope.camera",
  2099. success() {},
  2100. });
  2101. // #endif
  2102. // #ifdef H5
  2103. if (
  2104. (window.navigator.mediaDevices &&
  2105. window.navigator.mediaDevices.getUserMedia) ||
  2106. window.navigator.getUserMedia ||
  2107. window.navigator.webkitGetUserMedia ||
  2108. window.navigator.mozGetUserMedia
  2109. ) {
  2110. console.log("getUserMedia----");
  2111. // 调用用户媒体设备, 访问摄像头
  2112. this.getUserMedia(
  2113. {
  2114. video: {
  2115. width: 400,
  2116. height: 300,
  2117. facingMode: "user",
  2118. },
  2119. },
  2120. this.photographSuccess,
  2121. this.photographError
  2122. );
  2123. } else {
  2124. console.log("1111没有摄像");
  2125. this.photographError();
  2126. }
  2127. // #endif
  2128. },
  2129. /**
  2130. * 人脸匹配
  2131. */
  2132. faceRecognition() {
  2133. return new Promise((resolve) => {
  2134. // #ifdef MP-WEIXIN
  2135. let fileSystem = uni.getFileSystemManager();
  2136. fileSystem.readFile({
  2137. filePath: `${this.avatarUrl}`,
  2138. encoding: "base64",
  2139. position: 0,
  2140. success: (res) => {
  2141. let base64 = "data:image/jpg;base64," + res.data;
  2142. // console.log('base64Data人脸识别参数:', {
  2143. // imageA: base64,
  2144. // orderGoodsId: this.orderGoodsId,
  2145. // gradeId: this.gradeId,
  2146. // })
  2147. this.CompareFace(base64, resolve);
  2148. },
  2149. fail(err) {
  2150. // this.$u.toast('人脸识别错误!')
  2151. console.error(err, "err-----人脸识别错误");
  2152. },
  2153. });
  2154. // #endif
  2155. // #ifdef H5
  2156. this.CompareFace(this.faceUrl, resolve);
  2157. // #endif
  2158. });
  2159. },
  2160. CompareFace(url, resolve) {
  2161. let timer = setTimeout(() => {
  2162. uni.showToast({
  2163. icon: "none",
  2164. title: "拍照超时,请重新拍照",
  2165. duration: 2000,
  2166. success: () => {
  2167. setTimeout(() => {
  2168. uni.navigateBack();
  2169. }, 1000);
  2170. },
  2171. });
  2172. }, 10 * 1000);
  2173. this.$api
  2174. .faceCertificationCompareFace({
  2175. imageA: url,
  2176. orderGoodsId: this.orderGoodsId,
  2177. gradeId: this.gradeId,
  2178. })
  2179. .then((res) => {
  2180. clearTimeout(timer);
  2181. console.log(res, "人脸识别成功res");
  2182. resolve(res.data.data);
  2183. })
  2184. .catch((err) => {
  2185. clearTimeout(timer);
  2186. // 当前网络延迟,
  2187. console.log("人脸识别错误:", err);
  2188. uni.showModal({
  2189. content: "当前网络延迟",
  2190. showCancel: false,
  2191. success: (resultst) => {
  2192. if (resultst.confirm) {
  2193. uni.navigateBack();
  2194. }
  2195. },
  2196. });
  2197. });
  2198. },
  2199. //确认拍照
  2200. takePhoto() {
  2201. // #ifdef MP-WEIXIN
  2202. const ctx = uni.createCameraContext();
  2203. ctx.takePhoto({
  2204. quality: "high",
  2205. success: (res) => {
  2206. this.avatarUrl = res.tempImagePath;
  2207. console.log("开始拍照this.avatarUrl:", this.avatarUrl);
  2208. this.isTaking = false;
  2209. },
  2210. fail: (err) => {},
  2211. });
  2212. // #endif
  2213. // #ifdef H5
  2214. const canvas = document.createElement("canvas");
  2215. canvas.width = 400;
  2216. canvas.height = 400;
  2217. const context = canvas.getContext("2d");
  2218. const box = document.querySelector(".photo_v");
  2219. const video = box.querySelector("video");
  2220. context.drawImage(video, 0, 0, 400, 400);
  2221. this.faceUrl = canvas.toDataURL("image/png");
  2222. this.isTaking = false;
  2223. // #endif
  2224. },
  2225. playError(e) {
  2226. console.log(e);
  2227. },
  2228. //拍照报错
  2229. error(e) {
  2230. console.log(e.detail);
  2231. },
  2232. //关闭相机
  2233. closePhoto() {
  2234. this.photoPopup = false;
  2235. this.enableAutoRotation = true;
  2236. },
  2237. async userConfirmInfoDetail() {
  2238. let info = await this.$api.userConfirmInfoDetail({
  2239. orderGoodsId: this.orderGoodsId,
  2240. });
  2241. if (!info.data.data || info.data.data.pushInfo !== 1) {
  2242. uni.showModal({
  2243. showCancel: false,
  2244. title: "提示",
  2245. content: "开通信息推送不成功,无法进入学习!",
  2246. success: (resultst) => {
  2247. uni.navigateBack();
  2248. },
  2249. });
  2250. return Promise.reject();
  2251. }
  2252. },
  2253. checkFinishRequiredCourse() {
  2254. return this.$api
  2255. .checkFinishRequiredCourse({
  2256. businessId: this.goodsData.businessId,
  2257. goodsId: this.goodsId,
  2258. })
  2259. .then((res) => {
  2260. if (res.data.data > 0) {
  2261. uni.showModal({
  2262. showCancel: false,
  2263. confirmText: "确定",
  2264. content: "该业务层次下有未学完的商品,无法学习新商品!",
  2265. success: function (resultst) {
  2266. uni.navigateBack();
  2267. },
  2268. });
  2269. return Promise.reject();
  2270. }
  2271. return Promise.resolve();
  2272. });
  2273. },
  2274. async getGoodsDetail() {
  2275. let { data } = await this.$api.goodsDetail(this.goodsId);
  2276. console.log(
  2277. "🚀 ~ file: detail.vue:2342 ~ getGoodsDetail ~ data:",
  2278. data.data
  2279. );
  2280. this.goodsData = data.data;
  2281. this.erJianErZao = this.goodsData.erJianErZao;
  2282. this.erJianErZao && (await this.userConfirmInfoDetail());
  2283. this.option.periodWaitTime && (await this.checkFinishRequiredCourse());
  2284. this.goodsData.buyNote && this.baseHandoutTipList();
  2285. this.gradeId = this.goodsData.gradeId;
  2286. if (this.goodsData.goodsPlayConfig) {
  2287. this.goodsPlayConfig = JSON.parse(this.goodsData.goodsPlayConfig);
  2288. if (this.goodsPlayConfig.autoPlay > 0) {
  2289. this.autoplay = true;
  2290. }
  2291. if (this.goodsPlayConfig.drag > 0 && !this.erJianErZao) {
  2292. // #ifdef MP-WEIXIN
  2293. this.isAllowSeek = "yes";
  2294. // #endif
  2295. // #ifdef H5
  2296. this.isAllowSeek = "off";
  2297. // #endif
  2298. }
  2299. if (this.goodsPlayConfig.speed > 0) {
  2300. this.playbackRate = [0.5, 0.8, 1.0, 1.25, 1.5, 2.0];
  2301. }
  2302. }
  2303. if (this.goodsData.goodsPhotographConfig) {
  2304. this.goodsPhotographConfig = JSON.parse(
  2305. this.goodsData.goodsPhotographConfig
  2306. );
  2307. if (this.goodsPhotographConfig.photoNum > 0) {
  2308. this.photoNum = this.goodsPhotographConfig.photoNum;
  2309. }
  2310. }
  2311. },
  2312. getReMenuList() {
  2313. let self = this;
  2314. this.$api
  2315. .reMenuList({
  2316. orderGoodsId: this.orderGoodsId,
  2317. courseId: this.courseId,
  2318. rebuild: 1,
  2319. gradeId: this.gradeId,
  2320. })
  2321. .then((res) => {
  2322. if (res.data.code == 200) {
  2323. for (let i = 0; i < res.data.rows.length; i++) {
  2324. let item = res.data.rows[i];
  2325. item.down = true;
  2326. item.id = item.menuId;
  2327. item.name = item.menuName;
  2328. }
  2329. self.reMenuList = res.data.rows;
  2330. this.current = 0;
  2331. if (self.reMenuList.length > 0) {
  2332. this.showNotes = false;
  2333. if (Object.keys(this.sectionItem).length) {
  2334. let playNextIdisRebuild = `moduleId${this.sectionItem.moduleId}chapterId${this.sectionItem.chapterId}sectionId${this.sectionItem.sectionId}isRebuild`;
  2335. this.$store.commit("updatePlayNextId", playNextIdisRebuild);
  2336. }
  2337. } else {
  2338. if (Object.keys(this.sectionItem).length) {
  2339. let playNextId = `moduleId${this.sectionItem.moduleId}chapterId${this.sectionItem.chapterId}sectionId${this.sectionItem.sectionId}`;
  2340. this.$store.commit("updatePlayNextId", playNextId);
  2341. }
  2342. }
  2343. }
  2344. });
  2345. },
  2346. courseDetail() {
  2347. this.$api.courseDetail(this.courseId).then((res) => {
  2348. if (res.data.code == 200) {
  2349. this.detail = res.data.data;
  2350. }
  2351. });
  2352. },
  2353. open(item) {
  2354. item.showChildren = !item.showChildren;
  2355. },
  2356. change(index) {
  2357. this.current = index;
  2358. },
  2359. clears() {
  2360. return new Promise((resolve, reject) => {
  2361. this.vid = "";
  2362. polyvPlayerContext && polyvPlayerContext.destroy();
  2363. polyvPlayerContext = null;
  2364. resolve();
  2365. });
  2366. },
  2367. /**
  2368. * 退出全屏
  2369. */
  2370. exitFullscreen() {
  2371. try {
  2372. var de = document;
  2373. if (de.exitFullscreen) {
  2374. de.exitFullscreen();
  2375. } else if (de.mozCancelFullScreen) {
  2376. de.mozCancelFullScreen();
  2377. } else if (de.webkitCancelFullScreen) {
  2378. de.webkitCancelFullScreen();
  2379. }
  2380. } catch (err) {}
  2381. },
  2382. fullele() {
  2383. return (
  2384. document.fullscreenElement ||
  2385. document.webkitFullscreenElement ||
  2386. document.msFullscreenElement ||
  2387. document.mozFullScreenElement ||
  2388. null
  2389. );
  2390. },
  2391. //判断是否全屏
  2392. isFullScreen() {
  2393. return !!(document.webkitIsFullScreen || this.fullele());
  2394. },
  2395. getUserMedia(constraints, success, error) {
  2396. console.log("getUserMedia===", constraints, "success:", success);
  2397. if (window.navigator.mediaDevices.getUserMedia) {
  2398. // 最新的标准API
  2399. window.navigator.mediaDevices
  2400. .getUserMedia(constraints)
  2401. .then(success)
  2402. .catch(error);
  2403. } else if (window.navigator.webkitGetUserMedia) {
  2404. // webkit核心浏览器
  2405. window.navigator.webkitGetUserMedia(constraints, success, error);
  2406. } else if (window.navigator.mozGetUserMedia) {
  2407. // firfox浏览器
  2408. window.navigator.mozGetUserMedia(constraints, success, error);
  2409. } else if (window.navigator.getUserMedia) {
  2410. // 旧版API
  2411. window.navigator.getUserMedia(constraints, success, error);
  2412. }
  2413. },
  2414. photographSuccess(stream) {
  2415. console.log("有摄像头---", stream);
  2416. this.photoPopup = true;
  2417. this.isTaking = true;
  2418. this.enableAutoRotation = false;
  2419. this.$nextTick(() => {
  2420. const box = document.querySelector(".photo_v");
  2421. const video = box.querySelector("video");
  2422. video.srcObject = stream;
  2423. video.play();
  2424. });
  2425. },
  2426. photographError(err) {
  2427. console.log("没有摄像头:", err);
  2428. uni.showModal({
  2429. title: "提示",
  2430. content:
  2431. "课程学习需要开启摄像头进行拍照,经检测您的设备无摄像头可使用,请检测环境是否支持。",
  2432. cancelText: "取消",
  2433. confirmText: "确定",
  2434. success: (res) => {
  2435. if (res.confirm) {
  2436. uni.navigateBack();
  2437. } else if (res.cancel) {
  2438. }
  2439. },
  2440. });
  2441. },
  2442. },
  2443. };
  2444. </script>
  2445. <style lang="scss" scope>
  2446. @import "./css/detail.scss";
  2447. .top {
  2448. &__header {
  2449. position: relative;
  2450. width: 100%;
  2451. height: 150rpx;
  2452. padding: 24rpx 150rpx 24rpx 24rpx;
  2453. .img {
  2454. position: absolute;
  2455. left: 0;
  2456. top: 0;
  2457. width: 100%;
  2458. }
  2459. .note {
  2460. position: relative;
  2461. z-index: 10;
  2462. font-size: 24rpx;
  2463. font-family: PingFang SC;
  2464. font-weight: bold;
  2465. color: #efdbff;
  2466. }
  2467. .title {
  2468. position: relative;
  2469. z-index: 10;
  2470. font-size: 26rpx;
  2471. font-family: PingFang SC;
  2472. font-weight: bold;
  2473. color: #ffffff;
  2474. }
  2475. }
  2476. }
  2477. #top {
  2478. position: relative;
  2479. z-index: 99;
  2480. }
  2481. .polyv_detail {
  2482. display: flex;
  2483. flex-direction: column;
  2484. height: 100vh;
  2485. position: relative;
  2486. top: 0;
  2487. left: 0;
  2488. .pops {
  2489. position: absolute;
  2490. top: 0;
  2491. left: 0;
  2492. background: #ccc;
  2493. opacity: 0.5;
  2494. width: 100%;
  2495. height: 300rpx;
  2496. z-index: 9999;
  2497. }
  2498. .box {
  2499. flex: 1;
  2500. overflow: hidden;
  2501. margin: 16rpx 16rpx 100rpx 16rpx;
  2502. .box_in {
  2503. height: 100%;
  2504. }
  2505. }
  2506. .first_ml {
  2507. margin: 16rpx 16rpx 16rpx 16rpx;
  2508. }
  2509. }
  2510. .btnSet {
  2511. width: 440rpx;
  2512. height: 80rpx;
  2513. background: #007aff;
  2514. border-radius: 40rpx;
  2515. color: #ffffff;
  2516. font-size: 28rpx;
  2517. line-height: 80rpx;
  2518. }
  2519. .lecture-content {
  2520. background: #fff;
  2521. margin-top: 10rpx;
  2522. padding: 10rpx;
  2523. border-radius: 16rpx;
  2524. }
  2525. .tBox {
  2526. display: flex;
  2527. align-items: center;
  2528. padding-top: 10rpx;
  2529. }
  2530. .title {
  2531. font-size: 24rpx;
  2532. color: #999999;
  2533. }
  2534. .t_content1 {
  2535. color: #007aff;
  2536. margin-left: 10rpx;
  2537. }
  2538. .tag1 {
  2539. border: 2rpx solid #007aff;
  2540. border-radius: 8rpx;
  2541. font-size: 20rpx;
  2542. color: #007aff;
  2543. padding: 5rpx;
  2544. }
  2545. .b_title {
  2546. color: #333333;
  2547. font-size: 30rpx;
  2548. font-weight: bold;
  2549. }
  2550. page {
  2551. background: #eaeef1;
  2552. }
  2553. .menuBox {
  2554. width: 100%;
  2555. background: #ffffff;
  2556. border-radius: 16rpx;
  2557. padding: 0rpx 20rpx;
  2558. margin-bottom: 20rpx;
  2559. }
  2560. .btnspric {
  2561. border-top: 1rpx solid #eee;
  2562. display: flex;
  2563. align-items: center;
  2564. justify-content: space-between;
  2565. height: 108rpx;
  2566. padding-left: 43rpx;
  2567. padding-right: 32rpx;
  2568. }
  2569. .btnspric > .lefprL {
  2570. font-size: 36rpx;
  2571. color: #0c141f;
  2572. font-weight: bold;
  2573. }
  2574. .btnspric > .lefprR {
  2575. padding: 0rpx 24rpx;
  2576. height: 60rpx;
  2577. line-height: 60rpx;
  2578. text-align: center;
  2579. color: #fff;
  2580. background: #32467b;
  2581. border-radius: 24rpx;
  2582. box-shadow: 0rpx 0rpx 16rpx 4rpx rgba(145, 156, 178, 0.1);
  2583. }
  2584. .yhj,
  2585. .hdyhj {
  2586. padding: 24rpx 29rpx 24rpx 34rpx;
  2587. }
  2588. .yhj {
  2589. border-bottom: 16rpx solid #f9f9f9;
  2590. }
  2591. .yhjtit {
  2592. font-size: 30rpx;
  2593. color: #0c141f;
  2594. font-weight: 500;
  2595. margin-bottom: 14rpx;
  2596. }
  2597. .yhjList {
  2598. display: flex;
  2599. align-items: center;
  2600. justify-content: space-between;
  2601. margin-bottom: 14rpx;
  2602. }
  2603. .yhjList > .yhjLefts {
  2604. display: flex;
  2605. align-items: center;
  2606. }
  2607. .yhjLefts > .yhl {
  2608. color: #32467b;
  2609. font-size: 30rpx;
  2610. margin-right: 31rpx;
  2611. }
  2612. .yhjLefts > .yhbq {
  2613. font-size: 24rpx;
  2614. color: #ff9500;
  2615. border-radius: 18rpx;
  2616. background-color: rgba(255, 149, 0, 0.2);
  2617. border: 2rpx solid #ff9500;
  2618. height: 38rpx;
  2619. line-height: 38rpx;
  2620. padding: 0rpx 16rpx;
  2621. }
  2622. .ts {
  2623. font-size: 24rpx;
  2624. color: #999;
  2625. margin: 14rpx 0rpx;
  2626. padding-right: 29rpx;
  2627. padding-left: 34rpx;
  2628. }
  2629. .yh {
  2630. padding-top: 20rpx;
  2631. }
  2632. .yh > .yhtitle {
  2633. display: flex;
  2634. align-items: center;
  2635. justify-content: space-between;
  2636. padding-right: 29rpx;
  2637. padding-left: 34rpx;
  2638. }
  2639. .priceBxs {
  2640. display: flex;
  2641. align-items: center;
  2642. }
  2643. .priceBxs > .pricleft {
  2644. border-radius: 24rpx;
  2645. border: 1rpx solid #e91313;
  2646. background-color: rgba(233, 19, 19, 0.1);
  2647. padding: 0rpx 18rpx;
  2648. height: 49rpx;
  2649. line-height: 49rpx;
  2650. text-align: center;
  2651. font-size: 30rpx;
  2652. font-weight: 500;
  2653. color: #e91313;
  2654. margin-right: 13rpx;
  2655. }
  2656. .topBox {
  2657. padding: 32rpx 32rpx 24rpx;
  2658. border-bottom: 1rpx solid #eeeeee;
  2659. }
  2660. .topBox > .boldFonstType {
  2661. font-weight: 500;
  2662. font-size: 30rpx;
  2663. margin: 16rpx 0rpx 23rpx;
  2664. }
  2665. .topBox > .firstTopL {
  2666. display: flex;
  2667. align-items: center;
  2668. }
  2669. .topBox > .firstTopL > .imageBs {
  2670. width: 331rpx;
  2671. height: 160rpx;
  2672. border-radius: 6rpx;
  2673. overflow: hidden;
  2674. margin-right: 8rpx;
  2675. box-shadow: 0rpx 6rpx 6rpx 0rpx rgba(47, 67, 121, 0.08);
  2676. }
  2677. .topBox > .firstTopL > .imageBs > image {
  2678. width: 100%;
  2679. height: 100%;
  2680. }
  2681. .topBox > .firstTopL > .textBs {
  2682. font-size: 30rpx;
  2683. font-weight: bold;
  2684. color: #0c141f;
  2685. }
  2686. .content {
  2687. padding: 24rpx;
  2688. text-align: left;
  2689. }
  2690. .catalogBox {
  2691. display: flex;
  2692. align-items: center;
  2693. flex-wrap: nowrap;
  2694. overflow-x: auto;
  2695. padding-left: 38rpx;
  2696. max-height: 305rpx;
  2697. overflow-y: auto;
  2698. transition: all 0.4s;
  2699. }
  2700. .catalogBox > .catalogA {
  2701. min-width: 200rpx;
  2702. height: 48rpx;
  2703. line-height: 48rpx;
  2704. // text-align: center;
  2705. border: 2rpx solid transparent;
  2706. white-space: nowrap;
  2707. text-overflow: ellipsis;
  2708. overflow: hidden;
  2709. word-break: break-all;
  2710. border-radius: 10rpx;
  2711. background: rgba(22, 119, 255, 0.05);
  2712. padding-left: 19rpx;
  2713. box-sizing: border-box;
  2714. padding-right: 15rpx;
  2715. margin-right: 16rpx;
  2716. margin-bottom: 20rpx;
  2717. margin-top: 15rpx;
  2718. font-size: 24rpx;
  2719. color: #666;
  2720. }
  2721. .catalogBox > .activesq {
  2722. border-color: #1677ff;
  2723. }
  2724. .changeCatalogBox {
  2725. display: block;
  2726. }
  2727. .catalogBox::-webkit-scrollbar {
  2728. display: none; /* Chrome Safari */
  2729. }
  2730. .price_t2 {
  2731. font-size: 18rpx;
  2732. font-family: PingFang SC;
  2733. font-weight: 500;
  2734. text-decoration: line-through;
  2735. color: #999999;
  2736. }
  2737. .price_t1 {
  2738. font-size: 33rpx;
  2739. font-family: PingFang SC;
  2740. font-weight: bold;
  2741. color: #e91313;
  2742. }
  2743. .sc_t {
  2744. font-size: 22rpx;
  2745. color: #000000;
  2746. }
  2747. .sc {
  2748. width: 29rpx;
  2749. height: 29rpx;
  2750. }
  2751. .buy {
  2752. width: 138rpx;
  2753. height: 48rpx;
  2754. line-height: 48rpx;
  2755. background: #32467b;
  2756. border-radius: 10rpx;
  2757. color: #ffffff;
  2758. font-size: 28rpx;
  2759. text-align: center;
  2760. vertical-align: middle;
  2761. position: absolute;
  2762. right: 30rpx;
  2763. }
  2764. .video_body {
  2765. padding-bottom: 96rpx;
  2766. }
  2767. .footer_tab {
  2768. position: fixed;
  2769. bottom: 0;
  2770. height: 96rpx;
  2771. width: 100%;
  2772. background-color: #ffffff;
  2773. }
  2774. .tj_box {
  2775. width: 50%;
  2776. display: inline-block;
  2777. text-align: center;
  2778. margin: 10rpx 0;
  2779. }
  2780. .teacher_t {
  2781. font-size: 24rpx;
  2782. font-family: PingFang SC;
  2783. font-weight: 400;
  2784. color: #666666;
  2785. line-height: 36rpx;
  2786. margin-left: 15rpx;
  2787. }
  2788. .teacher_img {
  2789. width: 87rpx;
  2790. height: 129rpx;
  2791. }
  2792. .t2 {
  2793. font-size: 24rpx;
  2794. font-family: PingFang SC;
  2795. color: #666666;
  2796. line-height: 36rpx;
  2797. margin: 15rpx;
  2798. }
  2799. .r_t2 {
  2800. width: 201rpx;
  2801. height: 49rpx;
  2802. background: rgba(22, 119, 255, 0.05);
  2803. border: 1rpx solid #32467b;
  2804. border-radius: 16rpx;
  2805. color: #666666;
  2806. font-size: 23rpx;
  2807. text-align: center;
  2808. display: flex;
  2809. align-items: center;
  2810. padding: 5rpx;
  2811. }
  2812. .scroll_box {
  2813. width: 100%;
  2814. height: 60rpx;
  2815. background: #ffffff;
  2816. box-shadow: 0rpx 0rpx 16rpx 4rpx rgba(145, 156, 178, 0.1);
  2817. white-space: nowrap;
  2818. overflow: hidden;
  2819. margin: 15rpx 0;
  2820. }
  2821. .r_sliper {
  2822. padding: 0 20rpx;
  2823. }
  2824. .top_line {
  2825. width: 6rpx;
  2826. height: 22rpx;
  2827. background: #32467b;
  2828. margin-right: 10rpx;
  2829. }
  2830. .video_t2 {
  2831. font-size: 24rpx;
  2832. font-family: PingFang SC;
  2833. font-weight: 500;
  2834. color: #666666;
  2835. }
  2836. .video_play {
  2837. position: absolute;
  2838. width: 95rpx;
  2839. height: 95rpx;
  2840. top: 0;
  2841. left: 0;
  2842. right: 0;
  2843. bottom: 0;
  2844. margin: auto;
  2845. }
  2846. .video_box {
  2847. position: relative;
  2848. }
  2849. .rotoct {
  2850. transform: rotate(90deg);
  2851. }
  2852. .slot-content {
  2853. padding: 0 20rpx;
  2854. }
  2855. .notice_modal {
  2856. .content {
  2857. width: 100%;
  2858. height: 100%;
  2859. padding: 56rpx 56rpx 56rpx 64rpx;
  2860. .title {
  2861. color: #222;
  2862. line-height: 40rpx;
  2863. font-size: 36rpx;
  2864. text-align: center;
  2865. font-weight: bold;
  2866. margin-bottom: 24rpx;
  2867. }
  2868. .text {
  2869. height: 340rpx;
  2870. line-height: 40rpx;
  2871. text-indent: 2em;
  2872. font-size: 32rpx;
  2873. color: #222;
  2874. }
  2875. .had_read {
  2876. width: 100%;
  2877. height: 88rpx;
  2878. line-height: 88rpx;
  2879. text-align: center;
  2880. background: #3577e8;
  2881. border-radius: 240rpx;
  2882. font-size: 32rpx;
  2883. font-weight: 500;
  2884. color: #fff;
  2885. margin-top: 20rpx;
  2886. &.gray {
  2887. background: #bbbec5;
  2888. }
  2889. }
  2890. }
  2891. }
  2892. .full_mulu {
  2893. position: absolute;
  2894. top: 100rpx;
  2895. right: 20rpx;
  2896. width: 700rpx;
  2897. height: 515rpx;
  2898. color: #333;
  2899. display: flex;
  2900. align-items: center;
  2901. justify-content: space-between;
  2902. z-index: 99999;
  2903. .mulus {
  2904. width: 623rpx;
  2905. // height: 515rpx;
  2906. height: 400rpx;
  2907. overflow-y: scroll;
  2908. // overflow: scroll;
  2909. }
  2910. .mulu_box_in {
  2911. background-color: #b7b7b7;
  2912. border-radius: 16rpx;
  2913. // transition: all 0.5s;
  2914. &::after {
  2915. content: "";
  2916. width: 0;
  2917. height: 0;
  2918. position: absolute;
  2919. top: 235rpx;
  2920. right: 27px;
  2921. border-top: 16rpx solid transparent;
  2922. border-right: 16rpx solid transparent;
  2923. border-left: 16rpx solid #b7b7b7;
  2924. border-bottom: 16rpx solid transparent;
  2925. }
  2926. }
  2927. .mulu_box_out {
  2928. // visibility: hidden;
  2929. display: none;
  2930. }
  2931. .menuBox_mulu {
  2932. // width: 100%;
  2933. background: #ffffff;
  2934. border-radius: 16rpx;
  2935. padding: 0rpx 20rpx;
  2936. margin-bottom: 20rpx;
  2937. }
  2938. .ml_img {
  2939. position: absolute;
  2940. right: 0;
  2941. top: 100rpx;
  2942. }
  2943. .items {
  2944. width: 620rpx;
  2945. height: 100rpx;
  2946. border: 1rpx solid red;
  2947. }
  2948. }
  2949. </style>