<template>
|
<view class="page">
|
<view class="bg-gradient"></view>
|
|
<view class="hero">
|
<view class="hero-text">
|
<text class="welcome-text">欢迎使用</text>
|
<text class="platform-title">攀枝花市</text>
|
<text class="platform-title">东区人才服务平台</text>
|
</view>
|
<image class="mascot" src="/static/login-mascot.png" mode="aspectFit"></image>
|
</view>
|
|
<view class="card">
|
<view class="field-heading">
|
<text class="field-label">账号</text>
|
<text v-if="accountError.length > 0" class="field-error">{{accountError}}</text>
|
</view>
|
<view class="input-box">
|
<image class="input-icon" src="/static/icons/user.svg" mode="aspectFit"></image>
|
<input class="input-field" type="text" maxlength="32" v-model="account" placeholder="请输入账号" :placeholder-style="placeholderStyle" @input="clearAccountError" />
|
</view>
|
|
<view class="field-heading">
|
<text class="field-label">密码</text>
|
<text v-if="passwordError.length > 0" class="field-error">{{passwordError}}</text>
|
</view>
|
<view class="input-box">
|
<image class="input-icon" src="/static/icons/lock.svg" mode="aspectFit"></image>
|
<input class="input-field" type="text" :password="true" maxlength="64" v-model="password" placeholder="请输入登录密码" :placeholder-style="placeholderStyle" @input="clearPasswordError" />
|
</view>
|
|
<text v-if="loginError.length > 0" class="login-error">{{loginError}}</text>
|
|
<text v-if="agreementError.length > 0" class="agreement-error">{{agreementError}}</text>
|
|
<view class="agreement" @tap="toggleAgree">
|
<view class="checkbox" :class="agreed ? 'checked' : (agreementError.length > 0 ? 'input-invalid' : '')">
|
<text v-if="agreed" class="check-mark">✓</text>
|
</view>
|
<text class="agree-text">我已阅读并同意</text>
|
<text class="agree-link" @tap.stop="showAgreement">《商户服务协议》</text>
|
<text class="agree-link" @tap.stop="showPrivacy">《隐私政策》</text>
|
</view>
|
|
<button class="login-btn" :loading="submitting" :disabled="submitting" @tap="handleLogin"><text class="login-btn-text">登录</text></button>
|
</view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { loginRemote } from '../../services/auth.uts'
|
import { hasActiveSession } from '../../services/session.uts'
|
import { apiEnvironment, skipMerchantLoginInDevelopment } from '../../config/api.uts'
|
const account = ref('')
|
const password = ref('')
|
const agreed = ref(false)
|
const accountError = ref('')
|
const passwordError = ref('')
|
const loginError = ref('')
|
const agreementError = ref('')
|
const submitting = ref(false)
|
const placeholderStyle = 'color:#C0C0C0;font-size:28rpx;'
|
|
onLoad(() => {
|
if (apiEnvironment == 'development' && skipMerchantLoginInDevelopment) {
|
uni.reLaunch({ url: '/pages/index/index' })
|
}
|
})
|
|
const toggleAgree = () => {
|
agreed.value = !agreed.value
|
if (agreed.value) {
|
agreementError.value = ''
|
}
|
}
|
|
const clearAccountError = () => {
|
accountError.value = ''
|
loginError.value = ''
|
}
|
|
const clearPasswordError = () => {
|
passwordError.value = ''
|
loginError.value = ''
|
}
|
|
const handleLogin = () => {
|
if (submitting.value) return
|
accountError.value = ''
|
passwordError.value = ''
|
loginError.value = ''
|
agreementError.value = ''
|
account.value = account.value.trim()
|
let hasError = false
|
if (account.value.length == 0) {
|
accountError.value = '账号未录入,请联系管理员'
|
hasError = true
|
}
|
if (password.value.length == 0) {
|
passwordError.value = '请输入登录密码'
|
hasError = true
|
}
|
if (!agreed.value) {
|
agreementError.value = '请先阅读并同意相关协议'
|
hasError = true
|
}
|
if (hasError) {
|
return
|
}
|
submitting.value = true
|
uni.showLoading({ title: '正在登录', mask: true })
|
loginRemote(account.value, password.value, () => {
|
uni.hideLoading()
|
submitting.value = false
|
password.value = ''
|
uni.reLaunch({ url: '/pages/index/index' })
|
}, (error: any) => {
|
uni.hideLoading()
|
submitting.value = false
|
if (error.code == 'UNAUTHORIZED') {
|
loginError.value = error.message != null && error.message.length > 0 ? error.message : '账号或密码错误'
|
return
|
}
|
uni.showToast({ title: error.message != null ? error.message : '登录失败,请稍后重试', icon: 'none' })
|
})
|
}
|
|
onShow(() => {
|
if (hasActiveSession()) uni.reLaunch({ url: '/pages/index/index' })
|
})
|
|
const showAgreement = () => {
|
uni.navigateTo({ url: '/pages/agreement-detail/agreement-detail?type=user&source=login' })
|
}
|
|
const showPrivacy = () => {
|
uni.navigateTo({ url: '/pages/agreement-detail/agreement-detail?type=privacy&source=login' })
|
}
|
</script>
|
|
<style>
|
.page { display: flex; flex: 1; flex-direction: column; width: 100%; min-height: 100vh; position: relative; overflow: hidden; background-color: #FFFFFF; }
|
.bg-gradient { position: absolute; top: 0; left: 0; right: 0; height: 660rpx; background-image: linear-gradient(112deg, #FF4B36 0%, #FF8B25 100%); }
|
|
.hero { display: flex; flex-direction: row; align-items: flex-end; height: 576rpx; padding: 0 50rpx 72rpx; position: relative; z-index: 1; box-sizing: border-box; }
|
.hero-text { display: flex; flex: 1; flex-direction: column; padding-bottom: 6rpx; position: relative; z-index: 1; }
|
.welcome-text { color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; font-weight: 600; margin-bottom: 18rpx; }
|
.platform-title { color: #FFFFFF; font-size: 50rpx; line-height: 62rpx; font-weight: 700; letter-spacing: 1rpx; }
|
.mascot { position: absolute; right: 28rpx; bottom: 58rpx; width: 322rpx; height: 266rpx; }
|
|
.card { display: flex; flex: 1; flex-direction: column; margin-top: -8rpx; padding: 62rpx 50rpx 56rpx; background-color: #FFFFFF; border-radius: 48rpx 48rpx 0 0; position: relative; z-index: 2; box-sizing: border-box; }
|
.field-heading { display: flex; flex-direction: row; align-items: center; min-height: 40rpx; margin-bottom: 16rpx; }
|
.field-label { color: #313744; font-size: 28rpx; line-height: 40rpx; font-weight: 700; }
|
.field-error { color: #F06A66; font-size: 24rpx; line-height: 34rpx; font-weight: 500; margin-left: 20rpx; }
|
|
.input-box { display: flex; flex-direction: row; align-items: center; height: 104rpx; background-color: #FFFFFF; border-radius: 30rpx; padding: 0 30rpx; margin-bottom: 34rpx; box-shadow: 0 14rpx 28rpx rgba(43, 44, 52, 0.10); box-sizing: border-box; }
|
.input-icon { width: 34rpx; height: 34rpx; margin-right: 20rpx; flex-shrink: 0; }
|
.input-field { flex: 1; height: 104rpx; font-size: 28rpx; color: #313744; }
|
|
.login-error { color: #F06A66; font-size: 24rpx; line-height: 34rpx; margin-top: -18rpx; margin-bottom: 20rpx; }
|
|
.agreement-error { color: #F06A66; font-size: 24rpx; line-height: 34rpx; margin-top: -18rpx; margin-bottom: 12rpx; }
|
|
.agreement { display: flex; flex-direction: row; align-items: center; margin-top: 2rpx; margin-bottom: 32rpx; min-height: 32rpx; }
|
.checkbox { display: flex; width: 28rpx; height: 28rpx; background-color: #FFFFFF; border-width: 2rpx; border-style: solid; border-color: #B8C0CC; border-radius: 6rpx; justify-content: center; align-items: center; margin-right: 14rpx; flex-shrink: 0; }
|
.checkbox.checked { background-color: #FF7028; border-color: #FF7028; }
|
.checkbox.input-invalid { border-color: #F05243; }
|
.check-mark { color: #FFFFFF; font-size: 20rpx; line-height: 24rpx; font-weight: 700; }
|
.agree-text { color: #596170; font-size: 24rpx; line-height: 32rpx; }
|
.agree-link { color: #F05243; font-size: 24rpx; line-height: 32rpx; }
|
|
.login-btn { display: flex; margin: 0; height: 100rpx; border-radius: 50rpx; background-image: linear-gradient(90deg, #FF8A24 0%, #FF4B36 100%); justify-content: center; align-items: center; line-height: 100rpx; box-shadow: 0 14rpx 26rpx rgba(255, 101, 48, 0.22); }
|
.login-btn::after { border-width: 0; }
|
.login-btn-text { color: #FFFFFF; font-size: 30rpx; line-height: 42rpx; font-weight: 700; }
|
</style>
|