acfunlivedanmake

acfunlivedanmaku

  1. // ==UserScript==
  2. // @name acfunlivedanmake
  3. // @description acfunlivedanmaku
  4. // @namespace syachiku
  5. // @author syachiku
  6. // @match https://live.acfun.cn/live/*
  7. // @run-at document-end
  8. // @grant GM_addStyle
  9. // @grant GM_getResourceURL
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @version 1.1.0
  14. // @require https://cdn.jsdelivr.net/npm/qs@6.9.4/dist/qs.min.js
  15. // @require https://cdn.jsdelivr.net/npm/uuid@8.3.2/dist/umd/uuidv4.min.js
  16. // @require https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js
  17. // @require https://cdn.jsdelivr.net/npm/moment@2.18.1/min/moment.min.js
  18. // @require https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js
  19. // @require https://cdn.jsdelivr.net/npm/element-ui@2.15.1/lib/index.js
  20. // @require https://cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/FileSaver.min.js
  21. // @require https://cdn.jsdelivr.net/npm/exceljs@4.2.1/dist/exceljs.min.js
  22. // @require https://cdn.jsdelivr.net/npm/clipboard-polyfill@2.5.4/build/clipboard-polyfill.min.js
  23. // @require https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
  24. // @require https://cdn.jsdelivr.net/npm/keymaster@1.6.2/keymaster.min.js
  25. // @require https://cdn.jsdelivr.net/npm/easy-danmaku-js@1.0.6/demo/easy-Danmaku.min.js
  26. // @require https://cdn.jsdelivr.net/npm/jquery-countdown@2.2.0/dist/jquery.countdown.min.js
  27. // @require https://cdn.jsdelivr.net/npm/hotkeys-js@3.7.3/dist/hotkeys.min.js
  28. // ==/UserScript==
  29.  
  30.  
  31.  
  32. ;(async function(){
  33.  
  34. const config = {
  35. UID : parseInt(window.location.href.split('/').pop()),
  36. TOKEN : null,
  37. ACFUN_SERVER : 'https://www.acfun.cn',
  38. ACFUNLIVE_SERVER : 'https://live.acfun.cn',
  39. URLS : {
  40. USER : {
  41. INFO : '/user/info',
  42. },
  43. ACFUN_USER : {
  44. VUP : '/user/acfun_user_info_simplified',
  45. INFO : '/rest/pc-direct/user/userInfo',
  46. SPACE : '/u'
  47. },
  48. DOUGA : {
  49. COMMENT : '/rest/pc-direct/comment/list',
  50. VIDEO : '/v',
  51. ARTICLE : '/a',
  52. BANGUMI : '/bangumi'
  53. },
  54. },
  55. RESPONSE : {
  56. FIELD : {
  57. STATUS : 'code',
  58. MSG : 'message',
  59. DATA : 'data',
  60. },
  61. STATUS : {
  62. SUCCESS : 200,
  63. }
  64. }
  65. };
  66. const css = `
  67. /* base html */
  68. #header,#footer,
  69. .main>.list-container.outer-wrapper,
  70. .main>.list-container.outer-wrapper:before,
  71. .container-live>.left,
  72. .live-feed-watching,
  73. .container-live-feed-messages:before
  74. {display:none!important}
  75. #app>.main,
  76. .player-outer-wrapper,
  77. .player-outer-wrapper>.container-live,
  78. .player-outer-wrapper>.container-live>.container-live-feed.right,
  79. .player-outer-wrapper>.container-live>.container-live-feed.right>.live-feed,
  80. .player-outer-wrapper>.container-live>.container-live-feed.right>.live-feed>.container-live-feed-messages,
  81. .player-outer-wrapper>.container-live>.container-live-feed.right>.live-feed>.container-live-feed-messages>.live-feed-messages
  82. {
  83. display:block!important;
  84. position:fixed!important;
  85. width:100%!important;
  86. height:100%!important;
  87. margin:0!important;
  88. padding:0!important;
  89. border:0!important;
  90. top:0!important;
  91. bottom:0!important;
  92. word-break:break-all!important;
  93. }
  94. .live-feed-input{
  95. display : none !important;
  96. }
  97. @keyframes scaleDraw { /*定义关键帧、scaleDrew是需要绑定到选择器的关键帧名称*/
  98. 0%{
  99. transform: scale(1); /*开始为原始大小*/
  100. }
  101. 25%{
  102. transform: scale(1.5); /*放大1.1倍*/
  103. }
  104. 50%{
  105. transform: scale(1);
  106. }
  107. 75%{
  108. transform: scale(1.5);
  109. }
  110. }
  111. .scale-anime{
  112. -webkit-animation: scaleDraw 5s ease-in-out;
  113. }
  114. .container-live-feed-messages-acfunlive:before{
  115. display:none!important;
  116. }
  117. .container-live-feed-messages-acfunlive{
  118. display:block!important;
  119. position:fixed!important;
  120. width:100%!important;
  121. height:100%!important;
  122. margin:0!important;
  123. padding:0!important;
  124. border:0!important;
  125. top:0!important;
  126. bottom:0!important;
  127. word-break:break-all!important;
  128. }
  129. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-vertical{
  130. height: 100%;
  131. overflow-y: auto;
  132. }
  133. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-horizontal{
  134. width: 100%;
  135. height: 100%;
  136. overflow-x: auto;
  137. overflow-y: hidden;
  138. display : flex;
  139. flex-direction: column;
  140. flex-wrap: wrap;
  141. }
  142. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-vertical.active::-webkit-scrollbar{
  143. width: 10px;
  144. height: 1px;
  145. }
  146. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-vertical.inactive::-webkit-scrollbar{
  147. display:none;
  148. }
  149. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-vertical::-webkit-scrollbar-thumb {
  150. border-radius: 10px;
  151. -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
  152. background: #535353;
  153. }
  154. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-vertical::-webkit-scrollbar-track {
  155. -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
  156. border-radius: 10px;
  157. background: #EDEDED;
  158. }
  159. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-horizontal.active::-webkit-scrollbar{
  160. height: 10px;
  161. width: 1px;
  162. }
  163. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-horizontal.inactive::-webkit-scrollbar{
  164. display:none;
  165. }
  166. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-horizontal::-webkit-scrollbar-thumb {
  167. border-radius: 10px;
  168. -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
  169. background: #535353;
  170. }
  171. .container-live-feed-messages-acfunlive .live-message-container.live-message-container-horizontal::-webkit-scrollbar-track {
  172. -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
  173. border-radius: 10px;
  174. background: #EDEDED;
  175. }
  176. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup{
  177. display:flex;
  178. padding: 7px;
  179. width: 100%;
  180. }
  181. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-popup-right{
  182. margin-left : 5px;
  183. width: 90%;
  184. }
  185. @keyframes clippath{
  186. 0%, 100% {clip-path: inset(0 0 100% 0);}
  187. 25% {clip-path: inset(0 100% 0 0);}
  188. 50% {clip-path: inset(100% 0 0 0);}
  189. 75% {clip-path: inset(0 0 0 100%);}
  190. }
  191. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-border-flow{
  192. position: relative;
  193. }
  194. .live-message-border-flow .border-flow-item::before,.live-message-border-flow .border-flow-item::after{
  195. content: "";
  196. position: absolute;
  197. transition: all .5s;
  198. animation: clippath 3s infinite linear;
  199. }
  200. .live-message-border-flow .border-flow-item::after{
  201. animation: clippath 3s -1.5s infinite linear;
  202. }
  203. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku{
  204. margin-top: 3px;
  205. padding: 5px 12px 5px 12px;
  206. box-sizing: border-box;
  207. word-break: break-all;
  208. cursor: pointer;
  209. }
  210. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga{
  211. margin-top: 10px;
  212. }
  213. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .el-card{
  214. background: transparent;
  215. border: transparent;
  216. box-shadow: 0px 2px 12px rgba(0, 0, 0, 30%);
  217. border-radius: 0px;
  218. }
  219. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .el-card__body{
  220. padding: 10px;
  221. }
  222. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info{
  223. }
  224. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .douga-info-right{
  225. height : 100%;
  226. display:flex;
  227. flex-direction:column;
  228. justify-content:space-between;
  229. }
  230. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .cover{
  231. display:inline-block;
  232. border-radius : 4px;
  233. }
  234. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .cover >img{
  235. object-fit:cover;
  236. display: block;
  237. height: 100%;
  238. vertical-align: middle;
  239. }
  240. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .title-container{
  241. overflow: hidden;
  242. text-overflow: ellipsis;
  243. display: -webkit-box;
  244. -webkit-line-clamp: 3;
  245. -webkit-box-orient: vertical;
  246. }
  247. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info.article .title{
  248. overflow: hidden;
  249. text-overflow: ellipsis;
  250. display: -webkit-box;
  251. -webkit-line-clamp: 1;
  252. -webkit-box-orient: vertical;
  253. }
  254. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info.article .description{
  255. overflow: hidden;
  256. text-overflow: ellipsis;
  257. display: -webkit-box;
  258. -webkit-line-clamp: 2;
  259. -webkit-box-orient: vertical;
  260. filter : invert(100%);
  261. }
  262. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .up{
  263. overflow: hidden;
  264. text-overflow: ellipsis;
  265. display: -webkit-box;
  266. -webkit-line-clamp: 1;
  267. -webkit-box-orient: vertical;
  268. }
  269. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-danmaku .live-message-douga .douga-info .play-count{
  270. }
  271. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-gift{
  272. width: max-content;
  273. display: table-cell;
  274. margin-top: 3px;
  275. padding: 5px 12px 5px 12px;
  276. box-sizing: border-box;
  277. word-break: break-all;
  278. }
  279. .container-live-feed-messages-acfunlive .live-message-container .live-message-popup .live-message-gift .live-message-gift-count{
  280. pointer-events: none;
  281. display: inline-block;
  282. }
  283. .user-info-badge{
  284. border-radius: 4px;
  285. padding-left: 4px;
  286. padding-right: 4px;
  287. color: white;
  288. }
  289. .user-info-badge-green{
  290. background-image: linear-gradient(30deg, #14bd5a, #40E584, #01b54c);
  291. }
  292. .user-info-badge-blue{
  293. background-image: linear-gradient(30deg, #32A9DE, #4fbdef, #0e9ada);
  294. }
  295. .user-info-badge-orange{
  296. background-image: linear-gradient(30deg, #eb9f19, #e6a01b, #f19b00);
  297. }
  298. .user-info-badge-red{
  299. background-image: linear-gradient(30deg, #d43535, #e25537, #e94a47);
  300. }
  301. .user-info-badge-purple{
  302. background-image: linear-gradient(30deg, #4801FF, #7918F2, #AC32E4);
  303. }
  304. .user-info-badge-black{
  305. background-image: linear-gradient(30deg, #5e659d, #5e659d, #3c4270);
  306. }
  307. .user-info-badge-vup{
  308. background-image: linear-gradient(30deg, #32A9DE, #4fbdef, #0e9ada);
  309. }
  310. .user-info-username{
  311. }
  312. .container-live-feed-messages-acfunlive .live-message-container .live-message-inline{
  313. text-align : center;
  314. display: -webkit-box;
  315. -webkit-box-orient: vertical;
  316. -webkit-line-clamp: 1;
  317. overflow: hidden;
  318. }
  319. .container-live-feed-messages-acfunlive .live-feed-dropmenu.live-feed-dropmenu-horizontal{
  320. display:block;
  321. position:absolute;
  322. top : 50px;
  323. left : 50px;
  324. z-index : 999999;
  325. }
  326. .container-live-feed-messages-acfunlive .live-feed-dropmenu.live-feed-dropmenu-vertical{
  327. display:block;
  328. position:absolute;
  329. top : 50px;
  330. right : 50px;
  331. z-index : 999999;
  332. }
  333. /*.live-feed-input:hover{
  334. opacity : 1;
  335. }*/
  336. @font-face{
  337. font-family:element-icons;
  338. src:url('https://cdn.jsdelivr.net/npm/element-ui@2.15.1/lib/theme-chalk/fonts/element-icons.ttf');
  339. }
  340. .lottery-form-dialog {
  341. z-index : 999999;
  342. height: 400px;
  343. }
  344. .lottery-form-dialog .el-dialog__body{
  345. height : 80%;
  346. overflow-y: auto;
  347. }
  348. .lottery-form .gift-selector .el-select-dropdown__item{
  349. height : 40px;
  350. line-height : 40px;
  351. }
  352. .lottery-form .el-divider{
  353. margin-top : 40px;
  354. }
  355. .lottery-form .el-divider .el-divider__text{
  356. color : #409EFF;
  357. }
  358. .lottery-form .douga-list-wrapper{
  359. overflow:auto;
  360. height:200px;
  361. }
  362. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.video .douga-info{
  363. height : 100px;
  364. }
  365. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.article .douga-info{
  366. height : 60px;
  367. }
  368. .lottery-form .douga-list-wrapper .douga-list .douga-list-item .douga-info .douga-info-right{
  369. height : 100%;
  370. display:flex;
  371. flex-direction:column;
  372. justify-content:space-between;
  373. }
  374. .lottery-form .douga-list-wrapper .douga-list .douga-list-item .cover{
  375. display:inline-block;
  376. height:100px;
  377. width:177px;
  378. line-height:100px;
  379. border-radius : 4px;
  380. }
  381. .lottery-form .douga-list-wrapper .douga-list .douga-list-item .cover >img{
  382. object-fit:cover;
  383. display: block;
  384. height: 100%;
  385. vertical-align: middle;
  386. }
  387. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.video .title{
  388. height:60px;
  389. overflow: hidden;
  390. text-overflow: ellipsis;
  391. display: -webkit-box;
  392. -webkit-line-clamp: 3;
  393. -webkit-box-orient: vertical;
  394. font-weight : bold;
  395. }
  396. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.article .title{
  397. height:40px;
  398. overflow: hidden;
  399. text-overflow: ellipsis;
  400. display: -webkit-box;
  401. -webkit-line-clamp: 2;
  402. -webkit-box-orient: vertical;
  403. text-decoration : underline;
  404. font-weight : bold;
  405. }
  406. .lottery-form .douga-list-wrapper .douga-list .douga-list-item .el-card:hover{
  407. background-color : #409effb0;
  408. cursor:pointer;
  409. color : white;
  410. }
  411. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.active .el-timeline-item__tail{
  412. border-left: 2px solid #409EFF;
  413. }
  414. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.active .el-timeline-item__node{
  415. background-color : #409EFF;
  416. }
  417. .lottery-form .douga-list-wrapper .douga-list .douga-list-item.active .el-card{
  418. color : white;
  419. background-color : #409eff;
  420. }
  421. .lottery-table .lottery-status-finish{
  422. background: #8aec53;
  423. }
  424. .lottery-table-dialog{
  425. z-index : 999999;
  426. }
  427. .lottery-table-dialog .el-dialog__body{
  428. height : 80%;
  429. overflow-y: auto;
  430. }
  431. .user-table-dialog{
  432. z-index : 999999;
  433. }
  434. .user-table-dialog .el-dialog__body{
  435. height : 80%;
  436. overflow-y: auto;
  437. }
  438. .lottery-countdown-dialog{
  439. z-index : 999999;
  440. background-color : rgba(0,0,0,0) !important;
  441. box-shadow : none !important;
  442. }
  443. .lottery-countdown-dialog button[data-setter] {
  444. outline: none;
  445. background: transparent;
  446. border: none;
  447. font-family: 'Roboto';
  448. font-weight: 300;
  449. font-size: 18px;
  450. width: 25px;
  451. height: 30px;
  452. color: #409EFF;
  453. cursor: pointer;
  454. }
  455. .lottery-countdown-dialog button[data-setter]:hover {
  456. opacity: 0.5;
  457. }
  458. .lottery-countdown-dialog .container {
  459. position: relative;
  460. top: 30px;
  461. width: 300px;
  462. margin: 0 auto;
  463. }
  464. .lottery-countdown-dialog .setters {
  465. position: absolute;
  466. left: 85px;
  467. top: 75px;
  468. }
  469. .lottery-countdown-dialog .minutes-set {
  470. float: left;
  471. margin-right: 28px;
  472. }
  473. .lottery-countdown-dialog .seconds-set {
  474. float: right;
  475. }
  476. .lottery-countdown-dialog .controlls {
  477. position: absolute;
  478. left: 75px;
  479. top: 105px;
  480. text-align: center;
  481. }
  482. .lottery-countdown-dialog .display-remain-time {
  483. font-family: 'Roboto';
  484. font-weight: bold;
  485. font-size: 65px;
  486. color: #409EFF;
  487. }
  488. .lottery-countdown-dialog #pause {
  489. outline: none;
  490. background: transparent;
  491. border: none;
  492. margin-top: 10px;
  493. width: 50px;
  494. height: 50px;
  495. position: relative;
  496. }
  497. .lottery-countdown-dialog .play::before {
  498. display: block;
  499. content: "";
  500. position: absolute;
  501. top: 8px;
  502. left: 16px;
  503. border-top: 15px solid transparent;
  504. border-bottom: 15px solid transparent;
  505. border-left: 22px solid #409EFF;
  506. }
  507. .lottery-countdown-dialog .pause::after {
  508. content: "";
  509. position: absolute;
  510. top: 8px;
  511. left: 12px;
  512. width: 15px;
  513. height: 30px;
  514. background-color: transparent;
  515. border-radius: 1px;
  516. border: 5px solid #409EFF;
  517. border-top: none;
  518. border-bottom: none;
  519. }
  520. .lottery-countdown-dialog #pause:hover {
  521. opacity: 0.8;
  522. }
  523. .lottery-countdown-dialog .e-c-base {
  524. fill: none;
  525. stroke: #B6B6B6;
  526. stroke-width: 15px;
  527. }
  528. .lottery-countdown-dialog .e-c-progress {
  529. fill: none;
  530. stroke: #409EFF;
  531. stroke-width: 15px;
  532. transition: stroke-dashoffset 0.7s;
  533. }
  534. .lottery-countdown-dialog .e-c-pointer {
  535. fill: #FFF;
  536. stroke: #409EFF;
  537. stroke-width: 2px;
  538. }
  539. .lottery-countdown-dialog #e-pointer {
  540. transition: transform 0.7s;
  541. }
  542. .lottery-start-dialog{
  543. z-index : 999999;
  544. }
  545. .lottery-start-dialog .el-carousel__item{
  546. background-color: #99a9bf;
  547. opacity: 0.75;
  548. margin: 0;
  549. display:flex;
  550. width : 250px;
  551. justify-content:center;
  552. align-items:center;
  553. flex-direction:column;
  554. }
  555. .lottery-start-dialog .el-dialog__header{
  556. display:none;
  557. }
  558. .lottery-start-dialog .el-carousel__item.highlight{
  559. background-color: gold;
  560. }
  561. .lottery-start-dialog .el-carousel__item .nickname{
  562. font-size : 20px;
  563. color : white;
  564. text-shadow : 0px 2px 2px black, 0px -2px 2px black, 2px 0px 2px black, -2px 0px 2px black, 2px 2px 2px black, -2px -2px 2px black, 2px -2px 2px black, -2px 2px 2px black;
  565. font-weight: bold;
  566. }
  567. .login-form-dialog {
  568. z-index : 999999;
  569. height: 400px;
  570. }
  571. .audio-form-dialog {
  572. z-index : 999999;
  573. height: 400px;
  574. }
  575. .audio-form-dialog .el-dialog__body{
  576. height : 80%;
  577. overflow-y: auto;
  578. }
  579. .interaction-form-dialog {
  580. z-index : 999999;
  581. height: 400px;
  582. }
  583. .interaction-form-dialog .el-dialog__body{
  584. height : 80%;
  585. overflow-y: auto;
  586. }
  587. .interaction-form .el-divider .el-divider__text{
  588. color : #409EFF;
  589. }
  590. .guess-form-dialog {
  591. z-index : 999999;
  592. height: 400px;
  593. }
  594. .guess-form-dialog .el-dialog__body{
  595. height : 100%;
  596. overflow-y: auto;
  597. }
  598. .guess-start-dialog {
  599. z-index : 999999;
  600. height: 400px;
  601. }
  602. .guess-start-dialog .el-dialog__body{
  603. height : 100%;
  604. overflow-y: auto;
  605. }
  606. .guess-danmaku-dialog {
  607. z-index : 999999;
  608. height: 400px;
  609. }
  610. .guess-danmaku-dialog .el-dialog__body{
  611. height : 100%;
  612. overflow-y: auto;
  613. }
  614. .guess-danmaku-dialog .guess-danmaku-container{
  615. height : 100%;
  616. width : 100%;
  617. }
  618. .guess-danmaku-dialog .guess-danmaku-danmu{
  619. display : flex;
  620. color: rgb(255, 255, 255);
  621. font-size: 25px;
  622. font-family: buding;
  623. text-shadow: rgb(0 0 0) 0px 1px 1px, rgb(0 0 0) 0px -1px 1px, rgb(0 0 0) 1px 0px 1px, rgb(0 0 0) -1px 0px 1px, rgb(0 0 0) 1px 1px 1px, rgb(0 0 0) -1px -1px 1px, rgb(0 0 0) 1px -1px 1px, rgb(0 0 0) -1px 1px 1px;
  624. cursor : pointer;
  625. }
  626. .guess-danmaku-dialog .guess-danmaku-danmu.is-select{
  627. border : 1px solid red;
  628. }
  629. .guess-danmaku-dialog .guess-danmaku-danmu >img{
  630. width : 20px;
  631. height : 20px;
  632. border-radius : 10px;
  633. }
  634. .guess-table-dialog {
  635. z-index : 999999;
  636. height: 400px;
  637. }
  638. .guess-table-dialog .el-dialog__body{
  639. height : 100%;
  640. overflow-y: auto;
  641. }
  642. `;
  643. GM_addStyle(css);
  644. var audio = null;
  645. // 检查token是否存在
  646. var token = await GM_getValue("token");
  647. if(token){
  648. config.TOKEN = token;
  649. }
  650. // 禁止播放视频
  651. function disableVideos(){
  652. window.setInterval(function(){
  653. let videoEles = document.querySelectorAll('video');
  654. for(let videoEle of videoEles){
  655. videoEle.src = null;
  656. }
  657. }, 500);
  658. }
  659. // 是否为空
  660. function isNullOrEmpty(val){
  661. return _.isUndefined(val) || _.isNull(val) || _.isNaN(val) || (((_.isObject(val) && !_.isDate(val)) || _.isArray(val) || _.isString(val)) && _.isEmpty(val))
  662. }
  663. // 通用请求
  664. function commonRequrest(url, method, form, raw, callback, headers){
  665. var isSuccess = false;
  666. var data = null;
  667. if(!headers){
  668. headers = {};
  669. }
  670. if(!raw){
  671. if(method == 'post'){
  672. headers['Content-Type'] = 'application/x-www-form-urlencoded';
  673. if(form){
  674. form = Qs.stringify(form);
  675. }
  676. }
  677. }
  678. if(method == 'get' && form){
  679. form = Qs.stringify(form);
  680. url += '?' + form;
  681. }
  682. // 获取了token
  683. if(config.TOKEN){
  684. headers['Authorization'] = `Token ${config.TOKEN}`;
  685. }
  686. GM_xmlhttpRequest({
  687. synchronous : !_.isFunction(callback),
  688. method : method,
  689. url : url,
  690. data : form,
  691. headers : headers,
  692. onload : function(res){
  693. // 200
  694. if(res.status==200){
  695. if(raw){
  696. callback(true, res.responseText);
  697. }
  698. else{
  699. res = JSON.parse(res.responseText);
  700. isSuccess = res[config.RESPONSE.FIELD.STATUS] == config.RESPONSE.STATUS.SUCCESS;
  701. data = res[config.RESPONSE.FIELD.DATA];
  702. if(_.isFunction(callback)){
  703. callback(isSuccess, data);
  704. }
  705. }
  706. }
  707. else{
  708. if(_.isFunction(callback)){
  709. callback(isSuccess, data);
  710. }
  711. }
  712. },
  713. onerror : function(){
  714. if(_.isFunction(callback)){
  715. callback(isSuccess, data);
  716. }
  717. },
  718. onabort : function(){
  719. if(_.isFunction(callback)){
  720. callback(isSuccess, data);
  721. }
  722. },
  723. });
  724. return [isSuccess, data];
  725. }
  726. function formatText(text, params){
  727. for(var key in params){
  728. text = text.replace(new RegExp('\\$\\{'+ key +'\\}', 'g'), params[key]);
  729. }
  730. return text;
  731. }
  732. function addCssResource(url){
  733. commonRequrest(url, 'get', null, true, function(isSuccess, css){
  734. if(isSuccess){
  735. GM_addStyle(css);
  736. }
  737. })
  738. }
  739. // 添加element-ui样式
  740. addCssResource('https://cdn.jsdelivr.net/npm/element-ui@2.15.1/lib/theme-chalk/index.css');
  741. class CallbackManager{
  742. constructor(feedback){
  743. this.callbacks = {};
  744. this.callbackGroupMapper = {};
  745. }
  746. register(group, func, params, first){
  747. if(!(group in this.callbacks)){
  748. this.callbacks[group] = [];
  749. }
  750. var callback = {
  751. id : uuidv4(),
  752. func : func,
  753. params : params
  754. };
  755. // 添加到最前
  756. if(first){
  757. this.callbacks[group].splice(0, 0, callback);
  758. }
  759. else{
  760. this.callbacks[group].push(callback);
  761. }
  762. this.callbackGroupMapper[callback.id] = group;
  763. return callback.id;
  764. }
  765. cancel(handler){
  766. if(!(handler in this.callbackGroupMapper) || !(this.callbackGroupMapper[handler] in this.callbacks)){
  767. return;
  768. }
  769. var findIndex = _.findIndex(this.callbacks[this.callbackGroupMapper[handler]], {id:handler});
  770. if(findIndex==-1){
  771. return;
  772. }
  773. this.callbacks[this.callbackGroupMapper[handler]].splice(findIndex, 1);
  774. delete this.callbackGroupMapper[handler];
  775. }
  776. feed(group, data){
  777. if(!(group in this.callbacks)){
  778. return;
  779. }
  780. this.callbacks[group].forEach(function(callback){
  781. callback.func(data, callback.params);
  782. });
  783. }
  784. }
  785. var callbackManager = new CallbackManager();
  786. const acSeriesPattern = /[aA][aAbBcC][=号]?\s*(\d+)/;
  787. function extractAcSeries(text){
  788. var match = text.match(acSeriesPattern);
  789. if(match){
  790. return parseInt(match[match.length-1]);
  791. }
  792. else{
  793. return null;
  794. }
  795. }
  796. const giftPattern = /送出((?<count>\d+)个)?(?<name>.+)/;
  797. function extractGift(text){
  798. var match = text.match(giftPattern);
  799. if(match){
  800. return {
  801. name : match.groups.name,
  802. count : match.groups.count?parseInt(match.groups.count):1,
  803. };
  804. }
  805. else{
  806. return null;
  807. }
  808. }
  809. const modalClassNamePattern = /medal-lv-(?<level>\d+)/;
  810. function extractLevel(text){
  811. var match = text.match(modalClassNamePattern);
  812. if(match){
  813. return parseInt(match.groups.level);
  814. }
  815. else{
  816. return null;
  817. }
  818. }
  819. const commandPattern = /^\/(?<command>[^s]+)\s+(?<text>[^s]+)$/;
  820. function extractCommand(text){
  821. var match = text.match(commandPattern);
  822. if(match){
  823. return match.groups;
  824. }
  825. else{
  826. return null;
  827. }
  828. }
  829. var userInfo = {};
  830. var vupInfo = {};
  831. var refreshTimeoutHandler = null;
  832. // 开始
  833. function start(){
  834. // 监听弹幕添加
  835. var messageContainer = document.querySelector('.live-feed-messages');
  836. var observer = new MutationObserver(function(mutations, observer){
  837. // 遍历改变
  838. mutations.forEach(function(mutation){
  839. // 子节点发生变化
  840. if(mutation.type == 'childList'){
  841. if(refreshTimeoutHandler){
  842. window.clearTimeout(refreshTimeoutHandler);
  843. }
  844. // 3分钟内没接收到信息,刷新
  845. refreshTimeoutHandler = window.setTimeout(()=>window.location.reload(), 3 * 60 * 1000);
  846. // 遍历新增节点
  847. mutation.addedNodes.forEach(function(node){
  848. // 获取用户名元素
  849. var userEle = node.querySelector('.nickname');
  850. var userName = userEle.innerText;
  851. var uid = null;
  852. var badgeName = null;
  853. var badgeLevel = null;
  854. var badgeColor = null;
  855. // 查看是否有徽章
  856. var badgeContainerEle = node.querySelector('.medal-wrapper');
  857. if(badgeContainerEle){
  858. badgeName = badgeContainerEle.querySelector('.medal-name').innerText;
  859. badgeContainerEle.classList.forEach(function(className){
  860. if(extractLevel(className)!=null){
  861. badgeLevel = extractLevel(className);
  862. }
  863. });
  864. if(badgeLevel >= 1 && badgeLevel <= 3){
  865. badgeColor = 'green';
  866. }
  867. else if(badgeLevel >= 4 && badgeLevel <= 6){
  868. badgeColor = 'blue';
  869. }
  870. else if(badgeLevel >= 7 && badgeLevel <= 9){
  871. badgeColor = 'orange';
  872. }
  873. else if(badgeLevel >= 10 && badgeLevel <= 12){
  874. badgeColor = 'red';
  875. }
  876. else if(badgeLevel >= 13 && badgeLevel <= 15){
  877. badgeColor = 'purple';
  878. }
  879. else if(badgeLevel >= 16 && badgeLevel <= 20){
  880. badgeColor = 'black';
  881. }
  882. }
  883. if(userEle && userEle.attributes['data-user-id']){
  884. uid = parseInt(userEle.attributes['data-user-id'].value);
  885. }
  886. var interaction = {
  887. id : uuidv4(),
  888. sendTime : new Date(),
  889. };
  890. // 弹幕
  891. if(node.classList.contains('comment')){
  892. interaction.type = 'danmaku';
  893. interaction.content = _.trimStart(node.querySelector('.comment-text').innerText, ':');
  894. }
  895. // 送礼
  896. else if(node.classList.contains('gift')){
  897. var giftText = userEle.nextElementSibling.innerText;
  898. var gift = extractGift(giftText);
  899. interaction.type = 'gift';
  900. interaction.giftName = gift.name;
  901. interaction.giftCount = gift.count;
  902. }
  903. // 点赞
  904. else if(node.classList.contains('like')){
  905. interaction.type = 'like';
  906. }
  907. // 进入直播间
  908. else if(node.classList.contains('user-enter')){
  909. interaction.type = 'enterroom';
  910. }
  911. // 关注了主播
  912. else if(node.classList.contains('follow')){
  913. interaction.type = 'follow';
  914. }
  915. // 加入守护团
  916. else if(node.classList.contains('join-club')){
  917. interaction.type = 'joinclub';
  918. }
  919. // vup
  920. if(uid in vupInfo){
  921. addBadgeToUser(userEle);
  922. }
  923. if(uid in userInfo){
  924. callbackManager.feed('message', {
  925. uid : uid,
  926. vup : uid in vupInfo,
  927. userName : userName,
  928. badgeName : badgeName,
  929. badgeLevel : badgeLevel,
  930. badgeColor : badgeColor,
  931. photo : userInfo[uid].photo,
  932. interaction : interaction,
  933. });
  934. }
  935. else{
  936. // 获取用户信息
  937. commonRequrest(config.ACFUNLIVE_SERVER + config.URLS.ACFUN_USER.INFO + `?userId=${uid}`, 'get', null, true, function(isSuccess, data){
  938. var photo = null;
  939. if(data){
  940. data = JSON.parse(data);
  941. if(data.result == 0){
  942. photo = data.profile.headUrl;
  943. }
  944. }
  945. userInfo[uid] = {
  946. userName : userName,
  947. photo : photo,
  948. };
  949. callbackManager.feed('message', {
  950. uid : uid,
  951. vup : uid in vupInfo,
  952. userName : userName,
  953. photo : userInfo[uid].photo,
  954. badgeName : badgeName,
  955. badgeLevel : badgeLevel,
  956. badgeColor : badgeColor,
  957. interaction : interaction,
  958. });
  959. });
  960. }
  961. });
  962. }
  963. });
  964. });
  965. observer.observe(messageContainer, {childList:true});
  966. }
  967. // 加载Vue实例
  968. function loadVue(){
  969. var vue = null;
  970. // 获取礼物列表
  971. var giftList = [];
  972. var giftContainerEles = document.querySelectorAll('.item-gift');
  973. giftContainerEles.forEach(function(giftContainerEle, giftIndex){
  974. var gift = {
  975. index : giftIndex,
  976. id : giftContainerEle.attributes['data-gift-id'].value,
  977. name : giftContainerEle.querySelector('.name').textContent,
  978. price : _.parseInt(giftContainerEle.attributes['data-gift-price'].value),
  979. img : giftContainerEle.querySelector('img').attributes['src'].value,
  980. };
  981. // 设置香蕉的价格为0
  982. if(gift.name == '香蕉'){
  983. gift.price = 0;
  984. }
  985. giftList.push(gift);
  986. });
  987. // 无法获取礼物列表
  988. if(giftList.length==0){
  989. return;
  990. }
  991. // 将礼物按照AC币排序
  992. giftList = _.sortBy(giftList, ['price', 'index']);
  993. // 隐藏原有弹幕
  994. document.querySelector('.container-live-feed-messages').style.setProperty('display', 'none', 'important');
  995. // 添加悬浮菜单
  996. // 容器
  997. var containerEle = document.querySelector('.container-live-feed .live-feed');
  998. // 悬浮菜单元素
  999. var menuEle = document.createElement('div');
  1000. menuEle.className = 'container-live-feed-messages-acfunlive';
  1001. menuEle.innerHTML = `
  1002. <div :class="{'live-message-container':true, 'active':mouseOn, 'inactive':!mouseOn, 'live-message-container-horizontal':interactionFormData.direction=='horizontal', 'live-message-container-vertical':interactionFormData.direction!='horizontal'}" :style="backgroundStyle">
  1003. <template v-for="message in messageData">
  1004. <!--弹幕-->
  1005. <template v-if="message.interaction.type=='danmaku' && message.interaction.content">
  1006. <div class="live-message-popup" :style="danmakuMessageContainerStyle" :key="message.interaction.id">
  1007. <div class="live-message-popup-left">
  1008. <el-avatar shape="circle" fit="fill" :size="userPhotoSize" :src="message.photo"></el-avatar>
  1009. </div>
  1010. <div class="live-message-popup-right">
  1011. <div class="user-info-container">
  1012. <span class="user-info-badge user-info-badge-vup" :style="userBadgeStyle" v-if="message.vup">VUP</span>
  1013. <span :style="userBadgeStyle" :class="{'user-info-badge':true,'user-info-badge-black':message.badgeColor=='black','user-info-badge-purple':message.badgeColor=='purple','user-info-badge-red':message.badgeColor=='red','user-info-badge-orange':message.badgeColor=='orange','user-info-badge-blue':message.badgeColor=='blue','user-info-badge-green':message.badgeColor=='green'}" v-if="message.badgeName">LV.{{message.badgeLevel}} {{message.badgeName}}</span>
  1014. <span class="user-info-username" :style="userNameMessageStyle">{{message.userName}}</span>
  1015. </div>
  1016. <div :class="danmakuMessageClass.concat([!lastDanmakuMessage || message.interaction.id == lastDanmakuMessage.interaction.id?'active':'inactive'])" :style="danmakuMessageStyle">
  1017. <div class="border-flow-item" :style="borderFlowItemStyle"></div>
  1018. <span @click="clickMessage(message)">{{message.interaction.content}}</span>
  1019. <div class="live-message-douga" @click="clickMessage(message, true)" v-if="message.interaction.douga">
  1020. <el-card :title="message.interaction.douga.title" :style="danmakuDougaMessageStyle">
  1021. <el-row :class="{'douga-info':true, 'article':message.interaction.douga.type == 'article', 'video':['video', 'bangumi'].indexOf(message.interaction.douga.type) != -1}" :style="danmakuDougaContainerStyle">
  1022. <template v-if="message.interaction.douga.type == 'video'">
  1023. <div class="douga-info-left" :style="danmakuDougaLeftColStyle">
  1024. <span class="cover" :style="danmakuDougaCoverStyle">
  1025. <img :src="message.interaction.douga.cover">
  1026. </span>
  1027. </div>
  1028. <div class="douga-info-right" :style="danmakuDougaRightColStyle">
  1029. <div class="title-container" :style="danmakuDougaTitleStyle"><span>{{message.interaction.douga.title}}</span></div>
  1030. <div class="info" :style="danmakuDougaInfoStyle">
  1031. <p class="up"><span>up主:</span><span>{{message.interaction.douga.userName}}</span></p>
  1032. <p class="play-count" :style="danmakuDougaInfoStyle">播放量:<span>{{message.interaction.douga.playCountText}}</span></p>
  1033. </div>
  1034. </div>
  1035. </template>
  1036. <template v-if="message.interaction.douga.type == 'article'">
  1037. <div class="title-container" :style="danmakuDougaTitleStyle">
  1038. <p class="title">{{message.interaction.douga.title}}</p>
  1039. <p class="description" :style="danmakuDougaDescriptionStyle">{{message.interaction.douga.description}}</p>
  1040. </div>
  1041. <div class="info" :style="danmakuDougaInfoStyle">
  1042. <p class="up"><span>up主:</span><span>{{message.interaction.douga.userName}}</span></p>
  1043. <p class="play-count" :style="danmakuDougaInfoStyle">访问量:<span>{{message.interaction.douga.playCountText}}</span></p>
  1044. </div>
  1045. </template>
  1046. <template v-if="message.interaction.douga.type == 'bangumi'">
  1047. <div class="douga-info-left" :style="danmakuDougaLeftColStyle">
  1048. <span class="cover" :style="danmakuDougaCoverStyle">
  1049. <img :src="message.interaction.douga.cover">
  1050. </span>
  1051. </div>
  1052. <div class="douga-info-right" :style="danmakuDougaRightColStyle">
  1053. <div class="title-container" :style="danmakuDougaTitleStyle">
  1054. <el-tag effect="dark" type="danger" v-if="message.interaction.douga.acfunOnly">A站独播</el-tag>
  1055. <span>{{message.interaction.douga.title}}</span>
  1056. </div>
  1057. <div class="info" :style="danmakuDougaInfoStyle">
  1058. <p class="stow-count" :style="danmakuDougaInfoStyle">追番人数:<span>{{message.interaction.douga.stowCountText}}</span></p>
  1059. <p class="play-count" :style="danmakuDougaInfoStyle">播放量:<span>{{message.interaction.douga.playCountText}}</span></p>
  1060. </div>
  1061. </div>
  1062. </template>
  1063. </el-row>
  1064. </el-card>
  1065. </div>
  1066. </div>
  1067. </div>
  1068. </div>
  1069. </template>
  1070. <!--点赞-->
  1071. <template v-if="message.interaction.type=='like' && message.interaction.content">
  1072. <div class="live-message-inline" :style="likeMessageContainerStyle" :key="message.interaction.id">
  1073. <span :style="likeMessageStyle">
  1074. <span v-if="message.vup">【VUP】</span>
  1075. <span class="user-info-username">{{message.userName}}</span>
  1076. <span>{{message.interaction.content}}</span>
  1077. </span>
  1078. </div>
  1079. </template>
  1080. <!--礼物-->
  1081. <template v-if="message.interaction.type=='gift' && message.interaction.content">
  1082. <div class="live-message-popup" :style="giftMessageContainerStyle" :key="message.interaction.id">
  1083. <div class="live-message-popup-left">
  1084. <el-avatar shape="circle" fit="fill" :size="userPhotoSize" :src="message.photo"></el-avatar>
  1085. </div>
  1086. <div class="live-message-popup-right">
  1087. <div class="user-info-container">
  1088. <span :style="userBadgeStyle" class="user-info-badge user-info-badge-vup" v-if="message.vup">VUP</span>
  1089. <span :style="userBadgeStyle" :class="{'user-info-badge':true,'user-info-badge-black':message.badgeColor=='black','user-info-badge-purple':message.badgeColor=='purple','user-info-badge-red':message.badgeColor=='red','user-info-badge-orange':message.badgeColor=='orange','user-info-badge-blue':message.badgeColor=='blue','user-info-badge-green':message.badgeColor=='green'}" v-if="message.badgeName">LV.{{message.badgeLevel}} {{message.badgeName}}</span>
  1090. <span class="user-info-username" :style="userNameMessageStyle">{{message.userName}}</span>
  1091. </div>
  1092. <div class="live-message-gift" :style="giftMessageStyle">
  1093. <span >{{message.interaction.content}}</span>
  1094. <el-avatar shape="square" :size="giftImageSize" fit="fill" :src="giftNameMapper[message.interaction.giftName].img" style="background-color:transparent;vertical-align:bottom;"></el-avatar>
  1095. <span :class="{'live-message-gift-count':true, 'scale-anime':message.interaction.doAnime}" v-if="message.interaction.giftCount>0" :style="giftCountStyle">
  1096. x{{message.interaction.giftCount}}
  1097. </span>
  1098. </div>
  1099. </div>
  1100. </div>
  1101. </template>
  1102. <!--进入直播间-->
  1103. <template v-if="message.interaction.type=='enterroom' && message.interaction.content">
  1104. <div class="live-message-inline" :style="enterroomMessageContainerStyle" :key="message.interaction.id">
  1105. <span :style="enterroomMessageStyle">
  1106. <span v-if="message.vup">【VUP】</span>
  1107. <span class="user-info-username">{{message.userName}}</span>
  1108. <span class="live-message-enterroom">{{message.interaction.content}}</span>
  1109. </span>
  1110. </div>
  1111. </template>
  1112. <!--关注-->
  1113. <template v-if="message.interaction.type=='follow' && message.interaction.content">
  1114. <div class="live-message-inline" :style="followMessageContainerStyle" :key="message.interaction.id">
  1115. <span :style="followMessageStyle">
  1116. <span v-if="message.vup">【VUP】</span>
  1117. <span class="user-info-username">{{message.userName}}</span>
  1118. <span class="live-message-follow">{{message.interaction.content}}</span>
  1119. </span>
  1120. </div>
  1121. </template>
  1122. <!--加入守护团-->
  1123. <template v-if="message.interaction.type=='joinclub' && message.interaction.content">
  1124. <div class="live-message-inline" :style="joinclubMessageContainerStyle" :key="message.interaction.id">
  1125. <span :style="joinclubMessageStyle">
  1126. <span v-if="message.vup">【VUP】</span>
  1127. <span class="user-info-username">{{message.userName}}</span>
  1128. <span class="live-message-joinclub">{{message.interaction.content}}</span>
  1129. </span>
  1130. </div>
  1131. </template>
  1132. </template>
  1133. </div>
  1134. <div :class="{'live-feed-dropmenu':true, 'live-feed-dropmenu-horizontal':interactionFormData.direction=='horizontal','live-feed-dropmenu-vertical':interactionFormData.direction!='horizontal'}">
  1135. <el-dropdown trigger="click" @command="handleDropdownMenuClick">
  1136. <span class="el-dropdown-link" style="font-size:32px;">
  1137. <i class="el-icon-menu"></i>
  1138. </span>
  1139. <el-dropdown-menu slot="dropdown">
  1140. <el-dropdown-item icon="el-icon-edit" command="editInteraction">弹幕编辑</el-dropdown-item>
  1141. <el-dropdown-item>
  1142. <el-dropdown trigger="hover" placement="right-start" trigger="click" @command="handleDropdownMenuClick">
  1143. <span class="el-icon-lollipop">
  1144. 直播抽奖<i class="el-icon-arrow-down el-icon--right"></i>
  1145. </span>
  1146. <el-dropdown-menu slot="dropdown">
  1147. <el-dropdown-item icon="el-icon-circle-plus" command="newLottery">添加抽奖</el-dropdown-item>
  1148. <el-dropdown-item icon="el-icon-info" command="lotteryTable">查看抽奖</el-dropdown-item>
  1149. <el-dropdown-item icon="el-icon-download" command="exportLottery">导出名单</el-dropdown-item>
  1150. </el-dropdown-menu>
  1151. </el-dropdown>
  1152. </el-dropdown-item>
  1153. <!--
  1154. <el-dropdown-item>
  1155. <el-dropdown trigger="hover" placement="right-start" trigger="click" @command="handleDropdownMenuClick">
  1156. <span class="el-icon-chat-dot-round">
  1157. 直播竞猜<i class="el-icon-arrow-down el-icon--right"></i>
  1158. </span>
  1159. <el-dropdown-menu slot="dropdown">
  1160. <el-dropdown-item icon="el-icon-circle-plus" command="newGuess">添加竞猜</el-dropdown-item>
  1161. <el-dropdown-item icon="el-icon-info" command="guessTable">查看竞猜</el-dropdown-item>
  1162. <el-dropdown-item icon="el-icon-download" command="exportGuess">导出名单</el-dropdown-item>
  1163. </el-dropdown-menu>
  1164. </el-dropdown>
  1165. </el-dropdown-item>
  1166. -->
  1167. <el-dropdown-item icon="el-icon-notebook-1" command="help">帮助文档</el-dropdown-item>
  1168. </el-dropdown-menu>
  1169. </el-dropdown>
  1170. <el-dialog v-dialog-drag title="查看抽奖" :visible.sync="lotteryTableDialogVisible" :modal="false" width="550px" custom-class="lottery-table-dialog" :close-on-click-modal="false">
  1171. <el-table :data="lotteryTableData" style="width: 100%" class="lottery-table" :row-class-name="lotteryTableRowClassName">
  1172. <el-table-column prop="name" label="奖品名称" :width="150">
  1173. </el-table-column>
  1174. <el-table-column prop="startTimeText" label="开奖时间" :width="100">
  1175. </el-table-column>
  1176. <el-table-column label="操作" :width="250">
  1177. <template slot-scope="scope">
  1178. <el-button type="text" @click="handleLotteryModify(scope.row)">编辑</el-button>
  1179. <el-button type="text" @click="handleLotteryCopy(scope.row)">复制</el-button>
  1180. <el-button type="text" @click="handleLotteryCandidatesCheck(scope.row)" v-if="scope.row.status == '未开始'">查看候选名单</el-button>
  1181. <el-button type="text" @click="handleLotteryWinnersCheck(scope.row)" v-if="scope.row.status == '已结束'">查看中奖名单</el-button>
  1182. </template>
  1183. </el-table-column>
  1184. </el-table>
  1185. </el-dialog>
  1186. <el-dialog v-dialog-drag :title="lotteryFormDialogTitle" :visible.sync="lotteryFormDialogVisible" :modal="false" width="550px" custom-class="lottery-form-dialog" :close-on-click-modal="false">
  1187. <el-form :model="lotteryFormData" class="lottery-form" ref="form" :rules="lotteryFormRules" @submit.native.prevent v-loading="lotteryFormData.loading" :element-loading-text="lotteryFormData.loadingText" element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
  1188. <el-form-item label="奖品名称" label-width="130px" prop="name">
  1189. <el-input v-model="lotteryFormData.name" autocomplete="off" style="width:100%;"></el-input>
  1190. </el-form-item>
  1191. <el-form-item>
  1192. <el-col :span="11">
  1193. <el-form-item label="抽奖倒计时(秒)" label-width="130px" prop="countdown">
  1194. <el-input-number size="medium" v-model="lotteryFormData.countdown" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1195. </el-form-item>
  1196. </el-col>
  1197. <el-col :span="13">
  1198. <el-form-item label="开奖时间" label-width="100px" prop="startTime">
  1199. <el-time-picker v-model="lotteryFormData.startTime" :picker-options="timePickerOptions" style="width:100%;">
  1200. </el-time-picker>
  1201. </el-form-item>
  1202. </el-row>
  1203. </el-form-item>
  1204. <el-form-item label="互斥抽奖" label-width="130px" prop="mutualLotteryList">
  1205. <el-select v-model="lotteryFormData.mutualLotteryList" multiple placeholder="请选择互斥的抽奖" style="width:100%;">
  1206. <el-option
  1207. :value="lottery.id"
  1208. :label="lottery.name"
  1209. :key="lottery.id"
  1210. v-if="lottery.id != lotteryFormData.id"
  1211. v-for="lottery in lotteryTableData"
  1212. >
  1213. </el-option>
  1214. </el-select>
  1215. </el-form-item>
  1216. <!--
  1217. <el-form-item label="抽奖权重" label-width="130px" prop="weight">
  1218. <el-switch v-model="lotteryFormData.weight" active-text="加权" inactive-text="等权">
  1219. </el-switch>
  1220. </el-form-item>
  1221. -->
  1222. <el-form-item>
  1223. <el-col :span="13">
  1224. <el-form-item label="牌子名称" label-width="130px" prop="medalName">
  1225. <el-input v-model="lotteryFormData.medalName" autocomplete="off" style="width:100%;"></el-input>
  1226. </el-form-item>
  1227. </el-col>
  1228. <el-col :span="11">
  1229. <el-form-item label="牌子等级" label-width="130px" prop="medalLevel">
  1230. <el-input-number v-model="lotteryFormData.medalLevel" autocomplete="off" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1231. </el-form-item>
  1232. </el-col>
  1233. </el-form-item>
  1234. <el-form-item label="抽奖来源" label-width="130px" prop="source">
  1235. <el-radio-group v-model="lotteryFormData.source" style="width:100%;">
  1236. <el-radio label="livestream">直播间抽奖</el-radio>
  1237. <el-radio label="douga">稿件抽奖</el-radio>
  1238. </el-radio-group>
  1239. </el-form-item>
  1240. <template v-if="lotteryFormData.source == 'livestream'">
  1241. <el-form-item label="抽奖参与方式" label-width="130px" prop="joinMethod">
  1242. <el-checkbox-group v-model="lotteryFormData.joinMethod" style="width:100%;">
  1243. <el-checkbox label="danmaku">弹幕</el-checkbox>
  1244. <el-checkbox label="gift">礼物</el-checkbox>
  1245. <el-checkbox label="like">点赞</el-checkbox>
  1246. </el-checkbox-group>
  1247. </el-form-item>
  1248. <!-- 弹幕 -->
  1249. <template v-if="lotteryFormData.joinMethod.indexOf('danmaku')!=-1">
  1250. <el-form-item label="抽奖参与关键词" label-width="130px" prop="joinKeyword">
  1251. <el-input v-model="lotteryFormData.joinKeyword" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1252. </el-form-item>
  1253. </template>
  1254. <!-- 礼物 -->
  1255. <template v-if="lotteryFormData.joinMethod.indexOf('gift')!=-1">
  1256. <el-form-item label="抽奖参与礼物" label-width="130px" prop="joinGiftList">
  1257. <el-select v-model="lotteryFormData.joinGiftList" multiple placeholder="请选择礼物" popper-class="gift-selector" style="width:100%;">
  1258. <el-option
  1259. :label="gift.name"
  1260. :value="gift.id"
  1261. :key="gift.id"
  1262. v-for="gift in giftList"
  1263. >
  1264. <el-row>
  1265. <el-col :span="8">
  1266. <el-avatar shape="square" :size="40" fit="fill" :src="gift.img" style="background-color:white;"></el-avatar>
  1267. </el-col>
  1268. <el-col :span="6" :offset="4">
  1269. <span>{{gift.name}}</span>
  1270. </el-col>
  1271. <el-col :span="6">
  1272. <span v-if="gift.price>0">({{gift.price}}AC币)</span>
  1273. </el-col>
  1274. </el-row>
  1275. </el-option>
  1276. </el-select>
  1277. </el-form-item>
  1278. </template>
  1279. </template>
  1280. <template v-else-if="lotteryFormData.source == 'douga'">
  1281. <el-form-item label="抽奖稿件类型" label-width="130px" prop="dougaType">
  1282. <el-radio-group v-model="lotteryFormData.dougaType" style="width:100%;" @change="refreshDougaList">
  1283. <el-radio label="video">视频投稿</el-radio>
  1284. <el-radio label="article">文章投稿</el-radio>
  1285. </el-radio-group>
  1286. </el-form-item>
  1287. <!--
  1288. <el-form-item label="抽奖参与方式" label-width="130px" prop="joinMethod">
  1289. <el-checkbox-group v-model="lotteryFormData.joinMethod" style="width:100%;">
  1290. <el-checkbox label="comment">评论</el-checkbox>
  1291. <el-checkbox label="subcomment">楼中楼</el-checkbox>
  1292. <el-checkbox label="floor">指定楼层</el-checkbox>
  1293. </el-checkbox-group>
  1294. </el-form-item>
  1295. -->
  1296. <el-divider>请选取参与抽奖的稿件</el-divider>
  1297. <el-row class="douga-list-wrapper">
  1298. <el-timeline class="douga-list" v-infinite-scroll="getDougaList" :infinite-scroll-disabled="lotteryFormData.dougaListDisabled">
  1299. <el-timeline-item :class="{'douga-list-item':true,'active':lotteryFormData.dougaListActiveIndex == dougaIndex, 'video':lotteryFormData.dougaType == 'video', 'article' : lotteryFormData.dougaType == 'article'}" :timestamp="douga.uploadDateText" placement="top" v-for="(douga, dougaIndex) in lotteryFormData.dougaList" :key="douga.id">
  1300. <el-card :title="douga.title" @click.native="handleDougaListItemClick(dougaIndex)">
  1301. <el-row class="douga-info">
  1302. <template v-if="lotteryFormData.dougaType == 'video'">
  1303. <el-col :span="12" class="douga-info-left">
  1304. <span class="cover">
  1305. <img :src="douga.cover" @click.stop="handleDougaListItemDetailClick(dougaIndex)">
  1306. </span>
  1307. </el-col>
  1308. <el-col :span="12" class="douga-info-right">
  1309. <p class="title"><span>{{douga.title}}</span></p>
  1310. <p class="play-count">播放量:<span>{{douga.playCountText}}</span></p>
  1311. </el-col>
  1312. </template>
  1313. <template v-if="lotteryFormData.dougaType == 'article'">
  1314. <p class="title"><span @click.stop="handleDougaListItemDetailClick(dougaIndex)">{{douga.title}}</span></p>
  1315. <p class="play-count">访问量:<span>{{douga.playCountText}}</span></p>
  1316. </template>
  1317. </el-row>
  1318. </el-card>
  1319. </el-timeline-item>
  1320. </el-timeline>
  1321. <p v-if="lotteryFormData.dougaListLoading">...正在加载投稿稿件...</p>
  1322. <p v-if="lotteryFormData.dougaListNoMore">加载完毕</p>
  1323. </el-row>
  1324. </template>
  1325. <el-form-item style="display:flex;justify-content:center;">
  1326. <el-button type="primary" @click="handleLotteryFormSubmit">立即创建</el-button>
  1327. </el-form-item>
  1328. </el-form>
  1329. </el-dialog>
  1330. <el-dialog v-dialog-drag :title="userTableDialogTitle" :visible.sync="userTableDialogVisible" :modal="false" width="550px" custom-class="user-table-dialog" :close-on-click-modal="false">
  1331. <el-table :data="userTableData" style="width: 100%" class="user-table">
  1332. <el-table-column prop="userName" label="用户名" :width="300">
  1333. <template slot-scope="scope">
  1334. <el-avatar shape="circle" fit="fill" size="40" :src="scope.row.photo"></el-avatar>
  1335. <span>{{scope.row.userName}}</span>
  1336. </template>
  1337. </el-table-column>
  1338. <el-table-column prop="uid" label="用户uid" :width="150">
  1339. </el-table-column>
  1340. </el-table>
  1341. </el-dialog>
  1342. <el-dialog v-dialog-drag :visible.sync="lotteryCountdownDialogVisible" :modal="false" width="550px" custom-class="lottery-countdown-dialog" :close-on-click-modal="false" :show-close="false">
  1343. <div class="container">
  1344. <div class="circle">
  1345. <svg width="300" viewBox="0 0 220 220" xmlns="http://www.w3.org/2000/svg">
  1346. <g transform="translate(110,110)">
  1347. <circle r="100" class="e-c-base"/>
  1348. <g transform="rotate(-90)">
  1349. <circle r="100" class="e-c-progress"/>
  1350. <g id="e-pointer">
  1351. <circle cx="100" cy="0" r="15" class="e-c-pointer"/>
  1352. </g>
  1353. </g>
  1354. </g>
  1355. </svg>
  1356. </div>
  1357. <div class="controlls">
  1358. <div class="display-remain-time">{{activeLottery.countdownText}}</div>
  1359. </div>
  1360. </div>
  1361. </el-dialog>
  1362. <el-dialog v-dialog-drag :visible.sync="lotteryStartDialogVisible" :modal="false" width="550px" custom-class="lottery-start-dialog" :close-on-click-modal="false" :destroy-on-close="true" :show-close="false">
  1363. <el-row v-if="activeLottery!=null && activeLottery.name" style="text-align:center;margin-bottom:20px;">
  1364. <p style="font-size:30px;font-weight:bold;">{{activeLottery.name}}</p>
  1365. <p>
  1366. <span>已中奖人数:<span style="color:#409EFF;font-weight:bold;">{{activeLottery.winnerCount}}</span>名</span>
  1367. <span style="margin-left:20px;">候选人数:<span style="color:#409EFF;font-weight:bold;">{{activeLottery.startCandidates.length}}</span>人</span>
  1368. </p>
  1369. </el-row>
  1370. <el-row class="carousel-container" v-if="activeLottery!=null && activeLottery.name">
  1371. <el-carousel ref="carousel" type="card" height="200px" indicator-position="none" arrow="never" :autoplay="false">
  1372. <el-carousel-item v-for="candidate in activeLottery.startCandidates" :class="candidate.highlight?'highlight':''">
  1373. <el-row>
  1374. <el-avatar shape="square" :size="120" fit="fill" :src="candidate.photo"></el-avatar>
  1375. </el-row>
  1376. <el-row>
  1377. <span class="nickname" style="cursor:pointer;">{{candidate.userName}}</span>
  1378. </el-row>
  1379. <el-row>
  1380. <span :style="userBadgeStyle" :class="{'user-info-badge':true,'user-info-badge-black':candidate.medalColor=='black','user-info-badge-purple':candidate.medalColor=='purple','user-info-badge-red':candidate.medalColor=='red','user-info-badge-orange':candidate.medalColor=='orange','user-info-badge-blue':candidate.medalColor=='blue','user-info-badge-green':candidate.medalColor=='green'}" v-if="candidate.medalName">LV.{{candidate.medalLevel}} {{candidate.medalName}}</span>
  1381. </el-row>
  1382. </el-carousel-item>
  1383. </el-carousel>
  1384. </el-row>
  1385. <el-row style="text-align:center;margin-top:20px;" v-if="!activeLottery.rolling && activeLottery.activeIndex!=null && activeLottery.activeIndex!=-1">
  1386. <el-tag type="warning" effect="dark" v-if="userInfo[activeLottery.startCandidates[activeLottery.activeIndex].uid].hasEnterroom">进入直播间</el-tag>
  1387. <el-tag type="warning" effect="dark" v-if="userInfo[activeLottery.startCandidates[activeLottery.activeIndex].uid].hasLike">点赞</el-tag>
  1388. <el-tag type="warning" effect="dark" v-if="userInfo[activeLottery.startCandidates[activeLottery.activeIndex].uid].hasDanmaku">弹幕</el-tag>
  1389. <el-tag type="warning" effect="dark" v-if="userInfo[activeLottery.startCandidates[activeLottery.activeIndex].uid].hasGift">礼物</el-tag>
  1390. <el-tag type="warning" effect="dark" v-if="userInfo[activeLottery.startCandidates[activeLottery.activeIndex].uid].hasFollow">关注</el-tag>
  1391. </el-row>
  1392. <el-row style="text-align:center;margin-top:20px;">
  1393. <el-button-group v-if="!activeLottery.rolling">
  1394. <el-button type="primary" icon="el-icon-refresh-left" @click="activeLottery.nextFunc(false)">重新抽选</el-button>
  1395. <el-button type="primary" icon="el-icon-right" @click="activeLottery.nextFunc(true)">继续抽选</el-button>
  1396. <el-button type="primary" icon="el-icon-check" @click="activeLottery.finishFunc()">结束抽选</el-button>
  1397. <el-button-group>
  1398. </el-row>
  1399. </el-dialog>
  1400. <el-dialog v-dialog-drag title="弹幕预设" :visible.sync="interactionPresetDialogVisible" :modal="false" width="550px" custom-class="interaction-preset-dialog" :close-on-click-modal="false">
  1401. <el-row style="display:flex;justify-content:end;">
  1402. <el-button icon="el-icon-circle-plus-outline" type="primary" @click="handleInteractionFormOpen()">新增预设</el-button>
  1403. </el-row>
  1404. <!-- 预设 -->
  1405. <el-table :data="interactionPreset" style="width: 100%" class="interaction-preset-table" border ref="interactionPresetTable" height="200">
  1406. <el-table-column prop="index" label="序号" min-width="10%">
  1407. </el-table-column>
  1408. <el-table-column prop="name" label="名称" min-width="45%">
  1409. </el-table-column>
  1410. <el-table-column prop="hotkey" label="热键" min-width="20%">
  1411. </el-table-column>
  1412. <el-table-column label="操作" min-width="25%">
  1413. <template slot-scope="scope">
  1414. <el-button type="text" @click="handleInteractionFormOpen(scope.row)">编辑</el-button>
  1415. <el-button type="text" @click="removeInteractionPreset(scope.row)">删除</el-button>
  1416. </template>
  1417. </el-table-column>
  1418. </el-table>
  1419. </el-dialog>
  1420. <el-dialog v-dialog-drag title="弹幕编辑" :visible.sync="interactionFormDialogVisible" :modal="false" width="550px" custom-class="interaction-form-dialog" :close-on-click-modal="false" :destroy-on-close="true">
  1421. <el-form :model="interactionFormData" class="interaction-form" ref="interactionForm" :rules="interactionFormRules" @submit.native.prevent>
  1422. <!--
  1423. <el-form-item label="是否启动" label-width="130px" prop="enable">
  1424. <el-switch v-model="interactionFormData.enable" active-text="启动" inactive-text="不启动">
  1425. </el-switch>
  1426. </el-form-item>
  1427. -->
  1428. <template v-if="interactionFormData.enable">
  1429. <!-- 排版 -->
  1430. <!--<el-divider>排版配置</el-divider>
  1431. <el-form-item label="排版方向" label-width="130px" prop="direction">
  1432. <el-select v-model="interactionFormData.direction" style="width:100%;">
  1433. <el-option value="vertical" label="纵版" key="vertical"></el-option>
  1434. <el-option value="horizontal" label="横版" key="horizontal"></el-option>
  1435. </el-select>
  1436. </el-form-item>
  1437. <el-form-item label="弹幕/礼物单列显示" label-width="130px" prop="displayNotWrap">
  1438. <el-switch v-model="interactionFormData.displayNotWrap" active-text="是" inactive-text="否">
  1439. </el-switch>
  1440. </el-form-item>-->
  1441. <!-- 历史 -->
  1442. <!-- <el-divider>互动历史配置</el-divider>
  1443. -->
  1444. <!-- 全局 -->
  1445. <el-divider>全局配置</el-divider>
  1446. <el-form-item>
  1447. <el-form-item label="预设名称" label-width="130px" prop="name">
  1448. <el-input v-model="interactionFormData.name" autocomplete="off" style="width:100%;" placeholder="请输入预设名称"></el-input>
  1449. </el-form-item>
  1450. </el-form-item>
  1451. <el-form-item>
  1452. <el-col :span="11">
  1453. <el-form-item label="背景颜色" label-width="130px" prop="backgroundColor">
  1454. <el-color-picker v-model="interactionFormData.backgroundColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1455. </el-form-item>
  1456. </el-col>
  1457. </el-form-item>
  1458. <el-form-item label="互动信息保留" label-width="130px" prop="historyMinutes">
  1459. <el-input-number size="medium" v-model="interactionFormData.historyMinutes" :min="1" controls-position="right" style="width:30%;"></el-input-number>
  1460. <span>分钟</span>
  1461. </el-form-item>
  1462. <el-form-item label="互动信息保留" label-width="130px" prop="historyCount">
  1463. <el-input-number size="medium" v-model="interactionFormData.historyCount" :min="1" controls-position="right" style="width:30%;"></el-input-number>
  1464. <span>条</span>
  1465. </el-form-item>
  1466. <el-form-item style="display:flex;justify-content:center;">
  1467. <el-button type="primary" @click="handleInteractionFormInit">重置为默认配置</el-button>
  1468. </el-form-item>
  1469. <!-- 边框 -->
  1470. <el-divider>边框配置</el-divider>
  1471. <el-form-item>
  1472. <el-col :span="11">
  1473. <el-form-item label="边框宽度" label-width="130px" prop="borderWidth">
  1474. <el-select v-model="interactionFormData.borderWidth" style="width:100%;">
  1475. <el-option value="auto" label="自适应" key="auto"></el-option>
  1476. <el-option value="fixed" label="定宽" key="fixed"></el-option>
  1477. </el-select>
  1478. </el-form-item>
  1479. </el-col>
  1480. <el-col :span="13">
  1481. <el-form-item label="边框形状" label-width="100px" prop="borderShape">
  1482. <el-select v-model="interactionFormData.borderShape" style="width:100%;">
  1483. <el-option value="rect" label="矩形" key="rect"></el-option>
  1484. <el-option value="round" label="圆角矩形" key="round"></el-option>
  1485. <el-option value="flow" label="流动矩形" key="flow"></el-option>
  1486. </el-select>
  1487. </el-form-item>
  1488. </el-col>
  1489. </el-form-item>
  1490. <el-form-item>
  1491. <el-col :span="11">
  1492. <el-form-item label="边框颜色" label-width="130px" prop="borderColor">
  1493. <el-color-picker v-model="interactionFormData.borderColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1494. </el-form-item>
  1495. </el-col>
  1496. <el-col :span="13">
  1497. <el-form-item label="边框大小" label-width="100px" prop="borderSize">
  1498. <el-input-number size="medium" v-model="interactionFormData.borderSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1499. </el-form-item>
  1500. </el-col>
  1501. </el-form-item>
  1502. <!-- 用户名 -->
  1503. <!-- 字体 -->
  1504. <el-divider>字体配置</el-divider>
  1505. <el-form-item label="字体名称" label-width="130px" prop="fontFamily">
  1506. <el-cascader v-model="interactionFormData.fontFamily" :options="fontOptions" style="width:100%;" @change="handleFontSelectChange">
  1507. </elcascader>
  1508. </el-form-item>
  1509. <!-- 用户名 -->
  1510. <el-divider>用户名配置</el-divider>
  1511. <el-form-item>
  1512. <el-col :span="11">
  1513. <el-form-item label="字体颜色" label-width="130px" prop="userNameFontColor">
  1514. <el-color-picker v-model="interactionFormData.userNameFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1515. </el-form-item>
  1516. </el-col>
  1517. <el-col :span="13">
  1518. <el-form-item label="字体大小" label-width="100px" prop="userNameFontSize">
  1519. <el-input-number size="medium" v-model="interactionFormData.userNameFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1520. </el-form-item>
  1521. </el-row>
  1522. </el-form-item>
  1523. <el-form-item>
  1524. <el-col :span="11">
  1525. <el-form-item label="阴影颜色" label-width="130px" prop="userNameFontShadowColor">
  1526. <el-color-picker v-model="interactionFormData.userNameFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1527. </el-form-item>
  1528. </el-col>
  1529. <el-col :span="13">
  1530. <el-form-item label="阴影大小" label-width="100px" prop="userNameFontShadowSize">
  1531. <el-input-number size="medium" v-model="interactionFormData.userNameFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1532. </el-form-item>
  1533. </el-row>
  1534. </el-form-item>
  1535. <!-- 弹幕 -->
  1536. <el-divider>弹幕信息配置</el-divider>
  1537. <el-form-item>
  1538. <el-col :span="11">
  1539. <el-form-item label="背景颜色" label-width="130px" prop="danmakuBackgroundColor">
  1540. <el-color-picker v-model="interactionFormData.danmakuBackgroundColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1541. </el-form-item>
  1542. </el-col>
  1543. <el-col :span="13">
  1544. <el-form-item label="渐变为" label-width="100px" prop="danmakuBackgroundGradientColor">
  1545. <el-color-picker v-model="interactionFormData.danmakuBackgroundGradientColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1546. </el-form-item>
  1547. </el-col>
  1548. </el-form-item>
  1549. <el-form-item>
  1550. <el-col :span="11">
  1551. <el-form-item label="字体颜色" label-width="130px" prop="danmakuFontColor">
  1552. <el-color-picker v-model="interactionFormData.danmakuFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1553. </el-form-item>
  1554. </el-col>
  1555. <el-col :span="13">
  1556. <el-form-item label="字体大小" label-width="100px" prop="danmakuFontSize">
  1557. <el-input-number size="medium" v-model="interactionFormData.danmakuFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1558. </el-form-item>
  1559. </el-row>
  1560. </el-form-item>
  1561. <el-form-item>
  1562. <el-col :span="11">
  1563. <el-form-item label="阴影颜色" label-width="130px" prop="danmakuFontShadowColor">
  1564. <el-color-picker v-model="interactionFormData.danmakuFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1565. </el-form-item>
  1566. </el-col>
  1567. <el-col :span="13">
  1568. <el-form-item label="阴影大小" label-width="100px" prop="danmakuFontShadowSize">
  1569. <el-input-number size="medium" v-model="interactionFormData.danmakuFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1570. </el-form-item>
  1571. </el-row>
  1572. </el-form-item>
  1573. <el-form-item>
  1574. <el-col :span="11">
  1575. <el-form-item label="稿件背景颜色" label-width="130px" prop="danmakuDougaBackgroundColor">
  1576. <el-color-picker v-model="interactionFormData.danmakuDougaBackgroundColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1577. </el-form-item>
  1578. </el-col>
  1579. </el-form-item>
  1580. <el-form-item style="display:flex;justify-content:center;">
  1581. <el-button type="primary" @click="pushTestDanmakuMessage">测试一下</el-button>
  1582. </el-form-item>
  1583. <!-- 礼物 -->
  1584. <el-divider>礼物信息配置</el-divider>
  1585. <el-form-item>
  1586. <el-col :span="11">
  1587. <el-form-item label="背景颜色" label-width="130px" prop="giftBackgroundColor">
  1588. <el-color-picker v-model="interactionFormData.giftBackgroundColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1589. </el-form-item>
  1590. </el-col>
  1591. <el-col :span="13">
  1592. <el-form-item label="渐变为" label-width="100px" prop="giftBackgroundGradientColor">
  1593. <el-color-picker v-model="interactionFormData.giftBackgroundGradientColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1594. </el-form-item>
  1595. </el-col>
  1596. </el-form-item>
  1597. <el-form-item label="礼物信息" label-width="130px" prop="giftFormat">
  1598. <el-input v-model="interactionFormData.giftFormat" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1599. </el-form-item>
  1600. <el-form-item>
  1601. <el-col :span="11">
  1602. <el-form-item label="字体颜色" label-width="130px" prop="giftFontColor">
  1603. <el-color-picker v-model="interactionFormData.giftFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1604. </el-form-item>
  1605. </el-col>
  1606. <el-col :span="13">
  1607. <el-form-item label="字体大小" label-width="100px" prop="giftFontSize">
  1608. <el-input-number size="medium" v-model="interactionFormData.giftFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1609. </el-form-item>
  1610. </el-row>
  1611. </el-form-item>
  1612. <el-form-item>
  1613. <el-col :span="11">
  1614. <el-form-item label="阴影颜色" label-width="130px" prop="giftFontShadowColor">
  1615. <el-color-picker v-model="interactionFormData.giftFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1616. </el-form-item>
  1617. </el-col>
  1618. <el-col :span="13">
  1619. <el-form-item label="阴影大小" label-width="100px" prop="giftFontShadowSize">
  1620. <el-input-number size="medium" v-model="interactionFormData.giftFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1621. </el-form-item>
  1622. </el-row>
  1623. </el-form-item>
  1624. <el-form-item style="display:flex;justify-content:center;">
  1625. <el-button type="primary" @click="pushTestGiftMessage">测试一下</el-button>
  1626. </el-form-item>
  1627. <!-- 点赞 -->
  1628. <el-divider>点赞信息配置</el-divider>
  1629. <el-form-item label="点赞信息" label-width="130px" prop="likeFormat">
  1630. <el-input v-model="interactionFormData.likeFormat" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1631. </el-form-item>
  1632. <el-form-item>
  1633. <el-col :span="11">
  1634. <el-form-item label="字体颜色" label-width="130px" prop="likeFontColor">
  1635. <el-color-picker v-model="interactionFormData.likeFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1636. </el-form-item>
  1637. </el-col>
  1638. <el-col :span="13">
  1639. <el-form-item label="字体大小" label-width="100px" prop="likeFontSize">
  1640. <el-input-number size="medium" v-model="interactionFormData.likeFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1641. </el-form-item>
  1642. </el-row>
  1643. </el-form-item>
  1644. <el-form-item>
  1645. <el-col :span="11">
  1646. <el-form-item label="阴影颜色" label-width="130px" prop="likeFontShadowColor">
  1647. <el-color-picker v-model="interactionFormData.likeFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1648. </el-form-item>
  1649. </el-col>
  1650. <el-col :span="13">
  1651. <el-form-item label="阴影大小" label-width="100px" prop="likeFontShadowSize">
  1652. <el-input-number size="medium" v-model="interactionFormData.likeFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1653. </el-form-item>
  1654. </el-row>
  1655. </el-form-item>
  1656. <el-form-item style="display:flex;justify-content:center;">
  1657. <el-button type="primary" @click="pushTestLikeMessage">测试一下</el-button>
  1658. </el-form-item>
  1659. <!-- 进入直播间 -->
  1660. <el-divider>进入直播间信息配置</el-divider>
  1661. <el-form-item label="进入直播间信息" label-width="130px" prop="enterroomFormat">
  1662. <el-input v-model="interactionFormData.enterroomFormat" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1663. </el-form-item>
  1664. <el-form-item>
  1665. <el-col :span="11">
  1666. <el-form-item label="字体颜色" label-width="130px" prop="enterroomFontColor">
  1667. <el-color-picker v-model="interactionFormData.enterroomFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1668. </el-form-item>
  1669. </el-col>
  1670. <el-col :span="13">
  1671. <el-form-item label="字体大小" label-width="100px" prop="enterroomFontSize">
  1672. <el-input-number size="medium" v-model="interactionFormData.enterroomFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1673. </el-form-item>
  1674. </el-row>
  1675. </el-form-item>
  1676. <el-form-item>
  1677. <el-col :span="11">
  1678. <el-form-item label="阴影颜色" label-width="130px" prop="enterroomFontShadowColor">
  1679. <el-color-picker v-model="interactionFormData.enterroomFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1680. </el-form-item>
  1681. </el-col>
  1682. <el-col :span="13">
  1683. <el-form-item label="阴影大小" label-width="100px" prop="enterroomFontShadowSize">
  1684. <el-input-number size="medium" v-model="interactionFormData.enterroomFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1685. </el-form-item>
  1686. </el-row>
  1687. </el-form-item>
  1688. <el-form-item style="display:flex;justify-content:center;">
  1689. <el-button type="primary" @click="pushTestEnterRoomMessage">测试一下</el-button>
  1690. </el-form-item>
  1691. <!-- 关注 -->
  1692. <el-divider>关注信息配置</el-divider>
  1693. <el-form-item label="关注信息" label-width="130px" prop="followFormat">
  1694. <el-input v-model="interactionFormData.followFormat" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1695. </el-form-item>
  1696. <el-form-item>
  1697. <el-col :span="11">
  1698. <el-form-item label="字体颜色" label-width="130px" prop="followFontColor">
  1699. <el-color-picker v-model="interactionFormData.followFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1700. </el-form-item>
  1701. </el-col>
  1702. <el-col :span="13">
  1703. <el-form-item label="字体大小" label-width="100px" prop="followFontSize">
  1704. <el-input-number size="medium" v-model="interactionFormData.followFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1705. </el-form-item>
  1706. </el-row>
  1707. </el-form-item>
  1708. <el-form-item>
  1709. <el-col :span="11">
  1710. <el-form-item label="阴影颜色" label-width="130px" prop="followFontShadowColor">
  1711. <el-color-picker v-model="interactionFormData.followFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1712. </el-form-item>
  1713. </el-col>
  1714. <el-col :span="13">
  1715. <el-form-item label="阴影大小" label-width="100px" prop="followFontShadowSize">
  1716. <el-input-number size="medium" v-model="interactionFormData.followFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1717. </el-form-item>
  1718. </el-row>
  1719. </el-form-item>
  1720. <el-form-item style="display:flex;justify-content:center;">
  1721. <el-button type="primary" @click="pushTestFollowMessage">测试一下</el-button>
  1722. </el-form-item>
  1723. <!-- 加入守护团 -->
  1724. <el-divider>加入守护团信息配置</el-divider>
  1725. <el-form-item label="加入守护团信息" label-width="130px" prop="joinclubFormat">
  1726. <el-input v-model="interactionFormData.joinclubFormat" autocomplete="off" style="width:100%;" placeholder="请输入参与抽奖的弹幕关键词"></el-input>
  1727. </el-form-item>
  1728. <el-form-item>
  1729. <el-col :span="11">
  1730. <el-form-item label="字体颜色" label-width="130px" prop="joinclubFontColor">
  1731. <el-color-picker v-model="interactionFormData.joinclubFontColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1732. </el-form-item>
  1733. </el-col>
  1734. <el-col :span="13">
  1735. <el-form-item label="字体大小" label-width="100px" prop="joinclubFontSize">
  1736. <el-input-number size="medium" v-model="interactionFormData.joinclubFontSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1737. </el-form-item>
  1738. </el-row>
  1739. </el-form-item>
  1740. <el-form-item>
  1741. <el-col :span="11">
  1742. <el-form-item label="阴影颜色" label-width="130px" prop="joinclubFontShadowColor">
  1743. <el-color-picker v-model="interactionFormData.joinclubFontShadowColor" show-alpha style="width:100%;" :predefine="predefineColors"></el-color-picker>
  1744. </el-form-item>
  1745. </el-col>
  1746. <el-col :span="13">
  1747. <el-form-item label="阴影大小" label-width="100px" prop="joinclubFontShadowSize">
  1748. <el-input-number size="medium" v-model="interactionFormData.joinclubFontShadowSize" :min="0" controls-position="right" style="width:100%;"></el-input-number>
  1749. </el-form-item>
  1750. </el-row>
  1751. </el-form-item>
  1752. <el-form-item style="display:flex;justify-content:center;">
  1753. <el-button type="primary" @click="pushTestJoinClubMessage">测试一下</el-button>
  1754. </el-form-item>
  1755. </template>
  1756. <el-form-item style="display:flex;justify-content:center;">
  1757. <el-button type="primary" @click="handleInteractionFormSubmit">提交</el-button>
  1758. </el-form-item>
  1759. </el-form>
  1760. </el-dialog>
  1761. <el-dialog v-dialog-drag title="添加竞猜" :visible.sync="guessFormDialogVisible" :modal="false" width="550px" custom-class="guess-form-dialog" :close-on-click-modal="false">
  1762. <el-form :model="guessFormData" class="guess-form" ref="form" :rules="guessFormRules" @submit.native.prevent>
  1763. <el-form-item label="竞猜标题" label-width="150px" prop="title">
  1764. <el-input v-model="guessFormData.title" autocomplete="off" style="width:100%;"></el-input>
  1765. </el-form-item>
  1766. <el-form-item label="竞猜问题挑选方式" label-width="150px" prop="chooseWay">
  1767. <el-radio-group v-model="guessFormData.chooseWay" style="width:100%;">
  1768. <el-radio label="manual">手动挑选</el-radio>
  1769. <el-radio label="random">随机挑选</el-radio>
  1770. </el-radio-group>
  1771. </el-form-item>
  1772. <el-form-item label="竞猜问题挑选倒计时" label-width="150px" prop="countdown">
  1773. <el-input-number size="medium" v-model="guessFormData.countdown" :min="0" controls-position="right" style="width:50%;"></el-input-number><span>(秒,0表示不做限制)</span>
  1774. </el-form-item>
  1775. <el-form-item label="总共可竞猜次数" label-width="150px" prop="maxGuess">
  1776. <el-input-number size="medium" v-model="guessFormData.maxGuess" :min="0" controls-position="right" style="width:50%;"></el-input-number><span>(0表示不做限制)</span>
  1777. </el-form-item>
  1778. <el-form-item label="每个用户可竞猜次数" label-width="150px" prop="maxGuessPerUser">
  1779. <el-input-number size="medium" v-model="guessFormData.maxGuessPerUser" :min="0" controls-position="right" style="width:50%;"></el-input-number><span>(0表示不做限制)</span>
  1780. </el-form-item>
  1781. <el-form-item label="总共可狙击次数" label-width="150px" prop="maxDirectGuess">
  1782. <el-input-number size="medium" v-model="guessFormData.maxDirectGuess" :min="0" controls-position="right" style="width:50%;"></el-input-number><span>(0表示不做限制)</span>
  1783. </el-form-item>
  1784. <el-form-item label="每个用户可狙击次数" label-width="150px" prop="maxDirectGuessPerUser">
  1785. <el-input-number size="medium" v-model="guessFormData.maxDirectGuessPerUser" :min="0" controls-position="right" style="width:50%;"></el-input-number><span>(0表示不做限制)</span>
  1786. </el-form-item>
  1787. <el-form-item style="display:flex;justify-content:center;">
  1788. <el-button type="primary" @click="handleGuessFormSubmit">立即创建</el-button>
  1789. </el-form-item>
  1790. </el-form>
  1791. </el-dialog>
  1792. <el-dialog ref="guessStartDialog" v-dialog-drag title="竞猜详情" :visible.sync="guessStartDialogVisible" :modal="false" width="1000px" custom-class="guess-start-dialog" :close-on-click-modal="false" @closed="handleGuessStartDialogClose">
  1793. <template v-if="activeGuess">
  1794. <el-row>
  1795. <span>{{activeGuess.displayDirectGuess?'狙击':'竞猜'}}问题列表</span>
  1796. <el-button type="primary" @click="handleGuessDanmakuSubmit" size="small" v-if="activeGuess.status == '进行中' && (activeGuess.maxGuess==0 || activeGuess.maxGuess > activeGuess.guessList.length)">开始下一个问题</el-button>
  1797. <el-button type="primary" @click="activeGuess.displayDirectGuess = !activeGuess.displayDirectGuess" size="small">{{activeGuess.displayDirectGuess?'隐藏狙击问题':'显示狙击问题'}}</el-button>
  1798. <el-button type="primary" @click="activeGuess.onlyDisplayMatch = !activeGuess.onlyDisplayMatch" size="small">{{activeGuess.onlyDisplayMatch?'显示未命中问题':'隐藏未命中问题'}}</el-button>
  1799. </el-row>
  1800. <el-table :data="activeGuessGuessListData" style="width: 100%" class="guess-list-table" border ref="guessListTable" height="330">
  1801. <el-table-column prop="index" label="序号" min-width="5%">
  1802. </el-table-column>
  1803. <el-table-column prop="userName" label="用户" min-width="25%">
  1804. <template slot-scope="scope">
  1805. <el-avatar shape="circle" fit="fill" size="40" :src="scope.row.photo"></el-avatar>
  1806. <span>{{scope.row.userName}}</span>
  1807. </template>
  1808. </el-table-column>
  1809. <el-table-column prop="guess" label="问题" min-width="60%">
  1810. </el-table-column>
  1811. <el-table-column prop="isValid" label="是否命中" min-width="10%">
  1812. <template slot-scope="scope">
  1813. <i class="el-icon-check" v-if="scope.row.isValid==true" style="font-size:50px;color:#409EFF;"></i>
  1814. <i class="el-icon-close" v-if="scope.row.isValid==false" style="font-size:50px;color:#F56C6C;"></i>
  1815. </template>
  1816. </el-table-column>
  1817. </el-table>
  1818. </template>
  1819. </el-dialog>
  1820. <el-dialog ref="guessDanmakuDialog" v-dialog-drag :visible.sync="guessDanmakuDialogVisible" :modal="false" width="550px" custom-class="guess-danmaku-dialog" :close-on-click-modal="false" :destroy-on-close="true" @closed="handleGuessDanmakuDialogClose">
  1821. <template slot="title">
  1822. <el-row type="flex">
  1823. <span class="el-dialog__title">弹幕竞猜</span>
  1824. <span style="font-size:16px;color:#303133" v-show="activeGuess && activeGuess.chooseWay == 'random'">
  1825. <span>(倒计时</span>
  1826. <span style="background-color:#66b1ff;color:white;border-radius:5px;font-weight:bold;" id="guess-danmaku-countdown-text"></span>
  1827. <span>)</span>
  1828. </span>
  1829. </el-row>
  1830. </template>
  1831. <el-row class="guess-danmaku-container">
  1832. </el-row>
  1833. </el-dialog>
  1834. <el-dialog v-dialog-drag title="查看竞猜" :visible.sync="guessTableDialogVisible" :modal="false" width="1000px" custom-class="guess-table-dialog" :close-on-click-modal="false">
  1835. <el-table :data="guessTableData" style="width: 100%" class="guess-table" >
  1836. <el-table-column prop="title" label="竞猜标题" min-width="50%">
  1837. </el-table-column>
  1838. <el-table-column prop="status" label="状态" min-width="10%">
  1839. </el-table-column>
  1840. <el-table-column prop="winner" label="狙击成功用户" min-width="30%">
  1841. <template slot-scope="scope">
  1842. <template v-if="scope.row.winner">
  1843. <el-avatar shape="circle" fit="fill" size="40" :src="scope.row.winner.photo"></el-avatar>
  1844. <span>{{scope.row.winner.userName}}</span>
  1845. </template>
  1846. <span v-else></span>
  1847. </template>
  1848. </el-table-column>
  1849. <el-table-column label="操作" min-width="10%">
  1850. <template slot-scope="scope">
  1851. <el-button type="text" @click="openGuessStartDialog(scope.row)">进入竞猜</el-button>
  1852. </template>
  1853. </el-table-column>
  1854. </el-table>
  1855. </el-dialog>
  1856. <div id="douga-result" style="display:none;"></div>
  1857. </div>
  1858. `;
  1859. containerEle.append(menuEle);
  1860. // 对话框拖动指令
  1861. Vue.directive('dialog-drag', function(el, binding, vnode, oldVnode) {
  1862. // 弹框可拉伸最小宽高
  1863. const minWidth = 400
  1864. const minHeight = 300
  1865. // 初始非全屏
  1866. let isFullScreen = false
  1867. // 当前顶部高度
  1868. let nowMarginTop = 0
  1869. // 获取弹框头部(这部分可双击全屏)
  1870. const dialogHeaderEl = el.querySelector('.el-dialog__header')
  1871. // 弹窗
  1872. const dragDom = el.querySelector('.el-dialog')
  1873. // 给弹窗加上overflow auto;不然缩小时框内的标签可能超出dialog;
  1874. dragDom.style.overflow = 'hidden'
  1875. // 清除选择头部文字效果
  1876. // dialogHeaderEl.onselectstart = new Function("return false");
  1877. // 头部加上可拖动cursor
  1878. dialogHeaderEl.style.cursor = 'move'
  1879. // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
  1880. const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
  1881. const moveDown = e => {
  1882. // 鼠标按下,计算当前元素距离可视区的距离
  1883. const disX = e.clientX - dialogHeaderEl.offsetLeft
  1884. const disY = e.clientY - dialogHeaderEl.offsetTop
  1885. // 获取到的值带px 正则匹配替换
  1886. let styL, styT
  1887. // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
  1888. if (sty.left.includes('%')) {
  1889. styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
  1890. styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
  1891. } else {
  1892. styL = +sty.left.replace(/\px/g, '')
  1893. styT = +sty.top.replace(/\px/g, '')
  1894. }
  1895. document.onmousemove = function(e) {
  1896. // 通过事件委托,计算移动的距离
  1897. const l = e.clientX - disX
  1898. const t = e.clientY - disY
  1899. // 移动当前元素
  1900. dragDom.style.left = `${l + styL}px`
  1901. dragDom.style.top = `${t + styT}px`
  1902. // 将此时的位置传出去
  1903. // binding.value({x:e.pageX,y:e.pageY})
  1904. }
  1905. document.onmouseup = function(e) {
  1906. document.onmousemove = null
  1907. document.onmouseup = null
  1908. }
  1909. }
  1910. dialogHeaderEl.onmousedown = moveDown
  1911. // 当前宽高
  1912. let nowWidth = 0
  1913. // let nowHight = 0
  1914. // 双击头部全屏效果
  1915. dialogHeaderEl.ondblclick = e => {
  1916. if (isFullScreen === false) {
  1917. // nowHight = dragDom.clientHeight
  1918. nowWidth = dragDom.clientWidth
  1919. nowMarginTop = dragDom.style.marginTop
  1920. dragDom.style.left = 0
  1921. dragDom.style.top = 0
  1922. dragDom.style.height = '100VH'
  1923. dragDom.style.width = '100VW'
  1924. dragDom.style.marginTop = 0
  1925. isFullScreen = true
  1926. dialogHeaderEl.style.cursor = 'initial'
  1927. dialogHeaderEl.onmousedown = null
  1928. } else {
  1929. dragDom.style.height = 'auto'
  1930. dragDom.style.width = nowWidth + 'px'
  1931. dragDom.style.marginTop = nowMarginTop
  1932. isFullScreen = false
  1933. dialogHeaderEl.style.cursor = 'move'
  1934. dialogHeaderEl.onmousedown = moveDown
  1935. }
  1936. }
  1937. dragDom.onmousemove = function(e) {
  1938. // let moveE = e
  1939. if (
  1940. e.clientX > dragDom.offsetLeft + dragDom.clientWidth - 10 ||
  1941. dragDom.offsetLeft + 10 > e.clientX
  1942. ) {
  1943. dragDom.style.cursor = 'w-resize'
  1944. } else if (
  1945. el.scrollTop + e.clientY >
  1946. dragDom.offsetTop + dragDom.clientHeight - 10
  1947. ) {
  1948. dragDom.style.cursor = 's-resize'
  1949. } else {
  1950. dragDom.style.cursor = 'default'
  1951. dragDom.onmousedown = null
  1952. }
  1953. dragDom.onmousedown = e => {
  1954. const clientX = e.clientX
  1955. const clientY = e.clientY
  1956. const elW = dragDom.clientWidth
  1957. const elH = dragDom.clientHeight
  1958. const EloffsetLeft = dragDom.offsetLeft
  1959. const EloffsetTop = dragDom.offsetTop
  1960. dragDom.style.userSelect = 'none'
  1961. const ELscrollTop = el.scrollTop
  1962. // 判断点击的位置是不是为头部
  1963. if (
  1964. clientX > EloffsetLeft &&
  1965. clientX < EloffsetLeft + elW &&
  1966. clientY > EloffsetTop &&
  1967. clientY < EloffsetTop + 100
  1968. ) {
  1969. // 如果是头部在此就不做任何动作,以上有绑定dialogHeaderEl.onmousedown = moveDown;
  1970. } else {
  1971. document.onmousemove = function(e) {
  1972. // 移动时禁用默认事件
  1973. e.preventDefault()
  1974. // 左侧鼠标拖拽位置
  1975. if (clientX > EloffsetLeft && clientX < EloffsetLeft + 10) {
  1976. // 往左拖拽
  1977. if (clientX > e.clientX) {
  1978. dragDom.style.width = elW + (clientX - e.clientX) * 2 + 'px'
  1979. }
  1980. // 往右拖拽
  1981. if (clientX < e.clientX) {
  1982. if (dragDom.clientWidth < minWidth) {
  1983. } else {
  1984. dragDom.style.width = elW - (e.clientX - clientX) * 2 + 'px'
  1985. }
  1986. }
  1987. }
  1988. // 右侧鼠标拖拽位置
  1989. if (
  1990. clientX > EloffsetLeft + elW - 10 &&
  1991. clientX < EloffsetLeft + elW
  1992. ) {
  1993. // 往左拖拽
  1994. if (clientX > e.clientX) {
  1995. if (dragDom.clientWidth < minWidth) {
  1996. } else {
  1997. dragDom.style.width = elW - (clientX - e.clientX) * 2 + 'px'
  1998. }
  1999. }
  2000. // 往右拖拽
  2001. if (clientX < e.clientX) {
  2002. dragDom.style.width = elW + (e.clientX - clientX) * 2 + 'px'
  2003. }
  2004. }
  2005. // 底部鼠标拖拽位置
  2006. if (
  2007. ELscrollTop + clientY > EloffsetTop + elH - 20 &&
  2008. ELscrollTop + clientY < EloffsetTop + elH
  2009. ) {
  2010. // 往上拖拽
  2011. if (clientY > e.clientY) {
  2012. if (dragDom.clientHeight < minHeight) {
  2013. } else {
  2014. dragDom.style.height = elH - (clientY - e.clientY) * 2 + 'px'
  2015. }
  2016. }
  2017. // 往下拖拽
  2018. if (clientY < e.clientY) {
  2019. dragDom.style.height = elH + (e.clientY - clientY) * 2 + 'px'
  2020. }
  2021. }
  2022. }
  2023. // 拉伸结束
  2024. document.onmouseup = function(e) {
  2025. document.onmousemove = null
  2026. document.onmouseup = null
  2027. vue.onDialogResize(dragDom);
  2028. }
  2029. }
  2030. }
  2031. }
  2032. });
  2033. Vue.use(ELEMENT);
  2034. Vue.prototype.$message = ELEMENT.Message;
  2035. if(!document.querySelector('.container-live-feed-messages-acfunlive')){
  2036. return false;
  2037. }
  2038. // 初始化实例
  2039. vue = new Vue({
  2040. el : '.container-live-feed-messages-acfunlive',
  2041. components : {
  2042. },
  2043. data : function(){
  2044. var vue = this;
  2045. var lotteryFormDataDefault = {
  2046. joinMethod : [],
  2047. weight : false,
  2048. joinGiftList : [],
  2049. mutualLotteryList : [],
  2050. countdown : 10,
  2051. status : '未开始',
  2052. candidates : {},
  2053. winners : [],
  2054. startCandidates : [],
  2055. medalName : '',
  2056. medalLevel : 0,
  2057. source : 'livestream',
  2058. dougaType : 'video',
  2059. dougaList : [],
  2060. dougaListPage : 1,
  2061. dougaListDisabled : false,
  2062. dougaListLoading : false,
  2063. dougaListNoMore : false,
  2064. dougaListActiveIndex : null,
  2065. loading : false,
  2066. rolling : false,
  2067. loadingText : '',
  2068. winnerCount : 0,
  2069. };
  2070. var guessFormDataDefault = {
  2071. title : '',
  2072. countdown : 20,
  2073. chooseWay : 'random',
  2074. status : '进行中',
  2075. maxGuess : 0,
  2076. maxGuessPerUser : 0,
  2077. maxQuestionPerUser : 0,
  2078. maxDirectGuess : 0,
  2079. maxDirectGuessPerUser : 1,
  2080. guessList : [],
  2081. matchGuessList : [],
  2082. directGuessList : [],
  2083. guessIndex : 1,
  2084. displayDirectGuess : false,
  2085. onlyDisplayMatch : false,
  2086. };
  2087. var interactionFormDataInit = {
  2088. enable : true,
  2089. historyMinutes : 10,
  2090. historyCount : 200,
  2091. direction : 'vertical',
  2092. displayNotWrap : false,
  2093. fontFamily : ['default', 'inherit'],
  2094. userNameFontSize : 18,
  2095. userNameFontColor : 'rgba(255, 69, 0, 1)',
  2096. userNameFontShadowColor : 'rgba(0, 0, 0, 1)',
  2097. userNameFontShadowSize : 1,
  2098. danmakuBackgroundColor : 'rgba(30, 144, 255, 1)',
  2099. danmakuBackgroundGradientColor : 'rgba(30, 144, 255, 1)',
  2100. danmakuFontSize : 25,
  2101. danmakuFontColor : 'rgba(255, 255, 255, 1)',
  2102. danmakuFontShadowColor : 'rgba(0, 0, 0, 1)',
  2103. danmakuFontShadowSize : 1,
  2104. danmakuDougaBackgroundColor: 'rgba(255, 255, 255, 1)',
  2105. giftBackgroundColor: 'rgba(255, 166, 104, 1)',
  2106. giftBackgroundGradientColor: 'rgba(252, 91, 108, 1)',
  2107. giftFormat : '送出了${礼物名称}',
  2108. giftFontSize : 25,
  2109. giftFontColor : 'rgba(255, 176, 35, 1)',
  2110. giftFontShadowColor : 'rgba(0, 0, 0, 1)',
  2111. giftFontShadowSize : 1,
  2112. likeFormat : '点赞了',
  2113. likeFontSize : 18,
  2114. likeFontColor : 'rgba(255, 0, 0, 1)',
  2115. likeFontShadowColor : 'rgba(255, 255, 255, 1)',
  2116. likeFontShadowSize : 2,
  2117. enterroomFormat : '进入直播间',
  2118. enterroomFontSize : 18,
  2119. enterroomFontColor : 'rgba(30, 144, 255, 1)',
  2120. enterroomFontShadowColor : '#FFF',
  2121. enterroomFontShadowSize : 2,
  2122. followFormat : '关注了主播',
  2123. followFontSize : 20,
  2124. followFontColor : 'rgba(199, 21, 133, 1)',
  2125. followFontShadowColor : 'rgba(255, 255, 255, 1)',
  2126. followFontShadowSize : 2,
  2127. joinclubFormat : '加入主播的守护团',
  2128. joinclubFontSize : 20,
  2129. joinclubFontColor : 'rgba(199, 21, 133, 1)',
  2130. joinclubFontShadowColor : 'rgba(255, 255, 255, 1)',
  2131. joinclubFontShadowSize : 2,
  2132. backgroundColor: 'rgba(0, 204, 0, 1)',
  2133. borderShape: 'round',
  2134. borderWidth: 'auto',
  2135. borderSize: 2,
  2136. borderColor: 'rgba(30, 144, 255, 1)',
  2137. };
  2138. return {
  2139. messageData : [],
  2140. lastDanmakuMessage : null,
  2141. userInfo : userInfo,
  2142. uid : config.UID,
  2143. loginFormDialogVisible : false,
  2144. lotteryTableDialogVisible : false,
  2145. lotteryFormDialogVisible : false,
  2146. userTableDialogVisible : false,
  2147. lotteryCountdownDialogVisible : false,
  2148. lotteryStartDialogVisible : false,
  2149. lotteryFormDialogTitle : '',
  2150. userTableDialogTitle : '',
  2151. audioFormDialogVisible : false,
  2152. interactionFormDialogVisible : false,
  2153. interactionPresetDialogVisible: false,
  2154. loginFormData : {
  2155. token : '',
  2156. },
  2157. loginFormRules : {
  2158. token : [{
  2159. required : true, message : '请输入口令', trigger : 'blur',
  2160. }],
  2161. },
  2162. lotteryFormDataDefault : _.cloneDeep(lotteryFormDataDefault),
  2163. lotteryFormData : _.cloneDeep(lotteryFormDataDefault),
  2164. lotteryFormRules : {
  2165. name : [{
  2166. required : true, message : '请输入奖品名称', trigger : 'blur',
  2167. }],
  2168. countdown : [{
  2169. required : true, message : '请输入抽奖倒计时', trigger : 'blur',
  2170. }],
  2171. startTime : [{
  2172. required : true, message : '请设置开奖时间', trigger : 'blur',
  2173. },{
  2174. trigger : 'blur',
  2175. message : '开奖时间与当前时间差距低于1分钟',
  2176. validator : function(rule, value, callback){
  2177. if(value && moment(value, 'HH:mm:ss').subtract(1, 'minutes') < moment()){
  2178. callback(new Error(rule.message));
  2179. }
  2180. else{
  2181. callback();
  2182. }
  2183. },
  2184. },],
  2185. /*joinMethod : [{
  2186. required : true, message : '请设置抽奖参与方式', trigger : 'blur',
  2187. },{
  2188. trigger : 'blur',
  2189. message : '请选择至少一个抽奖参与方式',
  2190. validator : function(rule, value, callback){
  2191. if(value.length==0){
  2192. callback(new Error(rule.message));
  2193. }
  2194. else{
  2195. callback();
  2196. }
  2197. },
  2198. }],*/
  2199. /*joinKeyword : [{
  2200. trigger : 'blur',
  2201. message : '请输入参与抽奖的弹幕关键词',
  2202. validator : function(rule, value, callback){
  2203. // 抽奖参与方式为弹幕
  2204. if(vue.lotteryFormData.joinMethod == 'danmaku' && !value){
  2205. callback(new Error(rule.message));
  2206. }else{
  2207. callback();
  2208. }
  2209. },
  2210. }],*/
  2211. /*joinGiftList : [{
  2212. trigger : 'blur',
  2213. message : '请选择参与抽奖的礼物,至少一个',
  2214. validator : function(rule, value, callback){
  2215. // 抽奖参与方式为礼物
  2216. if(vue.lotteryFormData.joinMethod == 'gift' && (!value || value.length==0)){
  2217. callback(new Error(rule.message));
  2218. }else{
  2219. callback();
  2220. }
  2221. },
  2222. }],*/
  2223. },
  2224. giftList : giftList,
  2225. giftNameMapper : {},
  2226. lotteryTableData : [],
  2227. userTableData : [],
  2228. // 时间筛选范围,为当前时间至23:59:59
  2229. timePickerOptions : {
  2230. selectableRange : `${moment().format("HH:mm:ss")} - 23:59:59`,
  2231. "value-format" : 'HH:mm:ss',
  2232. },
  2233. // 当前活跃抽奖
  2234. activeLottery : {},
  2235. // 可选语音
  2236. audioVoiceOptions : null,
  2237. // 语速
  2238. speechRateMarks : {
  2239. 250 : '更慢',
  2240. 500 : '默认',
  2241. 750 : '更快',
  2242. },
  2243. speechVolumeMarks : {
  2244. 25 : '更小',
  2245. 50 : '默认',
  2246. 75 : '更大',
  2247. },
  2248. // 语音配置表单规则
  2249. audioFormRules : {
  2250. },
  2251. // 语音配置表单数据
  2252. audioFormData : {
  2253. voice : ['general', 'Xiaoyun'],
  2254. enable : false,
  2255. speechRate : 500,
  2256. speechVolume : 50,
  2257. joinMethod : ['danmaku', 'gift', 'follow'],
  2258. danmakuFormat : '${用户名}说${弹幕内容}',
  2259. likeFormat : '${用户名}点赞了',
  2260. giftFormat : '${用户名}送出${礼物数量}个${礼物名称}',
  2261. enterroomFormat : '${用户名}进入直播间',
  2262. followFormat : '${用户名}关注了主播',
  2263. danmakuTimeout : 0,
  2264. likeTimeout : 0,
  2265. giftTimeout : 2,
  2266. enterroomTimeout : 0,
  2267. followTimeout : 0,
  2268. consumeing : false,
  2269. interactions : [],
  2270. messageBuffer : {},
  2271. },
  2272. // 弹幕配置表单规则
  2273. interactionFormRules : {},
  2274. interactionFormDataInit: interactionFormDataInit,
  2275. // 弹幕配置表单数据
  2276. interactionFormData : {
  2277. index: null,
  2278. name: "",
  2279. ..._.cloneDeep(interactionFormDataInit)
  2280. },
  2281. // 竞猜表单
  2282. guessFormDialogVisible : false,
  2283. guessFormDataDefault : _.cloneDeep(guessFormDataDefault),
  2284. guessFormData : _.cloneDeep(guessFormDataDefault),
  2285. guessFormRules : {
  2286. title : [{
  2287. required : true, message : '竞猜标题不可为空', trigger : 'blur',
  2288. }],
  2289. countdown : [{
  2290. trigger : 'blur',
  2291. message : '竞猜挑选方式为“随机挑选”时,竞猜挑选倒计时至少为10(秒)',
  2292. validator : function(rule, value, callback){
  2293. if(vue.guessFormData.chooseWay == 'random' && value < 10){
  2294. callback(new Error(rule.message));
  2295. }
  2296. else{
  2297. callback();
  2298. }
  2299. },
  2300. }],
  2301. },
  2302. // 当前活跃竞猜
  2303. activeGuess : null,
  2304. // 竞猜表格
  2305. guessTableDialogVisible : false,
  2306. guessTableData : [],
  2307. // 开始竞猜
  2308. guessStartDialogVisible : false,
  2309. // 弹幕竞猜
  2310. guessDanmakuDialogVisible : false,
  2311. // 请求acfun用户投稿时使用
  2312. reqId : 1,
  2313. // 鼠标悬浮状态
  2314. mouseOn : false,
  2315. // 预定义颜色
  2316. predefineColors : [
  2317. '#ff4500',
  2318. '#ff8c00',
  2319. '#ffd700',
  2320. '#90ee90',
  2321. '#00ced1',
  2322. '#1e90ff',
  2323. '#c71585',
  2324. 'rgba(255, 69, 0, 0.68)',
  2325. 'rgb(255, 120, 0)',
  2326. 'hsv(51, 100, 98)',
  2327. 'hsva(120, 40, 94, 0.5)',
  2328. 'hsl(181, 100%, 37%)',
  2329. 'hsla(209, 100%, 56%, 0.73)',
  2330. '#c7158577'
  2331. ],
  2332. // 字体选项
  2333. fontOptions : null,
  2334. // 下载过的字体
  2335. downloadedFonts : {},
  2336. // 弹幕配置
  2337. danmaku : null,
  2338. candidateInfoCallbacks : [],
  2339. isGettingCandidate : false,
  2340. // 弹幕配置预设值
  2341. interactionPreset : [
  2342. ],
  2343. };
  2344. },
  2345. methods : {
  2346. filter : _.filter,
  2347. // 下拉菜单点击回调
  2348. handleDropdownMenuClick : function(command){
  2349. var vue = this;
  2350. // 添加抽奖
  2351. if(command=='newLottery'){
  2352. this.addOrUpdateLottery();
  2353. }
  2354. // 查看抽奖表格
  2355. else if(command=='lotteryTable'){
  2356. this.lotteryTableDialogVisible = true;
  2357. }
  2358. // 导出中奖名单
  2359. else if(command=='exportLottery'){
  2360. this.exportLottery();
  2361. }
  2362. // 添加竞猜
  2363. else if(command=='newGuess'){
  2364. this.guessFormDialogVisible = true;
  2365. }
  2366. // 查看竞猜表格
  2367. else if(command=='guessTable'){
  2368. this.guessTableDialogVisible = true;
  2369. }
  2370. // 弹幕编辑
  2371. else if(command=='editInteraction'){
  2372. this.interactionPresetDialogVisible = true;
  2373. }
  2374. // 帮助手册
  2375. else if(command == 'help'){
  2376. window.open('https://docs.qq.com/doc/DZXF2aFJWb0FxR3N4', '_blank');
  2377. }
  2378. },
  2379. // 对话框大小修改
  2380. onDialogResize : function(diagDom){
  2381. },
  2382. // 抽奖表单提交回调
  2383. handleLotteryFormSubmit : function(){
  2384. var vue = this;
  2385. function addOrUpdateLottery(){
  2386. var isNew = !vue.lotteryFormData.id;
  2387. // 设置状态
  2388. vue.lotteryFormData.status = '未开始';
  2389. // 设置开奖时间文字
  2390. vue.lotteryFormData.startTimeText = moment(vue.lotteryFormData.startTime).format('HH:mm:ss');
  2391. // 备选
  2392. vue.lotteryFormData.candidates = {};
  2393. vue.lotteryFormData.startCandidates = [];
  2394. // 获奖者
  2395. vue.lotteryFormData.winners = [];
  2396. // 新增抽奖
  2397. if(!vue.lotteryFormData.id){
  2398. // 添加id
  2399. vue.lotteryFormData.id = uuidv4();
  2400. vue.lotteryTableData.splice(0, 0, vue.lotteryFormData);
  2401. }
  2402. // 注册(不可用)抽奖回调
  2403. vue.registerLotteryCallback(vue.lotteryFormData);
  2404. vue.lotteryFormDialogVisible = false;
  2405. vue.$message({
  2406. message : isNew?'添加抽奖成功':'修改抽奖信息成功',
  2407. type : 'info',
  2408. duration : 2000,
  2409. });
  2410. }
  2411. this.$refs.form.validate((valid) => {
  2412. // 通过校验
  2413. if(valid){
  2414. // 如果是稿件抽奖,查看是否有选中稿件
  2415. if(this.lotteryFormData.source == 'douga' && this.lotteryFormData.dougaListActiveIndex==null){
  2416. this.$message({
  2417. message : '请选择稿件',
  2418. type : 'error',
  2419. });
  2420. return;
  2421. }
  2422. // 稿件抽奖
  2423. if(this.lotteryFormData.source=='douga'){
  2424. var processMessage = null;;
  2425. // 获取稿件评论
  2426. this.getCommentList(this.lotteryFormData.dougaList[this.lotteryFormData.dougaListActiveIndex], true, function(isSuccess, isFinish, commentCount, page, totalPage){
  2427. // 错误
  2428. if(!isSuccess){
  2429. vue.lotteryFormData.loading = false;
  2430. vue.$message({
  2431. message : '获取稿件评论信息错误',
  2432. type : 'error',
  2433. });
  2434. }
  2435. else{
  2436. // 开始加载
  2437. vue.lotteryFormData.loadingText = `正在加载 ${page}/${totalPage} 页评论`;
  2438. vue.lotteryFormData.loading = true;
  2439. // 加载完毕
  2440. if(isFinish){
  2441. vue.lotteryFormData.loading = false;
  2442. addOrUpdateLottery();
  2443. }
  2444. }
  2445. });
  2446. }
  2447. else{
  2448. addOrUpdateLottery();
  2449. }
  2450. }
  2451. else{
  2452. this.$message({
  2453. message : '提交失败,请检查是否填写正确',
  2454. type : 'error',
  2455. });
  2456. }
  2457. });
  2458. },
  2459. // 修改抽奖信息
  2460. handleLotteryModify : function(lottery){
  2461. this.addOrUpdateLottery(lottery);
  2462. },
  2463. // 复制抽奖
  2464. handleLotteryCopy : function(lottery){
  2465. this.addOrUpdateLottery(lottery, true);
  2466. },
  2467. // 查看候选人
  2468. handleLotteryCandidatesCheck : function(lottery){
  2469. var userTableData = [];
  2470. for(var uid in lottery.candidates){
  2471. var candidate = lottery.candidates[uid];
  2472. userTableData.push(candidate);
  2473. }
  2474. this.userTableData = _.sortBy(userTableData, 'index');
  2475. this.userTableDialogTitle = '候选名单';
  2476. this.userTableDialogVisible = true;
  2477. },
  2478. // 查看中奖者
  2479. handleLotteryWinnersCheck : function(lottery){
  2480. this.userTableData = lottery.winners;
  2481. this.userTableDialogTitle = '中奖名单';
  2482. this.userTableDialogVisible = true;
  2483. },
  2484. login : function(callback){
  2485. this.loginFormDialogVisible = true;
  2486. if(_.isFunction(callback)){
  2487. this.loginFormData.nextCallback = callback;
  2488. }
  2489. else{
  2490. this.loginFormData.nextCallback = null;
  2491. }
  2492. },
  2493. addOrUpdateLottery : function(lottery, copy){
  2494. if(lottery && lottery.id){
  2495. // 复制抽奖信息
  2496. if(copy){
  2497. this.lotteryFormData = {
  2498. name : lottery.name,
  2499. weight : lottery.weight,
  2500. soucre : lottery.source,
  2501. startTime : lottery.startTime,
  2502. countdown : lottery.countdown,
  2503. medalName : lottery.medalName,
  2504. medalLevel : lotter.medalLevel,
  2505. mutualLotteryList : _.cloneDeep(lottery.mutualLotteryList),
  2506. joinMethod : _.cloneDeep(lottery.joinMethod),
  2507. joinKeyword : lottery.joinKeyword,
  2508. joinGiftList : _.cloneDeep(lottery.joinGiftList),
  2509. candidates : {},
  2510. startCandidates : [],
  2511. winners : [],
  2512. dougaType : lottery.dougaType,
  2513. dougaList : lottery.dougaList,
  2514. dougaListPage : lottery.dougaListPage,
  2515. dougaListDisabled : lottery.dougaListDisabled,
  2516. dougaListLoading : lottery.dougaListLoading,
  2517. dougaListNoMore : lottery.dougaListNoMore,
  2518. dougaListActiveIndex : lottery.dougaListActiveIndex,
  2519. };
  2520. this.lotteryFormDialogTitle = '添加抽奖';
  2521. }
  2522. // 修改抽奖信息
  2523. else{
  2524. this.lotteryFormData = lottery;
  2525. this.lotteryFormDialogTitle = '修改抽奖';
  2526. }
  2527. }
  2528. else{
  2529. this.lotteryFormData = _.cloneDeep(this.lotteryFormDataDefault);
  2530. this.lotteryFormDialogTitle = '添加抽奖';
  2531. }
  2532. this.lotteryFormDialogVisible = true;
  2533. },
  2534. // 注册(不可用)抽奖回调
  2535. registerLotteryCallback : function(lottery){
  2536. var vue = this;
  2537. function updateCandidates(){
  2538. var candidates = {};
  2539. lottery.dougaList[lottery.dougaListActiveIndex].commentList.forEach(function(comment, index){
  2540. var pass = true;
  2541. // 如果设置了互斥抽奖
  2542. if(lottery.mutualLotteryList && lottery.mutualLotteryList.length>0){
  2543. _.filter(vue.lotteryTableData, function(o){return lottery.mutualLotteryList.indexOf(o.id)!=-1}).every(function(anotherLottery){
  2544. if(_.findIndex(anotherLottery.winners, {uid:comment.uid})!=-1){
  2545. pass = false;
  2546. return false;
  2547. }
  2548. else{
  2549. return true;
  2550. }
  2551. });
  2552. }
  2553. if(!pass){
  2554. return;
  2555. }
  2556. if(!(comment.uid in candidates)){
  2557. let candidate = {
  2558. index : -comment.floor,
  2559. uid : comment.uid,
  2560. userName : comment.userName,
  2561. photo : comment.photo,
  2562. };
  2563. // 如果筛选了牌子
  2564. if(lottery.medalName && lottery.medalLevel){
  2565. vue.getCandidateInfo(candidate, (isSuccess, )=>{
  2566. if(candidate.medalName == lottery.medalName && candidate.medalLevel >= lottery.medalLevel){
  2567. candidates[comment.uid] = candidate;
  2568. }
  2569. });
  2570. }
  2571. else{
  2572. candidates[comment.uid] = candidate;
  2573. }
  2574. }
  2575. });
  2576. lottery.candidates = candidates;
  2577. }
  2578. // 如果是稿件抽奖
  2579. if(lottery.source=='douga'){
  2580. // 是否注册(不可用)过数据回调
  2581. if(lottery.messageHandler){
  2582. window.clearInterval(lottery.messageHandler);
  2583. lottery.messageHandler = null;
  2584. // 清空候选
  2585. lottery.candidates = {};
  2586. lottery.startCandidates = [];
  2587. lottery.winners = [];
  2588. }
  2589. // 更新候选名单
  2590. updateCandidates();
  2591. // 注册(不可用)数据回调(每30秒执行)
  2592. /*lottery.messageHandler = window.setInterval(function(){
  2593. // 获取评论
  2594. vue.getCommentList(lottery.dougaList[lottery.dougaListActiveIndex], true, function(isSuccess, isFinish, commentCount, page, totalPage){
  2595. // 获取完毕
  2596. if(isFinish){
  2597. // 更新候选名单
  2598. updateCandidates();
  2599. }
  2600. });
  2601. }, 30 * 1000);*/
  2602. }
  2603. // 直播间抽奖
  2604. else{
  2605. // 是否注册(不可用)过数据回调
  2606. if(lottery.messageHandler){
  2607. callbackManager.cancel(lottery.messageHandler);
  2608. lottery.messageHandler = null;
  2609. // 清空候选
  2610. lottery.candidates = {};
  2611. lottery.startCandidates = [];
  2612. lottery.winners = [];
  2613. }
  2614. // 注册(不可用)数据回调
  2615. lottery.messageHandler = callbackManager.register('message', function(data){
  2616. // 不符合参与方式
  2617. if(!(
  2618. (lottery.joinMethod.indexOf('danmaku')!=-1 && data.interaction.type=='danmaku')
  2619. || (lottery.joinMethod.indexOf('gift')!=-1 && data.interaction.type=='gift')
  2620. || (lottery.joinMethod.indexOf('like')!=-1 && data.interaction.type=='like')
  2621. )
  2622. ){
  2623. return;
  2624. }
  2625. // 如果参与方式是弹幕
  2626. if(data.interaction.type=='danmaku' && (lottery.joinKeyword && data.interaction.content.indexOf(lottery.joinKeyword)==-1)){
  2627. return;
  2628. }
  2629. // 如果参与方式是礼物
  2630. if(data.interaction.type=='gift' && (lottery.joinGiftList.length>0 && lottery.joinGiftList.indexOf(vue.giftNameMapper[data.interaction.giftName].id)==-1)){
  2631. return;
  2632. }
  2633. var pass = true;
  2634. // 如果设置了互斥抽奖
  2635. if(lottery.mutualLotteryList && lottery.mutualLotteryList.length>0){
  2636. _.filter(vue.lotteryTableData, function(o){return lottery.mutualLotteryList.indexOf(o.id)!=-1}).every(function(anotherLottery){
  2637. if(_.findIndex(anotherLottery.winners, {uid:data.uid})!=-1){
  2638. pass = false;
  2639. return false;
  2640. }
  2641. else{
  2642. return true;
  2643. }
  2644. });
  2645. }
  2646. if(!pass){
  2647. return;
  2648. }
  2649. // 第一次被侦测
  2650. if(!(data.uid in lottery.candidates)){
  2651. let candidate = {
  2652. uid : data.uid,
  2653. userName : data.userName,
  2654. photo : data.photo,
  2655. weight : 0,
  2656. index : Object.keys(lottery.candidates).length,
  2657. };
  2658. // 如果筛选了牌子
  2659. if(lottery.medalName && lottery.medalLevel){
  2660. vue.getCandidateInfo(candidate, (isSuccess, )=>{
  2661. if(candidate.medalName == lottery.medalName && candidate.medalLevel >= lottery.medalLevel){
  2662. lottery.candidates[data.uid] = candidate;
  2663. if(data.interaction.type=='danmaku'){
  2664. lottery.candidates[data.uid].weight += 1;
  2665. }
  2666. else if(data.interaction.type=='gift'){
  2667. lottery.candidates[data.uid].weight += vue.giftNameMapper[data.interaction.giftName].price * data.interaction.giftCount;
  2668. }
  2669. else if(data.interaction.type=='like'){
  2670. lottery.candidates[data.uid].weight += 0.5;
  2671. }
  2672. }
  2673. });
  2674. }
  2675. else{
  2676. lottery.candidates[data.uid] = candidate;
  2677. if(data.interaction.type=='danmaku'){
  2678. lottery.candidates[data.uid].weight += 1;
  2679. }
  2680. else if(data.interaction.type=='gift'){
  2681. lottery.candidates[data.uid].weight += vue.giftNameMapper[data.interaction.giftName].price * data.interaction.giftCount;
  2682. }
  2683. else if(data.interaction.type=='like'){
  2684. lottery.candidates[data.uid].weight += 0.5;
  2685. }
  2686. }
  2687. }
  2688. });
  2689. }
  2690. var startLotteryTimeout = lottery.startTime - new Date();
  2691. var countdownTimeout = startLotteryTimeout - lottery.countdown * 1000;
  2692. // 是否已经注册(不可用)过抽奖倒计时
  2693. if(lottery.countdownHandler){
  2694. window.clearTimeout(lottery.countdownHandler);
  2695. lottery.countdownHandler = null;
  2696. }
  2697. if(lottery.countdown>0){
  2698. lottery.countdownHandler = window.setTimeout(function(){
  2699. lottery.countdownHandler = null;
  2700. lottery.countdownText = `${lottery.countdown/60>=10?'':'0'}${_.floor(lottery.countdown/60)}:${lottery.countdown%60>=10?'':'0'}${lottery.countdown%60}`;
  2701. vue.activeLottery = lottery;
  2702. // 展示倒计时
  2703. vue.showLotteryCountdown(lottery);
  2704. }, countdownTimeout);
  2705. }
  2706. // 是否已经注册(不可用)过抽奖
  2707. if(lottery.startHandler){
  2708. window.clearTimeout(lottery.startHandler);
  2709. lottery.startHandler = null;
  2710. }
  2711. lottery.startHandler = window.setTimeout(function(){
  2712. // 清除数据回调
  2713. if(lottery.source=='douga'){
  2714. window.clearInterval(lottery.messageHandler)
  2715. }
  2716. else{
  2717. callbackManager.cancel(lottery.messageHandler);
  2718. }
  2719. lottery.messageHandler = null;
  2720. lottery.startHandler = null;
  2721. vue.activeLottery = lottery;
  2722. // 展示结果
  2723. vue.showLotteryStart(lottery);
  2724. }, startLotteryTimeout);
  2725. },
  2726. // 返回抽奖表格行类名
  2727. lotteryTableRowClassName : function({row}){
  2728. if(row.status == '未开始'){
  2729. return 'lottery-status-unstart';
  2730. }
  2731. if(row.status == '已结束'){
  2732. return 'lottery-status-finish';
  2733. }
  2734. },
  2735. getCandidateInfo(candidate, callback){
  2736. this.candidateInfoCallbacks.push([candidate, callback]);
  2737. },
  2738. // 获取用户信息
  2739. _getCandidateInfo(candidate, callback){
  2740. if(candidate.medalName){
  2741. if(_.isFunction){
  2742. callback(true, candidate);
  2743. }
  2744. return;
  2745. }
  2746. commonRequrest(config.ACFUN_SERVER + config.URLS.ACFUN_USER.SPACE + `/${candidate.uid}`, 'get', null, true, function(isSuccess, data){
  2747. // 获取成功
  2748. if(isSuccess){
  2749. let medalDataJsonMatch = data.match(/data-medal='(?<medal>.+?)'/);
  2750. if(medalDataJsonMatch!=null){
  2751. let medalData = JSON.parse(medalDataJsonMatch.groups['medal']);
  2752. let medalColor = 'green';
  2753. if(medalData.level >= 1 && medalData.level <= 3){
  2754. medalColor = 'green';
  2755. }
  2756. else if(medalData.level >= 4 && medalData.level <= 6){
  2757. medalColor = 'blue';
  2758. }
  2759. else if(medalData.level >= 7 && medalData.level <= 9){
  2760. medalColor = 'orange';
  2761. }
  2762. else if(medalData.level >= 10 && medalData.level <= 12){
  2763. medalColor = 'red';
  2764. }
  2765. else if(medalData.level >= 13 && medalData.level <= 15){
  2766. medalColor = 'purple';
  2767. }
  2768. else if(medalData.level >= 16 && medalData.level <= 20){
  2769. medalColor = 'black';
  2770. }
  2771. Object.assign(candidate, {
  2772. medalName : medalData.clubName,
  2773. medalLevel : medalData.level,
  2774. medalColor : medalColor,
  2775. });
  2776. }
  2777. if(_.isFunction){
  2778. callback(true, candidate);
  2779. }
  2780. }
  2781. else{
  2782. if(_.isFunction){
  2783. callback(false, candidate);
  2784. }
  2785. }
  2786. });
  2787. },
  2788. // 开始倒计时
  2789. showLotteryCountdown : function(lottery){
  2790. var vue = this;
  2791. this.lotteryStartDialogVisible = false;
  2792. this.lotteryCountdownDialogVisible = true;
  2793. this.$nextTick(function(){
  2794. //circle start
  2795. var progressBar = document.querySelector('.lottery-countdown-dialog .e-c-progress');
  2796. var indicator = document.querySelector('.lottery-countdown-dialog #e-indicator');
  2797. var pointer = document.querySelector('.lottery-countdown-dialog #e-pointer');
  2798. var length = Math.PI * 2 * 100;
  2799. progressBar.style.strokeDasharray = length;
  2800. function update(value, timePercent) {
  2801. var offset = - length - length * value / (timePercent);
  2802. progressBar.style.strokeDashoffset = offset;
  2803. pointer.style.transform = `rotate(${360 * value / (timePercent)}deg)`;
  2804. };
  2805. //circle ends
  2806. const displayOutput = document.querySelector('.lottery-countdown-dialog .display-remain-time')
  2807. var intervalTimer;
  2808. var timeLeft;
  2809. var wholeTime = lottery.countdown; // manage this to set the whole time
  2810. var isPaused = false;
  2811. var isStarted = false;
  2812. update(wholeTime,wholeTime); //refreshes progress bar
  2813. displayTimeLeft(wholeTime);
  2814. function changeWholeTime(seconds){
  2815. if ((wholeTime + seconds) > 0){
  2816. wholeTime += seconds;
  2817. update(wholeTime,wholeTime);
  2818. }
  2819. }
  2820. function timer (seconds){ //counts time, takes seconds
  2821. var remainTime = Date.now() + (seconds * 1000);
  2822. displayTimeLeft(seconds);
  2823. intervalTimer = window.setInterval(function(){
  2824. timeLeft = Math.round((remainTime - Date.now()) / 1000);
  2825. // 倒计时结束
  2826. if(timeLeft < 0){
  2827. window.clearInterval(intervalTimer);
  2828. isStarted = false;
  2829. // 隐藏对话框
  2830. vue.lotteryCountdownDialogVisible = false;
  2831. return ;
  2832. }
  2833. displayTimeLeft(timeLeft);
  2834. }, 1000);
  2835. }
  2836. function displayTimeLeft (timeLeft){ //displays time on the input
  2837. var minutes = Math.floor(timeLeft / 60);
  2838. var seconds = timeLeft % 60;
  2839. var displayString = `${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
  2840. displayOutput.textContent = displayString;
  2841. update(timeLeft, wholeTime);
  2842. }
  2843. timer(wholeTime);
  2844. });
  2845. },
  2846. // 开始抽奖
  2847. showLotteryStart : function(lottery){
  2848. var vue = this;
  2849. var stop = false;
  2850. lottery.activeIndex = -1;
  2851. this.lotteryCountdownDialogVisible = false;
  2852. var rollIntervalHandler = null;
  2853. // 候选人数为0
  2854. if(Object.keys(lottery.candidates).length == 0){
  2855. this.$message({
  2856. type : 'info',
  2857. message : '无参与抽奖人员',
  2858. });
  2859. lottery.winners = _.flatMap(lottery.candidates);
  2860. lottery.status = '已结束';
  2861. this.handleLotteryWinnersCheck(lottery);
  2862. return;
  2863. }
  2864. function roll(){
  2865. if(stop){
  2866. // 获取当前赢家
  2867. // vue.$refs.carousel.setActiveItem(_.findIndex(lottery.startCandidates, {uid:lottery.winners[lottery.winners.length-1].uid}));
  2868. }
  2869. else{
  2870. vue.$refs.carousel.next();
  2871. }
  2872. }
  2873. function changeRollIntervals(intervals, timeouts, callback){
  2874. if(!timeouts){
  2875. timeouts = 0;
  2876. }
  2877. if(!_.isArray(intervals)){
  2878. intervals = [intervals];
  2879. }
  2880. if(!_.isArray(timeouts)){
  2881. timeouts = [timeouts];
  2882. if(timeouts.length>intervals.length){
  2883. timeouts = timeouts.slice(0, intervals.length);
  2884. }
  2885. if(timeouts.length<intervals.length){
  2886. var npads = intervals.length-timeouts.length;
  2887. for(var i=0;i<npads;++i){
  2888. timeouts.splice(0, 0, 0);
  2889. }
  2890. }
  2891. }
  2892. intervals.forEach(function(interval, index){
  2893. window.setTimeout(function(){
  2894. if(rollIntervalHandler!=null){
  2895. window.clearTimeout(rollIntervalHandler);
  2896. }
  2897. if(interval != null){
  2898. rollIntervalHandler = window.setInterval(roll, interval);
  2899. }
  2900. else{
  2901. stop = true;
  2902. vue.$nextTick(function(){
  2903. vue.$refs.carousel.$nextTick(function(){
  2904. // 获取当前赢家
  2905. lottery.activeIndex = vue.$refs.carousel.activeIndex;
  2906. // 高亮
  2907. lottery.startCandidates[lottery.activeIndex].highlight = true;
  2908. vue.$refs.carousel.$nextTick(function(){
  2909. // 停止回调
  2910. if(_.isFunction(callback)){
  2911. callback();
  2912. }
  2913. });
  2914. });
  2915. });
  2916. }
  2917. }, timeouts[index]);
  2918. });
  2919. }
  2920. // 当前滚动名单
  2921. vue.$set(lottery, 'startCandidates', _.flatMap(lottery.candidates));
  2922. this.$nextTick(function(){
  2923. var startChoose = function(){
  2924. lottery.rolling = true;
  2925. // 获取中奖者
  2926. vue.lotteryStartDialogVisible = true;
  2927. stop = false;
  2928. // 8秒内需要滚动完所有用户
  2929. var interval = Math.min(Math.max(Math.floor(8000/lottery.startCandidates.length), 150), 350);
  2930. // 高速转动
  2931. // 10秒后停止转动
  2932. changeRollIntervals(
  2933. [interval, null],
  2934. [0, 10000],
  2935. // 转动结束后获取当前用户
  2936. function(){
  2937. lottery.rolling = false;
  2938. },
  2939. );
  2940. };
  2941. function checkWinner(valid){
  2942. // 去除高亮
  2943. lottery.startCandidates[lottery.activeIndex].highlight = false;
  2944. // 删除候选
  2945. let candidate = lottery.startCandidates.splice(lottery.activeIndex, 1)[0];
  2946. // 删除候选
  2947. delete lottery.candidates[candidate.uid];
  2948. // 如果有效,添加入赢家
  2949. if(valid){
  2950. // 添加赢家
  2951. lottery.winners.push(candidate);
  2952. }
  2953. lottery.winnerCount = lottery.winners.length;
  2954. }
  2955. // 设置抽奖结束函数
  2956. lottery.finishFunc = function(){
  2957. if(lottery.rolling){
  2958. return;
  2959. }
  2960. checkWinner(true);
  2961. lottery.status = '已结束';
  2962. // 关闭弹窗
  2963. vue.lotteryStartDialogVisible = false;
  2964. // 打开中奖名单
  2965. vue.handleLotteryWinnersCheck(lottery);
  2966. };
  2967. // 设置继续抽奖函数
  2968. lottery.nextFunc = function(valid){
  2969. if(lottery.rolling){
  2970. return;
  2971. }
  2972. // 查看候选人数是否足够
  2973. if(valid && lottery.startCandidates.length == 0){
  2974. vue.$message({
  2975. type : 'error',
  2976. message : '人数不足,无法抽奖'
  2977. });
  2978. return;
  2979. }
  2980. checkWinner(valid);
  2981. // 关闭弹窗
  2982. // vue.lotteryStartDialogVisible = false;
  2983. vue.activeLottery = {};
  2984. vue.$nextTick(function(){
  2985. // 1秒后重新开始
  2986. window.setTimeout(function(){
  2987. // 打乱顺序
  2988. vue.$set(lottery, 'startCandidates', _.shuffle(lottery.startCandidates));
  2989. vue.$nextTick(function(){
  2990. lottery.activeIndex = -1;
  2991. vue.activeLottery = lottery;
  2992. startChoose();
  2993. });
  2994. }, 1000);
  2995. });
  2996. };
  2997. // 开始抽奖
  2998. startChoose();
  2999. });
  3000. },
  3001. // 导出中奖名单
  3002. exportLottery : function(){
  3003. var workbook = new ExcelJS.Workbook();
  3004. var sheet = workbook.addWorksheet('中奖名单');
  3005. sheet.columns = [
  3006. { key: 'userName', width: 30, },
  3007. { key: 'uid', width: 10, },
  3008. ];
  3009. var rowIndex = 1;
  3010. // 遍历所有已结束的抽奖
  3011. this.lotteryTableData.forEach(function(lottery){
  3012. if(lottery.status != '已结束'){
  3013. return true;
  3014. }
  3015. // 添加标题
  3016. sheet.addRow([lottery.name]);
  3017. sheet.mergeCells(rowIndex,1,rowIndex,2);
  3018. // 设置标题样式
  3019. var titleRow = sheet.getRow(rowIndex);
  3020. var titleCell = titleRow.getCell(1);
  3021. // 文字居中
  3022. titleCell.alignment = {vertical: 'middle', horizontal: 'center'};
  3023. // 添加边框
  3024. titleCell.border = {
  3025. top: {style:'thin'},
  3026. left: {style:'thin'},
  3027. bottom: {style:'thin'},
  3028. right: {style:'thin'},
  3029. };
  3030. // 加粗字体
  3031. titleCell.font = {
  3032. bold : true,
  3033. };
  3034. sheet.addRow({userName: '用户名', uid : '用户uid'});
  3035. // 设置列名样式
  3036. var headerRow = sheet.getRow(rowIndex + 1);
  3037. for(var i=1;i<=2;++i){
  3038. headerRow.getCell(i).alignment = {vertical: 'middle', horizontal: 'center'};
  3039. headerRow.getCell(i).border = {
  3040. top: {style:'thin'},
  3041. left: {style:'thin'},
  3042. bottom: {style:'thin'},
  3043. right: {style:'thin'},
  3044. };
  3045. }
  3046. // 遍历中奖用户
  3047. lottery.winners.forEach(function(winner, index){
  3048. sheet.addRow({userName: winner.userName, uid : winner.uid});
  3049. // 设置记录样式
  3050. var recordRow = sheet.getRow(rowIndex + 2 + index);
  3051. for(var i=1;i<=2;++i){
  3052. recordRow.getCell(i).alignment = {vertical: 'middle', horizontal: 'center'};
  3053. recordRow.getCell(i).border = {
  3054. top: {style:'thin'},
  3055. left: {style:'thin'},
  3056. bottom: {style:'thin'},
  3057. right: {style:'thin'},
  3058. };
  3059. }
  3060. });
  3061. // 添加两个空白行
  3062. sheet.addRow();
  3063. sheet.addRow();
  3064. rowIndex += 1 + 1 + lottery.winners.length + 2;
  3065. });
  3066. ;(async function(){
  3067. var buffer = await workbook.xlsx.writeBuffer();
  3068. var file = new File([buffer], '中奖名单.xlsx');
  3069. saveAs(file);
  3070. })();
  3071. },
  3072. // 语音配置表单提交
  3073. handleAudioFormSubmit : function(){
  3074. var vue = this;
  3075. this.$refs.audioForm.validate((valid) => {
  3076. // 通过校验
  3077. if(valid){
  3078. // 提交修改
  3079. GM_setValue('audioFormData', JSON.stringify({
  3080. voice : this.audioFormData.voice,
  3081. enable : this.audioFormData.enable,
  3082. speechRate : this.audioFormData.speechRate,
  3083. speechVolume : this.audioFormData.speechVolume,
  3084. joinMethod : this.audioFormData.joinMethod,
  3085. danmakuFormat : this.audioFormData.danmakuFormat,
  3086. likeFormat : this.audioFormData.likeFormat,
  3087. giftFormat : this.audioFormData.giftFormat,
  3088. enterroomFormat : this.audioFormData.enterroomFormat,
  3089. followFormat : this.audioFormData.followFormat,
  3090. danmakuTimeout : this.audioFormData.danmakuTimeout,
  3091. likeTimeout : this.audioFormData.likeTimeout,
  3092. giftTimeout : this.audioFormData.giftTimeout,
  3093. enterroomTimeout : this.audioFormData.enterroomTimeout,
  3094. followTimeout : this.audioFormData.followTimeout,
  3095. consumeing : false,
  3096. interactions : [],
  3097. messageBuffer : {},
  3098. }));
  3099. this.handleAudioFormSubmitValid();
  3100. }
  3101. });
  3102. },
  3103. handleAudioFormSubmitValid : function(){
  3104. var vue = this;
  3105. // 检测当前是否有缓冲
  3106. for(var bufferKey in this.audioFormData.messageBuffer){
  3107. window.clearTimeout(this.audioFormData.messageBuffer[bufferKey].handler);
  3108. }
  3109. this.audioFormData.messageBuffer = {};
  3110. // 检测是否已经添加了语音播报
  3111. if(this.audioFormData.messageHandler){
  3112. callbackManager.cancel(this.audioFormData.messageHandler);
  3113. this.audioFormData.messageHandler = null;
  3114. }
  3115. // 开启播报
  3116. if(this.audioFormData.enable){
  3117. this.audioFormData.messageHandler = callbackManager.register('message', function(data){
  3118. // 不是允许的互动类型
  3119. if(vue.audioFormData.joinMethod.indexOf(data.interaction.type)==-1){
  3120. return;
  3121. }
  3122. var bufferKey = null;
  3123. var timeout = 0;
  3124. if(data.interaction.type=='gift'){
  3125. bufferKey = data.uid + data.interaction.type + data.interaction.giftName;
  3126. timeout = vue.audioFormData.giftTimeout;
  3127. }
  3128. // 不进行缓存,直接播报
  3129. if(bufferKey==null || timeout == 0){
  3130. data.interaction = [data.interaction];
  3131. vue.consumeAudio(vue.audioFormData, data);
  3132. return;
  3133. }
  3134. // 已有相同信息
  3135. if(bufferKey in vue.audioFormData.messageBuffer){
  3136. window.clearTimeout(vue.audioFormData.messageBuffer[bufferKey].handler);
  3137. vue.audioFormData.messageBuffer[bufferKey].interaction.push(data.interaction);
  3138. }
  3139. else{
  3140. vue.audioFormData.messageBuffer[bufferKey] = {
  3141. uid : data.uid,
  3142. userName : data.userName,
  3143. interaction : [data.interaction],
  3144. };
  3145. }
  3146. vue.audioFormData.messageBuffer[bufferKey].handler = window.setTimeout(function(){
  3147. var data = vue.audioFormData.messageBuffer[bufferKey];
  3148. vue.audioFormData.messageBuffer[bufferKey].handler = null;
  3149. delete vue.audioFormData.messageBuffer[bufferKey];
  3150. vue.consumeAudio(vue.audioFormData, data);
  3151. }, timeout*1000);
  3152. });
  3153. }
  3154. // 关闭弹窗
  3155. this.audioFormDialogVisible = false;
  3156. },
  3157. // 字体选择回调
  3158. handleFontSelectChange : function(value){
  3159. this.addFont(value[value.length-1]);
  3160. },
  3161. // 新增预设
  3162. addInteractionPreset: function(item){
  3163. this.interactionPreset.push(_.cloneDeep(item));
  3164. this.interactionPreset[this.interactionPreset.length-1].index = this.interactionPreset.length;
  3165. this.interactionPreset[this.interactionPreset.length-1].hotkey = this.interactionPreset.length<=9?`ctrl+alt+${this.interactionPreset.length}`:'预设过多,无法激活';
  3166. return this.interactionPreset[this.interactionPreset.length-1];
  3167. },
  3168. // 编辑预设
  3169. editInteractionPreset: function(item){
  3170. console.log('edit', item)
  3171. this.interactionPreset[item.index-1] = _.cloneDeep(item);
  3172. return this.interactionPreset[item.index-1];
  3173. },
  3174. // 激活某一预设
  3175. activeInteractionPreset: function(item){
  3176. for(var preset of this.interactionPreset){
  3177. preset.active = false;
  3178. }
  3179. item.active = true;
  3180. console.log('item', item)
  3181. this.interactionFormData = _.cloneDeep(item);
  3182. this.handleInteractionFormSubmitValid();
  3183. },
  3184. // 删除某一预设
  3185. removeInteractionPreset: function(item){
  3186. this.$confirm(`确定删除预设:${item.name}`, '是否删除', {
  3187. confirmButtonText: '确定',
  3188. cancelButtonText: '取消',
  3189. }).then(() => {
  3190. this.interactionPreset.splice(item.index-1, 1);
  3191. this.interactionPreset.forEach(function(item, index){
  3192. item.index = index + 1;
  3193. item.hotkey = index<9?`ctrl+alt+${index+1}`:'预设过多,无法激活';
  3194. });
  3195. // 提交修改
  3196. GM_setValue('interactionPreset', JSON.stringify(vue.interactionPreset));
  3197. });
  3198. },
  3199. // 打开弹幕配置表单
  3200. handleInteractionFormOpen(item){
  3201. var vue = this;
  3202. // 新增预设
  3203. if(item==null){
  3204. this.interactionFormData.name = "";
  3205. this.interactionFormData.index = null;
  3206. }
  3207. // 编辑预设
  3208. else{
  3209. this.activeInteractionPreset(item);
  3210. }
  3211. this.interactionFormDialogVisible = true;
  3212. },
  3213. // 重置配置表单
  3214. handleInteractionFormInit: function(){
  3215. this.interactionFormData = {
  3216. ...this.interactionFormData,
  3217. ..._.cloneDeep(this.interactionFormDataInit),
  3218. };
  3219. },
  3220. // 弹幕配置表单提交
  3221. handleInteractionFormSubmit : function(){
  3222. var vue = this;
  3223. this.$refs.interactionForm.validate((valid) => {
  3224. // 通过校验
  3225. if(valid){
  3226. var fontName = vue.interactionFormData.fontFamily[vue.interactionFormData.fontFamily.length-1];
  3227. if(fontName!='inherit'){
  3228. // 添加缓存
  3229. GM_setValue('downloadedFonts', vue.downloadedFonts);
  3230. }
  3231. var preset = null;
  3232. // 编辑
  3233. if(vue.interactionFormData.index!=null){
  3234. preset = vue.editInteractionPreset(vue.interactionFormData);
  3235. }
  3236. // 新增
  3237. else{
  3238. preset = vue.addInteractionPreset(vue.interactionFormData);
  3239. }
  3240. console.log('submit', preset)
  3241. vue.activeInteractionPreset(preset);
  3242. vue.interactionFormDialogVisible = false;
  3243. // 提交修改
  3244. GM_setValue('interactionPreset', JSON.stringify(vue.interactionPreset));
  3245. }
  3246. });
  3247. },
  3248. handleInteractionFormSubmitValid : function(){
  3249. var vue = this;
  3250. if(vue.interactionFormData.enable){
  3251. vue.interactionFormData.modifyContentFunc = function(data){
  3252. if(data.interaction.type == 'gift'){
  3253. data.interaction.content = formatText(vue.interactionFormData.giftFormat, {
  3254. '礼物名称' : data.interaction.giftName,
  3255. });
  3256. }
  3257. else if(data.interaction.type == 'enterroom'){
  3258. data.interaction.content = vue.interactionFormData.enterroomFormat;
  3259. }
  3260. else if(data.interaction.type == 'like'){
  3261. data.interaction.content = vue.interactionFormData.likeFormat;
  3262. }
  3263. else if(data.interaction.type == 'follow'){
  3264. data.interaction.content = vue.interactionFormData.followFormat;
  3265. }
  3266. else if(data.interaction.type == 'joinclub'){
  3267. data.interaction.content = vue.interactionFormData.joinclubFormat;
  3268. }
  3269. }
  3270. if(vue.interactionFormData.cleanInteractionHistoryHandler){
  3271. window.clearInterval(vue.interactionFormData.cleanInteractionHistoryHandler);
  3272. }
  3273. // 清除弹幕历史
  3274. vue.interactionFormData.cleanInteractionHistoryHandler = window.setInterval(function(){
  3275. let historyMinuteStartIndex = 0;
  3276. let historyCountStartIndex = vue.messageData.length - vue.interactionFormData.historyCount;
  3277. let sendTimeLower = new Date();
  3278. sendTimeLower.setMinutes(sendTimeLower.getMinutes()-vue.interactionFormData.historyMinutes);
  3279. vue.messageData.every(function(message, messageIndex){
  3280. if(message.interaction.sendTime>=sendTimeLower){
  3281. historyMinuteStartIndex = messageIndex;
  3282. return false;
  3283. }
  3284. else{
  3285. return true;
  3286. }
  3287. });
  3288. let historyStartIndex = Math.max(0, Math.max(historyMinuteStartIndex, historyCountStartIndex));
  3289. if(historyStartIndex==0){
  3290. return;
  3291. }
  3292. else{
  3293. vue.messageData.splice(0, historyStartIndex);
  3294. }
  3295. }, 5 * 1000);
  3296. }
  3297. else{
  3298. vue.interactionFormData.modifyContentFunc = null;
  3299. window.clearInterval(vue.interactionFormData.cleanInteractionHistoryHandler);
  3300. }
  3301. },
  3302. consumeAudio(audioFormData, interactionData){
  3303. var vue = this;
  3304. // 弹幕合并
  3305. if(interactionData.interaction[0].type == 'danmaku'){
  3306. interactionData.interaction = {
  3307. type : 'danmaku',
  3308. content : _.reduce(interactionData.interaction, function(text, interaction){
  3309. return text + (text==''?'':'<break time=\"500ms\"/>') + interaction.content;
  3310. }, ''),
  3311. };
  3312. }
  3313. // 礼物合并
  3314. else if(interactionData.interaction[0].type == 'gift'){
  3315. // 有连击
  3316. let findIndex = _.findIndex(interactionData.interaction, {'doAnime': true});
  3317. interactionData.interaction = {
  3318. type : 'gift',
  3319. giftName : interactionData.interaction[0].giftName,
  3320. giftCount : findIndex==-1?_.sumBy(interactionData.interaction, 'giftCount'):interactionData.interaction[findIndex].giftCount,
  3321. }
  3322. }
  3323. // 其他合并
  3324. else{
  3325. interactionData.interaction = interactionData.interaction[0];
  3326. }
  3327. audioFormData.interactions.push(interactionData);
  3328. if(audioFormData.consumeing){
  3329. return;
  3330. }
  3331. audioFormData.consumeing = true;
  3332. new Promise(function(resolve, reject) {
  3333. var readInteractionAudio = function(){
  3334. // 如果停止了语音
  3335. if(!audioFormData.enable){
  3336. audioFormData.interactions = [];
  3337. resolve();
  3338. return;
  3339. }
  3340. if(audioFormData.interactions.length==0){
  3341. audioFormData.consumeing = false;
  3342. resolve();
  3343. return;
  3344. }
  3345. var interactionData = audioFormData.interactions.splice(0, 1)[0];
  3346. vue.readInteractionAudio(audioFormData, interactionData, readInteractionAudio);
  3347. }
  3348. readInteractionAudio();
  3349. }).then(function(){
  3350. }).catch(function(err){
  3351. });
  3352. },
  3353. readInteractionAudio(audioFormData, interactionData, callback){
  3354. // 弹幕
  3355. if(interactionData.interaction.type == 'danmaku'){
  3356. nls(`<speak>${audioFormData.danmakuFormat}</speak>`, audioFormData.voice[audioFormData.voice.length-1], audioFormData.speechRate - 500, audioFormData.speechVolume, {
  3357. '用户名' : interactionData.userName + '<break time=\"500ms\"/>',
  3358. '弹幕内容' : '<break time=\"500ms\"/>' + interactionData.interaction.content,
  3359. }, callback);
  3360. }
  3361. // 礼物
  3362. else if(interactionData.interaction.type == 'gift'){
  3363. nls(`<speak>${audioFormData.giftFormat}</speak>`, audioFormData.voice[audioFormData.voice.length-1], audioFormData.speechRate - 500, audioFormData.speechVolume, {
  3364. '用户名' : interactionData.userName + '<break time=\"500ms\"/>',
  3365. '礼物数量' : interactionData.interaction.giftCount,
  3366. '礼物名称' : interactionData.interaction.giftName,
  3367. }, callback);
  3368. }
  3369. // 点赞
  3370. else if(interactionData.interaction.type == 'like'){
  3371. nls(`<speak>${audioFormData.likeFormat}</speak>`, audioFormData.voice[audioFormData.voice.length-1], audioFormData.speechRate - 500, audioFormData.speechVolume, {
  3372. '用户名' : interactionData.userName + '<break time=\"500ms\"/>',
  3373. }, callback);
  3374. }
  3375. // 进入直播间
  3376. else if(interactionData.interaction.type == 'enterroom'){
  3377. nls(`<speak>${audioFormData.enterroomFormat}</speak>`, audioFormData.voice[audioFormData.voice.length-1], audioFormData.speechRate - 500, audioFormData.speechVolume, {
  3378. '用户名' : interactionData.userName + '<break time=\"500ms\"/>',
  3379. }, callback);
  3380. }
  3381. // 关注
  3382. else if(interactionData.interaction.type == 'follow'){
  3383. nls(`<speak>${audioFormData.followFormat}</speak>`, audioFormData.voice[audioFormData.voice.length-1], audioFormData.speechRate - 500, audioFormData.speechVolume, {
  3384. '用户名' : interactionData.userName + '<break time=\"500ms\"/>',
  3385. }, callback);
  3386. }
  3387. else{
  3388. callback();
  3389. }
  3390. },
  3391. // 竞猜表单提交回调
  3392. handleGuessFormSubmit : function(){
  3393. var vue = this;
  3394. this.$refs.form.validate((valid) => {
  3395. // 通过校验
  3396. if(valid){
  3397. // 添加id
  3398. vue.guessFormData.id = uuidv4();
  3399. vue.guessTableData.splice(0, 0, vue.guessFormData);
  3400. vue.guessFormDialogVisible = false;
  3401. vue.guessFormData = _.cloneDeep(vue.guessFormDataDefault);
  3402. vue.$message({
  3403. message : '添加竞猜成功',
  3404. type : 'info',
  3405. duration : 2000,
  3406. });
  3407. // 进入竞猜
  3408. vue.openGuessStartDialog(vue.guessTableData[0]);
  3409. }
  3410. else{
  3411. this.$message({
  3412. message : '提交失败,请检查是否填写正确',
  3413. type : 'error',
  3414. });
  3415. }
  3416. });
  3417. },
  3418. // 竞猜开始下一个问题回调
  3419. handleGuessDanmakuSubmit : function(){
  3420. var vue = this;
  3421. this.guessDanmakuDialogVisible = true;
  3422. this.$nextTick(function(){
  3423. this.activeGuess.guessDanmakuList = [];
  3424. var danmakuList = [];
  3425. var danmakuUserMapper = {};
  3426. var danmakuContentMapper = {};
  3427. var hoverDanmakuItem = null;
  3428. var randomId = uuidv4();
  3429. // 随机给定一个id
  3430. var containerEle = document.querySelector('.guess-danmaku-dialog .guess-danmaku-container');
  3431. containerEle.setAttribute('id', 'R' + randomId);
  3432. this.activeGuess.danmaku = new EasyDanmaku({
  3433. el: `#R${randomId}`,
  3434. // 彩色弹幕
  3435. colourful : false,
  3436. // 弹幕行数
  3437. line : 10,
  3438. // 弹幕样式
  3439. wrapperStyle : 'guess-danmaku-danmu',
  3440. // 弹幕播放速度
  3441. speed : 3,
  3442. // 播放一次时长
  3443. runtime : 3,
  3444. // 鼠标悬浮暂停
  3445. hover : true,
  3446. coefficient : 5,
  3447. });
  3448. // 添加监听
  3449. this.activeGuess.messageHandler = callbackManager.register('command:提问', function(data){
  3450. // 非弹幕
  3451. if(data.interaction.type != 'danmaku'){
  3452. return;
  3453. }
  3454. // 只有一次提问机会
  3455. if(data.uid in danmakuUserMapper){
  3456. return;
  3457. }
  3458. // 不允许重复提问
  3459. if(data.interaction.content in danmakuContentMapper){
  3460. return;
  3461. }
  3462. // 达到单个用户可提问上限
  3463. if(vue.activeGuess.maxGuessPerUser>0){
  3464. if(_.filter(vue.activeGuess.guessList, {uid:data.uid}).length>=vue.activeGuess.maxGuessPerUser){
  3465. return;
  3466. }
  3467. }
  3468. var danmakuItem = _.cloneDeep(data);
  3469. danmakuList.push(danmakuItem);
  3470. vue.activeGuess.danmaku.send(danmakuItem.interaction.content);
  3471. danmakuItem.sendTime = new Date();
  3472. danmakuUserMapper[danmakuItem.uid] = true;
  3473. danmakuContentMapper[danmakuItem.interaction.content] = danmakuItem;
  3474. });
  3475. // 鼠标事件回调
  3476. $('.guess-danmaku-container').on('mouseenter', '.guess-danmaku-danmu', function(e){
  3477. var danmakuItem = danmakuContentMapper[e.target.innerText];
  3478. if(danmakuItem){
  3479. danmakuItem.hover = true;
  3480. hoverDanmakuItem = danmakuItem;
  3481. // 如果是手动选择模式,添加class
  3482. if(vue.activeGuess.chooseWay == 'manual'){
  3483. e.target.classList.add('is-select');
  3484. }
  3485. }
  3486. });
  3487. $('.guess-danmaku-container').on('mouseout', '.guess-danmaku-danmu', function(e){
  3488. if(hoverDanmakuItem){
  3489. hoverDanmakuItem.hover = false;
  3490. hoverDanmakuItem.sendTime = new Date();
  3491. hoverDanmakuItem = null;
  3492. // 如果是手动选择模式,删除class
  3493. if(vue.activeGuess.chooseWay == 'manual'){
  3494. e.target.classList.remove('is-select');
  3495. }
  3496. }
  3497. });
  3498. $('.guess-danmaku-container').on('click', '.guess-danmaku-danmu', function(e){
  3499. // 只有挑选方式为手动时才可点击
  3500. if(hoverDanmakuItem && vue.activeGuess.chooseWay == 'manual'){
  3501. // 暂停播放
  3502. vue.activeGuess.danmaku.pause();
  3503. var hoverDanmakuItem_ = hoverDanmakuItem;
  3504. hoverDanmakuItem_.guess = hoverDanmakuItem_.interaction.content;
  3505. vue.$confirm(`${hoverDanmakuItem_.guess}${hoverDanmakuItem_.guess.endsWith('?')?'':'?'}`, '是否命中', {
  3506. confirmButtonText: '命中',
  3507. cancelButtonText: '未命中',
  3508. closeOnClickModal : false,
  3509. closeOnPressEscape : false,
  3510. }).then(() => {
  3511. hoverDanmakuItem_.isValid = true;
  3512. hoverDanmakuItem_.index = vue.activeGuess.guessIndex;
  3513. vue.activeGuess.guessIndex += 1;
  3514. vue.activeGuess.guessList.splice(0, 0, hoverDanmakuItem_);
  3515. vue.activeGuess.matchGuessList.splice(0, 0, hoverDanmakuItem_);
  3516. // 关闭弹窗
  3517. vue.guessDanmakuDialogVisible = false;
  3518. }).catch((action) => {
  3519. if(action=='cancel'){
  3520. hoverDanmakuItem_.isValid = false;
  3521. hoverDanmakuItem_.index = vue.activeGuess.guessIndex;
  3522. vue.activeGuess.guessIndex += 1;
  3523. vue.activeGuess.guessList.splice(0, 0, hoverDanmakuItem_);
  3524. // 关闭弹窗
  3525. vue.guessDanmakuDialogVisible = false;
  3526. }
  3527. else{
  3528. }
  3529. });
  3530. }
  3531. });
  3532. // 开启轮询
  3533. this.activeGuess.danmakuLoopHandler = window.setInterval(function(){
  3534. var now = new Date();
  3535. var findIndex = _.findIndex(danmakuList, function(danmaku){
  3536. // 距离上次发送已经超过5秒
  3537. return now - danmaku.sendTime >= 3*1000 && !danmaku.hover;
  3538. });
  3539. if(findIndex != -1){
  3540. vue.activeGuess.danmaku.send(danmakuList[findIndex].interaction.content);
  3541. danmakuList[findIndex].sendTime = now;
  3542. }
  3543. }, 50);
  3544. // 如果是随机选择,随机选中
  3545. if(vue.activeGuess.chooseWay == 'random'){
  3546. var now = new Date();
  3547. // 竞猜问题倒计时结束
  3548. vue.activeGuess.handleGuessDanmakuCountdownFinish = function(){
  3549. // 暂停随机选中
  3550. window.clearInterval(vue.activeGuess.randomSelectHandler);
  3551. // 暂停弹幕
  3552. vue.activeGuess.danmaku.pause();
  3553. // 获取选中
  3554. var selectedEle = containerEle.querySelector('.is-select');
  3555. // 未选中信息
  3556. if(selectedEle==null){
  3557. vue.$message({
  3558. type : 'error',
  3559. message : '无法选中弹幕信息,本次问题获取失败',
  3560. });
  3561. // 关闭弹窗
  3562. vue.guessDanmakuDialogVisible = false;
  3563. }
  3564. else{
  3565. // 获取对应弹幕信息
  3566. var danmakuItem = danmakuContentMapper[selectedEle.innerText];
  3567. danmakuItem.guess = danmakuItem.interaction.content;
  3568. vue.$confirm(`${danmakuItem.guess}${danmakuItem.guess.endsWith('?')?'':'?'}`, '是否命中', {
  3569. distinguishCancelAndClose : true,
  3570. confirmButtonText: '命中',
  3571. cancelButtonText: '未命中',
  3572. closeOnClickModal : false,
  3573. closeOnPressEscape : false,
  3574. }).then(() => {
  3575. danmakuItem.isValid = true;
  3576. // 添加序号
  3577. danmakuItem.index = vue.activeGuess.guessIndex;
  3578. vue.activeGuess.guessIndex += 1;
  3579. vue.activeGuess.guessList.splice(0, 0, danmakuItem);
  3580. vue.activeGuess.matchGuessList.splice(0, 0, danmakuItem);
  3581. // 关闭弹窗
  3582. vue.guessDanmakuDialogVisible = false;
  3583. }).catch((action ) => {
  3584. // 未命中
  3585. if(action=='cancel'){
  3586. danmakuItem.isValid = false;
  3587. danmakuItem.index = vue.activeGuess.guessIndex;
  3588. vue.activeGuess.guessIndex += 1;
  3589. vue.activeGuess.guessList.splice(0, 0, danmakuItem);
  3590. // 关闭弹窗
  3591. vue.guessDanmakuDialogVisible = false;
  3592. }
  3593. else{
  3594. }
  3595. });
  3596. }
  3597. };
  3598. vue.activeGuess.randomSelectHandler = window.setInterval(function(){
  3599. // 删除已选中
  3600. var selectedEle = containerEle.querySelector('.is-select');
  3601. if(selectedEle){
  3602. selectedEle.classList.remove('is-select');
  3603. }
  3604. if(containerEle.children.length>0){
  3605. // 如果未有选中,或者没有兄弟节点,则选中第一个
  3606. if(selectedEle==null || selectedEle.nextElementSibling==null){
  3607. selectedEle = containerEle.children[0];
  3608. }
  3609. // 否则选中下一个
  3610. else{
  3611. selectedEle = selectedEle.nextElementSibling;
  3612. }
  3613. selectedEle.classList.add('is-select');
  3614. }
  3615. }, 100);
  3616. vue.$refs.guessDanmakuDialog.$nextTick(function(){
  3617. // 倒计时文字
  3618. $('#guess-danmaku-countdown-text')
  3619. .countdown(new Date().getTime() + vue.activeGuess.countdown*1000, function(event){$(this).html(event.strftime('【%M:%S】'))})
  3620. .on('finish.countdown', vue.activeGuess.handleGuessDanmakuCountdownFinish);
  3621. });
  3622. }
  3623. });
  3624. },
  3625. openGuessStartDialog : function(guessFormData){
  3626. var vue = this;
  3627. this.guessStartDialogVisible = true;
  3628. this.activeGuess = guessFormData;
  3629. var danmakuContentMapper = {};
  3630. var isInDirectGuess = false;
  3631. // 监听狙击消息
  3632. this.activeGuess.directGuessMessageHandler = callbackManager.register('command:狙击', function(data){
  3633. // 当前是否位于狙击状态
  3634. if(isInDirectGuess){
  3635. return;
  3636. }
  3637. // 非弹幕
  3638. if(data.interaction.type != 'danmaku'){
  3639. return;
  3640. }
  3641. // 已结束竞猜
  3642. if(vue.activeGuess.status != '进行中'){
  3643. return;
  3644. }
  3645. // 达到狙击总上限
  3646. if(vue.activeGuess.maxDirectGuess > 0 && vue.activeGuess.directGuessList.length>=vue.activeGuess.maxDirectGuess){
  3647. return;
  3648. }
  3649. // 达到单个用户可狙击上限
  3650. if(vue.activeGuess.maxDirectGuessPerUser>0 && _.filter(vue.activeGuess.directGuessList, {uid:data.uid}).length >= vue.activeGuess.maxDirectGuessPerUser ){
  3651. return;
  3652. }
  3653. // 不允许重复提问
  3654. if(data.interaction.content in danmakuContentMapper){
  3655. return;
  3656. }
  3657. isInDirectGuess = true;
  3658. var danmakuItem = _.cloneDeep(data);
  3659. danmakuItem.guess = data.interaction.content;
  3660. danmakuItem.sendTime = new Date();
  3661. danmakuContentMapper[danmakuItem.interaction.content] = danmakuItem;
  3662. // 打开询问
  3663. vue.$confirm(`${danmakuItem.guess}${danmakuItem.guess.endsWith('?')?'':'?'}`, '狙击!是否命中?', {
  3664. confirmButtonText: '命中',
  3665. cancelButtonText: '未命中',
  3666. closeOnClickModal : false,
  3667. closeOnPressEscape : false,
  3668. }).then(() => {
  3669. danmakuItem.isValid = true;
  3670. danmakuItem.index = vue.activeGuess.guessIndex;
  3671. vue.activeGuess.guessIndex += 1;
  3672. vue.activeGuess.directGuessList.splice(0, 0, danmakuItem);
  3673. // 关闭弹窗
  3674. vue.guessDanmakuDialogVisible = false;
  3675. isInDirectGuess = false;
  3676. vue.activeGuess.winner = danmakuItem;
  3677. // 修改状态
  3678. vue.activeGuess.status = '竞猜成功';
  3679. vue.$message({
  3680. type : 'info',
  3681. message : '竞猜成功!',
  3682. });
  3683. }).catch((action) => {
  3684. if(action=='cancel'){
  3685. danmakuItem.isValid = false;
  3686. danmakuItem.index = vue.activeGuess.guessIndex;
  3687. vue.activeGuess.guessIndex += 1;
  3688. vue.activeGuess.directGuessList.splice(0, 0, danmakuItem);
  3689. isInDirectGuess = false;
  3690. // 达到狙击上限,竞猜失败
  3691. if(vue.activeGuess.maxDirectGuess>0 && vue.activeGuess.maxDirectGuess<=vue.activeGuess.directGuessList.length){
  3692. // 修改状态
  3693. vue.activeGuess.status = '竞猜失败';
  3694. vue.$message({
  3695. type : 'error',
  3696. message : '竞猜失败!',
  3697. });
  3698. }
  3699. }
  3700. else{
  3701. }
  3702. });
  3703. });
  3704. },
  3705. // 竞猜弹幕弹窗关闭回调
  3706. handleGuessDanmakuDialogClose : function(){
  3707. delete this.activeGuess.danmaku;
  3708. if(this.activeGuess.messageHandler){
  3709. callbackManager.cancel(this.activeGuess.messageHandler);
  3710. this.activeGuess.messageHandler = null;
  3711. }
  3712. window.clearInterval(this.activeGuess.danmakuLoopHandler);
  3713. this.activeGuess.danmakuLoopHandler = null;
  3714. if(this.activeGuess.randomSelectHandler){
  3715. window.clearInterval(this.activeGuess.randomSelectHandler);
  3716. this.activeGuess.randomSelectHandler = null;
  3717. }
  3718. if(this.activeGuess.handleGuessDanmakuCountdownFinish){
  3719. this.activeGuess.handleGuessDanmakuCountdownFinish = null;
  3720. }
  3721. },
  3722. // 竞猜详情弹窗关闭
  3723. handleGuessStartDialogClose : function(){
  3724. // 关闭狙击消息获取
  3725. if(this.activeGuess.directGuessMessageHandler){
  3726. callbackManager.cancel(this.activeGuess.directGuessMessageHandler);
  3727. this.activeGuess.directGuessMessageHandler = null;
  3728. }
  3729. this.activeGuess = null;
  3730. },
  3731. // 获取投稿数据
  3732. getDougaList(){
  3733. var vue = this;
  3734. var pageSize = null;
  3735. if(this.lotteryFormData.dougaType == 'video'){
  3736. pageSize = 10;
  3737. }
  3738. else if(this.lotteryFormData.dougaType == 'article'){
  3739. pageSize = 10;
  3740. }
  3741. var params = {
  3742. 'quickViewId' : `ac-space-${this.lotteryFormData.dougaType}-list`,
  3743. 'reqID': this.reqId,
  3744. 'ajaxpipe': 1,
  3745. 'type': this.lotteryFormData.dougaType,
  3746. 'order': 'newest',
  3747. 'page' : this.lotteryFormData.dougaListPage,
  3748. 'pageSize': pageSize,
  3749. };
  3750. this.reqId += 1;
  3751. vue.lotteryFormData.dougaListLoading = true;
  3752. commonRequrest(config.ACFUN_SERVER + config.URLS.ACFUN_USER.SPACE + `/${this.uid}`, 'get', params, true, function(isSuccess, data){
  3753. // 获取成功
  3754. if(isSuccess){
  3755. document.querySelector('#douga-result').innerHTML = JSON.parse(data.replace('/*<!-- fetch-stream -->*/', ''))['html'];
  3756. var dougaEleList = null;
  3757. // 查看视频投稿
  3758. if(params.type == 'video'){
  3759. dougaEleList = document.querySelectorAll('#douga-result #ac-space-video-list a');
  3760. }
  3761. // 查看文章投稿
  3762. else if(params.type == 'article'){
  3763. dougaEleList = document.querySelectorAll('#douga-result #ac-space-article-list article');
  3764. }
  3765. // 无结果
  3766. if(!dougaEleList || dougaEleList.length==0){
  3767. vue.lotteryFormData.dougaListDisabled = true;
  3768. vue.lotteryFormData.dougaListNoMore = true;
  3769. }
  3770. else{
  3771. var dougaList = [];
  3772. // 视频投稿
  3773. if(params.type == 'video'){
  3774. dougaEleList.forEach(function(dougaEle){
  3775. dougaList.push({
  3776. id : dougaEle.attributes.href.value.split('/').pop().replace('ac', ''),
  3777. href : config.ACFUN_SERVER + dougaEle.attributes.href.value,
  3778. cover : dougaEle.querySelector('img').src,
  3779. title : dougaEle.querySelector('figcaption .title').title,
  3780. uploadDateText : dougaEle.querySelector('figcaption .date').innerText,
  3781. playCountText : dougaEle.querySelector('figcaption .play-info').innerText.match(/观看(?<count>[0-9\.万]+)/).groups['count'],
  3782. });
  3783. });
  3784. }
  3785. // 文章投稿
  3786. else if(params.type == 'article'){
  3787. dougaEleList.forEach(function(dougaEle){
  3788. dougaList.push({
  3789. id : dougaEle.querySelector('a').attributes.href.value.split('/').pop().replace('ac', ''),
  3790. href : config.ACFUN_SERVER + dougaEle.querySelector('a').attributes.href.value,
  3791. title : dougaEle.querySelector('a').title,
  3792. uploadDateText : dougaEle.querySelector('.info').innerText.match(/发布于\s*(?<date>\d{4}\/\d{2}\/\d{2})/).groups['date'],
  3793. playCountText : dougaEle.querySelector('.info').innerText.match(/(?<count>[0-9\.万]+)人围观/).groups['count'],
  3794. });
  3795. });
  3796. }
  3797. vue.lotteryFormData.dougaList.splice(vue.lotteryFormData.dougaList.length, 0, ...dougaList);
  3798. vue.lotteryFormData.dougaListPage += 1;
  3799. }
  3800. }
  3801. else{
  3802. vue.lotteryFormData.dougaListDisabled = true;
  3803. vue.lotteryFormData.dougaListNoMore = true;
  3804. }
  3805. vue.lotteryFormData.dougaListLoading = false;
  3806. });
  3807. },
  3808. // 刷新投稿数据
  3809. refreshDougaList(){
  3810. this.lotteryFormData.dougaList = [];
  3811. this.lotteryFormData.dougaListPage = 1;
  3812. this.lotteryFormData.dougaListDisabled = false;
  3813. this.lotteryFormData.dougaListLoading = false;
  3814. this.lotteryFormData.dougaListNoMore = false;
  3815. this.lotteryFormData.dougaListActiveIndex = null;
  3816. this.getDougaList();
  3817. },
  3818. handleDougaListItemClick(dougaIndex){
  3819. // 点击已选中投稿表示取消选中
  3820. if(this.lotteryFormData.dougaListActiveIndex == dougaIndex){
  3821. this.lotteryFormData.dougaListActiveIndex = null;
  3822. }
  3823. else{
  3824. this.lotteryFormData.dougaListActiveIndex = dougaIndex;
  3825. }
  3826. },
  3827. handleDougaListItemDetailClick(dougaIndex){
  3828. window.open(this.lotteryFormData.dougaList[dougaIndex].href, '_blank');
  3829. },
  3830. // 获取投稿
  3831. getCommentList(douga, all, callback){
  3832. var vue = this;
  3833. var params = {
  3834. sourceId : douga.id,
  3835. sourceType : 3,
  3836. page : 1,
  3837. pivotCommentId : 0,
  3838. supportZtEmot : true,
  3839. };
  3840. var fetch = true;
  3841. var commentList = [];
  3842. var preCommentId = douga.commentList && douga.commentList.length>0?douga.commentList[0].id:null;
  3843. var getData = function(){
  3844. commonRequrest(config.ACFUN_SERVER + config.URLS.DOUGA.COMMENT, 'get', params, true, function(isSuccess, data){
  3845. // 获取失败
  3846. if(!isSuccess){
  3847. callback(false, false, douga.commentCount, params.page, douga.commentListTotalPage);
  3848. fetch = false;
  3849. }
  3850. else{
  3851. data = JSON.parse(data);
  3852. // 返回错误
  3853. if(data.result != 0){
  3854. fetch = false;
  3855. return;
  3856. }
  3857. // 设置评论数量
  3858. douga.commentCount = data.commentCount;
  3859. douga.commentListTotalPage = data.totalPage;
  3860. data.rootComments.every(function(comment, commentIndex){
  3861. // 如果是已经获取过的评论,结束获取
  3862. if(comment.commentId == preCommentId){
  3863. fetch = false;
  3864. return false;
  3865. }
  3866. // 如果是未获取过的用户,添加
  3867. if(!(comment.userId in userInfo)){
  3868. userInfo[comment.userId] = {
  3869. uid : comment.userId,
  3870. userName : comment.userName,
  3871. photo : comment.headUrl[0].url,
  3872. };
  3873. }
  3874. commentList.push({
  3875. id : comment.commentId,
  3876. uid : comment.userId,
  3877. userName : comment.userName,
  3878. photo : comment.headUrl[0].url,
  3879. content : comment.content.replace(/\[emot=[^\[\]]*\]/g, ''),
  3880. floor : comment.floor,
  3881. date : moment(comment.timestamp).toDate(),
  3882. dateText : moment(comment.timestamp).format('MM-DD HH:mm'),
  3883. });
  3884. return true;
  3885. });
  3886. // 获取结束
  3887. if(params.page == douga.commentListTotalPage){
  3888. fetch = false;
  3889. }
  3890. }
  3891. if(!all){
  3892. fetch = false;
  3893. }
  3894. // 获取完毕
  3895. else if(params.page == douga.commentListTotalPage){
  3896. fetch = false;
  3897. }
  3898. // 继续获取
  3899. if(fetch){
  3900. params.page += 1;
  3901. // 回调
  3902. if(_.isFunction(callback)){
  3903. callback(true, false, douga.commentCount, params.page, douga.commentListTotalPage);
  3904. }
  3905. getData();
  3906. }
  3907. // 获取结束
  3908. else{
  3909. // 添加评论
  3910. if(!douga.commentList){
  3911. douga.commentList = [];
  3912. }
  3913. if(commentList.length>0){
  3914. douga.commentList.splice(0, 0, ...commentList);
  3915. }
  3916. // 回调
  3917. if(_.isFunction(callback)){
  3918. callback(true, true, douga.commentCount, params.page, douga.commentListTotalPage);
  3919. }
  3920. }
  3921. });
  3922. }
  3923. // 获取数据
  3924. getData();
  3925. },
  3926. clickMessage : function(message, isDouga){
  3927. // 弹幕
  3928. if(message.interaction.type == 'danmaku'){
  3929. // 点击稿件,则复制稿件ac号
  3930. if(isDouga){
  3931. clipboard.writeText(message.interaction.acSeries);
  3932. this.$message({
  3933. message : '已复制ac号:' + message.interaction.acSeries,
  3934. type : 'info',
  3935. duration : 2000,
  3936. });
  3937. }
  3938. // 否则复制弹幕内容
  3939. else{
  3940. clipboard.writeText(message.interaction.content);
  3941. this.$message({
  3942. message : '已复制弹幕:' + message.interaction.content,
  3943. type : 'info',
  3944. duration : 2000,
  3945. });
  3946. }
  3947. }
  3948. },
  3949. getDougaByAcSeries(acSeries, callback){
  3950. let vue = this;
  3951. // 获取视频
  3952. this.getVideoByAcSeries(acSeries, function(isSuccess, data){
  3953. // 获取成功
  3954. if(isSuccess){
  3955. if(_.isFunction(callback)){
  3956. callback(isSuccess, data);
  3957. }
  3958. }
  3959. // 获取失败,则获取文章
  3960. else{
  3961. // 获取文章
  3962. vue.getArticleByAcSeries(acSeries, function(isSuccess, data){
  3963. // 获取成功
  3964. if(isSuccess){
  3965. if(_.isFunction(callback)){
  3966. callback(isSuccess, data);
  3967. }
  3968. }
  3969. else{
  3970. // 获取番剧
  3971. vue.getBangumiByAcSeries(acSeries, function(isSuccess, data){
  3972. // 获取成功
  3973. if(isSuccess){
  3974. if(_.isFunction(callback)){
  3975. callback(isSuccess, data);
  3976. }
  3977. }
  3978. else{
  3979. if(_.isFunction(callback)){
  3980. callback(isSuccess, data);
  3981. }
  3982. }
  3983. });
  3984. }
  3985. });
  3986. }
  3987. });
  3988. },
  3989. getVideoByAcSeries(acSeries, callback){
  3990. commonRequrest(config.ACFUN_SERVER + config.URLS.DOUGA.VIDEO + `/ac${acSeries}`, 'get', null, true, function(isSuccess, data){
  3991. // 获取成功
  3992. if(isSuccess){
  3993. var match = data.match(new RegExp('window.videoInfo = (?<data>.+)'));
  3994. // 匹配成功
  3995. if(match){
  3996. eval('var videoInfo = ' + match.groups.data);
  3997. if(_.isFunction(callback)){
  3998. callback(true, {
  3999. type : 'video',
  4000. title : videoInfo.title,
  4001. cover : videoInfo.coverUrl,
  4002. channel : videoInfo.channel,
  4003. description : videoInfo.description,
  4004. danmakuCount : videoInfo.danmakuCount,
  4005. danmakuCountText : videoInfo.danmakuCountShow,
  4006. playCount : videoInfo.viewCount,
  4007. playCountText : videoInfo.viewCountShow,
  4008. uid : parseInt(videoInfo.user.id),
  4009. userName : videoInfo.user.name,
  4010. });
  4011. }
  4012. }
  4013. else{
  4014. if(_.isFunction(callback)){
  4015. callback(false);
  4016. }
  4017. }
  4018. }
  4019. else{
  4020. if(_.isFunction(callback)){
  4021. callback(false);
  4022. }
  4023. }
  4024. });
  4025. },
  4026. getArticleByAcSeries(acSeries, callback){
  4027. commonRequrest(config.ACFUN_SERVER + config.URLS.DOUGA.ARTICLE + `/ac${acSeries}`, 'get', null, true, function(isSuccess, data){
  4028. // 获取成功
  4029. if(isSuccess){
  4030. var match = data.match(new RegExp('window.articleInfo = (?<data>.+)'));
  4031. // 匹配成功
  4032. if(match){
  4033. eval('var articleInfo = ' + match.groups.data);
  4034. if(_.isFunction(callback)){
  4035. callback(true, {
  4036. type : 'article',
  4037. title : articleInfo.title,
  4038. // cover : articleInfo.coverUrl,
  4039. description : articleInfo.description,
  4040. channel : articleInfo.channel,
  4041. playCount : articleInfo.viewCount,
  4042. playCountText : articleInfo.formatViewCount,
  4043. uid : parseInt(articleInfo.user.id),
  4044. userName : articleInfo.user.name,
  4045. });
  4046. }
  4047. }
  4048. else{
  4049. if(_.isFunction(callback)){
  4050. callback(false);
  4051. }
  4052. }
  4053. }
  4054. else{
  4055. if(_.isFunction(callback)){
  4056. callback(false);
  4057. }
  4058. }
  4059. });
  4060. },
  4061. getBangumiByAcSeries(acSeries, callback){
  4062. commonRequrest(config.ACFUN_SERVER + config.URLS.DOUGA.BANGUMI + `/aa${acSeries}`, 'get', null, true, function(isSuccess, data){
  4063. // 获取成功
  4064. if(isSuccess){
  4065. var match = data.match(new RegExp('window.bangumiData = (?<data>.+)'));
  4066. // 匹配成功
  4067. if(match){
  4068. eval('var bangumiData = ' + match.groups.data);
  4069. if(_.isFunction(callback)){
  4070. callback(true, {
  4071. type : 'bangumi',
  4072. title : bangumiData.bangumiTitle,
  4073. acfunOnly : bangumiData.acfunOnly,
  4074. cover : bangumiData.bangumiCoverImageH,
  4075. description : bangumiData.bangumiIntro,
  4076. channel : bangumiData.channel,
  4077. playCount : bangumiData.playCount,
  4078. playCountText : bangumiData.playCountShow,
  4079. stowCount : bangumiData.stowCount,
  4080. stowCountText : bangumiData.stowCountShow,
  4081. });
  4082. }
  4083. }
  4084. else{
  4085. if(_.isFunction(callback)){
  4086. callback(false);
  4087. }
  4088. }
  4089. }
  4090. else{
  4091. if(_.isFunction(callback)){
  4092. callback(false);
  4093. }
  4094. }
  4095. });
  4096. },
  4097. // 发送一条测试用的弹幕
  4098. pushTestMessage : function(type){
  4099. var message = {
  4100. uid : 37693149,
  4101. vup : false,
  4102. userName : '特工澪',
  4103. badgeName : '505团',
  4104. badgeLevel : 999,
  4105. badgeColor : 'black',
  4106. photo : 'https://tx-free-imgs2.acfun.cn/kimg/EJjM1y8qPQoFYWNmdW4SBWltYWdlGi0zNzY5MzE0OV8yYTE4YjBiZTZhZTM0OTc1OTkyNDIyZGFhMTdlNzA0MS5wbmc.png',
  4107. interaction : {
  4108. type: type,
  4109. sendTime : new Date(),
  4110. id: uuidv4(),
  4111. giftName: '猴岛',
  4112. giftCount: 1,
  4113. content: type == 'danmaku'?'听我说👂👂👂谢谢你🙏🙏🙏因为有你👉👉👉温暖了四季🌈🌈🌈谢谢你🙏🙏🙏感谢有你👉👉👉世界更美丽🌏🌏🌏我要谢谢你🙏🙏🙏因为有你👉👉👉爱常在心底💃💃💃谢谢你 🙏🙏🙏感谢有你🙇♂🙇♂🙇♂把幸福传递 ac32814163':'',
  4114. },
  4115. };
  4116. if(!(message.uid in userInfo)){
  4117. userInfo[message.uid] = {
  4118. userName: message.userName,
  4119. photo: message.photo,
  4120. };
  4121. }
  4122. callbackManager.feed('message', message);
  4123. },
  4124. pushTestDanmakuMessage: function(){
  4125. this.pushTestMessage('danmaku');
  4126. },
  4127. pushTestGiftMessage: function(){
  4128. this.pushTestMessage('gift');
  4129. },
  4130. pushTestLikeMessage: function(){
  4131. this.pushTestMessage('like');
  4132. },
  4133. pushTestEnterRoomMessage: function(){
  4134. this.pushTestMessage('enterroom');
  4135. },
  4136. pushTestFollowMessage: function(){
  4137. this.pushTestMessage('follow');
  4138. },
  4139. pushTestJoinClubMessage: function(){
  4140. this.pushTestMessage('joinclub');
  4141. },
  4142. },
  4143. computed : {
  4144. danmakuMessageClass: function(){
  4145. return ['live-message-danmaku'].concat(this.borderClass);
  4146. },
  4147. backgroundStyle: function(){
  4148. style = {
  4149. background: this.interactionFormData.backgroundColor,
  4150. };
  4151. return style;
  4152. },
  4153. borderClass: function(){
  4154. classes = [];
  4155. // 圆角矩形边框
  4156. if(this.interactionFormData.borderShape == 'round'){
  4157. }
  4158. // 矩形
  4159. else if(this.interactionFormData.borderShape == 'rect'){
  4160. }
  4161. // 流动
  4162. else if(this.interactionFormData.borderShape == 'flow'){
  4163. classes.push('live-message-border-flow')
  4164. }
  4165. return classes;
  4166. },
  4167. borderStyle: function(){
  4168. style = {};
  4169. // 圆角矩形边框
  4170. if(this.interactionFormData.borderShape == 'round'){
  4171. style['border-radius'] = '6px';
  4172. style['border'] = `${this.interactionFormData.borderSize}px solid ${this.interactionFormData.borderColor}`;
  4173. }
  4174. // 矩形
  4175. else if(this.interactionFormData.borderShape == 'rect'){
  4176. style['border'] = `${this.interactionFormData.borderSize}px solid ${this.interactionFormData.borderColor}`;
  4177. }
  4178. // 流动
  4179. else if(this.interactionFormData.borderShape == 'flow'){
  4180. GM_addStyle(`
  4181. .live-message-border-flow.active{
  4182. border: ${this.interactionFormData.borderSize}px solid transparent
  4183. }
  4184. .live-message-border-flow.inactive{
  4185. border: ${this.interactionFormData.borderSize}px solid ${this.interactionFormData.borderColor}
  4186. }
  4187. `);
  4188. }
  4189. // 自适应宽度
  4190. if(this.interactionFormData.borderWidth == 'auto'){
  4191. style['width'] = 'max-content';
  4192. style['display'] = 'table-cell';
  4193. }
  4194. // 定宽
  4195. else if(this.interactionFormData.borderWidth == 'fixed'){
  4196. style['width'] = '100%';
  4197. style['display'] = 'block';
  4198. }
  4199. return style;
  4200. },
  4201. borderFlowItemStyle: function(){
  4202. style = {};
  4203. // 圆角矩形边框
  4204. if(this.interactionFormData.borderShape == 'round'){
  4205. }
  4206. // 矩形
  4207. else if(this.interactionFormData.borderShape == 'rect'){
  4208. }
  4209. // 流动
  4210. else if(this.interactionFormData.borderShape == 'flow'){
  4211. GM_addStyle(`
  4212. .live-message-border-flow.active .border-flow-item::before,.live-message-border-flow.active .border-flow-item::after{
  4213. border: ${this.interactionFormData.borderSize}px solid ${this.interactionFormData.borderColor}
  4214. }
  4215. .live-message-border-flow .border-flow-item::before,.live-message-border-flow .border-flow-item::after{
  4216. top: -${this.interactionFormData.borderSize}px;
  4217. bottom: -${this.interactionFormData.borderSize}px;
  4218. left: -${this.interactionFormData.borderSize}px;
  4219. right: -${this.interactionFormData.borderSize}px;
  4220. }
  4221. `);
  4222. }
  4223. return style;
  4224. },
  4225. userNameMessageStyle : function(){
  4226. return {
  4227. 'color':this.interactionFormData.userNameFontColor,
  4228. 'font-size':this.interactionFormData.userNameFontSize + 'px',
  4229. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4230. 'text-shadow':`0px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, 0px -${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, ${this.interactionFormData.userNameFontShadowSize}px 0px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, -${this.interactionFormData.userNameFontShadowSize}px 0px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, -${this.interactionFormData.userNameFontShadowSize}px -${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, ${this.interactionFormData.userNameFontShadowSize}px -${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}, -${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowSize}px ${this.interactionFormData.userNameFontShadowColor}`,
  4231. };
  4232. },
  4233. userBadgeStyle : function(){
  4234. return {
  4235. 'font-size':this.interactionFormData.userNameFontSize*0.8 + 'px',
  4236. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4237. };
  4238. },
  4239. userPhotoSize : function(){
  4240. return this.interactionFormData.userNameFontSize*2;
  4241. },
  4242. danmakuMessageStyle : function(){
  4243. return {
  4244. ...this.borderStyle,
  4245. 'background-image' : `linear-gradient(to right, ${this.interactionFormData.danmakuBackgroundColor}, ${this.interactionFormData.danmakuBackgroundGradientColor})`,
  4246. 'color':this.interactionFormData.danmakuFontColor,
  4247. 'font-size':this.interactionFormData.danmakuFontSize + 'px',
  4248. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4249. 'text-shadow':`0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, 0px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px 0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px 0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}`,
  4250. };
  4251. },
  4252. danmakuMessageContainerStyle : function(){
  4253. // 横版
  4254. if(this.interactionFormData.direction == 'horizontal'){
  4255. var style = {
  4256. 'max-width' : this.interactionFormData.danmakuFontSize*20 + 'px',
  4257. };
  4258. if(this.interactionFormData.displayNotWrap){
  4259. style.height = '100%';
  4260. }
  4261. return style;
  4262. }
  4263. // 纵版
  4264. else{
  4265. }
  4266. },
  4267. danmakuDougaMessageStyle: function(){
  4268. return {
  4269. ...this.borderStyle,
  4270. 'border': 'transparent',
  4271. 'display': 'block',
  4272. 'width': this.danmakuDougaContainerStyle.width,
  4273. 'background-image' : `linear-gradient(to right, ${this.interactionFormData.danmakuDougaBackgroundColor}, ${this.interactionFormData.danmakuDougaBackgroundColor})`,
  4274. 'color':this.interactionFormData.danmakuFontColor,
  4275. 'font-size':this.interactionFormData.danmakuFontSize + 'px',
  4276. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4277. 'text-shadow':`0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, 0px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px 0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px 0px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${this.interactionFormData.danmakuFontShadowSize}px -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowSize}px ${this.interactionFormData.danmakuFontShadowColor}`,
  4278. };
  4279. },
  4280. danmakuDougaContainerStyle: function(){
  4281. var height = Math.ceil(this.interactionFormData.danmakuFontSize*0.8*1.3*3 + this.interactionFormData.danmakuFontSize*0.6*1.3*2);
  4282. var width = Math.ceil(height*16/9)*2;
  4283. return {
  4284. 'width' : width + 'px',
  4285. };
  4286. },
  4287. danmakuDougaTitleStyle : function(){
  4288. var fontSize = Math.ceil(this.interactionFormData.danmakuFontSize*0.8);
  4289. var shadowSize = Math.ceil(this.interactionFormData.danmakuFontShadowSize*0.8);
  4290. var height = Math.ceil(fontSize*1.3 * 3);
  4291. return {
  4292. 'height': height + 'px',
  4293. 'color':this.interactionFormData.danmakuFontColor,
  4294. 'font-size':fontSize + 'px',
  4295. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4296. 'text-shadow':`0px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, 0px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}`,
  4297. };
  4298. },
  4299. danmakuDougaDescriptionStyle : function(){
  4300. var fontSize = Math.ceil(this.interactionFormData.danmakuFontSize*0.6);
  4301. var shadowSize = Math.ceil(this.interactionFormData.danmakuFontShadowSize*0.6);
  4302. return {
  4303. 'font-size':fontSize + 'px',
  4304. 'text-shadow':`0px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, 0px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}`,
  4305. };
  4306. },
  4307. danmakuDougaCoverStyle : function(){
  4308. var height = Math.ceil(this.interactionFormData.danmakuFontSize*0.8*1.3*3 + this.interactionFormData.danmakuFontSize*0.6*1.3*2);
  4309. var width = Math.ceil(height*16/9);
  4310. return {
  4311. 'height' : height + 'px',
  4312. 'width' : width + 'px',
  4313. };
  4314. },
  4315. danmakuDougaLeftColStyle : function(){
  4316. return {
  4317. 'width': this.danmakuDougaCoverStyle.width,
  4318. 'display': 'inline-block',
  4319. 'float': 'left',
  4320. };
  4321. },
  4322. danmakuDougaRightColStyle : function(){
  4323. return {
  4324. 'width': `calc(100% - ${this.danmakuDougaLeftColStyle.width})`,
  4325. 'display': 'inline-block',
  4326. 'float': 'left',
  4327. };
  4328. },
  4329. danmakuDougaInfoStyle : function(){
  4330. var fontSize = Math.ceil(this.interactionFormData.danmakuFontSize*0.6);
  4331. var shadowSize = Math.ceil(this.interactionFormData.danmakuFontShadowSize*0.6);
  4332. return {
  4333. 'color':this.interactionFormData.danmakuFontColor,
  4334. 'font-size':fontSize + 'px',
  4335. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4336. 'text-shadow':`0px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, 0px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px 0px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, ${shadowSize}px -${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}, -${shadowSize}px ${shadowSize}px ${shadowSize}px ${this.interactionFormData.danmakuFontShadowColor}`,
  4337. };
  4338. },
  4339. giftMessageStyle : function(){
  4340. return {
  4341. ...this.borderStyle,
  4342. 'background-image' : `linear-gradient(to right, ${this.interactionFormData.giftBackgroundColor}, ${this.interactionFormData.giftBackgroundGradientColor})`,
  4343. 'color':this.interactionFormData.giftFontColor,
  4344. 'font-size':this.interactionFormData.giftFontSize + 'px',
  4345. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4346. 'text-shadow':`0px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, 0px -${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, ${this.interactionFormData.giftFontShadowSize}px 0px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, -${this.interactionFormData.giftFontShadowSize}px 0px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, -${this.interactionFormData.giftFontShadowSize}px -${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, ${this.interactionFormData.giftFontShadowSize}px -${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}, -${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowSize}px ${this.interactionFormData.giftFontShadowColor}`,
  4347. };
  4348. },
  4349. giftMessageContainerStyle : function(){
  4350. // 横版
  4351. if(this.interactionFormData.direction == 'horizontal'){
  4352. var style = {
  4353. 'max-width' : this.interactionFormData.giftFontSize*20 + 'px',
  4354. };
  4355. if(this.interactionFormData.displayNotWrap){
  4356. style.height = '100%';
  4357. }
  4358. return style;
  4359. }
  4360. // 纵版
  4361. else{
  4362. }
  4363. },
  4364. giftImageSize : function(){
  4365. return this.interactionFormData.giftFontSize*1.5;
  4366. },
  4367. giftCountStyle : function(){
  4368. return {
  4369. 'font-size':this.interactionFormData.giftFontSize*1.5 + 'px',
  4370. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1] == 'inherit' ? 'gift':this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4371. };
  4372. },
  4373. likeMessageStyle : function(){
  4374. return {
  4375. 'color':this.interactionFormData.likeFontColor,
  4376. 'font-size':this.interactionFormData.likeFontSize + 'px',
  4377. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4378. 'text-shadow':`0px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, 0px -${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, ${this.interactionFormData.likeFontShadowSize}px 0px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, -${this.interactionFormData.likeFontShadowSize}px 0px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, -${this.interactionFormData.likeFontShadowSize}px -${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, ${this.interactionFormData.likeFontShadowSize}px -${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}, -${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowSize}px ${this.interactionFormData.likeFontShadowColor}`,
  4379. };
  4380. },
  4381. likeMessageContainerStyle : function(){
  4382. // 横版
  4383. if(this.interactionFormData.direction == 'horizontal'){
  4384. return {
  4385. 'max-width' : this.interactionFormData.likeFontSize*20 + 'px',
  4386. }
  4387. }
  4388. // 纵版
  4389. else{
  4390. }
  4391. },
  4392. enterroomMessageStyle : function(){
  4393. return {
  4394. 'color':this.interactionFormData.enterroomFontColor,
  4395. 'font-size':this.interactionFormData.enterroomFontSize + 'px',
  4396. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4397. 'text-shadow':`0px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, 0px -${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, ${this.interactionFormData.enterroomFontShadowSize}px 0px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, -${this.interactionFormData.enterroomFontShadowSize}px 0px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, -${this.interactionFormData.enterroomFontShadowSize}px -${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, ${this.interactionFormData.enterroomFontShadowSize}px -${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}, -${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowSize}px ${this.interactionFormData.enterroomFontShadowColor}`,
  4398. };
  4399. },
  4400. enterroomMessageContainerStyle : function(){
  4401. // 横版
  4402. if(this.interactionFormData.direction == 'horizontal'){
  4403. return {
  4404. 'max-width' : this.interactionFormData.enterroomFontSize*20 + 'px',
  4405. }
  4406. }
  4407. // 纵版
  4408. else{
  4409. }
  4410. },
  4411. followMessageStyle : function(){
  4412. return {
  4413. 'color':this.interactionFormData.followFontColor,
  4414. 'font-size':this.interactionFormData.followFontSize + 'px',
  4415. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4416. 'text-shadow':`0px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, 0px -${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, ${this.interactionFormData.followFontShadowSize}px 0px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, -${this.interactionFormData.followFontShadowSize}px 0px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, -${this.interactionFormData.followFontShadowSize}px -${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, ${this.interactionFormData.followFontShadowSize}px -${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}, -${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowSize}px ${this.interactionFormData.followFontShadowColor}`,
  4417. };
  4418. },
  4419. followMessageContainerStyle : function(){
  4420. // 横版
  4421. if(this.interactionFormData.direction == 'horizontal'){
  4422. return {
  4423. 'max-width' : this.interactionFormData.followFontSize*20 + 'px',
  4424. }
  4425. }
  4426. // 纵版
  4427. else{
  4428. }
  4429. },
  4430. joinclubMessageStyle : function(){
  4431. return {
  4432. 'color':this.interactionFormData.joinclubFontColor,
  4433. 'font-size':this.interactionFormData.joinclubFontSize + 'px',
  4434. 'font-family' : this.interactionFormData.fontFamily[this.interactionFormData.fontFamily.length-1],
  4435. 'text-shadow':`0px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, 0px -${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, ${this.interactionFormData.joinclubFontShadowSize}px 0px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, -${this.interactionFormData.joinclubFontShadowSize}px 0px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, -${this.interactionFormData.joinclubFontShadowSize}px -${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, ${this.interactionFormData.joinclubFontShadowSize}px -${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}, -${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowSize}px ${this.interactionFormData.joinclubFontShadowColor}`,
  4436. };
  4437. },
  4438. joinclubMessageContainerStyle : function(){
  4439. // 横版
  4440. if(this.interactionFormData.direction == 'horizontal'){
  4441. return {
  4442. 'max-width' : this.interactionFormData.joinclubFontSize*20 + 'px',
  4443. }
  4444. }
  4445. // 纵版
  4446. else{
  4447. }
  4448. },
  4449. activeGuessGuessListData : function(){
  4450. var listData = this.activeGuess.displayDirectGuess?this.activeGuess.directGuessList:(this.activeGuess.onlyDisplayMatch?this.activeGuess.matchGuessList:this.activeGuess.guessList);
  4451. return listData?listData:[];
  4452. },
  4453. toUserSpace: function(...args){
  4454. console.log(args)
  4455. // console.log(candidate, config.ACFUN_SERVER + config.URLS.ACFUN_USER.SPACE + '/' + candidate.uid)
  4456. // window.open(config.ACFUN_SERVER + config.URLS.ACFUN_USER.SPACE + '/' + candidate.uid);
  4457. },
  4458. },
  4459. mounted : function(){
  4460. var vue = this;
  4461. var messageContainerEle = document.querySelector('.container-live-feed-messages-acfunlive .live-message-container');
  4462. // 获取弹幕消息
  4463. callbackManager.register('message', function(data){
  4464. // 修改信息
  4465. if(_.isFunction(vue.interactionFormData.modifyContentFunc)){
  4466. vue.interactionFormData.modifyContentFunc(data);
  4467. }
  4468. if(data.interaction.type == 'enterroom'){
  4469. userInfo[data.uid].hasEnterroom = true;
  4470. }
  4471. else if(data.interaction.type == 'danmaku'){
  4472. userInfo[data.uid].hasDanmaku = true;
  4473. vue.lastDanmakuMessage = data;
  4474. }
  4475. else if(data.interaction.type == 'like'){
  4476. userInfo[data.uid].hasLike = true;
  4477. }
  4478. else if(data.interaction.type == 'gift'){
  4479. userInfo[data.uid].hasGift = true;
  4480. }
  4481. else if(data.interaction.type == 'follow'){
  4482. userInfo[data.uid].hasFollow = true;
  4483. }
  4484. // 礼物合并
  4485. if(
  4486. data.interaction.type == 'gift'
  4487. && userInfo[data.uid].lastInteraction
  4488. && userInfo[data.uid].lastInteraction.type == 'gift'
  4489. && data.interaction.giftName == userInfo[data.uid].lastInteraction.giftName
  4490. && data.interaction.sendTime - userInfo[data.uid].lastInteraction.sendTime<=5*1000*(data.interaction.giftName=='香蕉'?2:1)
  4491. ){
  4492. userInfo[data.uid].lastInteraction.giftCount += data.interaction.giftCount;
  4493. userInfo[data.uid].lastInteraction.sendTime = data.interaction.sendTime;
  4494. userInfo[data.uid].lastInteraction.doAnime = false;
  4495. userInfo[data.uid].lastInteraction.doAnime = true;
  4496. }
  4497. else{
  4498. vue.messageData.push(data);
  4499. userInfo[data.uid].lastInteraction = data.interaction;
  4500. }
  4501. if(data.interaction.type == 'danmaku'){
  4502. // 如果有ac号,获取稿件信息
  4503. data.interaction.acSeries = extractAcSeries(data.interaction.content);
  4504. if(data.interaction.acSeries != null){
  4505. vue.getDougaByAcSeries(data.interaction.acSeries, function(isSuccess, douga){
  4506. // 获取稿件成功
  4507. if(isSuccess){
  4508. data.interaction.douga = douga;
  4509. }
  4510. });
  4511. }
  4512. // 如果是指令
  4513. var command = extractCommand(data.interaction.content);
  4514. if(command!=null){
  4515. data = _.cloneDeep(data);
  4516. data.command = command.command;
  4517. data.interaction.content = command.text;
  4518. callbackManager.feed('command:'+command.command, data);
  4519. }
  4520. }
  4521. // 如果鼠标没有悬浮,则自动滚动至底部
  4522. if(!vue.mouseOn){
  4523. vue.$nextTick(function(){
  4524. // 横版滚动
  4525. if(vue.interactionFormData.direction=='horizontal'){
  4526. messageContainerEle.scrollLeft = messageContainerEle.scrollWidth;
  4527. }
  4528. // 纵版滚动
  4529. else{
  4530. messageContainerEle.scrollTop = messageContainerEle.scrollHeight;
  4531. }
  4532. });
  4533. }
  4534. });
  4535. // 监听事件
  4536. messageContainerEle.addEventListener('mouseenter', function(e){
  4537. // 鼠标悬浮,显示滚动条
  4538. vue.mouseOn = true;
  4539. });
  4540. messageContainerEle.addEventListener('mouseleave', function(e){
  4541. // 鼠标离开,隐藏滚动条
  4542. vue.mouseOn = false;
  4543. });
  4544. this.giftList.forEach(function(gift){
  4545. vue.giftNameMapper[gift.name] = gift;
  4546. });
  4547. async function loadData(){
  4548. // 查看缓存的数据
  4549. var audioFormData = await GM_getValue('audioFormData');
  4550. if(audioFormData){
  4551. vue.audioFormData = JSON.parse(audioFormData);
  4552. vue.handleAudioFormSubmitValid();
  4553. }
  4554. var interactionPreset = await GM_getValue('interactionPreset');
  4555. if(interactionPreset){
  4556. interactionPreset = JSON.parse(interactionPreset);
  4557. vue.interactionPreset = interactionPreset;
  4558. var activeInteractionPresetIndex = _.findIndex(interactionPreset, {active:true});
  4559. if(activeInteractionPresetIndex!=-1){
  4560. vue.activeInteractionPreset(interactionPreset[activeInteractionPresetIndex]);
  4561. vue.downloadedFonts = await GM_getValue('downloadedFonts');
  4562. for(var fontName in vue.downloadedFonts){
  4563. // 添加字体
  4564. GM_addStyle(`
  4565. @font-face{
  4566. font-family : ${fontName};
  4567. src: url(data:font/truetype;charset=utf-8;base64,${vue.downloadedFonts[fontName]}) format('truetype');
  4568. }
  4569. `);
  4570. }
  4571. }
  4572. }
  4573. else{
  4574. vue.handleInteractionFormSubmitValid();
  4575. }
  4576. /*var interactionFormData = await GM_getValue('interactionFormData');
  4577. if(interactionFormData){
  4578. interactionFormData = JSON.parse(interactionFormData);
  4579. if(!interactionFormData.historyMinutes){
  4580. interactionFormData.historyMinutes = 10;
  4581. }
  4582. if(!interactionFormData.historyCount){
  4583. interactionFormData.historyCount = 200;
  4584. }
  4585. if(interactionFormData.joinclubFontSize){
  4586. vue.interactionFormData = interactionFormData;
  4587. vue.handleInteractionFormSubmitValid();
  4588. // 默认字体
  4589. if(!vue.interactionFormData.fontFamily){
  4590. vue.interactionFormData.fontFamily = ['default', 'inherit'];
  4591. }
  4592. var fontName = vue.interactionFormData.fontFamily[vue.interactionFormData.fontFamily.length-1];
  4593. if(fontName!='inherit'){
  4594. vue.downloadedFonts = await GM_getValue('downloadedFonts');
  4595. for(var fontName in vue.downloadedFonts){
  4596. // 添加字体
  4597. GM_addStyle(`
  4598. @font-face{
  4599. font-family : ${fontName};
  4600. src: url(data:font/truetype;charset=utf-8;base64,${vue.downloadedFonts[fontName]}) format('truetype');
  4601. }
  4602. `);
  4603. }
  4604. }
  4605. }
  4606. }
  4607. else{
  4608. vue.handleInteractionFormSubmitValid();
  4609. }*/
  4610. }
  4611. loadData();
  4612. // 每500毫秒获取一次用户信息,避免被封
  4613. window.setInterval(()=>{
  4614. if(vue.candidateInfoCallbacks.length>0){
  4615. let [candidate, callback] = vue.candidateInfoCallbacks.splice(0, 1)[0];
  4616. vue._getCandidateInfo(candidate, callback);
  4617. [candidate, callback] = vue.candidateInfoCallbacks.splice(0, 1)[0];
  4618. vue._getCandidateInfo(candidate, callback);
  4619. [candidate, callback] = vue.candidateInfoCallbacks.splice(0, 1)[0];
  4620. vue._getCandidateInfo(candidate, callback);
  4621. }
  4622. }, 500);
  4623. // 按键绑定,从ctrl+alt+0到ctrl+alt+9
  4624. hotkeys(_.range(10).map((index)=>`ctrl+alt+${index}`).join(','), (event, handler)=>{
  4625. var index = parseInt(handler.key.split('+')[2]);
  4626. console.log('key', index, this.interactionPreset[index-1])
  4627. if(index<=this.interactionPreset.length){
  4628. this.activeInteractionPreset(this.interactionPreset[index-1]);
  4629. }
  4630. });
  4631. },
  4632. });
  4633. return vue;
  4634. }
  4635. disableVideos();
  4636. window.onload = function(){
  4637. start();
  4638. var intervalHandler = window.setInterval(function(){
  4639. if(loadVue()){
  4640. window.clearInterval(intervalHandler);
  4641. }
  4642. }, 500);
  4643. };
  4644. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址