需要修复商城顶部筛选左右滑动问题
@@ -2,8 +2,13 @@
|
|||||||
"version" : "1.0",
|
"version" : "1.0",
|
||||||
"configurations" : [
|
"configurations" : [
|
||||||
{
|
{
|
||||||
|
"customPlaygroundType" : "device",
|
||||||
"playground" : "standard",
|
"playground" : "standard",
|
||||||
"type" : "uni-app:app-ios"
|
"type" : "uni-app:app-ios"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"playground" : "standard",
|
||||||
|
"type" : "uni-app:app-android"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
printWidth: 74,
|
printWidth: 74,
|
||||||
28
api/index.js
@@ -1 +1,27 @@
|
|||||||
// 各模块api
|
import http from '@/utils/request'
|
||||||
|
|
||||||
|
/** 注册 */
|
||||||
|
export const userRegister = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/register',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 登录 */
|
||||||
|
export const userLogin = data => {
|
||||||
|
return http({
|
||||||
|
url: '/api/login',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取用户信息 */
|
||||||
|
export const getUserData = () => {
|
||||||
|
return http({
|
||||||
|
url: '/api/userInfo',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
67
components/cb-search/cb-search.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps()
|
||||||
|
|
||||||
|
const placeholderStyle = `font-family: PingFang SC, PingFang SC; font-weight: 500; color: #666666; font-size: 24rpx; font-style: normal; text-transform: none;`
|
||||||
|
|
||||||
|
const name = defineModel({
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="cb-search">
|
||||||
|
<image
|
||||||
|
src="/static/images/public/search.png"
|
||||||
|
mode="heightFix"
|
||||||
|
class="left-icon"
|
||||||
|
></image>
|
||||||
|
<input
|
||||||
|
v-model="name"
|
||||||
|
:placeholder-style="placeholderStyle"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
class="search-box"
|
||||||
|
/>
|
||||||
|
<button class="search-btn">搜索</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cb-search {
|
||||||
|
height: 64rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f9f9f9;
|
||||||
|
border-radius: 64rpx;
|
||||||
|
padding: 0 0 0 32rpx;
|
||||||
|
.left-icon {
|
||||||
|
height: 48rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
.search-box {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.search-btn {
|
||||||
|
margin: 0 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
line-height: 56rpx;
|
||||||
|
background: linear-gradient(180deg, #00d993 0%, #00d9c5 100%);
|
||||||
|
border-radius: 64rpx;
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, computed } from 'vue'
|
import { reactive, computed } from 'vue'
|
||||||
import { reLaunch } from '@/utils/router'
|
import { reLaunch } from '@/utils/router'
|
||||||
|
import {
|
||||||
|
validatePhone,
|
||||||
|
validateEmail,
|
||||||
|
validatePassword,
|
||||||
|
validateConfirmPassword
|
||||||
|
} from '@/utils/validate'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
import { userRegister } from '@/api'
|
||||||
|
|
||||||
|
const { showToast } = useUI()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
/**
|
/**
|
||||||
@@ -17,32 +27,67 @@
|
|||||||
const isPhone = computed(() => props.type === 'phone')
|
const isPhone = computed(() => props.type === 'phone')
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
// 手机号
|
// 手机号、邮箱
|
||||||
name: '',
|
name: '',
|
||||||
// 验证码
|
|
||||||
code: '',
|
|
||||||
// 密码
|
// 密码
|
||||||
password: '',
|
password: '',
|
||||||
// 确认密码
|
// 确认密码
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
// 邀请码
|
// 邀请码
|
||||||
inviteCode: '',
|
invitationCode: '54321',
|
||||||
agreement: false
|
agreement: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const isBtn = computed(() => {
|
const isBtn = computed(() => {
|
||||||
return (
|
return (
|
||||||
formData.name &&
|
formData.name &&
|
||||||
formData.code &&
|
|
||||||
formData.password &&
|
formData.password &&
|
||||||
formData.confirmPassword &&
|
formData.confirmPassword &&
|
||||||
formData.inviteCode &&
|
formData.invitationCode &&
|
||||||
!formData.agreement
|
!formData.agreement
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const onRegister = () => {
|
/** 注册 */
|
||||||
console.log('注册')
|
const onRegister = async () => {
|
||||||
|
if (isPhone.value) {
|
||||||
|
const phoneValue = validatePhone(formData.name)
|
||||||
|
if (!phoneValue.valid) {
|
||||||
|
showToast(phoneValue.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const emailValue = validateEmail(formData.name)
|
||||||
|
if (!emailValue.valid) {
|
||||||
|
showToast(emailValue.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordValue = validatePassword(formData.password)
|
||||||
|
if (!passwordValue.valid) {
|
||||||
|
showToast(passwordValue.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmPasswordValue = validateConfirmPassword(
|
||||||
|
formData.password,
|
||||||
|
formData.confirmPassword
|
||||||
|
)
|
||||||
|
if (!confirmPasswordValue.valid) {
|
||||||
|
showToast(confirmPasswordValue.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
type: isPhone.value ? 2 : 1,
|
||||||
|
mobile: formData.name,
|
||||||
|
password: formData.password,
|
||||||
|
invitationCode: formData.invitationCode
|
||||||
|
}
|
||||||
|
await userRegister(data)
|
||||||
|
await showToast('注册成功', 'success')
|
||||||
|
onLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLogin = () => {
|
const onLogin = () => {
|
||||||
@@ -73,12 +118,12 @@
|
|||||||
icon="3"
|
icon="3"
|
||||||
:placeholder="`请输入${isPhone ? '手机号' : '邮箱'}`"
|
:placeholder="`请输入${isPhone ? '手机号' : '邮箱'}`"
|
||||||
></cb-input>
|
></cb-input>
|
||||||
<cb-input
|
<!-- <cb-input
|
||||||
v-model="formData.code"
|
v-model="formData.code"
|
||||||
type="number"
|
type="number"
|
||||||
icon="6"
|
icon="6"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
></cb-input>
|
></cb-input> -->
|
||||||
<cb-input
|
<cb-input
|
||||||
v-model="formData.password"
|
v-model="formData.password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -92,7 +137,7 @@
|
|||||||
placeholder="请输入确认密码"
|
placeholder="请输入确认密码"
|
||||||
></cb-input>
|
></cb-input>
|
||||||
<cb-input
|
<cb-input
|
||||||
v-model="formData.inviteCode"
|
v-model="formData.invitationCode"
|
||||||
type="number"
|
type="number"
|
||||||
icon="4"
|
icon="4"
|
||||||
placeholder="请输入邀请码"
|
placeholder="请输入邀请码"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export const STORAGE_KEYS = {
|
export const STORAGE_KEYS = {
|
||||||
TOKEN: 'token',
|
TOKEN: 'token',
|
||||||
};
|
USER: 'userInfo'
|
||||||
|
}
|
||||||
|
|||||||
77
pages.json
@@ -33,6 +33,49 @@
|
|||||||
"navigationBarTitleText": "忘记密码",
|
"navigationBarTitleText": "忘记密码",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/news-list/news-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "消息"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/contacts/contacts",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "通讯录"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/discover/discover",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "发现"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/my-index/my-index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "我的",
|
||||||
|
"backgroundColor": "#f7f7f7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/mall/list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商城"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/mall/detail",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品详情"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/mall/confirm-order",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "确认订单"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
@@ -41,5 +84,37 @@
|
|||||||
"navigationBarBackgroundColor": "#F8F8F8",
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
"backgroundColor": "#F8F8F8"
|
"backgroundColor": "#F8F8F8"
|
||||||
},
|
},
|
||||||
"uniIdRouter": {}
|
"uniIdRouter": {},
|
||||||
|
"tabBar": {
|
||||||
|
"color": "#333333",
|
||||||
|
"selectedColor": "#00D993",
|
||||||
|
"borderStyle": "black",
|
||||||
|
"backgroundColor": "#ffffff",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"pagePath": "pages/news-list/news-list",
|
||||||
|
"iconPath": "static/images/tabBar/news.png",
|
||||||
|
"selectedIconPath": "static/images/tabBar/newsHL.png",
|
||||||
|
"text": "消息"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/contacts/contacts",
|
||||||
|
"iconPath": "static/images/tabBar/contacts.png",
|
||||||
|
"selectedIconPath": "static/images/tabBar/contactsHL.png",
|
||||||
|
"text": "通讯录"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/discover/discover",
|
||||||
|
"iconPath": "static/images/tabBar/discover.png",
|
||||||
|
"selectedIconPath": "static/images/tabBar/discoverHL.png",
|
||||||
|
"text": "发现"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/my-index/my-index",
|
||||||
|
"iconPath": "static/images/tabBar/my.png",
|
||||||
|
"selectedIconPath": "static/images/tabBar/myHL.png",
|
||||||
|
"text": "我的"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
7
pages/contacts/contacts.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="contacts">通讯录</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
75
pages/discover/discover.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<script setup>
|
||||||
|
import { navigateTo } from '@/utils/router'
|
||||||
|
|
||||||
|
const btnList = [
|
||||||
|
{ name: '等级排行榜', icon: 'grade' },
|
||||||
|
{ name: '签到', icon: 'sign' },
|
||||||
|
{ name: '公司介绍', icon: 'company' },
|
||||||
|
{ name: '朋友圈', icon: 'circle' },
|
||||||
|
{ name: '线上商城', icon: 'mall' },
|
||||||
|
{ name: '我的拼团', icon: 'team' },
|
||||||
|
{ name: '项目入口', icon: 'project' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const onGo = item => {
|
||||||
|
if (item === 'mall') {
|
||||||
|
navigateTo('/pages/mall/list')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="discover-box">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in btnList"
|
||||||
|
:key="index"
|
||||||
|
class="card-box"
|
||||||
|
@click="onGo(item.icon)"
|
||||||
|
>
|
||||||
|
<view class="left-box">
|
||||||
|
<image
|
||||||
|
:src="`/static/images/discover/${item.icon}.png`"
|
||||||
|
mode="heightFix"
|
||||||
|
class="icon"
|
||||||
|
></image>
|
||||||
|
<text>{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
src="/static/images/public/right-arrow.png"
|
||||||
|
mode="heightFix"
|
||||||
|
class="right-box"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.discover-box {
|
||||||
|
padding: 32rpx 24rpx;
|
||||||
|
.card-box {
|
||||||
|
padding: 20rpx 32rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.left-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.icon {
|
||||||
|
height: 80rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-box {
|
||||||
|
height: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,59 +1,67 @@
|
|||||||
<template>
|
|
||||||
<view class="content">
|
|
||||||
<image class="logo" src="/static/logo.png"></image>
|
|
||||||
<view class="text-area">
|
|
||||||
<text class="title">{{ title }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
const cb = v => {
|
import { onMounted, ref } from 'vue'
|
||||||
console.log(v)
|
import { reLaunch } from '@/utils/router'
|
||||||
}
|
const cb = v => {
|
||||||
|
console.log(v)
|
||||||
|
}
|
||||||
|
|
||||||
// export default {
|
// export default {
|
||||||
// data() {
|
// data() {
|
||||||
// return {
|
// return {
|
||||||
// title: 'Hello'
|
// title: 'Hello'
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// onLoad() {
|
// onLoad() {
|
||||||
|
|
||||||
// },
|
// },
|
||||||
// methods: {
|
// methods: {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
const title = ref('你112好')
|
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
// 3秒后跳转
|
||||||
|
setTimeout(() => {
|
||||||
|
reLaunch('/pages/news-list/news-list')
|
||||||
|
}, 3000)
|
||||||
|
})
|
||||||
|
const title = ref('这个是启动页')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="content">
|
||||||
|
<image class="logo" src="/static/logo.png"></image>
|
||||||
|
<view class="text-area">
|
||||||
|
<text class="title">{{ title }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 200rpx;
|
height: 200rpx;
|
||||||
width: 200rpx;
|
width: 200rpx;
|
||||||
margin-top: 200rpx;
|
margin-top: 200rpx;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-bottom: 50rpx;
|
margin-bottom: 50rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-area {
|
.text-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
color: #8f8f94;
|
color: #8f8f94;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
|
import { useUI } from '@/utils/use-ui'
|
||||||
|
|
||||||
|
const { showToast } = useUI()
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -15,6 +18,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onBottomBtn = () => {
|
const onBottomBtn = () => {
|
||||||
|
if (!formData.username) {
|
||||||
|
showToast('请输入手机号/邮箱')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.password) {
|
||||||
|
showToast('请输入密码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log('确认')
|
console.log('确认')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -38,14 +51,14 @@
|
|||||||
v-model="formData.username"
|
v-model="formData.username"
|
||||||
placeholder="请输入手机号/邮箱"
|
placeholder="请输入手机号/邮箱"
|
||||||
></cb-input>
|
></cb-input>
|
||||||
<cb-input
|
<!-- <cb-input
|
||||||
v-model="formData.code"
|
v-model="formData.code"
|
||||||
v-model:code="isCode"
|
v-model:code="isCode"
|
||||||
type="number"
|
type="number"
|
||||||
icon="6"
|
icon="6"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
@onGetCode="getCode"
|
@onGetCode="getCode"
|
||||||
></cb-input>
|
></cb-input> -->
|
||||||
<cb-input
|
<cb-input
|
||||||
v-model="formData.password"
|
v-model="formData.password"
|
||||||
type="password"
|
type="password"
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { useUI } from '@/utils/use-ui'
|
import { useUI } from '@/utils/use-ui'
|
||||||
import { reLaunch, navigateTo } from '@/utils/router'
|
import { reLaunch, navigateTo } from '@/utils/router'
|
||||||
|
import { userLogin } from '@/api'
|
||||||
|
import { useTokenStore } from '@/stores/token'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
const { showToast } = useUI()
|
const { showToast } = useUI()
|
||||||
|
const { setToken } = useTokenStore()
|
||||||
|
const { fetchUserInfo } = useUserStore()
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
@@ -12,9 +17,18 @@
|
|||||||
agreement: false
|
agreement: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const onLogin = () => {
|
const onLogin = async () => {
|
||||||
showToast('登录成功')
|
if (!formData.agreement) {
|
||||||
console.log('登录:', formData)
|
showToast('请同意协议')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await userLogin({
|
||||||
|
account: formData.username,
|
||||||
|
password: formData.password
|
||||||
|
})
|
||||||
|
setToken(res.token)
|
||||||
|
await fetchUserInfo()
|
||||||
|
reLaunch('/pages/news-list/news-list')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRegister = () => {
|
const onRegister = () => {
|
||||||
@@ -26,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLoad(e => {
|
onLoad(e => {
|
||||||
console.log('接收==:', e.id)
|
console.log('接收参数,返回对应页面的时候使用', e)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -50,9 +64,7 @@
|
|||||||
<agreement-checkbox v-model="formData.agreement" />
|
<agreement-checkbox v-model="formData.agreement" />
|
||||||
<cb-button
|
<cb-button
|
||||||
class="bottom-btn"
|
class="bottom-btn"
|
||||||
:disabled="
|
:disabled="!formData.username || !formData.password"
|
||||||
!formData.username || !formData.password || !formData.agreement
|
|
||||||
"
|
|
||||||
@click="onLogin"
|
@click="onLogin"
|
||||||
>
|
>
|
||||||
登录
|
登录
|
||||||
|
|||||||
7
pages/mall/confirm-order.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="mall-confirm-order">确认订单</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
7
pages/mall/detail.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="mall-detail">商品详情</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
78
pages/mall/list.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
// 顶部分类选项
|
||||||
|
const topNavOptions = [
|
||||||
|
{ name: '全部', value: '0' },
|
||||||
|
{ name: '休闲零食', value: '1' },
|
||||||
|
{ name: '中外名酒', value: '2' },
|
||||||
|
{ name: '家用洗漱', value: '3' },
|
||||||
|
{ name: '家电家具', value: '4' },
|
||||||
|
{ name: '电子产品', value: '5' },
|
||||||
|
{ name: '户外用品', value: '6' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
name: '',
|
||||||
|
type: '0'
|
||||||
|
})
|
||||||
|
|
||||||
|
const onTop = value => {
|
||||||
|
formData.type = value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="mall-list">
|
||||||
|
<cb-search v-model="formData.name"></cb-search>
|
||||||
|
<view class="top-options">
|
||||||
|
<view
|
||||||
|
v-for="item in topNavOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:class="{ active: item.value === formData.type }"
|
||||||
|
class="text"
|
||||||
|
@click="onTop(item.value)"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mall-list {
|
||||||
|
padding: 24rpx;
|
||||||
|
.top-options {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
white-space: nowrap; /* 重要:防止换行 */
|
||||||
|
-webkit-overflow-scrolling: touch; /* iOS 平滑滚动 */
|
||||||
|
|
||||||
|
.text + .text {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
background: #f4f4f4;
|
||||||
|
border-radius: 64rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
padding: 6rpx 14rpx;
|
||||||
|
border-radius: 64rpx;
|
||||||
|
border: 2rpx solid #00d993;
|
||||||
|
color: #00d993;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
221
pages/my-index/my-index.vue
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
|
const bottomList = [
|
||||||
|
{ name: '我的钱包', icon: 'wallet' },
|
||||||
|
{ name: '我的团队', icon: 'team' },
|
||||||
|
{ name: '会议记录', icon: 'meeting' },
|
||||||
|
{ name: '我的朋友圈', icon: 'circle' },
|
||||||
|
{ name: '我的收藏', icon: 'collection' },
|
||||||
|
{ name: '在线客服', icon: 'customer' },
|
||||||
|
{ name: '系统设置', icon: 'system' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const { userInfo } = useUserStore()
|
||||||
|
|
||||||
|
console.log(userInfo)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="my-index">
|
||||||
|
<view class="top-info">
|
||||||
|
<view class="left-box">
|
||||||
|
<image
|
||||||
|
src="https://wx1.sinaimg.cn/mw690/92eeb099gy1i29hl0ne80j21jk2bcash.jpg"
|
||||||
|
mode="scaleToFill"
|
||||||
|
class="avatar"
|
||||||
|
></image>
|
||||||
|
<view class="nickname">
|
||||||
|
<text class="name">{{ userInfo.userName }}</text>
|
||||||
|
<text class="name">ID:{{ userInfo.userId }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
src="/static/images/public/right-arrow.png"
|
||||||
|
mode="heightFix"
|
||||||
|
class="right-box"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
<!-- 卡片列表 -->
|
||||||
|
<view class="card-list">
|
||||||
|
<view class="top-box">
|
||||||
|
<view class="left-name">
|
||||||
|
<text>账户积分</text>
|
||||||
|
<text>2933</text>
|
||||||
|
</view>
|
||||||
|
<view class="right-btn">
|
||||||
|
<button>充值</button>
|
||||||
|
<button>提现</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 入口列表 -->
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in bottomList"
|
||||||
|
:key="index"
|
||||||
|
class="item-box"
|
||||||
|
>
|
||||||
|
<view class="item-name">
|
||||||
|
<image
|
||||||
|
:src="`/static/images/my-index/${item.icon}.png`"
|
||||||
|
mode="heightFix"
|
||||||
|
class="icon"
|
||||||
|
></image>
|
||||||
|
<text>{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
<image
|
||||||
|
src="/static/images/public/right-arrow.png"
|
||||||
|
mode="heightFix"
|
||||||
|
class="right-box"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
page {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-index {
|
||||||
|
padding: 32rpx 26rpx;
|
||||||
|
.right-box {
|
||||||
|
height: 32rpx;
|
||||||
|
}
|
||||||
|
.top-info {
|
||||||
|
padding: 32rpx 46rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
height: 192rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.left-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.avatar {
|
||||||
|
width: 128rpx;
|
||||||
|
height: 128rpx;
|
||||||
|
border-radius: 128rpx;
|
||||||
|
margin-right: 32rpx;
|
||||||
|
}
|
||||||
|
.nickname {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.name {
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333333;
|
||||||
|
text-align: left;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
&:last-child {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-list {
|
||||||
|
margin-top: 28rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
.top-box {
|
||||||
|
padding: 12rpx 36rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(180deg, #00d993 0%, #00d9c5 100%);
|
||||||
|
position: relative;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-image: url('/static/images/my-index/my-card-bg.png');
|
||||||
|
background-size: 30%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.left-name {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text {
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
&:last-child {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.right-btn {
|
||||||
|
display: flex;
|
||||||
|
button {
|
||||||
|
width: 128rpx;
|
||||||
|
height: 64rpx;
|
||||||
|
line-height: 64rpx;
|
||||||
|
border-radius: 100rpx 100rpx 100rpx 100rpx;
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
background: transparent;
|
||||||
|
border: 2rpx solid #ffffff;
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #00d993;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.item-box {
|
||||||
|
padding: 23rpx 32rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.item-name {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.icon {
|
||||||
|
height: 64rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
text {
|
||||||
|
font-family: PingFang SC, PingFang SC;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
12
pages/news-list/news-list.vue
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
const { userInfo } = useUserStore()
|
||||||
|
|
||||||
|
console.log(userInfo.userId, '====userInfo===')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="news-list">消息列表</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
BIN
static/images/discover/circle.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
static/images/discover/company.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
static/images/discover/grade.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
static/images/discover/mall.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
static/images/discover/project.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
static/images/discover/sign.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
static/images/discover/team.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
static/images/my-index/circle.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
static/images/my-index/collection.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
static/images/my-index/customer.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
static/images/my-index/meeting.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/images/my-index/my-card-bg.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
static/images/my-index/system.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
static/images/my-index/team.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
static/images/my-index/wallet.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
static/images/public/right-arrow.png
Normal file
|
After Width: | Height: | Size: 331 B |
BIN
static/images/public/search.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
static/images/tabBar/contacts.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
static/images/tabBar/contactsHL.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
static/images/tabBar/discover.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/images/tabBar/discoverHL.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
static/images/tabBar/my.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
static/images/tabBar/myHL.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
static/images/tabBar/news.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/images/tabBar/newsHL.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,42 +1,48 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
import { STORAGE_KEYS } from '@/constants/storage-keys';
|
import { STORAGE_KEYS } from '@/constants/storage-keys'
|
||||||
import { getToken, removeToken } from '@/utils/storage';
|
import { setTokenData, getToken, removeToken } from '@/utils/storage'
|
||||||
|
|
||||||
/** 登录状态 */
|
/** 登录状态 */
|
||||||
export const useTokenStore = defineStore(STORAGE_KEYS.TOKEN, () => {
|
export const useTokenStore = defineStore(STORAGE_KEYS.TOKEN, () => {
|
||||||
|
// 从本地存储获取token
|
||||||
|
const token = ref(getToken() || null)
|
||||||
|
const isLogin = ref(!!token.value)
|
||||||
|
|
||||||
// 从本地存储获取token
|
/** 设置token并保存到本地 */
|
||||||
const token = ref(getToken() || null);
|
const setToken = newToken => {
|
||||||
const isLogin = ref(!!token.value);
|
token.value = newToken
|
||||||
|
isLogin.value = true
|
||||||
|
setTokenData(newToken)
|
||||||
|
}
|
||||||
|
|
||||||
/** 设置token并保存到本地 */
|
/** 清除token */
|
||||||
const setToken = (newToken) => {
|
const clearToken = () => {
|
||||||
token.value = newToken;
|
token.value = null
|
||||||
isLogin.value = true;
|
isLogin.value = false
|
||||||
setToken(newToken);
|
removeToken()
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 清除token */
|
/** 检查token是否有效(可扩展过期时间判断) */
|
||||||
const clearToken = () => {
|
const checkToken = () => {
|
||||||
token.value = null;
|
// 简单判断:token存在且不为空
|
||||||
isLogin.value = false;
|
return !!token.value
|
||||||
removeToken()
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/** 检查token是否有效(可扩展过期时间判断) */
|
/** 验证token是否过期(实际项目中可添加过期时间判断) */
|
||||||
const checkToken = () => {
|
const isTokenExpired = () => {
|
||||||
// 简单判断:token存在且不为空
|
// 示例:如果token中包含过期时间,这里可以判断
|
||||||
return !!token.value;
|
// 例如:const expireTime = parseInt(token.value.split('.')[1]);
|
||||||
};
|
// return Date.now() > expireTime * 1000;
|
||||||
|
return false // 默认不过期
|
||||||
|
}
|
||||||
|
|
||||||
/** 验证token是否过期(实际项目中可添加过期时间判断) */
|
return {
|
||||||
const isTokenExpired = () => {
|
token,
|
||||||
// 示例:如果token中包含过期时间,这里可以判断
|
isLogin,
|
||||||
// 例如:const expireTime = parseInt(token.value.split('.')[1]);
|
setToken,
|
||||||
// return Date.now() > expireTime * 1000;
|
clearToken,
|
||||||
return false; // 默认不过期
|
checkToken,
|
||||||
};
|
isTokenExpired
|
||||||
|
}
|
||||||
return { token, isLogin, setToken, clearToken, checkToken, isTokenExpired };
|
})
|
||||||
})
|
|
||||||
|
|||||||
70
stores/user.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import {
|
||||||
|
getToken,
|
||||||
|
getUserInfoData,
|
||||||
|
setUserInfoData,
|
||||||
|
removeUserInfoData
|
||||||
|
} from '@/utils/storage'
|
||||||
|
import { useTokenStore } from './token'
|
||||||
|
import { getUserData } from '@/api'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', () => {
|
||||||
|
const { clearToken } = useTokenStore()
|
||||||
|
|
||||||
|
/** 用户信息对象 */
|
||||||
|
const userInfo = ref(JSON.parse(getUserInfoData()) || null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息(可从缓存或接口)
|
||||||
|
*/
|
||||||
|
const fetchUserInfo = async () => {
|
||||||
|
// 示例:先尝试从本地缓存读取
|
||||||
|
const cachedToken = getToken()
|
||||||
|
const cachedUserInfo = getUserInfoData()
|
||||||
|
|
||||||
|
if (cachedToken && cachedUserInfo) {
|
||||||
|
userInfo.value = JSON.parse(cachedUserInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await getUserData()
|
||||||
|
await setUserInfo(res.data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置用户信息
|
||||||
|
*/
|
||||||
|
const setUserInfo = async data => {
|
||||||
|
console.log('存储数据到userInfo==', data)
|
||||||
|
userInfo.value = data
|
||||||
|
// 同步到本地存储
|
||||||
|
setUserInfoData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除用户信息(退出登录)
|
||||||
|
*/
|
||||||
|
const clearUserInfo = () => {
|
||||||
|
userInfo.value = null
|
||||||
|
clearToken()
|
||||||
|
removeUserInfoData()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新部分用户信息(例如昵称、头像)
|
||||||
|
*/
|
||||||
|
const updateUserInfo = partialData => {
|
||||||
|
if (!userInfo.value) return
|
||||||
|
userInfo.value = { ...userInfo.value, ...partialData }
|
||||||
|
setUserInfoData(userInfo.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo: userInfo.value,
|
||||||
|
fetchUserInfo,
|
||||||
|
setUserInfo,
|
||||||
|
clearUserInfo,
|
||||||
|
updateUserInfo
|
||||||
|
}
|
||||||
|
})
|
||||||
220
utils/request.js
@@ -1,75 +1,81 @@
|
|||||||
import { getToken, removeToken } from './storage';
|
import { getToken, removeToken } from './storage'
|
||||||
|
|
||||||
const BASE_URL = 'xxxxx'
|
const BASE_URL = 'http://c36bd4b4.natappfree.cc'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网络请求封装
|
* 网络请求封装
|
||||||
* @param {Object} options 请求参数
|
* @param {Object} options 请求参数
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
const request = (options) => {
|
const request = options => {
|
||||||
// 默认配置
|
// 默认配置
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
url: '',
|
url: '',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
data: {},
|
data: {},
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json' // 默认请求内容类型
|
'Content-Type': 'application/json' // 默认请求内容类型
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并配置
|
// 合并配置
|
||||||
const config = { ...defaultOptions, ...options }
|
const config = { ...defaultOptions, ...options }
|
||||||
|
|
||||||
// 请求拦截:添加token等通用header
|
// 请求拦截:添加token等通用header
|
||||||
if (getToken()) {
|
if (getToken()) {
|
||||||
config.header['Authorization'] = 'Bearer ' + getToken()
|
config.header['Authorization'] = 'Bearer ' + getToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示加载状态(可选)
|
// 显示加载状态(可选)
|
||||||
if (options.loading !== false) {
|
if (options.loading !== false) {
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: '加载中...',
|
title: '加载中...',
|
||||||
mask: true
|
mask: true
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
url: BASE_URL + config.url,
|
url: BASE_URL + config.url,
|
||||||
method: config.method,
|
method: config.method,
|
||||||
data: config.data,
|
data: config.data,
|
||||||
timeout: 10000, // 请求超时时间
|
timeout: 10000, // 请求超时时间
|
||||||
header: config.header,
|
header: config.header,
|
||||||
success: (response) => {
|
success: response => {
|
||||||
// 响应拦截:根据状态码处理
|
console.log(response)
|
||||||
if (response.statusCode === 200) {
|
// 响应拦截:根据状态码处理
|
||||||
// 这里可以根据后端数据格式调整
|
if (response.statusCode === 200) {
|
||||||
// 例如:if (response.data.code === 0) {...}
|
// 这里可以根据后端数据格式调整
|
||||||
resolve(response.data)
|
// 例如:if (response.data.code === 0) {...}
|
||||||
} else {
|
if (response.data.code === 200) {
|
||||||
// 状态码错误处理
|
resolve(response.data)
|
||||||
handleError(response.statusCode, response.data)
|
} else {
|
||||||
reject(response)
|
handleError(response.data.code, response.data)
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
fail: (error) => {
|
// 状态码错误处理
|
||||||
// 网络错误处理
|
handleError(response.statusCode, response.data)
|
||||||
uni.showToast({
|
reject(response)
|
||||||
title: '网络异常,请检查网络连接',
|
}
|
||||||
icon: 'none',
|
},
|
||||||
duration: 2000
|
fail: error => {
|
||||||
})
|
// 网络错误处理
|
||||||
reject(error)
|
uni.showToast({
|
||||||
},
|
title: '网络异常,请检查网络连接',
|
||||||
complete: () => {
|
icon: 'none',
|
||||||
// 隐藏加载状态
|
duration: 2000,
|
||||||
if (options.loading !== false) {
|
mask: true
|
||||||
uni.hideLoading()
|
})
|
||||||
}
|
reject(error)
|
||||||
}
|
},
|
||||||
})
|
complete: () => {
|
||||||
})
|
// 隐藏加载状态
|
||||||
|
if (options.loading !== false) {
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,49 +84,53 @@ const request = (options) => {
|
|||||||
* @param {Object} data 响应数据
|
* @param {Object} data 响应数据
|
||||||
*/
|
*/
|
||||||
const handleError = (statusCode, data) => {
|
const handleError = (statusCode, data) => {
|
||||||
switch (statusCode) {
|
switch (statusCode) {
|
||||||
case 401:
|
case 401:
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '登录已过期,请重新登录',
|
content: '登录已过期,请重新登录',
|
||||||
showCancel: false,
|
showCancel: false,
|
||||||
success: () => {
|
success: () => {
|
||||||
// 清除本地存储的token并跳转到登录页
|
// 清除本地存储的token并跳转到登录页
|
||||||
removeToken()
|
removeToken()
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/login/index'
|
url: '/pages/login/index'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '没有权限访问',
|
title: '没有权限访问',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
})
|
mask: true
|
||||||
break
|
})
|
||||||
case 404:
|
break
|
||||||
uni.showToast({
|
case 404:
|
||||||
title: '请求资源不存在',
|
uni.showToast({
|
||||||
icon: 'none',
|
title: '请求资源不存在',
|
||||||
duration: 2000
|
icon: 'none',
|
||||||
})
|
duration: 2000,
|
||||||
break
|
mask: true
|
||||||
case 500:
|
})
|
||||||
uni.showToast({
|
break
|
||||||
title: '服务器内部错误',
|
case 500:
|
||||||
icon: 'none',
|
uni.showToast({
|
||||||
duration: 2000
|
title: data.msg || '服务器内部错误',
|
||||||
})
|
icon: 'none',
|
||||||
break
|
duration: 2000,
|
||||||
default:
|
mask: true
|
||||||
uni.showToast({
|
})
|
||||||
title: data.message || '请求失败,请重试',
|
break
|
||||||
icon: 'none',
|
default:
|
||||||
duration: 2000
|
uni.showToast({
|
||||||
})
|
title: data.msg || '请求失败,请重试',
|
||||||
}
|
icon: 'none',
|
||||||
|
duration: 2000,
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default request
|
export default request
|
||||||
|
|||||||
@@ -1,16 +1,31 @@
|
|||||||
import { STORAGE_KEYS } from '@/constants/storage-keys'
|
import { STORAGE_KEYS } from '@/constants/storage-keys'
|
||||||
|
|
||||||
/** 保存 token */
|
/** 保存 token */
|
||||||
export const setToken = (v) => {
|
export const setTokenData = v => {
|
||||||
return uni.setStorageSync(STORAGE_KEYS.TOKEN, v)
|
return uni.setStorageSync(STORAGE_KEYS.TOKEN, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取 token */
|
/** 获取 token */
|
||||||
export const getToken = () => {
|
export const getToken = () => {
|
||||||
return uni.getStorageSync(STORAGE_KEYS.TOKEN) || ''
|
return uni.getStorageSync(STORAGE_KEYS.TOKEN) || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 清楚 token */
|
/** 清楚 token */
|
||||||
export const removeToken = () => {
|
export const removeToken = () => {
|
||||||
return uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
return uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 保存用户信息 */
|
||||||
|
export const setUserInfoData = v => {
|
||||||
|
return uni.setStorageSync(STORAGE_KEYS.USER, JSON.stringify(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取用户信息 */
|
||||||
|
export const getUserInfoData = () => {
|
||||||
|
return uni.getStorageSync(STORAGE_KEYS.USER) || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除用户信息 */
|
||||||
|
export const removeUserInfoData = () => {
|
||||||
|
return uni.removeStorageSync(STORAGE_KEYS.USER)
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,11 +37,14 @@ const showToast = (message, type = 'none', duration = 2000) => {
|
|||||||
if (type === 'error') icon = 'error'
|
if (type === 'error') icon = 'error'
|
||||||
if (type === 'warning') icon = 'none'
|
if (type === 'warning') icon = 'none'
|
||||||
|
|
||||||
uni.showToast({
|
return new Promise(resolve => {
|
||||||
title: message,
|
uni.showToast({
|
||||||
icon,
|
title: message,
|
||||||
duration,
|
icon,
|
||||||
mask: true
|
duration,
|
||||||
|
mask: true
|
||||||
|
})
|
||||||
|
setTimeout(() => resolve(), duration)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
utils/validate.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// 正则
|
||||||
|
|
||||||
|
/** 手机号正则(中国大陆) */
|
||||||
|
const PHONE_REGEX = /^1[3-9]\d{9}$/
|
||||||
|
|
||||||
|
/** 邮箱正则 */
|
||||||
|
const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
|
|
||||||
|
/** 身份证号(简化版) */
|
||||||
|
const ID_CARD_REGEX = /(^\d{15}$)|(^\d{18}$)|(^\d{17}[\dXx]$)/
|
||||||
|
|
||||||
|
/** 密码强度(至少8位,包含数字和字母) */
|
||||||
|
const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验手机号
|
||||||
|
* @param {string} phone - 待校验的手机号
|
||||||
|
* @returns {{ valid: boolean, message: string }}
|
||||||
|
*/
|
||||||
|
export const validatePhone = phone => {
|
||||||
|
if (!phone) return { valid: false, message: '手机号不能为空' }
|
||||||
|
if (!PHONE_REGEX.test(phone)) {
|
||||||
|
return { valid: false, message: '手机号格式不正确' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验邮箱
|
||||||
|
* @param {string} email - 待校验的邮箱
|
||||||
|
* @returns {{ valid: boolean, message: string }}
|
||||||
|
*/
|
||||||
|
export const validateEmail = email => {
|
||||||
|
if (!email) return { valid: false, message: '邮箱不能为空' }
|
||||||
|
if (!EMAIL_REGEX.test(email)) {
|
||||||
|
return { valid: false, message: '邮箱格式不正确' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验密码强度
|
||||||
|
* @param {string} password - 待校验的密码
|
||||||
|
* @returns {{ valid: boolean, message: string }}
|
||||||
|
*/
|
||||||
|
export const validatePassword = password => {
|
||||||
|
if (!password) return { valid: false, message: '密码不能为空' }
|
||||||
|
if (password.length < 8) {
|
||||||
|
return { valid: false, message: '密码长度不能少于8位' }
|
||||||
|
}
|
||||||
|
if (!PASSWORD_REGEX.test(password)) {
|
||||||
|
return { valid: false, message: '密码需包含字母和数字' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验确认密码是否与原密码一致
|
||||||
|
* @param {string} password - 原始密码
|
||||||
|
* @param {string} confirmPassword - 确认密码
|
||||||
|
* @returns {{ valid: boolean, message: string }}
|
||||||
|
*/
|
||||||
|
export function validateConfirmPassword(password, confirmPassword) {
|
||||||
|
if (!confirmPassword) {
|
||||||
|
return { valid: false, message: '请再次输入密码' }
|
||||||
|
}
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
return { valid: false, message: '两次输入的密码不一致' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验身份证号(仅格式校验,不验证真实性)
|
||||||
|
* @param {string} idCard - 待校验的身份证号
|
||||||
|
* @returns {{ valid: boolean, message: string }}
|
||||||
|
*/
|
||||||
|
export const validateIdCard = idCard => {
|
||||||
|
if (!idCard) return { valid: false, message: '身份证号不能为空' }
|
||||||
|
if (!ID_CARD_REGEX.test(idCard)) {
|
||||||
|
return { valid: false, message: '身份证号格式不正确' }
|
||||||
|
}
|
||||||
|
return { valid: true, message: '' }
|
||||||
|
}
|
||||||