9 changed files with 864 additions and 171 deletions
@ -0,0 +1,5 @@ |
|||
NODE_ENV='development' |
|||
# 请求域名前缀 |
|||
VUE_APP_URL='https://swsz.api.js-dyyj.com' |
|||
# 打包后输出目录 |
|||
VUE_APP_OUTPUTDIR='dist_dev' |
@ -0,0 +1,5 @@ |
|||
NODE_ENV='production' |
|||
# 请求域名前缀 |
|||
VUE_APP_URL='https://api.sutenong.com' |
|||
# 打包后输出目录 |
|||
VUE_APP_OUTPUTDIR='dist' |
@ -0,0 +1,351 @@ |
|||
<template> |
|||
<div class="login-page"> |
|||
<!-- 背景图容器 --> |
|||
<div class="bg-container"> |
|||
<img src="@/assets/logo.png" alt="login background" class="bg-img" /> |
|||
</div> |
|||
|
|||
<!-- 登录表单卡片 --> |
|||
<div class="form-card"> |
|||
<!-- 切换登录方式 --> |
|||
<div class="tab-bar"> |
|||
<div |
|||
class="tab-item" |
|||
:class="{ active: loginType === 'sms' }" |
|||
@click="loginType = 'sms'" |
|||
> |
|||
短信登录 |
|||
</div> |
|||
<div |
|||
class="tab-item" |
|||
:class="{ active: loginType === 'password' }" |
|||
@click="loginType = 'password'" |
|||
> |
|||
密码登录 |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 表单内容 --> |
|||
<el-form |
|||
ref="loginForm" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="0" |
|||
class="login-form" |
|||
> |
|||
<!-- 手机号输入(通用) --> |
|||
<el-form-item prop="phone"> |
|||
<el-input |
|||
v-model="form.phone" |
|||
placeholder="请输入手机号码" |
|||
clearable |
|||
prefix-icon="el-icon-phone" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 短信验证码(短信登录时显示) --> |
|||
<el-form-item prop="code" v-if="loginType === 'sms'"> |
|||
<el-input |
|||
v-model="form.code" |
|||
placeholder="请输入验证码" |
|||
clearable |
|||
style="width: 65%" |
|||
prefix-icon="el-icon-message" |
|||
/> |
|||
<el-button |
|||
type="primary" |
|||
class="code-btn" |
|||
@click="handleGetCode" |
|||
:disabled="codeDisabled" |
|||
> |
|||
{{ codeText }} |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<!-- 密码输入(密码登录时显示) --> |
|||
<el-form-item prop="password" v-if="loginType === 'password'"> |
|||
<el-input |
|||
v-model="form.password" |
|||
type="password" |
|||
placeholder="请输入密码" |
|||
clearable |
|||
prefix-icon="el-icon-lock" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 记住账号 --> |
|||
<el-form-item class="remember-item"> |
|||
<el-checkbox v-model="rememberPhone" size="mini"> |
|||
记住用户名/手机号码 |
|||
</el-checkbox> |
|||
</el-form-item> |
|||
|
|||
<!-- 登录按钮 --> |
|||
<el-form-item> |
|||
<el-button |
|||
type="primary" |
|||
class="login-btn" |
|||
@click="handleLogin" |
|||
:loading="submitting" |
|||
> |
|||
立即登录 |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<!-- 注册引导 --> |
|||
<div class="register-guide"> |
|||
首次使用? |
|||
<router-link |
|||
to="/Register" |
|||
class="register-link" |
|||
@click="goToRegister" |
|||
> |
|||
点击注册 |
|||
</router-link> |
|||
</div> |
|||
</el-form> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "Login", |
|||
data() { |
|||
return { |
|||
// 登录方式:sms-短信 / password-密码 |
|||
loginType: "sms", |
|||
form: { |
|||
phone: "", |
|||
code: "", |
|||
password: "", |
|||
}, |
|||
rules: { |
|||
phone: [ |
|||
{ required: true, message: "请输入手机号码", trigger: "blur" }, |
|||
{ |
|||
pattern: /^1\d{10}$/, |
|||
message: "手机号码格式错误", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
code: [ |
|||
{ required: true, message: "请输入验证码", trigger: "blur" }, |
|||
{ pattern: /^\d{6}$/, message: "验证码为6位数字", trigger: "blur" }, |
|||
], |
|||
password: [ |
|||
{ required: true, message: "请输入密码", trigger: "blur" }, |
|||
{ min: 6, message: "密码至少6位", trigger: "blur" }, |
|||
], |
|||
}, |
|||
// 验证码逻辑 |
|||
codeText: "获取验证码", |
|||
codeDisabled: false, |
|||
countdown: 60, |
|||
|
|||
// 记住账号 |
|||
rememberPhone: false, |
|||
|
|||
// 加载状态 |
|||
submitting: false, |
|||
}; |
|||
}, |
|||
mounted() { |
|||
// 读取记住的手机号(示例:从localStorage读取) |
|||
const rememberedPhone = localStorage.getItem("rememberedPhone"); |
|||
if (rememberedPhone) { |
|||
this.form.phone = rememberedPhone; |
|||
this.rememberPhone = true; |
|||
} |
|||
}, |
|||
methods: { |
|||
// 切换登录方式(演示用,实际可通过路由或状态管理实现) |
|||
switchLoginType(type) { |
|||
this.loginType = type; |
|||
this.resetForm(); |
|||
}, |
|||
|
|||
// 重置表单 |
|||
resetForm() { |
|||
this.form.code = ""; |
|||
this.form.password = ""; |
|||
this.$refs.loginForm.resetValidation(); |
|||
}, |
|||
|
|||
// 获取短信验证码 |
|||
handleGetCode() { |
|||
if (!this.form.phone) { |
|||
this.$message.warning("请先输入手机号码"); |
|||
return; |
|||
} |
|||
// 模拟接口请求 |
|||
this.codeDisabled = true; |
|||
this.codeText = `重新发送(${this.countdown}s)`; |
|||
const timer = setInterval(() => { |
|||
this.countdown--; |
|||
this.codeText = `重新发送(${this.countdown}s)`; |
|||
if (this.countdown <= 0) { |
|||
clearInterval(timer); |
|||
this.codeText = "获取验证码"; |
|||
this.codeDisabled = false; |
|||
this.countdown = 60; |
|||
} |
|||
}, 1000); |
|||
// 实际项目替换为: |
|||
// axios.post('/api/sendSms', { phone: this.form.phone }).then(res => {}) |
|||
}, |
|||
|
|||
// 登录提交 |
|||
handleLogin() { |
|||
this.$refs.loginForm.validate((valid) => { |
|||
if (valid) { |
|||
this.submitting = true; |
|||
// 模拟登录请求 |
|||
setTimeout(() => { |
|||
this.$message.success("登录成功!"); |
|||
this.submitting = false; |
|||
// 记住账号 |
|||
if (this.rememberPhone) { |
|||
localStorage.setItem("rememberedPhone", this.form.phone); |
|||
} else { |
|||
localStorage.removeItem("rememberedPhone"); |
|||
} |
|||
// 实际项目替换为: |
|||
// axios.post('/api/login', this.form).then(res => { |
|||
// this.$router.push('/home') |
|||
// }) |
|||
}, 1500); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 前往注册页 |
|||
goToRegister() { |
|||
this.$router.push("/register"); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.login-page { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
|
|||
// 背景图容器 |
|||
.bg-container { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
z-index: 1; |
|||
|
|||
.bg-img { |
|||
width: 100%; |
|||
height: 100%; |
|||
object-fit: cover; |
|||
} |
|||
} |
|||
|
|||
// 登录表单卡片 |
|||
.form-card { |
|||
position: absolute; |
|||
right: 8%; |
|||
top: 50%; |
|||
transform: translateY(-50%); |
|||
width: 360px; |
|||
background: #fff; |
|||
border-radius: 8px; |
|||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.1); |
|||
z-index: 2; |
|||
padding: 30px 25px; |
|||
|
|||
// 标签切换栏 |
|||
.tab-bar { |
|||
display: flex; |
|||
justify-content: center; |
|||
margin-bottom: 25px; |
|||
|
|||
.tab-item { |
|||
width: 50%; |
|||
text-align: center; |
|||
padding: 10px 0; |
|||
color: #666; |
|||
cursor: pointer; |
|||
border-bottom: 2px solid transparent; |
|||
|
|||
&.active { |
|||
color: #1890ff; |
|||
border-bottom-color: #1890ff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 表单样式 |
|||
.login-form { |
|||
.el-form-item { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.el-input { |
|||
height: 40px; |
|||
line-height: 40px; |
|||
} |
|||
|
|||
// 验证码按钮 |
|||
.code-btn { |
|||
padding: 0 15px; |
|||
height: 40px; |
|||
} |
|||
|
|||
// 记住账号 |
|||
.remember-item { |
|||
padding-left: 0; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
// 登录按钮 |
|||
.login-btn { |
|||
width: 100%; |
|||
background: #ff4d4f; |
|||
border: none; |
|||
&:hover { |
|||
background: #e03e40; |
|||
} |
|||
} |
|||
|
|||
// 注册引导 |
|||
.register-guide { |
|||
text-align: center; |
|||
margin-top: 15px; |
|||
color: #999; |
|||
|
|||
.register-link { |
|||
color: #ff4d4f; |
|||
text-decoration: underline; |
|||
margin-left: 5px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 响应式适配 |
|||
@media (max-width: 992px) { |
|||
.form-card { |
|||
right: 5%; |
|||
width: 320px; |
|||
} |
|||
} |
|||
|
|||
@media (max-width: 576px) { |
|||
.form-card { |
|||
right: 50%; |
|||
transform: translate(50%, -50%); |
|||
width: 90%; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,356 @@ |
|||
<template> |
|||
<div class="register-page"> |
|||
<!-- 顶部Logo与电话 --> |
|||
<div class="header"> |
|||
<div class="logo-area"> |
|||
<img src="@/assets/logo.png" alt="logo" class="logo-img" /> |
|||
<span class="domain">yunming.com</span> |
|||
</div> |
|||
<div class="hotline">400-123-4xxx</div> |
|||
</div> |
|||
|
|||
<!-- 注册表单主体 --> |
|||
<div class="form-container"> |
|||
<h2 class="form-title">注册页</h2> |
|||
<el-form |
|||
ref="registerForm" |
|||
:model="form" |
|||
:rules="rules" |
|||
label-width="80px" |
|||
class="register-form" |
|||
> |
|||
<!-- 手机号 --> |
|||
<el-form-item label="手机号码" prop="phone"> |
|||
<el-input |
|||
v-model="form.phone" |
|||
placeholder="请输入手机号码" |
|||
clearable |
|||
prefix-icon="el-icon-phone" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 手机验证码 --> |
|||
<el-form-item label="手机验证码" prop="code"> |
|||
<el-input |
|||
v-model="form.code" |
|||
placeholder="请输入验证码" |
|||
clearable |
|||
style="width: 60%" |
|||
prefix-icon="el-icon-message" |
|||
/> |
|||
<el-button |
|||
type="success" |
|||
class="code-btn" |
|||
@click="handleGetCode" |
|||
:disabled="codeBtnDisabled" |
|||
> |
|||
{{ codeBtnText }} |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<!-- 用户名 --> |
|||
<el-form-item label="用户名" prop="username"> |
|||
<el-input |
|||
v-model="form.username" |
|||
placeholder="账户唯一识别,可用来登录" |
|||
clearable |
|||
prefix-icon="el-icon-user" |
|||
/> |
|||
<div class="form-tip"> |
|||
请输入4-32位字符,只能包含英文字母、数字和下划线,不能为纯数字/不能为纯下划线 |
|||
</div> |
|||
</el-form-item> |
|||
|
|||
<!-- 设重密码 --> |
|||
<el-form-item label="设重密码" prop="password"> |
|||
<el-input |
|||
v-model="form.password" |
|||
type="password" |
|||
placeholder="请设置登录密码" |
|||
clearable |
|||
prefix-icon="el-icon-lock" |
|||
@input="handlePasswordInput" |
|||
/> |
|||
<div class="form-tip"> |
|||
请输入8-30位密码,密码必须同时包含字母和数字 |
|||
</div> |
|||
</el-form-item> |
|||
|
|||
<!-- 确认密码 --> |
|||
<el-form-item label="确认密码" prop="confirmPassword"> |
|||
<el-input |
|||
v-model="form.confirmPassword" |
|||
type="password" |
|||
placeholder="请再次输入登录密码" |
|||
clearable |
|||
prefix-icon="el-icon-lock" |
|||
/> |
|||
<div class="form-tip">请与上方填写的密码保持一致</div> |
|||
</el-form-item> |
|||
|
|||
<!-- 协议勾选 --> |
|||
<el-form-item> |
|||
<el-checkbox v-model="agreement" class="agreement-checkbox"> |
|||
我已阅读并同意 |
|||
<a href="javascript:;" class="protocol-link">《用户注册协议》</a> |
|||
《<a href="javascript:;" class="protocol-link">隐私协议</a>》 |
|||
</el-checkbox> |
|||
</el-form-item> |
|||
|
|||
<!-- 注册按钮 --> |
|||
<el-form-item> |
|||
<el-button |
|||
type="primary" |
|||
class="register-btn" |
|||
@click="handleRegister" |
|||
:disabled="!agreement || submitting" |
|||
> |
|||
{{ submitting ? "注册中..." : "立即注册" }} |
|||
</el-button> |
|||
</el-form-item> |
|||
|
|||
<!-- 兜底提示 --> |
|||
<div class="bottom-tip" v-if="false"> |
|||
如需注册供应商请点击 |
|||
<a href="javascript:;" class="supplier-link">这里</a> |
|||
</div> |
|||
</el-form> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "RegisterPage", |
|||
data() { |
|||
// 密码强度校验函数 |
|||
const validatePassword = (rule, value, callback) => { |
|||
if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,30}$/.test(value)) { |
|||
callback(new Error("请输入8-30位密码,必须同时包含字母和数字")); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}; |
|||
|
|||
// 用户名校验函数 |
|||
const validateUsername = (rule, value, callback) => { |
|||
if (!/^(?!_+$)(?!\d+$)[a-zA-Z0-9_]{4,32}$/.test(value)) { |
|||
callback( |
|||
new Error( |
|||
"请输入4-32位字符,只能包含字母、数字和下划线,不能为纯数字/纯下划线" |
|||
) |
|||
); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}; |
|||
|
|||
return { |
|||
form: { |
|||
phone: "", |
|||
code: "", |
|||
username: "", |
|||
password: "", |
|||
confirmPassword: "", |
|||
}, |
|||
rules: { |
|||
phone: [ |
|||
{ required: true, message: "请输入手机号码", trigger: "blur" }, |
|||
{ |
|||
pattern: /^1\d{10}$/, |
|||
message: "请输入正确的手机号码格式", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
code: [ |
|||
{ required: true, message: "请输入验证码", trigger: "blur" }, |
|||
{ |
|||
pattern: /^\d{6}$/, |
|||
message: "请输入6位数字验证码", |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
username: [ |
|||
{ required: true, message: "请输入用户名", trigger: "blur" }, |
|||
{ validator: validateUsername, trigger: "blur" }, |
|||
], |
|||
password: [ |
|||
{ required: true, message: "请输入密码", trigger: "blur" }, |
|||
{ validator: validatePassword, trigger: "blur" }, |
|||
], |
|||
confirmPassword: [ |
|||
{ required: true, message: "请确认密码", trigger: "blur" }, |
|||
{ |
|||
validator: (rule, value, callback) => { |
|||
if (value !== this.form.password) { |
|||
callback(new Error("两次输入密码不一致")); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}, |
|||
trigger: "blur", |
|||
}, |
|||
], |
|||
}, |
|||
agreement: false, // 协议勾选状态 |
|||
codeBtnText: "获取验证码", |
|||
codeBtnDisabled: false, |
|||
submitting: false, // 注册按钮加载状态 |
|||
countdown: 60, // 验证码倒计时 |
|||
}; |
|||
}, |
|||
methods: { |
|||
// 获取验证码 |
|||
handleGetCode() { |
|||
if (!this.form.phone) { |
|||
this.$message.warning("请先输入手机号码"); |
|||
return; |
|||
} |
|||
// 模拟接口请求 |
|||
this.codeBtnDisabled = true; |
|||
this.codeBtnText = `重新发送(${this.countdown}s)`; |
|||
const timer = setInterval(() => { |
|||
this.countdown--; |
|||
this.codeBtnText = `重新发送(${this.countdown}s)`; |
|||
if (this.countdown <= 0) { |
|||
clearInterval(timer); |
|||
this.codeBtnText = "获取验证码"; |
|||
this.codeBtnDisabled = false; |
|||
this.countdown = 60; |
|||
} |
|||
}, 1000); |
|||
// 实际项目中可替换为: |
|||
// axios.post('/api/sendCode', { phone: this.form.phone }).then(res => {}) |
|||
}, |
|||
|
|||
// 密码输入实时校验(可选) |
|||
handlePasswordInput() { |
|||
// 可在此实现密码强度实时反馈 |
|||
}, |
|||
|
|||
// 注册提交 |
|||
handleRegister() { |
|||
this.$refs.registerForm.validate((valid) => { |
|||
if (valid && this.agreement) { |
|||
this.submitting = true; |
|||
// 模拟注册请求 |
|||
setTimeout(() => { |
|||
this.$message.success("注册成功!"); |
|||
this.submitting = false; |
|||
// 实际项目中可替换为: |
|||
// axios.post('/api/register', this.form).then(res => { |
|||
// this.$router.push('/login') |
|||
// }) |
|||
}, 1500); |
|||
} else if (!this.agreement) { |
|||
this.$message.warning("请勾选用户协议"); |
|||
} |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.register-page { |
|||
min-height: 100vh; |
|||
background-color: #fff; |
|||
padding: 40px 20px; |
|||
|
|||
// 顶部区域 |
|||
.header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
margin-bottom: 40px; |
|||
|
|||
.logo-area { |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.logo-img { |
|||
height: 40px; |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
.domain { |
|||
font-size: 14px; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.hotline { |
|||
font-size: 14px; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
// 表单容器 |
|||
.form-container { |
|||
max-width: 600px; |
|||
margin: 0 auto; |
|||
|
|||
.form-title { |
|||
font-size: 20px; |
|||
font-weight: bold; |
|||
margin-bottom: 30px; |
|||
color: #333; |
|||
} |
|||
|
|||
// 表单项间距 |
|||
.el-form-item { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
// 验证码按钮 |
|||
.code-btn { |
|||
margin-left: 10px; |
|||
padding: 0 20px; |
|||
height: 40px; |
|||
} |
|||
|
|||
// 表单提示文本 |
|||
.form-tip { |
|||
font-size: 12px; |
|||
color: #999; |
|||
margin-top: 4px; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
// 协议勾选 |
|||
.agreement-checkbox { |
|||
font-size: 12px; |
|||
color: #666; |
|||
|
|||
.protocol-link { |
|||
color: #ff4d4f; |
|||
text-decoration: underline; |
|||
margin: 0 4px; |
|||
} |
|||
} |
|||
|
|||
// 注册按钮 |
|||
.register-btn { |
|||
width: 100%; |
|||
background-color: #c00; |
|||
border: none; |
|||
&:hover { |
|||
background-color: #a00; |
|||
} |
|||
} |
|||
|
|||
// 兜底提示 |
|||
.bottom-tip { |
|||
text-align: center; |
|||
margin-top: 20px; |
|||
font-size: 12px; |
|||
color: #666; |
|||
|
|||
.supplier-link { |
|||
color: #ff4d4f; |
|||
text-decoration: underline; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
Loading…
Reference in new issue