Files
tuanwei-uniapp/src/pages/login/index.vue
T
ticket-02 agent 81a093120c build(cli): 迁移到 uni-app 官方 Vite CLI 骨架(src/ 布局)
按地图口径把 HBuilderX 工程改成官方 #vite(JS 版)CLI 工程:

- 业务代码 git mv 进 src/(176 个 rename,逐文件字节比对零内容改动):
  App.vue / main.js / manifest.json / pages.json / uni.scss /
  pages/ / packageMine/ / components/ / utils/ / static/ /
  uni_modules/ / uni.promisify.adaptor.js
- 新增官方骨架:package.json(@dcloudio/* 同版本 pin
  3.0.0-5020620260917001,vite 精确 5.2.8,vue ^3.4.21;只留 h5 与
  mp-weixin 脚本)、vite.config.js、shims-uni.d.ts;补 sass devDependency
  以支持既有 lang="scss"
- index.html 沿用本仓文件,入口改为 /src/main.js
- manifest.json 删除 app-plus / quickapp / mp-alipay / mp-baidu /
  mp-toutiao 五个死平台段,只留 mp-weixin
- .gitignore 加 dist/
- pnpm-workspace.yaml 新增 allowBuilds 白名单:pnpm 12 起
  onlyBuiltDependencies 已被移除,且 strictDepBuilds 默认 true 会把
  「未复核构建脚本」判成非零退出。白名单只列传递依赖
  esbuild / @parcel/watcher / core-js / core-js-pure。
  未使用 shamefully-hoist 或 node-linker=hoisted。

验收:pnpm build:mp-weixin 退出码 0,产物 app.json 的
pages / subPackages / tabBar / window 与改造前基线逐项一致;
project.config.json 落产物且 appid = wxf7ee9cd5801bf7a6。
2026-10-15 15:17:32 +08:00

363 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page">
<view class="hero-card">
<view class="hero-title">进入智慧团委小程序</view>
<view class="hero-desc">支持手机号密码登录、微信手机号授权登录,也支持学校统一身份认证登录。</view>
</view>
<view class="login-card">
<view class="tab-row">
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'password' }"
@tap="loginMode = 'password'"
>
手机号密码登录
</view>
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'wechat' }"
@tap="loginMode = 'wechat'"
>
微信授权登录
</view>
<view
class="tab-item"
:class="{ 'tab-item--active': loginMode === 'gxmu' }"
@tap="loginMode = 'gxmu'"
>
统一身份认证
</view>
</view>
<view v-if="loginMode === 'password'" class="form-section">
<uv-input
v-model.trim="form.username"
class="field-input"
type="text"
placeholder="请输入手机号或账号"
:maxlength="-1"
border="none"
/>
<uv-input
v-model="form.password"
class="field-input"
type="password"
placeholder="请输入密码"
:maxlength="-1"
border="none"
password
/>
<view class="submit-btn" :class="{ 'submit-btn--loading': loading }" @tap="submitPasswordLogin">
{{ loading ? '登录中...' : '立即登录' }}
</view>
</view>
<view v-else-if="loginMode === 'wechat'" class="form-section">
<view class="wechat-intro">授权后将使用微信手机号完成登录。</view>
<button
class="submit-btn submit-btn--wechat"
:disabled="loading"
open-type="getPhoneNumber"
@getphonenumber="handleWxPhoneLogin"
>
{{ loading ? '登录中...' : '微信授权登录' }}
</button>
</view>
<view v-else class="form-section">
<view class="wechat-intro">使用学校统一身份认证账号(学号)和密码登录。</view>
<uv-input
v-model.trim="ssoForm.username"
class="field-input"
type="text"
placeholder="请输入学号"
:maxlength="-1"
border="none"
/>
<uv-input
v-model="ssoForm.password"
class="field-input"
type="password"
placeholder="请输入统一身份认证密码"
:maxlength="-1"
border="none"
password
/>
<view class="submit-btn" :class="{ 'submit-btn--loading': loading }" @tap="submitGxmuLogin">
{{ loading ? '登录中...' : '统一身份认证登录' }}
</view>
</view>
</view>
</view>
</template>
<script>
import { loginByGxmuSso, loginByMpWxPhone, loginByPassword } from './service'
import { hasToken, setAuthInfo } from '../../utils/request'
export default {
data() {
return {
loginMode: 'password',
loading: false,
redirect: '',
wxCode: '',
form: {
username: '',
password: ''
},
ssoForm: {
username: '',
password: ''
}
}
},
onLoad(options) {
this.redirect = decodeURIComponent((options && options.redirect) || '')
},
onShow() {
if (hasToken()) {
this.navigateAfterLogin()
return
}
this.refreshWxCode()
},
methods: {
showToast(title) {
uni.showToast({
title,
icon: 'none'
})
},
refreshWxCode() {
uni.login({
success: (res) => {
this.wxCode = (res && res.code) || ''
}
})
},
navigateAfterLogin() {
const url = this.redirect || '/pages/index/index'
uni.reLaunch({
url
})
},
async submitPasswordLogin() {
if (this.loading) {
return
}
if (!this.form.username) {
this.showToast('请输入手机号或账号')
return
}
if (!this.form.password) {
this.showToast('请输入密码')
return
}
this.loading = true
try {
const result = await loginByPassword({
username: this.form.username,
password: this.form.password
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '登录失败')
} finally {
this.loading = false
}
},
async submitGxmuLogin() {
if (this.loading) {
return
}
if (!this.ssoForm.username) {
this.showToast('请输入学号')
return
}
if (!this.ssoForm.password) {
this.showToast('请输入统一身份认证密码')
return
}
this.loading = true
try {
const result = await loginByGxmuSso({
username: this.ssoForm.username,
password: this.ssoForm.password
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '统一身份认证登录失败')
} finally {
this.loading = false
}
},
async handleWxPhoneLogin(event) {
if (this.loading) {
return
}
const detail = (event && event.detail) || {}
if (!detail.code) {
this.showToast(detail.errMsg || '未获取到微信手机号凭证')
return
}
this.loading = true
try {
if (!this.wxCode) {
await new Promise((resolve) => {
uni.login({
success: (res) => {
this.wxCode = (res && res.code) || ''
resolve()
},
fail: () => resolve()
})
})
}
const result = await loginByMpWxPhone({
phoneCode: detail.code,
code: this.wxCode || undefined
})
setAuthInfo(result)
this.showToast('登录成功')
setTimeout(() => {
this.navigateAfterLogin()
}, 300)
} catch (error) {
this.showToast((error && error.message) || '微信登录失败')
this.refreshWxCode()
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
padding: 28rpx 24rpx 40rpx;
background:
radial-gradient(circle at top left, rgba(20, 150, 242, 0.14), transparent 30%),
linear-gradient(180deg, #f5fbff 0%, #eef7ff 100%);
}
.hero-card,
.login-card {
border-radius: 32rpx;
box-shadow: 0 18rpx 40rpx rgba(20, 118, 194, 0.12);
}
.hero-card {
padding: 34rpx 30rpx;
background: linear-gradient(145deg, #0d6fba 0%, #1496f2 58%, #5bc2ff 100%);
color: #fff;
}
.hero-badge {
display: inline-flex;
padding: 10rpx 18rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.16);
font-size: 22rpx;
}
.hero-title {
margin-top: 22rpx;
font-size: 42rpx;
font-weight: 700;
}
.hero-desc {
margin-top: 14rpx;
font-size: 24rpx;
line-height: 1.7;
color: rgba(255, 255, 255, 0.86);
}
.login-card {
margin-top: 24rpx;
padding: 28rpx 24rpx;
background: rgba(255, 255, 255, 0.94);
}
.tab-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14rpx;
}
.tab-item {
height: 82rpx;
border-radius: 22rpx;
background: #edf5ff;
color: #1496f2;
font-size: 22rpx;
font-weight: 600;
line-height: 82rpx;
text-align: center;
}
.tab-item--active {
background: linear-gradient(135deg, #1496f2, #0d6fba);
color: #fff;
}
.form-section {
margin-top: 22rpx;
}
:deep(.field-input.uv-input) {
width: 100%;
height: 88rpx;
margin-top: 16rpx;
padding: 0 24rpx !important;
border-radius: 24rpx;
background: #f8fbff !important;
border: 1rpx solid rgba(20, 150, 242, 0.12) !important;
}
:deep(.field-input.uv-input .uv-input__content__field-wrapper__field) {
font-size: 24rpx;
color: #1f2329;
}
.wechat-intro {
font-size: 24rpx;
line-height: 1.7;
color: #667085;
}
.submit-btn {
width: 100%;
height: 88rpx;
margin-top: 22rpx;
border: none;
border-radius: 24rpx;
background: linear-gradient(135deg, #1496f2, #0d6fba);
color: #fff;
font-size: 26rpx;
font-weight: 700;
line-height: 88rpx;
text-align: center;
}
.submit-btn--wechat {
background: linear-gradient(135deg, #1dbf73, #12a150);
}
.submit-btn--loading {
opacity: 0.72;
}
</style>