CheckStyle.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. <template>
  2. <div class="check-article">
  3. <div class="main">
  4. <div class="main-top">
  5. <div style="display: flex">
  6. <b>校对样式</b>
  7. </div>
  8. <!-- <div class="btn-box">
  9. <el-button type="primary" @click="savePinyin">保存</el-button>
  10. </div> -->
  11. </div>
  12. <div class="article">
  13. <div v-for="(item, index) in indexArr" :key="index + 'paragraph'" class="paragraph">
  14. <div v-for="(items, indexs) in item" :key="indexs + 'words'" class="sentence-box">
  15. <div
  16. class="sentence"
  17. :style="{
  18. marginRight: items.marginRight ? '8px' : '',
  19. color: activeIndex === index + '_' + items.sentenceIndex + '_' + items.wordIndex ? '#F2555A' : '',
  20. }"
  21. @click="selectItem(items, index)"
  22. >
  23. <span
  24. class="words"
  25. :class="[/^[0-9]*$/.test(items.text)] ? (/^[\u4e00-\u9fa5]/.test(items.text) ? 'hanzi' : 'en') : ''"
  26. :style="{
  27. fontFamily: items.fontFamily,
  28. color: items.color,
  29. textDecoration: items.textDecoration,
  30. borderBottom: items.border === 'dotted' ? '1px dotted' : '',
  31. fontWeight: items.fontWeight,
  32. }"
  33. >
  34. {{ items.text }}
  35. </span>
  36. </div>
  37. </div>
  38. </div>
  39. </div>
  40. </div>
  41. <el-dialog
  42. v-if="dialogFlag"
  43. :visible.sync="dialogFlag"
  44. :show-close="false"
  45. :close-on-click-modal="false"
  46. :modal-append-to-body="false"
  47. :modal="false"
  48. width="400px"
  49. class="login-dialog"
  50. >
  51. <div class="check-box">
  52. <div class="content">
  53. <div class="words-box">
  54. <span
  55. class="words"
  56. :style="{
  57. fontFamily: itemActive.fontFamily,
  58. color: itemActive.color,
  59. textDecoration: itemActive.textDecoration,
  60. borderBottom: itemActive.border === 'dotted' ? '1px dotted' : '',
  61. fontWeight: itemActive.fontWeight,
  62. }"
  63. >
  64. {{ itemActive.text }}
  65. </span>
  66. </div>
  67. </div>
  68. <div ref="toolbarMenu" class="toolbar">
  69. <el-select v-model="itemActive.fontFamily" placeholder="请选择">
  70. <el-option v-for="item in fontFamilyList" :key="item.value" :label="item.name" :value="item.value" />
  71. </el-select>
  72. <span class="picker-area" :class="['tool-item', itemActive.color ? 'active' : '']">
  73. <el-color-picker
  74. ref="colorPicker"
  75. v-model="itemActive.color"
  76. @click="itemActive.color = itemActive.color"
  77. />
  78. <SvgIcon icon-class="fontcolor" title="字体颜色" size="18" />
  79. </span>
  80. <span :class="['tool-item', itemActive.textDecoration === 'underline' ? 'active' : '']">
  81. <SvgIcon icon-class="underline" title="下划线" size="18" @click="setActiveTextStyle('underline')" />
  82. </span>
  83. <span :class="['tool-item', itemActive.fontWeight === 'bold' ? 'active' : '']">
  84. <SvgIcon icon-class="bold" title="加粗" size="20" @click="setActiveTextStyle('bold')" />
  85. </span>
  86. <span :class="['tool-item', itemActive.textDecoration === 'line-through' ? 'active' : '']">
  87. <SvgIcon icon-class="strikethrough" title="删除线" size="20" @click="setActiveTextStyle('line-through')" />
  88. </span>
  89. <span :class="['tool-item', itemActive.border === 'dotted' ? 'active' : '']">
  90. <SvgIcon icon-class="borderDotted" title="下划虚线" size="16" @click="setActiveTextStyle('border')" />
  91. </span>
  92. </div>
  93. <div class="match-info">
  94. <label>关联生词:</label>
  95. <el-input
  96. v-model="itemActive.matchWords"
  97. placeholder="请输入生词表的“生词/短语”"
  98. @blur="onBlur(itemActive, 'matchWords')"
  99. ></el-input>
  100. </div>
  101. <div class="match-info">
  102. <label>关联注释:</label>
  103. <el-input
  104. v-model="itemActive.matchNotes"
  105. placeholder="请输入注释表的“内容”"
  106. @blur="onBlur(itemActive, 'matchNotes')"
  107. ></el-input>
  108. <el-color-picker v-model="itemActive.notesColor"></el-color-picker>
  109. </div>
  110. <div class="btn-box">
  111. <el-button type="info" size="small" @click="cancleDialog">取消</el-button>
  112. <el-button type="primary" size="small" @click="surePinyin">保存</el-button>
  113. </div>
  114. </div>
  115. </el-dialog>
  116. </div>
  117. </template>
  118. <script>
  119. // import { publicMethods, reparse } from '@/api/api';
  120. import { fileUpload } from '@/api/app';
  121. export default {
  122. components: {},
  123. props: ['data'],
  124. data() {
  125. return {
  126. loading: false,
  127. id: '',
  128. ArticelData: null,
  129. indexArr: [], // 索引数组
  130. activeIndex: null,
  131. itemActive: null,
  132. dialogFlag: false,
  133. checkPinyinInput: '',
  134. oldInput: '',
  135. toneList: [' ', 'ˉ', 'ˊ', 'ˇ', 'ˋ'],
  136. fontFamilyList: [
  137. {
  138. value: '楷体, 微软雅黑',
  139. name: '楷体',
  140. },
  141. {
  142. value: '黑体, 微软雅黑',
  143. name: '黑体',
  144. },
  145. {
  146. value: '宋体, 微软雅黑',
  147. name: '宋体',
  148. },
  149. {
  150. value: 'arial, helvetica, sans-serif',
  151. name: 'Arial',
  152. },
  153. {
  154. value: 'times new roman, times, serif',
  155. name: 'Times New Roman',
  156. },
  157. {
  158. value: 'League',
  159. name: '拼音',
  160. },
  161. // {
  162. // value: 'Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif, alabo',
  163. // name: '系统字体',
  164. // },
  165. ],
  166. };
  167. },
  168. // 生命周期 - 创建完成(可以访问当前this实例)
  169. created() {
  170. this.getArticleData();
  171. },
  172. methods: {
  173. onBlur(item, field) {
  174. item[field] = item[field] ? item[field].trim() : '';
  175. },
  176. // 获取分析结果
  177. getArticleData() {
  178. this.loading = true;
  179. let newdata = [];
  180. this.data.detail.forEach((item) => {
  181. if (item.wordsList.length !== 0 || (item.noticeWordList && item.noticeWordList.length !== 0)) {
  182. newdata.push(item);
  183. }
  184. });
  185. // this.ArticelData = JSON.parse(JSON.stringify(newdata));
  186. let saveArr = [];
  187. let arr = [];
  188. let saveIndex = 0;
  189. // 添加索引
  190. newdata.forEach((item, index) => {
  191. arr.push([]);
  192. if (item.wordsList.length > 0) {
  193. item.wordsList.forEach((items, indexs) => {
  194. items.forEach((itemss, indexss) => {
  195. let obj = {
  196. text: itemss.chs,
  197. paraIndex: item.paraIndex,
  198. sentenceIndex: indexs,
  199. wordIndex: indexss,
  200. marginRight: true,
  201. saveIndex,
  202. fontFamily: itemss.fontFamily,
  203. textDecoration: itemss.textDecoration,
  204. fontWeight: itemss.fontWeight,
  205. border: itemss.border,
  206. color: itemss.color,
  207. matchWords: itemss.matchWords,
  208. matchNotes: itemss.matchNotes,
  209. notesColor: itemss.notesColor,
  210. img: itemss.img,
  211. imgPosition: itemss.imgPosition,
  212. };
  213. arr[index].push(obj);
  214. let saveObj = {
  215. word: itemss.chs,
  216. // pinyin_lt: itemss.pinyin_lt?itemss.pinyin_lt:''
  217. };
  218. saveArr.push(saveObj);
  219. saveIndex++;
  220. });
  221. });
  222. } else {
  223. item.noticeWordList.forEach((items, indexs) => {
  224. items.forEach((itemss, indexss) => {
  225. let obj = {
  226. text: itemss.chs,
  227. paraIndex: item.paraIndex,
  228. sentenceIndex: indexs,
  229. wordIndex: indexss,
  230. marginRight: true,
  231. saveIndex,
  232. fontFamily: itemss.fontFamily,
  233. textDecoration: itemss.textDecoration,
  234. fontWeight: itemss.fontWeight,
  235. border: itemss.border,
  236. color: itemss.color,
  237. matchWords: itemss.matchWords,
  238. matchNotes: itemss.matchNotes,
  239. notesColor: itemss.notesColor,
  240. img: itemss.img,
  241. imgPosition: itemss.imgPosition,
  242. };
  243. arr[index].push(obj);
  244. let saveObj = {
  245. word: itemss.chs,
  246. // pinyin_lt: itemss.pinyin_lt?itemss.pinyin_lt:''
  247. };
  248. saveArr.push(saveObj);
  249. saveIndex++;
  250. });
  251. });
  252. }
  253. });
  254. this.indexArr = arr;
  255. this.ArticelData = saveArr;
  256. this.loading = false;
  257. },
  258. selectItem(item, index) {
  259. this.activeIndex = `${index}_${item.sentenceIndex}_${item.wordIndex}`;
  260. this.itemActive = item;
  261. this.dialogFlag = true;
  262. this.checkPinyinInput = '';
  263. },
  264. cancleDialog() {
  265. this.activeIndex = null;
  266. this.itemActive = null;
  267. this.checkPinyinInput = '';
  268. this.oldInput = '';
  269. this.dialogFlag = false;
  270. },
  271. surePinyin() {
  272. this.saveStyle();
  273. this.dialogFlag = false;
  274. },
  275. saveStyle() {
  276. this.$emit(
  277. 'saveStyle',
  278. this.itemActive.paraIndex,
  279. this.itemActive.sentenceIndex,
  280. this.itemActive.wordIndex,
  281. this.itemActive.fontFamily,
  282. this.itemActive.textDecoration,
  283. this.itemActive.fontWeight,
  284. this.itemActive.border,
  285. this.itemActive.color,
  286. this.itemActive.matchWords,
  287. this.itemActive.matchNotes,
  288. this.itemActive.notesColor,
  289. this.itemActive.img,
  290. this.itemActive.imgPosition,
  291. );
  292. this.$message.success('保存成功');
  293. this.activeIndex = null;
  294. // this.itemActive = null;
  295. this.dialogFlag = false;
  296. },
  297. // 设置样式
  298. setActiveTextStyle(type) {
  299. let style = this.itemActive;
  300. if (type === 'line-through') {
  301. style.textDecoration = style.textDecoration === 'line-through' ? '' : 'line-through';
  302. this.$set(this.itemActive, 'textDecoration', style.textDecoration);
  303. } else if (type === 'bold') {
  304. style.fontWeight = style.fontWeight === 'bold' ? '' : 'bold';
  305. this.$set(this.itemActive, 'fontWeight', style.fontWeight);
  306. } else if (type === 'underline') {
  307. style.textDecoration = style.textDecoration === 'underline' ? '' : 'underline';
  308. this.$set(this.itemActive, 'textDecoration', style.textDecoration);
  309. } else if (type === 'border') {
  310. style.border = style.border === 'dotted' ? '' : 'dotted';
  311. this.$set(this.itemActive, 'border', style.border);
  312. }
  313. },
  314. // 处理超出图片个数操作
  315. handleExceed(files, fileList) {
  316. this.$message.warning(
  317. `当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`,
  318. );
  319. },
  320. // 图片上传前处理
  321. handleBeforeImage(file) {
  322. // 判断文件是否为图片
  323. if (!file.type.includes('image')) {
  324. this.$message.error('请选择图片文件');
  325. return false;
  326. }
  327. },
  328. // 图片上传
  329. handleImage(file) {
  330. fileUpload('Mid', file, { isGlobalprogress: true }).then(({ file_info_list }) => {
  331. if (file_info_list.length > 0) {
  332. this.itemActive.img = file_info_list;
  333. this.itemActive.img[0].name = file_info_list[0].file_name;
  334. }
  335. });
  336. },
  337. },
  338. };
  339. </script>
  340. <style lang="scss" scoped>
  341. .check-article {
  342. min-height: 100%;
  343. background: #f6f6f6;
  344. .wheader {
  345. background: #fff;
  346. }
  347. .el-button {
  348. padding: 5px 16px;
  349. font-size: 14px;
  350. font-weight: 400;
  351. line-height: 22px;
  352. color: #4e5969;
  353. background: #f2f3f5;
  354. border: 1px solid #f2f3f5;
  355. border-radius: 2px;
  356. &.el-button--primary {
  357. color: #fff;
  358. background: #165dff;
  359. border: 1px solid #165dff;
  360. }
  361. }
  362. .main {
  363. background: #fff;
  364. &-top {
  365. position: relative;
  366. display: flex;
  367. align-items: center;
  368. justify-content: space-between;
  369. margin-bottom: 24px;
  370. b {
  371. margin-left: 16px;
  372. font-size: 24px;
  373. font-weight: 500;
  374. line-height: 34px;
  375. color: #000;
  376. }
  377. }
  378. .go-back {
  379. display: flex;
  380. align-items: center;
  381. width: 60px;
  382. padding: 5px 8px;
  383. font-size: 14px;
  384. font-weight: 400;
  385. line-height: 22px;
  386. color: #333;
  387. cursor: pointer;
  388. background: #fff;
  389. border: 1px solid #d9d9d9;
  390. border-radius: 4px;
  391. box-shadow: 0 2px 0 0 rgba(0, 0, 0, 2%);
  392. .el-icon-arrow-left {
  393. margin-right: 8px;
  394. font-size: 16px;
  395. }
  396. }
  397. .article {
  398. padding: 8px;
  399. background: #fcfcfc;
  400. border: 1px solid rgba(0, 0, 0, 8%);
  401. border-radius: 2px;
  402. }
  403. .paragraph {
  404. display: flex;
  405. flex-flow: wrap;
  406. width: 100%;
  407. margin-bottom: 24px;
  408. text-align: center;
  409. .sentence-box {
  410. display: flex;
  411. flex-flow: wrap;
  412. float: left;
  413. .sentence {
  414. margin-top: 8px;
  415. color: #000;
  416. text-align: center;
  417. cursor: pointer;
  418. .words {
  419. display: block;
  420. font-size: 20px;
  421. font-weight: 400;
  422. line-height: 28px;
  423. }
  424. .pinyin {
  425. display: block;
  426. height: 14px;
  427. font-family: 'League';
  428. font-size: 14px;
  429. font-weight: 400;
  430. line-height: 1;
  431. }
  432. }
  433. }
  434. }
  435. }
  436. }
  437. .check-box {
  438. padding: 24px;
  439. background: #fff;
  440. border-radius: 4px;
  441. box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 25%);
  442. .content {
  443. display: flex;
  444. align-items: center;
  445. justify-content: center;
  446. .words-box {
  447. color: #000;
  448. text-align: center;
  449. .words {
  450. display: block;
  451. font-size: 28px;
  452. font-weight: 400;
  453. line-height: 40px;
  454. }
  455. .pinyin {
  456. display: block;
  457. height: 20px;
  458. font-family: 'League';
  459. font-size: 20px;
  460. font-weight: 400;
  461. line-height: 1;
  462. }
  463. }
  464. }
  465. .checkPinyinInput {
  466. margin: 16px 0 8px;
  467. }
  468. .tips {
  469. margin: 0 0 24px;
  470. font-size: 12px;
  471. font-weight: 400;
  472. line-height: 18px;
  473. color: #a0a0a0;
  474. }
  475. .btn-box {
  476. text-align: right;
  477. .el-button {
  478. padding: 2px 12px;
  479. font-size: 12px;
  480. line-height: 20px;
  481. }
  482. }
  483. }
  484. .toolbar {
  485. display: flex;
  486. column-gap: 10px;
  487. align-items: center;
  488. margin: 10px 0;
  489. cursor: pointer;
  490. .picker-area {
  491. position: relative;
  492. :deep .el-color-picker {
  493. position: absolute;
  494. &__trigger {
  495. border: none;
  496. }
  497. &__color {
  498. border: none !important;
  499. &-inner {
  500. background-color: rgba(0, 0, 0, 0%) !important;
  501. }
  502. }
  503. &__icon {
  504. display: none !important;
  505. }
  506. }
  507. }
  508. .tool-item {
  509. padding: 4px 6px;
  510. border-radius: 8px;
  511. &.active {
  512. background: #a6ccf7;
  513. }
  514. }
  515. }
  516. .match-info {
  517. display: flex;
  518. align-items: center;
  519. margin: 10px 0;
  520. label {
  521. width: 100px;
  522. }
  523. }
  524. .remark-box {
  525. display: flex;
  526. gap: 8px;
  527. align-items: center;
  528. justify-content: center;
  529. width: fit-content;
  530. height: 24px;
  531. padding: 2px 12px;
  532. font-size: 12px;
  533. font-weight: 400;
  534. line-height: 20px; /* 166.667% */
  535. color: #165dff;
  536. cursor: pointer;
  537. border: 1px solid #165dff;
  538. border-radius: 2px;
  539. }
  540. </style>
  541. <style lang="scss">
  542. .check-article {
  543. .login-dialog {
  544. .el-dialog__header {
  545. padding: 0;
  546. }
  547. .el-dialog__body {
  548. padding: 0;
  549. }
  550. .el-input__inner {
  551. text-align: center;
  552. background: #f3f3f3;
  553. }
  554. }
  555. .el-color-picker__empty {
  556. display: none;
  557. }
  558. }
  559. </style>