feat: 上传文件接口&表单成功提交页面
This commit is contained in:
3
src/assets/images/award/arrow_down.svg
Normal file
3
src/assets/images/award/arrow_down.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="22" height="12" viewBox="0 0 22 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 1L11 11L21 1" stroke="#585858" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 212 B |
BIN
src/assets/images/award/form_bg.png
Normal file
BIN
src/assets/images/award/form_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 930 KiB |
@@ -2,18 +2,17 @@ import axios from 'axios'
|
|||||||
// import qs from 'qs'
|
// import qs from 'qs'
|
||||||
// import message from '@/components/public/message/src'
|
// import message from '@/components/public/message/src'
|
||||||
import router from '@/router/index'
|
import router from '@/router/index'
|
||||||
import {getCookie,clonAllCookie} from '@/tool/cookie'
|
import { getCookie, clonAllCookie } from '@/tool/cookie'
|
||||||
// import cookie from '@/tools/cookie.js'
|
// import cookie from '@/tools/cookie.js'
|
||||||
|
|
||||||
axios.defaults.timeout = 60000; //响应时间
|
axios.defaults.timeout = 60000 //响应时间
|
||||||
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; //配置请求头
|
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; //配置请求头
|
||||||
axios.defaults.headers.post["Content-Type"] = "application/json";
|
axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||||
|
|
||||||
|
axios.defaults.headers.post['lang'] = 'en' //配置语言请求头
|
||||||
axios.defaults.headers.post['lang'] = 'en'; //配置语言请求头
|
axios.defaults.withCredentials = true //跨域携带cookie
|
||||||
axios.defaults.withCredentials = true; //跨域携带cookie
|
import { message } from 'ant-design-vue'
|
||||||
import { message } from 'ant-design-vue';
|
import store from '@/store'
|
||||||
import store from '@/store';
|
|
||||||
// if(import.meta.env.VITE_USER_NODE_ENV == "development"){
|
// if(import.meta.env.VITE_USER_NODE_ENV == "development"){
|
||||||
// axios.defaults.baseURL = ""; //配置接口地址
|
// axios.defaults.baseURL = ""; //配置接口地址
|
||||||
// }else{
|
// }else{
|
||||||
@@ -25,110 +24,128 @@ import store from '@/store';
|
|||||||
// }else{
|
// }else{
|
||||||
// httpIp = ''
|
// httpIp = ''
|
||||||
// }
|
// }
|
||||||
let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? "" : "";
|
let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? '' : ''
|
||||||
// let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? "https://192.168.1.8:10086" : "";
|
// let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? "https://192.168.1.8:10086" : "";
|
||||||
|
|
||||||
axios.defaults.baseURL = httpIp; //配置接口地址
|
axios.defaults.baseURL = httpIp //配置接口地址
|
||||||
// console.log(axios.defaults.baseURL);
|
// console.log(axios.defaults.baseURL);
|
||||||
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_URL; //配置接口地址
|
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_URL //配置接口地址
|
||||||
console.log(import.meta.env.VITE_APP_BASE_URL)
|
console.log(import.meta.env.VITE_APP_BASE_URL)
|
||||||
|
|
||||||
// 创建取消令牌
|
// 创建取消令牌
|
||||||
const CancelToken = axios.CancelToken;
|
const CancelToken = axios.CancelToken
|
||||||
const source = CancelToken.source();
|
const source = CancelToken.source()
|
||||||
// console.log(import.meta.env.VITE_APP_BASE_URL);
|
// console.log(import.meta.env.VITE_APP_BASE_URL);
|
||||||
let isLoginTime = false
|
let isLoginTime = false
|
||||||
//POST传参序列化(添加请求拦截器)
|
//POST传参序列化(添加请求拦截器)
|
||||||
axios.interceptors.request.use((config) => {
|
axios.interceptors.request.use(
|
||||||
//在发送请求之前做某件事
|
config => {
|
||||||
|
//在发送请求之前做某件事
|
||||||
|
|
||||||
|
// config.cancelToken = source.token
|
||||||
|
if (
|
||||||
// config.cancelToken = source.token
|
config.method === 'post' ||
|
||||||
if(config.method === 'post' || config.method === 'put' || config.method === 'delete'){
|
config.method === 'put' ||
|
||||||
// config.data = qs.stringify(config.data);
|
config.method === 'delete'
|
||||||
// config.data = JSON.stringify(config.data);
|
) {
|
||||||
}
|
// config.data = qs.stringify(config.data);
|
||||||
// config.headers.Authorization = 'Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA';
|
// config.data = JSON.stringify(config.data);
|
||||||
config.headers.Authorization = getCookie('token');
|
}
|
||||||
return config;
|
// config.headers.Authorization = 'Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA';
|
||||||
},(error) =>{
|
config.headers.Authorization = getCookie('token')
|
||||||
return Promise.reject(error);
|
return config
|
||||||
});
|
},
|
||||||
const binaryToUrl = (binary,type = 'application/json',res)=>{
|
error => {
|
||||||
let blob = new Blob([binary], {'content-type':type});
|
return Promise.reject(error)
|
||||||
let url = URL.createObjectURL(blob);
|
}
|
||||||
|
)
|
||||||
|
const binaryToUrl = (binary, type = 'application/json', res) => {
|
||||||
|
let blob = new Blob([binary], { 'content-type': type })
|
||||||
|
let url = URL.createObjectURL(blob)
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
//返回状态判断(添加响应拦截器)
|
//返回状态判断(添加响应拦截器)
|
||||||
axios.interceptors.response.use((res) =>{
|
axios.interceptors.response.use(
|
||||||
// if(res.data.data == null){
|
res => {
|
||||||
// message.warning(res.data.errMsg)
|
// 允许透传完整响应:请求时传 config.fullData = true
|
||||||
// return Promise.reject(res.data);
|
|
||||||
// }else
|
|
||||||
if(res?.config?.env?.binary){
|
|
||||||
let url = binaryToUrl(res.data,res.config.env.binaryType,res)
|
|
||||||
return Promise.resolve({url,data:res.data})
|
|
||||||
}
|
|
||||||
if (res?.data) {
|
|
||||||
if (res?.data?.errCode === 0) {
|
|
||||||
// message.error(res?.data?.errMsg)
|
|
||||||
return Promise.resolve(res?.data?.data);
|
|
||||||
} else if(res?.data?.errCode === 1){
|
|
||||||
message.warning(res?.data?.errMsg)
|
|
||||||
return Promise.reject(res?.data);
|
|
||||||
} else if(res?.data?.errCode === 2){
|
|
||||||
return Promise.reject(res?.data);
|
|
||||||
}else if(res?.data?.errCode === -1){
|
|
||||||
message.error(res?.data?.errMsg)
|
|
||||||
return Promise.reject(res?.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
// if(res.data.data == null){
|
||||||
if (res?.data?.errCode === 0) {
|
// message.warning(res.data.errMsg)
|
||||||
message.warning(res?.data?.errMsg)
|
// return Promise.reject(res.data);
|
||||||
return Promise.reject(res?.data);
|
// }else
|
||||||
} else if(res?.data?.errCode === 1){
|
if (res?.config?.env?.binary) {
|
||||||
message.warning(res?.data?.errMsg)
|
let url = binaryToUrl(res.data, res.config.env.binaryType, res)
|
||||||
return Promise.reject(res?.data);
|
return Promise.resolve({ url, data: res.data })
|
||||||
} else if(res?.data?.errCode === 2){
|
|
||||||
return Promise.reject(res?.data);
|
|
||||||
}else if(res?.data?.errCode === -1){
|
|
||||||
message.error(res?.data?.errMsg)
|
|
||||||
return Promise.reject(res?.data);
|
|
||||||
}
|
}
|
||||||
}
|
if (res?.data) {
|
||||||
}, function(error) {
|
if (res?.data?.errCode === 0) {
|
||||||
if(error?.response?.status === 401 && router.currentRoute._value.name != 'setIdentification'){//如果是记录浏览器页面就不跳转login
|
// message.error(res?.data?.errMsg)
|
||||||
clonAllCookie()
|
if (res?.config?.fullData) {
|
||||||
if(!isLoginTime){
|
return Promise.resolve(res.data)
|
||||||
isLoginTime = true
|
|
||||||
let isSystemUserRouteList = ['/Square']//如果是这两个页面就无需跳转未登录页
|
|
||||||
let sSystemUser = false
|
|
||||||
for (let index = 0; index < isSystemUserRouteList.length; index++) {
|
|
||||||
if(router.currentRoute.value.path.indexOf(isSystemUserRouteList[index]) > -1){
|
|
||||||
sSystemUser = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return Promise.resolve(res?.data?.data)
|
||||||
|
} else if (res?.data?.errCode === 1) {
|
||||||
|
message.warning(res?.data?.errMsg)
|
||||||
|
return Promise.reject(res?.data)
|
||||||
|
} else if (res?.data?.errCode === 2) {
|
||||||
|
return Promise.reject(res?.data)
|
||||||
|
} else if (res?.data?.errCode === -1) {
|
||||||
|
message.error(res?.data?.errMsg)
|
||||||
|
return Promise.reject(res?.data)
|
||||||
}
|
}
|
||||||
if(!sSystemUser){
|
} else {
|
||||||
router.replace('/')
|
if (res?.data?.errCode === 0) {
|
||||||
|
message.warning(res?.data?.errMsg)
|
||||||
|
return Promise.reject(res?.data)
|
||||||
|
} else if (res?.data?.errCode === 1) {
|
||||||
|
message.warning(res?.data?.errMsg)
|
||||||
|
return Promise.reject(res?.data)
|
||||||
|
} else if (res?.data?.errCode === 2) {
|
||||||
|
return Promise.reject(res?.data)
|
||||||
|
} else if (res?.data?.errCode === -1) {
|
||||||
|
message.error(res?.data?.errMsg)
|
||||||
|
return Promise.reject(res?.data)
|
||||||
}
|
}
|
||||||
message.warning('Please login and try again~')
|
|
||||||
store.commit('createDetail')
|
|
||||||
store.commit('createProbject')
|
|
||||||
store.commit('createProjectPath')
|
|
||||||
setTimeout(()=>[
|
|
||||||
isLoginTime = false
|
|
||||||
],2000)
|
|
||||||
}
|
}
|
||||||
// source.cancel('取消后续接口调用');
|
},
|
||||||
return Promise.reject()
|
function (error) {
|
||||||
}
|
if (
|
||||||
let data_new = error?.response?.data
|
error?.response?.status === 401 &&
|
||||||
// message.error(data_new?.errMsg || 'Error: server exception')
|
router.currentRoute._value.name != 'setIdentification'
|
||||||
return Promise.reject(data_new);
|
) {
|
||||||
});
|
//如果是记录浏览器页面就不跳转login
|
||||||
|
clonAllCookie()
|
||||||
|
if (!isLoginTime) {
|
||||||
|
isLoginTime = true
|
||||||
|
let isSystemUserRouteList = ['/Square'] //如果是这两个页面就无需跳转未登录页
|
||||||
|
let sSystemUser = false
|
||||||
|
for (let index = 0; index < isSystemUserRouteList.length; index++) {
|
||||||
|
if (
|
||||||
|
router.currentRoute.value.path.indexOf(
|
||||||
|
isSystemUserRouteList[index]
|
||||||
|
) > -1
|
||||||
|
) {
|
||||||
|
sSystemUser = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sSystemUser) {
|
||||||
|
router.replace('/')
|
||||||
|
}
|
||||||
|
message.warning('Please login and try again~')
|
||||||
|
store.commit('createDetail')
|
||||||
|
store.commit('createProbject')
|
||||||
|
store.commit('createProjectPath')
|
||||||
|
setTimeout(() => [(isLoginTime = false)], 2000)
|
||||||
|
}
|
||||||
|
// source.cancel('取消后续接口调用');
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
let data_new = error?.response?.data
|
||||||
|
// message.error(data_new?.errMsg || 'Error: server exception')
|
||||||
|
return Promise.reject(data_new)
|
||||||
|
}
|
||||||
|
)
|
||||||
export const Https = {
|
export const Https = {
|
||||||
httpUrls: {
|
httpUrls: {
|
||||||
interfaceUrl: '',
|
interfaceUrl: '',
|
||||||
@@ -447,9 +464,15 @@ export const Https = {
|
|||||||
segAnything: `/api/python/segAnything`, //分割Anything
|
segAnything: `/api/python/segAnything`, //分割Anything
|
||||||
|
|
||||||
// award页面
|
// award页面
|
||||||
uploadPDF: '/api/global-award/uploads/pdf', // 上传pdf
|
checkEmail: '/api/global-award/checkEmail', // 检查邮箱是否存在
|
||||||
uploadVideo: '/api/global-award/uploads/video', // 上传video
|
checkOTP: '/api/global-award/checkCode', // 检查验证码是否正确
|
||||||
|
initPdfUpload: '/api/global-award/uploads/pdf/init', // 初始化pdf上传
|
||||||
|
initVideoUpload: '/api/global-award/uploads/video/init', // 初始化video上传
|
||||||
|
uploadPDF: '/api/global-award/uploads/pdf/chunk', // 上传pdf
|
||||||
|
uploadVideo: '/api/global-award/uploads/video/chunk', // 上传video
|
||||||
|
uploadPDFComplete: '/api/global-award/uploads/pdf/complete', // 上传pdf完成
|
||||||
|
uploadVideoComplete: '/api/global-award/uploads/video/complete', // 上传video完成
|
||||||
|
submitForm: '/api/global-award/contestants/save' // 提交表单
|
||||||
},
|
},
|
||||||
|
|
||||||
axiosGet(url, config) {
|
axiosGet(url, config) {
|
||||||
|
|||||||
@@ -5,12 +5,19 @@
|
|||||||
BLOOM YOUR CREATIVITY • AiDA GLOBAL FASHION AWARD 2026
|
BLOOM YOUR CREATIVITY • AiDA GLOBAL FASHION AWARD 2026
|
||||||
</div>
|
</div>
|
||||||
<div class="title poppins-medium">Application Form</div>
|
<div class="title poppins-medium">Application Form</div>
|
||||||
<div class="form-header">
|
<div
|
||||||
|
class="form-header"
|
||||||
|
v-if="!isCompleted"
|
||||||
|
>
|
||||||
<div class="form-title poppins-bold">Email Verification</div>
|
<div class="form-title poppins-bold">Email Verification</div>
|
||||||
<div class="desc">AiDA Users Only</div>
|
<div class="desc">AiDA Users Only</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-container">
|
<Success v-if="isCompleted" />
|
||||||
|
<div
|
||||||
|
class="form-container"
|
||||||
|
v-if="!isCompleted"
|
||||||
|
>
|
||||||
<div class="form-content">
|
<div class="form-content">
|
||||||
<a-form
|
<a-form
|
||||||
name="form"
|
name="form"
|
||||||
@@ -66,7 +73,22 @@
|
|||||||
:label="item.label"
|
:label="item.label"
|
||||||
:name="item.key"
|
:name="item.key"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="form[item.key]" />
|
<a-input
|
||||||
|
v-if="item.type === 'input'"
|
||||||
|
v-model:value="form[item.key]"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
v-if="item.type === 'select'"
|
||||||
|
v-model:value="form[item.key]"
|
||||||
|
:options="genderOptions"
|
||||||
|
>
|
||||||
|
<template #suffixIcon>
|
||||||
|
<img
|
||||||
|
class="arrow-down-icon"
|
||||||
|
src="@/assets/images/award/arrow_down.svg"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,13 +106,13 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
class="full-row design-desc"
|
class="full-row design-desc"
|
||||||
name="description"
|
name="designDescription"
|
||||||
label="Design description"
|
label="Design description"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
class="textarea"
|
class="textarea"
|
||||||
v-model:value="form.description"
|
v-model:value="form.designDescription"
|
||||||
placeholder="Briefly describe your design concept, inspiration, and creative direction..."
|
placeholder="Briefly describe your design concept, inspiration, and creative direction..."
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -307,7 +329,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onUnmounted } from 'vue'
|
import { ref, onUnmounted } from 'vue'
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import type { Rule } from 'ant-design-vue/es/form'
|
import type { Rule } from 'ant-design-vue/es/form'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
@@ -315,8 +337,11 @@
|
|||||||
import VerifycationCodeInput from './components/VerificationCodeInput.vue'
|
import VerifycationCodeInput from './components/VerificationCodeInput.vue'
|
||||||
import { Https } from '@/tool/https'
|
import { Https } from '@/tool/https'
|
||||||
import UploadStatus from './components/UploadStatus.vue'
|
import UploadStatus from './components/UploadStatus.vue'
|
||||||
|
import Success from './components/Success.vue'
|
||||||
|
|
||||||
const hasValidEmail = ref(true)
|
const isCompleted = ref(false)
|
||||||
|
|
||||||
|
const hasValidEmail = ref(false)
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
const form = ref({
|
const form = ref({
|
||||||
email: '',
|
email: '',
|
||||||
@@ -325,14 +350,15 @@
|
|||||||
gender: '',
|
gender: '',
|
||||||
occupation: '',
|
occupation: '',
|
||||||
age: '',
|
age: '',
|
||||||
counterOrRegion: '',
|
countryRegionCity: '',
|
||||||
phone: '',
|
phoneNumber: '',
|
||||||
portfoilo: '',
|
portfolioUrl: '',
|
||||||
// code: '',
|
// code: '',
|
||||||
designTitle: '',
|
designTitle: '',
|
||||||
description: '',
|
designDescription: '',
|
||||||
pdfPath: null,
|
pdfPath: 'test.pdf',
|
||||||
videoPath: null
|
videoPath: 'test.video',
|
||||||
|
secureToken: null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 验证码输入组件引用
|
// 验证码输入组件引用
|
||||||
@@ -393,6 +419,20 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const genderOptions = [
|
||||||
|
{
|
||||||
|
label: 'Male',
|
||||||
|
value: 'Male'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Female',
|
||||||
|
value: 'Female'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Other',
|
||||||
|
value: 'Other'
|
||||||
|
}
|
||||||
|
]
|
||||||
const formKeys = ref([
|
const formKeys = ref([
|
||||||
{
|
{
|
||||||
label: 'First Name',
|
label: 'First Name',
|
||||||
@@ -428,20 +468,20 @@
|
|||||||
label: 'Country/Region and City',
|
label: 'Country/Region and City',
|
||||||
required: true,
|
required: true,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
key: 'counterOrRegion'
|
key: 'countryRegionCity'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Phone Number',
|
label: 'Phone Number',
|
||||||
required: true,
|
required: true,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
key: 'phone'
|
key: 'phoneNumber'
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: 'Portfoilo Website/Instagram(Optional)',
|
label: 'Portfoilo Website/Instagram(Optional)',
|
||||||
required: false,
|
required: false,
|
||||||
type: 'input',
|
type: 'input',
|
||||||
key: 'portfoilo'
|
key: 'portfolioUrl'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -488,11 +528,13 @@
|
|||||||
try {
|
try {
|
||||||
await formRef.value.validateFields(['email'])
|
await formRef.value.validateFields(['email'])
|
||||||
// TODO: 发送验证码的逻辑
|
// TODO: 发送验证码的逻辑
|
||||||
message.success('Verification code sent successfully!')
|
await Https.axiosGet(Https.httpUrls.checkEmail, {
|
||||||
|
params: {
|
||||||
|
email: form.value.email
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 开始倒计时
|
|
||||||
startCountdown()
|
startCountdown()
|
||||||
|
|
||||||
showModal.value = true
|
showModal.value = true
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, 300)
|
}, 300)
|
||||||
@@ -506,6 +548,26 @@
|
|||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
showModal.value = false
|
showModal.value = false
|
||||||
}
|
}
|
||||||
|
const handleVerifyCode = () => {
|
||||||
|
console.log(verifyCode.value)
|
||||||
|
|
||||||
|
if (verifyCode.value.length !== 6) {
|
||||||
|
message.error('Please enter the complete 6-digit verification code')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Https.axiosGet(Https.httpUrls.checkOTP, {
|
||||||
|
params: {
|
||||||
|
email: form.value.email,
|
||||||
|
code: verifyCode.value
|
||||||
|
},
|
||||||
|
fullData: true
|
||||||
|
}).then(res => {
|
||||||
|
form.value.secureToken = res.data.secureToken
|
||||||
|
|
||||||
|
message.success('Verification successful!')
|
||||||
|
showModal.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmitForm = () => {
|
const handleSubmitForm = () => {
|
||||||
const validCondition = conditionsList.value.filter(
|
const validCondition = conditionsList.value.filter(
|
||||||
@@ -515,24 +577,16 @@
|
|||||||
message.error('Please check the terms and conditions')
|
message.error('Please check the terms and conditions')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
formRef.value.validate().then(res => {
|
formRef.value
|
||||||
console.log(res)
|
.validate()
|
||||||
}).catch(err => {
|
.then(res => {
|
||||||
console.log(err)
|
console.log(res)
|
||||||
message.error('Please fill in all the required fields')
|
Https.axiosPost(Https.httpUrls.submitForm, form.value)
|
||||||
})
|
})
|
||||||
}
|
.catch(err => {
|
||||||
|
console.log(err)
|
||||||
const handleVerifyCode = () => {
|
message.error('Please fill in all the required fields')
|
||||||
if (verifyCode.value.length !== 6) {
|
})
|
||||||
message.error('Please enter the complete 6-digit verification code')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
message.success('Verification successful!')
|
|
||||||
|
|
||||||
// 关闭模态框
|
|
||||||
showModal.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pdfList = ref([])
|
const pdfList = ref([])
|
||||||
@@ -544,6 +598,14 @@
|
|||||||
const pdfUploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle')
|
const pdfUploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle')
|
||||||
const videoUploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle')
|
const videoUploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle')
|
||||||
|
|
||||||
|
const chunkUploadState: Record<
|
||||||
|
FileType,
|
||||||
|
{ uploadId: string | null; chunkSize: number; uploadedChunks: Set<number> }
|
||||||
|
> = {
|
||||||
|
pdf: { uploadId: null, chunkSize: 0, uploadedChunks: new Set() },
|
||||||
|
video: { uploadId: null, chunkSize: 0, uploadedChunks: new Set() }
|
||||||
|
}
|
||||||
|
|
||||||
// 统一的文件上传前验证
|
// 统一的文件上传前验证
|
||||||
const beforeUploadFile = (type: FileType, file: File) => {
|
const beforeUploadFile = (type: FileType, file: File) => {
|
||||||
if (!hasValidEmail.value) {
|
if (!hasValidEmail.value) {
|
||||||
@@ -626,6 +688,88 @@
|
|||||||
return beforeUploadFile('video', file)
|
return beforeUploadFile('video', file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initializeChunkUpload = async (type: FileType, file: File) => {
|
||||||
|
const endpoint =
|
||||||
|
type === 'pdf' ? Https.httpUrls.initPdfUpload : Https.httpUrls.initVideoUpload
|
||||||
|
|
||||||
|
const data = await Https.axiosPost(endpoint, {
|
||||||
|
fileName: file.name,
|
||||||
|
fileSize: file.size,
|
||||||
|
fileType: file.type,
|
||||||
|
email: form.value.email,
|
||||||
|
secureToken: form.value.secureToken
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadId: data?.uploadId as string,
|
||||||
|
chunkSize: data?.chunkSize as number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createFileChunks = (file: File, chunkSize: number) => {
|
||||||
|
const chunks: Blob[] = []
|
||||||
|
const totalChunks = Math.ceil(file.size / chunkSize)
|
||||||
|
|
||||||
|
for (let i = 0; i < totalChunks; i++) {
|
||||||
|
const start = i * chunkSize
|
||||||
|
const end = Math.min(start + chunkSize, file.size)
|
||||||
|
const chunk = file.slice(start, end)
|
||||||
|
chunks.push(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateUploadProgress = (
|
||||||
|
type: FileType,
|
||||||
|
uploadedCount: number,
|
||||||
|
total: number
|
||||||
|
) => {
|
||||||
|
const percent = Math.round((uploadedCount / total) * 100)
|
||||||
|
if (type === 'pdf') {
|
||||||
|
uploadProgressPdf.value = percent
|
||||||
|
} else {
|
||||||
|
uploadProgressVideo.value = percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadChunk = async (
|
||||||
|
type: FileType,
|
||||||
|
chunk: Blob,
|
||||||
|
chunkIndex: number,
|
||||||
|
totalChunks: number
|
||||||
|
) => {
|
||||||
|
const endpoint =
|
||||||
|
type === 'pdf' ? Https.httpUrls.uploadPDF : Https.httpUrls.uploadVideo
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('chunk', chunk)
|
||||||
|
formData.append('uploadId', chunkUploadState[type].uploadId || '')
|
||||||
|
formData.append('chunkIndex', String(chunkIndex))
|
||||||
|
formData.append('totalChunks', String(totalChunks))
|
||||||
|
formData.append('secureToken', form.value.secureToken)
|
||||||
|
|
||||||
|
await Https.axiosPost(endpoint, formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
|
||||||
|
chunkUploadState[type].uploadedChunks.add(chunkIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
const completeChunkUpload = async (type: FileType, file: File) => {
|
||||||
|
const endpoint =
|
||||||
|
type === 'pdf'
|
||||||
|
? Https.httpUrls.uploadPDFComplete
|
||||||
|
: Https.httpUrls.uploadVideoComplete
|
||||||
|
|
||||||
|
await Https.axiosPost(endpoint, {
|
||||||
|
uploadId: chunkUploadState[type].uploadId,
|
||||||
|
fileName: file.name,
|
||||||
|
totalSize: file.size,
|
||||||
|
secureToken: form.value.secureToken
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type FileType = 'pdf' | 'video'
|
type FileType = 'pdf' | 'video'
|
||||||
const handleFileChange = (info: UploadChangeParam, type: FileType) => {
|
const handleFileChange = (info: UploadChangeParam, type: FileType) => {
|
||||||
const status = info.file.status
|
const status = info.file.status
|
||||||
@@ -656,65 +800,68 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 统一的上传处理函数
|
// 统一的上传处理函数
|
||||||
const handleUploadFile = (option: any, type: FileType) => {
|
const handleUploadFile = async (option: any, type: FileType) => {
|
||||||
console.log(option, type)
|
const file = option.file as File
|
||||||
|
|
||||||
const file = option.file
|
if (!form.value.email) {
|
||||||
|
message.error('Please input the email address first')
|
||||||
|
option.onError?.(new Error('Email required'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 根据类型设置上传状态和进度
|
|
||||||
if (type === 'pdf') {
|
if (type === 'pdf') {
|
||||||
isUploadingPdf.value = true
|
isUploadingPdf.value = true
|
||||||
uploadProgressPdf.value = 0
|
uploadProgressPdf.value = 0
|
||||||
pdfUploadStatus.value = 'uploading'
|
pdfUploadStatus.value = 'uploading'
|
||||||
} else if (type === 'video') {
|
} else {
|
||||||
isUploadingVideo.value = true
|
isUploadingVideo.value = true
|
||||||
uploadProgressVideo.value = 0
|
uploadProgressVideo.value = 0
|
||||||
videoUploadStatus.value = 'uploading'
|
videoUploadStatus.value = 'uploading'
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = new FormData()
|
try {
|
||||||
params.append('file', file)
|
const { uploadId, chunkSize } = await initializeChunkUpload(type, file)
|
||||||
params.append('email', form.value.email)
|
chunkUploadState[type].uploadId = uploadId
|
||||||
|
chunkUploadState[type].chunkSize = chunkSize
|
||||||
|
chunkUploadState[type].uploadedChunks = new Set()
|
||||||
|
|
||||||
// 根据类型选择不同的上传接口
|
const chunks = createFileChunks(file, chunkSize)
|
||||||
const uploadUrl = Https.httpUrls.uploadAvatar
|
for (let i = 0; i < chunks.length; i++) {
|
||||||
// const uploadUrl =
|
await uploadChunk(type, chunks[i], i, chunks.length)
|
||||||
// type === 'pdf' ? Https.httpUrls.uploadPDF : Https.httpUrls.uploadVideo
|
updateUploadProgress(type, i + 1, chunks.length)
|
||||||
|
|
||||||
Https.axiosPost(uploadUrl, params, {
|
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
onUploadProgress: progressEvent => {
|
|
||||||
if (progressEvent.total) {
|
|
||||||
const percentCompleted = Math.round(
|
|
||||||
(progressEvent.loaded * 100) / progressEvent.total
|
|
||||||
)
|
|
||||||
if (type === 'pdf') {
|
|
||||||
uploadProgressPdf.value = percentCompleted
|
|
||||||
} else if (type === 'video') {
|
|
||||||
uploadProgressVideo.value = percentCompleted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.then(res => {
|
const res = await completeChunkUpload(type, file)
|
||||||
console.log(res)
|
console.log('上传完成-----', res)
|
||||||
if (type === 'pdf') pdfUploadStatus.value = 'success'
|
|
||||||
if (type === 'video') videoUploadStatus.value = 'success'
|
if (type === 'pdf') {
|
||||||
option.onSuccess(res, option.file)
|
pdfUploadStatus.value = 'success'
|
||||||
})
|
uploadProgressPdf.value = 100
|
||||||
.catch(error => {
|
isUploadingPdf.value = false
|
||||||
console.error('Upload error:', error)
|
form.value.pdfPath = uploadId
|
||||||
option.onError(error)
|
} else {
|
||||||
if (type === 'pdf') {
|
videoUploadStatus.value = 'success'
|
||||||
isUploadingPdf.value = false
|
uploadProgressVideo.value = 100
|
||||||
uploadProgressPdf.value = 0
|
isUploadingVideo.value = false
|
||||||
pdfUploadStatus.value = 'error'
|
form.value.videoPath = uploadId
|
||||||
} else if (type === 'video') {
|
}
|
||||||
isUploadingVideo.value = false
|
|
||||||
uploadProgressVideo.value = 0
|
option.onSuccess?.({ uploadId }, option.file)
|
||||||
videoUploadStatus.value = 'error'
|
} catch (error: any) {
|
||||||
}
|
console.error('Upload error:', error)
|
||||||
})
|
message.error(error?.message || 'Upload failed')
|
||||||
|
option.onError?.(error)
|
||||||
|
|
||||||
|
if (type === 'pdf') {
|
||||||
|
isUploadingPdf.value = false
|
||||||
|
uploadProgressPdf.value = 0
|
||||||
|
pdfUploadStatus.value = 'error'
|
||||||
|
} else {
|
||||||
|
isUploadingVideo.value = false
|
||||||
|
uploadProgressVideo.value = 0
|
||||||
|
videoUploadStatus.value = 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PDF上传处理
|
// PDF上传处理
|
||||||
@@ -773,10 +920,18 @@
|
|||||||
.full-row {
|
.full-row {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.arrow-down-icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
.apply-container{
|
||||||
|
min-height: calc(100% -18rem);
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
.banner {
|
.banner {
|
||||||
height: 54.8rem;
|
height: 54.8rem;
|
||||||
background: url('@/assets/images/award/apply_bg.png') no-repeat;
|
background: url('@/assets/images/award/form_bg.png') no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 12rem 21.4rem 0;
|
padding: 12rem 21.4rem 0;
|
||||||
@@ -888,7 +1043,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-input) {
|
:deep(.ant-input),
|
||||||
|
:deep(.ant-select-selector) {
|
||||||
border: 0.2rem solid #d5d5d5;
|
border: 0.2rem solid #d5d5d5;
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
border-radius: 0.8rem;
|
border-radius: 0.8rem;
|
||||||
@@ -899,6 +1055,20 @@
|
|||||||
&.textarea {
|
&.textarea {
|
||||||
height: 20rem;
|
height: 20rem;
|
||||||
}
|
}
|
||||||
|
.ant-select-selection-search {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.ant-select-selection-item {
|
||||||
|
line-height: 6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.ant-select-arrow) {
|
||||||
|
height: 4rem;
|
||||||
|
width: 6.2rem;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-left: 0.1rem solid #d5d5d5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1092,7 +1262,6 @@
|
|||||||
|
|
||||||
:deep(.ant-checkbox-wrapper) {
|
:deep(.ant-checkbox-wrapper) {
|
||||||
.ant-checkbox-inner {
|
.ant-checkbox-inner {
|
||||||
//修改边框的颜色
|
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
border: 0.2rem solid #585858 !important;
|
border: 0.2rem solid #585858 !important;
|
||||||
@@ -1100,17 +1269,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-checkbox-checked .ant-checkbox-inner {
|
.ant-checkbox-checked .ant-checkbox-inner {
|
||||||
//修改选中框的背景颜色
|
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
/* 将背景颜色修改为白色 */
|
|
||||||
//修改边框颜色
|
|
||||||
border-color: #585858 !important;
|
border-color: #585858 !important;
|
||||||
/* 将边框颜色修改为黑色 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-checkbox-checked .ant-checkbox-inner::after {
|
.ant-checkbox-checked .ant-checkbox-inner::after {
|
||||||
//antd的checkbox组件的选中框里面的透明的钩子,是通过设置底部边框和右边框的颜色再旋转得到的钩子,
|
|
||||||
// 所以设置底部边框和右边框的样式就可以修改钩子的样式
|
|
||||||
border-bottom: 0.2rem solid #585858;
|
border-bottom: 0.2rem solid #585858;
|
||||||
border-right: 0.2rem solid #585858;
|
border-right: 0.2rem solid #585858;
|
||||||
width: 0.65rem;
|
width: 0.65rem;
|
||||||
@@ -1202,6 +1365,7 @@
|
|||||||
font-family: 'ArialBold';
|
font-family: 'ArialBold';
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cutdown {
|
.cutdown {
|
||||||
|
|||||||
51
src/views/AwardPage/components/Success.vue
Normal file
51
src/views/AwardPage/components/Success.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<div class="success-container flex flex-col align-center">
|
||||||
|
<img
|
||||||
|
src="@/assets/images/award/successful.png"
|
||||||
|
alt=""
|
||||||
|
class="icon-img"
|
||||||
|
/>
|
||||||
|
<div class="title">Submission Successful</div>
|
||||||
|
<div class="desc">
|
||||||
|
<div>
|
||||||
|
Please review your submitted information in the AiDA in-platform message.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
You may edit it if needed. Competition updates and results will be sent
|
||||||
|
via email.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.success-container {
|
||||||
|
margin: 0 21.5rem;
|
||||||
|
padding: 10.6rem 27.3rem 0;
|
||||||
|
height: 50rem;
|
||||||
|
position: relative;
|
||||||
|
top: -16.8rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
.icon-img {
|
||||||
|
width: 12rem;
|
||||||
|
height: 12rem;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-family: 'PoppinsBold';
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 3rem;
|
||||||
|
color: #232323;
|
||||||
|
text-align: center;
|
||||||
|
margin: 2rem 0 4rem;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #585858;
|
||||||
|
font-family: Arial;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right flex align-center">
|
<div class="header-right flex align-center">
|
||||||
<div class="text">Submit your Application</div>
|
<div class="text">{{ btnText }}</div>
|
||||||
<img
|
<img
|
||||||
src="@/assets/images/award/arrow.png"
|
src="@/assets/images/award/arrow.png"
|
||||||
alt=""
|
alt=""
|
||||||
@@ -89,12 +89,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
const showQRcode = ref(false)
|
const showQRcode = ref(false)
|
||||||
const handleCloseQRcode = () => {
|
const handleCloseQRcode = () => {
|
||||||
showQRcode.value = false
|
showQRcode.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BtnType = 'index' | 'form'
|
||||||
|
const btnType = ref<BtnType>('index')
|
||||||
|
const btnText = computed(() => {
|
||||||
|
if (btnType.value === 'index') {
|
||||||
|
return 'Submit your Application'
|
||||||
|
}
|
||||||
|
if (btnType.value === 'form') {
|
||||||
|
return 'Back to Introduction'
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@@ -123,6 +134,7 @@
|
|||||||
}
|
}
|
||||||
.header-right {
|
.header-right {
|
||||||
column-gap: 1rem;
|
column-gap: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
.text {
|
.text {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
Reference in New Issue
Block a user