2025-09-15

This commit is contained in:
2025-09-15 21:03:25 +08:00
parent 7f7ef99c18
commit 178bd0d1e9
26 changed files with 1606 additions and 25 deletions

20
api/auth.js Normal file
View File

@@ -0,0 +1,20 @@
import { http } from "../util/api"
// 认证相关API
export const authAPI = {
// 登录
login: (data) => http.post('/auth/login', data),
// 注册
register: (data) => http.post('/auth/register', data),
// 获取当前用户信息
me: () => http.get('/auth/me'),
// 修改密码
changePassword: (data) => http.put('/auth/change-password', data)
}
export default {
authAPI
}

16
api/captcha.js Normal file
View File

@@ -0,0 +1,16 @@
import {
http
} from "../util/api"
// 验证码相关API
export const captchaAPI = {
// 生成验证码
generate: () => http.get('/captcha/generate'),
// 验证验证码
verify: (data) => http.post('/captcha/verify', data)
}
export default {
captchaAPI
}

9
api/common.js Normal file
View File

@@ -0,0 +1,9 @@
import { http } from "../util/api";
export const commonAPI = {
getRegion: () => http.get('/regions/provinces'),
}
export default {
commonAPI
}

24
api/payment.js Normal file
View File

@@ -0,0 +1,24 @@
import { http } from "../util/api"
// 支付相关API
export const paymentAPI = {
// 获取支付方式
getMethods: () => http.get('/payment/methods'),
// 创建支付订单
createOrder: (data) => http.post('/payment/create-order', data),
// 查询支付状态
queryStatus: (outTradeNo) => http.get(`/payment/query-status/${outTradeNo}`),
getOrder: () => http.get('/payment/check-status'),
// 获取支付记录
getOrders: (params = {}) => http.get('/payment/orders', {
params
})
}
export default {
paymentAPI
}

65
api/transfer.js Normal file
View File

@@ -0,0 +1,65 @@
import {
http
} from "../util/api";
// 转账相关API
export const transferAPI = {
// 获取公户信息
getPublicAccount: () => http.get('/transfers/public-account'),
// 创建转账记录
create: (data) => {
const formData = new FormData()
Object.keys(data).forEach(key => {
formData.append(key, data[key])
})
return http.post('/transfers', data)
},
// 确认转账
confirm: (id) => http.put(`/transfers/${id}/confirm`),
// 拒绝转账
reject: (id) => http.put(`/transfers/${id}/reject`),
// 确认收款
confirmReceived: (id) => http.post('/transfers/confirm-received', {
transfer_id: id
}),
// 确认未收到款
confirmNotReceived: (id) => http.post('/transfers/confirm-not-received', {
transfer_id: id
}),
// 获取用户转账记录
getUserTransfers: (params = {}) => http.get('/transfers/user', {
params
}),
// 获取指定用户的转账记录
getUserTransfersByUserId: (userId, params = {}) => http.get(`/transfers/user/${userId}`, {
params
}),
// 获取待确认转账
getPendingTransfers: (params = {}) => http.get('/transfers/pending', {
params
}),
// 获取用户账户信息
getUserAccount: () => http.get('/transfers/account'),
// 获取转账列表(管理员)
getList: (params = {}) => http.get('/transfers', {
params
}),
// 获取转账统计
getStats: () => http.get('/transfers/stats')
}
export default {
transferAPI
}

27
api/user.js Normal file
View File

@@ -0,0 +1,27 @@
import {
http
} from "../util/api"
// 用户相关API
export const userAPI = {
// 获取用户列表
getList: (params = {}) => http.get('/users', {
params
}),
// 获取用户详情
getDetail: (id) => http.get(`/users/${id}`),
// 更新用户信息
update: (id, data) => http.put(`/users/${id}`, data),
// 删除用户
delete: (id) => http.delete(`/users/${id}`),
// 获取用户统计
getStats: () => http.get('/users/stats/overview')
}
export default {
userAPI
}

View File

@@ -2,7 +2,6 @@ import App from './App'
// 引入 uView UI
import uView from './uni_modules/vk-uview-ui';
// 引入Tabbar
import TabbarVue from './components/Tabbar/Tabbar.vue';
@@ -23,10 +22,8 @@ import {
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(uView)
app.use("Tabbar", TabbarVue)
app.component("Tabbar", TabbarVue)
return {
app

240
package-lock.json generated
View File

@@ -5,13 +5,61 @@
"packages": {
"": {
"dependencies": {
"vk-uview-ui": "^1.5.2"
"vk-uview-ui": "^1.5.2",
"vue": "^3.5.21"
},
"devDependencies": {
"sass": "^1.92.1",
"sass-loader": "^16.0.5"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"dependencies": {
"@babel/types": "^7.28.4"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
@@ -308,6 +356,97 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
"integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/shared": "3.5.21",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
"integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
"dependencies": {
"@vue/compiler-core": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/compiler-core": "3.5.21",
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.18",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
"integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
"dependencies": {
"@vue/compiler-dom": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
"integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
"dependencies": {
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
"integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
"dependencies": {
"@vue/reactivity": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
"integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
"dependencies": {
"@vue/reactivity": "3.5.21",
"@vue/runtime-core": "3.5.21",
"@vue/shared": "3.5.21",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
"integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
"dependencies": {
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21"
},
"peerDependencies": {
"vue": "3.5.21"
}
},
"node_modules/@vue/shared": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw=="
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
@@ -336,6 +475,11 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@@ -349,6 +493,22 @@
"node": ">=0.10"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -401,6 +561,14 @@
"node": ">=0.12.0"
}
},
"node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -415,6 +583,23 @@
"node": ">=8.6"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
@@ -428,6 +613,11 @@
"dev": true,
"optional": true
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -441,6 +631,33 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -518,7 +735,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -543,6 +759,26 @@
"engines": {
"HBuilderX": "^3.1.0"
}
},
"node_modules/vue": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
"dependencies": {
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-sfc": "3.5.21",
"@vue/runtime-dom": "3.5.21",
"@vue/server-renderer": "3.5.21",
"@vue/shared": "3.5.21"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
}
}
}

View File

@@ -36,6 +36,14 @@
"style": {
"navigationBarTitleText": "金融"
}
},
{
"path" : "pages/register/register",
"style" :
{
"navigationBarTitleText" : "注册",
"navigationStyle": "custom"
}
}
],
"globalStyle": {

View File

@@ -12,21 +12,24 @@
<view class="login-form">
<u-form :model="userLogin.userForm" ref="userLoginRef" :border-bottom="false" :error-type="['message']"
label-width="0">
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/login/user.png" prop="username">
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/user.png" prop="username">
<u-input v-model="userLogin.userForm.username" placeholder="请输入用户名或手机号"
placeholder-style="color: #737373;" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/login/Lock.png" prop="password">
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Lock.png" prop="password">
<u-input type="password" v-model="userLogin.userForm.password" placeholder="请输入密码"
placeholder-style="color: #737373;" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/login/Globe.png" prop="code">
<u-input v-model="userLogin.userForm.code" placeholder="请输入验证码"
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Globe.png" prop="captcha">
<u-input v-model="userLogin.userForm.captcha" placeholder="请输入验证码"
placeholder-style="color: #737373;" />
<template v-slot:right>
<image class="captcha-img" :src="captcha" mode=""></image>
</template>
</u-form-item>
<view class="reflash" @click="reflash">
<image class="reflash-icon" src="/static/login/Repeat.png" mode=""></image>
刷新
<image class="reflash-icon" src="/static/icon/Repeat.png" mode=""></image>
刷新验证码
</view>
<view class="rember">
<u-checkbox v-model="userLogin.userForm.is_rember">记住我</u-checkbox>
@@ -35,8 +38,8 @@
<u-button class="submit-btn" :ripple="true" shape="circle" type="primary" @click="submit">登录</u-button>
<view class="register">
<view class="register-text">没有账号</view>
<view class="register-link">点击注册
<image class="register-link-icon" src="/static/Chevron right.png" mode=""></image>
<view class="register-link" @click="handleRegister">点击注册
<image class="register-link-icon" src="/static/icon/Chevron right.png" mode=""></image>
</view>
</view>
</u-form>
@@ -45,15 +48,24 @@
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import { onLoad, onReady } from '@dcloudio/uni-app';
import { captchaAPI } from '@/api/captcha.js';
// 表单图标样式
const inputIcon = {
width: '32rpx',
verticalAlign: 'middle'
}
// 登录表单
const captcha = ref()
const userLoginRef = ref()
const userLogin = reactive({
userForm: {
username: '',
password: '',
code: '',
captcha: '',
is_rember: false
},
ruls: {
@@ -81,14 +93,29 @@
}
})
// 初始化绑定
onReady(() => {
userLoginRef.value.setRules(userLogin.ruls);
});
onMounted(() => {
loadCaptcha()
})
// 刷新
const reflash = () => {
userLoginRef.value.resetFields()
// userLoginRef.value.resetFields()
loadCaptcha()
}
// 请求验证码
const loadCaptcha = () => {
captchaAPI.generate().then(res => {
captcha.value = res.data.image
})
}
// 登录处理
const submit = () => {
userLoginRef.value.validate((valid : any) => {
if (valid) {
@@ -97,9 +124,11 @@
});
}
const inputIcon = {
width: '32rpx',
verticalAlign: 'middle'
// 注册
const handleRegister = () => {
uni.redirectTo({
url: '/pages/register/register'
})
}
</script>
@@ -145,6 +174,11 @@
.login-form {
padding: 10rpx 32rpx;
.captcha-img {
width: 200rpx;
height: 80rpx;
}
.reflash {
width: 100%;
display: flex;
@@ -192,7 +226,7 @@
font-style: Expanded Semibold;
font-size: 40rpx;
leading-trim: NONE;
line-height: 28px;
line-height: 46rpx;
letter-spacing: 0%;
text-align: center;
}
@@ -207,7 +241,7 @@
// 文字
font-family: Work Sans;
font-weight: 400;
font-size: 13px;
font-size: 26rpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;

490
pages/register/register.vue Normal file
View File

@@ -0,0 +1,490 @@
<template>
<view class="register-container">
<view class="register-title">
<view class="title">
用户注册
</view>
<view class="sub-title">
创建你的账号欢迎来到炬融圈
</view>
</view>
<view class="register-main">
<u-form :model="userRegister.userRegisterForm" class="register-form" ref="userRegisterRef"
:border-bottom="false" :error-type="['message']" label-width="0">
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Phone.png" prop="phone">
<u-input v-model="userRegister.userRegisterForm.phone" placeholder="请输入手机号" maxlength="11"
type="number" placeholder-style="color: #737373;" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Mail.png" prop="smsCode">
<u-input v-model="userRegister.userRegisterForm.smsCode" placeholder="请输入短信验证码" maxlength="6"
type="number" placeholder-style="color: #737373;" />
<template v-slot:right>
<u-verification-code ref="msgCodeRef" @change="codeMsgChange"></u-verification-code>
<u-button :disabled="!canSendSMS" size="mini" type="primary"
@tap="getMsgCode">{{msgCodeBtn}}</u-button>
</template>
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Map pin.png" prop="regionLabel">
<u-input type="select" v-model="userRegister.userRegisterForm.regionLabel" placeholder="请选择省市区"
placeholder-style="color: #737373;" @click="showRegitionPicker = true" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Lock.png" prop="password">
<u-input type="password" v-model="userRegister.userRegisterForm.password" placeholder="请输入密码"
maxlength="20" placeholder-style="color: #737373;" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Lock.png" prop="confirmPassword">
<u-input type="password" v-model="userRegister.userRegisterForm.confirmPassword" maxlength="20"
placeholder="请输入确认密码" placeholder-style="color: #737373;" />
</u-form-item>
<u-form-item :left-icon-style="inputIcon" left-icon="../../static/icon/Globe.png" prop="captcha">
<u-input v-model="userRegister.userRegisterForm.captcha" placeholder="请输入验证码" maxlength="4"
placeholder-style="color: #737373;" />
<template v-slot:right>
<image @click="loadCaptcha" class="captcha-img" :src="captcha" mode=""></image>
</template>
</u-form-item>
</u-form>
<view class="reflash" @click="loadCaptcha">
<image class="reflash-icon" src="/static/icon/Repeat.png" mode=""></image>
刷新验证码
</view>
<u-checkbox v-model="is_read" size="26" class="must-read" :label-disabled="true">
我已阅读并同意<span @click="showFile1=true">用户协议</span><span @click="showFile2=true">隐私政策</span>
</u-checkbox>
<u-button type="primary" class="register-btn" @click="handleRegister">立即注册</u-button>
<view class="login">
<view class="login-text">没有账号</view>
<view class="login-link" @click="handleLogin">立即登录</view>
</view>
</view>
<!-- 省市区 -->
<u-select v-model="showRegitionPicker" :list="regionOptions" mode="mutil-column-auto" label-name="label"
value-name="code" child-name="children" @confirm="handleRegion"></u-select>
<!-- 协议1 -->
<u-popup v-model="showFile1" mode="bottom" border-radius="14" :closeable="true">
<view class="file">
<h3>用户协议</h3>
<p>1. 用户应当遵守法律法规不得发布违法违规内容</p>
<p>2. 用户对自己发布的内容承担全部责任</p>
<p>3. 平台有权对违规内容进行删除或限制</p>
<p>4. 用户应当保护好自己的账号安全</p>
<p>5. 平台保留修改本协议的权利</p>
</view>
</u-popup>
<!-- 协议2 -->
<u-popup v-model="showFile2" mode="bottom" border-radius="14" :closeable="true">
<view class="file">
<h3>隐私政策</h3>
<p>1. 我们重视用户隐私保护</p>
<p>2. 我们只收集必要的用户信息</p>
<p>3. 用户信息仅用于提供服务</p>
<p>4. 我们不会向第三方泄露用户信息</p>
<p>5. 用户有权查看修改或删除个人信息</p>
</view>
</u-popup>
</view>
</template>
<script setup lang="ts">
import {
onMounted,
reactive,
ref,
computed
} from 'vue';
import {
onReady
} from '@dcloudio/uni-app';
import {
captchaAPI
} from '../../api/captcha.js';
import {
commonAPI
} from '../../api/common.js';
import {
authAPI
} from '../../api/auth.js';
// 输入框图标样式
const inputIcon = {
width: '32rpx',
verticalAlign: 'middle'
}
// 省市区
const showRegitionPicker = ref(false)
const regionOptions = ref()
// 加载省市区
const loadRegion = () => {
commonAPI.getRegion().then(res => {
if (res.success) {
regionOptions.value = res.data
}
})
}
// 选择省市区
const handleRegion = (e) => {
console.log(e);
userRegister.userRegisterForm.regionLabel = e[0].label + "/" + e[1].label + "/" + e[2].label
userRegister.userRegisterForm.region = e[2].value
}
// 验证码
const captcha = ref()
const captchaId = ref()
// 请求验证码
const loadCaptcha = () => {
captchaAPI.generate().then(res => {
captcha.value = res.data.image
captchaId.value = res.data.captchaId
})
}
// 校验规则
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'));
} else if (value.length < 6) {
callback(new Error('密码至少6个字符'));
} else if (value.length > 20) {
callback(new Error('密码不能超过20个字符'));
} else if (!/(?=.*[a-zA-Z])(?=.*\d)/.test(value)) {
callback(new Error('密码必须包含字母和数字'));
} else {
// 如果确认密码已输入,重新验证确认密码
if (userRegister.userRegisterForm.confirmPassword) {
userRegisterRef.value?.validateField('confirmPassword');
}
callback();
}
};
const validateConfirmPassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请确认密码'));
} else if (value !== userRegister.userRegisterForm.password) {
callback(new Error('两次输入的密码不一致'));
} else {
callback();
}
};
// 注册表单
const userRegisterRef = ref()
const userRegister = reactive({
userRegisterForm: {
username: '13758452159',
phone: '13758452159',
smsCode: 'asrfdv',
region: '',
regionLabel: '',
password: 's123456s',
confirmPassword: 's123456s',
captcha: 'vfjs',
},
rules: {
phone: [{
required: true,
message: '请输入手机号',
trigger: ['blur']
},
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号格式',
trigger: ['blur']
}
],
smsCode: [{
required: true,
message: '请输入短信验证码',
trigger: ['blur']
},
{
pattern: /^\d{6}$/,
message: '短信验证码为6位数字',
trigger: ['blur']
},
],
regionLabel: [{
required: true,
message: '请选择省市区',
trigger: ['change']
}],
password: [{
validator: validatePassword,
trigger: ['blur']
}],
confirmPassword: [{
validator: validateConfirmPassword,
trigger: ['blur']
}],
captcha: [{
required: true,
message: '请输入验证码',
trigger: ['blur']
},
{
min: 4,
max: 4,
message: '验证码长度为4位',
trigger: 'blur'
}
],
}
})
// 注册处理
const handleRegister = async () => {
if (!is_read.value) {
uni.showToast({
title: '请先同意协议',
icon: 'none'
})
return
}
// 等待表单验证完成
const valid = await userRegisterRef.value.validate();
if (valid) {
console.log(valid);
}
// userRegisterRef.value.validate((valid : any) => {
// if (valid) {
// // 验证验证码
// captchaAPI.verify({
// captchaId: captchaId.value,
// captchaText: userRegister.userRegisterForm.captcha
// }).then(verifyResponse => {
// console.log(verifyResponse);
// if (!verifyResponse.data.success) {
// uni.showToast({
// title: verifyResponse.data.message || '验证码错误',
// icon: 'error'
// })
// // 重新加载验证码
// loadCaptcha()
// return
// }
// })
// const registerData = {
// username: userRegister.userRegisterForm.phone,
// phone: userRegister.userRegisterForm.phone,
// city: userRegister.userRegisterForm.region,
// password: userRegister.userRegisterForm.password,
// smsCode: userRegister.userRegisterForm.smsCode,
// captchaId: captchaId.value,
// captchaText: userRegister.userRegisterForm.captcha,
// province: userRegister.userRegisterForm.region,
// }
// authAPI.register(registerData).then(response => {
// // 直接注册成功的情况
// // setToken(response.data.token)
// // setUser(response.data.user)
// console.log(response);
// })
// }
// })
}
// 手机验证码
const msgCodeBtn = ref()
const msgCodeRef = ref()
// 手机验证码变化
const codeMsgChange = (text) => {
msgCodeBtn.value = text;
}
// 是否能够发送手机验证码
const canSendSMS = computed(() => {
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(userRegister.userRegisterForm.phone);
});
// 获取手机验证码
const getMsgCode = () => {
// 校验手机号
if (msgCodeRef.value.canGetCode) {
console.log("发送验证码");
msgCodeRef.value.start();
} else {
uni.showToast({
title: '等待...'
})
}
}
// 是否阅读
const is_read = ref(false)
const showFile1 = ref(false)
const showFile2 = ref(false)
// 跳转登录页面
const handleLogin = () => {
uni.redirectTo({
url: '/pages/login/login'
})
}
onMounted(() => {
loadCaptcha()
loadRegion()
})
onReady(() => {
userRegisterRef.value.setRules(userRegister.rules)
})
</script>
<style scoped lang="scss">
.register-container {
width: 100%;
height: 100vh;
background: linear-gradient(180deg, #BAC9FF 0%, #FFFFFF 100%);
.register-title {
padding-top: 114rpx;
.title {
font-family: SF Pro;
font-weight: 650;
font-style: Expanded Semibold;
font-size: 48rpx;
leading-trim: NONE;
line-height: 80rpx;
letter-spacing: 0%;
text-align: center;
color: #000000;
}
.sub-title {
font-family: Work Sans;
font-weight: 400;
font-size: 26rpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
text-align: center;
color: #5B5B5B;
}
}
.register-main {
.register-form {
padding: 20rpx 98rpx 10rpx;
}
.captcha-img {
width: 200rpx;
height: 80rpx;
}
.reflash {
width: 90%;
margin-bottom: 30rpx;
display: flex;
justify-content: flex-end;
align-items: center;
// 文字
font-family: Work Sans;
font-weight: 400;
font-size: 26rpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
color: #3781EF;
.reflash-icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
.must-read {
padding: 0 76rpx;
font-family: Work Sans;
font-weight: 400;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
text-align: center;
color: #000000;
text-align: left;
span {
font-family: Work Sans;
font-weight: 400;
font-size: 26rpxpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
text-align: center;
color: #3781EF;
}
}
.register-btn {
margin: 40rpx 54rpx 20rpx;
font-family: SF Pro;
font-weight: 650;
font-style: Expanded Semibold;
font-size: 40rpx;
leading-trim: NONE;
line-height: 46rpx;
letter-spacing: 0%;
text-align: center;
}
.login {
margin: 40rpx 0 80rpx;
padding-bottom: 80rpx;
display: flex;
justify-content: center;
align-items: center;
// 文字
font-family: Work Sans;
font-weight: 400;
font-size: 26rpx;
leading-trim: NONE;
line-height: 100%;
letter-spacing: -2%;
text-align: center;
.login-text {
color: #737373;
}
.login-link {
display: flex;
justify-content: center;
align-items: center;
color: #3781EF;
}
}
}
.file {
padding: 80rpx 40rpx;
}
}
</style>

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

BIN
static/icon/Credit card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

BIN
static/icon/Mail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

BIN
static/icon/Map pin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/icon/Phone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

View File

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 650 B

View File

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 839 B

View File

@@ -260,7 +260,7 @@ export default {
let num = 1;
let column = this.list;
// 只要有元素并且第一个元素有children属性继续历遍
while(column[0][this.childName]) {
while(column[0][this.childName] && column[0][this.childName].length>0) {
column = column[0] ? column[0][this.childName] : {};
num ++;
}

264
uni_modules/vk-uview-ui/package-lock.json generated Normal file
View File

@@ -0,0 +1,264 @@
{
"name": "vk-uview-ui",
"version": "1.6.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vk-uview-ui",
"version": "1.6.5",
"dependencies": {
"vue": "^3.5.21"
},
"engines": {
"HBuilderX": "^3.1.0",
"uni-app": "^4.36",
"uni-app-x": ""
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
"dependencies": {
"@babel/types": "^7.28.4"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"node_modules/@vue/compiler-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
"integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/shared": "3.5.21",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
"integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
"dependencies": {
"@vue/compiler-core": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
"dependencies": {
"@babel/parser": "^7.28.3",
"@vue/compiler-core": "3.5.21",
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.18",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
"integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
"dependencies": {
"@vue/compiler-dom": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
"integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
"dependencies": {
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
"integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
"dependencies": {
"@vue/reactivity": "3.5.21",
"@vue/shared": "3.5.21"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
"integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
"dependencies": {
"@vue/reactivity": "3.5.21",
"@vue/runtime-core": "3.5.21",
"@vue/shared": "3.5.21",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
"integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
"dependencies": {
"@vue/compiler-ssr": "3.5.21",
"@vue/shared": "3.5.21"
},
"peerDependencies": {
"vue": "3.5.21"
}
},
"node_modules/@vue/shared": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw=="
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/vue": {
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
"dependencies": {
"@vue/compiler-dom": "3.5.21",
"@vue/compiler-sfc": "3.5.21",
"@vue/runtime-dom": "3.5.21",
"@vue/server-renderer": "3.5.21",
"@vue/shared": "3.5.21"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
}
}
}

View File

@@ -10,7 +10,7 @@
"vue3.0",
"鸿蒙",
"uview"
],
],
"repository": "https://gitee.com/vk-uni/vk-uview-ui.git",
"engines": {
"HBuilderX": "^3.1.0",
@@ -98,5 +98,8 @@
}
}
}
},
"dependencies": {
"vue": "^3.5.21"
}
}
}

368
util/api.js Normal file
View File

@@ -0,0 +1,368 @@
// api.js - 适配uView3+uni-app版本
// 基础配置
const BASE_URL = 'http://192.168.1.43:3000/api'
const TIMEOUT = 10000
// 初始化时设置token
const token = uni.getStorageSync('token')
const commonHeaders = {
'Content-Type': 'application/json'
}
if (token) {
commonHeaders['Authorization'] = `Bearer ${token}`
}
// 请求队列用于管理loading
let requestQueue = 0
let loadingTimer = null
// 显示加载动画
const showLoading = () => {
requestQueue++
if (loadingTimer) {
clearTimeout(loadingTimer)
loadingTimer = null
}
loadingTimer = setTimeout(() => {
if (requestQueue > 0) {
uni.showLoading({
title: '加载中...',
mask: true
})
}
}, 300)
}
// 隐藏加载动画
const hideLoading = () => {
requestQueue--
if (requestQueue <= 0) {
requestQueue = 0
if (loadingTimer) {
clearTimeout(loadingTimer)
loadingTimer = null
}
uni.hideLoading()
}
}
// 核心请求函数
const request = (config) => {
return new Promise((resolve, reject) => {
// 请求配置处理
const {
url,
method = 'GET',
data = {},
params = {},
header = {},
showLoading = true
} = config
// 处理请求参数
let requestUrl = BASE_URL + url
let queryString = ''
if (Object.keys(params).length > 0) {
queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
requestUrl += (requestUrl.includes('?') ? '&' : '?') + queryString
}
// 合并请求头
const requestHeader = {
...commonHeaders,
...header
}
// 显示loading
if (showLoading) {
uni.showLoading()
}
// 发送请求
uni.request({
url: requestUrl,
method: method.toUpperCase(),
data: data,
header: requestHeader,
timeout: TIMEOUT,
success: (response) => {
// 处理响应
const res = response.data
const statusCode = response.statusCode
if (statusCode >= 200 && statusCode < 300) {
resolve(res)
} else {
// 处理HTTP错误状态
handleError({
response,
config
})
reject(response)
}
},
fail: (error) => {
console.log(error);
// 处理网络错误
handleError({
error,
config
})
reject(error)
},
complete: () => {
// 隐藏loading
if (showLoading) {
uni.hideLoading()
}
}
})
})
}
// 错误处理函数
const handleError = (errorInfo) => {
const {
response,
error,
config
} = errorInfo
if (response) {
const {
statusCode,
data
} = response
switch (statusCode) {
case 401:
// 未授权清除token并根据当前页面跳转到相应的登录页
uni.removeStorageSync('token')
uni.removeStorageSync('agentInfo') // 清除代理信息
delete commonHeaders['Authorization']
// 获取当前页面栈
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentRoute = currentPage ? currentPage.route : ''
// 判断当前是否在代理相关页面
if (currentRoute && currentRoute.includes('agent')) {
uni.redirectTo({
url: '/pages/agent/mylogin'
})
// uToast.error('')
uni.showToast({
title: '代理登录已过期,请重新登录'
})
} else {
uni.redirectTo({
url: '/pages/user/mylogin'
})
// uToast.error('登录已过期,请重新登录')
uni.showToast({
title: '登录已过期,请重新登录'
})
}
break
case 403:
// 检查是否是用户被拉黑
if (data.code === 'USER_BLACKLISTED') {
// 清除token并跳转到登录页
uni.removeStorageSync('token')
delete commonHeaders['Authorization']
uni.redirectTo({
url: '/pages/user/mylogin'
})
// uToast.error(data.message || '账户已被拉黑,请联系管理员')
uni.showToast({
title: data.message || '账户已被拉黑,请联系管理员'
})
} else if (data.code === 'PAYMENT_REQUIRED') {
// 需要支付,跳转到支付页面
// 获取当前页面避免重复跳转
const currentPages = getCurrentPages()
const currentPage = currentPages[currentPages.length - 1]
if (!currentPage || currentPage.route !== 'pages/payment/index') {
uni.redirectTo({
url: '/pages/payment/index'
})
uni.showToast({
title: data.message || '您的账户尚未激活,请完成支付后再使用'
})
// uToast.warning(data.message || '您的账户尚未激活,请完成支付后再使用')
}
} else {
// uToast.error(data.message || '权限不足')
uni.showToast({
title: data.message || '权限不足'
})
}
break
case 404:
// uToast.error(data.message || '请求的资源不存在')
uni.showToast({
title: data.message || '请求的资源不存在'
})
break
case 422:
// uToast.error(data.message || '请求参数错误')
uni.showToast({
title: data.message || '请求参数错误'
})
break
case 429:
// uToast.error('请求过于频繁,请稍后再试')
uni.showToast({
title: '请求过于频繁,请稍后再试'
})
break
case 500:
// uToast.error('服务器内部错误')
uni.showToast({
title: '服务器内部错误'
})
break
case 400:
// 处理业务逻辑错误(如坏账等)
// uToast.error(data.error?.message || data.message || '请求失败')
uni.showToast({
title: data.error?.message || data.message || '请求失败'
})
break
default:
// uToast.error(data.error?.message || data.message || '请求失败')
uni.showToast({
title: data.error?.message || data.message || '请求失败'
})
}
} else if (error) {
// 网络错误
// uToast.error('网络连接失败,请检查网络设置')
uni.showToast({
title: '网络连接失败,请检查网络设置'
})
} else {
// 其他错误
// uToast.error('请求配置错误')
uni.showToast({
title: '请求配置错误'
})
}
}
// 封装常用的请求方法
export const http = {
get: (url, config = {}) => request({
url,
method: 'GET',
...config
}),
post: (url, data = {}, config = {}) => request({
url,
data,
method: 'POST',
...config
}),
put: (url, data = {}, config = {}) => request({
url,
data,
method: 'PUT',
...config
}),
delete: (url, config = {}) => request({
url,
method: 'DELETE',
...config
}),
patch: (url, data = {}, config = {}) => request({
url,
data,
method: 'PATCH',
...config
})
}
// 设置token
export const setToken = (newToken) => {
if (newToken) {
commonHeaders['Authorization'] = `Bearer ${newToken}`
uni.setStorageSync('token', newToken)
} else {
delete commonHeaders['Authorization']
uni.removeStorageSync('token')
}
}
// 文件上传API
export const uploadAPI = {
// 上传图片
uploadImage: (filePath, formData = {}) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: BASE_URL + '/upload/image',
filePath: filePath,
name: 'image',
formData: formData,
header: {
'Authorization': commonHeaders['Authorization']
},
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data)
resolve(data)
},
fail: (error) => {
reject(error)
}
})
})
},
// 上传文件
uploadFile: (filePath, formData = {}) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: BASE_URL + '/upload/file',
filePath: filePath,
name: 'file',
formData: formData,
header: {
'Authorization': commonHeaders['Authorization']
},
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data)
resolve(data)
},
fail: (error) => {
reject(error)
}
})
})
}
}
export const distributionAPI = {
getLowerUsers: (params) => http.get('/agents/distribution', {
params
}),
}
export default {
http,
setToken,
uploadAPI,
distributionAPI
}

5
util/common.js Normal file
View File

@@ -0,0 +1,5 @@
// 校验
export const validatePhone = (phone) => {
const reg = /^1[3-9]\d{9}$/;
return reg.test(phone);
};

15
vite.config.js Normal file
View File

@@ -0,0 +1,15 @@
import {
defineConfig
} from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
export default defineConfig({
plugins: [uni()],
server: {
host: "0.0.0.0", // 指定服务器应该监听哪个IP地址,默认localhost
port: 5173, // 指定开发服务器端口,默认5173
proxy: { // 为开发服务器配置自定义代理规则
}
}
});