Browse Source
- 新增 CMS 文章查询接口 getCmsArticleByCode - 调整 UserCard 组件界面样式和逻辑-优化 BestSellers 商品展示组件 - 更新 IsDealer 组件支持网站字段配置 - 移除用户页面部分冗余代码和样式 - 增加主题样式支持和背景装饰元素 - 调整用户相关组件层级和定位样式master
30 changed files with 1190 additions and 294 deletions
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.3 KiB |
@ -0,0 +1,102 @@ |
|||||
|
import {Cell} from '@nutui/nutui-react-taro' |
||||
|
import navTo from "@/utils/common"; |
||||
|
import {View, Text} from '@tarojs/components' |
||||
|
import {ArrowRight, Reward, Setting} from '@nutui/icons-react-taro' |
||||
|
import {useUser} from '@/hooks/useUser' |
||||
|
import {useEffect, useState} from "react"; |
||||
|
import {useDealerUser} from "@/hooks/useDealerUser"; |
||||
|
import {configWebsiteField} from "@/api/cms/cmsWebsiteField"; |
||||
|
import {Config} from "@/api/cms/cmsWebsiteField/model"; |
||||
|
|
||||
|
const UserCell = () => { |
||||
|
const {isSuperAdmin} = useUser(); |
||||
|
const {dealerUser} = useDealerUser() |
||||
|
const [config, setConfig] = useState<Config>() |
||||
|
|
||||
|
useEffect(() => { |
||||
|
configWebsiteField().then(data => { |
||||
|
console.log(data) |
||||
|
setConfig(data) |
||||
|
}) |
||||
|
}, []) |
||||
|
|
||||
|
/** |
||||
|
* 管理中心 |
||||
|
*/ |
||||
|
if (isSuperAdmin()) { |
||||
|
return ( |
||||
|
<> |
||||
|
<View className={'px-4'}> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
style={{ |
||||
|
backgroundImage: 'linear-gradient(to right bottom, #e53e3e, #c53030)', |
||||
|
}} |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Setting className={'text-white '} size={16}/> |
||||
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-white font-medium'}>管理中心</Text> |
||||
|
</View> |
||||
|
} |
||||
|
extra={<ArrowRight color="#ffffff" size={18}/>} |
||||
|
onClick={() => navTo('/admin/index', true)} |
||||
|
/> |
||||
|
</View> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分销中心 |
||||
|
*/ |
||||
|
if (dealerUser) { |
||||
|
return ( |
||||
|
<> |
||||
|
<View className={'px-4'}> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
style={{ |
||||
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)', |
||||
|
}} |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Reward className={'text-orange-100 '} size={16}/> |
||||
|
<Text style={{fontSize: '16px'}} |
||||
|
className={'pl-3 text-orange-100 font-medium'}>分销中心</Text> |
||||
|
{/*<Text className={'text-white opacity-80 pl-3'}>门店核销</Text>*/} |
||||
|
</View> |
||||
|
} |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => navTo('/dealer/index', true)} |
||||
|
/> |
||||
|
</View> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 普通用户 |
||||
|
*/ |
||||
|
return ( |
||||
|
<> |
||||
|
<View className={'px-4'}> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
style={{ |
||||
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)', |
||||
|
}} |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Reward className={'text-orange-100 '} size={16}/> |
||||
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText}开通VIP</Text> |
||||
|
<Text className={'text-white opacity-80 pl-3'}>{config?.vipComments}享优惠</Text> |
||||
|
</View> |
||||
|
} |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => navTo('/dealer/apply/add', true)} |
||||
|
/> |
||||
|
</View> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
export default UserCell |
@ -0,0 +1,277 @@ |
|||||
|
import {Avatar, Tag, Space, Button} from '@nutui/nutui-react-taro' |
||||
|
import {View, Text, Image} from '@tarojs/components' |
||||
|
import {getUserInfo, getWxOpenId} from '@/api/layout'; |
||||
|
import Taro from '@tarojs/taro'; |
||||
|
import {useEffect, useState, forwardRef, useImperativeHandle} from "react"; |
||||
|
import {User} from "@/api/system/user/model"; |
||||
|
import navTo from "@/utils/common"; |
||||
|
import {TenantId} from "@/config/app"; |
||||
|
import {useUser} from "@/hooks/useUser"; |
||||
|
import {useUserData} from "@/hooks/useUserData"; |
||||
|
import {getStoredInviteParams} from "@/utils/invite"; |
||||
|
import UnifiedQRButton from "@/components/UnifiedQRButton"; |
||||
|
|
||||
|
const UserCard = forwardRef<any, any>((_, ref) => { |
||||
|
const {data, refresh} = useUserData() |
||||
|
const {getDisplayName, getRoleName} = useUser(); |
||||
|
const [IsLogin, setIsLogin] = useState<boolean>(false) |
||||
|
const [userInfo, setUserInfo] = useState<User>() |
||||
|
|
||||
|
// 下拉刷新
|
||||
|
const handleRefresh = async () => { |
||||
|
await refresh() |
||||
|
Taro.showToast({ |
||||
|
title: '刷新成功', |
||||
|
icon: 'success' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 暴露方法给父组件
|
||||
|
useImperativeHandle(ref, () => ({ |
||||
|
handleRefresh |
||||
|
})) |
||||
|
|
||||
|
useEffect(() => { |
||||
|
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
||||
|
Taro.getSetting({ |
||||
|
success: (res) => { |
||||
|
if (res.authSetting['scope.userInfo']) { |
||||
|
// 用户已经授权过,可以直接获取用户信息
|
||||
|
console.log('用户已经授权过,可以直接获取用户信息') |
||||
|
reload(); |
||||
|
} else { |
||||
|
// 用户未授权,需要弹出授权窗口
|
||||
|
console.log('用户未授权,需要弹出授权窗口') |
||||
|
showAuthModal(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}, []); |
||||
|
|
||||
|
const reload = () => { |
||||
|
Taro.getUserInfo({ |
||||
|
success: (res) => { |
||||
|
const avatar = res.userInfo.avatarUrl; |
||||
|
setUserInfo({ |
||||
|
avatar, |
||||
|
nickname: res.userInfo.nickName, |
||||
|
sexName: res.userInfo.gender == 1 ? '男' : '女' |
||||
|
}) |
||||
|
getUserInfo().then((data) => { |
||||
|
if (data) { |
||||
|
setUserInfo(data) |
||||
|
setIsLogin(true); |
||||
|
Taro.setStorageSync('UserId', data.userId) |
||||
|
|
||||
|
// 获取openId
|
||||
|
if (!data.openid) { |
||||
|
Taro.login({ |
||||
|
success: (res) => { |
||||
|
getWxOpenId({code: res.code}).then(() => { |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}).catch(() => { |
||||
|
console.log('未登录') |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const showAuthModal = () => { |
||||
|
Taro.showModal({ |
||||
|
title: '授权提示', |
||||
|
content: '需要获取您的用户信息', |
||||
|
confirmText: '去授权', |
||||
|
cancelText: '取消', |
||||
|
success: (res) => { |
||||
|
if (res.confirm) { |
||||
|
// 用户点击确认,打开授权设置页面
|
||||
|
openSetting(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
const openSetting = () => { |
||||
|
// Taro.openSetting:调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
|
||||
|
Taro.openSetting({ |
||||
|
success: (res) => { |
||||
|
if (res.authSetting['scope.userInfo']) { |
||||
|
// 用户授权成功,可以获取用户信息
|
||||
|
reload(); |
||||
|
} else { |
||||
|
// 用户拒绝授权,提示授权失败
|
||||
|
Taro.showToast({ |
||||
|
title: '授权失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
/* 获取用户手机号 */ |
||||
|
const handleGetPhoneNumber = ({detail}: { detail: { code?: string, encryptedData?: string, iv?: string } }) => { |
||||
|
const {code, encryptedData, iv} = detail |
||||
|
|
||||
|
// 获取存储的邀请参数
|
||||
|
const inviteParams = getStoredInviteParams() |
||||
|
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0 |
||||
|
|
||||
|
Taro.login({ |
||||
|
success: function () { |
||||
|
if (code) { |
||||
|
Taro.request({ |
||||
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone', |
||||
|
method: 'POST', |
||||
|
data: { |
||||
|
code, |
||||
|
encryptedData, |
||||
|
iv, |
||||
|
notVerifyPhone: true, |
||||
|
refereeId: refereeId, // 使用解析出的推荐人ID
|
||||
|
sceneType: 'save_referee', |
||||
|
tenantId: TenantId |
||||
|
}, |
||||
|
header: { |
||||
|
'content-type': 'application/json', |
||||
|
TenantId |
||||
|
}, |
||||
|
success: function (res) { |
||||
|
if (res.data.code == 1) { |
||||
|
Taro.showToast({ |
||||
|
title: res.data.message, |
||||
|
icon: 'error', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
return false; |
||||
|
} |
||||
|
// 登录成功
|
||||
|
Taro.setStorageSync('access_token', res.data.data.access_token) |
||||
|
Taro.setStorageSync('UserId', res.data.data.user.userId) |
||||
|
setUserInfo(res.data.data.user) |
||||
|
setIsLogin(true) |
||||
|
} |
||||
|
}) |
||||
|
} else { |
||||
|
console.log('登录失败!') |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<View className={'header-bg pt-20'}> |
||||
|
<View className={'p-4'}> |
||||
|
{/* 使用相对定位容器,让个人资料图片可以绝对定位在右上角 */} |
||||
|
<View className="relative"> |
||||
|
<View |
||||
|
className={'user-card w-full flex flex-col justify-around rounded-xl'} |
||||
|
style={{ |
||||
|
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', |
||||
|
height: '170px', |
||||
|
}} |
||||
|
> |
||||
|
<View className={'user-card-header flex w-full justify-between items-center pt-4'}> |
||||
|
<View className={'flex items-center mx-4'}> |
||||
|
{ |
||||
|
IsLogin ? ( |
||||
|
<Avatar size="large" |
||||
|
src={userInfo?.avatar || 'https://oss.wsdns.cn/20250623/62f830b85edb4a7293b8948c25e6f987.jpeg'} |
||||
|
shape="round"/> |
||||
|
) : ( |
||||
|
<Button className={'text-black'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}> |
||||
|
<Avatar size="large" |
||||
|
src={userInfo?.avatar || 'https://oss.wsdns.cn/20250623/62f830b85edb4a7293b8948c25e6f987.jpeg'} |
||||
|
shape="round"/> |
||||
|
</Button> |
||||
|
) |
||||
|
} |
||||
|
<View className={'user-info flex flex-col px-2'}> |
||||
|
<View className={'py-1 text-black font-bold'}>{getDisplayName()}</View> |
||||
|
{IsLogin ? ( |
||||
|
<View className={'grade text-xs py-1'}> |
||||
|
<Tag type="success" round> |
||||
|
<Text className={'p-1'}> |
||||
|
{getRoleName()} |
||||
|
</Text> |
||||
|
</Tag> |
||||
|
</View> |
||||
|
) : ''} |
||||
|
</View> |
||||
|
</View> |
||||
|
<Space style={{ |
||||
|
marginTop: '30px', |
||||
|
marginRight: '10px' |
||||
|
}}> |
||||
|
{/*统一扫码入口 - 支持登录和核销*/} |
||||
|
<UnifiedQRButton |
||||
|
text="扫码" |
||||
|
size="small" |
||||
|
onSuccess={(result) => { |
||||
|
console.log('统一扫码成功:', result); |
||||
|
// 根据扫码类型给出不同的提示
|
||||
|
if (result.type === 'verification') { |
||||
|
// 核销成功,可以显示更多信息或跳转到详情页
|
||||
|
Taro.showModal({ |
||||
|
title: '核销成功', |
||||
|
content: `已成功核销的品类:${result.data.goodsName || '礼品卡'},面值¥${result.data.faceValue}` |
||||
|
}); |
||||
|
} |
||||
|
}} |
||||
|
onError={(error) => { |
||||
|
console.error('统一扫码失败:', error); |
||||
|
}} |
||||
|
/> |
||||
|
</Space> |
||||
|
</View> |
||||
|
<View className={'flex justify-around mt-1'}> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/wallet/wallet', true)}> |
||||
|
<Text className={'text-sm text-gray-500'}>余额</Text> |
||||
|
<Text className={'text-xl'}>{data?.balance || '0.00'}</Text> |
||||
|
</View> |
||||
|
<View className={'item flex justify-center flex-col items-center'}> |
||||
|
<Text className={'text-sm text-gray-500'}>积分</Text> |
||||
|
<Text className={'text-xl'}>{data?.points || 0}</Text> |
||||
|
</View> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/coupon/index', true)}> |
||||
|
<Text className={'text-sm text-gray-500'}>优惠券</Text> |
||||
|
<Text className={'text-xl'}>{data?.coupons || 0}</Text> |
||||
|
</View> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/gift/index', true)}> |
||||
|
<Text className={'text-sm text-gray-500'}>礼品卡</Text> |
||||
|
<Text className={'text-xl'}>{data?.giftCards || 0}</Text> |
||||
|
</View> |
||||
|
</View> |
||||
|
</View> |
||||
|
|
||||
|
{/* 个人资料图片,定位在右上角 */} |
||||
|
<View |
||||
|
className="absolute top-0 right-0 overflow-hidden" |
||||
|
style={{ |
||||
|
borderRadius: "0 0.75rem 0 0" |
||||
|
}} |
||||
|
onClick={() => navTo('/user/profile/profile', true)} |
||||
|
> |
||||
|
<Image |
||||
|
src="https://oss.wsdns.cn/20250913/7c3de38b377344b89131aba40214f63f.png" |
||||
|
style={{ |
||||
|
width: "200rpx" |
||||
|
}} |
||||
|
mode="widthFix" |
||||
|
/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</View> |
||||
|
</View> |
||||
|
) |
||||
|
}) |
||||
|
|
||||
|
export default UserCard; |
@ -0,0 +1,144 @@ |
|||||
|
import {Cell} from '@nutui/nutui-react-taro' |
||||
|
import navTo from "@/utils/common"; |
||||
|
import Taro from '@tarojs/taro' |
||||
|
import {View, Text} from '@tarojs/components' |
||||
|
import {ArrowRight, ShieldCheck, LogisticsError, Location, Tips, Ask} from '@nutui/icons-react-taro' |
||||
|
import {useUser} from '@/hooks/useUser' |
||||
|
|
||||
|
const UserCell = () => { |
||||
|
const {logoutUser, isCertified} = useUser(); |
||||
|
|
||||
|
const onLogout = () => { |
||||
|
Taro.showModal({ |
||||
|
title: '提示', |
||||
|
content: '确定要退出登录吗?', |
||||
|
success: function (res) { |
||||
|
if (res.confirm) { |
||||
|
// 使用 useUser hook 的 logoutUser 方法
|
||||
|
logoutUser(); |
||||
|
Taro.reLaunch({ |
||||
|
url: '/pages/index/index' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<View className={'px-4'}> |
||||
|
|
||||
|
<Cell.Group divider={true} description={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Text style={{marginTop: '12px'}}>我的服务</Text> |
||||
|
</View> |
||||
|
}> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
style={{ |
||||
|
display: 'none' |
||||
|
}} |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<LogisticsError size={16}/> |
||||
|
<Text className={'pl-3 text-sm'}>我的钱包</Text> |
||||
|
</View> |
||||
|
} |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => { |
||||
|
navTo('/user/wallet/index', true) |
||||
|
}} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Location size={16}/> |
||||
|
<Text className={'pl-3 text-sm'}>收货地址</Text> |
||||
|
</View> |
||||
|
} |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => { |
||||
|
navTo('/user/address/index', true) |
||||
|
}} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<ShieldCheck size={16} color={isCertified() ? '#52c41a' : '#666'}/> |
||||
|
<Text className={'pl-3 text-sm'}>实名认证</Text> |
||||
|
{isCertified() && ( |
||||
|
<Text className={'pl-2 text-xs text-green-500'}>已认证</Text> |
||||
|
)} |
||||
|
</View> |
||||
|
} |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => { |
||||
|
navTo('/user/userVerify/index', true) |
||||
|
}} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Ask size={16}/> |
||||
|
<Text className={'pl-3 text-sm'}>常见问题</Text> |
||||
|
</View> |
||||
|
} |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => { |
||||
|
navTo('/user/help/index') |
||||
|
}} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Tips size={16}/> |
||||
|
<Text className={'pl-3 text-sm'}>关于我们</Text> |
||||
|
</View> |
||||
|
} |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => { |
||||
|
navTo('/user/about/index') |
||||
|
}} |
||||
|
/> |
||||
|
</Cell.Group> |
||||
|
<Cell.Group divider={true} description={ |
||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}> |
||||
|
<Text style={{marginTop: '12px'}}>账号管理</Text> |
||||
|
</View> |
||||
|
}> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title="账号安全" |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => navTo('/user/profile/profile', true)} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title="切换主题" |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={() => navTo('/user/theme/index', true)} |
||||
|
/> |
||||
|
<Cell |
||||
|
className="nutui-cell-clickable" |
||||
|
title="退出登录" |
||||
|
align="center" |
||||
|
extra={<ArrowRight color="#cccccc" size={18}/>} |
||||
|
onClick={onLogout} |
||||
|
/> |
||||
|
</Cell.Group> |
||||
|
</View> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
export default UserCell |
@ -0,0 +1,102 @@ |
|||||
|
import {loginBySms} from "@/api/passport/login"; |
||||
|
import {useState} from "react"; |
||||
|
import Taro from '@tarojs/taro' |
||||
|
import {Popup} from '@nutui/nutui-react-taro' |
||||
|
import {UserParam} from "@/api/system/user/model"; |
||||
|
import {Button} from '@nutui/nutui-react-taro' |
||||
|
import {Form, Input} from '@nutui/nutui-react-taro' |
||||
|
import {Copyright, Version} from "@/config/app"; |
||||
|
const UserFooter = () => { |
||||
|
const [openLoginByPhone, setOpenLoginByPhone] = useState(false) |
||||
|
const [clickNum, setClickNum] = useState<number>(0) |
||||
|
const [FormData, setFormData] = useState<UserParam>( |
||||
|
{ |
||||
|
phone: undefined, |
||||
|
password: undefined |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
const onLoginByPhone = () => { |
||||
|
setFormData({}) |
||||
|
setClickNum(clickNum + 1); |
||||
|
if (clickNum > 10) { |
||||
|
setOpenLoginByPhone(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const closeLoginByPhone = () => { |
||||
|
setClickNum(0) |
||||
|
setOpenLoginByPhone(false) |
||||
|
} |
||||
|
|
||||
|
// 提交表单
|
||||
|
const submitByPhone = (values: any) => { |
||||
|
loginBySms({ |
||||
|
phone: values.phone, |
||||
|
code: values.code |
||||
|
}).then(() => { |
||||
|
setOpenLoginByPhone(false); |
||||
|
setTimeout(() => { |
||||
|
Taro.reLaunch({ |
||||
|
url: '/pages/index/index' |
||||
|
}) |
||||
|
},1000) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<div className={'text-center py-4 w-full text-gray-300'} onClick={onLoginByPhone}> |
||||
|
<div className={'text-xs text-gray-400 py-1'}>当前版本:{Version}</div> |
||||
|
<div className={'text-xs text-gray-400 py-1'}>Copyright © { new Date().getFullYear() } {Copyright}</div> |
||||
|
</div> |
||||
|
|
||||
|
<Popup |
||||
|
style={{width: '350px', padding: '10px'}} |
||||
|
visible={openLoginByPhone} |
||||
|
closeOnOverlayClick={false} |
||||
|
closeable={true} |
||||
|
onClose={closeLoginByPhone} |
||||
|
> |
||||
|
<Form |
||||
|
style={{width: '350px',padding: '10px'}} |
||||
|
divider |
||||
|
initialValues={FormData} |
||||
|
labelPosition="left" |
||||
|
onFinish={(values) => submitByPhone(values)} |
||||
|
footer={ |
||||
|
<div |
||||
|
style={{ |
||||
|
display: 'flex', |
||||
|
justifyContent: 'center', |
||||
|
width: '100%' |
||||
|
}} |
||||
|
> |
||||
|
<Button nativeType="submit" block style={{backgroundColor: '#000000',color: '#ffffff'}}> |
||||
|
提交 |
||||
|
</Button> |
||||
|
</div> |
||||
|
} |
||||
|
> |
||||
|
<Form.Item |
||||
|
label={'手机号码'} |
||||
|
name="phone" |
||||
|
required |
||||
|
rules={[{message: '手机号码'}]} |
||||
|
> |
||||
|
<Input placeholder="请输入手机号码" maxLength={11} type="text"/> |
||||
|
</Form.Item> |
||||
|
<Form.Item |
||||
|
label={'短信验证码'} |
||||
|
name="code" |
||||
|
required |
||||
|
rules={[{message: '短信验证码'}]} |
||||
|
> |
||||
|
<Input placeholder="请输入短信验证码" maxLength={6} type="text"/> |
||||
|
</Form.Item> |
||||
|
</Form> |
||||
|
</Popup> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
export default UserFooter |
@ -0,0 +1,145 @@ |
|||||
|
import {Grid, ConfigProvider} from '@nutui/nutui-react-taro' |
||||
|
import navTo from "@/utils/common"; |
||||
|
import Taro from '@tarojs/taro' |
||||
|
import {View, Button} from '@tarojs/components' |
||||
|
import { |
||||
|
ShieldCheck, |
||||
|
Location, |
||||
|
Tips, |
||||
|
Ask, |
||||
|
// Dongdong,
|
||||
|
People, |
||||
|
// AfterSaleService,
|
||||
|
Logout, |
||||
|
ShoppingAdd, |
||||
|
Service |
||||
|
} from '@nutui/icons-react-taro' |
||||
|
import {useUser} from "@/hooks/useUser"; |
||||
|
|
||||
|
const UserCell = () => { |
||||
|
const {logoutUser} = useUser(); |
||||
|
|
||||
|
const onLogout = () => { |
||||
|
Taro.showModal({ |
||||
|
title: '提示', |
||||
|
content: '确定要退出登录吗?', |
||||
|
success: function (res) { |
||||
|
if (res.confirm) { |
||||
|
// 使用 useUser hook 的 logoutUser 方法
|
||||
|
logoutUser(); |
||||
|
Taro.reLaunch({ |
||||
|
url: '/pages/index/index' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<View className="bg-white mx-4 mt-4 rounded-xl"> |
||||
|
<View className="font-semibold text-gray-800 pt-4 pl-4">我的服务</View> |
||||
|
<ConfigProvider> |
||||
|
<Grid |
||||
|
columns={4} |
||||
|
className="no-border-grid" |
||||
|
style={{ |
||||
|
'--nutui-grid-border-color': 'transparent', |
||||
|
'--nutui-grid-item-border-width': '0px', |
||||
|
border: 'none' |
||||
|
} as React.CSSProperties} |
||||
|
> |
||||
|
<Grid.Item text="企业采购" onClick={() => navTo('/user/poster/poster', true)}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<ShoppingAdd color="#3b82f6" size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
{/* 修改联系我们为微信客服 */} |
||||
|
<Grid.Item text="联系我们"> |
||||
|
<Button |
||||
|
open-type="contact" |
||||
|
className="w-full h-full flex flex-col items-center justify-center p-0 bg-transparent" |
||||
|
hover-class="none" |
||||
|
style={{border: 'none'}} |
||||
|
> |
||||
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<Service color="#67C23A" size="20"/> |
||||
|
</View> |
||||
|
</Button> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
<Grid.Item text="收货地址" onClick={() => navTo('/user/address/index', true)}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-emerald-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<Location color="#3b82f6" size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
<Grid.Item text={'实名认证'} onClick={() => navTo('/user/userVerify/index', true)}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-green-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<ShieldCheck color="#10b981" size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
<Grid.Item text={'推广邀请'} onClick={() => navTo('/dealer/team/index', true)}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-purple-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<People color="#8b5cf6" size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
{/*<Grid.Item text={'我的邀请码'} onClick={() => navTo('/dealer/qrcode/index', true)}>*/} |
||||
|
{/* <View className="text-center">*/} |
||||
|
{/* <View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/} |
||||
|
{/* <Dongdong color="#f59e0b" size="20"/>*/} |
||||
|
{/* </View>*/} |
||||
|
{/* </View>*/} |
||||
|
{/*</Grid.Item>*/} |
||||
|
|
||||
|
{/*<Grid.Item text={'管理中心'} onClick={() => navTo('/admin/index', true)}>*/} |
||||
|
{/* <View className="text-center">*/} |
||||
|
{/* <View className="w-12 h-12 bg-red-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/} |
||||
|
{/* <AfterSaleService className={'text-red-500'} size="20"/>*/} |
||||
|
{/* </View>*/} |
||||
|
{/* </View>*/} |
||||
|
{/*</Grid.Item>*/} |
||||
|
|
||||
|
<Grid.Item text={'常见问题'} onClick={() => navTo('/user/help/index')}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-cyan-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<Ask className={'text-cyan-500'} size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
<Grid.Item text={'关于我们'} onClick={() => navTo('/user/about/index')}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-amber-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<Tips className={'text-amber-500'} size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
<Grid.Item text={'安全退出'} onClick={onLogout}> |
||||
|
<View className="text-center"> |
||||
|
<View className="w-12 h-12 bg-pink-50 rounded-xl flex items-center justify-center mx-auto mb-2"> |
||||
|
<Logout className={'text-pink-500'} size="20"/> |
||||
|
</View> |
||||
|
</View> |
||||
|
</Grid.Item> |
||||
|
|
||||
|
</Grid> |
||||
|
</ConfigProvider> |
||||
|
</View> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
export default UserCell |
||||
|
|
@ -0,0 +1,122 @@ |
|||||
|
import navTo from "@/utils/common"; |
||||
|
import {View, Text} from '@tarojs/components'; |
||||
|
import {Badge} from '@nutui/nutui-react-taro'; |
||||
|
import {ArrowRight, Wallet, Comment, Transit, Refund, Package} from '@nutui/icons-react-taro'; |
||||
|
import {useOrderStats} from "@/hooks/useOrderStats"; |
||||
|
|
||||
|
function UserOrder() { |
||||
|
const { orderStats, refreshOrderStats } = useOrderStats(); |
||||
|
|
||||
|
// 处理长按刷新
|
||||
|
const handleLongPress = () => { |
||||
|
refreshOrderStats(); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<View className={'px-4 pb-2'}> |
||||
|
<View |
||||
|
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'} |
||||
|
style={{ |
||||
|
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
|
||||
|
// margin: '10px auto 0px auto',
|
||||
|
height: '120px', |
||||
|
// paddingBottom: '3px'
|
||||
|
// borderRadius: '22px 22px 0 0',
|
||||
|
}} |
||||
|
> |
||||
|
<View className={'title-bar flex justify-between pt-2'}> |
||||
|
<Text className={'title font-medium px-4'}>我的订单</Text> |
||||
|
<View |
||||
|
className={'more flex items-center px-2'} |
||||
|
onClick={() => navTo('/user/order/order', true)} |
||||
|
onLongPress={handleLongPress} |
||||
|
> |
||||
|
<Text className={'text-xs text-gray-500'}>全部订单</Text> |
||||
|
<ArrowRight color="#cccccc" size={12}/> |
||||
|
</View> |
||||
|
</View> |
||||
|
<View className={'flex justify-around pb-1 mt-4'}> |
||||
|
{/* 待付款 */} |
||||
|
{orderStats.pending > 0 ? ( |
||||
|
<Badge value={orderStats.pending} max={99} fill={'outline'}> |
||||
|
<View className={'item flex justify-center flex-col items-center'}> |
||||
|
<Wallet size={26} className={'font-normal text-gray-500'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=0', true)}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待付款</Text> |
||||
|
</View> |
||||
|
</Badge> |
||||
|
) : ( |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=0', true)}> |
||||
|
<Wallet size={26} className={'font-normal text-gray-500'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待付款</Text> |
||||
|
</View> |
||||
|
)} |
||||
|
|
||||
|
{/* 待发货 */} |
||||
|
{orderStats.paid > 0 ? ( |
||||
|
<Badge value={orderStats.paid} max={99} fill={'outline'}> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=1', true)}> |
||||
|
<Package size={26} className={'text-gray-500 font-normal'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待发货</Text> |
||||
|
</View> |
||||
|
</Badge> |
||||
|
) : ( |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=1', true)}> |
||||
|
<Package size={26} className={'text-gray-500 font-normal'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待发货</Text> |
||||
|
</View> |
||||
|
)} |
||||
|
|
||||
|
{/* 待收货 */} |
||||
|
{orderStats.shipped > 0 ? ( |
||||
|
<Badge value={orderStats.shipped} max={99} fill={'outline'}> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=3', true)}> |
||||
|
<Transit size={24} className={'text-gray-500 font-normal'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待收货</Text> |
||||
|
</View> |
||||
|
</Badge> |
||||
|
) : ( |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=3', true)}> |
||||
|
<Transit size={24} className={'text-gray-500 font-normal'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>待收货</Text> |
||||
|
</View> |
||||
|
)} |
||||
|
|
||||
|
{/* 已完成 - 不显示badge */} |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=5', true)}> |
||||
|
<Comment size={24} className={'text-gray-500 font-normal'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>已完成</Text> |
||||
|
</View> |
||||
|
|
||||
|
{/* 退货/售后 */} |
||||
|
{orderStats.refund > 0 ? ( |
||||
|
<Badge value={orderStats.refund} max={99} fill={'outline'}> |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=6', true)}> |
||||
|
<Refund size={26} className={'font-normal text-gray-500'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>退货/售后</Text> |
||||
|
</View> |
||||
|
</Badge> |
||||
|
) : ( |
||||
|
<View className={'item flex justify-center flex-col items-center'} |
||||
|
onClick={() => navTo('/user/order/order?statusFilter=6', true)}> |
||||
|
<Refund size={26} className={'font-normal text-gray-500'}/> |
||||
|
<Text className={'text-sm text-gray-600 py-1'}>退货/售后</Text> |
||||
|
</View> |
||||
|
)} |
||||
|
</View> |
||||
|
</View> |
||||
|
</View> |
||||
|
</> |
||||
|
|
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default UserOrder; |
@ -0,0 +1,5 @@ |
|||||
|
export default definePageConfig({ |
||||
|
navigationBarTitleText: '我的', |
||||
|
navigationStyle: 'custom', |
||||
|
navigationBarBackgroundColor: '#e9fff2' |
||||
|
}) |
@ -0,0 +1,9 @@ |
|||||
|
.header-bg{ |
||||
|
//background: url('https://oss.wsdns.cn/20250621/edb5d4da976b4d97ba185cb7077d2858.jpg') no-repeat top center; |
||||
|
background: linear-gradient(to bottom, #03605c, #18ae4f); |
||||
|
background-size: 100%; |
||||
|
} |
||||
|
|
||||
|
.my-bg{ |
||||
|
background: url('https://oss.wsdns.cn/20250913/5ae575a50dbb4ccaab086c3679c5e2c3.png') no-repeat top center; |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
import {useEffect, useRef} from 'react' |
||||
|
import {PullToRefresh} from '@nutui/nutui-react-taro' |
||||
|
import UserCard from "./components/UserCard"; |
||||
|
import UserOrder from "./components/UserOrder"; |
||||
|
import UserFooter from "./components/UserFooter"; |
||||
|
import {useUserData} from "@/hooks/useUserData"; |
||||
|
import './user.scss' |
||||
|
import IsDealer from "./components/IsDealer"; |
||||
|
import UserGrid from "@/pages/user/components/UserGrid"; |
||||
|
|
||||
|
function User() { |
||||
|
|
||||
|
const { refresh } = useUserData() |
||||
|
const userCardRef = useRef<any>() |
||||
|
|
||||
|
// 下拉刷新处理
|
||||
|
const handleRefresh = async () => { |
||||
|
await refresh() |
||||
|
// 如果 UserCard 组件有自己的刷新方法,也可以调用
|
||||
|
if (userCardRef.current?.handleRefresh) { |
||||
|
await userCardRef.current.handleRefresh() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
useEffect(() => { |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<PullToRefresh |
||||
|
onRefresh={handleRefresh} |
||||
|
headHeight={60} |
||||
|
> |
||||
|
<div className={'w-full'} style={{ |
||||
|
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)' |
||||
|
}}> |
||||
|
<UserCard ref={userCardRef}/> |
||||
|
<UserOrder/> |
||||
|
<IsDealer/> |
||||
|
<UserGrid/> |
||||
|
<UserFooter/> |
||||
|
</div> |
||||
|
</PullToRefresh> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default User |
Loading…
Reference in new issue