需要修复商城顶部筛选左右滑动问题
@@ -2,8 +2,13 @@
|
||||
"version" : "1.0",
|
||||
"configurations" : [
|
||||
{
|
||||
"customPlaygroundType" : "device",
|
||||
"playground" : "standard",
|
||||
"type" : "uni-app:app-ios"
|
||||
},
|
||||
{
|
||||
"playground" : "standard",
|
||||
"type" : "uni-app:app-android"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @ts-check
|
||||
|
||||
module.exports = {
|
||||
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>
|
||||
import { reactive, computed } from 'vue'
|
||||
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({
|
||||
/**
|
||||
@@ -17,32 +27,67 @@
|
||||
const isPhone = computed(() => props.type === 'phone')
|
||||
|
||||
const formData = reactive({
|
||||
// 手机号
|
||||
// 手机号、邮箱
|
||||
name: '',
|
||||
// 验证码
|
||||
code: '',
|
||||
// 密码
|
||||
password: '',
|
||||
// 确认密码
|
||||
confirmPassword: '',
|
||||
// 邀请码
|
||||
inviteCode: '',
|
||||
agreement: false
|
||||
invitationCode: '54321',
|
||||
agreement: true
|
||||
})
|
||||
|
||||
const isBtn = computed(() => {
|
||||
return (
|
||||
formData.name &&
|
||||
formData.code &&
|
||||
formData.password &&
|
||||
formData.confirmPassword &&
|
||||
formData.inviteCode &&
|
||||
formData.invitationCode &&
|
||||
!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 = () => {
|
||||
@@ -73,12 +118,12 @@
|
||||
icon="3"
|
||||
:placeholder="`请输入${isPhone ? '手机号' : '邮箱'}`"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
<!-- <cb-input
|
||||
v-model="formData.code"
|
||||
type="number"
|
||||
icon="6"
|
||||
placeholder="请输入验证码"
|
||||
></cb-input>
|
||||
></cb-input> -->
|
||||
<cb-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
@@ -92,7 +137,7 @@
|
||||
placeholder="请输入确认密码"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
v-model="formData.inviteCode"
|
||||
v-model="formData.invitationCode"
|
||||
type="number"
|
||||
icon="4"
|
||||
placeholder="请输入邀请码"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export const STORAGE_KEYS = {
|
||||
TOKEN: 'token',
|
||||
};
|
||||
USER: 'userInfo'
|
||||
}
|
||||
|
||||
77
pages.json
@@ -33,6 +33,49 @@
|
||||
"navigationBarTitleText": "忘记密码",
|
||||
"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": {
|
||||
@@ -41,5 +84,37 @@
|
||||
"navigationBarBackgroundColor": "#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>
|
||||
import { onMounted, ref } from 'vue'
|
||||
const cb = v => {
|
||||
console.log(v)
|
||||
}
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { reLaunch } from '@/utils/router'
|
||||
const cb = v => {
|
||||
console.log(v)
|
||||
}
|
||||
|
||||
// export default {
|
||||
// data() {
|
||||
// return {
|
||||
// title: 'Hello'
|
||||
// }
|
||||
// },
|
||||
// onLoad() {
|
||||
// export default {
|
||||
// data() {
|
||||
// return {
|
||||
// title: 'Hello'
|
||||
// }
|
||||
// },
|
||||
// onLoad() {
|
||||
|
||||
// },
|
||||
// methods: {
|
||||
// },
|
||||
// methods: {
|
||||
|
||||
// }
|
||||
// }
|
||||
const title = ref('你112好')
|
||||
// }
|
||||
// }
|
||||
|
||||
onLoad(() => {
|
||||
// 3秒后跳转
|
||||
setTimeout(() => {
|
||||
reLaunch('/pages/news-list/news-list')
|
||||
}, 3000)
|
||||
})
|
||||
const title = ref('这个是启动页')
|
||||
</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>
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 200rpx;
|
||||
width: 200rpx;
|
||||
margin-top: 200rpx;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
.logo {
|
||||
height: 200rpx;
|
||||
width: 200rpx;
|
||||
margin-top: 200rpx;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.text-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.text-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #8f8f94;
|
||||
}
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #8f8f94;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
|
||||
const { showToast } = useUI()
|
||||
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
@@ -15,6 +18,16 @@
|
||||
}
|
||||
|
||||
const onBottomBtn = () => {
|
||||
if (!formData.username) {
|
||||
showToast('请输入手机号/邮箱')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.password) {
|
||||
showToast('请输入密码')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('确认')
|
||||
}
|
||||
</script>
|
||||
@@ -38,14 +51,14 @@
|
||||
v-model="formData.username"
|
||||
placeholder="请输入手机号/邮箱"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
<!-- <cb-input
|
||||
v-model="formData.code"
|
||||
v-model:code="isCode"
|
||||
type="number"
|
||||
icon="6"
|
||||
placeholder="请输入验证码"
|
||||
@onGetCode="getCode"
|
||||
></cb-input>
|
||||
></cb-input> -->
|
||||
<cb-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { reLaunch, navigateTo } from '@/utils/router'
|
||||
import { userLogin } from '@/api'
|
||||
import { useTokenStore } from '@/stores/token'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const { showToast } = useUI()
|
||||
const { setToken } = useTokenStore()
|
||||
const { fetchUserInfo } = useUserStore()
|
||||
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
@@ -12,9 +17,18 @@
|
||||
agreement: false
|
||||
})
|
||||
|
||||
const onLogin = () => {
|
||||
showToast('登录成功')
|
||||
console.log('登录:', formData)
|
||||
const onLogin = async () => {
|
||||
if (!formData.agreement) {
|
||||
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 = () => {
|
||||
@@ -26,7 +40,7 @@
|
||||
}
|
||||
|
||||
onLoad(e => {
|
||||
console.log('接收==:', e.id)
|
||||
console.log('接收参数,返回对应页面的时候使用', e)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -50,9 +64,7 @@
|
||||
<agreement-checkbox v-model="formData.agreement" />
|
||||
<cb-button
|
||||
class="bottom-btn"
|
||||
:disabled="
|
||||
!formData.username || !formData.password || !formData.agreement
|
||||
"
|
||||
:disabled="!formData.username || !formData.password"
|
||||
@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 { ref } from 'vue';
|
||||
import { STORAGE_KEYS } from '@/constants/storage-keys';
|
||||
import { getToken, removeToken } from '@/utils/storage';
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { STORAGE_KEYS } from '@/constants/storage-keys'
|
||||
import { setTokenData, getToken, removeToken } from '@/utils/storage'
|
||||
|
||||
/** 登录状态 */
|
||||
export const useTokenStore = defineStore(STORAGE_KEYS.TOKEN, () => {
|
||||
// 从本地存储获取token
|
||||
const token = ref(getToken() || null)
|
||||
const isLogin = ref(!!token.value)
|
||||
|
||||
// 从本地存储获取token
|
||||
const token = ref(getToken() || null);
|
||||
const isLogin = ref(!!token.value);
|
||||
/** 设置token并保存到本地 */
|
||||
const setToken = newToken => {
|
||||
token.value = newToken
|
||||
isLogin.value = true
|
||||
setTokenData(newToken)
|
||||
}
|
||||
|
||||
/** 设置token并保存到本地 */
|
||||
const setToken = (newToken) => {
|
||||
token.value = newToken;
|
||||
isLogin.value = true;
|
||||
setToken(newToken);
|
||||
};
|
||||
/** 清除token */
|
||||
const clearToken = () => {
|
||||
token.value = null
|
||||
isLogin.value = false
|
||||
removeToken()
|
||||
}
|
||||
|
||||
/** 清除token */
|
||||
const clearToken = () => {
|
||||
token.value = null;
|
||||
isLogin.value = false;
|
||||
removeToken()
|
||||
};
|
||||
/** 检查token是否有效(可扩展过期时间判断) */
|
||||
const checkToken = () => {
|
||||
// 简单判断:token存在且不为空
|
||||
return !!token.value
|
||||
}
|
||||
|
||||
/** 检查token是否有效(可扩展过期时间判断) */
|
||||
const checkToken = () => {
|
||||
// 简单判断:token存在且不为空
|
||||
return !!token.value;
|
||||
};
|
||||
/** 验证token是否过期(实际项目中可添加过期时间判断) */
|
||||
const isTokenExpired = () => {
|
||||
// 示例:如果token中包含过期时间,这里可以判断
|
||||
// 例如:const expireTime = parseInt(token.value.split('.')[1]);
|
||||
// return Date.now() > expireTime * 1000;
|
||||
return false // 默认不过期
|
||||
}
|
||||
|
||||
/** 验证token是否过期(实际项目中可添加过期时间判断) */
|
||||
const isTokenExpired = () => {
|
||||
// 示例:如果token中包含过期时间,这里可以判断
|
||||
// 例如:const expireTime = parseInt(token.value.split('.')[1]);
|
||||
// return Date.now() > expireTime * 1000;
|
||||
return false; // 默认不过期
|
||||
};
|
||||
|
||||
return { token, isLogin, setToken, clearToken, 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
|
||||
}
|
||||
})
|
||||
218
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 请求参数
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const request = (options) => {
|
||||
// 默认配置
|
||||
const defaultOptions = {
|
||||
url: '',
|
||||
method: 'GET',
|
||||
data: {},
|
||||
header: {
|
||||
'Content-Type': 'application/json' // 默认请求内容类型
|
||||
}
|
||||
}
|
||||
const request = options => {
|
||||
// 默认配置
|
||||
const defaultOptions = {
|
||||
url: '',
|
||||
method: 'GET',
|
||||
data: {},
|
||||
header: {
|
||||
'Content-Type': 'application/json' // 默认请求内容类型
|
||||
}
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
const config = { ...defaultOptions, ...options }
|
||||
// 合并配置
|
||||
const config = { ...defaultOptions, ...options }
|
||||
|
||||
// 请求拦截:添加token等通用header
|
||||
if (getToken()) {
|
||||
config.header['Authorization'] = 'Bearer ' + getToken()
|
||||
}
|
||||
// 请求拦截:添加token等通用header
|
||||
if (getToken()) {
|
||||
config.header['Authorization'] = 'Bearer ' + getToken()
|
||||
}
|
||||
|
||||
// 显示加载状态(可选)
|
||||
if (options.loading !== false) {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
};
|
||||
// 显示加载状态(可选)
|
||||
if (options.loading !== false) {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: BASE_URL + config.url,
|
||||
method: config.method,
|
||||
data: config.data,
|
||||
timeout: 10000, // 请求超时时间
|
||||
header: config.header,
|
||||
success: (response) => {
|
||||
// 响应拦截:根据状态码处理
|
||||
if (response.statusCode === 200) {
|
||||
// 这里可以根据后端数据格式调整
|
||||
// 例如:if (response.data.code === 0) {...}
|
||||
resolve(response.data)
|
||||
} else {
|
||||
// 状态码错误处理
|
||||
handleError(response.statusCode, response.data)
|
||||
reject(response)
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
// 网络错误处理
|
||||
uni.showToast({
|
||||
title: '网络异常,请检查网络连接',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
reject(error)
|
||||
},
|
||||
complete: () => {
|
||||
// 隐藏加载状态
|
||||
if (options.loading !== false) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: BASE_URL + config.url,
|
||||
method: config.method,
|
||||
data: config.data,
|
||||
timeout: 10000, // 请求超时时间
|
||||
header: config.header,
|
||||
success: response => {
|
||||
console.log(response)
|
||||
// 响应拦截:根据状态码处理
|
||||
if (response.statusCode === 200) {
|
||||
// 这里可以根据后端数据格式调整
|
||||
// 例如:if (response.data.code === 0) {...}
|
||||
if (response.data.code === 200) {
|
||||
resolve(response.data)
|
||||
} else {
|
||||
handleError(response.data.code, response.data)
|
||||
}
|
||||
} else {
|
||||
// 状态码错误处理
|
||||
handleError(response.statusCode, response.data)
|
||||
reject(response)
|
||||
}
|
||||
},
|
||||
fail: error => {
|
||||
// 网络错误处理
|
||||
uni.showToast({
|
||||
title: '网络异常,请检查网络连接',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
})
|
||||
reject(error)
|
||||
},
|
||||
complete: () => {
|
||||
// 隐藏加载状态
|
||||
if (options.loading !== false) {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,49 +84,53 @@ const request = (options) => {
|
||||
* @param {Object} data 响应数据
|
||||
*/
|
||||
const handleError = (statusCode, data) => {
|
||||
switch (statusCode) {
|
||||
case 401:
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '登录已过期,请重新登录',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 清除本地存储的token并跳转到登录页
|
||||
removeToken()
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
break
|
||||
case 403:
|
||||
uni.showToast({
|
||||
title: '没有权限访问',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
break
|
||||
case 404:
|
||||
uni.showToast({
|
||||
title: '请求资源不存在',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
break
|
||||
case 500:
|
||||
uni.showToast({
|
||||
title: '服务器内部错误',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
break
|
||||
default:
|
||||
uni.showToast({
|
||||
title: data.message || '请求失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
switch (statusCode) {
|
||||
case 401:
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '登录已过期,请重新登录',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 清除本地存储的token并跳转到登录页
|
||||
removeToken()
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
break
|
||||
case 403:
|
||||
uni.showToast({
|
||||
title: '没有权限访问',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
})
|
||||
break
|
||||
case 404:
|
||||
uni.showToast({
|
||||
title: '请求资源不存在',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
})
|
||||
break
|
||||
case 500:
|
||||
uni.showToast({
|
||||
title: data.msg || '服务器内部错误',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
})
|
||||
break
|
||||
default:
|
||||
uni.showToast({
|
||||
title: data.msg || '请求失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default request
|
||||
@@ -1,16 +1,31 @@
|
||||
import { STORAGE_KEYS } from '@/constants/storage-keys'
|
||||
|
||||
/** 保存 token */
|
||||
export const setToken = (v) => {
|
||||
return uni.setStorageSync(STORAGE_KEYS.TOKEN, v)
|
||||
export const setTokenData = v => {
|
||||
return uni.setStorageSync(STORAGE_KEYS.TOKEN, v)
|
||||
}
|
||||
|
||||
/** 获取 token */
|
||||
export const getToken = () => {
|
||||
return uni.getStorageSync(STORAGE_KEYS.TOKEN) || ''
|
||||
return uni.getStorageSync(STORAGE_KEYS.TOKEN) || ''
|
||||
}
|
||||
|
||||
/** 清楚 token */
|
||||
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 === 'warning') icon = 'none'
|
||||
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon,
|
||||
duration,
|
||||
mask: true
|
||||
return new Promise(resolve => {
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon,
|
||||
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: '' }
|
||||
}
|
||||