| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- <template>
- <ModuleBase :type="data.type">
- <template #content>
- <!-- eslint-disable max-len -->
- <div v-loading="loading" class="article-wrapper">
- <el-input v-model="data.content" placeholder="输入" type="textarea" />
- <SelectUpload label="课文音频" type="audio" width="500px" @uploadSuccess="uploadAudioSuccess" />
- <div v-if="data.mp3_list.length > 0" class="upload-file">
- <div class="file-name">
- <span>
- <SvgIcon icon-class="note" size="12" />
- <span>{{ data.mp3_list[0].name }}</span>
- </span>
- </div>
- <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
- </div>
- <div v-if="data.content" class="btn-box">
- <a @click="handleChangeContent">生成分词</a>
- <a @click="checkArticle">文章校对</a>
- <a @click="picArticle">添加图片</a>
- <a @click="editWordsFlag = !editWordsFlag">编辑生词短语注释</a>
- <template v-if="data.wordTime && data.wordTime.length > 0">
- <!-- <span>已有字幕时间节点</span> -->
- <a type="text" @click="againWordTime">重新生成字幕时间</a>
- <a size="medium" @click="compareTime('句子')">校对句子字幕时间</a>
- <a size="medium" @click="compareTime('文字')">校对文字字幕时间</a>
- </template>
- <template v-else>
- <a v-if="!isWordTime" size="medium" @click="createWordTimes">自动生成字幕节点</a>
- <p v-else>字幕节点生成中...请等待</p>
- </template>
- <el-button @click="handleMultilingual">课文多语言</el-button>
- <MultilingualFill
- :visible.sync="multilingualVisible"
- :text="multilingualText"
- :translations="data.multilingual"
- @SubmitTranslation="handleMultilingualTranslation"
- />
- </div>
- </div>
- <el-dialog
- v-if="showArticleFlag"
- :visible.sync="showArticleFlag"
- :show-close="true"
- :close-on-click-modal="true"
- :modal-append-to-body="true"
- :append-to-body="true"
- :lock-scroll="true"
- width="80%"
- class="practiceBox"
- >
- <CheckArticle :data="data" @saveWord="saveWord" @savePinyin="savePinyin" @saveStyle="saveStyle" />
- </el-dialog>
- <el-dialog title="校对字幕时间" :visible.sync="compareShow" width="50%" :before-close="handleClose" top="0">
- <CompareTime :data="compareData" :type="compareType" :changewords-result-list="changewordsResultList" />
- <span slot="footer" class="dialog-footer">
- <el-button @click="handleClose">取 消</el-button>
- <el-button :loading="compareloading" type="primary" @click="saveCompare">确 定</el-button>
- </span>
- </el-dialog>
- <!-- <el-dialog title="" :visible.sync="editWordsFlag" width="80%" :close-on-click-modal="true" top="0"> -->
- <template v-if="editWordsFlag">
- <div class="tabs-box">
- <a :class="[editWordIndex === 0 ? 'active' : '']" @click="editWordIndex = 0">生词</a>
- <a :class="[editWordIndex === 2 ? 'active' : '']" @click="editWordIndex = 2">其他词汇</a>
- <a :class="[editWordIndex === 1 ? 'active' : '']" @click="editWordIndex = 1">注释</a>
- </div>
- <NewWord
- v-if="editWordIndex === 0"
- key="new_word"
- :data-new-word="data.new_word_list"
- @sureNewWords="sureNewWords"
- />
- <Notes v-if="editWordIndex === 1" key="notes" :data-notes="data.notes_list" @sureNotes="sureNotes" />
- <NewWord v-if="editWordIndex === 2" :data-new-word="data.other_word_list" @sureNewWords="sureOtherNewWords" />
- </template>
- <!-- </el-dialog> -->
- <!-- 添加图片 -->
- <el-dialog
- v-if="showPicArticleFlag"
- :visible.sync="showPicArticleFlag"
- :show-close="true"
- :close-on-click-modal="true"
- :modal-append-to-body="true"
- :append-to-body="true"
- :lock-scroll="true"
- width="80%"
- class="practiceBox"
- >
- <CheckPic :data="data" />
- </el-dialog>
- </template>
- </ModuleBase>
- </template>
- <script>
- import ModuleMixin from '../../common/ModuleMixin';
- import SelectUpload from '@/views/book/courseware/create/components/common/SelectUpload.vue';
- import CheckArticle from './CheckArticle.vue';
- import CompareTime from './CompareTime.vue';
- import NewWord from './NewWord.vue';
- import Notes from './Notes.vue';
- import CheckPic from './CheckPic.vue';
- import { getArticleData } from '@/views/book/courseware/data/article';
- import {
- segSentences,
- BatchSegContent,
- getWordTime,
- prepareTranscribe,
- fileToBase64Text,
- getWordTimes,
- } from '@/api/article';
- const Base64 = require('js-base64').Base64;
- import cnchar from 'cnchar';
- export default {
- name: 'ArticlePage',
- components: {
- SelectUpload,
- CheckArticle,
- CompareTime,
- NewWord,
- Notes,
- CheckPic,
- },
- mixins: [ModuleMixin],
- data() {
- return {
- data: getArticleData(),
- showArticleFlag: false, // 校对文章
- showPicArticleFlag: false, // 添加图片
- toneList: [' ', 'ˉ', 'ˊ', 'ˇ', 'ˋ'],
- loading: false,
- isWordTime: false,
- compareType: '', // 校对类型
- compareShow: false,
- compareData: null,
- compareloading: false,
- editWordsFlag: false,
- editWordIndex: 0,
- multilingualText: '',
- };
- },
- watch: {
- 'data.content': 'handleMindMap',
- },
- created() {},
- methods: {
- // 解析输入内容
- handleChangeContent() {
- this.loading = true;
- this.data.detail = [];
- if (this.data.content.trim()) {
- let contentArr = this.data.content.split('\n');
- let textList = [];
- let detailItem = {
- paraIndex: 0,
- para: '',
- sentences: [],
- segList: [],
- seg_words: [],
- wordsList: [],
- timeList: [],
- isTitle: false,
- sentencesEn: [],
- paraAlign: 'left',
- remark: {
- chs: '',
- en: '',
- heightNumber: null,
- img_list: [],
- widthNumber: null,
- },
- sourceList: [],
- sourcePosition: 'after',
- heightNumber: null,
- widthNumber: null,
- };
- // 分段
- contentArr.forEach((item, index) => {
- if (item.trim()) {
- detailItem.para = item;
- detailItem.paraIndex = index;
- this.data.detail.push(JSON.parse(JSON.stringify(detailItem)));
- let str = Base64.encode(item);
- textList.push(str);
- }
- });
- // 分句
- let sentenceList = []; // 句子按段数组
- let data = {
- textList,
- };
- segSentences(data)
- .then((res) => {
- let result = res.data.result;
- sentenceList = JSON.parse(JSON.stringify(res.data.result));
- result.forEach((item, index) => {
- let sentenceListBase64 = [];
- this.data.detail[index].sentences = item;
- item.forEach((items) => {
- sentenceListBase64.push(Base64.encode(items));
- });
- // 分词
- BatchSegContent({ textList: sentenceListBase64 })
- .then((res) => {
- let list = res.data.result.list;
- this.data.detail[index].segList = list;
- this.setWordsList(list, index);
- if (index === result.length - 1) {
- this.handleSenWord();
- this.loading = false;
- }
- })
- .catch(() => {
- this.loading = false;
- });
- });
- })
- .catch(() => {
- this.loading = false;
- });
- }
- },
- // 处理句子和词的关系
- handleSenWord() {
- this.data.sentence_list_mp = [];
- this.data.detail.forEach((item) => {
- item.sentences.forEach((items, indexs) => {
- let word_list = [];
- item.segList[indexs].forEach((itemw) => {
- word_list.push({
- word: itemw,
- });
- });
- let obj = {
- sentence: items,
- word_list,
- };
- this.data.sentence_list_mp.push(obj);
- });
- });
- },
- setWordsList(list, paraIndex) {
- let wordsList = [];
- list.forEach((item, index) => {
- let sentArr = [];
- item.map((sItem) => {
- let toneStr = [];
- for (let i = 0; i < sItem.length; i++) {
- const pattern = /[\u4e00-\u9fa5]/;
- if (cnchar.isCnChar(sItem[i]) && !pattern.test(cnchar.spell(sItem[i], 'low', 'tone'))) {
- toneStr.push(this.toneList[cnchar.spellInfo(cnchar.spell(sItem[i], 'low', 'tone')).tone]);
- } else {
- toneStr.push(' ');
- }
- }
- let obj = {
- chs: sItem,
- pinyin: cnchar.spell(sItem, 'low', 'tone'),
- pinyin_up:
- cnchar.spell(sItem, 'low', 'tone').charAt(0).toUpperCase() + cnchar.spell(sItem, 'low', 'tone').slice(1),
- pinyin_tone: toneStr.join(' '),
- fontFamily: '楷体',
- textDecoration: '',
- fontWeight: '',
- border: '',
- color: '',
- matchWords: '',
- matchNotes: '',
- img: [],
- imgPosition: 'after',
- };
- sentArr.push(obj);
- });
- wordsList.push(sentArr);
- });
- this.data.detail[paraIndex].wordsList = wordsList;
- },
- uploadAudioSuccess(fileList) {
- if (fileList.length > 0) {
- const { file_name: name, file_url: temporary_url, file_id, media_duration } = fileList[0];
- this.data.mp3_list = [
- {
- name,
- media_duration,
- temporary_url,
- url: file_id,
- file_id,
- },
- ];
- this.data.file_id_list = [file_id];
- }
- },
- removeFile() {
- this.data.mp3_list = [];
- this.data.file_id_list = [];
- },
- // 校对文章
- checkArticle() {
- let verseList = [];
- this.data.detail.forEach((item) => {
- verseList = verseList.concat(item.sentences);
- });
- if (verseList.length > 0) {
- this.showArticleFlag = true;
- } else {
- this.$message.warning('请先生成分词');
- }
- },
- saveWord(saveArr) {
- saveArr.forEach((item, index) => {
- let para = '';
- let sentenceStr = [];
- let sentences = [];
- let wordsList = [];
- item.forEach((items) => {
- para += items.join('');
- sentenceStr.push(items.join(' '));
- sentences.push(items.join(''));
- let sentArr = [];
- items.forEach((sItem) => {
- let toneStr = [];
- for (let i = 0; i < sItem.length; i++) {
- const pattern = /[\u4e00-\u9fa5]/;
- if (cnchar.isCnChar(sItem[i]) && !pattern.test(cnchar.spell(sItem[i], 'low', 'tone'))) {
- toneStr.push(this.toneList[cnchar.spellInfo(cnchar.spell(sItem[i], 'low', 'tone')).tone]);
- } else {
- toneStr.push(' ');
- }
- }
- let obj = {
- chs: sItem,
- pinyin: cnchar.spell(sItem, 'low', 'tone'),
- pinyin_up:
- cnchar.spell(sItem, 'low', 'tone').charAt(0).toUpperCase() +
- cnchar.spell(sItem, 'low', 'tone').slice(1),
- pinyin_tone: toneStr.join(' '),
- fontFamily: '楷体',
- textDecoration: '',
- fontWeight: '',
- border: '',
- color: '',
- matchWords: '',
- matchNotes: '',
- img: [],
- imgPosition: 'after',
- };
- sentArr.push(obj);
- });
- wordsList.push(sentArr);
- });
- if (this.data.detail[index]) {
- this.data.detail[index].segList = item;
- this.data.detail[index].para = para;
- this.data.detail[index].sentenceStr = sentenceStr;
- this.data.detail[index].sentences = sentences;
- this.data.detail[index].wordsList = wordsList;
- } else {
- let obj = {
- paraIndex: index,
- para,
- sentences,
- segList: item,
- seg_words: [],
- wordsList,
- timeList: [],
- isTitle: false,
- sentencesEn: [],
- sentenceStr,
- paraAlign: 'left',
- remark: {
- chs: '',
- en: '',
- heightNumber: null,
- img_list: [],
- widthNumber: null,
- },
- sourceList: [],
- sourcePosition: 'after',
- heightNumber: null,
- widthNumber: null,
- };
- this.data.detail.push(obj);
- }
- });
- this.$message.success('保存成功,请校对拼音');
- this.handleSenWord();
- },
- // 保存拼音
- savePinyin(paraIndex, sentenceIndex, wordIndex, pinyin) {
- if (this.data.pinyin_type === 'tone') {
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].pinyin_tone = pinyin;
- } else if (wordIndex === 0) {
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].pinyin_up = pinyin;
- } else {
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].pinyin = pinyin;
- }
- },
- saveStyle(
- paraIndex,
- sentenceIndex,
- wordIndex,
- fontFamily,
- textDecoration,
- fontWeight,
- border,
- color,
- matchWords,
- matchNotes,
- img,
- imgPosition,
- ) {
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].fontFamily = fontFamily;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].textDecoration = textDecoration;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].fontWeight = fontWeight;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].border = border;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].color = color;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].matchWords = matchWords;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].matchNotes = matchNotes;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].img = img;
- this.data.detail[paraIndex].wordsList[sentenceIndex][wordIndex].imgPosition = imgPosition;
- },
- // 保存校对
- saveCompare() {
- this.compareloading = false;
- this.data.wordTime = this.compareData;
- this.handleClose();
- // this.compareloading = true;
- // compareSenTenceTime({ matchList: JSON.stringify(this.compareData) })
- // .then((res) => {
- // this.compareloading = false;
- // this.data.wordTime = res.data.result;
- // })
- // .catch(() => {
- // this.compareloading = false;
- // });
- },
- // 校对时间
- compareTime(type) {
- this.compareType = type;
- this.compareData = JSON.parse(JSON.stringify(this.data.wordTime));
- this.compareShow = true;
- },
- handleClose() {
- this.compareShow = false;
- this.compareData = null;
- this.compareType = '';
- },
- // 校对每个字的时间
- changewordsResultList(index, item) {
- this.data.wordTime[index].wordsResultList = JSON.parse(JSON.stringify(item));
- },
- againWordTime() {
- this.isWordTime = false;
- this.data.wordTime = [];
- },
- createWordTimes() {
- let _this = this;
- let verseList = [];
- if (_this.data.mp3_list && _this.data.mp3_list.length > 0 && _this.data.mp3_list[0].file_id) {
- if (_this.data.content) {
- _this.data.detail.forEach((item) => {
- verseList = verseList.concat(item.sentences);
- });
- if (verseList.length > 0) {
- _this.isWordTime = true;
- let data = {
- audio_file_id: _this.data.mp3_list[0].file_id,
- text: _this.data.content,
- text_type: 'line_text_list',
- line_text_list: verseList,
- };
- getWordTimes(data).then((res) => {
- _this.data.wordTime = res.data.result;
- _this.isWordTime = false;
- });
- } else {
- this.$message.warning('请先生成分词');
- }
- }
- } else {
- _this.$message.warning('请先上传音频');
- _this.loading = false;
- }
- },
- createWordTime() {
- let _this = this;
- this.getfillLiu().then(() => {
- if (_this.data.taskId) {
- let verseList = [];
- _this.data.detail.forEach((item) => {
- verseList = verseList.concat(item.sentences);
- });
- if (verseList.length > 0) {
- _this.isWordTime = true;
- let data = {
- taskId: _this.data.taskId,
- verseList: JSON.stringify(verseList),
- matchType: 'chinese',
- language: 'ch',
- };
- getWordTime(data).then((res) => {
- _this.data.wordTime = res.data.result;
- _this.isWordTime = false;
- });
- }
- } else {
- _this.$message.warning('请先上传音频');
- _this.loading = false;
- }
- });
- },
- // 得到文件流
- getfillLiu() {
- this.loading = true;
- let _this = this;
- return new Promise(function (resolve, reject) {
- if (_this.data.mp3_list && _this.data.mp3_list.length > 0 && _this.data.mp3_list[0].file_id) {
- let id = _this.data.mp3_list[0].file_id;
- let data = {
- file_id: id,
- };
- fileToBase64Text(data).then((res) => {
- let taskIddata = {
- fileName: _this.data.mp3_list[0].name,
- speechBase64: res.base64_text,
- language: 'ch',
- };
- prepareTranscribe(taskIddata)
- .then((ress) => {
- _this.$set(_this.data, 'taskId', ress.data.taskId);
- _this.loading = false;
- resolve();
- })
- .catch(() => {
- _this.loading = false;
- });
- });
- } else {
- _this.$message.warning('请先上传音频');
- _this.loading = false;
- }
- });
- },
- sureNewWords(data) {
- this.data.new_word_list = data;
- },
- sureOtherNewWords(data) {
- this.data.other_word_list = data;
- },
- sureNotes(data) {
- this.data.notes_list = data;
- },
- handleMindMap() {
- // 思维导图数据
- let node_list = [];
- node_list.push({
- name: this.data.content,
- id: Math.random().toString(36).substring(2, 12),
- });
- this.data.mind_map.node_list = node_list;
- },
- handleMultilingual() {
- this.multilingualText = '';
- let flag = false;
- this.data.detail.forEach((item) => {
- if (item.para) {
- if (item.sentences && item.sentences.length > 0) {
- flag = true;
- }
- item.sentences.forEach((items) => {
- this.multilingualText += `<p>${items}<p>`;
- });
- } else {
- this.multilingualText += '<p> </p>';
- }
- // this.multilingualText += item.para ? '<p>' + item.para + '<p>' : '<p> </p>';
- });
- if (flag) {
- this.multilingualVisible = true;
- } else {
- this.$message.warning('请先生成分词');
- }
- },
- // 点击插入图片按钮
- picArticle() {
- let verseList = [];
- this.data.detail.forEach((item) => {
- verseList = verseList.concat(item.sentences);
- });
- if (verseList.length > 0) {
- this.showPicArticleFlag = true;
- } else {
- this.$message.warning('请先生成分词');
- }
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .article-wrapper {
- display: flex;
- flex-direction: column;
- row-gap: 16px;
- align-items: flex-start;
- }
- .upload-file {
- display: flex;
- column-gap: 12px;
- align-items: center;
- margin: 8px 0;
- .file-name {
- display: flex;
- column-gap: 14px;
- align-items: center;
- justify-content: space-between;
- max-width: 360px;
- padding: 8px 12px;
- font-size: 14px;
- color: #1d2129;
- background-color: #f7f8fa;
- span {
- display: flex;
- column-gap: 14px;
- align-items: center;
- }
- }
- .svg-icon {
- cursor: pointer;
- }
- }
- .btn-box {
- display: flex;
- flex-flow: wrap;
- gap: 16px;
- a {
- padding: 5px 16px;
- font-size: 14px;
- line-height: 22px;
- color: #4e5969;
- cursor: pointer;
- background: #f2f3f5;
- border-radius: 2px;
- }
- p {
- margin: 0;
- }
- }
- .tabs-box {
- display: flex;
- column-gap: 12px;
- margin: 12px 0;
- a {
- padding: 5px 16px;
- font-size: 14px;
- font-weight: 400;
- line-height: 22px; /* 157.143% */
- color: #4e5969;
- cursor: pointer;
- border-radius: 100px;
- &.active {
- color: #165dff;
- background: #f2f3f5;
- }
- }
- }
- </style>
|