VoiceMatrix.vue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. <template>
  2. <div v-if="curQue" class="voice-matrix">
  3. <div class="voice-matrix-audio">
  4. <div v-if="curQue.voiceMatrix.isAudioNumber" class="audio-number">
  5. <span
  6. :class="[
  7. themeColor.length === 0 || themeColor === 'red'
  8. ? 'serial-number'
  9. : `serial-number-${themeColor}`
  10. ]"
  11. >
  12. {{ curQue.voiceMatrix.audioSerialNumber }}
  13. </span>
  14. </div>
  15. <div v-show="hasSelectedCell" class="audio-simple">
  16. <img :src="playing ? voicePlaySrc : voicePauseSrc" @click="playAudio">
  17. <span
  18. :class="['Repeat-16', isRepeat ? '' : 'disabled']"
  19. @click="isRepeat = !isRepeat"
  20. />
  21. </div>
  22. <audio-line
  23. v-show="!hasSelectedCell"
  24. ref="audioLine"
  25. audio-id="voiceMatrixAudio"
  26. :mp3="mp3Url"
  27. :get-cur-time="getCurTime"
  28. :stop-audio="stopAudio"
  29. :mp3-source="mp3Source"
  30. @handleChangeStopAudio="handleChangeStopAudio"
  31. @playChange="playChange"
  32. />
  33. </div>
  34. <!-- 语音矩阵 -->
  35. <div class="voice-matrix-container">
  36. <div
  37. v-if="curQue.voiceMatrix.matrix.length > 0"
  38. class="matrix"
  39. :style="{
  40. 'grid-template': `36px repeat(${curQue.voiceMatrix.matrix.length}, auto) minmax(36px, 1fr) / 36px repeat(${curQue.voiceMatrix.matrix[0].length}, auto) minmax(36px, 1fr)`
  41. }"
  42. @mouseleave="clearSelectCell"
  43. >
  44. <!-- 顶部单元格 -->
  45. <div class="matrix-top" @mouseenter="clearSelectCell" />
  46. <template v-for="(row, i) in curQue.voiceMatrix.matrix[0]">
  47. <div
  48. :key="`top-${i}`"
  49. :class="[
  50. 'matrix-top',
  51. curQue.voiceMatrix.columnSelection &&
  52. (selectColumn === i ||
  53. (selectedLine.type === 'column' && selectedLine.index === i))
  54. ? 'read'
  55. : ''
  56. ]"
  57. @mouseenter="checkboxMouseenter(selectColumn === i, 'column')"
  58. >
  59. <span
  60. v-if="
  61. row.type !== 'connection' && curQue.voiceMatrix.columnSelection
  62. "
  63. v-show="
  64. selectColumn === i ||
  65. (selectedLine.type === 'column' && selectedLine.index === i)
  66. "
  67. :class="[
  68. `matrix-checkbox-row-${themeColor}`,
  69. selectedLine.type === 'column' && selectedLine.index === i
  70. ? 'active'
  71. : ''
  72. ]"
  73. @click="selectRowOrColumn(i, 'column')"
  74. />
  75. </div>
  76. </template>
  77. <div class="matrix-top" @mouseenter="clearSelectCell" />
  78. <!-- 主矩阵 -->
  79. <template v-for="(row, i) in curQue.voiceMatrix.matrix">
  80. <div
  81. :key="`start-${i}`"
  82. :class="[
  83. 'column-wrapper',
  84. curQue.voiceMatrix.rowSelection &&
  85. (selectRow === i ||
  86. (selectedLine.type === 'row' && selectedLine.index === i))
  87. ? 'read'
  88. : ''
  89. ]"
  90. @mouseenter="checkboxMouseenter(selectRow === i, 'row')"
  91. >
  92. <span
  93. v-if="curQue.voiceMatrix.rowSelection"
  94. v-show="
  95. selectRow === i ||
  96. (selectedLine.type === 'row' && selectedLine.index === i)
  97. "
  98. :class="[
  99. `matrix-checkbox-column-${themeColor}`,
  100. selectedLine.type === 'row' && selectedLine.index === i
  101. ? 'active'
  102. : ''
  103. ]"
  104. @click="selectRowOrColumn(i, 'row')"
  105. />
  106. </div>
  107. <!-- 单元格 -->
  108. <template v-for="(column, j) in row">
  109. <div
  110. :key="`wrapper-${i}-${j}`"
  111. :class="[
  112. 'column-wrapper',
  113. (curQue.voiceMatrix.rowSelection && selectRow === i) ||
  114. (curQue.voiceMatrix.columnSelection && selectColumn === j) ||
  115. (curQue.voiceMatrix.columnSelection &&
  116. selectedLine.type === 'column' &&
  117. selectedLine.index === j) ||
  118. (curQue.voiceMatrix.rowSelection &&
  119. selectedLine.type === 'row' &&
  120. selectedLine.index === i)
  121. ? 'read'
  122. : '',
  123. (i === 0 && curQue.voiceMatrix.firstLineHighlight) ||
  124. (j === row.length - 1 && curQue.voiceMatrix.lastColumnHighlight)
  125. ? `highlight-${themeColor}`
  126. : ''
  127. ]"
  128. @mouseenter="matrixCellMouseenter(i, j, column.type)"
  129. >
  130. <!-- 文本 -->
  131. <div
  132. v-if="column.type === 'text'"
  133. :key="`column-${i}-${j}`"
  134. :class="[
  135. column.text.length === 0 ? 'space' : `column-${themeColor}`,
  136. (selectCell.row === i && selectCell.column === j) ||
  137. (selectedLine.type === 'column' &&
  138. selectedLine.index === j) ||
  139. (selectedLine.type === 'row' && selectedLine.index === i)
  140. ? 'selected'
  141. : '',
  142. playing &&
  143. column.lrc_data.begin_time / 1000 <= curTime &&
  144. (curTime < column.lrc_data.end_time / 1000 ||
  145. column.lrc_data.end_time === -1)
  146. ? 'playing'
  147. : '',
  148. column.isTitle ? 'title' : ''
  149. ]"
  150. @click="matrixCellClick(i, j)"
  151. >
  152. <span>{{ column.text }}</span>
  153. </div>
  154. <!-- 连接线 -->
  155. <div
  156. v-else-if="column.type === 'connection'"
  157. :key="`column-${i}-${j}`"
  158. :class="[
  159. 'connection',
  160. i === 0 && curQue.voiceMatrix.firstLineHighlight
  161. ? `highlight-bc-${themeColor}`
  162. : ''
  163. ]"
  164. />
  165. <!-- 分词 -->
  166. <div
  167. v-else-if="column.type === 'SentenceSegwordChs'"
  168. :key="`column-${i}-${j}`"
  169. :class="[
  170. `sentence-${themeColor}`,
  171. (selectCell.row === i && selectCell.column === j) ||
  172. (selectedLine.type === 'column' &&
  173. selectedLine.index === j) ||
  174. (selectedLine.type === 'row' && selectedLine.index === i)
  175. ? 'selected'
  176. : '',
  177. playing &&
  178. column.lrc_data.begin_time / 1000 <= curTime &&
  179. (curTime < column.lrc_data.end_time / 1000 ||
  180. column.lrc_data.end_time === -1)
  181. ? 'playing'
  182. : '',
  183. column.isTitle ? 'title' : ''
  184. ]"
  185. :style="{
  186. 'grid-template-columns': `repeat(${column.sentence_data.wordsList.length}, auto)`
  187. }"
  188. @click="matrixCellClick(i, j)"
  189. >
  190. <template v-for="(word, w) in column.sentence_data.wordsList">
  191. <span
  192. v-if="column.sentence_data.pyPosition === 'top'"
  193. :key="`pinyin-${w}`"
  194. class="pinyin"
  195. >
  196. {{ word.pinyin }}
  197. </span>
  198. <span v-else :key="`chs-${w}`" class="chs">
  199. {{ word.chs }}
  200. </span>
  201. </template>
  202. <template v-for="(word, w) in column.sentence_data.wordsList">
  203. <span
  204. v-if="column.sentence_data.pyPosition === 'top'"
  205. :key="`chs-${w}`"
  206. class="chs"
  207. >
  208. {{ word.chs }}
  209. </span>
  210. <span v-else :key="`pinyin-${w}`" class="pinyin">
  211. {{ word.pinyin }}
  212. </span>
  213. </template>
  214. </div>
  215. <!-- 拼音 + 英文 -->
  216. <div
  217. v-else-if="column.type === 'PinyinEnglish'"
  218. :key="`column-${i}-${j}`"
  219. :class="[
  220. `pinyinEnglish-${themeColor}`,
  221. (selectCell.row === i && selectCell.column === j) ||
  222. (selectedLine.type === 'column' &&
  223. selectedLine.index === j) ||
  224. (selectedLine.type === 'row' && selectedLine.index === i)
  225. ? 'selected'
  226. : '',
  227. playing &&
  228. column.lrc_data.begin_time / 1000 <= curTime &&
  229. (curTime < column.lrc_data.end_time / 1000 ||
  230. column.lrc_data.end_time === -1)
  231. ? 'playing'
  232. : '',
  233. column.isTitle ? 'title' : ''
  234. ]"
  235. @click="matrixCellClick(i, j)"
  236. >
  237. <div class="inside-wrapper">
  238. <div class="pinyin">
  239. {{ column.pinyin_english_data.pinyin }}
  240. </div>
  241. <div class="english">
  242. {{ column.pinyin_english_data.english }}
  243. </div>
  244. </div>
  245. </div>
  246. </div>
  247. </template>
  248. <div
  249. :key="`end-${i}`"
  250. :class="[
  251. curQue.voiceMatrix.rowSelection &&
  252. (selectRow === i ||
  253. (selectedLine.type === 'row' && selectedLine.index === i))
  254. ? 'read'
  255. : ''
  256. ]"
  257. @mouseenter="clearSelectCell"
  258. />
  259. </template>
  260. <!-- 底部格子 -->
  261. <div class="matrix-bottom" @mouseenter="clearSelectCell" />
  262. <template v-for="(row, i) in curQue.voiceMatrix.matrix[0]">
  263. <div
  264. :key="`bottom-${i}`"
  265. :class="[
  266. 'matrix-bottom',
  267. curQue.voiceMatrix.columnSelection &&
  268. (selectColumn === i ||
  269. (selectedLine.type === 'column' && selectedLine.index === i))
  270. ? 'read'
  271. : ''
  272. ]"
  273. @mouseenter="clearSelectCell"
  274. />
  275. </template>
  276. <div class="matrix-bottom" @mouseenter="clearSelectCell" />
  277. </div>
  278. </div>
  279. <div class="voice-luyin">
  280. <soundrecord
  281. type="promax"
  282. class="luyin-box"
  283. :file-name="fileName"
  284. :select-data="selectData"
  285. @getWavblob="getWavblob"
  286. @getSelectData="getSelectData"
  287. @handleParentPlay="handleParentPlay"
  288. @sentPause="sentPause"
  289. />
  290. <audio-compare
  291. :theme-color="themeColor"
  292. :wavblob="wavblob"
  293. :url="mp3Url"
  294. :is-record="isRecord"
  295. :sent-pause="sentPause"
  296. :matrix-select-lrc="matrixSelectLrc"
  297. :get-cur-time="getCurTime"
  298. :cur-time="curTime"
  299. :handle-change-stop-audio="handleChangeStopAudio"
  300. @playing="playChange"
  301. />
  302. </div>
  303. </div>
  304. </template>
  305. <script>
  306. import Bus from "./components/Bus.js";
  307. import AudioLine from "./AudioLine.vue";
  308. import Soundrecord from "./Soundrecord.vue";
  309. import AudioCompare from "./AudioCompareMatrix.vue";
  310. export default {
  311. components: {
  312. AudioLine,
  313. Soundrecord,
  314. AudioCompare
  315. },
  316. props: ["curQue", "themeColor"],
  317. data() {
  318. return {
  319. curTime: 0,
  320. playing: false,
  321. stopAudio: true,
  322. unWatch: null,
  323. lrcArray: [],
  324. cellTimer: null,
  325. fileName: "",
  326. // 底色行、列
  327. selectRow: -1,
  328. selectColumn: -1,
  329. // 行、列选中
  330. selectedLine: {
  331. type: "",
  332. index: 0
  333. },
  334. // 点击选中
  335. selectCell: {
  336. row: -1,
  337. column: -1
  338. },
  339. isRepeat: false,
  340. // 跟读所需属性
  341. wavblob: null,
  342. isRecord: false,
  343. matrixSelectLrc: null
  344. };
  345. },
  346. computed: {
  347. mp3Url() {
  348. let mp3_list = this.curQue.mp3_list[0];
  349. if (mp3_list === undefined) return "";
  350. return mp3_list.url.match(/^\[FID##/)
  351. ? mp3_list.temporary_url
  352. : mp3_list.url;
  353. },
  354. mp3Source() {
  355. let mp3_list = this.curQue.mp3_list[0];
  356. if (mp3_list === undefined) return "";
  357. return "source" in mp3_list ? mp3_list.source : "";
  358. },
  359. mp3Duration() {
  360. let mp3_list = this.curQue.mp3_list[0];
  361. if (mp3_list === undefined) return 0;
  362. return mp3_list.media_duration * 1000;
  363. },
  364. hasSelectedCell() {
  365. let { type, index } = this.selectedLine;
  366. let { row, column } = this.selectCell;
  367. return (type.length > 0 && index >= 0) || (row >= 0 && column >= 0);
  368. },
  369. selectData() {
  370. let { type, index } = this.selectedLine;
  371. let { row, column } = this.selectCell;
  372. return {
  373. type: type.length > 0 && index >= 0 ? type : "cell",
  374. index,
  375. row,
  376. column
  377. };
  378. },
  379. voicePauseSrc() {
  380. let themeColor = this.themeColor;
  381. if (themeColor.length === 0 || themeColor === "red") {
  382. return require("../../../assets/NPC/play-red.png");
  383. }
  384. return require(`../../../assets/NPC/play-${themeColor}.png`);
  385. },
  386. voicePlaySrc() {
  387. let themeColor = this.themeColor;
  388. if (themeColor.length === 0 || themeColor === "red") {
  389. return require("../../../assets/NPC/icon-voice-play-red.png");
  390. }
  391. return require(`../../../assets/NPC/icon-voice-play-${themeColor}.png`);
  392. }
  393. },
  394. watch: {
  395. hasSelectedCell() {
  396. this.handleParentPlay();
  397. }
  398. },
  399. created() {
  400. Bus.$on("audioPause", () => {
  401. this.handleParentPlay();
  402. });
  403. },
  404. methods: {
  405. // 鼠标移入移出
  406. matrixCellMouseenter(i, j, type) {
  407. if (type === "connection") {
  408. this.selectRow = -1;
  409. this.selectColumn = -1;
  410. } else {
  411. this.selectRow = i;
  412. this.selectColumn = j;
  413. }
  414. },
  415. clearSelectCell() {
  416. this.selectRow = -1;
  417. this.selectColumn = -1;
  418. },
  419. // 单击单元格
  420. matrixCellClick(row, column) {
  421. if (this.playing) this.handleParentPlay();
  422. if (this.unWatch) this.unWatch();
  423. this.lrcArray = [];
  424. if (row === this.selectCell.row && column === this.selectCell.column) {
  425. this.selectCell = { row: -1, column: -1 };
  426. return;
  427. }
  428. this.selectedLine = { type: "", index: -1 };
  429. this.selectCell = { row, column };
  430. this.handleChangeTime(
  431. this.curQue.voiceMatrix.matrix[row][column].lrc_data
  432. );
  433. // 设置录音文件名
  434. this.setRecordingFileName(row, column);
  435. },
  436. setRecordingFileName(row, column) {
  437. let {
  438. type,
  439. text,
  440. sentence_data,
  441. pinyin_english_data
  442. } = this.curQue.voiceMatrix.matrix[row][column];
  443. if (type === "text") this.fileName = text;
  444. if (type === "SentenceSegwordChs") this.fileName = sentence_data.sentence;
  445. if (type === "PinyinEnglish") this.fileName = pinyin_english_data.pinyin;
  446. },
  447. checkboxMouseenter(isSelected, type) {
  448. if (!isSelected) return this.clearSelectCell();
  449. if (type === "row") this.selectColumn = -1;
  450. if (type === "column") this.selectRow = -1;
  451. },
  452. // 选中行、列
  453. selectRowOrColumn(index, type) {
  454. this.handleParentPlay();
  455. this.lrcArray = [];
  456. this.selectCell = { row: -1, column: -1 };
  457. if (this.unWatch) this.unWatch();
  458. if (
  459. this.selectedLine.type === type &&
  460. this.selectedLine.index === index
  461. ) {
  462. this.selectedLine = { type: "", index: -1 };
  463. return;
  464. }
  465. this.selectedLine = { type, index };
  466. let number = index;
  467. if (type === "column") {
  468. this.curQue.voiceMatrix.matrix[index].forEach(({ type }, i) => {
  469. if (i >= index) return;
  470. if (type === "connection") number -= 1;
  471. });
  472. }
  473. this.fileName = `第 ${number + 1} ${type === "row" ? "行" : "列"}`;
  474. },
  475. playAudio() {
  476. if (!this.hasSelectedCell) return;
  477. if (this.playing) return this.handleParentPlay();
  478. if (this.lrcArray.length > 0) return this.$refs.audioLine.PlayAudio();
  479. if (this.unWatch) this.unWatch();
  480. this.lrcArray = [];
  481. let { type, index } = this.selectedLine;
  482. if (type.length > 0 && index >= 0 && type === "row") {
  483. this.curQue.voiceMatrix.matrix[index].forEach(
  484. ({ type, text, lrc_data }) => {
  485. if (
  486. type === "SentenceSegwordChs" ||
  487. type === "PinyinEnglish" ||
  488. (type === "text" && text.length > 0)
  489. ) {
  490. if (lrc_data.end_time === -1) {
  491. this.lrcArray.push({
  492. begin_time: lrc_data.begin_time,
  493. end_time: this.mp3Duration,
  494. text: lrc_data.text
  495. });
  496. return;
  497. }
  498. this.lrcArray.push(lrc_data);
  499. }
  500. }
  501. );
  502. if (this.lrcArray.length > 0) this.lrcPlay(this.lrcArray[0], 0);
  503. return;
  504. }
  505. if (type.length > 0 && index >= 0 && type === "column") {
  506. this.curQue.voiceMatrix.matrix.forEach(item => {
  507. let { type, text, lrc_data } = item[index];
  508. if (
  509. type === "SentenceSegwordChs" ||
  510. type === "PinyinEnglish" ||
  511. (type === "text" && text.length > 0)
  512. ) {
  513. if (lrc_data.end_time === -1) {
  514. this.lrcArray.push({
  515. begin_time: lrc_data.begin_time,
  516. end_time: this.mp3Duration,
  517. text: lrc_data.text
  518. });
  519. return;
  520. }
  521. this.lrcArray.push(lrc_data);
  522. }
  523. });
  524. if (this.lrcArray.length > 0) this.lrcPlay(this.lrcArray[0], 0);
  525. return;
  526. }
  527. let { row, column } = this.selectCell;
  528. if (row >= 0 && column >= 0) {
  529. this.handleChangeTime(
  530. this.curQue.voiceMatrix.matrix[row][column].lrc_data
  531. );
  532. }
  533. },
  534. lrcPlay({ begin_time, end_time }, index) {
  535. this.handleParentPlay();
  536. this.$nextTick(() => {
  537. this.$refs.audioLine.onTimeupdateTime(begin_time / 1000);
  538. this.$refs.audioLine.PlayAudio();
  539. if (end_time === -1) return;
  540. let end = end_time / 1000 - 0.01;
  541. this.unWatch = this.$watch("curTime", val => {
  542. if (val >= end) {
  543. if (!this.hasSelectedCell) return this.unWatch();
  544. this.handleParentPlay();
  545. this.$refs.audioLine.onTimeupdateTime(end);
  546. this.unWatch();
  547. let i = index + 1;
  548. if (i < this.lrcArray.length) {
  549. return this.lrcPlay(this.lrcArray[i], i);
  550. }
  551. if (this.isRepeat) {
  552. return this.lrcPlay(this.lrcArray[0], 0);
  553. }
  554. this.lrcArray = [];
  555. }
  556. });
  557. });
  558. },
  559. playChange(playing) {
  560. this.playing = playing;
  561. // 子组件通信,同时只能播放一个音频
  562. if (playing) Bus.$emit("audioPause");
  563. },
  564. // 暂停音频播放
  565. handleParentPlay() {
  566. this.stopAudio = true;
  567. },
  568. // 音频播放时改变布尔值
  569. handleChangeStopAudio() {
  570. this.stopAudio = false;
  571. },
  572. getCurTime(curTime) {
  573. this.curTime = curTime;
  574. },
  575. getWavblob(wavblob) {
  576. this.wavblob = wavblob;
  577. },
  578. getSelectData({ type, index, row, column }) {
  579. if (type === '') return;
  580. let arr = [];
  581. if (type.length > 0 && index >= 0 && type === "row") {
  582. this.curQue.voiceMatrix.matrix[index].forEach(
  583. ({ type, text, lrc_data }) => {
  584. if (
  585. type === "SentenceSegwordChs" ||
  586. type === "PinyinEnglish" ||
  587. (type === "text" && text.length > 0)
  588. ) {
  589. if (lrc_data.end_time === -1) {
  590. arr.push({
  591. begin_time: lrc_data.begin_time,
  592. end_time: this.mp3Duration,
  593. text: lrc_data.text
  594. });
  595. return;
  596. }
  597. arr.push(lrc_data);
  598. }
  599. }
  600. );
  601. this.matrixSelectLrc = arr;
  602. return;
  603. }
  604. if (type.length > 0 && index >= 0 && type === "column") {
  605. this.curQue.voiceMatrix.matrix.forEach(item => {
  606. let { type, text, lrc_data } = item[index];
  607. if (
  608. type === "SentenceSegwordChs" ||
  609. type === "PinyinEnglish" ||
  610. (type === "text" && text.length > 0)
  611. ) {
  612. if (lrc_data.end_time === -1) {
  613. arr.push({
  614. begin_time: lrc_data.begin_time,
  615. end_time: this.mp3Duration,
  616. text: lrc_data.text
  617. });
  618. return;
  619. }
  620. arr.push(lrc_data);
  621. }
  622. });
  623. this.matrixSelectLrc = arr;
  624. return;
  625. }
  626. if (type === "cell" && row >= 0 && column >= 0) {
  627. let lrcData = this.curQue.voiceMatrix.matrix[row][column].lrc_data;
  628. if (lrcData.end_time === -1) lrcData.end_time = this.mp3Duration;
  629. this.matrixSelectLrc = [lrcData];
  630. }
  631. },
  632. sentPause(isRecord) {
  633. this.isRecord = isRecord;
  634. },
  635. handleChangeTime({ begin_time, end_time }) {
  636. if (this.unWatch) this.unWatch();
  637. this.handleParentPlay();
  638. this.$nextTick(() => {
  639. this.$refs.audioLine.onTimeupdateTime(begin_time / 1000);
  640. this.$refs.audioLine.PlayAudio();
  641. // 监听是否已到结束时间,为了选中效果 - 0.01
  642. if (end_time === -1) return;
  643. let end = end_time / 1000 - 0.01;
  644. this.unWatch = this.$watch("curTime", val => {
  645. if (val >= end) {
  646. this.handleParentPlay();
  647. this.$refs.audioLine.onTimeupdateTime(end);
  648. this.unWatch();
  649. this.unWatch = null;
  650. }
  651. });
  652. });
  653. }
  654. }
  655. };
  656. </script>
  657. <style lang="scss" scoped>
  658. $select-color: #de4444;
  659. $border-color: #e6e6e6;
  660. $select-color-green: #24b99e;
  661. $select-color-green-bc: rgba(36, 185, 158, 0.25);
  662. $select-color-green-hover: #3dd4b8;
  663. $select-color-green-active: #1fa189;
  664. $select-color-brown: #bd8865;
  665. $select-color-brown-bc: rgba(189, 136, 101, 0.25);
  666. $select-color-brown-hover: #d6a687;
  667. $select-color-brown-active: #a37557;
  668. .voice-matrix {
  669. height: 100%;
  670. width: 100%;
  671. padding-bottom: 40px;
  672. color: #262626;
  673. &-audio {
  674. display: flex;
  675. height: 42px;
  676. border: 1px solid $border-color;
  677. border-radius: 8px 8px 0 0;
  678. .audio-number {
  679. padding: 11px 0 0 12px;
  680. %serial-number,
  681. .serial-number {
  682. display: inline-block;
  683. width: 16px;
  684. height: 16px;
  685. text-align: center;
  686. line-height: 16px;
  687. font-size: 12px;
  688. background-color: $select-color;
  689. font-family: "robot";
  690. color: #fff;
  691. border-radius: 50%;
  692. }
  693. .serial-number-green {
  694. @extend %serial-number;
  695. background-color: $select-color-green;
  696. }
  697. .serial-number-brown {
  698. @extend %serial-number;
  699. background-color: $select-color-brown;
  700. }
  701. }
  702. .audio-simple {
  703. flex-grow: 1;
  704. line-height: 46px;
  705. height: 100%;
  706. display: flex;
  707. align-items: center;
  708. justify-content: space-between;
  709. img {
  710. cursor: pointer;
  711. width: 16px;
  712. height: 16px;
  713. margin-left: 12px;
  714. }
  715. .Repeat-16 {
  716. display: inline-block;
  717. width: 16px;
  718. height: 16px;
  719. margin-right: 12px;
  720. cursor: pointer;
  721. }
  722. }
  723. }
  724. // 语音矩阵
  725. &-container {
  726. height: calc(100% - 80px);
  727. background-color: #f5f5f5;
  728. border-left: 1px solid $border-color;
  729. border-right: 1px solid $border-color;
  730. word-break: break-word;
  731. .matrix {
  732. display: inline-grid;
  733. width: 100%;
  734. height: 100%;
  735. %matrix-checkbox {
  736. position: relative;
  737. top: calc(50% - 5px);
  738. display: block;
  739. width: 14px;
  740. height: 14px;
  741. border: 1.5px solid #b0b0b0;
  742. border-radius: 4px;
  743. margin: 0 auto;
  744. cursor: pointer;
  745. &.active {
  746. border-color: $select-color;
  747. &::after {
  748. box-sizing: content-box;
  749. content: "";
  750. border: 1px solid $select-color;
  751. border-left: 0;
  752. border-top: 0;
  753. height: 7px;
  754. left: 4px;
  755. position: absolute;
  756. width: 3px;
  757. transform: rotate(45deg) scaleY(1);
  758. transition: transform 0.15s ease-in 0.05s;
  759. transform-origin: center;
  760. }
  761. }
  762. }
  763. .matrix-checkbox-row-,
  764. .matrix-checkbox-row-red {
  765. @extend %matrix-checkbox;
  766. }
  767. .matrix-checkbox-row-green {
  768. @extend %matrix-checkbox;
  769. &.active {
  770. border-color: $select-color-green-active;
  771. &::after {
  772. border-color: $select-color-green-active;
  773. }
  774. }
  775. }
  776. .matrix-checkbox-row-brown {
  777. @extend %matrix-checkbox;
  778. &.active {
  779. border-color: $select-color-brown-active;
  780. &::after {
  781. border-color: $select-color-brown-active;
  782. }
  783. }
  784. }
  785. %matrix-checkbox-column,
  786. .matrix-checkbox-column-,
  787. .matrix-checkbox-column-red {
  788. @extend %matrix-checkbox;
  789. top: calc(50% - 7px);
  790. right: -2px;
  791. }
  792. .matrix-checkbox-column-green {
  793. @extend %matrix-checkbox-column;
  794. &.active {
  795. border-color: $select-color-green-active;
  796. &::after {
  797. border-color: $select-color-green-active;
  798. }
  799. }
  800. }
  801. .matrix-checkbox-column-brown {
  802. @extend %matrix-checkbox-column;
  803. &.active {
  804. border-color: $select-color-brown-active;
  805. &::after {
  806. border-color: $select-color-brown-active;
  807. }
  808. }
  809. }
  810. .read {
  811. background-color: #eaeaea;
  812. }
  813. .highlight-,
  814. .highlight-red {
  815. color: $select-color;
  816. }
  817. .highlight-green {
  818. color: $select-color-green;
  819. }
  820. .highlight-brown {
  821. color: $select-color-brown;
  822. }
  823. .column-wrapper {
  824. padding: 4px;
  825. %column {
  826. width: 100%;
  827. height: 100%;
  828. min-height: 32px;
  829. background-color: #fff;
  830. border: 1px solid $border-color;
  831. border-radius: 8px;
  832. transition: 0.2s;
  833. cursor: pointer;
  834. user-select: none;
  835. &:hover {
  836. border-color: #8c8c8c;
  837. }
  838. &.selected {
  839. color: $select-color;
  840. border-color: $select-color;
  841. }
  842. &.playing {
  843. background-color: #fee;
  844. }
  845. &.title {
  846. background-color: transparent;
  847. border-color: transparent;
  848. }
  849. > span {
  850. display: inline-block;
  851. padding: 4px 12px;
  852. line-height: 24px;
  853. }
  854. }
  855. %column-red,
  856. .column-,
  857. .column-red {
  858. @extend %column;
  859. position: relative;
  860. font-family: "GB-PINYINOK-B", "FZJCGFKTK";
  861. &::before {
  862. display: inline-block;
  863. content: "";
  864. vertical-align: middle;
  865. }
  866. }
  867. .column-green {
  868. @extend %column-red;
  869. &.selected {
  870. color: $select-color-green;
  871. border-color: $select-color-green;
  872. }
  873. &.playing {
  874. background-color: $select-color-green-bc;
  875. }
  876. }
  877. .column-brown {
  878. @extend %column-red;
  879. &.selected {
  880. color: $select-color-brown;
  881. border-color: $select-color-brown;
  882. }
  883. &.playing {
  884. background-color: $select-color-brown-bc;
  885. }
  886. }
  887. %sentence,
  888. .sentence-,
  889. .sentence-red {
  890. @extend %column;
  891. display: inline-grid;
  892. padding: 4px 12px;
  893. line-height: 24px;
  894. column-gap: 8px;
  895. justify-items: center;
  896. justify-content: start;
  897. > span {
  898. padding: 0;
  899. }
  900. .pinyin {
  901. font-family: "GB-PINYINOK-B";
  902. opacity: 0.45;
  903. font-size: 12px;
  904. line-height: 20px;
  905. }
  906. .chs {
  907. font-family: "FZJCGFKTK";
  908. font-size: 16px;
  909. line-height: 24px;
  910. }
  911. }
  912. .sentence-green {
  913. @extend %sentence;
  914. &.selected {
  915. color: $select-color-green;
  916. border-color: $select-color-green;
  917. }
  918. &.playing {
  919. background-color: $select-color-green-hover;
  920. }
  921. }
  922. .sentence-brown {
  923. @extend %sentence;
  924. &.selected {
  925. color: $select-color-brown;
  926. border-color: $select-color-brown;
  927. }
  928. &.playing {
  929. background-color: $select-color-brown-hover;
  930. }
  931. }
  932. .connection {
  933. position: relative;
  934. top: calc(50% - 1px);
  935. height: 2px;
  936. width: 16px;
  937. margin: 0 -4px;
  938. border-radius: 4px;
  939. background-color: #252525;
  940. &.highlight-bc-,
  941. &.highlight-bc-red {
  942. background-color: $select-color;
  943. }
  944. &.highlight-bc-green {
  945. background-color: $select-color-green;
  946. }
  947. &.highlight-bc-brown {
  948. background-color: $select-color-brown;
  949. }
  950. }
  951. // 拼音 + 文字
  952. %pinyinEnglish,
  953. .pinyinEnglish-,
  954. .pinyinEnglish-red {
  955. @extend %column;
  956. .inside-wrapper {
  957. padding: 4px 12px;
  958. .pinyin {
  959. font-family: "GB-PINYINOK-B";
  960. font-size: 16px;
  961. line-height: 24px;
  962. }
  963. .english {
  964. font-family: "robot";
  965. opacity: 0.45;
  966. font-size: 12px;
  967. line-height: 20px;
  968. }
  969. }
  970. }
  971. .pinyinEnglish-green {
  972. @extend %pinyinEnglish;
  973. &.selected {
  974. color: $select-color-green;
  975. border-color: $select-color-green;
  976. }
  977. &.playing {
  978. background-color: $select-color-green-hover;
  979. }
  980. }
  981. .pinyinEnglish-brown {
  982. @extend %pinyinEnglish;
  983. &.selected {
  984. color: $select-color-brown;
  985. border-color: $select-color-brown;
  986. }
  987. &.playing {
  988. background-color: $select-color-brown-hover;
  989. }
  990. }
  991. }
  992. }
  993. .matrix-audio {
  994. width: 228px;
  995. height: 40px;
  996. padding: 4px 4px 4px 16px;
  997. margin: 24px 24px 0 0;
  998. background-color: #fff;
  999. border: 1px solid $border-color;
  1000. border-radius: 8px;
  1001. }
  1002. }
  1003. .voice-luyin {
  1004. display: flex;
  1005. border: 1px solid $border-color;
  1006. border-radius: 0 0 8px 8px;
  1007. align-items: center;
  1008. padding: 3px 16px;
  1009. height: 40px;
  1010. }
  1011. }
  1012. </style>
  1013. <style lang="scss" scoped>
  1014. .NNPE-tableList-tr-last {
  1015. .voice-matrix {
  1016. padding-bottom: 0;
  1017. }
  1018. }
  1019. </style>
  1020. <style lang="scss">
  1021. .voice-matrix {
  1022. &-audio {
  1023. .audioLine {
  1024. border-radius: 8px 8px 0 0 !important;
  1025. }
  1026. .el-slider {
  1027. width: 100% !important;
  1028. }
  1029. }
  1030. .luyin-box {
  1031. .el-select .el-input {
  1032. width: 136px;
  1033. }
  1034. }
  1035. }
  1036. </style>