videoDetail.vue 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439
  1. <template>
  2. <view class="content">
  3. <view class="header-nav" :style="{height: `calc(88rpx + ${statusBarHeight}px)`,paddingTop: statusBarHeight + 'px'}">
  4. <view class="arrow-left-warning" @click="feedback">
  5. <image src="/static/images/warning.png"></image>
  6. <text>投诉</text>
  7. </view>
  8. <view class="header-title" :style="{width:menuButtonLeft + 'px',height:menuButtonH+'px',lineHeight:menuButtonH+'px'}">{{courseInfo.title}}</view>
  9. </view>
  10. <view class="video-box">
  11. <image v-if="!isLogin || isAddKf!=1" class="video-poster" :src="courseInfo.imgUrl" mode="aspectFill">
  12. </image>
  13. <video
  14. @timeupdate="onTimeUpdate"
  15. @progress="progressChange"
  16. @error="videoErrorCallback"
  17. @play="getPlay"
  18. @pause="getPause"
  19. @ended="getEnded"
  20. @fullscreenchange="fullscreenchange"
  21. @controlstoggle="controlstoggle"
  22. @waiting="getWaiting"
  23. :title="courseInfo.title"
  24. style="width: 100%;height: 420rpx;"
  25. :poster="poster"
  26. id="video-content-box"
  27. controls
  28. :show-fullscreen-btn="true"
  29. :auto-pause-if-open-native="true"
  30. :auto-pause-if-navigate="true"
  31. :enable-progress-gesture="false"
  32. :show-progress="true"
  33. :picture-in-picture-mode="[]"
  34. :show-background-playback-button="false"
  35. :src="videoUrl"
  36. >
  37. <!-- :danmu-list="danmuList"
  38. enable-danmu
  39. danmu-btn -->
  40. <!-- 弹幕展示 -->
  41. <template v-if="showDanmu==1&&openCommentStatus==2">
  42. <text v-for="(item, index) in activeDanmus" :key="item.commentId" class="danmu-item danmuMove"
  43. :style="item.danmustyle" @animationend="animationend(item,index)">
  44. {{ item.content }}
  45. </text>
  46. </template>
  47. <cover-view class="video-danmu-btnbox" :style="{display: openCommentStatus==2&&isfull&&crtShow&&isLogin&&isAddKf==1 ? 'block':'none'}">
  48. <cover-image class="video-danmu-image"
  49. :src="baseUrl+'/images/danmu_set.png'"
  50. @click="openDanmu(1)"></cover-image>
  51. </cover-view>
  52. <treasureChest ref="treasureChest" :isfull="isfull" :onHide="onHide" :boxList="fsUserCourseRewardRoundStatusVO" @claimFun="claimFun"></treasureChest>
  53. </video>
  54. </view>
  55. <!-- 弹幕方法 -->
  56. <commentBox
  57. v-if="openCommentStatus==2"
  58. ref="danmuBox"
  59. :height="height"
  60. :urlOption="urlOption"
  61. :time="playTime"
  62. :showDanmu="showDanmu"
  63. :viewCommentNum="viewCommentNum"
  64. :openCommentStatus="openCommentStatus"
  65. :isSocketOpen="isSocketOpen"
  66. @socketSend="socketSend"
  67. @initSocket="initSocket"
  68. @setInputText="setInputText"
  69. @getScrollTop="getScrollTop"
  70. @getMore="getMore"
  71. @getActiveDanmus="getActiveDanmus"></commentBox>
  72. <view id="title-contentnav">
  73. <template v-show="isLogin&&notice">
  74. <u-notice-bar :text="notice" color="red" speed='60' fontSize="18"></u-notice-bar>
  75. </template>
  76. </view>
  77. <scroll-view
  78. class="scroll-view"
  79. :style="{height: height}"
  80. :scroll-top="scrollTop"
  81. :scroll-y="true"
  82. :refresher-enabled="currentTab == 2"
  83. :refresher-triggered="triggered"
  84. @refresherrefresh="handleRefresher">
  85. <view style="font-size: 30rpx;color: #999;padding: 10rpx 24rpx;">本页内容受知识产权保护,请勿截屏录屏及转发,感谢理解。</view>
  86. <view class="title-content" v-if="openCommentStatus!=1">
  87. <!-- 答题时展示小节课程名,其他展示课程名 -->
  88. <!-- 小节课程名 -->
  89. <view class="subtitlebox" v-if="isLogin&&isAddKf==1">
  90. {{courseInfo.title|| ''}}
  91. </view>
  92. <!-- 课程名字 -->
  93. <view class="miantitlebox" v-else>
  94. {{courseInfo.courseName|| ''}}
  95. </view>
  96. </view>
  97. <view class="tabbox-bar" v-if="openCommentStatus==1">
  98. <view class="tabbox">
  99. <view :class="currentTab == nav.id ? 'tabbox-active':''" v-for="nav in navList" :key="nav.id" @click="handleTab(nav.id)">{{nav.name}}</view>
  100. </view>
  101. </view>
  102. <template v-if="openCommentStatus==1">
  103. <view v-show="currentTab==0">
  104. <descInfoNav ref="descInfoNav" :isLogin="isLogin" :isAddKf="isAddKf" :courseInfo="courseInfo"></descInfoNav>
  105. </view>
  106. <view v-show="currentTab==2">
  107. <commentBox
  108. ref="commentBox"
  109. :height="height"
  110. :urlOption="urlOption"
  111. :time="playTime"
  112. :flagTime="flagTime"
  113. :showDanmu="showDanmu"
  114. :viewCommentNum="viewCommentNum"
  115. :openCommentStatus="openCommentStatus"
  116. :isSocketOpen="isSocketOpen"
  117. @socketSend="socketSend"
  118. @initSocket="initSocket"
  119. @setInputText="setInputText"
  120. @getScrollTop="getScrollTop"
  121. @getMore="getMore"></commentBox>
  122. </view>
  123. </template>
  124. <view v-show="currentTab==1">
  125. <template v-if="openCommentStatus!=1">
  126. <!-- 介绍 -->
  127. <descInfo ref="descInfo" :isLogin="isLogin" :isAddKf="isAddKf" :courseInfo="courseInfo"></descInfo>
  128. </template>
  129. <!-- 问题 -->
  130. <template v-if="isLogin&&isAddKf==1&&linkType!=1">
  131. <ques ref="ques" :urlOption="urlOption" :courseNote="courseNote" :showNote="showNote" :treatmentPackage="treatmentPackage" :showTreatment="showTreatment" :quesList="quesList" :openCommentStatus="openCommentStatus" @handleAnswer="handleAnswer" @showBtnType="showBtnType"></ques>
  132. </template>
  133. </view>
  134. </scroll-view>
  135. <!-- 线路 -->
  136. <view class="video-line" v-if="isLogin&&isAddKf==1" @click="openPop">
  137. <image src="https://cos.his.cdwjyyh.com/fs/20250915/04889779f7f44e22a7a378d26823b2e8.png"></image>
  138. <text>线路{{numberToChinese(lineIndex + 1)}}</text>
  139. </view>
  140. <!-- 线路弹窗 -->
  141. <uni-popup ref="popup" type="bottom" class="full-width-popup">
  142. <view class="popupbox">
  143. <view class="popupbox-head">
  144. <text>线路选择</text>
  145. <image class="close-icon" src="https://cos.his.cdwjyyh.com/fs/20250915/b950ff20d115453f9d7f908590c9c0e7.png" mode="aspectFill" @click="close">
  146. </image>
  147. </view>
  148. <view class="popupbox-content">
  149. <view :class="lineIndex == index ? 'line-item line-active': 'line-item'"
  150. v-for="(it,index) in lineList" :key="index" @click="handleLine(index)">
  151. 线路{{numberToChinese(index + 1)}}</view>
  152. </view>
  153. </view>
  154. </uni-popup>
  155. <!-- 发送弹幕 -->
  156. <view class="video-line danmu-line" v-if="isLogin&&isAddKf==1&&openCommentStatus==2" @click="openDanmu(0)" >
  157. <image class="set_image" src="https://cos.his.cdwjyyh.com/fs/20250418/5e508642737a44169061382566043ac9.png" mode="aspectFill"></image>
  158. <text>发弹幕</text>
  159. </view>
  160. <!-- 发送弹幕弹窗 -->
  161. <uni-popup ref="danmuPopup" type="bottom" style="z-index: 999;" @change="changeShowPopup">
  162. <view class="danmuPopup" :style="{marginLeft:isfull ? statusBarHeight+'px': 0,marginBottom: danmuboxHeight+'px'}">
  163. <view class="danmuPopup-head border-line">
  164. <image class="danmu-icon" :src="showDanmu==0?baseUrl+'/images/danmu-off.png':baseUrl+'/images/danmu-on.png'" mode="heightFix" @click="switchDanmu()"></image>
  165. <view class="u-border">
  166. <u-input
  167. class="danmuPopup-input"
  168. placeholder="发个弹幕吧~"
  169. border="border"
  170. :focus="focus"
  171. :adjustPosition="false"
  172. :autoBlur="true"
  173. maxlength="50"
  174. v-model.trim="inputText">
  175. </u-input>
  176. <text style="font-size: 24rpx;color: #bbb;margin-left: 10rpx;">{{inputText?inputText.trim().length:0}}/50</text>
  177. </view>
  178. <button class="danmuPopup-send" @click="handleChatInput">发送</button>
  179. </view>
  180. </view>
  181. </uni-popup>
  182. <!-- 答题弹窗 -->
  183. <uni-popup ref="answerPopup" type="center">
  184. <view :class="errTitle == '恭喜你,回答正确' ? 'answerPopup-box bg':'answerPopup-box'">
  185. <!-- 正确 -->
  186. <image class="tipimg" v-if="errTitle == '恭喜你,回答正确'" src="https://cos.his.cdwjyyh.com/fs/20250915/f7b1075c21b442069ec9721a1a4790ec.png"
  187. mode="aspectFill"></image>
  188. <!-- 错误 -->
  189. <image class="tipimg" v-else src="https://cos.his.cdwjyyh.com/fs/20250915/a4b145c94b684c778c03ad9b52d717f3.png" mode="aspectFill">
  190. </image>
  191. <view class="answerPopup-title">{{errTitle}}</view>
  192. <view class="answerPopup-desc" v-html="errDesc"></view>
  193. <view style="color: #FF5C03;">{{currentRewardText}}</view>
  194. <!-- 选择奖励 -->
  195. <view class="reward-list" v-if="errTitle == '恭喜你,回答正确'">
  196. <view :class="item.value == currentReward?'reward-item reward-active':'reward-item'"
  197. v-for="(item, index) in rewardType" :key="item.value" @click="rewardChange(item)">
  198. {{item.name}}
  199. </view>
  200. <!-- <radio-group class="reward-list-group" @change="rewardChange">
  201. <label class="reward-list-option" v-for="(item, index) in rewardType" :key="item.value">
  202. <radio :value="item.value+ ''" :checked="item.value == currentReward"
  203. activeBorderColor="#FF5C03" activeBackgroundColor="#FF5C03"
  204. style="transform:scale(0.7)" color="#FF5C03" />
  205. <view :style="{color: item.value == currentReward ? '#FF5C03':''}">{{item.name}}</view>
  206. </label>
  207. </radio-group> -->
  208. </view>
  209. <!-- 错误题目 -->
  210. <view class="errQuesbox" v-if="errQues&&errQues.length>0">
  211. <view class="errQuesbox-item textOne" v-for="(it,index) in errQues" :key="index">{{it.title}}</view>
  212. </view>
  213. <view class="answerPopup-btn" v-if="errTitle == '恭喜你,回答正确'" @click="closeAnswerPopup">确认</view>
  214. <view class="tipsPopup-btn-box" v-else
  215. :style="{marginTop: errQues&&errQues.length>0 ? '40rpx':'54rpx'}">
  216. <view class="tipsPopup-btn" @click="closeAnswerPopup">{{remain > 0 ? '重新答题': '确认'}}</view>
  217. </view>
  218. </view>
  219. </uni-popup>
  220. <!-- 展示兑换奖品弹窗 -->
  221. <appPopup ref="appPopup"></appPopup>
  222. <!-- 客服二维码弹窗 -->
  223. <uni-popup ref="kfPopup" type="center" :mask-click="false">
  224. <view class="kfqrcode-box">
  225. <image class="kfqrcode" v-if="qrcode" :src="qrcode" show-menu-by-longpress="true"></image>
  226. <view v-show="qrcodeMsg" style="margin-top: 30rpx;" v-html="qrcodeMsg"></view>
  227. <image class="kfqrcode-close" src="https://cos.his.cdwjyyh.com/fs/20250915/97978486cc6c47c6949c5bcc9bc0c4a3.png" mode="aspectFill"
  228. @click="closeKFPop"></image>
  229. </view>
  230. </uni-popup>
  231. <!-- 可以答题提示 -->
  232. <view class="answerTip" v-if="currentTab!=1&&openCommentStatus==1&&showAnswerTip" @click="handleTab(1)">
  233. 可以答题啦
  234. </view>
  235. <!-- footer -->
  236. <view class="footer" v-show="currentTab!=2&&videoId">
  237. <view class="footer-btn" v-if="!isLogin || isAddKf !=1" @click="submit">
  238. <text>立即学习</text>
  239. </view>
  240. <view v-if="isLogin&&isAddKf==1&&linkType!=1&&currentTab==1&&quesList&&quesList.length>0&&showBtn==0" class="footer-btn footer-btn-border" @click="submit">
  241. <!-- <image class="footer-btn-img" :src="baseUrl+'/images/red_envelope_btnimg.png'" mode="aspectFill"></image> -->
  242. <text>提交答案领取奖励</text>
  243. </view>
  244. <!-- <view @click="checked=!checked" class="agreement" v-if="!isLogin">
  245. <radio value="r1" :checked="checked" color="#ff5c03" activeBorderColor="#ff5c03"
  246. style="transform:scale(0.6);" />
  247. <view>阅读并同意<text style="color: #ff5c03;" @click.stop="goWeb(0)">《用户协议》</text>和<text
  248. style="color: #ff5c03;" @click.stop="goWeb(1)">《隐私协议》</text></view>
  249. </view> -->
  250. <!-- <view class="footer-tips">重庆云联融智提供技术支持</view> -->
  251. </view>
  252. <view v-show="currentTab==2">
  253. <view class="chatinput" :style="{bottom:danmuboxHeight>0?danmuboxHeight+'px':'calc(var(--window-bottom) + 24rpx)'}">
  254. <input class="uni-input" v-model.trim="inputText" :adjustPosition="false" :autoBlur="false" maxlength="140" placeholder="发消息···" confirm-type="send" @confirm="handleChatInput" />
  255. <button class="send" @click="handleChatInput">发送</button>
  256. </view>
  257. </view>
  258. <turntableOne ref="turntableOne" @sendRewardFun="sendRewardFun" @openAppPop="openAppPop"></turntableOne>
  259. <turntableTwo ref="turntableTwo" @sendRewardFun="sendRewardFun" @openAppPop="openAppPop"></turntableTwo>
  260. <yk-screenRecord></yk-screenRecord>
  261. <userlogo ref="userlogo" @editUserSuccess="editUserSuccess"></userlogo>
  262. </view>
  263. </template>
  264. <script>
  265. import userlogo from "./components/userlogo"
  266. import ykScreenRecord from './components/yk-screenRecord/yk-screenRecord';
  267. import { mapGetters } from 'vuex';
  268. import turntableOne from "./components/turntableOne.vue"
  269. import turntableTwo from "./components/turntableTwo.vue"
  270. import appPopup from "./components/appPopup.vue"
  271. import ques from "./components/ques.vue"
  272. import descInfo from "./components/descInfo.vue"
  273. import descInfoNav from "./components/descInfoNav.vue"
  274. import commentBox from "./components/commentBox.vue"
  275. import treasureChest from "./components/treasureChest.vue"
  276. import dayjs from 'dayjs';
  277. import {
  278. getErrMsg,
  279. getH5CourseByVideoId,
  280. getH5CourseVideoDetails,
  281. courseAnswer,
  282. getFinishCourseVideo,
  283. getIsAddKf,
  284. internetTraffic,
  285. getIntegralByH5Video,
  286. sendReward,
  287. loginByMp,
  288. getRealLink,
  289. getConfigByKey,
  290. claim,
  291. getVideoRewardTypes,
  292. getUserPhoneDeviceType
  293. } from "@/api/course.js"
  294. export default {
  295. components: {
  296. descInfoNav,
  297. descInfo,
  298. commentBox,
  299. ques,
  300. treasureChest,
  301. appPopup,
  302. turntableOne,
  303. turntableTwo,
  304. ykScreenRecord,
  305. userlogo
  306. },
  307. data() {
  308. return {
  309. socket: null,
  310. isSocketOpen: false,
  311. pingpangTimes: null,
  312. baseUrl:uni.getStorageSync('requestImagesPath'),
  313. // 1 红包 2 芳华币
  314. rewardType:[],
  315. currentReward: -1,
  316. player: null,
  317. loading: true,
  318. progress: 0,
  319. code: null,
  320. statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
  321. scrollTop: 0,
  322. height: '0px',
  323. isLogin: false,
  324. videoUrl: "",
  325. videoId: "",
  326. //现在的时长
  327. playTime: 0,
  328. //总时长
  329. duration: 0,
  330. playDuration: 0,
  331. // 用于续播
  332. playDurationSeek: 0,
  333. // 温馨提醒时间节点,
  334. tipsTime: 0,
  335. tipsOpen: false,
  336. config: {},
  337. courseInfo: {},
  338. quesList: [],
  339. lineList: [],
  340. // 错题
  341. errQues: [],
  342. // 答题机会
  343. remain: 0,
  344. errTitle: "",
  345. errDesc: "",
  346. showPlay: true,
  347. showControls: false,
  348. playStatus: "",
  349. isfull: false,
  350. isAddKf: 0,
  351. lineIndex: 0,
  352. textHeight: 0, //文本高度
  353. qwUserId: "",
  354. qrcode: "",
  355. corpId: "",
  356. qrcodeMsg: "",
  357. urlOption: {},
  358. bufferRate: 0, // 缓冲时间
  359. uuId: "",
  360. isEnded: false,
  361. // 是否允许拖动进度条
  362. linkType: 0, // 1表示应急链接
  363. ip: null,
  364. checked: true,
  365. isFinish: 0, // 是否完课
  366. interval: null,
  367. intervalIntegral: null, // 芳华币定时
  368. options: {
  369. sources: [{
  370. src: ""
  371. }],
  372. poster: "",
  373. live: false /* 是否直播 */ ,
  374. controls: true,
  375. autoplay: false,
  376. licenseUrl: 'https://license.vod2.myqcloud.com/license/v2/1323137866_1/v_cube.license', // license 地址,参考准备工作部分,在视立方控制台申请 license 后可获得 licenseUrl,
  377. LicenseKey: 'bcc5bd9a14b798b48c52ff005a21d926',
  378. controlBar: {
  379. volumePanel: false,
  380. playbackRateMenuButton: false,
  381. QualitySwitcherMenuButton: false,
  382. // progressControl: false
  383. },
  384. plugins: {
  385. // ProgressMarker: false,
  386. ContextMenu: {
  387. statistic: false
  388. }
  389. },
  390. },
  391. poster: "",
  392. // 错误请求次数
  393. errorCount: 0,
  394. answerPopup: false,
  395. sortLink:"",
  396. // 课程是否过期
  397. isExpire: false,
  398. menuButtonLeft: 281,
  399. menuButtonH: 45,
  400. timer: null,
  401. flag: false,
  402. focus: false,
  403. openDanmuType: 0,
  404. danmuboxHeight: 0,
  405. user: {},
  406. crtShow: true,
  407. // 是否获取到对应观看者的真实链接
  408. isCheckRealUrl: false,
  409. courseLogo: '',
  410. isfull: false,
  411. navList:[{
  412. id: 0,
  413. name: '介绍'
  414. },{
  415. id: 1,
  416. name: '答题'
  417. },{
  418. id: 2,
  419. name: '评论'
  420. }],
  421. currentTab: 1,
  422. triggered: false,
  423. // 没有更多
  424. isMore: false,
  425. inputText:"",
  426. // 获取最多历史评论条数
  427. viewCommentNum: 200,
  428. // 1-开启评论;2-开启弹幕;3-关闭
  429. openCommentStatus: 3,
  430. showAnswerTip: false,
  431. showDanmu: 1,
  432. activeDanmus:[],
  433. flagTime: 0,
  434. notice: '',
  435. fsUserCourseRewardRoundStatusVO: [],
  436. onHide: false,
  437. courseNote:'',
  438. showBtn: 0,
  439. showNote: 0,
  440. showTreatment: 1, // 1不展示,0展示
  441. treatmentPackage: []
  442. }
  443. },
  444. computed:{
  445. ...mapGetters(['coureLogin']),
  446. currentRewardText(){
  447. const index = this.rewardType.findIndex(it=>it.value==this.currentReward)
  448. return index > -1 ? this.rewardType[index].text || '':''
  449. }
  450. },
  451. watch: {
  452. coureLogin: {
  453. immediate: true, // 页面一进入就检查一次
  454. handler(val) {
  455. if (val == 2) {
  456. console.log("AppToken失效,请重新登录")
  457. this.isLogin = false
  458. this.isAddKf = 0
  459. this.goLogin()
  460. }
  461. }
  462. }
  463. },
  464. onLoad(option) {
  465. this.code = option.code
  466. if (!option.course) {
  467. const keys = decodeURIComponent(Object.keys(option)[0]);
  468. this.urlOption = JSON.parse(keys.split('course=')[1])
  469. } else {
  470. this.urlOption = option.course ? JSON.parse(decodeURIComponent(option.course)) : {}
  471. }
  472. this.videoId = this.urlOption.videoId || ''
  473. this.qwUserId = this.urlOption.qwUserId || ''
  474. this.corpId = this.urlOption.corpId || ''
  475. this.linkType = this.urlOption.linkType || 0
  476. // if (this.code) {
  477. // this.loginByMp()
  478. // }
  479. var that=this;
  480. if (this.videoId) {
  481. this.getH5CourseByVideo()
  482. this.getConfigByKey()
  483. }
  484. this.sortLink = this.urlOption.link || ''
  485. this.getMenuButton()
  486. // #ifndef H5
  487. uni.onKeyboardHeightChange(this.keyboardHeightChange);
  488. // #endif
  489. },
  490. onShow() {
  491. this.onHide = false
  492. this.tipsOpen = false
  493. // this.isLogin = this.utils.isLoginCourse()
  494. this.uuId = this.utils.generateRandomString(16)
  495. if(uni.getStorageSync('auto_userInfo') && JSON.stringify(uni.getStorageSync('auto_userInfo'))!='{}') {
  496. this.user = JSON.parse(uni.getStorageSync('auto_userInfo'))
  497. } else {
  498. this.user = {}
  499. }
  500. if(this.sortLink){
  501. this.getLink()
  502. } else {
  503. uni.showToast({
  504. title: 'sortLink is not found',
  505. icon: 'none'
  506. });
  507. if(this.isLogin) {
  508. this.getUserPhoneDeviceType()
  509. }
  510. }
  511. },
  512. mounted() {
  513. this.getIP()
  514. this.getHeight()
  515. },
  516. onHide() {
  517. this.onHide = true
  518. // this.player = uni.createVideoContext('video-content-box');
  519. if (this.player) {
  520. this.player.pause()
  521. }
  522. // if (this.interval != null) {
  523. // clearInterval(this.interval)
  524. // this.interval = null
  525. // }
  526. this.closeSocket()
  527. },
  528. onUnload() {
  529. if (this.interval != null) {
  530. clearInterval(this.interval)
  531. this.interval = null
  532. }
  533. this.clearIntegral()
  534. // #ifndef H5
  535. uni.offKeyboardHeightChange(this.keyboardHeightChange);
  536. // #endif
  537. this.closeSocket()
  538. },
  539. beforeDestroy() {
  540. this.player = uni.createVideoContext('video-content-box');
  541. if (this.player) {
  542. this.player.stop()
  543. this.player = null
  544. }
  545. if (this.interval != null) {
  546. clearInterval(this.interval)
  547. this.interval = null
  548. }
  549. this.clearIntegral()
  550. // #ifndef H5
  551. uni.offKeyboardHeightChange(this.keyboardHeightChange);
  552. // #endif
  553. this.closeSocket()
  554. },
  555. methods: {
  556. getVideoRewardTypes() {
  557. getVideoRewardTypes({...this.urlOption}).then(res=>{
  558. if(res.code == 200) {
  559. this.rewardType = res.data
  560. this.currentReward = this.rewardType&&this.rewardType.length>0 ? this.rewardType[0].value : -1
  561. }
  562. })
  563. },
  564. getConfigByKey(){
  565. let param = {key:"course.config"};
  566. getConfigByKey(param).then(
  567. res => {
  568. if(res.code==200){
  569. let data=res.data ? JSON.parse(res.data) : {};
  570. this.notice = data.notify || ''
  571. }
  572. },
  573. rej => {}
  574. );
  575. },
  576. numberToChinese(number) {
  577. if (number) {
  578. const chineseNumber = ['一', '二', '三', '四', '五', '六', '七', '八', '九'];
  579. return chineseNumber[number - 1];
  580. } else {
  581. return ''
  582. }
  583. },
  584. keyboardHeightChange(res) {
  585. // #ifndef H5
  586. console.log("this.danmuboxHeight",this.danmuboxHeight)
  587. this.danmuboxHeight = res.height
  588. // #endif
  589. },
  590. getMenuButton(){
  591. const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
  592. this.menuButtonLeft = menuButtonInfo.left
  593. this.menuButtonH = menuButtonInfo.height
  594. },
  595. //播放时间更新事件方法
  596. onTimeUpdate(e){
  597. let currentTime = Math.round(e.detail.currentTime)
  598. if (this.playDurationSeek > 0) {
  599. this.playTime = this.playDurationSeek
  600. this.throttle(() => this.changeTime(this), 1000, false)
  601. } else {
  602. if (this.linkType != 1 && (currentTime - this.playTime > 3 || currentTime - this.playTime < -3)&&this.isFinish!=1) {
  603. uni.showToast({
  604. title: '不能快进哦',
  605. icon: 'none',
  606. });
  607. currentTime = this.playTime
  608. this.player.seek(this.playTime);
  609. }
  610. this.playTime = currentTime
  611. }
  612. if (Math.floor(e.detail.currentTime) != this.flagTime) {
  613. this.flagTime = Math.floor(e.detail.currentTime)
  614. const isNearEnd = this.playTime >= this.duration - 60;
  615. this.showAnswerTip = this.isEnded || isNearEnd;
  616. const progress = this.duration ? (this.playTime || 0) / this.duration : 0;
  617. const answerRate = Number(this.config.answerRate ?? 1); // 默认 100%
  618. const hitRate = progress >= answerRate;
  619. this.showNote = (this.isEnded || this.isFinish === 1 || hitRate) ? 1 : 0;
  620. if(this.fsUserCourseRewardRoundStatusVO&&this.fsUserCourseRewardRoundStatusVO.length>0) {
  621. this.$refs.treasureChest&&this.$refs.treasureChest.showTreasure(this.flagTime)
  622. }
  623. if(this.openCommentStatus == 2) {
  624. this.$refs.danmuBox&&this.$refs.danmuBox.checkDanmu(this.flagTime)
  625. }
  626. }
  627. },
  628. changeTime(that,e) {
  629. that.playDurationSeek = 0
  630. },
  631. videoErrorCallback(e) {
  632. this.errorCount++
  633. if (this.errorCount > 3) return
  634. if (this.interval != null) {
  635. clearInterval(this.interval)
  636. }
  637. this.clearIntegral()
  638. console.log(e)
  639. this.getErrMsg(e.target.errMsg)
  640. this.getH5CourseVideoDetails('error')
  641. },
  642. // 当开始/继续播放时触发play事件
  643. getPlay() {
  644. this.errorCount = 0
  645. // this.judgeDuration()
  646. },
  647. getPause() {
  648. this.clearIntegral()
  649. },
  650. getEnded() {
  651. this.clearIntegral()
  652. this.isEnded = true
  653. this.showAnswerTip = true
  654. this.isFinish = 1
  655. this.showNote = 1
  656. this.getFinishCourseVideo()
  657. },
  658. getWaiting() {
  659. this.getErrMsg('','waiting')
  660. },
  661. fullscreenchange(event) {
  662. this.isfull = event.detail.fullScreen
  663. if(this.isfull) {
  664. this.$refs.danmuBox&&this.$refs.danmuBox.initTracks()
  665. }
  666. },
  667. controlstoggle(event) {
  668. this.crtShow = event.detail.show
  669. },
  670. getIP() {
  671. uni.request({
  672. url: 'https://ipinfo.io/json', //仅为示例,并非真实接口地址。
  673. method: 'GET',
  674. success: (res) => {
  675. this.ip = res.data.ip
  676. }
  677. });
  678. },
  679. getHeight() {
  680. setTimeout(()=>{
  681. const query = uni.createSelectorQuery().in(this);
  682. query
  683. .select("#title-contentnav")
  684. .boundingClientRect((data) => {
  685. if(data) {
  686. const footerH = this.showBtn==0? 80: 0
  687. this.height =
  688. `calc(100vh - ${data.height}px - 420rpx - ${this.statusBarHeight}px - ${footerH}px - 88rpx)`
  689. }
  690. })
  691. .exec();
  692. },200)
  693. },
  694. numberToLetter(num) {
  695. // 将数字转换为字母的 ASCII 码
  696. let letterCode = num + 65;
  697. // 将 ASCII 码转换为大写字母
  698. let letter = String.fromCharCode(letterCode);
  699. return letter;
  700. },
  701. updateTime() {
  702. var that = this;
  703. if (this.interval != null) {
  704. clearInterval(this.interval)
  705. }
  706. this.interval = setInterval(function() {
  707. that.getFinishCourseVideo()
  708. that.getInternetTraffic()
  709. }, 60000);
  710. },
  711. judgeDuration() {
  712. var that = this;
  713. if (this.intervalIntegral != null) {
  714. clearInterval(this.intervalIntegral)
  715. this.intervalIntegral = null
  716. }
  717. // 观看10分钟获得芳华币
  718. this.intervalIntegral = setInterval(function() {
  719. that.getIntegralByH5Video()
  720. }, 600000);
  721. },
  722. clearIntegral() {
  723. if (this.intervalIntegral != null) {
  724. clearInterval(this.intervalIntegral)
  725. this.intervalIntegral = null
  726. }
  727. },
  728. getH5CourseByVideo() {
  729. this.loading = true
  730. getH5CourseByVideoId({
  731. videoId: this.videoId
  732. }).then(res => {
  733. this.loading = false
  734. if (res.code == 200) {
  735. this.courseInfo = res.data
  736. uni.setNavigationBarTitle({
  737. title: this.courseInfo && this.courseInfo.title ? this.courseInfo.title : ''
  738. });
  739. }
  740. this.getHeight()
  741. this.$nextTick(()=>{
  742. this.$refs.descInfo&&this.$refs.descInfo.getDescHeight()
  743. this.$refs.descInfoNav&&this.$refs.descInfoNav.getDescHeight()
  744. })
  745. },
  746. rej => {
  747. this.loading = false
  748. }
  749. ).catch(() => {
  750. this.loading = false
  751. })
  752. },
  753. getH5CourseVideoDetails(type) {
  754. getH5CourseVideoDetails(this.urlOption).then(res => {
  755. if (res.code == 200) {
  756. this.config = res.config || {}
  757. this.courseNote = res.course&&res.course.note
  758. this.isFinish = res.isFinish || 0
  759. this.showNote = this.isFinish==1 ? 1:0
  760. this.duration = res.course && res.course.duration ? res.course.duration : 0
  761. this.playDuration = res.playDuration || 0
  762. this.tipsTime = res.tipsTime || 0
  763. const showTreatmentFlag = res.course&&res.course.showTreatment!=null ? res.course.showTreatment : 1
  764. this.treatmentPackage = res.course&&res.course.treatmentPackage ? res.course.treatmentPackage : []
  765. this.showTreatment = showTreatmentFlag==0&&this.treatmentPackage.length>0 ? 0 : 1
  766. let lineList = []
  767. if (res.course && res.course.lineOne) {
  768. lineList.push(res.course.lineOne)
  769. }
  770. if (res.course && res.course.lineTwo) {
  771. lineList.push(res.course.lineTwo)
  772. }
  773. if (res.course && res.course.lineThree) {
  774. lineList.push(res.course.lineThree)
  775. }
  776. this.lineList = lineList
  777. this.quesList = res.questions && res.questions.length > 0 ? res.questions : []
  778. this.quesList = this.quesList.map(item => ({
  779. ...item,
  780. questionOption: JSON.parse(item.question),
  781. answer: ''
  782. }))
  783. this.courseLogo = res.config&&res.config.courseLogo
  784. this.viewCommentNum = res.config&&res.config.viewCommentNum || 200
  785. this.openCommentStatus = res.config&&res.config.openCommentStatus || 3;
  786. // this.openCommentStatus = source
  787. // console.log('socket===',socket,this.openCommentStatus)
  788. if(this.openCommentStatus == 3) {
  789. this.closeSocket()
  790. }else {
  791. this.initSocket()
  792. }
  793. this.currentTab = 1
  794. if(this.openCommentStatus!=2 || this.showDanmu!=1) {
  795. this.activeDanmus = []
  796. }
  797. this.fsUserCourseRewardRoundStatusVO = this.getBox(res)
  798. if (!this.player || type == 'error') {
  799. this.lineIndex = this.config.defaultLine
  800. this.videoUrl = lineList[this.lineIndex]
  801. this.poster= res.course && res.course.imgUrl ? res.course.imgUrl : ''
  802. // this.options.sources = [{
  803. // src: this.videoUrl
  804. // }]
  805. // this.options.poster = res.course && res.course.imgUrl ? res.course.imgUrl : ''
  806. // this.initVideo()
  807. this.playTime = this.playDuration >= this.duration ? 0 : this.playDuration
  808. this.playDurationSeek = this.playTime
  809. setTimeout(()=>{
  810. this.player = uni.createVideoContext('video-content-box');
  811. this.player.seek(this.playTime)
  812. this.player.play();
  813. },500);
  814. } else {
  815. // let div = document.querySelector(".vjs-progress-control");
  816. // if(div) {
  817. // if (this.isFinish == 1 || this.isEnded || this.linkType == 1) {
  818. // div.style.pointerEvents = "auto";
  819. // } else {
  820. // div.style.pointerEvents = "none"; //禁止所有事件
  821. // }
  822. // }
  823. this.playTime = this.playTime > this.playDuration ? this.playTime : this.playDuration >= this.duration ? 0 : this.playDuration
  824. this.playDurationSeek = this.playTime
  825. this.player.seek(this.playTime)
  826. this.player.play();
  827. }
  828. this.updateTime();
  829. } else {
  830. uni.showToast({
  831. title: res.msg,
  832. icon: 'none'
  833. })
  834. }
  835. this.getHeight()
  836. this.$nextTick(()=>{
  837. this.$refs.descInfo&&this.$refs.descInfo.getDescHeight()
  838. this.$refs.descInfoNav&&this.$refs.descInfoNav.getDescHeight()
  839. })
  840. },
  841. rej => {}
  842. )
  843. },
  844. handleAnswer(val) {
  845. let {item, option,index} = val
  846. let time = this.playTime
  847. if(this.isEnded||this.isFinish==1) {
  848. time = this.duration
  849. } else {
  850. if(time < this.playDuration&&this.isFinish!=1) {
  851. // 没完课且小于续播的时间
  852. time = this.playDuration
  853. }
  854. }
  855. if(Number(this.duration || 0) == 0 || time < this.duration - 60) {
  856. uni.showToast({
  857. title: "请先观看完整课程再答题哦~",
  858. icon: "none"
  859. })
  860. return
  861. }
  862. if (this.quesList[index].type == 1) {
  863. // 单选option
  864. this.quesList[index].answer = option.name
  865. } else if (this.quesList[index].type == 2) {
  866. // 多选
  867. let answer = this.quesList[index].answer ? this.quesList[index].answer.split(',') : []
  868. if (answer.indexOf(option.name) === -1) {
  869. answer.push(option.name)
  870. this.quesList[index].answer = answer.join(',')
  871. } else {
  872. answer.splice(answer.indexOf(option.name), 1)
  873. this.quesList[index].answer = answer.join(',')
  874. }
  875. }
  876. },
  877. submit() {
  878. if(this.isExpire){
  879. uni.showToast({
  880. title: '课程已过期或链接无效',
  881. icon: 'none'
  882. });
  883. return
  884. }
  885. // 登录
  886. this.utils.isLoginCourse().then(
  887. res => {
  888. if(res){
  889. if (this.isAddKf == 1&&this.isCheckRealUrl) {
  890. // 答题
  891. // 您已提交过答案,请领取红包
  892. this.courseAnswer()
  893. } else {
  894. // 添加客服
  895. if (this.videoId && this.qwUserId) {
  896. this.getIsAddKf()
  897. } else {
  898. uni.showToast({
  899. title: '请添加客服',
  900. icon: 'none'
  901. })
  902. }
  903. }
  904. } else{
  905. this.goLogin()
  906. }
  907. },
  908. rej => {}
  909. );
  910. },
  911. // 答题
  912. courseAnswer() {
  913. let time = this.playTime
  914. if (this.isEnded || this.isFinish==1) {
  915. time = this.duration
  916. } else {
  917. if (time < this.playDuration && this.isFinish != 1) {
  918. // 没完课且小于续播的时间
  919. time = this.playDuration
  920. }
  921. }
  922. if (Number(this.duration || 0) == 0 || time < this.duration - 60) {
  923. uni.showToast({
  924. title: "请先观看完整课程再答题哦~",
  925. icon: "none"
  926. })
  927. return
  928. }
  929. if (this.quesList.some(item => !item.answer)) {
  930. uni.showToast({
  931. title: "请确认是否答完所有题目",
  932. icon: "none"
  933. })
  934. return
  935. }
  936. const questions = this.quesList.map(obj => {
  937. const {
  938. questionOption,
  939. ...rest
  940. } = obj;
  941. return rest;
  942. });
  943. if(!this.isCheckRealUrl) return;
  944. const param = {
  945. ...this.urlOption,
  946. questions: questions,
  947. videoId: this.videoId,
  948. duration: this.playTime,
  949. }
  950. this.errTitle = ""
  951. this.errDesc = ""
  952. this.errQues = []
  953. courseAnswer(param).then(res => {
  954. if (res.code == 200) {
  955. if (res.incorrectQuestions) {
  956. // 答题失败
  957. if (res.incorrectQuestions.length > 0) {
  958. this.errQues = res.incorrectQuestions
  959. }
  960. this.remain = res.remain || 0
  961. if (res.remain > 0) {
  962. this.errTitle = "很遗憾答错了"
  963. this.errDesc = `<span style="color:#FF5C03">还有${res.remain}次机会,继续加油</span>`
  964. this.$refs.answerPopup.open("center")
  965. }
  966. } else {
  967. // 答题成功
  968. this.errTitle = "恭喜你,回答正确"
  969. this.errDesc = `请选择奖励`
  970. this.currentReward = this.rewardType&&this.rewardType.length>0 ? this.rewardType[0].value : -1
  971. this.$refs.answerPopup.open("center")
  972. }
  973. } else {
  974. if (res.msg == "该课题到达答错次数限制") {
  975. this.errTitle = "答题次数超过限制"
  976. this.errDesc = "以后的课程要认真学习哦"
  977. this.$refs.answerPopup.open("center")
  978. } else {
  979. uni.showToast({
  980. title: res.msg,
  981. icon: "none"
  982. })
  983. }
  984. }
  985. },
  986. rej => {}
  987. )
  988. },
  989. // 选择
  990. rewardChange(e) {
  991. this.currentReward = e.value
  992. },
  993. closeAnswerPopup() {
  994. this.$refs.answerPopup.close()
  995. if(!this.isCheckRealUrl) return;
  996. if (this.errTitle == '恭喜你,回答正确') {
  997. if(this.currentReward==4) {
  998. this.$refs.turntableTwo.open(this.urlOption)
  999. } else if(this.currentReward==3) {
  1000. this.$refs.turntableOne.open(this.urlOption)
  1001. } else {
  1002. this.sendRewardFun(this.currentReward)
  1003. }
  1004. }
  1005. },
  1006. sendRewardFun(currentReward) {
  1007. this.currentReward = currentReward
  1008. const param = {
  1009. ...this.urlOption,
  1010. rewardType: Number(this.currentReward),
  1011. source: 2,
  1012. appId: getApp().globalData.appId
  1013. }
  1014. sendReward(param).then(res => {
  1015. if(res.code == 200) {
  1016. if(currentReward == 4) {
  1017. this.$refs.turntableTwo.endSuccess(res.data)
  1018. }else if(currentReward == 3) {
  1019. this.$refs.turntableOne.endSuccess(res.data)
  1020. }else {
  1021. uni.showToast({
  1022. title: res.msg,
  1023. icon: 'none'
  1024. })
  1025. this.openAppPop()
  1026. }
  1027. } else {
  1028. this.currentReward == 4 ? this.$refs.turntableTwo.close('close') : this.currentReward == 3? this.$refs.turntableOne.close('close'):''
  1029. uni.showToast({
  1030. title: res.msg,
  1031. icon: 'none'
  1032. })
  1033. }
  1034. })
  1035. },
  1036. openAppPop() {
  1037. this.$refs.appPopup.openPop()
  1038. },
  1039. // 线路
  1040. openPop() {
  1041. this.$refs.popup.open('bottom')
  1042. },
  1043. close() {
  1044. this.$refs.popup.close()
  1045. },
  1046. handleLine(index) {
  1047. var that=this;
  1048. if (this.lineIndex == index && this.videoUrl == this.lineList[index]) {
  1049. this.close()
  1050. return
  1051. } else {
  1052. // let div = document.querySelector(".vjs-progress-control");
  1053. // if(div) {
  1054. // if (this.isFinish == 1 || this.isEnded || this.linkType == 1) {
  1055. // div.style.pointerEvents = "auto";
  1056. // } else {
  1057. // div.style.pointerEvents = "none"; //禁止所有事件
  1058. // }
  1059. // }
  1060. this.lineIndex = index
  1061. this.videoUrl = this.lineList[index]
  1062. this.tipsOpen = false
  1063. this.playDurationSeek = this.playTime || 0
  1064. this.player = uni.createVideoContext('video-content-box');
  1065. setTimeout(function(){
  1066. that.player.seek(that.playDurationSeek)
  1067. that.player.play();
  1068. },500);
  1069. // this.player.src(this.lineList[index])
  1070. // this.player.one('loadedmetadata', () => {
  1071. // this.player.currentTime(this.playDurationSeek);
  1072. // this.player.play();
  1073. // });
  1074. this.close()
  1075. }
  1076. },
  1077. // 客服
  1078. getIsAddKf() {
  1079. this.qrcode = ''
  1080. this.qrcodeMsg = ''
  1081. this.isAddKf = 0
  1082. // {videoId: this.videoId,qwUserId: this.qwUserId,corpId: this.corpId}
  1083. getIsAddKf(this.urlOption).then(res => {
  1084. if (res.code == 200) {
  1085. this.isAddKf = 1
  1086. this.isCheckRealUrl = true
  1087. this.getH5CourseVideoDetails()
  1088. } else if (res.code == 400) {
  1089. this.isAddKf = 0
  1090. this.isCheckRealUrl = false
  1091. this.qrcode = res.qrcode
  1092. this.qrcodeMsg = res.msg
  1093. this.$refs.kfPopup.open()
  1094. } else if (res.code == 566) {
  1095. // 官方群发通用链接
  1096. const url = res.courseLink.realLink.split('?course=')[1]
  1097. this.urlOption = JSON.parse(url)
  1098. this.isAddKf = 1
  1099. this.isCheckRealUrl = true
  1100. this.getH5CourseVideoDetails()
  1101. } else if (res.code == 567) {
  1102. // 群聊通用链接
  1103. this.urlOption = {
  1104. ...this.urlOption,
  1105. qwExternalId: res.qwExternalId
  1106. }
  1107. this.isAddKf = 1
  1108. this.isCheckRealUrl = true
  1109. this.getH5CourseVideoDetails()
  1110. }else {
  1111. this.isCheckRealUrl = false
  1112. this.isAddKf = 0
  1113. uni.showToast({
  1114. title: res.msg,
  1115. icon: 'none'
  1116. });
  1117. if(res.code==401) return;
  1118. uni.redirectTo({
  1119. url: '/pages_course/courseExpiration?code='+res.code+'&msg='+res.msg
  1120. })
  1121. }
  1122. },
  1123. err => {}
  1124. ).catch((err) => {
  1125. uni.showToast({
  1126. title: err,
  1127. icon: 'none'
  1128. });
  1129. })
  1130. },
  1131. closeKFPop() {
  1132. this.$refs.kfPopup.close()
  1133. },
  1134. getFinishCourseVideo() {
  1135. if (!this.isLogin||!this.playTime || !this.isCheckRealUrl) return
  1136. // {videoId: this.videoId,duration:this.playTime}
  1137. const param = {
  1138. duration: this.playTime,
  1139. ...this.urlOption
  1140. }
  1141. getFinishCourseVideo(param)
  1142. },
  1143. // 每十分钟获得芳华币
  1144. getIntegralByH5Video() {
  1145. if(!this.isLogin||!this.isCheckRealUrl) return
  1146. const param = {
  1147. duration: this.playTime,
  1148. ...this.urlOption
  1149. }
  1150. getIntegralByH5Video(param).then(res => {
  1151. if (res.code == 200) {
  1152. uni.showToast({
  1153. title: "芳华币+10",
  1154. icon: "none"
  1155. })
  1156. }
  1157. })
  1158. },
  1159. progressChange(e) {
  1160. this.bufferRate = Math.ceil(e.detail.buffered)
  1161. },
  1162. // 缓冲
  1163. getInternetTraffic() {
  1164. if(!this.isLogin||!this.isCheckRealUrl) return
  1165. const playVideoTime = Math.ceil(this.playTime / this.duration * 100) // 播放百分比
  1166. if(this.bufferRate == 0 || this.bufferRate < playVideoTime) {
  1167. this.bufferRate = playVideoTime
  1168. }
  1169. if(this.bufferRate == 0 || Number(this.bufferRate.toFixed(2)) == 0) return
  1170. const param = {
  1171. ...this.urlOption,
  1172. uuId: dayjs().format('YYYYMMDD') + this.uuId,
  1173. duration: this.playTime,
  1174. bufferRate: Number(this.bufferRate.toFixed(2)),
  1175. }
  1176. if(!param.bufferRate) return
  1177. internetTraffic(param)
  1178. },
  1179. getErrMsg(err,type) {
  1180. let msgerr = {
  1181. errType: type || 'error',
  1182. videoUrl: this.videoUrl,
  1183. lineIndex: this.lineIndex,
  1184. errTime: new Date(),
  1185. ip: this.ip,
  1186. errMsg: err || ''
  1187. }
  1188. getErrMsg({
  1189. msg: JSON.stringify(msgerr)
  1190. })
  1191. },
  1192. editUserSuccess() {
  1193. this.isLogin = true
  1194. this.getIsAddKf()
  1195. this.getVideoRewardTypes()
  1196. this.getUserPhoneDeviceType()
  1197. },
  1198. goLogin() {
  1199. this.utils.getProvider().then(provider=>{
  1200. console.log('当前的环境商',provider)
  1201. if (!provider) {
  1202. reject()
  1203. }
  1204. uni.login({
  1205. provider: provider,
  1206. success: async loginRes => {
  1207. console.log(loginRes)
  1208. uni.getUserInfo({
  1209. provider: provider,
  1210. success: (infoRes)=> {
  1211. uni.showToast({
  1212. title: '处理中...',
  1213. icon: 'loading'
  1214. });
  1215. loginByMp({code: loginRes.code,encryptedData:infoRes.encryptedData,iv:infoRes.iv,appId:getApp().globalData.appId}).then(res=>{
  1216. uni.hideLoading();
  1217. if (res.code == 200) {
  1218. this.$store.commit('setCoureLogin', 1);
  1219. uni.setStorageSync('AppTokenmini_RTCourse', res.token);
  1220. uni.setStorageSync('auto_userInfo', JSON.stringify(res.user));
  1221. this.user = res.user
  1222. if(this.urlOption.chatId) {
  1223. this.isLogin = false
  1224. this.$refs.userlogo.userlogo = true
  1225. } else {
  1226. this.isLogin = true
  1227. this.getIsAddKf()
  1228. this.getVideoRewardTypes()
  1229. this.getUserPhoneDeviceType()
  1230. }
  1231. } else {
  1232. uni.showToast({
  1233. title: res.msg,
  1234. icon: 'none'
  1235. });
  1236. }
  1237. }).catch(err=>{
  1238. uni.hideLoading();
  1239. uni.showToast({
  1240. icon:'none',
  1241. title: "登录失败,请重新登录",
  1242. });
  1243. });
  1244. }
  1245. });
  1246. }
  1247. })
  1248. }).catch(err => {})
  1249. },
  1250. getLink() {
  1251. let that = this;
  1252. getRealLink({sortLink:this.sortLink}).then(res=>{
  1253. if(res.code == 200) {
  1254. this.isExpire = false
  1255. if(this.isLogin) {
  1256. this.getUserPhoneDeviceType()
  1257. }
  1258. // 如果响应中包含真实链接,则跳转到真实链接
  1259. // window.location.href = res.realLink +"&sortLink="+this.sortLink+"&code="+this.code+"&time="+new Date().getTime()
  1260. if (this.isLogin && this.isAddKf == 1&&this.isCheckRealUrl) {
  1261. this.getH5CourseVideoDetails()
  1262. }
  1263. if (this.videoId &&(this.isAddKf != 1 || !this.isCheckRealUrl)) {
  1264. this.utils.isLoginCourse().then(
  1265. isLogin => {
  1266. this.isLogin = isLogin
  1267. if(isLogin){
  1268. this.getIsAddKf()
  1269. this.getVideoRewardTypes()
  1270. } else {
  1271. this.goLogin()
  1272. }
  1273. },
  1274. rej => {}
  1275. );
  1276. }
  1277. } else {
  1278. this.isExpire = true
  1279. uni.showToast({
  1280. title: '课程已过期或链接无效',
  1281. icon: 'none'
  1282. });
  1283. }
  1284. }).catch(err=>{
  1285. this.isExpire = true
  1286. uni.showToast({
  1287. title: '发生错误,请稍后再试',
  1288. icon: 'none'
  1289. });
  1290. })
  1291. },
  1292. /**
  1293. * 节流原理:在一定时间内,只能触发一次
  1294. *
  1295. * @param {Function} func 要执行的回调函数
  1296. * @param {Number} wait 延时的时间
  1297. * @param {Boolean} immediate 是否立即执行
  1298. * @return null
  1299. */
  1300. throttle(func, wait = 500, immediate = true) {
  1301. if (immediate) {
  1302. if (!this.flag) {
  1303. this.flag = true
  1304. // 如果是立即执行,则在wait毫秒内开始时执行
  1305. typeof func === 'function' && func()
  1306. this.timer = setTimeout(() => {
  1307. this.flag = false
  1308. }, wait)
  1309. }
  1310. } else if (!this.flag) {
  1311. this.flag = true
  1312. // 如果是非立即执行,则在wait毫秒内的结束处执行
  1313. this.timer = setTimeout(() => {
  1314. this.flag = false
  1315. typeof func === 'function' && func()
  1316. }, wait)
  1317. }
  1318. },
  1319. // 弹幕
  1320. openDanmu(type) {
  1321. this.openDanmuType = type
  1322. this.inputText = ''
  1323. if(type == 1) {
  1324. this.player.exitFullScreen()
  1325. }
  1326. this.$refs.danmuPopup.open()
  1327. },
  1328. changeShowPopup(val) {
  1329. this.focus = val.show
  1330. },
  1331. switchDanmu() {
  1332. this.showDanmu = this.showDanmu == 1 ? 0:1
  1333. if(this.showDanmu == 0&&this.$refs.danmuBox) {
  1334. this.$refs.danmuPopup.close()
  1335. this.activeDanmus = []
  1336. this.$refs.danmuBox.activeDanmus = []
  1337. this.$refs.danmuBox.initTracks()
  1338. }
  1339. },
  1340. getScrollTop(res) {
  1341. if(this.currentTab == 2) {
  1342. this.scrollTop = res
  1343. } else {
  1344. this.scrollTop = 0
  1345. }
  1346. },
  1347. handleTab(index) {
  1348. this.currentTab = index
  1349. if(this.currentTab==2) {
  1350. if(this.$refs.commentBox) {
  1351. this.$refs.commentBox.msgs = []
  1352. this.$refs.commentBox.pageNum = 1
  1353. this.$refs.commentBox.getComments()
  1354. }
  1355. } else {
  1356. setTimeout(()=>{
  1357. this.scrollTop = 0
  1358. },100)
  1359. }
  1360. },
  1361. handleRefresher() {
  1362. this.triggered = true;
  1363. if (!this.isMore&&this.currentTab==2&&this.openCommentStatus==1) {
  1364. this.$nextTick(()=>{
  1365. this.$refs.commentBox&&this.$refs.commentBox.getComments()
  1366. })
  1367. }
  1368. setTimeout(() => {
  1369. this.triggered = false;
  1370. }, 500);
  1371. },
  1372. getMore(val) {
  1373. this.triggered = false;
  1374. this.isMore = val == 1
  1375. },
  1376. handleChatInput() {
  1377. this.inputText = this.inputText.trim()
  1378. if (this.inputText == "" || this.inputText.trim() == "") {
  1379. uni.showToast({
  1380. title: '请输入评论',
  1381. icon: "none"
  1382. })
  1383. return;
  1384. }
  1385. if(this.openCommentStatus==1) {
  1386. this.$refs.commentBox&&this.$refs.commentBox.handleInput(this.inputText)
  1387. } else if(this.openCommentStatus==2) {
  1388. this.$refs.danmuBox&&this.$refs.danmuBox.handleInput(this.inputText)
  1389. }
  1390. },
  1391. setInputText() {
  1392. this.inputText = ""
  1393. if(this.openCommentStatus==2) {
  1394. this.$refs.danmuPopup.close()
  1395. }
  1396. },
  1397. getActiveDanmus(val) {
  1398. this.activeDanmus = val.map(item=>({
  1399. ...item,
  1400. danmustyle: {
  1401. top: item.top + 'px',
  1402. ...item.style,
  1403. 'animation-duration': '8s'
  1404. }
  1405. }))
  1406. },
  1407. animationend(moveItem, i) {
  1408. // 移除动画结束的弹幕(性能优化)
  1409. if(this.openCommentStatus==2) {
  1410. this.$refs.danmuBox&&this.$refs.danmuBox.animationend(moveItem, i)
  1411. }
  1412. },
  1413. feedback() {
  1414. const userId = this.user.userId || ''
  1415. const courseId = this.urlOption.courseId || ''
  1416. const videoId = this.urlOption.videoId || ''
  1417. uni.navigateTo({
  1418. url: './feedback?userId='+userId+'&courseId='+courseId+'&videoId='+videoId
  1419. })
  1420. },
  1421. getBox(data) {
  1422. if(data.fsUserCourseRewardRoundStatusVO&&data.fsUserCourseRewardRoundStatusVO.length>0) {
  1423. const list = data.fsUserCourseRewardRoundStatusVO.map(item=>({
  1424. ...item,
  1425. time: Math.round(this.duration * Number(item.question || 0) * 0.01)
  1426. }))
  1427. return list
  1428. } else {
  1429. return []
  1430. }
  1431. },
  1432. claimFun(val) {
  1433. claim({
  1434. ...this.urlOption,
  1435. second: val.time,
  1436. rewardId: val.rewardId,
  1437. status: val.status
  1438. }).then(res=>{
  1439. if(res.code == 200) {
  1440. this.$refs.treasureChest.claimSuccess(val.status)
  1441. } else {
  1442. this.$refs.treasureChest.close()
  1443. }
  1444. if(res.msg!="success") {
  1445. uni.showToast({
  1446. title: res.msg,
  1447. icon: 'none',
  1448. duration: 3000
  1449. })
  1450. }
  1451. })
  1452. },
  1453. showBtnType(value) {
  1454. this.showBtn = value
  1455. this.getHeight()
  1456. },
  1457. getUserPhoneDeviceType(){
  1458. uni.getSystemInfo({
  1459. success: (result) => {
  1460. const param = {
  1461. os: result.osName || '',
  1462. deviceId: result.deviceId || '',
  1463. appId: getApp().globalData.appId
  1464. }
  1465. getUserPhoneDeviceType({param:JSON.stringify(param)})
  1466. },
  1467. fail: (error) => {
  1468. console.log('获取型号失败',JSON.stringify(error))
  1469. }
  1470. })
  1471. },
  1472. initSocket(type) {
  1473. const userId = this.user.userId; // 假设你已经获取了用户ID
  1474. const that = this;
  1475. // 如果已经存在连接,先关闭
  1476. if (this.socket) {
  1477. this.socket.onClose(() => {
  1478. console.log('旧的 WebSocket 连接已完全关闭');
  1479. that.socket = null;
  1480. if(that.pingpangTimes) {
  1481. clearInterval(that.pingpangTimes)
  1482. that.pingpangTimes= null
  1483. }
  1484. that.createNewSocket(type,userId);
  1485. });
  1486. this.socket.close();
  1487. return;
  1488. }
  1489. // 创建一个新的 WebSocket 连接
  1490. this.createNewSocket(type,userId);
  1491. },
  1492. createNewSocket(type,userId) {
  1493. if(this.openCommentStatus == 3) return
  1494. //创建一个socket连接
  1495. const that = this;
  1496. // if (this.socket) {
  1497. // this.socket.close()
  1498. // }
  1499. this.socket = uni.connectSocket({
  1500. url: getApp().globalData.wsUrl + "/app/webSocket/" + userId,
  1501. multiple: true,
  1502. success: res => {
  1503. that.isSocketOpen = true
  1504. console.log('WebSocket连接已打开1!');
  1505. // 保持心跳
  1506. if(that.pingpangTimes) {
  1507. clearInterval(that.pingpangTimes)
  1508. that.pingpangTimes= null
  1509. }
  1510. that.pingpangTimes=setInterval(()=>{
  1511. let data={
  1512. userId: userId || '',
  1513. userType: 2, // 1-管理员,2-用户
  1514. courseId: that.urlOption.courseId,
  1515. videoId: that.urlOption.videoId,
  1516. type:1, // 评论类型 1:评论,2:回复,目前没有回复,默认传1就行了
  1517. // msg: that.inputText,
  1518. cmd:'heartbeat'
  1519. };
  1520. that.socket.send({
  1521. data: JSON.stringify(data),
  1522. success: () => {
  1523. // console.log('WebSocket发送心条数据!');
  1524. },
  1525. fail: () => {
  1526. that.isSocketOpen=false
  1527. }
  1528. });
  1529. },15000)
  1530. },
  1531. error: res => {
  1532. console.log(res)
  1533. },
  1534. })
  1535. this.socket.onMessage((res) => {
  1536. // console.log("收到消息parse",JSON.parse(res.data))
  1537. const redata = JSON.parse(res.data);
  1538. if(redata.cmd=="heartbeat"){
  1539. //心跳
  1540. // console.log("heartbeat")
  1541. }else if(redata.cmd=="sendMsg"){
  1542. if(that.openCommentStatus==1) {
  1543. that.$refs.commentBox.isSend=true;
  1544. that.$refs.commentBox.addMsg(redata);
  1545. } else if(that.openCommentStatus==2) {
  1546. that.$refs.danmuBox.isSend=true;
  1547. that.$refs.danmuBox.addMsg(redata);
  1548. }
  1549. // that.isSend=true;
  1550. // that.addMsg(redata);
  1551. }
  1552. })
  1553. //监听socket打开
  1554. this.socket.onOpen(() => {
  1555. that.isSocketOpen = true
  1556. console.log('WebSocket连接已打开2!');
  1557. // that.isSend = true;
  1558. if(that.openCommentStatus==1) {
  1559. that.$refs.commentBox.isSend=true;
  1560. } else if(that.openCommentStatus==2) {
  1561. that.$refs.danmuBox.isSend=true;
  1562. }
  1563. if(type=='reStart') {
  1564. // 重连的时候重新发消息
  1565. if(that.openCommentStatus==1) {
  1566. that.$refs.commentBox.saveMsg()
  1567. } else if(that.openCommentStatus==2) {
  1568. that.$refs.danmuBox.saveMsg()
  1569. }
  1570. // this.saveMsg()
  1571. }
  1572. })
  1573. //监听socket关闭
  1574. this.socket.onClose(() => {
  1575. that.isSocketOpen = false
  1576. that.socket = null
  1577. console.log('WebSocket连接已关闭!');
  1578. if(that.pingpangTimes) {
  1579. clearInterval(that.pingpangTimes)
  1580. that.pingpangTimes= null
  1581. }
  1582. })
  1583. //监听socket错误
  1584. this.socket.onError((err) => {
  1585. console.log("socket err:",err)
  1586. that.isSocketOpen = false
  1587. that.socket = null
  1588. if(that.pingpangTimes) {
  1589. clearInterval(that.pingpangTimes)
  1590. that.pingpangTimes= null
  1591. }
  1592. })
  1593. },
  1594. socketSend(data) {
  1595. let that = this
  1596. this.socket.send({
  1597. data: JSON.stringify(data),
  1598. success: () => {
  1599. console.log("发送成功")
  1600. // this.isSend = false;
  1601. if(that.openCommentStatus==1) {
  1602. that.$refs.commentBox.isSend = false;
  1603. } else if(that.openCommentStatus==2) {
  1604. that.$refs.danmuBox.isSend = false;
  1605. }
  1606. },
  1607. fail: () => {
  1608. console.log("发送失败")
  1609. }
  1610. });
  1611. },
  1612. closeSocket() {
  1613. if (this.socket) {
  1614. this.socket.close();
  1615. }
  1616. if (this.pingpangTimes) {
  1617. clearInterval(this.pingpangTimes);
  1618. this.pingpangTimes = null;
  1619. }
  1620. }
  1621. }
  1622. }
  1623. </script>
  1624. <style scoped>
  1625. .full-width-popup {
  1626. width: 100%;
  1627. }
  1628. </style>
  1629. <style lang="scss" scoped>
  1630. @mixin u-flex($flexD, $alignI, $justifyC) {
  1631. display: flex;
  1632. flex-direction: $flexD;
  1633. align-items: $alignI;
  1634. justify-content: $justifyC;
  1635. }
  1636. .reward-list {
  1637. display: flex;
  1638. align-items: center;
  1639. flex-wrap: wrap;
  1640. margin-top: 40rpx;
  1641. margin-bottom: -24rpx;
  1642. }
  1643. .reward-item{
  1644. padding: 16rpx 10rpx;
  1645. box-sizing: border-box;
  1646. border-radius: 10rpx;
  1647. width: calc(50% - 14rpx);
  1648. overflow: hidden;
  1649. text-align: center;
  1650. border-radius: 8rpx;
  1651. border: 1rpx solid #ededef;
  1652. // margin: 10rpx 5px;
  1653. margin-bottom: 24rpx;
  1654. color: #222;
  1655. background-color: #fff;
  1656. &:nth-child(odd) {
  1657. margin-right: 20rpx;
  1658. }
  1659. }
  1660. .reward-active {
  1661. border: 1rpx solid #ff5c03 !important;
  1662. color: #ff5c03 !important;
  1663. background: #FCF0E7 !important;
  1664. }
  1665. .footer-tips {
  1666. margin-top: 14rpx;
  1667. text-align: center;
  1668. font-family: PingFang SC,PingFang SC;
  1669. font-weight: 500;
  1670. font-size: 12px;
  1671. color: #bbb;
  1672. }
  1673. .textOne {
  1674. overflow: hidden;
  1675. white-space: nowrap;
  1676. text-overflow: ellipsis;
  1677. }
  1678. .textTwo {
  1679. overflow: hidden;
  1680. text-overflow: ellipsis;
  1681. display: -webkit-box;
  1682. -webkit-line-clamp: 2;
  1683. -webkit-box-orient: vertical;
  1684. }
  1685. .header-nav {
  1686. height: 88rpx;
  1687. @include u-flex(row, center, flex-start);
  1688. overflow: hidden;
  1689. background-color: #fff;
  1690. box-sizing: border-box;
  1691. .header-title {
  1692. text-align: center;
  1693. overflow: hidden;
  1694. white-space: nowrap;
  1695. text-overflow: ellipsis;
  1696. padding: 0 10rpx 0 100rpx;
  1697. font-family: PingFang SC,PingFang SC;
  1698. font-weight: 500;
  1699. font-size: 15px;
  1700. color: #000;
  1701. box-sizing: border-box;
  1702. }
  1703. }
  1704. .reward-list {
  1705. width: 100%;
  1706. // margin-top: 20rpx;
  1707. // margin-bottom: -40rpx;
  1708. &-group {
  1709. font-family: PingFang SC, PingFang SC;
  1710. font-weight: 400;
  1711. font-size: 14px;
  1712. color: #222222;
  1713. @include u-flex(row, center, center);
  1714. }
  1715. &-option {
  1716. @include u-flex(row, center, flex-start);
  1717. &:first-child {
  1718. margin-right: 40rpx;
  1719. }
  1720. }
  1721. }
  1722. .err {
  1723. color: #f56c6c !important;
  1724. }
  1725. .kfqrcode-box {
  1726. background-color: #fff;
  1727. border-radius: 16rpx;
  1728. max-width: 560rpx;
  1729. padding: 60rpx 40rpx;
  1730. margin-top: -100rpx;
  1731. box-sizing: border-box;
  1732. @include u-flex(column, center, flex-start);
  1733. font-family: PingFang SC, PingFang SC;
  1734. font-weight: 400;
  1735. font-size: 34rpx;
  1736. color: #222;
  1737. position: relative;
  1738. text-align: center;
  1739. .kfqrcode {
  1740. height: 460rpx;
  1741. width: 460rpx;
  1742. }
  1743. }
  1744. .kfqrcode-close {
  1745. width: 64rpx;
  1746. height: 64rpx;
  1747. position: absolute;
  1748. bottom: -100rpx;
  1749. left: 50%;
  1750. transform: translateX(-50%);
  1751. }
  1752. .tipsPopup-mask {
  1753. position: relative;
  1754. width: 560rpx;
  1755. background-color: transparent;
  1756. .red_envelope_top {
  1757. width: 480rpx;
  1758. height: 360rpx;
  1759. margin: 0 auto;
  1760. display: inherit;
  1761. }
  1762. }
  1763. .tipsPopup-btn-box {
  1764. width: 456rpx;
  1765. height: 104rpx;
  1766. padding: 4rpx;
  1767. box-sizing: border-box;
  1768. background: linear-gradient(180deg, rgba(252, 209, 94, 1), rgba(254, 253, 251, 1));
  1769. border-radius: 52rpx;
  1770. }
  1771. .tipsPopup-btn {
  1772. width: 100%;
  1773. height: 100%;
  1774. background: linear-gradient(180deg, #FF9F22 0%, #FA1E05 100%);
  1775. border-radius: 52rpx 52rpx 52rpx 52rpx;
  1776. font-family: PingFang SC, PingFang SC;
  1777. font-weight: 500;
  1778. font-size: 36rpx;
  1779. color: #FFFFFF;
  1780. line-height: 96rpx;
  1781. text-align: center;
  1782. }
  1783. .tipsPopup {
  1784. width: 560rpx;
  1785. padding: 12rpx;
  1786. margin-top: -72rpx;
  1787. box-sizing: border-box;
  1788. background: linear-gradient(180deg, #FFFBEF 0%, #FFFFF5 43%, #F5EAC2 100%);
  1789. border-radius: 32rpx 32rpx 32rpx 32rpx;
  1790. position: relative;
  1791. &-close {
  1792. width: 64rpx;
  1793. height: 64rpx;
  1794. position: absolute;
  1795. right: 0;
  1796. top: -188rpx;
  1797. }
  1798. &-line {
  1799. padding: 3rpx;
  1800. box-sizing: border-box;
  1801. background: linear-gradient(180deg, rgba(247, 245, 220, 1), rgba(250, 220, 157, 1));
  1802. border-radius: 24rpx;
  1803. }
  1804. &-box {
  1805. padding: 0 40rpx 40rpx 40rpx;
  1806. box-sizing: border-box;
  1807. background: linear-gradient(180deg, #FFFBEF 0%, #FFFFF5 43%, #F5EAC2 100%);
  1808. border-radius: 24rpx;
  1809. @include u-flex(column, center, flex-start);
  1810. }
  1811. &-head {
  1812. @include u-flex(row, center, center);
  1813. &-title {
  1814. width: 364rpx;
  1815. height: auto;
  1816. margin-top: -22rpx;
  1817. }
  1818. }
  1819. &-content {
  1820. margin: 48rpx 0;
  1821. font-family: PingFang SC, PingFang SC;
  1822. font-weight: 500;
  1823. font-size: 32rpx;
  1824. color: #222222;
  1825. text-align: center;
  1826. &-title {
  1827. margin-bottom: 26rpx;
  1828. font-weight: 600;
  1829. font-size: 40rpx;
  1830. color: #FF5C03;
  1831. }
  1832. }
  1833. }
  1834. .video-controls-box {
  1835. width: 100%;
  1836. height: 420rpx;
  1837. overflow: hidden;
  1838. position: absolute;
  1839. bottom: 0;
  1840. left: 0;
  1841. z-index: 2;
  1842. background: rgba(0, 0, 0, 0.2);
  1843. .video-play {
  1844. height: 72rpx;
  1845. width: 72rpx;
  1846. position: absolute;
  1847. top: 50%;
  1848. left: 50%;
  1849. transform: translate(-50%, -50%);
  1850. }
  1851. }
  1852. .video-controls {
  1853. width: 100%;
  1854. height: 80rpx;
  1855. padding: 0 28rpx;
  1856. box-sizing: border-box;
  1857. position: absolute;
  1858. bottom: 0;
  1859. left: 0;
  1860. display: flex;
  1861. align-items: center;
  1862. justify-content: space-between;
  1863. background: linear-gradient(to top, #222 0%, transparent 80%);
  1864. .video-icon {
  1865. height: 44rpx;
  1866. width: 44rpx;
  1867. }
  1868. }
  1869. .errQuesbox {
  1870. width: 100%;
  1871. max-height: 260rpx;
  1872. overflow-y: auto;
  1873. margin-top: 24rpx;
  1874. font-family: PingFang SC, PingFang SC;
  1875. font-weight: 500;
  1876. font-size: 30rpx;
  1877. color: #222222;
  1878. &-item {
  1879. width: 100%;
  1880. height: 128rpx;
  1881. line-height: 128rpx;
  1882. margin-bottom: 24rpx;
  1883. padding: 0 30rpx;
  1884. box-sizing: border-box;
  1885. overflow: hidden;
  1886. background: #fff;
  1887. border-radius: 16rpx 16rpx 16rpx 16rpx;
  1888. position: relative;
  1889. &::after {
  1890. content: "题目";
  1891. min-width: 64rpx;
  1892. height: 36rpx;
  1893. padding: 0 12rpx;
  1894. line-height: 36rpx;
  1895. background: #FF5C03;
  1896. box-sizing: border-box;
  1897. border-radius: 0rpx 0rpx 16rpx 0rpx;
  1898. text-align: center;
  1899. font-family: PingFang SC, PingFang SC;
  1900. font-weight: 500;
  1901. font-size: 20rpx;
  1902. color: #fff;
  1903. position: absolute;
  1904. left: 0;
  1905. top: 0;
  1906. }
  1907. }
  1908. }
  1909. .bg {
  1910. background: #fff !important;
  1911. }
  1912. .answerPopup {
  1913. &-box {
  1914. width: 560rpx;
  1915. background: linear-gradient(180deg, #FFFAF6 0%, #FEECD8 100%);
  1916. border-radius: 32rpx 32rpx 32rpx 32rpx;
  1917. background-color: #fff;
  1918. font-weight: 400;
  1919. padding: 32rpx;
  1920. box-sizing: border-box;
  1921. position: relative;
  1922. @include u-flex(column, center, flex-start);
  1923. font-family: PingFang SC, PingFang SC;
  1924. font-weight: 400;
  1925. .tipimg {
  1926. width: 206rpx;
  1927. height: 206rpx;
  1928. margin-bottom: 16rpx;
  1929. }
  1930. }
  1931. &-title {
  1932. font-weight: 600;
  1933. font-size: 36rpx;
  1934. color: #222222;
  1935. }
  1936. &-desc {
  1937. margin-top: 10rpx;
  1938. font-size: 28rpx;
  1939. color: #757575;
  1940. }
  1941. &-btn {
  1942. width: 464rpx;
  1943. height: 84rpx;
  1944. margin-top: 54rpx;
  1945. margin-bottom: 16rpx;
  1946. background: #FF5C03;
  1947. border-radius: 42rpx;
  1948. font-weight: 500;
  1949. font-size: 32rpx;
  1950. color: #FFFFFF;
  1951. text-align: center;
  1952. line-height: 84rpx;
  1953. }
  1954. }
  1955. .popupbox {
  1956. width: 100%;
  1957. background-color: #fff;
  1958. border-radius: 16rpx 16rpx 0 0;
  1959. padding: 24rpx 32rpx;
  1960. position: relative;
  1961. &-head {
  1962. height: 60rpx;
  1963. margin-bottom: 30rpx;
  1964. text-align: center;
  1965. overflow-y: auto;
  1966. color: #414858;
  1967. font-size: 32rpx;
  1968. font-weight: bold;
  1969. position: relative;
  1970. .close-icon {
  1971. position: absolute;
  1972. right: 0;
  1973. top: 0;
  1974. height: 40rpx;
  1975. width: 40rpx;
  1976. }
  1977. }
  1978. &-content {
  1979. height: 20vh;
  1980. overflow-y: auto;
  1981. display: flex;
  1982. align-items: flex-start;
  1983. flex-wrap: wrap;
  1984. gap: 32rpx;
  1985. .line-item {
  1986. display: inline-block;
  1987. min-width: 200rpx;
  1988. min-height: 60rpx;
  1989. padding: 0 20rpx;
  1990. box-sizing: border-box;
  1991. border-radius: 50rpx;
  1992. overflow: hidden;
  1993. background-color: #f7f7f7;
  1994. text-align: center;
  1995. color: #414858;
  1996. font-size: 28rpx;
  1997. line-height: 60rpx;
  1998. }
  1999. .line-active {
  2000. color: #f56c6c !important;
  2001. background-color: #fef0f0 !important;
  2002. }
  2003. }
  2004. }
  2005. .content {
  2006. padding-bottom: calc(var(--window-bottom));
  2007. .video-box {
  2008. width: 100%;
  2009. height: 420rpx;
  2010. overflow: hidden;
  2011. position: relative;
  2012. #myVideo {
  2013. width: 100%;
  2014. height: 100%;
  2015. }
  2016. }
  2017. .video-poster {
  2018. width: 100%;
  2019. height: 420rpx;
  2020. }
  2021. .miantitlebox {
  2022. padding: 30rpx 0;
  2023. border-bottom: 2rpx solid #F5F7FA;
  2024. font-family: PingFang SC, PingFang SC;
  2025. font-weight: 500;
  2026. font-size: 36rpx;
  2027. color: #222222;
  2028. }
  2029. .subtitlebox {
  2030. padding: 30rpx 0;
  2031. border-bottom: 2rpx solid #F5F7FA;
  2032. font-family: PingFang SC, PingFang SC;
  2033. font-weight: 500;
  2034. font-size: 36rpx;
  2035. color: #222222;
  2036. }
  2037. .title-content {
  2038. padding: 0 32rpx;
  2039. background-color: #fff;
  2040. font-size: 28rpx;
  2041. line-height: 1.6;
  2042. box-sizing: border-box;
  2043. @include u-flex(row, center, space-between);
  2044. .title {
  2045. font-size: 36rpx;
  2046. font-weight: 500;
  2047. color: #414858;
  2048. }
  2049. .time-or-subtitle {
  2050. margin-top: 12rpx;
  2051. color: #666666;
  2052. }
  2053. }
  2054. .warning {
  2055. flex-shrink: 0;
  2056. color: #888;
  2057. font-size: 24rpx;
  2058. @include u-flex(column, center, center);
  2059. image {
  2060. flex-shrink: 0;
  2061. height: 36rpx;
  2062. width: 36rpx;
  2063. }
  2064. }
  2065. .video-line {
  2066. min-width: 140rpx;
  2067. max-width: 200rpx;
  2068. height: 60rpx;
  2069. padding: 0 20rpx;
  2070. box-sizing: border-box;
  2071. border-radius: 50rpx 0 0 50rpx;
  2072. overflow: hidden;
  2073. background-color: #fff;
  2074. text-align: center;
  2075. color: #888;
  2076. font-size: 28rpx;
  2077. line-height: 60rpx;
  2078. display: inline-flex;
  2079. align-items: center;
  2080. justify-content: center;
  2081. position: fixed;
  2082. right: 0;
  2083. z-index: 9;
  2084. bottom: calc(var(--window-bottom) + 280rpx);
  2085. box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, .12);
  2086. image {
  2087. flex-shrink: 0;
  2088. height: 34rpx;
  2089. width: 34rpx;
  2090. margin-right: 6rpx;
  2091. }
  2092. }
  2093. .danmu-line {
  2094. bottom: calc(var(--window-bottom) + 370rpx);
  2095. word-break: keep-all;
  2096. .set_image {
  2097. height: 40rpx;
  2098. width: 40rpx;
  2099. }
  2100. }
  2101. .footer {
  2102. border-top: 1rpx solid #ededef;
  2103. background: #fff;
  2104. width: 100%;
  2105. position: fixed;
  2106. bottom: 0;
  2107. // padding: 32rpx;
  2108. padding-bottom: calc(var(--window-bottom));
  2109. box-sizing: border-box;
  2110. z-index: 9;
  2111. &-btn {
  2112. width: calc(100% - 64rpx);
  2113. height: 98rpx;
  2114. background: #FF5C03;
  2115. border-radius: 49rpx 49rpx 49rpx 49rpx;
  2116. line-height: 98rpx;
  2117. text-align: center;
  2118. font-family: PingFang SC, PingFang SC;
  2119. font-weight: 600;
  2120. font-size: 32rpx;
  2121. color: #FFFFFF;
  2122. @include u-flex(row, center, center);
  2123. margin: 24rpx 32rpx;
  2124. box-sizing: border-box;
  2125. &-img {
  2126. flex-shrink: 0;
  2127. width: 144rpx;
  2128. height: 144rpx;
  2129. margin-right: 8rpx;
  2130. margin-top: -24rpx;
  2131. }
  2132. }
  2133. &-btn-border {
  2134. position: relative;
  2135. &::after {
  2136. content: "";
  2137. background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
  2138. position: absolute;
  2139. top: -2rpx;
  2140. left: 0;
  2141. height: 103rpx;
  2142. width: 100%;
  2143. z-index: -1;
  2144. border-radius: 49rpx 49rpx 49rpx 49rpx;
  2145. box-shadow: 0rpx 8rpx 11rpx 0rpx rgba(255, 92, 3, 0.3);
  2146. overflow: hidden;
  2147. }
  2148. }
  2149. }
  2150. }
  2151. .agreement {
  2152. display: inline-flex;
  2153. margin-top: 16rpx;
  2154. font-size: 24rpx;
  2155. color: #525252;
  2156. align-items: center;
  2157. justify-content: center;
  2158. }
  2159. .video-danmu-btnbox {
  2160. width: 50px;
  2161. height: 50px;
  2162. border-radius: 50%;
  2163. overflow: hidden;
  2164. position: absolute;
  2165. right: 10px;
  2166. bottom: calc(50% - 50px);
  2167. transform: translateY(-50%);
  2168. padding: 8px;
  2169. box-sizing: border-box;
  2170. }
  2171. .video-danmu-image {
  2172. width: 100%;
  2173. height: 100%;
  2174. }
  2175. .danmuPopup {
  2176. background-color: #fff;
  2177. padding-bottom: calc(var(--window-bottom) + 10px);
  2178. .u-border {
  2179. flex: 1;
  2180. @include u-flex(row,center,flex-start);
  2181. padding: 0 6rpx;
  2182. border-radius: 6px;
  2183. }
  2184. &-head {
  2185. width: 100%;
  2186. padding: 10px;
  2187. box-sizing: border-box;
  2188. overflow: hidden;
  2189. @include u-flex(row,center,flex-start);
  2190. .danmu-icon {
  2191. height: 24px;
  2192. width: 24px;
  2193. margin-right: 10px;
  2194. }
  2195. }
  2196. &-input {
  2197. flex: 1;
  2198. height: 35px;
  2199. }
  2200. &-send {
  2201. flex-shrink: 0;
  2202. height: 35px;
  2203. display: flex;
  2204. align-items: center;
  2205. justify-content: center;
  2206. padding: 5px 15px;
  2207. box-sizing: border-box;
  2208. background: #FF5C03 !important;
  2209. border-radius: 6px;
  2210. font-family: PingFang SC, PingFang SC;
  2211. font-weight: 500;
  2212. font-size: 15px;
  2213. color: #fff !important;
  2214. margin-left: 12px;
  2215. &::after {
  2216. border: none;
  2217. }
  2218. }
  2219. &-con {
  2220. background-color: #F5F7FA;
  2221. padding: 24px 12px 48px 12px;
  2222. font-family: PingFang SC, PingFang SC;
  2223. font-weight: 400;
  2224. font-size: 14px;
  2225. color: #757575;
  2226. }
  2227. }
  2228. .danmu-icon{
  2229. height: 24px;
  2230. width: 24px;
  2231. margin-right: 12px;
  2232. }
  2233. .logo {
  2234. display: inline-block;
  2235. width: 30px;
  2236. height: auto;
  2237. margin: 20px 0 0 10px;
  2238. pointer-events: none;
  2239. object-fit: cover;
  2240. }
  2241. .logo-full {
  2242. display: inline-block;
  2243. width: 40px;
  2244. height: auto;
  2245. margin: 50px 0 0 30px;
  2246. pointer-events: none;
  2247. object-fit: cover;
  2248. }
  2249. .tabbox-bar {
  2250. @include u-flex(row, center, flex-start);
  2251. background-color: #fff;
  2252. .warning {
  2253. flex-shrink: 0;
  2254. padding-right: 20rpx;
  2255. }
  2256. }
  2257. .tabbox {
  2258. flex: 1;
  2259. @include u-flex(row, center, center);
  2260. border-bottom: 2rpx solid #F5F7FA;
  2261. height: 44px;
  2262. background-color: #fff;
  2263. view {
  2264. flex: 1;
  2265. padding: 20rpx 0;
  2266. margin-right: 40rpx;
  2267. text-align: center;
  2268. }
  2269. &-active {
  2270. position: relative;
  2271. &::after {
  2272. position: absolute;
  2273. bottom: 0;
  2274. left: 50%;
  2275. transform: translateX(-50%);
  2276. content: "";
  2277. width: 3rem;
  2278. border-bottom: 4px solid #FF5C03;
  2279. }
  2280. }
  2281. }
  2282. .chatinput {
  2283. position: fixed;
  2284. left: 32rpx;
  2285. right: 32rpx;
  2286. z-index: 999;
  2287. height: 96rpx;
  2288. background-color: green;
  2289. background: #FFFFFF;
  2290. box-shadow: 0rpx 8rpx 21rpx 0rpx rgba(0, 0, 0, 0.1);
  2291. border-radius: 24rpx 24rpx 24rpx 24rpx;
  2292. @include u-flex(row, center, center);
  2293. padding: 0 24rpx;
  2294. box-sizing: border-box;
  2295. .uni-input {
  2296. flex: 1;
  2297. margin-right: 32rpx;
  2298. font-size: 30rpx;
  2299. }
  2300. .send {
  2301. font-family: PingFang SC, PingFang SC;
  2302. font-weight: 400;
  2303. font-size: 28rpx;
  2304. color: #FFFFFF !important;
  2305. flex-shrink: 0;
  2306. padding: 0 20rpx;
  2307. height: 72rpx;
  2308. background: #FF5C03 !important;
  2309. border-radius: 8rpx 8rpx 8rpx 8rpx;
  2310. &::after {
  2311. border: none;
  2312. }
  2313. }
  2314. }
  2315. .answerTip {
  2316. position: fixed;
  2317. right: 0;
  2318. z-index: 9;
  2319. bottom: calc(var(--window-bottom) + 380rpx);
  2320. box-shadow: 0rpx 8rpx 21rpx 0rpx rgba(0, 0, 0, 0.1);
  2321. border-radius: 24rpx 24rpx 24rpx 24rpx;
  2322. background-color: #ff5c03;
  2323. color: #fff;
  2324. border-radius: 50%;
  2325. height: 100rpx;
  2326. width: 100rpx;
  2327. font-size: 25rpx;
  2328. text-align: center;
  2329. padding: 10rpx;
  2330. @include u-flex(row, center, center);
  2331. }
  2332. .danmu-item {
  2333. position: absolute;
  2334. top: 0;
  2335. white-space: nowrap;
  2336. font-size: 16px;
  2337. height: 20px;
  2338. display: inline-flex;
  2339. box-sizing: border-box;
  2340. align-items: center;
  2341. }
  2342. .danmuMove {
  2343. // animation: mymove 8s linear forwards;
  2344. // animation-duration: 8s;
  2345. animation-timing-function: linear;
  2346. animation-delay: 0s;
  2347. animation-iteration-count: 1;
  2348. animation-direction: normal;
  2349. animation-fill-mode: forwards;
  2350. animation-play-state: running;
  2351. animation-name: mymove;
  2352. will-change: transform;
  2353. }
  2354. @keyframes mymove {
  2355. from {
  2356. transform: translateX(100vw);
  2357. }
  2358. to {
  2359. transform: translateX(-100%);
  2360. }
  2361. }
  2362. .arrow-left-warning {
  2363. position: absolute;
  2364. left: 24rpx;
  2365. height: 88rpx;
  2366. overflow: hidden;
  2367. color: #888;
  2368. font-size: 24rpx;
  2369. @include u-flex(column, center, center);
  2370. image {
  2371. flex-shrink: 0;
  2372. height: 36rpx;
  2373. width: 36rpx;
  2374. }
  2375. }
  2376. </style>