自动化配置增加青年编委模块
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: '/', //正式
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -20,8 +20,15 @@
|
||||
<template v-for="subItem in item.subs">
|
||||
<el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index + '-submenu'">
|
||||
<template slot="title">
|
||||
{{ subItem.title }}
|
||||
<!-- <el-badge is-dot :hidden="false">{{ subItem.title }}</el-badge> -->
|
||||
<el-badge
|
||||
v-if="subItem.index === '45'"
|
||||
is-dot
|
||||
:hidden="applyBadgeYouth <= 0"
|
||||
class="sidebar-submenu-apply-badge"
|
||||
>
|
||||
<span>{{ subItem.title }}</span>
|
||||
</el-badge>
|
||||
<template v-else>{{ subItem.title }}</template>
|
||||
</template>
|
||||
|
||||
<template v-for="(threeItem, i) in subItem.subs">
|
||||
@@ -35,8 +42,16 @@
|
||||
{{ fourItem.title }}
|
||||
</el-menu-item>
|
||||
</el-submenu>
|
||||
<el-menu-item v-else :index="threeItem.index" :key="threeItem.index + '-item'"
|
||||
>{{ threeItem.title }}
|
||||
<el-menu-item v-else :index="threeItem.index" :key="threeItem.index + '-item'">
|
||||
<el-badge
|
||||
v-if="threeItem.index === 'youthApplyList'"
|
||||
is-dot
|
||||
:hidden="applyBadgeYouth <= 0"
|
||||
class="sidebar-menu-youth-apply-badge"
|
||||
>
|
||||
<span class="sidebar-menu-youth-apply-badge__text">{{ threeItem.title }}</span>
|
||||
</el-badge>
|
||||
<template v-else>{{ threeItem.title }}</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-submenu>
|
||||
@@ -220,6 +235,8 @@ export default {
|
||||
user_cap: localStorage.getItem('U_role'),
|
||||
|
||||
menuList: [],
|
||||
/** 青年编委申请红点:Young Scientist 父级 + Apply 子项,数据来自 getYboardApplys */
|
||||
applyBadgeYouth: 0,
|
||||
items: [],
|
||||
// 作者
|
||||
author_items: [
|
||||
@@ -677,6 +694,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (String(this.userrole) === '1') {
|
||||
this.fetchApplyBadgeSummary();
|
||||
}
|
||||
// if(this.user_cap.includes(',board')||this.user_cap.includes('board_editor')||this.user_cap.includes('chief')||this.user_cap.includes('chief_editor')||this.user_cap.includes('deputy_editor')){
|
||||
// Promise.all([
|
||||
// this.$api
|
||||
@@ -845,10 +865,33 @@ export default {
|
||||
localStorage.setItem('collapse', this.collapse);
|
||||
bus.$emit('collapse-content', msg);
|
||||
});
|
||||
bus.$on('apply-badge-refresh', () => {
|
||||
this.fetchApplyBadgeSummary();
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
bus.$off('apply-badge-refresh');
|
||||
},
|
||||
methods: {
|
||||
// 获取数据
|
||||
getDate() {}
|
||||
getDate() {},
|
||||
fetchApplyBadgeSummary() {
|
||||
if (String(this.userrole) !== '1') return;
|
||||
const editorId = localStorage.getItem('U_id');
|
||||
if (!editorId) return;
|
||||
this.$api
|
||||
.post('api/User/getYboardApplys', { editor_id: editorId })
|
||||
.then((res) => {
|
||||
if (res && res.code === 0 && res.data && Array.isArray(res.data.applys)) {
|
||||
this.applyBadgeYouth = res.data.applys.length > 0 ? 1 : 0;
|
||||
} else {
|
||||
this.applyBadgeYouth = 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.applyBadgeYouth = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -922,4 +965,38 @@ export default {
|
||||
.linkBar:hover {
|
||||
background: #00527a;
|
||||
}
|
||||
|
||||
/* Young Scientist 父级标题红点 */
|
||||
.sidebar-submenu-apply-badge {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.sidebar-submenu-apply-badge ::v-deep .el-badge__content.is-dot {
|
||||
top: 28%;
|
||||
margin-top: 6px;
|
||||
right: -4px;
|
||||
border: 0;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Apply 子菜单行上的红点,与菜单行高对齐 */
|
||||
.sidebar-menu-youth-apply-badge {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.sidebar-menu-youth-apply-badge ::v-deep .el-badge__content.is-dot {
|
||||
top: 28%;
|
||||
margin-top: 6px;
|
||||
right: -4px;
|
||||
border: 0;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.sidebar-menu-youth-apply-badge__text {
|
||||
color: inherit;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
//记得切换
|
||||
|
||||
//正式
|
||||
// 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 = 'https://submission.tmrjournals.com/public/';
|
||||
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
||||
// const baseUrl = '/api'
|
||||
|
||||
//测试环境
|
||||
|
||||
|
||||
@@ -263,7 +263,39 @@
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" style="line-height: 120px"></i>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="Contact Editor WeChat QR Code :" prop="wechat_yboard_qrcode">
|
||||
<div class="wechat-name-split__right">
|
||||
<div class="yboard-qrcode-side__label"></div>
|
||||
<div class="portrait WeChatCode">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
ref="upIconIMgYboard"
|
||||
:action="baseUrl + 'api/Journal/uploadYboardQrcode'"
|
||||
:show-file-list="false"
|
||||
name="qrcode_url"
|
||||
:on-success="handleAvatarSuccessYboard"
|
||||
:on-error="handleAvatarErrorYboard"
|
||||
:before-upload="beforeAvatarUpload2"
|
||||
>
|
||||
<img
|
||||
v-if="detailForm.wechat_yboard_qrcode"
|
||||
:src="
|
||||
/^https?:\/\//.test(detailForm.wechat_yboard_qrcode)
|
||||
? detailForm.wechat_yboard_qrcode
|
||||
: mediaUrl + 'journalyboardqrcode/' + detailForm.wechat_yboard_qrcode
|
||||
"
|
||||
class="avatar"
|
||||
accept=".png,.jpg"
|
||||
/>
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon" style="line-height: 120px"></i>
|
||||
</el-upload>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Journal Topic :" prop="ResearchAreas">
|
||||
<el-button @click="addArea" size="mini" type="primary" plain style="position: absolute; right: 0; margin-left: 10px"
|
||||
>+ Add</el-button
|
||||
@@ -283,8 +315,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="Wechat Name :" prop="wechat_name">
|
||||
<el-form-item label="Wechat Name :" prop="wechat_name" class="wechat-name-split">
|
||||
<div class="wechat-name-split__inner">
|
||||
<div class="wechat-name-split__left">
|
||||
<el-input v-model="detailForm.wechat_name" placeholder="eg:公众号名称"></el-input>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="Wechat APP ID :" prop="wechat_app_id">
|
||||
<el-input v-model="detailForm.wechat_app_id" placeholder="eg:公众号app_id"></el-input>
|
||||
@@ -657,6 +694,17 @@ export default {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
},
|
||||
async handleAvatarSuccessYboard(res, file) {
|
||||
if (res.code == 0) {
|
||||
this.detailForm.wechat_yboard_qrcode = res.upurl;
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
},
|
||||
handleAvatarErrorYboard(res, file) {
|
||||
// no-op
|
||||
},
|
||||
handleAvatarError2(res, file) {
|
||||
// this.$message.error(res);
|
||||
},
|
||||
@@ -883,7 +931,9 @@ export default {
|
||||
epassword: data.epassword,
|
||||
kfen: data.kfen,
|
||||
fee: data.fee,
|
||||
databases: data.databases || data.database_inclusion || '',
|
||||
editor_qrcode: data.editor_qrcode,
|
||||
wechat_yboard_qrcode: data.wechat_yboard_qrcode || data.yboard_qrcode,
|
||||
scope: data.scope,
|
||||
abstract_chinese: data.abstract_chinese,
|
||||
publish_author: data.publish_author,
|
||||
@@ -1369,6 +1419,29 @@ export default {
|
||||
border-radius: 110px;
|
||||
}
|
||||
|
||||
/* Wechat Name + Apply Youth Board QR:两列对齐到右侧 */
|
||||
.wechat-name-split__inner {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.wechat-name-split__left {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.wechat-name-split__right {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.yboard-qrcode-side__label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.portrait .ptmark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
238
src/components/page/YouthBoardSubmitSuccess.vue
Normal file
238
src/components/page/YouthBoardSubmitSuccess.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="submission-page">
|
||||
<div class="container">
|
||||
<div class="success-icon">✔</div>
|
||||
<h2 class="success-title">Application submitted successfully!</h2>
|
||||
<p class="success-desc" v-if="isFromChina">
|
||||
We have a WeChat group for academic discussions. Please feel free to scan the QR code below to join.
|
||||
</p>
|
||||
<p class="success-desc" v-else>
|
||||
Your application is currently under review. We appreciate your patience, and our team will notify you of the final decision via email as soon as possible.
|
||||
</p>
|
||||
|
||||
<div v-if="isFromChina" class="qr-section">
|
||||
<div v-if="qrLoading" class="qr-loading">Loading...</div>
|
||||
<div v-else class="qr-code-box" v-if="qrCodeUrl">
|
||||
<img :src="qrCodeUrl" alt="WeChat Group QR" />
|
||||
</div>
|
||||
<!-- <p class="remark-tip">Please use the following format for group remark:</p>
|
||||
<div class="remark-box">Name - Research Field - Affiliation</div> -->
|
||||
</div>
|
||||
|
||||
<router-link to="/login" replace class="back-btn"> Login to the Submission System Now </router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'YouthBoardSubmitSuccess',
|
||||
data() {
|
||||
return {
|
||||
qrCodeUrl: '',
|
||||
qrLoading: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isFromChina() {
|
||||
const s = String(this.$route.query.country || '').trim();
|
||||
if (!s) return false;
|
||||
// 兼容:1 / China / 中国 / CN / CHN
|
||||
if (s === '1') return true;
|
||||
if (s === 'China' || s === 'CN' || s === 'CHN') return true;
|
||||
if (s === '中国') return true;
|
||||
if (s === 'US' || s === 'United States' || s === '美国') return false;
|
||||
return s === 'China';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.isFromChina) this.loadApplyBaseInfo();
|
||||
},
|
||||
methods: {
|
||||
normalizeCountry(v) {
|
||||
const s = String(v || '').trim();
|
||||
if (!s) return '';
|
||||
if (s === '中国' || s === 'CN' || s === 'CHN') return 'China';
|
||||
if (s === '美国' || s === 'USA' || s === 'US') return 'United States';
|
||||
if (s === '英国' || s === 'UK' || s === 'GBR') return 'United Kingdom';
|
||||
return s;
|
||||
},
|
||||
buildMediaUrl(raw) {
|
||||
if (!raw) return '';
|
||||
if (/^https?:\/\//i.test(raw)) return raw;
|
||||
const mediaBase = (this.Common && this.Common.mediaUrl ? this.Common.mediaUrl : '').replace(/\/+$/, '');
|
||||
if (!mediaBase) return raw;
|
||||
const cleanPath = String(raw).replace(/^\/+/, '');
|
||||
// 与后台上传目录保持一致(JournalManagement/common.vue)
|
||||
return `${mediaBase}/journalyboardqrcode/${cleanPath}`;
|
||||
},
|
||||
loadApplyBaseInfo() {
|
||||
const journal_id = this.$route.query.journal_id || this.$route.query.journalId;
|
||||
const expert_id = this.$route.query.expert_id || this.$route.query.expertId;
|
||||
if (!journal_id || !expert_id) {
|
||||
return;
|
||||
}
|
||||
this.qrLoading = true;
|
||||
|
||||
this.$api
|
||||
.post('api/Ucenter/getApplyYboardForExpertBaseInfo', {
|
||||
journal_id,
|
||||
expert_id
|
||||
})
|
||||
.then((res) => {
|
||||
const data = (res && res.data) || {};
|
||||
const journal = data.journal || data.journal_info || data.journalInfo || null;
|
||||
const raw =
|
||||
(journal && (journal.wechat_yboard_qrcode || journal.yboard_qrcode || journal.qrcode_url)) || '';
|
||||
this.qrCodeUrl = this.buildMediaUrl(raw);
|
||||
})
|
||||
.catch(() => {
|
||||
this.qrCodeUrl = '';
|
||||
})
|
||||
.finally(() => {
|
||||
this.qrLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.submission-page {
|
||||
--primary-blue: #3a91d9;
|
||||
--text-main: #2c3e50;
|
||||
--bg-light: #f7f9fc;
|
||||
--success-green: #48bb78;
|
||||
--danger: #e53e3e;
|
||||
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: var(--bg-light);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: #ffffff;
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
height: fit-content;
|
||||
padding: 60px 40px;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.04);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
font-size: 60px;
|
||||
color: var(--success-green);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.success-title {
|
||||
font-size: 24px;
|
||||
color: var(--text-main);
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.success-desc {
|
||||
font-size: 16px;
|
||||
color: #718096;
|
||||
margin-bottom: 40px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.qr-section {
|
||||
background-color: #f0f7ff;
|
||||
border: 1px solid #c3dafe;
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.qr-header {
|
||||
color: #2b6cb0;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.qr-code-box {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: #fff;
|
||||
margin: 15px auto;
|
||||
border: 1px solid #eee;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-code-box img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.remark-tip {
|
||||
font-size: 14px;
|
||||
color: #4a5568;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.remark-box {
|
||||
background: #fff;
|
||||
border: 1px solid #feb2b2;
|
||||
color: var(--danger);
|
||||
font-weight: bold;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
display: inline-block;
|
||||
margin-top: 40px;
|
||||
color: #006699;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.back-btn:hover {
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #718096;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.qr-loading {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: #fff;
|
||||
margin: 15px auto;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #718096;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
598
src/components/page/YouthEditorialBoardRegistration.vue
Normal file
598
src/components/page/YouthEditorialBoardRegistration.vue
Normal file
@@ -0,0 +1,598 @@
|
||||
<template>
|
||||
<div class="registration-container">
|
||||
<div class="card">
|
||||
<h1>Youth Editorial Board Registration</h1>
|
||||
<div v-if="journalInfoLoaded" class="journal-hero">
|
||||
<div class="journal-cover-wrap" v-if="journalCoverUrl">
|
||||
<img :src="journalCoverUrl" :alt="journalTitle || 'Journal Cover'" class="journal-cover" />
|
||||
</div>
|
||||
<div class="journal-title">{{ journalTitle || 'Journal Information' }}</div>
|
||||
</div>
|
||||
<!-- <p v-if="journalId && expertId" class="link-meta">
|
||||
Journal ID: <strong>{{ journalId }}</strong> · Expert ID: <strong>{{ expertId }}</strong>
|
||||
</p> -->
|
||||
<!-- <p v-else class="link-meta link-meta--warn">
|
||||
Missing <code>journal_id</code> and/or <code>expert_id</code> in the URL. Please use the full invitation link.
|
||||
</p> -->
|
||||
|
||||
<div v-if="submitSuccess" class="success-panel">
|
||||
<h2>Thank you</h2>
|
||||
<p>Your registration has been submitted. We will contact you by email.</p>
|
||||
</div>
|
||||
|
||||
<form v-else @submit.prevent="handleSubmit" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label><span class="required-star">*</span> English Name</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.engName"
|
||||
placeholder=""
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><span class="required-star">*</span> Email (QQ mail is not allowed)</label>
|
||||
<input type="email" v-model="formData.email" placeholder="" required autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><span class="required-star">*</span> Password</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.password"
|
||||
placeholder=""
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><span class="required-star">*</span> Upload CV</label>
|
||||
<div v-if="!selectedFile" class="upload-trigger" @click="$refs.fileInput.click()">
|
||||
<div class="icon">📤</div>
|
||||
<div class="hint">Click to upload PDF CV</div>
|
||||
<input type="file" ref="fileInput" hidden accept=".pdf" @change="onFileChange" />
|
||||
</div>
|
||||
<div v-else class="file-display-box">
|
||||
<span class="file-icon">📄</span>
|
||||
<span class="file-name">{{ selectedFile.name }}</span>
|
||||
<span class="remove-btn" @click.prevent="removeFile">×</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <transition name="fade">
|
||||
<div v-if="formData.country === 'China'" class="qr-area">
|
||||
<div class="qr-title">Mandatory for Youth Scientists in China</div>
|
||||
<div v-if="wechatQrUrl" class="qr-box">
|
||||
<img :src="wechatQrUrl" alt="Group QR Code" @error="onQrImgError" />
|
||||
</div>
|
||||
<p v-else class="qr-fallback">Please add image file <code>public/youth-board-wechat-qr.png</code> for the group QR code.</p>
|
||||
<p class="qr-hint">Please use the following format for the group remark:</p>
|
||||
<div class="format-tag">Name - Research Field - Affiliation</div>
|
||||
</div>
|
||||
</transition> -->
|
||||
|
||||
<button type="submit" class="submit-btn" :disabled="submitting">
|
||||
{{ submitting ? 'Submitting...' : 'Register Now' }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
|
||||
/** 公开邀请注册:后端实现 multipart 接口后填写此路径(与 Dashboard 青年编委申请字段对齐可再改) */
|
||||
const SUBMIT_URL = '/api/User/youthBoardInviteRegister';
|
||||
|
||||
export default {
|
||||
name: 'YouthEditorialBoardRegistration',
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
engName: '',
|
||||
email: '',
|
||||
country: '',
|
||||
password: ''
|
||||
},
|
||||
countryList: [],
|
||||
selectedFile: null,
|
||||
submitting: false,
|
||||
submitSuccess: false,
|
||||
wechatQrUrl: '',
|
||||
journalInfoLoaded: false,
|
||||
journalDetail: null,
|
||||
uploadedCvPath: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
journalId() {
|
||||
const q = this.$route.query || {};
|
||||
return String(q.journal_id || q.journalId || '').trim();
|
||||
},
|
||||
expertId() {
|
||||
const q = this.$route.query || {};
|
||||
return String(q.expert_id || q.expertId || '').trim();
|
||||
},
|
||||
journalTitle() {
|
||||
const j = this.journalDetail || {};
|
||||
return j.full_name || j.journal_name || j.title || j.abbr || '';
|
||||
},
|
||||
journalCoverUrl() {
|
||||
const j = this.journalDetail || {};
|
||||
const raw =
|
||||
j.journal_icon ||
|
||||
j.cover ||
|
||||
j.img ||
|
||||
j.image ||
|
||||
j.icon ||
|
||||
j.logo ||
|
||||
j.thumb ||
|
||||
j.picture ||
|
||||
j.photo ||
|
||||
j.journal_cover ||
|
||||
j.journal_img ||
|
||||
'';
|
||||
if (!raw) return '';
|
||||
if (/^https?:\/\//i.test(raw)) return raw;
|
||||
const mediaBase = (this.Common.mediaUrl || '').replace(/\/+$/, '');
|
||||
const cleanPath = String(raw).replace(/^\/+/, '');
|
||||
if (!mediaBase) return `/${cleanPath}`;
|
||||
if (/^journal\//i.test(cleanPath) || /^reviewer\//i.test(cleanPath)) {
|
||||
return `${mediaBase}/${cleanPath}`;
|
||||
}
|
||||
return `${mediaBase}/journal/${cleanPath}`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const base = process.env.BASE_URL || '/';
|
||||
const prefix = base.endsWith('/') ? base : `${base}/`;
|
||||
this.wechatQrUrl = `${prefix}youth-board-wechat-qr.png`;
|
||||
this.fetchCountries();
|
||||
this.fetchApplyBaseInfo();
|
||||
},
|
||||
methods: {
|
||||
countryOptionKey(item) {
|
||||
return item.name || item.id || item.country || JSON.stringify(item);
|
||||
},
|
||||
countryOptionValue(item) {
|
||||
return item.name != null ? item.name : item.country || item.title || '';
|
||||
},
|
||||
countryOptionLabel(item) {
|
||||
return item.name != null ? item.name : item.country || item.title || '';
|
||||
},
|
||||
fetchCountries() {
|
||||
// this.$api
|
||||
// .post('api/Reviewer/getCountrys')
|
||||
// .then((res) => {
|
||||
// const list = res.countrys || res.data || [];
|
||||
// this.countryList = Array.isArray(list) ? list : [];
|
||||
// })
|
||||
// .catch(() => {
|
||||
// this.countryList = [{ name: 'China' }, { name: 'United States' }, { name: 'United Kingdom' }];
|
||||
// });
|
||||
},
|
||||
fetchApplyBaseInfo() {
|
||||
if (!this.journalId || !this.expertId) return;
|
||||
this.$api
|
||||
.post('api/Ucenter/getApplyYboardForExpertBaseInfo', {
|
||||
journal_id: this.journalId,
|
||||
expert_id: this.expertId
|
||||
})
|
||||
.then((res) => {
|
||||
const data = (res && res.data) || {};
|
||||
const expertCandidate =
|
||||
data.expert_info ||{}
|
||||
|
||||
const journal =
|
||||
data.journal ||
|
||||
data.journal_info ||
|
||||
data.journalInfo ||
|
||||
data.journal_detail ||
|
||||
data.journalDetail ||
|
||||
null;
|
||||
|
||||
// 为了兼容后端返回字段名,这里做多字段兜底取值
|
||||
const pickFirst = (obj, keys) => {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const k = keys[i];
|
||||
if (obj && obj[k] !== undefined && obj[k] !== null && String(obj[k]).trim() !== '') {
|
||||
return obj[k];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const pickFromCandidateOrData = (keys) => {
|
||||
const v1 = pickFirst(expertCandidate, keys);
|
||||
if (v1) return v1;
|
||||
return pickFirst(data, keys);
|
||||
};
|
||||
|
||||
const normalizeCountry = (v) => {
|
||||
const s = String(v || '').trim();
|
||||
if (!s) return '';
|
||||
// 常见中文/英文归一化(确保和下拉 options.value 匹配)
|
||||
if (s === '中国' || s === 'CN' || s === 'CHN') return 'China';
|
||||
if (s === '美国' || s === 'USA' || s === 'US') return 'United States';
|
||||
if (s === '英国' || s === 'UK' || s === 'GBR') return 'United Kingdom';
|
||||
return s;
|
||||
};
|
||||
|
||||
this.formData.engName = pickFromCandidateOrData([
|
||||
'eng_name',
|
||||
'english_name',
|
||||
'u_eng_name',
|
||||
'U_eng_name',
|
||||
'U_engName',
|
||||
'U_name_en',
|
||||
'realname',
|
||||
'u_relname',
|
||||
'U_relname',
|
||||
'name',
|
||||
'U_name'
|
||||
]);
|
||||
|
||||
this.formData.email = pickFromCandidateOrData([
|
||||
'email',
|
||||
'u_email',
|
||||
'U_email',
|
||||
'U_email_address',
|
||||
'user_email',
|
||||
'username',
|
||||
'U_username'
|
||||
]);
|
||||
|
||||
this.formData.country = normalizeCountry(
|
||||
pickFromCandidateOrData([
|
||||
'country',
|
||||
'country_name',
|
||||
'u_country',
|
||||
'U_country',
|
||||
'countryName',
|
||||
'U_country_name'
|
||||
])
|
||||
);
|
||||
|
||||
if (journal) {
|
||||
this.journalDetail = journal;
|
||||
this.journalInfoLoaded = true;
|
||||
}
|
||||
if (!journal) {
|
||||
this.journalDetail = null;
|
||||
this.journalInfoLoaded = true;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.journalDetail = null;
|
||||
this.journalInfoLoaded = true;
|
||||
});
|
||||
},
|
||||
async onFileChange(e) {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
if (file && file.type === 'application/pdf') {
|
||||
this.selectedFile = file;
|
||||
const fd = new FormData();
|
||||
fd.append('reviewerCV', file);
|
||||
try {
|
||||
const resp = await axios.post('/api/api/Ucenter/up_cv_file', fd, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
});
|
||||
const body = resp && resp.data ? resp.data : {};
|
||||
if (Number(body.code) === 0 && body.upurl) {
|
||||
this.uploadedCvPath = body.upurl;
|
||||
} else {
|
||||
this.selectedFile = null;
|
||||
this.uploadedCvPath = '';
|
||||
this.alertError((body && body.msg) || 'CV upload failed.');
|
||||
}
|
||||
} catch (err) {
|
||||
this.selectedFile = null;
|
||||
this.uploadedCvPath = '';
|
||||
this.alertError('CV upload failed. Please try again.');
|
||||
}
|
||||
} else if (file) {
|
||||
this.alertError('Please upload a valid PDF file.');
|
||||
}
|
||||
},
|
||||
removeFile() {
|
||||
this.selectedFile = null;
|
||||
this.uploadedCvPath = '';
|
||||
if (this.$refs.fileInput) this.$refs.fileInput.value = '';
|
||||
},
|
||||
onQrImgError() {
|
||||
this.wechatQrUrl = '';
|
||||
},
|
||||
alertError(msg) {
|
||||
this.$message.error(msg);
|
||||
},
|
||||
async handleSubmit() {
|
||||
if (!this.journalId || !this.expertId) {
|
||||
this.alertError('Invalid link: journal_id and expert_id are required in the URL.');
|
||||
return;
|
||||
}
|
||||
|
||||
const email = (this.formData.email || '').toLowerCase();
|
||||
if (email.endsWith('@qq.com')) {
|
||||
this.alertError('Registration failed: QQ email addresses are not accepted.');
|
||||
return;
|
||||
}
|
||||
if (!this.formData.password) {
|
||||
this.alertError('Please enter your password.');
|
||||
return;
|
||||
}
|
||||
if (!this.selectedFile) {
|
||||
this.alertError('Please upload your CV.');
|
||||
return;
|
||||
}
|
||||
if (!this.uploadedCvPath) {
|
||||
this.alertError('CV upload is not complete. Please upload again.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitting = true;
|
||||
this.$api
|
||||
.post('api/Ucenter/submitApplyYboardForExpert', {
|
||||
journal_id: this.journalId,
|
||||
expert_id: this.expertId,
|
||||
name: this.formData.engName,
|
||||
email: this.formData.email,
|
||||
cv: this.uploadedCvPath,
|
||||
password: this.formData.password
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.code == 0) {
|
||||
this.$router.replace({
|
||||
path: '/youthBoardSubmitSuccess',
|
||||
query: {
|
||||
country: this.formData.country == 'China' ? '1' : '0',
|
||||
journal_id: this.journalId,
|
||||
expert_id: this.expertId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.alertError((res && res.msg) || 'Submission failed.');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.alertError((err && err.msg) || 'Submission failed.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitting = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.registration-container {
|
||||
--primary: #3a91d9;
|
||||
--text: #2c3e50;
|
||||
--border: #e2e8f0;
|
||||
--danger: #e53e3e;
|
||||
--bg: #f7f9fc;
|
||||
|
||||
min-height: 100vh;
|
||||
background-color: var(--bg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
width: 100%;
|
||||
max-width: 520px;
|
||||
padding: 40px;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.journal-hero {
|
||||
text-align: center;
|
||||
margin: 6px 0 18px;
|
||||
}
|
||||
|
||||
.journal-cover-wrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 112px;
|
||||
height: 150px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.journal-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.journal-title {
|
||||
margin-top: 10px;
|
||||
color: #1d4f8c;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: var(--text);
|
||||
font-size: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.link-meta {
|
||||
font-size: 13px;
|
||||
color: #4a5568;
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.link-meta--warn {
|
||||
color: #c05621;
|
||||
}
|
||||
|
||||
.success-panel {
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.success-panel h2 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.required-star {
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload-trigger {
|
||||
border: 1px dashed #cbd5e0;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
}
|
||||
.upload-trigger:hover {
|
||||
background: #f8fafc;
|
||||
}
|
||||
.upload-trigger .icon {
|
||||
font-size: 28px;
|
||||
}
|
||||
.upload-trigger .hint {
|
||||
font-size: 14px;
|
||||
color: #718096;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.file-display-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.file-name {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.remove-btn {
|
||||
color: var(--danger);
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.qr-area {
|
||||
background-color: #f0f7ff;
|
||||
border: 1px solid #dbeafe;
|
||||
border-radius: 16px;
|
||||
padding: 25px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.qr-title {
|
||||
color: #2b6cb0;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.qr-box {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background: #fff;
|
||||
margin: 0 auto 15px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.qr-box img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.qr-fallback {
|
||||
font-size: 13px;
|
||||
color: #718096;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.qr-hint {
|
||||
font-size: 13px;
|
||||
color: #4a5568;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.format-tag {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #feb2b2;
|
||||
border-radius: 8px;
|
||||
color: var(--danger);
|
||||
font-weight: bold;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #4da1e6 0%, #3588d1 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.submit-btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -187,6 +187,7 @@
|
||||
|
||||
<script>
|
||||
import commonCv from '../common/cv.vue';
|
||||
import bus from '../common/bus';
|
||||
export default {
|
||||
components: {
|
||||
commonCv
|
||||
@@ -362,6 +363,7 @@ export default {
|
||||
for (var i = 0; i < this.tableData.length; i++) {
|
||||
this.getScoreData(i, this.tableData[i].score);
|
||||
}
|
||||
bus.$emit('apply-badge-refresh');
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ const i18n = new VueI18n({
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const currentRoute = to; // 获取当前路由路径,例如 "/home"
|
||||
|
||||
if (currentRoute.meta.hideJournal) {
|
||||
if (currentRoute.meta.hideJournal || currentRoute.meta.public) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
@@ -239,11 +239,11 @@ router.beforeEach(async (to, from, next) => {
|
||||
|
||||
|
||||
// 无论接口成功/失败,都执行原有跳转逻辑
|
||||
document.title = `${to.meta.title} | Traditional Medicine Research`;
|
||||
document.title = `${to.meta.title || 'TMR'} | Traditional Medicine Research`;
|
||||
const role = localStorage.getItem('U_name');
|
||||
const userrole = localStorage.getItem('U_status');
|
||||
|
||||
if (!role && to.path != '/register' && to.path !== '/submission' && to.path !== '/verification' && to.path !== '/orcidLink' && to.path !== '/img' && to.path !== '/reviewer' && to.path !== '/thanks' && to.path !== '/login' && to.path !== '/refuse' && to.path !== '/managing' && to.path.search(/retrieve/i) < 0) {
|
||||
if (!role && to.meta.public !== true && to.path != '/register' && to.path !== '/submission' && to.path !== '/verification' && to.path !== '/orcidLink' && to.path !== '/img' && to.path !== '/reviewer' && to.path !== '/thanks' && to.path !== '/login' && to.path !== '/refuse' && to.path !== '/managing' && to.path.search(/retrieve/i) < 0) {
|
||||
next('/login');
|
||||
} else {
|
||||
if (navigator.userAgent.indexOf('MSIE') > -1 && to.path === '/editor') {
|
||||
|
||||
@@ -1477,6 +1477,24 @@ export default new Router({
|
||||
title: 'img'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/youthBoardRegister',
|
||||
component: () => import( /* webpackChunkName: "youthBoardRegister" */ '../components/page/YouthEditorialBoardRegistration.vue'),
|
||||
meta: {
|
||||
title: 'Youth Editorial Board Registration',
|
||||
public: true,
|
||||
hideJournal: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/youthBoardSubmitSuccess',
|
||||
component: () => import( /* webpackChunkName: "youthBoardSubmitSuccess" */ '../components/page/YouthBoardSubmitSuccess.vue'),
|
||||
meta: {
|
||||
title: 'Submission Success',
|
||||
public: true,
|
||||
hideJournal: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
redirect: '/404'
|
||||
|
||||
Reference in New Issue
Block a user