Commit d081497d by wusiyi

feat: 官网2.0样式优化

parent d5494f62
......@@ -6,7 +6,7 @@
<p class="footer-title">{{ bannerContent.title }}</p>
<p class="footer-description">{{ bannerContent.description }}</p>
<button class="footer-register-btn" @click="goToLogin">立即使用</button>
<Register v-if="registerDialog" @close="registerDialog = false" />
<Register :visible="registerDialog" @close="registerDialog = false" />
</div>
<div class="bg-right"></div>
</div>
......
......@@ -98,6 +98,12 @@
</template>
<script>
import { mapMutations } from 'vuex'
import productionSvg from '../assets/images/home/production.svg'
import customSvg from '../assets/images/home/custom.svg'
import supplyChainSvg from '../assets/images/home/earth.svg'
import logisticsSvg from '../assets/images/home/logistics.svg'
import usSvg from '../assets/images/product/us.svg'
export default {
name: 'headerNavMobile',
data() {
......@@ -111,71 +117,34 @@ export default {
expanding: false,
children: [
{
name: '生产管理',
path: '/product/production',
name: '九猫ERP',
subTitle: '精细化运营和供应链一体化解决方案',
path: '/product/production?tab=product',
icon: productionSvg,
},
{
name: '一件定制',
path: '/product/custom-chain',
name: 'POD和满印供应链',
subTitle: '⼏⼗万件底胚库存,多种⼯艺,五千多款模型,品类⻬全',
path: '/product/production?tab=custom-chain',
icon: customSvg,
},
{
name: '海外仓物流',
path: '/product/logistics',
name: '九猫美国供应链 [LA POD]',
subTitle: '美国本⼟海外⽣产线,平台客户⾸选',
path: '/product/production?tab=supply-chain',
icon: supplyChainSvg,
},
{
name: '自建站平台',
path: '/product/platform',
name: '九猫物流',
subTitle: '集成国内主流物流,价格优惠时效快',
path: '/product/production?tab=logistics',
icon: logisticsSvg,
},
],
},
{ name: '价格', path: '/price' },
{ name: '一件定制', path: 'https://jomalls.com/custom/' },
{ name: '案例', path: '/case' },
{
name: '资源与支持',
path: '/support',
expanding: false,
children: [
{
name: '资源',
expanding: false,
children: [
{
name: '活动信息',
path: '',
},
{
name: '入住流程平台',
path: '',
},
{
name: '资讯',
path: '',
},
],
},
{
name: '支持',
expanding: false,
children: [
{
name: '帮助中心',
path: '',
},
],
},
{
name: '了解SaaS ERP',
expanding: false,
// children: [
// {
// name: '如何通过财务管控向内部要效益',
// path: '/financial',
// },
// ],
},
],
},
// { name: '案例', path: '/case' },
{
name: '关于',
path: '/about',
......@@ -183,14 +152,19 @@ export default {
children: [
{
name: '关于我们',
subTitle: '九猫ERP以精细化管理方案,帮助卖家实现业务增长',
path: '/about',
icon: usSvg,
},
{
name: '加入我们',
path: '/join',
},
// {
// name: '加入我们',
// subTitle: '加入九猫,一切皆有可能',
// path: '/join',
// icon: joinSvg,
// },
],
},
{ name: '帮助', path: '/help' },
],
}
},
......
......@@ -20,12 +20,18 @@ import './assets/css/index.css'
import './styles/index.scss'
const isMobile = () => window.innerWidth <= 1100
Vue.util.defineReactive(Vue.prototype, '$isMobile', isMobile())
// document.querySelector('html').style.fontSize = window.innerWidth / 100 + 'px'
// window.addEventListener('resize', () => {
// Vue.prototype.$isMobile = isMobile()
// document.querySelector('html').style.fontSize = window.innerWidth / 100 + 'px'
// })
// 响应式更新 $isMobile 状态
const mobileData = Vue.observable({ value: isMobile() })
Object.defineProperty(Vue.prototype, '$isMobile', {
get() {
return mobileData.value
},
enumerable: true,
configurable: true
})
window.addEventListener('resize', () => {
mobileData.value = isMobile()
})
new Vue({
store,
......
......@@ -20,7 +20,7 @@ const zImgPath = window.apiHostSetting.VUE_APP_ZIMG_URL
// 文件存储路径地址
const filePath = getStaticPath() + 'upload/erp'
const instance = axios.create({
baseURL: baseURL+'api/',
baseURL: baseURL + 'api/',
timeout: 5 * 60 * 1000,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
......@@ -43,12 +43,15 @@ instance.interceptors.request.use(
}
config.data = data
}
if (HTTPNUM === 0) {
loading = Vue.prototype.$loading({
background: 'rgba(0, 0, 0, 0.3)',
})
// 如果配置了 skipLoading,跳过全局 loading
if (!config.skipLoading) {
if (HTTPNUM === 0) {
loading = Vue.prototype.$loading({
background: 'rgba(0, 0, 0, 0.3)',
})
}
HTTPNUM++
}
HTTPNUM++
cancelToken.add(config)
return config
},
......@@ -62,9 +65,12 @@ instance.interceptors.request.use(
instance.interceptors.response.use(
(res) => {
cancelToken.remove(res.config)
HTTPNUM--
if (HTTPNUM === 0) {
loading.close()
// 如果配置了 skipLoading,跳过全局 loading 的关闭
if (!res.config.skipLoading) {
HTTPNUM--
if (HTTPNUM === 0) {
loading.close()
}
}
if (res.status === 200) {
if (res.data.code === 401) {
......@@ -144,22 +150,25 @@ instance.interceptors.response.use(
// 服务器状态码不是200的情况
(error) => {
cancelToken.clear()
HTTPNUM--
if (HTTPNUM == 0) {
Vue.prototype.$alert(error.message, 'Error', {
confirmButtonText: '确定',
callback: () => {},
})
loading.close()
// 如果配置了 skipLoading,跳过全局 loading 的关闭和错误提示
if (!error.config || !error.config.skipLoading) {
HTTPNUM--
if (HTTPNUM == 0) {
Vue.prototype.$alert(error.message, 'Error', {
confirmButtonText: '确定',
callback: () => {},
})
loading.close()
}
}
return Promise.reject(error)
}
)
function get(url, params) {
function get(url, params, config = {}) {
return new Promise((resolve, reject) => {
instance
.get(url, { params })
.get(url, { params, ...config })
.then((res) => {
if (res.status === 200 && res.data) {
resolve(res.data)
......
<template>
<div class="modal" :style="visible ? 'display: block' : 'display: none'">
<div class="forget-form">
<div class="forget-title">忘记密码</div>
<el-form size="medium" :model="forgetForm" ref="forgetForm">
<el-form-item
v-if="labelPosition === 'passwordLogin'"
prop="email"
label=""
:rules="[
{ required: true, message: '请输入邮箱地址', type: 'email' },
]">
<el-input
v-model="forgetForm.email"
prefix-icon="el-icon-postcard"
style="background: #fff"
placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-dialog
:visible.sync="dialogVisible"
title="忘记密码"
:width="$isMobile ? '100%' : '36%'"
:center="true"
:close-on-click-modal="false"
:close-on-press-escape="true"
@close="handleClose"
@opened="handleOpened"
class="forget-dialog">
<el-radio-group
v-model="labelPosition"
class="radioGroup flex justify-center items-center mb-5"
size="small">
<el-radio-button label="passwordLogin">邮箱找回</el-radio-button>
<el-radio-button label="verificationLogin">验证码找回</el-radio-button>
</el-radio-group>
<el-form
size="medium"
:model="forgetForm"
ref="forgetForm"
class="forget-form flex flex-col flex-1">
<el-form-item
v-if="labelPosition === 'passwordLogin'"
prop="email"
label=""
:rules="[{ required: true, message: '请输入邮箱地址', type: 'email' }]">
<el-input
v-model="forgetForm.email"
prefix-icon="el-icon-message"
placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item
v-if="labelPosition === 'passwordLogin'"
prop="code"
label=""
:rules="[{ required: true, message: '请输入验证码' }]">
<div style="display: flex; gap: 20px">
<div style="flex: 1">
<el-input
v-model="forgetForm.code"
style="background: #fff"
prefix-icon="el-icon-message"
placeholder="请输入验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verCode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
<el-form-item
v-if="labelPosition === 'passwordLogin'"
prop="code"
label=""
:rules="[{ required: true, message: '请输入邮箱验证码' }]">
<div class="flex gap-5">
<div class="flex-1">
<el-input
v-model="forgetForm.code"
prefix-icon="el-icon-chat-line-square"
placeholder="请输入邮箱验证码"></el-input>
</div>
</el-form-item>
<div>
<el-button
class="get-code-button"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verCode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
</div>
</el-form-item>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="phone"
:rules="[{ required: true, message: '请输入手机号' }]">
<el-input
prefix-icon="el-icon-phone"
style="background: #fff"
v-model="forgetForm.phone"
placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="phone"
:rules="[{ required: true, message: '请输入手机号' }]">
<el-input
prefix-icon="el-icon-phone"
v-model="forgetForm.phone"
placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="verifyKey"
label=""
:rules="[{ required: true, message: '请输入验证码' }]">
<div style="display: flex; gap: 20px; margin-bottom: -10px">
<div style="flex: 1">
<el-input
v-model="forgetForm.verifyKey"
prefix-icon="el-icon-postcard"
placeholder="请输入验证码,区分大小写"></el-input>
</div>
<div @click="getVerifycode">
<img style="width: 100px; height: 80%" :src="VerifyUrl" />
</div>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="verifyKey"
label=""
:rules="[{ required: true, message: '请输入验证码' }]">
<div class="flex gap-5 mb-0">
<div class="flex-1">
<el-input
v-model="forgetForm.verifyKey"
prefix-icon="el-icon-postcard"
placeholder="请输入验证码,区分大小写"></el-input>
</div>
</el-form-item>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="code"
label=""
:rules="[{ required: true, message: '请输入短信验证码' }]">
<div style="display: flex; gap: 20px">
<div style="flex: 1">
<el-input
v-model="forgetForm.code"
prefix-icon="el-icon-message"
placeholder="请输入短信验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button"
style="width: 112px"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="forgetPhonecode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
<div class="cursor-pointer" @click="getVerifycode">
<img class="w-20 lg:w-28 h-full" :src="VerifyUrl" />
</div>
</el-form-item>
<el-form-item
prop="password"
:rules="[{ required: true, message: '请输入新密码' }]">
<el-input
v-model="forgetForm.password"
placeholder="请输入新密码"
:type="showNewPwd ? 'text' : 'password'"
prefix-icon="el-icon-lock"
style="background-color: #fff">
<i
@click="showNewPwd = !showNewPwd"
slot="suffix"
:class="{ eyes: true, open: !showNewPwd, close: showNewPwd }"></i>
</el-input>
</el-form-item>
</div>
</el-form-item>
<el-form-item
prop="confirmNewPassword"
:rules="[{ validator: validatePass, trigger: 'blur' }]">
<el-input
v-model="forgetForm.confirmNewPassword"
placeholder="请再次输入新密码"
:type="showAgNewPwd ? 'text' : 'password'"
prefix-icon="el-icon-lock"
style="background-color: #fff">
<i
@click="showAgNewPwd = !showAgNewPwd"
slot="suffix"
:class="{
eyes: true,
open: !showAgNewPwd,
close: showAgNewPwd,
}"></i>
</el-input>
</el-form-item>
<el-form-item
v-if="labelPosition === 'verificationLogin'"
prop="code"
label=""
:rules="[{ required: true, message: '请输入短信验证码' }]">
<div class="flex gap-5">
<div class="flex-1">
<el-input
v-model="forgetForm.code"
prefix-icon="el-icon-chat-line-square"
placeholder="请输入短信验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="forgetPhonecode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
</div>
</el-form-item>
<el-form-item style="margin-bottom: 10px">
<el-button class="reset-password" round @click="resetPassword">
<el-form-item
prop="password"
:rules="[{ required: true, message: '请输入新密码' }]">
<el-input
v-model="forgetForm.password"
placeholder="请输入新密码"
:type="showNewPwd ? 'text' : 'password'"
prefix-icon="el-icon-lock">
<i
@click="showNewPwd = !showNewPwd"
slot="suffix"
:class="{ eyes: true, open: !showNewPwd, close: showNewPwd }"></i>
</el-input>
</el-form-item>
<el-form-item
prop="confirmNewPassword"
:rules="[{ validator: validatePass, trigger: 'blur' }]">
<el-input
v-model="forgetForm.confirmNewPassword"
placeholder="请再次输入新密码"
:type="showAgNewPwd ? 'text' : 'password'"
prefix-icon="el-icon-lock">
<i
@click="showAgNewPwd = !showAgNewPwd"
slot="suffix"
:class="{
eyes: true,
open: !showAgNewPwd,
close: showAgNewPwd,
}"></i>
</el-input>
</el-form-item>
<slot name="footer">
<div class="footer-buttons flex flex-col gap-2 w-full mt-auto">
<el-button
class="reset-password w-full lg:h-10 lg:text-base text-white"
round
@click="resetPassword">
重置密码
</el-button>
</el-form-item>
<div style="font-size: 14px">
<span>无需重置</span>
<span
style="
margin-left: 20px;
color: var(--primary-color);
cursor: pointer;
"
@click="$emit('close')">
登录
</span>
<el-button
class="login-btn w-full lg:h-10 lg:text-base text-white"
round
@click="handleClose">
无需重置,直接登录
</el-button>
</div>
</el-form>
<i class="el-icon-circle-close close-icon" @click="$emit('close')"></i>
</div>
</div>
</slot>
</el-form>
</el-dialog>
</template>
<script>
......@@ -163,10 +168,10 @@ export default {
name: 'ForgetPassword',
props: {
visible: { type: Boolean, default: false },
labelPosition: { type: String, default: 'passwordLogin' },
},
data() {
return {
labelPosition: 'passwordLogin',
validatePass: (rule, value, callback) => {
if (!value) {
callback(new Error('请再次输入密码'))
......@@ -189,24 +194,38 @@ export default {
getCodeDisabled() {
return this.codeCountDown > 0
},
dialogVisible: {
get() {
return this.visible
},
set(val) {
if (!val) {
this.$emit('close')
}
},
},
},
watch: {
visible(val) {
if (val) {
this.initForm()
if (this.labelPosition === 'verificationLogin') {
this.getVerifycode()
}
} else {
this.clearTimer()
}
},
},
mounted() {
if (this.visible) this.initForm()
labelPosition() {
this.initForm()
},
},
methods: {
initForm() {
this.forgetForm = {}
this.codeCountDown = 0
this.getVerifycode()
if (this.labelPosition === 'verificationLogin') {
this.getVerifycode()
}
this.$nextTick(() => {
this.$refs.forgetForm && this.$refs.forgetForm.clearValidate()
})
......@@ -299,13 +318,29 @@ export default {
},
getVerifycode() {
this.verifyKeyTime = new Date().getTime()
get('business/user/verifycode', {
verifyKey: this.verifyKeyTime,
}).then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
}
})
get(
'business/user/verifycode',
{
verifyKey: this.verifyKeyTime,
},
{ skipLoading: true }
)
.then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
} else {
this.VerifyUrl = ''
}
})
.catch(() => {
this.VerifyUrl = ''
})
},
handleClose() {
this.$emit('close')
},
handleOpened() {
this.initForm()
},
},
beforeDestroy() {
......@@ -314,56 +349,83 @@ export default {
}
</script>
<style scoped>
.modal {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
transition: all 0.3s;
}
.forget-form {
position: fixed;
width: 36%;
padding: 50px 60px;
box-sizing: border-box;
border-radius: 6px;
top: 50%;
left: 50%;
background-color: #fff;
transform: translate(-50%, -50%);
<style scoped lang="scss">
.forget-dialog ::v-deep .el-dialog__header {
padding: 40px 0 20px 0;
}
.forget-title {
text-align: center;
margin-bottom: 26px;
.forget-dialog ::v-deep .el-dialog__title {
font-size: 26px;
font-weight: 400;
font-weight: 500;
letter-spacing: 2px;
}
.forget-form .close-icon {
position: fixed;
top: -30px;
right: -28px;
font-size: 30px;
color: #fff;
cursor: pointer;
.forget-dialog ::v-deep .el-dialog__body {
padding: 0 60px 20px;
height: 460px;
display: flex;
flex-direction: column;
}
@media (max-width: 1100px) {
.forget-dialog ::v-deep .el-dialog {
margin-top: 10vh;
min-height: 200px;
}
.forget-dialog ::v-deep .el-dialog__title {
font-size: 20px;
display: flex;
flex-direction: column;
}
.forget-dialog ::v-deep .el-dialog__body {
padding: 0 30px 20px;
display: flex;
flex-direction: column;
}
}
.reset-password {
width: 100%;
background-image: linear-gradient(
to right,
var(--primary-color) 0%,
#d59723 51%,
#f0cb66 100%
);
color: #fff;
&:hover {
background-image: linear-gradient(
to right,
var(--hover-color) 0%,
#cf9e42 51%,
#eed183 100%
);
color: #fff;
}
&:focus {
background-image: linear-gradient(
to right,
var(--hover-color) 0%,
#cf9e42 51%,
#eed183 100%
);
color: #fff;
border: 1px solid var(--hover-color);
}
}
.login-btn {
background: #ffffff;
color: #838383;
border: 1px solid #a8a8a8;
&:hover {
background: #ffffff;
color: #949494;
border: 1px solid #c7c7c7;
}
&:focus {
background: #ffffff;
color: #838383;
border: 1px solid #a8a8a8;
}
}
.get-code-button:disabled:hover {
......@@ -401,4 +463,8 @@ export default {
.eyes.close {
background: url('../../assets/eyes-closed.png') no-repeat center / cover;
}
.forget-dialog ::v-deep .el-button + .el-button {
margin-left: 0;
}
</style>
......@@ -36,7 +36,7 @@
<platform />
<product-intro />
<Register v-if="registerDialog" @close="registerDialog = false" />
<Register :visible="registerDialog" @close="registerDialog = false" />
</div>
</template>
<script>
......
......@@ -16,7 +16,8 @@
<el-radio-group
v-model="labelPosition"
class="radioGroup mt-5 flex justify-center items-center mb-5"
size="small">
size="small"
@change="clickChange">
<el-radio-button label="passwordLogin">密码登录</el-radio-button>
<el-radio-button label="verificationLogin">验证码登录</el-radio-button>
</el-radio-group>
......@@ -75,7 +76,7 @@
prefix-icon="el-icon-postcard"
placeholder="请输入验证码(区分大小写)"></el-input>
</div>
<div @click="getVerifycode">
<div class="cursor-pointer" @click="getVerifycode">
<img class="w-20 lg:w-32 h-full" :src="VerifyUrl" />
</div>
</div>
......@@ -88,11 +89,11 @@
<div class="flex-1">
<el-input
v-model="ruleForm.code"
prefix-icon="el-icon-message"
prefix-icon="el-icon-chat-line-square"
placeholder="请输入短信验证码"></el-input>
</div>
<el-button
class="w-22 text-white border-0"
class="w-22 text-white text-xs lg:text-base border-0"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'get-code-button'"
@click="verPhonecode">
......@@ -119,8 +120,7 @@
<el-divider direction="vertical" class="bottom-line"></el-divider>
<div
class="register-btn-text cursor-pointer"
@click="registerDialog = true"
style="color: var(--primary-color)">
@click="registerDialog = true">
马上注册
</div>
</div>
......@@ -131,9 +131,8 @@
<ForgetPassword
v-if="isShowModal"
:visible="isShowModal"
:label-position="labelPosition"
@close="isShowModal = false" />
<Register v-if="registerDialog" @close="registerDialog = false" />
<Register :visible="registerDialog" @close="registerDialog = false" />
</div>
</template>
......@@ -175,7 +174,9 @@ export default {
trigger: ['blur', 'change'],
},
],
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
code: [
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
],
verifyKey: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
......@@ -188,11 +189,23 @@ export default {
return this.codeCountDown > 0
},
},
mounted() {
this.getVerifycode()
},
methods: {
...mapMutations(['setUserInfo']),
initForm() {
this.ruleForm = {}
this.codeCountDown = 0
if (this.labelPosition === 'verificationLogin') {
this.getVerifycode()
}
this.$nextTick(() => {
this.$refs.ruleForm && this.$refs.ruleForm.clearValidate()
})
},
clearTimer() {
if (this.timer) clearInterval(this.timer)
this.timer = null
},
async verPhonecode() {
try {
await new Promise((resolve, reject) => {
......@@ -263,13 +276,26 @@ export default {
},
getVerifycode() {
this.verifyKeyTime = new Date().getTime()
get('business/user/verifycode', {
verifyKey: this.verifyKeyTime,
}).then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
}
})
get(
'business/user/verifycode',
{
verifyKey: this.verifyKeyTime,
},
{ skipLoading: true }
)
.then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
} else {
this.VerifyUrl = ''
}
})
.catch(() => {
this.VerifyUrl = ''
})
},
clickChange() {
this.initForm()
},
},
}
......@@ -315,9 +341,10 @@ export default {
}
.disabled {
background-color: #d0d2d8;
background-color: #f0f0f0;
color: #666;
margin-left: 10px;
border: 1px solid #cccccc;
cursor: not-allowed;
}
.code-btn {
......@@ -348,6 +375,7 @@ export default {
}
.register-btn-text {
color: var(--primary-color);
&:hover {
color: var(--hover-color);
}
......
<template>
<div class="docking-platform flex justify-center">
<div>
<div class="flex justify-center flex-col items-center mb-5 gap-2">
<div class="mt-30 platforms-marquee flex justify-center">
<vue-seamless-scroll
:data="platforms"
:class-option="classOption"
class="warp">
<ul class="flex flex-wrap">
<li
class="platform-item"
v-for="platform in platforms"
:key="platform.name">
<a v-if="platform.link" :href="platform.link" target="_blank">
<img
class="platform-image"
:src="
require(`../../assets/platforms/${platform.name}.${platform.type}`)
"
:alt="platform.name" />
</a>
<img
v-else
class="platform-image"
:src="
require(`../../assets/platforms/${platform.name}.${platform.type}`)
"
:alt="platform.name" />
</li>
</ul>
</vue-seamless-scroll>
</div>
</div>
</div>
<div class="docking-platform flex justify-center py-8">
<vue-seamless-scroll
:data="platforms"
:class-option="classOption"
class="warp flex justify-center items-center">
<ul class="flex flex-wrap">
<li
class="platform-item flex justify-center items-center w-20"
v-for="platform in platforms"
:key="platform.name">
<a
v-if="platform.link"
:href="platform.link"
target="_blank"
class="flex justify-center items-center w-full h-full">
<img
class="platform-image"
:src="
require(`../../assets/platforms/${platform.name}.${platform.type}`)
"
:alt="platform.name" />
</a>
<img
v-else
class="platform-image"
:src="
require(`../../assets/platforms/${platform.name}.${platform.type}`)
"
:alt="platform.name" />
</li>
</ul>
</vue-seamless-scroll>
</div>
</template>
......@@ -68,7 +66,7 @@ export default {
],
classOption: {
step: 0.6, // 数值越大速度滚动越快
limitMoveNum: 5, // 开始无缝滚动的数据量 this.dataList.length
limitMoveNum: 18, // 开始无缝滚动的数据量 this.dataList.length
hoverStop: true, // 是否开启鼠标悬停stop
direction: 1, // 0向下 1向上 2向左 3向右
autoPlay: true, // 是否自动滚动
......@@ -82,12 +80,6 @@ export default {
<style scoped lang="scss">
.docking-platform {
background: var(--light-color);
padding: 30px 0 10px 0;
}
.docking-platform h2 {
color: #000;
font-weight: 500;
}
.warp {
......@@ -95,42 +87,24 @@ export default {
height: 100px;
overflow: hidden;
}
.lx-title {
font-size: 2.25rem;
color: #ffffff;
font-weight: 500;
line-height: 1.75rem;
margin-bottom: 0.25rem;
max-width: 840px;
text-align: center;
--tw-text-opacity: 1;
letter-spacing: 1px;
}
.platforms-marquee .platform-item {
.platform-item {
flex: 0 0 auto;
width: 140px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.platforms-marquee .platform-item a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
text-decoration: none;
transition: opacity 0.3s ease;
@media (max-width: 1100px) {
.warp {
width: 90%;
}
.platform-item {
width: 33%;
}
}
.platform-image {
width: 120px;
height: auto;
max-height: 80px;
object-fit: contain;
width: 80%;
transition: filter 0.3s ease;
filter: brightness(0) invert(1);
}
......
<template>
<div class="login_bg">
<div class="login_content">
<h2
style="
font-size: 24px;
text-align: center;
font-weight: 400;
color: #4a4c6d;
">
用户注册
</h2>
<el-radio-group
v-model="labelPosition"
class="radioGroup"
size="small"
@change="clickChange">
<el-radio-button label="phone">手机号注册</el-radio-button>
<el-radio-button label="mailbox">邮箱注册</el-radio-button>
</el-radio-group>
<el-form
size="medium"
:model="registerForm"
:rules="rules"
ref="registerForm"
class="login_form">
<el-form-item v-if="labelPosition === 'mailbox'" prop="email" label="">
<el-input
v-model="registerForm.email"
prefix-icon="el-icon-postcard"
placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item v-if="labelPosition === 'mailbox'" prop="code" label="">
<div style="display: flex; gap: 20px">
<div style="flex: 1">
<el-input
v-model="registerForm.code"
prefix-icon="el-icon-message"
placeholder="请输入邮箱验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button"
style="width: 112px"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verCode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
<el-dialog
:visible.sync="dialogVisible"
title="用户注册"
:width="$isMobile ? '100%' : '36%'"
:center="true"
:close-on-click-modal="false"
:close-on-press-escape="true"
@close="closeDialog"
@opened="handleOpened"
class="register-dialog">
<el-radio-group
v-model="labelPosition"
class="radioGroup flex justify-center items-center mb-5"
size="small">
<el-radio-button label="phone">手机号注册</el-radio-button>
<el-radio-button label="mailbox">邮箱注册</el-radio-button>
</el-radio-group>
<el-form
size="medium"
:model="registerForm"
:rules="rules"
ref="registerForm"
class="login_form flex flex-col flex-1">
<el-form-item v-if="labelPosition === 'mailbox'" prop="email" label="">
<el-input
v-model="registerForm.email"
prefix-icon="el-icon-message"
placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item v-if="labelPosition === 'mailbox'" prop="code" label="">
<div class="flex gap-5">
<div class="flex-1">
<el-input
v-model="registerForm.code"
prefix-icon="el-icon-postcard"
placeholder="请输入邮箱验证码"></el-input>
</div>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="phone">
<el-input
v-model="registerForm.phone"
prefix-icon="el-icon-phone"
placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="verifyKey">
<div style="display: flex; gap: 20px">
<div style="flex: 1">
<el-input
v-model="registerForm.verifyKey"
prefix-icon="el-icon-postcard"
placeholder="请输入验证码,区分大小写"></el-input>
</div>
<div @click="getVerifycode">
<img style="width: 120px; height: 100%" :src="VerifyUrl" />
</div>
<div>
<el-button
class="get-code-button"
style="width: 112px"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verCode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="code">
<div style="display: flex; gap: 20px">
<div style="flex: 1">
<el-input
v-model="registerForm.code"
prefix-icon="el-icon-message"
placeholder="请输入短信验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button"
style="width: 112px"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verPhonecode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
</div>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
v-model="registerForm.password"
prefix-icon="el-icon-lock"
placeholder="请输入密码"
autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input
type="password"
prefix-icon="el-icon-lock"
v-model="registerForm.checkPass"
placeholder="请再次输入密码"
autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="companyName">
<el-input
v-model="registerForm.companyName"
prefix-icon="el-icon-s-home"
placeholder="请输入公司名称"></el-input>
</el-form-item>
<el-form-item prop="job">
<el-input
v-model="registerForm.job"
prefix-icon="el-icon-s-order"
placeholder="请输入职务"></el-input>
</el-form-item>
</el-form>
<div class="bottom-section">
<div class="agree-terms" :class="agreeAnimate ? 'agree-animate' : ''">
<el-checkbox v-model="isRead" label="" :indeterminate="false" />
<span class="tos-link">
阅读并同意
<a class="link" href="/protocol/term" target="_blank">
《九猫ERP条款与协议》
</a>
<span></span>
<a class="link" href="/protocol/privacy" target="_blank">
《隐私协议》
</a>
</span>
</div>
<div class="register-btn-container">
<button class="register-btn" @click="onRegister">立即注册</button>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="phone">
<el-input
v-model="registerForm.phone"
prefix-icon="el-icon-phone"
placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="verifyKey">
<div class="flex gap-5">
<div class="flex-1">
<el-input
v-model="registerForm.verifyKey"
prefix-icon="el-icon-postcard"
placeholder="请输入验证码,区分大小写"></el-input>
</div>
<div class="cursor-pointer" @click="getVerifycode">
<img class="w-20 lg:w-28 h-full" :src="VerifyUrl" />
</div>
</div>
<div class="bottom-login-link">
已有账号?
<span
style="cursor: pointer; color: var(--primary-color)"
@click="goToLogin">
马上登录
</span>
</el-form-item>
<el-form-item v-if="labelPosition === 'phone'" prop="code">
<div class="flex gap-5">
<div class="flex-1">
<el-input
v-model="registerForm.code"
prefix-icon="el-icon-chat-line-square"
placeholder="请输入短信验证码"></el-input>
</div>
<div>
<el-button
class="get-code-button ml-2 w-28 border-none text-sm text-white"
:disabled="getCodeDisabled"
:class="getCodeDisabled ? 'disabled' : 'code-btn'"
@click="verPhonecode">
{{ codeCountDown === 0 ? '获取验证码' : `${codeCountDown}秒` }}
</el-button>
</div>
</div>
</el-form-item>
<el-form-item prop="password">
<el-input
type="password"
v-model="registerForm.password"
prefix-icon="el-icon-lock"
placeholder="请输入密码"
autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="checkPass">
<el-input
type="password"
prefix-icon="el-icon-lock"
v-model="registerForm.checkPass"
placeholder="请再次输入密码"
autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="companyName">
<el-input
v-model="registerForm.companyName"
prefix-icon="el-icon-s-home"
placeholder="请输入公司名称"></el-input>
</el-form-item>
<el-form-item prop="job">
<el-input
v-model="registerForm.job"
prefix-icon="el-icon-s-order"
placeholder="请输入职务"></el-input>
</el-form-item>
</el-form>
<div class="bottom-section mt-auto">
<div
class="agree-terms mb-5"
:class="agreeAnimate ? 'agree-animate' : ''">
<el-checkbox v-model="isRead" label="" :indeterminate="false" />
<span class="tos-link text-sm">
阅读并同意
<a class="link" href="/protocol/term" target="_blank">
《九猫ERP条款与协议》
</a>
<span></span>
<a class="link" href="/protocol/privacy" target="_blank">
《隐私协议》
</a>
</span>
</div>
<div class="register-btn-container mb-5 text-center">
<button
class="register-btn w-full h-10 text-base text-white rounded-lg cursor-pointer rounded-md"
@click="onRegister">
立即注册
</button>
</div>
<div class="bottom-login-link text-sm mb-2 text-center">
已有账号?
<span
class="cursor-pointer"
style="color: var(--primary-color)"
@click="goToLogin">
马上登录
</span>
</div>
</div>
<i class="el-icon-circle-close close-icon" @click="closeDialog"></i>
</div>
</el-dialog>
</template>
<script>
......@@ -158,6 +160,12 @@ export default {
inject: {
scrollParent: 'scrollParent',
},
props: {
visible: {
type: Boolean,
default: false,
},
},
data() {
var validatePass2 = (rule, value, callback) => {
if (value === '') {
......@@ -176,6 +184,7 @@ export default {
isRead: false,
agreeAnimate: false,
codeCountDown: 0,
timer: null,
rules: {
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
......@@ -214,11 +223,42 @@ export default {
getCodeDisabled() {
return this.codeCountDown > 0
},
dialogVisible: {
get() {
return this.visible
},
set(val) {
if (!val) {
this.closeDialog()
}
},
},
},
mounted() {
this.getVerifycode()
watch: {
visible(newVal) {
if (!newVal) {
this.clearTimer()
}
},
labelPosition() {
this.initForm()
},
},
methods: {
initForm() {
this.registerForm = {}
this.codeCountDown = 0
if (this.labelPosition === 'phone') {
this.getVerifycode()
}
this.$nextTick(() => {
this.$refs.registerForm && this.$refs.registerForm.clearValidate()
})
},
clearTimer() {
if (this.timer) clearInterval(this.timer)
this.timer = null
},
closeDialog() {
this.$emit('close')
},
......@@ -249,10 +289,11 @@ export default {
email: this.registerForm.email,
})
this.codeCountDown = 60
this.clearTimer()
this.timer = setInterval(() => {
this.codeCountDown--
if (this.codeCountDown <= 0) {
clearInterval(this.timer)
this.clearTimer()
}
}, 1000)
this.$message.success(res.message)
......@@ -277,10 +318,11 @@ export default {
verifyCode: this.registerForm.verifyKey,
})
this.codeCountDown = 60
this.clearTimer()
this.timer = setInterval(() => {
this.codeCountDown--
if (this.codeCountDown <= 0) {
clearInterval(this.timer)
this.clearTimer()
}
}, 1000)
this.$message.success(res.message)
......@@ -324,76 +366,69 @@ export default {
},
getVerifycode() {
this.verifyKeyTime = new Date().getTime()
get('business/user/verifycode', {
verifyKey: this.verifyKeyTime,
}).then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
}
})
get(
'business/user/verifycode',
{
verifyKey: this.verifyKeyTime,
},
{ skipLoading: true }
)
.then((res) => {
if (res.code === 200) {
this.VerifyUrl = res.message
} else {
this.VerifyUrl = ''
}
})
.catch(() => {
this.VerifyUrl = ''
})
},
clickChange() {
this.registerForm = {}
handleOpened() {
this.initForm()
},
},
beforeDestroy() {
this.clearTimer()
},
}
</script>
<style scoped>
.login_bg {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
<style scoped lang="scss">
::v-deep .el-dialog {
margin-top: 10vh !important;
}
.login_bg::v-deep .el-input__inner {
background-color: #fff;
.register-dialog ::v-deep .el-dialog__header {
padding: 40px 0 20px 0;
}
.el-form--inline .el-form-item {
margin-right: 0px;
.register-dialog ::v-deep .el-dialog__title {
font-size: 26px;
font-weight: 500;
letter-spacing: 2px;
}
.login_content {
padding: 30px;
background: #fff;
border-radius: 6px;
box-sizing: border-box;
width: 600px;
height: 700px;
overflow-y: auto;
position: relative;
.register-dialog ::v-deep .el-dialog__body {
padding: 0 60px 20px;
height: 630px;
display: flex;
flex-direction: column;
}
.login_content::v-deep .el-form-item__content {
white-space: nowrap;
}
.login_form {
padding: 20px 0px 0px 0px;
}
.agree-terms {
margin-bottom: 20px;
}
.register-btn-container {
text-align: center;
margin-bottom: 20px;
}
.bottom-section {
margin-top: auto;
@media (max-width: 1100px) {
.forget-dialog ::v-deep .el-dialog {
margin-top: 10vh;
height: 80%;
}
.register-dialog ::v-deep .el-dialog__title {
font-size: 20px;
display: flex;
flex-direction: column;
}
.register-dialog ::v-deep .el-dialog__body {
padding: 0 30px 20px;
display: flex;
flex-direction: column;
}
}
.get-code-button:disabled:hover {
......@@ -401,10 +436,6 @@ export default {
color: #666;
}
.tos-link {
font-size: 14px;
}
@keyframes shake {
0% {
transform: translateX(0);
......@@ -427,78 +458,20 @@ export default {
}
.register-btn {
width: 100%;
height: 38px;
display: block;
margin: 0 auto;
background: var(--primary-color);
box-shadow: 0 4px 8px 0 var(--shadow-color);
border-radius: 4px;
font-size: 16px;
color: #fff;
cursor: pointer;
}
.code-btn {
margin-left: 10px;
background: linear-gradient(
180deg,
var(--primary-color) 0%,
var(--primary-color) 100%
);
border: none;
color: #fff;
}
.disabled {
background-color: #d0d2d8;
color: #666;
margin-left: 10px;
}
.radioGroup {
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
}
.bottom-login-link {
font-size: 14px;
text-align: center;
margin-bottom: 10px;
}
.close-icon {
position: absolute;
top: calc(50% - 350px);
left: calc(50% + 330px);
font-size: 30px;
color: #c7c7c7;
cursor: pointer;
z-index: 1000;
}
.close-icon:hover {
color: #ffffff;
}
@media screen and (max-width: 1100px) {
.login_bg {
background: unset;
}
.logo {
position: unset;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.login_content {
position: unset;
transform: unset;
padding: 20px;
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment