提交
This commit is contained in:
@@ -19,8 +19,8 @@ const service = axios.create({
|
||||
// baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换
|
||||
// baseURL: 'http://www.tougao.com/', //测试本地 记得切换
|
||||
// baseURL: 'http://192.168.110.110/tougao/public/index.php/',
|
||||
// baseURL: '/api', //本地
|
||||
baseURL: '/', //正式
|
||||
baseURL: '/api', //本地
|
||||
// baseURL: '/', //正式
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -831,26 +831,27 @@ export default {
|
||||
// }
|
||||
|
||||
// },
|
||||
transformHtmlString(inputHtml, type) {
|
||||
|
||||
transformHtmlString(inputHtml, type, options = {}) {
|
||||
const { keepBr = false } = options;
|
||||
if (!keepBr) {
|
||||
inputHtml = inputHtml.replace(/<br\s*\/?>/g, '');
|
||||
}
|
||||
// inputHtml = inputHtml.replace(/(<[^>]+) style="[^"]*"/g, '$1'); // 移除style属性
|
||||
// inputHtml = inputHtml.replace(/(<[^>]+) class="[^"]*"/g, '$1'); // 移除class属性
|
||||
// inputHtml = inputHtml.replace(/<([a-zA-Z0-9]+)[^>]*>/g, '<$1>'); // 删除标签上的所有属性
|
||||
|
||||
inputHtml = inputHtml.replace(/<([a-zA-Z0-9]+)([^>]*)>/g, function (match, tag, attributes) {
|
||||
// 使用正则表达式删除属性(保留 data-latex)
|
||||
let updatedAttributes = attributes.replace(/\s([a-zA-Z0-9-]+)(="[^"]*")?/g, function (attrMatch, attrName) {
|
||||
// 只保留 data-latex 属性,其他属性删除
|
||||
|
||||
if (attrName === "data-latex") {
|
||||
return attrMatch;
|
||||
}
|
||||
if (type == 'table' && tag == 'img' && (attrName === "src" || attrName === "width" || attrName === "height")) {
|
||||
return attrMatch;
|
||||
}
|
||||
return ''; // 删除其他属性
|
||||
return '';
|
||||
});
|
||||
|
||||
// 返回标签,保留 data-latex 属性
|
||||
return `<${tag}${updatedAttributes}>`;
|
||||
});
|
||||
// 2. 删除所有不需要的标签 (除 `strong`, `em`, `sub`, `sup`, `b`, `i` 外的所有标签)
|
||||
@@ -861,14 +862,11 @@ export default {
|
||||
}
|
||||
|
||||
|
||||
// 3. 删除所有 `<br>` 标签
|
||||
inputHtml = inputHtml.replace(/<br\s*\/?>/g, ''); // 删除 <br> 标签
|
||||
// 3. 如果有 `<strong>` 和 `<em>` 标签,去掉内部样式并保留内容
|
||||
// 去掉样式
|
||||
inputHtml = inputHtml.replace(/<span[^>]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签
|
||||
inputHtml = inputHtml.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>'); // 将 `strong` 替换成 `b`
|
||||
inputHtml = inputHtml.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>'); // 将 `em` 替换成 `i`
|
||||
|
||||
// 4. 合并相同标签(如多个连续的 <b> 标签)
|
||||
// 4. 合并相同标签
|
||||
inputHtml = inputHtml.replace(/<b>(.*?)<\/b>\s*<b>/g, '<b>$1'); // 合并连续的 <b> 标签
|
||||
inputHtml = inputHtml.replace(/<i>(.*?)<\/i>\s*<i>/g, '<i>$1'); // 合并连续的 <i> 标签
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
//记得切换
|
||||
|
||||
//正式
|
||||
const mediaUrl = '/public/';
|
||||
const baseUrl = '/';
|
||||
// const mediaUrl = '/public/';
|
||||
// const baseUrl = '/';
|
||||
|
||||
// const mediaUrl = 'https://submission.tmrjournals.com/public/';
|
||||
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
||||
// const baseUrl = '/api'
|
||||
|
||||
// const mediaUrl = 'http://tougaotest.tmrjournals.com/public/';
|
||||
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
||||
// const baseUrl = '/api';
|
||||
const mediaUrl = 'http://tougaotest.tmrjournals.com/public/';
|
||||
// const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
||||
const baseUrl = '/api';
|
||||
|
||||
//本地(正式环境 )
|
||||
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
<font style="color: #f56c6c; margin-right: 5px">*</font>
|
||||
Table:
|
||||
</span>
|
||||
|
||||
<common-table
|
||||
:articleId="articleId"
|
||||
@getContent="getContent"
|
||||
v-if="threeVisible"
|
||||
ref="commonTable"
|
||||
|
||||
@@ -720,7 +720,7 @@ export default {
|
||||
article_id: this.$route.query.id
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.article_pay_info = {
|
||||
fee: res.data.article ? res.data.article.fee : 0,
|
||||
@@ -1049,34 +1049,7 @@ export default {
|
||||
return e.stage_year + ' Vol.' + e.stage_vol + ' issue.' + e.stage_no + e.stage_pagename + e.stage_page;
|
||||
},
|
||||
|
||||
// 1----保存稿件信息
|
||||
ZsSaveMes() {
|
||||
if (this.detailMes.journal_stage_id == 0) {
|
||||
this.$message.error('Please select an installment!');
|
||||
return;
|
||||
}
|
||||
this.$refs.Mes_Form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$api
|
||||
.post('api/Production/editProduction', this.detailMes)
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(`Successfully save the article!`);
|
||||
this.getData();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error(err);
|
||||
});
|
||||
} else {
|
||||
this.$message.error('Please complete Essential Information first!');
|
||||
this.jumpTab(0, this.tabsList[0]);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// 2----添加作者操作
|
||||
add_Authorclick(index, row) {
|
||||
|
||||
@@ -831,7 +831,7 @@ export default {
|
||||
article_id: this.article_id
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.feeStatus=res.data.state
|
||||
this.isShowCommit=res.data.state==1?true:false
|
||||
@@ -1146,34 +1146,7 @@ export default {
|
||||
return e.stage_year + ' Vol.' + e.stage_vol + ' issue.' + e.stage_no + e.stage_pagename + e.stage_page;
|
||||
},
|
||||
|
||||
// 1----保存稿件信息
|
||||
ZsSaveMes() {
|
||||
if (this.detailMes.journal_stage_id == 0) {
|
||||
this.$message.error('Please select an installment!');
|
||||
return;
|
||||
}
|
||||
this.$refs.Mes_Form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$api
|
||||
.post('api/Production/editProduction', this.detailMes)
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(`Successfully save the article!`);
|
||||
this.getData();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error(err);
|
||||
});
|
||||
} else {
|
||||
this.$message.error('Please complete Essential Information first!');
|
||||
this.jumpTab(0, this.tabsList[0]);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// 2----添加作者操作
|
||||
add_Authorclick(index, row) {
|
||||
|
||||
@@ -2084,7 +2084,7 @@ export default {
|
||||
restaurants[i].value = restaurants[i].email + ' | ' + restaurants[i].firstname + restaurants[i].lastname;
|
||||
}
|
||||
var results = value ? restaurants.filter(this.createMata(value)) : restaurants;
|
||||
console.log('🚀 ~ .then ~ results177:', results);
|
||||
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
// this.form.authorList[num].load = false;
|
||||
|
||||
@@ -11,7 +11,12 @@
|
||||
>
|
||||
Detailed for MS</el-button
|
||||
>
|
||||
<div class="tab_item" v-for="(item, index) in tabsList" @click="jumpTab(index, item)" :class="tabName == item.refName ? 'P_style' : ''">
|
||||
<div
|
||||
class="tab_item"
|
||||
v-for="(item, index) in tabsList"
|
||||
@click="jumpTab(index, item)"
|
||||
:class="tabName == item.refName ? 'P_style' : ''"
|
||||
>
|
||||
<h5>
|
||||
<span>{{ index + 1 }}</span>
|
||||
{{ item.name }}
|
||||
@@ -103,10 +108,66 @@
|
||||
<el-input v-model="detailMes.tradition_tag" placeholder="eg :Highlights"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Tradition Content :">
|
||||
<quill-editor ref="myTextEditor" v-model="detailMes.tradition" :options="editorOption"> </quill-editor>
|
||||
<tinymce
|
||||
type="tradition"
|
||||
:height="160"
|
||||
ref="tinymceChild2"
|
||||
:tinymceOtherInit="{
|
||||
forced_root_block: '',
|
||||
newline_behavior: 'linebreak',
|
||||
}"
|
||||
valid_elements=",br"
|
||||
:wordStyle="`p{font-size: 13px;}`"
|
||||
:isAutomaticUpdate="true"
|
||||
@getContent="getContent"
|
||||
@updateChange="(content) => updateChange('tradition',content)"
|
||||
:value="tradition"
|
||||
class="paste-area text-container"
|
||||
:toolbar="[
|
||||
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line|subscript superscript|clearButton'
|
||||
]"
|
||||
style="
|
||||
line-height: 12px;
|
||||
overflow: auto;
|
||||
font-size: 13px; /* 字体大小 */
|
||||
margin-top: 0pt; /* 段前间距 */
|
||||
margin-bottom: 0pt; /* 段后间距 */
|
||||
"
|
||||
></tinymce>
|
||||
|
||||
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.tradition" :options="editorOption"> </quill-editor> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="Medical history of objective :">
|
||||
<quill-editor ref="myTextEditor" v-model="detailMes.mhoo" :options="editorOption"> </quill-editor>
|
||||
|
||||
<tinymce
|
||||
type="mhooStr"
|
||||
:height="160"
|
||||
ref="tinymceChild3"
|
||||
:tinymceOtherInit="{
|
||||
forced_root_block: '',
|
||||
newline_behavior: 'linebreak',
|
||||
}"
|
||||
valid_elements=",br"
|
||||
:wordStyle="`p{font-size: 13px;}`"
|
||||
:isAutomaticUpdate="true"
|
||||
@getContent="getContent"
|
||||
@updateChange="(content) => updateChange('mhooStr',content)"
|
||||
:value="mhooStr"
|
||||
class="paste-area text-container"
|
||||
:toolbar="[
|
||||
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line|subscript superscript|clearButton'
|
||||
]"
|
||||
style="
|
||||
line-height: 12px;
|
||||
overflow: auto;
|
||||
font-size: 13px; /* 字体大小 */
|
||||
margin-top: 0pt; /* 段前间距 */
|
||||
margin-bottom: 0pt; /* 段后间距 */
|
||||
"
|
||||
></tinymce>
|
||||
|
||||
|
||||
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.mhoo" :options="editorOption"> </quill-editor> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="keywords :" prop="keywords">
|
||||
<el-input v-model="detailMes.keywords" placeholder="eg : keyword1,keyword2,keyword3"> </el-input>
|
||||
@@ -118,6 +179,7 @@
|
||||
<el-form-item label="Acknowledgment :" prop="acknowledgment">
|
||||
<el-input v-model="detailMes.acknowledgment"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Abbreviation :">
|
||||
<el-input v-model="detailMes.abbreviation"></el-input>
|
||||
</el-form-item>
|
||||
@@ -133,7 +195,7 @@
|
||||
:wordStyle="`p{font-size: 13px;}`"
|
||||
:isAutomaticUpdate="true"
|
||||
@getContent="getContent"
|
||||
@updateChange="updateChange"
|
||||
@updateChange="(content) => updateChange('abstract',content)"
|
||||
:value="abstract"
|
||||
class="paste-area text-container"
|
||||
:toolbar="[
|
||||
@@ -150,6 +212,9 @@
|
||||
|
||||
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.abstract" :options="editorOption"> </quill-editor> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="Executive editor :" prop="executive_editor">
|
||||
<el-input v-model="detailMes.executive_editor"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="text-align: center; margin: 25px 0 0 0">
|
||||
<el-button type="primary" @click="ZsSaveMes" class="save-btn">
|
||||
@@ -997,6 +1062,8 @@ export default {
|
||||
journal_special_id: 'None'
|
||||
},
|
||||
abstract: '',
|
||||
tradition: '',
|
||||
mhooStr: '',
|
||||
opInstal: [],
|
||||
// opMedical: [
|
||||
// {
|
||||
@@ -1358,6 +1425,18 @@ export default {
|
||||
message: 'Please enter mechanism',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
executive_editor: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please enter the executive editor',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
pattern: /^[^\u4e00-\u9fa5]*$/,
|
||||
message: 'Please use English characters only',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
@@ -1381,7 +1460,7 @@ export default {
|
||||
// article_id: 6075
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 1460:', res);
|
||||
|
||||
if (res.status == 1) {
|
||||
this.finalReview = res.data;
|
||||
} else {
|
||||
@@ -1389,15 +1468,17 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
updateChange(content) {
|
||||
this.abstract = content;
|
||||
updateChange(type, content) {
|
||||
|
||||
this[type] = content;
|
||||
},
|
||||
|
||||
getTinymceContent(type) {
|
||||
|
||||
this.$refs.tinymceChild1.getContent(type);
|
||||
},
|
||||
getContent(type, content) {
|
||||
console.log('content at line 1449:', content);
|
||||
console.log('type at line 1449:', type);
|
||||
|
||||
},
|
||||
// 跳转文章详情
|
||||
showdetaileditor(data) {
|
||||
@@ -1459,12 +1540,24 @@ export default {
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.detailMes = res.data.production;
|
||||
this.abstract = res.data.production.abstract;
|
||||
this.abstract = res.data.production.abstract?res.data.production.abstract:'';
|
||||
this.tradition = res.data.production.tradition?res.data.production.tradition:'';
|
||||
this.mhooStr = res.data.production.mhoo?res.data.production.mhoo:'';
|
||||
if (this.abstract != '') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tinymceChild1.setContent(this.abstract);
|
||||
});
|
||||
}
|
||||
if (this.tradition != '') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tinymceChild2.setContent(this.tradition);
|
||||
});
|
||||
}
|
||||
if (this.mhooStr != '') {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tinymceChild3.setContent(this.mhooStr);
|
||||
});
|
||||
}
|
||||
this.UpTyFIle = res.data.production.file_pdf;
|
||||
this.getArticleFinal(res.data.production.article_id);
|
||||
if (res.data.production.icon != '') {
|
||||
@@ -1753,23 +1846,17 @@ export default {
|
||||
return e.stage_year + ' Vol.' + e.stage_vol + ' issue.' + e.stage_no + e.stage_pagename + e.stage_page;
|
||||
},
|
||||
async abstractFormat(content) {
|
||||
content = this.$commonJS.transformHtmlString(content);
|
||||
content = this.$commonJS.transformHtmlString(content,'content',{keepBr:true});
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = content;
|
||||
|
||||
// 1. 剔除所有 img 标签
|
||||
var imgTags = div.getElementsByTagName('img');
|
||||
for (var i = imgTags.length - 1; i >= 0; i--) {
|
||||
imgTags[i].parentNode.removeChild(imgTags[i]);
|
||||
}
|
||||
|
||||
// 2. 新增:剔除所有 table 相关标签(table、tr、td)
|
||||
var tableTags = div.getElementsByTagName('table');
|
||||
for (var i = tableTags.length - 1; i >= 0; i--) {
|
||||
tableTags[i].parentNode.removeChild(tableTags[i]);
|
||||
}
|
||||
|
||||
// 3. 替换 strong → b、em → i(原有逻辑)
|
||||
var strongTags = div.getElementsByTagName('strong');
|
||||
for (var i = 0; i < strongTags.length; i++) {
|
||||
var bTag = document.createElement('b');
|
||||
@@ -1782,32 +1869,43 @@ export default {
|
||||
iTag.innerHTML = emTags[i].innerHTML;
|
||||
emTags[i].parentNode.replaceChild(iTag, emTags[i]);
|
||||
}
|
||||
|
||||
// 处理 p 标签 + 解码
|
||||
content = div.innerHTML;
|
||||
|
||||
content = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
|
||||
|
||||
content = await this.$commonJS.decodeHtml(content);
|
||||
content = content.replace(/ |\u00A0/g, ' ');
|
||||
|
||||
return content;
|
||||
},
|
||||
// 1----保存稿件信息
|
||||
async ZsSaveMes() {
|
||||
|
||||
var abstractStr = '';
|
||||
var mhooStr = '';
|
||||
var traditionStr = '';
|
||||
if (this.detailMes.journal_stage_id == 0) {
|
||||
this.$message.error('Please select an installment!');
|
||||
return;
|
||||
} if (this.tradition!='') {
|
||||
traditionStr = await this.abstractFormat(this.tradition);
|
||||
}
|
||||
if (this.mhooStr!='') {
|
||||
mhooStr = await this.abstractFormat(this.mhooStr);
|
||||
}
|
||||
|
||||
if (this.abstract == '') {
|
||||
this.$message.error('Please input abstract!');
|
||||
return;
|
||||
return false;
|
||||
} else {
|
||||
abstractStr = await this.abstractFormat(this.abstract);
|
||||
}
|
||||
console.log('abstractStr at line 1820:', abstractStr);
|
||||
|
||||
this.$refs.Mes_Form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$api
|
||||
.post('api/Production/editProduction', { ...this.detailMes, abstract: abstractStr })
|
||||
.post('api/Production/editProduction', { ...this.detailMes, abstract: abstractStr, mhoo: mhooStr, tradition: traditionStr })
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(`Successfully save the article!`);
|
||||
@@ -2904,30 +3002,30 @@ export default {
|
||||
},
|
||||
|
||||
onScroll(e) {
|
||||
const scrollContainer = e.target;
|
||||
const scrollItems = document.querySelectorAll('.scroll-item');
|
||||
const scrollContainer = e.target;
|
||||
const scrollItems = document.querySelectorAll('.scroll-item');
|
||||
|
||||
// 遍历所有滚动项,找到“当前在可视区域内”的项
|
||||
for (let i = 0; i < scrollItems.length; i++) {
|
||||
const item = scrollItems[i];
|
||||
// 计算项相对于滚动容器的位置(顶部距离)
|
||||
const itemTop = item.offsetTop - scrollContainer.offsetTop;
|
||||
// 计算项的底部距离
|
||||
const itemBottom = itemTop + item.offsetHeight;
|
||||
// 遍历所有滚动项,找到“当前在可视区域内”的项
|
||||
for (let i = 0; i < scrollItems.length; i++) {
|
||||
const item = scrollItems[i];
|
||||
// 计算项相对于滚动容器的位置(顶部距离)
|
||||
const itemTop = item.offsetTop - scrollContainer.offsetTop;
|
||||
// 计算项的底部距离
|
||||
const itemBottom = itemTop + item.offsetHeight;
|
||||
|
||||
// 判断:项的顶部 <= 滚动距离,且项的底部 >= 滚动距离(即项在可视区域内)
|
||||
const isInView = (itemTop <= scrollContainer.scrollTop) && (itemBottom >= scrollContainer.scrollTop);
|
||||
// 判断:项的顶部 <= 滚动距离,且项的底部 >= 滚动距离(即项在可视区域内)
|
||||
const isInView = itemTop <= scrollContainer.scrollTop && itemBottom >= scrollContainer.scrollTop;
|
||||
|
||||
if (isInView) {
|
||||
this.tabIndex = i.toString();
|
||||
// 先判断tabsList是否存在对应索引,避免报错
|
||||
if (this.tabsList[i]) {
|
||||
this.tabName = this.tabsList[i].refName;
|
||||
if (isInView) {
|
||||
this.tabIndex = i.toString();
|
||||
// 先判断tabsList是否存在对应索引,避免报错
|
||||
if (this.tabsList[i]) {
|
||||
this.tabName = this.tabsList[i].refName;
|
||||
}
|
||||
break; // 找到第一个匹配的项就停止
|
||||
}
|
||||
}
|
||||
break; // 找到第一个匹配的项就停止
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
getHight() {
|
||||
this.contentStyleObj.height = window.innerHeight - 60 + 'px';
|
||||
@@ -2969,9 +3067,9 @@ export default {
|
||||
font-size: 14px !important;
|
||||
padding: 6px 10px !important;
|
||||
}
|
||||
.tab_item{
|
||||
height: 6vh;
|
||||
min-height: 60px;
|
||||
.tab_item {
|
||||
height: 6vh;
|
||||
min-height: 60px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,14 +5,19 @@
|
||||
</template>
|
||||
<script>
|
||||
import { string } from 'html-docx-js/dist/html-docx';
|
||||
import axios from 'axios';
|
||||
import htmlDocx from 'html-docx-js/dist/html-docx.js';
|
||||
import { Document, Packer, PageOrientation, Paragraph, TextRun } from 'docx'; // 引入 docx.js
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
const tableStyle = `*{
|
||||
font-family: 'Charis SIL';
|
||||
}
|
||||
b span{
|
||||
import { extractHexImagesFromRTF, hexToBlob } from '@/utils/rtfParser';
|
||||
const tableStyle = `
|
||||
.word-img-placeholder {
|
||||
background: #f0f0f0 ;
|
||||
}
|
||||
*{
|
||||
font-family: 'Charis SIL';
|
||||
}
|
||||
b span{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
i span{
|
||||
@@ -62,7 +67,6 @@ const tableStyle = `*{
|
||||
mos-line-height: 20px !important;
|
||||
}
|
||||
table tbody tr td{
|
||||
|
||||
text-align:left !important;
|
||||
border-left:none !important;
|
||||
mso-border-left-alt:none !important;
|
||||
@@ -181,6 +185,17 @@ export default {
|
||||
wordStyle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
valid_elements: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tinymceOtherInit: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
articleId: {
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -301,101 +316,97 @@ export default {
|
||||
generateUniqueId() {
|
||||
return 'wmath-' + Math.random().toString(36).substr(2, 9);
|
||||
},
|
||||
// 同步将 Hex 字符串转为 Base64
|
||||
hexToBase64Sync(hex, mimeType) {
|
||||
try {
|
||||
// 将 Hex 字符串转为字节数组
|
||||
const bytes = new Uint8Array(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
||||
|
||||
// 同步转为二进制字符串
|
||||
let binary = '';
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
|
||||
// 使用 window.btoa 生成 base64
|
||||
return `data:${mimeType};base64,${window.btoa(binary)}`;
|
||||
} catch (e) {
|
||||
console.error('Hex to Base64 conversion failed', e);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
// 按照你要求的 XMLHttpRequest 格式编写
|
||||
uploadSingleImage(base64Data, index) {
|
||||
const _this = this;
|
||||
//转为 Blob
|
||||
const blob = _this.dataURLtoBlob(base64Data);
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
// 按照你截图中的参数名,这里假设是 'file'
|
||||
formData.append('mainImage', blob, `word_img_${index}.png`);
|
||||
formData.append('article_id', this.articleId);
|
||||
xhr.withCredentials = false;
|
||||
xhr.open('POST', _this.baseUrl + 'api/Preaccept/up_img_mainImage');
|
||||
xhr.onload = function () {
|
||||
if (xhr.status !== 200) {
|
||||
console.error('HTTP Error: ' + xhr.status);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const json = JSON.parse(xhr.responseText);
|
||||
if (json.code === 0) {
|
||||
// 2. 拼接服务器返回的 URL
|
||||
const finalUrl = _this.mediaUrl + 'articleImage/' + json.data.upurl;
|
||||
// 3. 找到对应的加载中占位图并替换
|
||||
const doc = tinymce.activeEditor.getDoc();
|
||||
const placeholder = doc.querySelector(`img[data-idx="${index}"]`);
|
||||
if (placeholder) {
|
||||
placeholder.src = finalUrl;
|
||||
placeholder.removeAttribute('data-idx'); // 任务完成,移除标记
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析响应失败', e);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
},
|
||||
|
||||
// 辅助工具:Base64 转 Blob
|
||||
dataURLtoBlob(dataurl) {
|
||||
const arr = dataurl.split(','),
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]);
|
||||
let n = bstr.length;
|
||||
const u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new Blob([u8arr], { type: mime });
|
||||
},
|
||||
initTinymce() {
|
||||
let globalImgCounter = 0; // 放在 init 闭包内,确保计数器持续增长
|
||||
let currentPasteBase64Images = [];
|
||||
var _this = this;
|
||||
window.tinymce.init({
|
||||
..._this.tinymceOtherInit,
|
||||
inline: false, // 使用 iframe 模式
|
||||
selector: `#${this.tinymceId}`,
|
||||
// noneditable_regexp: "/<wmath>.*?<\/wmath>/g",
|
||||
content_css: false, // 禁用默认样式
|
||||
table_resize_bars: true, // 启用拖动调整功能
|
||||
valid_elements: this.type == 'table' ? '*[*]' : 'img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,wmath', // 允许的标签和属性
|
||||
valid_elements:
|
||||
this.type == 'table'
|
||||
? '*[*]'
|
||||
: `img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,wmath${this.valid_elements}`, // 允许的标签和属性
|
||||
// valid_elements: '*[*]', // 允许所有 HTML 标签
|
||||
noneditable_editable_class: 'MathJax',
|
||||
height: this.height,
|
||||
paste_preprocess: function (plugin, args) {
|
||||
let content = args.content; // 获取粘贴的内容
|
||||
let tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = content;
|
||||
if (tempDiv.querySelector('table')) {
|
||||
console.log('粘贴的内容包含表格');
|
||||
if (_this.type == 'table') {
|
||||
_this.$commonJS.parseTableToArray(content, (tableList) => {
|
||||
var contentHtml = `
|
||||
<div class="thumbnailTableBox wordTableHtml table_Box" style="">
|
||||
<table border="1" style="width: auto; border-collapse: collapse; text-align: center;">
|
||||
${tableList
|
||||
.map((row) => {
|
||||
return `
|
||||
<tr>
|
||||
${row
|
||||
.map((cell) => {
|
||||
return `
|
||||
<td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}">
|
||||
<span>${cell.text || ''}</span>
|
||||
</td>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = contentHtml;
|
||||
|
||||
// _this.updateTableStyles(container); // 根据需要应用额外的样式
|
||||
args.content = container.innerHTML; // 更新处理后的内容
|
||||
});
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
console.log('Original content:', content); // 输出原始粘贴内容
|
||||
|
||||
// 改进的正则表达式,匹配 $$...$$ 格式的 LaTeX 公式
|
||||
const mathRegex = /\$\$([^$]+)\$\$/g;
|
||||
|
||||
// 如果粘贴的内容包含 $$...$$ 格式的公式,进行处理
|
||||
content = content.replace(mathRegex, function (match, formula) {
|
||||
console.log('Matched formula:', formula); // 输出每个匹配的公式
|
||||
// 将公式包裹在 <wmath> 标签中,保留 $$...$$ 结构
|
||||
return `<wmath data-latex="${match}">${match}</wmath>`;
|
||||
});
|
||||
|
||||
console.log('Processed content:', content); // 输出处理后的内容
|
||||
}
|
||||
|
||||
// 更新 args.content 为处理后的内容
|
||||
|
||||
// 阻止默认的粘贴行为,确保自定义处理优先执行
|
||||
if (args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
if (_this.isAutomaticUpdate) {
|
||||
args.content = _this.$commonJS.transformHtmlString(content); // 更新处理后的内容
|
||||
} else {
|
||||
args.content = content;
|
||||
}
|
||||
setTimeout(() => {
|
||||
window.renderMathJax(_this.tinymceId);
|
||||
}, 10);
|
||||
},
|
||||
|
||||
content_style: `
|
||||
${tableStyle}
|
||||
${_this.wordStyle}
|
||||
|
||||
|
||||
|
||||
|
||||
`,
|
||||
|
||||
formats: {
|
||||
bold: { inline: 'b' },
|
||||
italic: { inline: 'i' }
|
||||
@@ -407,7 +418,6 @@ export default {
|
||||
statusbar: false, // 关闭底部状态栏
|
||||
custom_colors: false,
|
||||
color_map: ['0082AA', 'TMR Blue'],
|
||||
|
||||
// image
|
||||
plugins: 'texttransform noneditable table image', // 启用 forecolor 和 code 插件
|
||||
// plugins: 'forecolor code paste table image mathType searchreplace raw', // 启用 forecolor 和 code 插件
|
||||
@@ -419,81 +429,76 @@ export default {
|
||||
config: 'TeX-AMS-MML_HTMLorMML'
|
||||
},
|
||||
// automatic_uploads: false,
|
||||
// images_upload_handler: function (blobInfo, success, failure, progress) {
|
||||
// console.log('blobInfo at line 419:', blobInfo);
|
||||
// return new Promise(function (resolve, reject) {
|
||||
// const xhr = new XMLHttpRequest();
|
||||
// const formData = new FormData();
|
||||
// const file = blobInfo.blob();
|
||||
// let filename = file.name;
|
||||
// if (!filename) {
|
||||
// // 如果没有名字,则手动生成一个
|
||||
// const ext = file.type.split('/').pop(); // 从 MIME 类型获取扩展名,例如 'png'
|
||||
// const timestamp = Date.now();
|
||||
// filename = `unnamed_${timestamp}.${ext}`;
|
||||
// }
|
||||
// console.log('file at line 424:', file);
|
||||
images_upload_handler: function (blobInfo, progress) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
const file = blobInfo.blob();
|
||||
// 1. 处理文件名:优先使用原始文件名,没有则生成
|
||||
let filename = blobInfo.filename() || `upload_${Date.now()}.png`;
|
||||
// 2. 构造符合你后端要求的参数
|
||||
formData.append('mainImage', file, filename);
|
||||
// 优先从路由取 id,其次取 data 里的 articleId
|
||||
formData.append('article_id', _this.articleId);
|
||||
xhr.withCredentials = false;
|
||||
// 拼接你的 baseUrl
|
||||
xhr.open('POST', _this.baseUrl + '/api/Preaccept/up_img_mainImage');
|
||||
// 上传进度(可选)
|
||||
xhr.upload.onprogress = (e) => {
|
||||
progress((e.loaded / e.total) * 100);
|
||||
};
|
||||
xhr.onload = function () {
|
||||
if (xhr.status !== 200) {
|
||||
reject('HTTP Error: ' + xhr.status);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const json = JSON.parse(xhr.responseText);
|
||||
if (json.code === 0) {
|
||||
// 3. 按照你的逻辑返回拼接后的完整 URL
|
||||
const finalUrl = _this.mediaUrl + 'articleImage/' + json.data.upurl;
|
||||
resolve(finalUrl);
|
||||
console.log('手动上传成功:', finalUrl);
|
||||
} else {
|
||||
reject('Upload Error: ' + json.msg);
|
||||
}
|
||||
} catch (e) {
|
||||
reject('Invalid response: ' + xhr.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
// xhr.withCredentials = false;
|
||||
// xhr.open('POST', _this.baseUrl + '/api/Preaccept/up_img_mainImage');
|
||||
xhr.onerror = function () {
|
||||
reject('网络请求失败');
|
||||
};
|
||||
|
||||
// xhr.onload = function () {
|
||||
// if (xhr.status !== 200) {
|
||||
// reject('HTTP Error: ' + xhr.status);
|
||||
// return;
|
||||
// }
|
||||
xhr.send(formData);
|
||||
});
|
||||
},
|
||||
|
||||
// try {
|
||||
// const json = JSON.parse(xhr.responseText);
|
||||
// console.log('json at line 434:', json);
|
||||
// if (json.code != 0) {
|
||||
// reject('Upload Error: ' + json.msg);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// resolve(_this.mediaUrl + 'articleImage/' + json.data.upurl); // ✅ 返回图片 URL 给 TinyMCE 插入
|
||||
// } catch (e) {
|
||||
// reject('Invalid response: ' + xhr.responseText);
|
||||
// }
|
||||
// };
|
||||
|
||||
// formData.append('mainImage', file, filename);
|
||||
// formData.append('article_id', _this.$route.query.id);
|
||||
// xhr.send(formData);
|
||||
// });
|
||||
// },
|
||||
//设置自定义按钮 myCustomToolbarButton
|
||||
setup(ed) {
|
||||
let currentPasteImages = [];
|
||||
_this.$commonJS.initEditorButton(_this, ed);
|
||||
|
||||
var currentWmathElement = null;
|
||||
|
||||
ed.on('click', function (e) {
|
||||
const wmathElement = e.target.closest('wmath');
|
||||
|
||||
if (wmathElement) {
|
||||
currentWmathElement = wmathElement; // 👈 保存当前点击的元素
|
||||
|
||||
const latexContentRaw = wmathElement.getAttribute('data-latex') || '';
|
||||
console.log('at line 488: raw =', latexContentRaw);
|
||||
|
||||
// 去除所有 $ 符号
|
||||
const latexContent = latexContentRaw.replace(/\$/g, '').trim();
|
||||
// console.log('at line 489: cleaned =', latexContent);
|
||||
|
||||
// 编码后用于传递到弹窗
|
||||
const encoded = encodeURIComponent(latexContent);
|
||||
|
||||
// 给 wmath 添加唯一 data-id,方便后续精准替换
|
||||
let wmathId = wmathElement.getAttribute('data-id');
|
||||
if (!wmathId) {
|
||||
wmathId = 'wmath-' + Math.random().toString(36).substr(2, 9);
|
||||
wmathElement.setAttribute('data-id', wmathId);
|
||||
}
|
||||
|
||||
// 当前编辑器 ID 也保存下来(如果你有多个编辑器)
|
||||
const editorId = ed.id;
|
||||
|
||||
// 打开编辑窗口并传参(传递 data-id + 内容)
|
||||
window.open(
|
||||
`/LateX?id=${encoded}&wmathId=${wmathId}&editorId=${editorId}`,
|
||||
@@ -502,6 +507,17 @@ export default {
|
||||
);
|
||||
}
|
||||
});
|
||||
// 修改 paste 事件中的逻辑
|
||||
ed.on('paste', (event) => {
|
||||
const rtf = event.clipboardData.getData('text/rtf');
|
||||
if (rtf && rtf.includes('\\pict')) {
|
||||
const extracted = extractHexImagesFromRTF(rtf);
|
||||
extracted.forEach((img, i) => {
|
||||
const base64 = _this.hexToBase64Sync(img.hex, img.mimeType);
|
||||
_this.uploadSingleImage(base64, i);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ed.ui.registry.addButton('uploadWord', {
|
||||
text: 'Word',
|
||||
@@ -527,19 +543,12 @@ export default {
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
|
||||
ed.on('init', function () {
|
||||
_this.$commonJS.inTinymceButtonClass();
|
||||
const editorBody = ed.getBody();
|
||||
|
||||
// 监听点击事件来确保用户可以删除<wmath>元素
|
||||
|
||||
// 创建 MutationObserver 监听内容变化
|
||||
const observer = new MutationObserver(() => {
|
||||
const currentContent = ed.getContent();
|
||||
// console.log('currentContent at line 484:', currentContent)
|
||||
// console.log('currentContent at line 447:', currentContent);
|
||||
|
||||
if (_this.isAutomaticUpdate) {
|
||||
_this.$emit('updateChange', currentContent);
|
||||
// _this.$emit('updateChange', _this.$commonJS.decodeHtml(currentContent));
|
||||
@@ -606,6 +615,91 @@ export default {
|
||||
e.content = e.content.replace(/<i>/g, '<em>').replace(/<\/i>/g, '</em>');
|
||||
});
|
||||
},
|
||||
paste_preprocess: function (plugin, args) {
|
||||
let imgIdx = 0;
|
||||
|
||||
const silentPlaceholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
||||
|
||||
let content = args.content.replace(/(<img[^>]*?)src="file:\/\/\/[^" ]*"/gi, (match, p1) => {
|
||||
// 保留 data-idx, Word 里的尺寸
|
||||
return `${p1}src="${silentPlaceholder}" class="word-img-placeholder" data-idx="${imgIdx++}"`;
|
||||
});
|
||||
let tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = content;
|
||||
if (tempDiv.querySelector('table')) {
|
||||
console.log('粘贴的内容包含表格');
|
||||
if (_this.type == 'table') {
|
||||
// 3. 在这里直接消费外部变量 currentPasteBase64Images
|
||||
// content = content.replace(new RegExp(`src="${silentPlaceholder}"`, 'gi'), () => {
|
||||
// // 按顺序取图
|
||||
// const base64Data = currentPasteBase64Images[globalImgCounter] || '';
|
||||
// globalImgCounter++;
|
||||
// return `src="${base64Data}"`;
|
||||
// });
|
||||
_this.$commonJS.parseTableToArray(content, (tableList) => {
|
||||
var contentHtml = `
|
||||
<div class="thumbnailTableBox wordTableHtml table_Box table_Box3333" style="">
|
||||
<table border="1" style="width: auto; border-collapse: collapse; text-align: center;">
|
||||
${tableList
|
||||
.map((row) => {
|
||||
return `
|
||||
<tr>
|
||||
${row
|
||||
.map((cell) => {
|
||||
return `
|
||||
<td colspan="${cell.colspan || 1}" rowspan="${cell.rowspan || 1}">
|
||||
<span>${cell.text || ''}</span>
|
||||
</td>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
.join('')}
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = contentHtml;
|
||||
|
||||
// _this.updateTableStyles(container); // 根据需要应用额外的样式
|
||||
args.content = container.innerHTML; // 更新处理后的内容
|
||||
});
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
console.log('Original content:', content); // 输出原始粘贴内容
|
||||
|
||||
// 改进的正则表达式,匹配 $$...$$ 格式的 LaTeX 公式
|
||||
const mathRegex = /\$\$([^$]+)\$\$/g;
|
||||
|
||||
// 如果粘贴的内容包含 $$...$$ 格式的公式,进行处理
|
||||
content = content.replace(mathRegex, function (match, formula) {
|
||||
console.log('Matched formula:', formula); // 输出每个匹配的公式
|
||||
// 将公式包裹在 <wmath> 标签中,保留 $$...$$ 结构
|
||||
return `<wmath data-latex="${match}">${match}</wmath>`;
|
||||
});
|
||||
|
||||
console.log('Processed content:', content); // 输出处理后的内容
|
||||
}
|
||||
// 更新 args.content 为处理后的内容
|
||||
// 阻止默认的粘贴行为,确保自定义处理优先执行
|
||||
if (args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
if (_this.isAutomaticUpdate) {
|
||||
args.content = _this.$commonJS.transformHtmlString(content); // 更新处理后的内容
|
||||
} else {
|
||||
args.content = content;
|
||||
}
|
||||
setTimeout(() => {
|
||||
window.renderMathJax(_this.tinymceId);
|
||||
}, 10);
|
||||
},
|
||||
init_instance_callback: (editor) => {
|
||||
if (_this.value) {
|
||||
editor.setContent(_this.value);
|
||||
@@ -694,19 +788,15 @@ export default {
|
||||
},
|
||||
//设置内容
|
||||
setContent(value) {
|
||||
|
||||
window.tinymce.get(this.tinymceId).setContent(value);
|
||||
},
|
||||
//获取内容
|
||||
async getContent(type) {
|
||||
var content = window.tinymce.get(this.tinymceId).getContent();
|
||||
|
||||
content = content.replace(/<span[^>]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签
|
||||
|
||||
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
|
||||
content = content.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>');
|
||||
content = content.replace(/ /g, ' '); // 将所有 替换为空格
|
||||
console.log('content at line 632:', content);
|
||||
this.$emit('getContent', type, content);
|
||||
},
|
||||
|
||||
@@ -817,4 +907,8 @@ export default {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
::v-deep .tox:not(.tox-tinymce-inline) .tox-editor-header {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -452,7 +452,7 @@ export default {
|
||||
article_id: this.articleId
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.articleInfo = res.data.article_detail;
|
||||
this.journalInfo = res.data.journal_detail;
|
||||
|
||||
@@ -353,7 +353,7 @@ export default {
|
||||
})
|
||||
.then((res) => {
|
||||
loading.close();
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.articleInfo = res.data.article_detail;
|
||||
this.journalInfo = res.data.journal_detail;
|
||||
|
||||
@@ -280,7 +280,7 @@ that.orderInfo=res.data.detail
|
||||
article_id: this.articleId
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.articleInfo = res.data.article_detail;
|
||||
this.journalInfo = res.data.journal_detail;
|
||||
|
||||
@@ -147,7 +147,7 @@ export default {
|
||||
.then((res) => {
|
||||
loading.close();
|
||||
this.paymentChecking = true;
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.articleBaseInfo = res.data.article_detail;
|
||||
this.journalBaseInfo = res.data.journal_detail;
|
||||
@@ -169,7 +169,7 @@ export default {
|
||||
article_id: this.articleId
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 191:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
|
||||
this.hideAlert();
|
||||
|
||||
@@ -577,7 +577,7 @@ export default {
|
||||
chekEmail(value) {
|
||||
setTimeout(() => {
|
||||
this.reviewerForm = {};
|
||||
console.log('value at line 504:', value, this.reviewerForm);
|
||||
|
||||
if (value) {
|
||||
this.chaSelChose({
|
||||
email: value
|
||||
@@ -602,7 +602,7 @@ export default {
|
||||
restaurants[i].value = restaurants[i].email;
|
||||
}
|
||||
var results = value ? restaurants.filter(this.createMata(value)) : restaurants;
|
||||
console.log('🚀 ~ .then ~ results177:', results);
|
||||
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
// this.form.authorList[num].load = false;
|
||||
@@ -659,7 +659,6 @@ export default {
|
||||
},
|
||||
// 选中用户
|
||||
async chaSelChose(value, num) {
|
||||
console.log('value at line 545:', value);
|
||||
|
||||
// console.log(value);
|
||||
// console.log(num);
|
||||
@@ -673,7 +672,7 @@ export default {
|
||||
email: this.addForm.email
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('res at line 554:', res);
|
||||
|
||||
if (res.code == 0) {
|
||||
this.btn_alert = false;
|
||||
this.reviewerForm = res.data.result;
|
||||
@@ -735,7 +734,7 @@ export default {
|
||||
? item.shu.split(',').map(Number)
|
||||
: [item.shu]
|
||||
}));
|
||||
console.log('this.majorValueList at line 903:', this.majorValueList);
|
||||
|
||||
// if (this.reviewerForm.majorList) {
|
||||
// if (this.reviewerForm.majorList.length >= 1) {
|
||||
// this.reviewerForm.major_a = this.reviewerForm.majorList[0];
|
||||
@@ -755,7 +754,7 @@ export default {
|
||||
// this.addForm.major = this.addForm.major_a;
|
||||
// }
|
||||
this.$forceUpdate();
|
||||
console.log('this.addForm.majorList at line 674:', this.reviewerForm);
|
||||
|
||||
this.$nextTick(async () => {
|
||||
// await this.jiLInaoan();
|
||||
await this.getJournalsForReviewerInEditor();
|
||||
@@ -778,7 +777,7 @@ export default {
|
||||
// this.reviewerForm = {
|
||||
// email: value.email
|
||||
// };
|
||||
console.log('value at line 529:', value, this.reviewerForm);
|
||||
|
||||
|
||||
// this.$forceUpdate();
|
||||
},
|
||||
|
||||
@@ -351,7 +351,7 @@ export default {
|
||||
onAction: function () {
|
||||
// 在选中的文本周围包裹 <blue> 标签
|
||||
var selectedText = ed.selection.getContent();
|
||||
console.log('selectedText at line 529:', selectedText);
|
||||
|
||||
if (selectedText) {
|
||||
var wrappedText = `<blue>${selectedText}</blue>`;
|
||||
ed.selection.setContent(wrappedText);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<!-- image -->
|
||||
<tinymce
|
||||
type="table"
|
||||
:articleId="articleId"
|
||||
ref="tinymceChild1"
|
||||
:wordStyle="wordStyle"
|
||||
@getContent="getContent"
|
||||
@@ -33,7 +34,7 @@
|
||||
<script>
|
||||
import Tinymce from '@/components/page/components/Tinymce';
|
||||
export default {
|
||||
props: ['lineStyle'],
|
||||
props: ['lineStyle','articleId'],
|
||||
components: {
|
||||
Tinymce
|
||||
},
|
||||
|
||||
141
src/main.js
141
src/main.js
@@ -59,46 +59,46 @@ async function loadJournalType() {
|
||||
let parsedData = null;
|
||||
|
||||
if (cachedString) {
|
||||
try {
|
||||
// 必须先解析 JSON 字符串
|
||||
parsedData = JSON.parse(cachedString);
|
||||
} catch (e) {
|
||||
console.error("缓存数据解析失败,将重新请求 API:", e);
|
||||
}
|
||||
try {
|
||||
// 必须先解析 JSON 字符串
|
||||
parsedData = JSON.parse(cachedString);
|
||||
} catch (e) {
|
||||
console.error("缓存数据解析失败,将重新请求 API:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查解析后的数据是否有效(是数组且长度大于 0)
|
||||
if (parsedData && Array.isArray(parsedData) && parsedData.length > 0) {
|
||||
// 缓存有效,直接返回缓存数据
|
||||
return parsedData;
|
||||
// 缓存有效,直接返回缓存数据
|
||||
return parsedData;
|
||||
}
|
||||
|
||||
// 3. 如果缓存无效或不存在,执行 API 调用
|
||||
try {
|
||||
// 第一个 API 调用,使用 await 接收结果
|
||||
const journalRes = await api.post('api/Articletype/getArticleType', {});
|
||||
if (journalRes.status === 1) {
|
||||
// 存储 journal data
|
||||
localStorage.setItem('journalTypeData', JSON.stringify(journalRes.data.base));
|
||||
localStorage.setItem('journalTypeDataAll', JSON.stringify([...journalRes.data.base, ...journalRes.data.supplement]));
|
||||
}
|
||||
// 第一个 API 调用,使用 await 接收结果
|
||||
const journalRes = await api.post('api/Articletype/getArticleType', {});
|
||||
if (journalRes.status === 1) {
|
||||
// 存储 journal data
|
||||
localStorage.setItem('journalTypeData', JSON.stringify(journalRes.data.base));
|
||||
localStorage.setItem('journalTypeDataAll', JSON.stringify([...journalRes.data.base, ...journalRes.data.supplement]));
|
||||
}
|
||||
|
||||
// 第二个 API 调用,串行执行
|
||||
const medicalRes = await api.post('api/Articletype/getMedicalType', {});
|
||||
if (medicalRes.status === 1) {
|
||||
// 存储 medical data
|
||||
localStorage.setItem('opMedicalListData', JSON.stringify(medicalRes.data));
|
||||
}
|
||||
// 第二个 API 调用,串行执行
|
||||
const medicalRes = await api.post('api/Articletype/getMedicalType', {});
|
||||
if (medicalRes.status === 1) {
|
||||
// 存储 medical data
|
||||
localStorage.setItem('opMedicalListData', JSON.stringify(medicalRes.data));
|
||||
}
|
||||
|
||||
// 4. 返回第一个 API 调用的主要数据
|
||||
if (journalRes.status === 1) {
|
||||
return journalRes.data.base;
|
||||
}
|
||||
// 4. 返回第一个 API 调用的主要数据
|
||||
if (journalRes.status === 1) {
|
||||
return journalRes.data.base;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 集中处理 API 调用失败
|
||||
console.error('Error loading data:', error);
|
||||
return []; // 失败时返回一个默认的空数组,避免程序崩溃
|
||||
// 集中处理 API 调用失败
|
||||
console.error('Error loading data:', error);
|
||||
return []; // 失败时返回一个默认的空数组,避免程序崩溃
|
||||
}
|
||||
|
||||
// 如果所有流程都没有返回,默认返回空数组
|
||||
@@ -171,51 +171,36 @@ Vue.filter('jtName', function (value) {
|
||||
// 使用 ES Module
|
||||
import * as echarts from 'echarts';
|
||||
Vue.prototype.$echarts = echarts
|
||||
|
||||
|
||||
Vue.prototype.$api = api
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
import Editor from "@tinymce/tinymce-vue"; // 默认导入
|
||||
|
||||
Vue.component("Editor", Editor);
|
||||
//自定义组件
|
||||
const components = {
|
||||
'common-table': () => import('@/components/page/components/table/table.vue'),
|
||||
'common-review-article': () => import('@/components/page/components/reviewArticle/index.vue'),
|
||||
'common-editor-article': () => import('@/components/page/components/EditorArticle/index.vue'),
|
||||
'common-editor-article-detail': () => import('@/components/page/components/EditorArticle/detail.vue'),
|
||||
'common-late-x': () => import('@/components/page/components/table/LateX.vue'),
|
||||
'common-major': () => import('@/components/page/components/major/index.vue'),
|
||||
'common-major-list': () => import('@/components/page/components/major/list.vue'),
|
||||
'common-paypal-button': () => import('@/components/page/components/pendingPayment/PayPalButton.vue'),
|
||||
'common-tiff': () => import('@/components/page/components/table/tiff.vue'),
|
||||
'common-content': () => import('@/components/page/components/table/content.vue'),
|
||||
'common-word': () => import('@/components/page/components/table/word.vue'),
|
||||
'common-comment': () => import('@/components/page/components/table/comment.vue'),
|
||||
'common-edit-table': () => import('@/components/page/components/table/editTable.vue'),
|
||||
'common-annotations': () => import('@/components/page/components/table/annotations.vue'),
|
||||
'common-word-html': () => import('@/components/page/components/table/wordHtml.vue'),
|
||||
'common-word-html-type-setting': () => import('@/components/page/components/table/wordHtmlTypesetting.vue'),
|
||||
'common-drag-word': () => import('@/components/page/components/table/dragWord.vue'),
|
||||
};
|
||||
|
||||
import commonTable from '@/components/page/components/table/table.vue'
|
||||
Vue.component('common-table', commonTable);
|
||||
import commonReviewArticle from '@/components/page/components/reviewArticle/index.vue'
|
||||
Vue.component('common-review-article', commonReviewArticle);
|
||||
import commonEditorArticle from '@/components/page/components/EditorArticle/index.vue'
|
||||
Vue.component('common-editor-article', commonEditorArticle);
|
||||
import commonEditorArticleDetail from '@/components/page/components/EditorArticle/detail.vue'
|
||||
Vue.component('common-editor-article-detail', commonEditorArticleDetail);
|
||||
import commonLateX from '@/components/page/components/table/LateX.vue'
|
||||
Vue.component('common-late-x', commonLateX);
|
||||
import commonMajor from '@/components/page/components/major/index.vue'
|
||||
Vue.component('common-major', commonMajor);
|
||||
import commonMajorList from '@/components/page/components/major/list.vue'
|
||||
Vue.component('common-major-list', commonMajorList);
|
||||
import commonPayPalButton from '@/components/page/components/pendingPayment/PayPalButton.vue'
|
||||
Vue.component('common-paypal-button', commonPayPalButton);
|
||||
import commonTiff from '@/components/page/components/table/tiff.vue'
|
||||
Vue.component('common-tiff', commonTiff);
|
||||
import commonContent from '@/components/page/components/table/content.vue'
|
||||
Vue.component('common-content', commonContent);
|
||||
import commonWord from '@/components/page/components/table/word.vue'
|
||||
Vue.component('common-comment', commonComment);
|
||||
import commonEditTable from '@/components/page/components/table/editTable.vue'
|
||||
Vue.component('common-edit-table', commonEditTable);
|
||||
import commonComment from '@/components/page/components/table/comment.vue'
|
||||
Vue.component('common-word', commonWord);
|
||||
import commonAnnotations from '@/components/page/components/table/annotations.vue'
|
||||
Vue.component('common-annotations', commonAnnotations);
|
||||
import commonWordHtml from '@/components/page/components/table/wordHtml.vue'
|
||||
Vue.component('common-word-html', commonWordHtml);
|
||||
import commonWordHtmlTypesetting from '@/components/page/components/table/wordHtmlTypesetting.vue'
|
||||
Vue.component('common-word-html-type-setting', commonWordHtmlTypesetting);
|
||||
Object.keys(components).forEach(key => {
|
||||
Vue.component(key, components[key]);
|
||||
});
|
||||
|
||||
|
||||
import commonDragWord from '@/components/page/components/table/dragWord.vue'
|
||||
Vue.component('common-drag-word', commonDragWord);
|
||||
Vue.use(VueI18n);
|
||||
Vue.use(ElementUI, {
|
||||
size: 'small',
|
||||
@@ -228,20 +213,20 @@ const i18n = new VueI18n({
|
||||
//使用钩子函数对路由进行权限跳转
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const currentRoute = to; // 获取当前路由路径,例如 "/home"
|
||||
console.log('currentRoute at line 211:', currentRoute.meta)
|
||||
if(currentRoute.meta.hideJournal){
|
||||
|
||||
}else{
|
||||
if (currentRoute.meta.hideJournal) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
// 尝试请求接口(即使失败也继续后续逻辑)
|
||||
await Promise.all([
|
||||
loadJournalList(),
|
||||
loadJournalType()
|
||||
]);
|
||||
} catch (err) {
|
||||
// 仅打印错误,不阻断路由
|
||||
// 尝试请求接口(即使失败也继续后续逻辑)
|
||||
await Promise.all([
|
||||
loadJournalList(),
|
||||
loadJournalType()
|
||||
]);
|
||||
} catch (err) {
|
||||
// 仅打印错误,不阻断路由
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
50
src/utils/rtfParser.js
Normal file
50
src/utils/rtfParser.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 从 RTF 字符串中提取图片并返回 Hex 数组
|
||||
*/
|
||||
export function extractHexImagesFromRTF(rtfData) {
|
||||
|
||||
const images = [];
|
||||
if (!rtfData) return images;
|
||||
|
||||
// 1. 按照图片起始符切分
|
||||
const blocks = rtfData.split('\\pict');
|
||||
|
||||
for (let i = 1; i < blocks.length; i++) {
|
||||
const block = blocks[i];
|
||||
|
||||
// 2. 识别格式
|
||||
let mimeType = '';
|
||||
if (block.includes('\\pngblip')) mimeType = 'image/png';
|
||||
else if (block.includes('\\jpegblip')) mimeType = 'image/jpeg';
|
||||
else continue;
|
||||
|
||||
// 3. 暴力匹配:寻找块中所有连续的 0-9a-f 字符(忽略空格和换行)
|
||||
// 我们找的是最短长度超过 128 位的字符串,这通常就是图片主体
|
||||
const allHexGroups = block.match(/[0-9a-fA-F\s\r\n]{128,}/g);
|
||||
|
||||
if (allHexGroups) {
|
||||
// 取这一组里最长的那段(防止误删中间的控制符)
|
||||
const longestHex = allHexGroups.sort((a, b) => b.length - a.length)[0];
|
||||
const cleanHex = longestHex.replace(/[\s\r\n]/g, '');
|
||||
|
||||
if (cleanHex.length > 256) { // 再次校验长度
|
||||
images.push({
|
||||
mimeType: mimeType,
|
||||
hex: cleanHex
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return images;
|
||||
}
|
||||
/**
|
||||
* 将 Hex 转换为 Blob
|
||||
*/
|
||||
export function hexToBlob(hexString, type) {
|
||||
|
||||
const bytes = new Uint8Array(hexString.length / 2);
|
||||
for (let i = 0; i < hexString.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hexString.substr(i, 2), 16);
|
||||
}
|
||||
return new Blob([bytes], { type });
|
||||
}
|
||||
@@ -76,8 +76,8 @@ module.exports = {
|
||||
// target: 'http://192.168.110.110/tougao/public/index.php/',
|
||||
// target: 'http://api.tmrjournals.com/public/index.php/',//正式
|
||||
// target: 'http://zmzm.tougao.dev.com/',//晓玲
|
||||
target: 'https://submission.tmrjournals.com/',//正式
|
||||
// target: 'http://tougaotest.tmrjournals.com/public/index.php/',//测试环境
|
||||
// target: 'https://submission.tmrjournals.com/',//正式
|
||||
target: 'http://tougaotest.tmrjournals.com/public/index.php/',//测试环境
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
|
||||
Reference in New Issue
Block a user