封装request

This commit is contained in:
小林
2025-08-05 20:54:21 +08:00
parent 07e409f037
commit d9dffb2537
9 changed files with 682 additions and 72 deletions

16
package-lock.json generated
View File

@@ -3812,7 +3812,6 @@
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@@ -3852,7 +3851,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3873,7 +3871,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3894,7 +3891,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3915,7 +3911,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3936,7 +3931,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3957,7 +3951,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3978,7 +3971,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3999,7 +3991,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4020,7 +4011,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4041,7 +4031,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4062,7 +4051,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4083,7 +4071,6 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4104,7 +4091,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6100,7 +6086,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"bin": { "bin": {
@@ -8812,7 +8797,6 @@
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },

51
src/api/login.js Normal file
View File

@@ -0,0 +1,51 @@
import { request } from '../utils/request'
/**
* 用户登录
* @param {Object} data - 登录数据 {username, password}
* @returns {Promise}
*/
export function login(data) {
return request({
url: '/user/login',
method: 'POST',
data
})
}
/**
* 获取用户信息
* @returns {Promise}
*/
export function getUserInfo() {
return request({
url: '/user/info',
method: 'GET'
})
}
/**
* 更新用户信息
* @param {Object} data - 用户信息
* @returns {Promise}
*/
export function updateUserInfo(data) {
return request({
url: '/user/info',
method: 'PUT',
data
})
}
/**
* 用户注销
* @returns {Promise}
*/
export function logout() {
return request({
url: '/user/logout',
method: 'POST'
})
}

View File

@@ -1,46 +1,47 @@
import { createSSRApp } from 'vue' import { createSSRApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
// 导入语言包 import { request, http } from '@/utils/request' // 引入封装的request
import en from './static/lang/enUS.json' import en from './static/lang/enUS.json'
import cn from './static/lang/zh_CN.json' import zh from './static/lang/zh_CN.json'
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
// 获取系统语言适配uni-app环境 // 1. 获取系统语言
const systemInfo = uni.getSystemInfoSync() || {}; const systemInfo = uni.getSystemInfoSync() || {}
const systemLanguage = systemInfo.language || 'zh-CN'; const systemLanguage = systemInfo.language || 'zh-CN'
let curLang = 'cn'; let curLang = 'zh'
// 语言判断(转换为小写处理)
switch (systemLanguage.toLowerCase()) { switch (systemLanguage.toLowerCase()) {
case 'en': case 'en':
case 'en-us': case 'en-us':
curLang = 'en'; curLang = 'en'
break; break
case 'zh': case 'zh':
case 'zh-cn': case 'zh-cn':
case 'zh-hans': case 'zh-hans':
curLang = 'cn'; curLang = 'zh'
break; break
} }
// 配置i18n // 2. 配置i18n
const i18n = createI18n({ const i18n = createI18n({
legacy: false, legacy: false,
locale: curLang, // 使用检测到的系统语言 locale: curLang,
fallbackLocale: 'en', fallbackLocale: 'en',
messages: { messages: { en, zh },
en, // 英文语言包
cn // 中文语言包
},
missing: (locale, key) => { missing: (locale, key) => {
console.error(`[i18n] 缺失: '${key}' 在 '${locale}' 语言包中不存在`); console.error(`[i18n] 缺失: '${key}' 在 '${locale}' 语言包中不存在`)
return process.env.NODE_ENV === 'development' ? `[缺失: ${key}]` : ''; return process.env.NODE_ENV === 'development' ? `[缺失: ${key}]` : ''
} }
}) })
// 3. 挂载request到全局
app.config.globalProperties.$request = request
app.config.globalProperties.$http = http
// 4. 使用i18n插件
app.use(i18n) app.use(i18n)
return { return {
@@ -52,5 +53,15 @@ export function createApp() {
// 非SSR模式下挂载应用 // 非SSR模式下挂载应用
if (!globalThis.__IS_SSR__) { if (!globalThis.__IS_SSR__) {
const { app } = createApp() const { app } = createApp()
// 全局错误处理
app.config.errorHandler = (err) => {
console.error('全局错误:', err)
uni.showToast({
title: '发生错误,请重试',
icon: 'none'
})
}
app.mount('#app') app.mount('#app')
} }

View File

@@ -11,6 +11,12 @@
"style": { "style": {
"navigationBarTitleText": "" "navigationBarTitleText": ""
} }
},
{
"path": "pages/home/index",
"style": {
"navigationBarTitleText": ""
}
} }
], ],
"globalStyle": { "globalStyle": {

372
src/pages/home/index.vue Normal file
View File

@@ -0,0 +1,372 @@
<template>
<view class="stock-home-container">
<!-- 顶部用户信息栏 -->
<view class="user-header">
<view class="user-info">
<image class="avatar" src="/static/images/avatar.png" mode="aspectFill"></image>
<view class="user-details">
<text class="username">Welcome, {{userName}}</text>
<text class="account-id">Account ID: {{accountId}}</text>
</view>
</view>
<view class="account-summary">
<text class="total-assets">${{formatNumber(totalAssets)}}</text>
<text class="asset-change" :class="{'positive': dailyChange >= 0, 'negative': dailyChange < 0}">
{{dailyChange >= 0 ? '+' : ''}}{{formatNumber(dailyChange)}} ({{dailyChangePercent >= 0 ? '+' : ''}}{{dailyChangePercent}}%)
</text>
</view>
</view>
<!-- 主要功能入口 -->
<view class="quick-actions">
<view class="action-item" @click="navigateTo('trade')">
<image src="/static/icons/trade.png"></image>
<text>Trade</text>
</view>
<view class="action-item" @click="navigateTo('deposit')">
<image src="/static/icons/deposit.png"></image>
<text>Deposit</text>
</view>
<view class="action-item" @click="navigateTo('withdraw')">
<image src="/static/icons/withdraw.png"></image>
<text>Withdraw</text>
</view>
<view class="action-item" @click="navigateTo('transfer')">
<image src="/static/icons/transfer.png"></image>
<text>Transfer</text>
</view>
</view>
<!-- 自选股票列表 -->
<view class="watchlist-section">
<view class="section-header">
<text class="section-title">Watchlist</text>
<text class="section-more" @click="navigateTo('watchlist')">More ></text>
</view>
<scroll-view scroll-x class="stock-scroll">
<view class="stock-card" v-for="(stock, index) in watchlist" :key="index" @click="viewStockDetail(stock)">
<text class="stock-symbol">{{stock.symbol}}</text>
<text class="stock-name">{{stock.name}}</text>
<text class="stock-price">${{stock.price}}</text>
<text class="stock-change" :class="{'positive': stock.change >= 0, 'negative': stock.change < 0}">
{{stock.change >= 0 ? '+' : ''}}{{stock.change}}%
</text>
</view>
</scroll-view>
</view>
<!-- 市场指数 -->
<view class="market-indices">
<view class="section-header">
<text class="section-title">Market Indices</text>
<text class="section-more" @click="navigateTo('markets')">More ></text>
</view>
<view class="indices-container">
<view class="index-item" v-for="(index, i) in marketIndices" :key="i">
<text class="index-name">{{index.name}}</text>
<text class="index-value">{{index.value}}</text>
<text class="index-change" :class="{'positive': index.change >= 0, 'negative': index.change < 0}">
{{index.change >= 0 ? '+' : ''}}{{index.change}}%
</text>
</view>
</view>
</view>
<!-- 新闻资讯 -->
<view class="news-section">
<view class="section-header">
<text class="section-title">Market News</text>
<text class="section-more" @click="navigateTo('news')">More ></text>
</view>
<view class="news-list">
<view class="news-item" v-for="(news, idx) in marketNews" :key="idx" @click="viewNewsDetail(news)">
<image class="news-image" :src="news.imageUrl" mode="aspectFill"></image>
<view class="news-content">
<text class="news-title">{{news.title}}</text>
<text class="news-time">{{formatTime(news.time)}}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
userName: "John Doe",
accountId: "EU20230001",
totalAssets: 125430.56,
dailyChange: 1254.32,
dailyChangePercent: 1.01,
watchlist: [
{ symbol: "AAPL", name: "Apple Inc.", price: 175.34, change: 1.23 },
{ symbol: "MSFT", name: "Microsoft", price: 328.39, change: 0.56 },
{ symbol: "TSLA", name: "Tesla", price: 260.54, change: -2.34 },
{ symbol: "AMZN", name: "Amazon", price: 134.56, change: 0.89 },
{ symbol: "GOOGL", name: "Alphabet", price: 125.67, change: -0.45 }
],
marketIndices: [
{ name: "S&P 500", value: 4524.09, change: 0.42 },
{ name: "NASDAQ", value: 14019.31, change: 0.85 },
{ name: "Dow 30", value: 34721.12, change: -0.12 },
{ name: "FTSE 100", value: 7527.53, change: 0.23 }
],
marketNews: [
{
title: "Fed signals more rate hikes to combat inflation",
imageUrl: "/static/images/news1.jpg",
time: "2023-06-15T09:30:00Z"
},
{
title: "Tech stocks rally as AI boom continues",
imageUrl: "/static/images/news2.jpg",
time: "2023-06-15T08:45:00Z"
},
{
title: "European markets open higher amid positive earnings",
imageUrl: "/static/images/news3.jpg",
time: "2023-06-15T07:15:00Z"
}
]
}
},
methods: {
// formatNumber(num) {
// return num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
// },
// formatTime(timeString) {
// const date = new Date(timeString);
// return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// },
// navigateTo(page) {
// uni.navigateTo({
// url: `/pages/${page}/${page}`
// });
// },
// viewStockDetail(stock) {
// uni.navigateTo({
// url: `/pages/stock/detail?symbol=${stock.symbol}`
// });
// },
// viewNewsDetail(news) {
// uni.navigateTo({
// url: `/pages/news/detail?title=${encodeURIComponent(news.title)}`
// });
// }
}
}
</script>
<style lang="scss">
.stock-home-container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
padding-bottom: 120rpx; /* 给底部导航留空间 */
}
.user-header {
background: linear-gradient(135deg, #1976D2, #2196F3);
border-radius: 16rpx;
padding: 30rpx;
color: white;
margin-bottom: 20rpx;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.user-details {
display: flex;
flex-direction: column;
}
.username {
font-size: 32rpx;
font-weight: bold;
}
.account-id {
font-size: 24rpx;
opacity: 0.8;
}
.account-summary {
display: flex;
flex-direction: column;
}
.total-assets {
font-size: 48rpx;
font-weight: bold;
}
.asset-change {
font-size: 28rpx;
}
.positive {
color: #4CAF50;
}
.negative {
color: #F44336;
}
.quick-actions {
display: flex;
justify-content: space-between;
background-color: white;
border-radius: 16rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
}
.action-item image {
width: 60rpx;
height: 60rpx;
margin-bottom: 10rpx;
}
.action-item text {
font-size: 24rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin: 30rpx 0 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
}
.section-more {
font-size: 26rpx;
color: #1976D2;
}
.stock-scroll {
white-space: nowrap;
width: 100%;
}
.stock-card {
display: inline-block;
background-color: white;
border-radius: 12rpx;
padding: 20rpx;
margin-right: 20rpx;
width: 200rpx;
}
.stock-symbol {
font-size: 28rpx;
font-weight: bold;
display: block;
}
.stock-name {
font-size: 24rpx;
color: #666;
display: block;
margin: 10rpx 0;
}
.stock-price {
font-size: 28rpx;
font-weight: bold;
display: block;
}
.stock-change {
font-size: 24rpx;
display: block;
}
.indices-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.index-item {
background-color: white;
border-radius: 12rpx;
padding: 20rpx;
width: 48%;
margin-bottom: 15rpx;
}
.index-name {
font-size: 26rpx;
color: #666;
}
.index-value {
font-size: 32rpx;
font-weight: bold;
margin: 10rpx 0;
display: block;
}
.news-list {
background-color: white;
border-radius: 16rpx;
overflow: hidden;
}
.news-item {
display: flex;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.news-image {
width: 160rpx;
height: 120rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.news-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.news-title {
font-size: 28rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.news-time {
font-size: 24rpx;
color: #999;
}
</style>

View File

@@ -140,6 +140,11 @@
</template> </template>
<script> <script>
// import {
// login,
// getUserInfo,
// } from '@/api/login'
export default { export default {
data() { data() {
return { return {
@@ -158,6 +163,7 @@ export default {
onLoad() { onLoad() {
this.currentLang = this.$i18n.locale this.currentLang = this.$i18n.locale
this.loadRememberedAccount() this.loadRememberedAccount()
// console.log(login(1))
}, },
methods: { methods: {
togglePasswordVisibility() { togglePasswordVisibility() {
@@ -194,37 +200,38 @@ export default {
}, },
handleLogin() { handleLogin() {
if (!this.agreeTerms) { // if (!this.agreeTerms) {
this.showError = true // this.showError = true
this.errorMessage = this.$t('login.errors.agreeTermsFirst') // this.errorMessage = this.$t('login.errors.agreeTermsFirst')
return // return
} // }
if (!this.username) { // if (!this.username) {
this.showError = true // this.showError = true
this.errorMessage = this.$t('login.errors.emptyUsername') // this.errorMessage = this.$t('login.errors.emptyUsername')
return // return
} // }
if (this.username.length !== 6 || !/^\d{6}$/.test(this.username)) { // if (this.username.length !== 6 || !/^\d{6}$/.test(this.username)) {
this.showError = true // this.showError = true
this.errorMessage = this.$t('login.errors.invalidIdCard') // this.errorMessage = this.$t('login.errors.invalidIdCard')
return // return
} // }
if (!this.password) { // if (!this.password) {
this.showError = true // this.showError = true
this.errorMessage = this.$t('login.errors.emptyPassword') // this.errorMessage = this.$t('login.errors.emptyPassword')
return // return
} // }
this.isLoading = true // this.isLoading = true
this.showError = false // this.showError = false
setTimeout(() => { setTimeout(() => {
console.log('登录信息:', { username: this.username, password: this.password }) console.log('登录信息:', { username: this.username, password: this.password })
this.saveRememberedAccount() this.saveRememberedAccount()
uni.navigateTo({ url: '/pages/index/index' }) uni.redirectTo({ url: '/pages/home/index' })
console.log('登录成功')
this.isLoading = false this.isLoading = false
}, 1500) }, 1500)
}, },
@@ -246,18 +253,19 @@ export default {
}) })
}, },
handleForgotPassword() { //修改密码的js实现方法
uni.showModal({ // handleForgotPassword() {
title: this.$t('login.forgotPassword'), // uni.showModal({
content: this.$t('login.forgotPasswordDesc'), // title: this.$t('login.forgotPassword'),
confirmText: this.$t('login.resetPassword'), // content: this.$t('login.forgotPasswordDesc'),
success: (res) => { // confirmText: this.$t('login.resetPassword'),
if (res.confirm) { // success: (res) => {
uni.navigateTo({ url: '/pages/reset-password/reset-password' }) // if (res.confirm) {
} // uni.navigateTo({ url: '/pages/reset-password/reset-password' })
} // }
}) // }
} // })
// }
} }
} }
</script> </script>

View File

@@ -1,5 +1,45 @@
{ {
"index.Loading": "Loading", "index.Loading": "Loading...",
"index.errMsg": "Error" "index.errMsg": "Failed to load",
"login.loginButton": "Login",
"login.usernamePlaceholder": "Please enter username",
"login.idCardLabel": "Last 6 digits of ID card",
"login.passwordPlaceholder": "Please enter password",
"login.passwordLabel": "Password",
"login.appTitle": "European Stock Software",
"login.rememberPassword": "Remember password",
"login.forgotPassword": "Forgot password",
"login.agreeTermsPrefix": "I have read and agree to",
"login.agreeTermsSuffix": "User Agreement",
"login.userAgreement": "User Agreement",
"login.and": "and",
"login.privacyPolicy": "Privacy Policy",
"login.registerButton": "Register",
"login.noAccount": "No account?",
"login.languagechangeclose": "Done",
"login.chooseLanguage": "Select Language",
"login.changelanguage": "Change Language",
"register.chooseLanguage": "Select Language",
"register.languagechangeclose": "Done",
"register.appTitle": "European & American Stock Software",
"register.registerDesc": "Create an account to continue",
"register.idCardLabel": "Last 6 digits of ID card",
"register.idCardPlaceholder": "Please enter last 6 digits of ID card",
"register.passwordLabel": "Password",
"register.passwordPlaceholder": "Please enter password",
"register.confirmPasswordLabel": "Confirm Password",
"register.confirmPasswordPlaceholder": "Please confirm password again",
"register.inviteCodeLabel": "Invitation Code",
"register.inviteCodePlaceholder": "Please enter invitation code",
"register.emailLabel": "Email",
"register.emailPlaceholder": "Please enter email",
"register.agreeTermsPrefix": "I have read and agree to",
"register.userAgreement": "User Agreement",
"register.and": "and",
"register.privacyPolicy": "Privacy Policy",
"register.registerButton": "Register",
"register.hasAccount": "Already have an account?",
"register.loginNow": "Login now",
"register.registerSuccess": "Registration successful"
} }

View File

@@ -20,6 +20,10 @@
"login.languagechangeclose": "完成", "login.languagechangeclose": "完成",
"login.chooseLanguage": "选择语言", "login.chooseLanguage": "选择语言",
"login.changelanguage": "切换语言", "login.changelanguage": "切换语言",
"login.errors.agreeTermsFirst": "请先同意用户协议和隐私政策",
"login.errors.emptyUsername": "请输入用户名",
"login.errors.invalidIdCard": "请输入正确身份证号码的后六位",
"login.errors.emptyPassword": "请输入密码",
"register.chooseLanguage": "选择语言", "register.chooseLanguage": "选择语言",

134
src/utils/request.js Normal file
View File

@@ -0,0 +1,134 @@
// utils/request.js
const BASE_URL = process.env.VUE_APP_API_BASE_URL || '/api'
const TOKEN_KEY = 'auth_token'
/**
* 基础请求方法
*/
export function request(options) {
// 默认配置
const defaultOptions = {
url: '',
method: 'GET',
data: {},
header: {
'Content-Type': 'application/json'
},
timeout: 10000,
showLoading: false,
loadingText: '加载中...',
checkAuth: true
}
// 合并配置
options = { ...defaultOptions, ...options }
// 请求拦截 - 处理URL
if (!options.url.startsWith('http')) {
options.url = BASE_URL + options.url
}
// 请求拦截 - 添加token
if (options.checkAuth) {
const token = uni.getStorageSync(TOKEN_KEY)
if (token) {
options.header.Authorization = `Bearer ${token}`
}
}
// 显示加载提示
if (options.showLoading) {
uni.showLoading({
title: options.loadingText,
mask: true
})
}
return new Promise((resolve, reject) => {
uni.request({
...options,
success: (res) => {
handleResponse(res, resolve, reject)
},
fail: (err) => {
handleError(err, reject)
},
complete: () => {
if (options.showLoading) {
uni.hideLoading()
}
}
})
})
}
// 响应处理函数
function handleResponse(res, resolve, reject) {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
handleUnauthorized()
reject(new Error('未授权'))
} else {
handleHttpError(res.statusCode)
reject(new Error(`HTTP错误: ${res.statusCode}`))
}
}
// 错误处理函数
function handleError(err, reject) {
uni.showToast({
title: '网络连接错误',
icon: 'none',
duration: 2000
})
reject(err)
}
// HTTP错误处理
function handleHttpError(statusCode) {
const errorMap = {
400: '请求参数错误',
403: '拒绝访问',
404: '资源不存在',
500: '服务器错误'
}
uni.showToast({
title: errorMap[statusCode] || `请求错误: ${statusCode}`,
icon: 'none',
duration: 2000
})
}
// 未授权处理
function handleUnauthorized() {
uni.removeStorageSync(TOKEN_KEY)
uni.showToast({
title: '登录已过期',
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateTo({
url: '/pages/login/login'
})
}, 1500)
}
/**
* 快捷方法
*/
export const http = {
get: (url, params, options) =>
request({ url, params, method: 'GET', ...options }),
post: (url, data, options) =>
request({ url, data, method: 'POST', ...options }),
put: (url, data, options) =>
request({ url, data, method: 'PUT', ...options }),
delete: (url, data, options) =>
request({ url, data, method: 'DELETE', ...options })
}