VoiceMatrix.vue 27 KB

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