forked from gxwebsoft/mp-10550
feat(admin): 完成门店核销功能
- 新增管理员页面和相关组件 - 实现用户认证和权限控制 - 添加用户订单、积分等功能 - 优化用户卡片和用户页面布局 - 实现礼品卡核销功能
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
export const ENV_CONFIG = {
|
export const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
API_BASE_URL: 'http://127.0.0.1:9200/api',
|
API_BASE_URL: 'https://cms-api.websoft.top/api',
|
||||||
APP_NAME: '开发环境',
|
APP_NAME: '开发环境',
|
||||||
DEBUG: 'true',
|
DEBUG: 'true',
|
||||||
},
|
},
|
||||||
|
|||||||
150
src/admin/components/StoreCell.tsx
Normal file
150
src/admin/components/StoreCell.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
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, Reward, Tips, Ask, Setting, Scan} from '@nutui/icons-react-taro'
|
||||||
|
import {useUser} from '@/hooks/useUser'
|
||||||
|
|
||||||
|
const UserCell = () => {
|
||||||
|
const {logoutUser, isCertified, hasRole, isAdmin} = 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"
|
||||||
|
title={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Scan 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"
|
||||||
|
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={onLogout}
|
||||||
|
/>
|
||||||
|
</Cell.Group>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default UserCell
|
||||||
249
src/admin/components/UserCard.tsx
Normal file
249
src/admin/components/UserCard.tsx
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
import {Button} from '@nutui/nutui-react-taro'
|
||||||
|
import {Avatar, Tag} from '@nutui/nutui-react-taro'
|
||||||
|
import {getUserInfo, getWxOpenId} from '@/api/layout';
|
||||||
|
import Taro from '@tarojs/taro';
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {User} from "@/api/system/user/model";
|
||||||
|
import navTo from "@/utils/common";
|
||||||
|
import {TenantId} from "@/config/app";
|
||||||
|
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
|
||||||
|
import {useUser} from "@/hooks/useUser";
|
||||||
|
|
||||||
|
function UserCard() {
|
||||||
|
const {getDisplayName, getRoleName} = useUser();
|
||||||
|
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||||
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
|
const [couponCount, setCouponCount] = useState(0)
|
||||||
|
// const [pointsCount, setPointsCount] = useState(0)
|
||||||
|
const [giftCount, setGiftCount] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
||||||
|
Taro.getSetting({
|
||||||
|
success: (res) => {
|
||||||
|
if (res.authSetting['scope.userInfo']) {
|
||||||
|
// 用户已经授权过,可以直接获取用户信息
|
||||||
|
console.log('用户已经授权过,可以直接获取用户信息')
|
||||||
|
reload();
|
||||||
|
} else {
|
||||||
|
// 用户未授权,需要弹出授权窗口
|
||||||
|
console.log('用户未授权,需要弹出授权窗口')
|
||||||
|
showAuthModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadUserStats = (userId: number) => {
|
||||||
|
// 加载优惠券数量
|
||||||
|
getMyAvailableCoupons()
|
||||||
|
.then((coupons: any) => {
|
||||||
|
setCouponCount(coupons?.length || 0)
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
console.error('Coupon count error:', error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载积分数量
|
||||||
|
console.log(userId)
|
||||||
|
// getUserPointsStats(userId)
|
||||||
|
// .then((res: any) => {
|
||||||
|
// setPointsCount(res.currentPoints || 0)
|
||||||
|
// })
|
||||||
|
// .catch((error: any) => {
|
||||||
|
// console.error('Points stats error:', error)
|
||||||
|
// })
|
||||||
|
// 加载礼品劵数量
|
||||||
|
setGiftCount(0)
|
||||||
|
// pageUserGiftLog({userId, page: 1, limit: 1}).then(res => {
|
||||||
|
// setGiftCount(res.count || 0)
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 加载用户统计数据
|
||||||
|
if (data.userId) {
|
||||||
|
loadUserStats(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
|
||||||
|
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: 0,
|
||||||
|
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 (
|
||||||
|
<div className={'header-bg pt-20'}>
|
||||||
|
<div className={'p-4'}>
|
||||||
|
<div
|
||||||
|
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
|
||||||
|
// width: '720rpx',
|
||||||
|
// margin: '10px auto 0px auto',
|
||||||
|
height: '170px',
|
||||||
|
// borderRadius: '22px 22px 0 0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={'user-card-header flex w-full justify-between items-center pt-4'}>
|
||||||
|
<div className={'flex items-center mx-4'}>
|
||||||
|
{
|
||||||
|
IsLogin ? (
|
||||||
|
<Avatar size="large" src={userInfo?.avatar} shape="round"/>
|
||||||
|
) : (
|
||||||
|
<Button className={'text-black'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||||
|
<Avatar size="large" src={userInfo?.avatar} shape="round"/>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div className={'user-info flex flex-col px-2'}>
|
||||||
|
<div className={'py-1 text-black font-bold'}>{getDisplayName()}</div>
|
||||||
|
{IsLogin ? (
|
||||||
|
<div className={'grade text-xs py-1'}>
|
||||||
|
<Tag type="success" round>
|
||||||
|
<div className={'p-1'}>
|
||||||
|
{getRoleName()}
|
||||||
|
</div>
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
) : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'mx-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
|
||||||
|
onClick={() => navTo('/user/profile/profile', true)}>
|
||||||
|
{'个人资料'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'flex justify-around mt-1'}>
|
||||||
|
<div className={'item flex justify-center flex-col items-center'}
|
||||||
|
onClick={() => navTo('/user/wallet/wallet', true)}>
|
||||||
|
<span className={'text-sm text-gray-500'}>余额</span>
|
||||||
|
<span className={'text-xl'}>¥ {userInfo?.balance || '0.00'}</span>
|
||||||
|
</div>
|
||||||
|
<div className={'item flex justify-center flex-col items-center'}
|
||||||
|
onClick={() => navTo('/user/coupon/index', true)}>
|
||||||
|
<span className={'text-sm text-gray-500'}>优惠券</span>
|
||||||
|
<span className={'text-xl'}>{couponCount}</span>
|
||||||
|
</div>
|
||||||
|
<div className={'item flex justify-center flex-col items-center'}
|
||||||
|
onClick={() => navTo('/user/gift/index', true)}>
|
||||||
|
<span className={'text-sm text-gray-500'}>礼品卡</span>
|
||||||
|
<span className={'text-xl'}>{giftCount}</span>
|
||||||
|
</div>
|
||||||
|
{/*<div className={'item flex justify-center flex-col items-center'}>*/}
|
||||||
|
{/* <span className={'text-sm text-gray-500'}>积分</span>*/}
|
||||||
|
{/* <span className={'text-xl'}>{pointsCount}</span>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserCard;
|
||||||
186
src/admin/components/UserCell.tsx
Normal file
186
src/admin/components/UserCell.tsx
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
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, Reward, Tips, Ask, Setting, Scan} from '@nutui/icons-react-taro'
|
||||||
|
import {useUser} from '@/hooks/useUser'
|
||||||
|
|
||||||
|
const UserCell = () => {
|
||||||
|
const {logoutUser, isCertified, hasRole, isAdmin} = 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'}>
|
||||||
|
|
||||||
|
{/*是否分销商*/}
|
||||||
|
{!hasRole('dealer') && !isAdmin() && (
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/dealer/index', true)}>
|
||||||
|
<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}/>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/*是否管理员*/}
|
||||||
|
{isAdmin() && (
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'linear-gradient(to right bottom, #ff8e0c, #ed680d)',
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/admin/article/index', true)}>
|
||||||
|
<Setting className={'text-orange-100 '} size={16}/>
|
||||||
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>管理中心</Text>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Cell.Group divider={true} description={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Text style={{marginTop: '12px'}}>我的服务</Text>
|
||||||
|
</View>
|
||||||
|
}>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
|
<Scan 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"
|
||||||
|
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={onLogout}
|
||||||
|
/>
|
||||||
|
</Cell.Group>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default UserCell
|
||||||
102
src/admin/components/UserFooter.tsx
Normal file
102
src/admin/components/UserFooter.tsx
Normal file
@@ -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
|
||||||
69
src/admin/components/UserOrder.tsx
Normal file
69
src/admin/components/UserOrder.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import {useEffect} from "react";
|
||||||
|
import navTo from "@/utils/common";
|
||||||
|
import {View, Text} from '@tarojs/components';
|
||||||
|
import {ArrowRight, Wallet, Comment, Transit, Refund, Package} from '@nutui/icons-react-taro';
|
||||||
|
|
||||||
|
function UserOrder() {
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload()
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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',
|
||||||
|
// 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)}>
|
||||||
|
<Text className={'text-xs text-gray-500'}>全部订单</Text>
|
||||||
|
<ArrowRight color="#cccccc" size={12}/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className={'flex justify-around pb-1'}>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<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;
|
||||||
3
src/admin/index.config.ts
Normal file
3
src/admin/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '管理中心'
|
||||||
|
})
|
||||||
25
src/admin/index.tsx
Normal file
25
src/admin/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import {useEffect} from 'react'
|
||||||
|
import {useUser} from "@/hooks/useUser";
|
||||||
|
import {Text} from '@tarojs/components';
|
||||||
|
|
||||||
|
function Admin() {
|
||||||
|
const {
|
||||||
|
isAdmin
|
||||||
|
} = useUser();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!isAdmin()) {
|
||||||
|
return (
|
||||||
|
<Text>您不是管理员</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text>待开发...</Text>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Admin
|
||||||
@@ -68,6 +68,7 @@ export default defineAppConfig({
|
|||||||
{
|
{
|
||||||
"root": "admin",
|
"root": "admin",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
"index",
|
||||||
"article/index",
|
"article/index",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -90,12 +91,12 @@ export default defineAppConfig({
|
|||||||
selectedIconPath: "assets/tabbar/home-active.png",
|
selectedIconPath: "assets/tabbar/home-active.png",
|
||||||
text: "首页",
|
text: "首页",
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
pagePath: "pages/find/find",
|
// pagePath: "pages/find/find",
|
||||||
iconPath: "assets/tabbar/find.png",
|
// iconPath: "assets/tabbar/find.png",
|
||||||
selectedIconPath: "assets/tabbar/find-active.png",
|
// selectedIconPath: "assets/tabbar/find-active.png",
|
||||||
text: "发现",
|
// text: "发现",
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
pagePath: "pages/cart/cart",
|
pagePath: "pages/cart/cart",
|
||||||
iconPath: "assets/tabbar/cart.png",
|
iconPath: "assets/tabbar/cart.png",
|
||||||
|
|||||||
@@ -24,6 +24,20 @@ const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
|||||||
faceValue
|
faceValue
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
|
// const copyToClipboard = () => {
|
||||||
|
// if (qrContent) {
|
||||||
|
// Taro.setClipboardData({
|
||||||
|
// data: qrContent,
|
||||||
|
// success: () => {
|
||||||
|
// Taro.showToast({
|
||||||
|
// title: '兑换码已复制',
|
||||||
|
// icon: 'success'
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup
|
<Popup
|
||||||
@@ -38,9 +52,9 @@ const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
|||||||
>
|
>
|
||||||
<View className="p-6">
|
<View className="p-6">
|
||||||
{/* 标题 */}
|
{/* 标题 */}
|
||||||
<View className="text-center mb-4">
|
<View className="mb-4">
|
||||||
<Text className="text-lg font-bold">礼品卡二维码</Text>
|
<Text className="text-lg font-bold">礼品卡二维码</Text>
|
||||||
<Text className="text-sm text-gray-500 mt-1">
|
<Text className="text-sm text-gray-400 mt-1 px-2">
|
||||||
请向商家出示此二维码
|
请向商家出示此二维码
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -64,13 +78,22 @@ const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
|||||||
{/* 二维码区域 */}
|
{/* 二维码区域 */}
|
||||||
<View className="text-center mb-4">
|
<View className="text-center mb-4">
|
||||||
<View className="p-4 bg-white border border-gray-200 rounded-lg">
|
<View className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
<View className="bg-gray-100 rounded flex items-center justify-center mx-auto"
|
{qrContent ? (
|
||||||
style={{width: '200px', height: '200px'}}>
|
<img
|
||||||
<View className="text-center">
|
src={`https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrContent)}`}
|
||||||
<Text className="text-gray-500 text-sm">二维码</Text>
|
alt="二维码"
|
||||||
<Text className="text-xs text-gray-400 mt-1">ID: {qrContent ? qrContent.slice(-6) : '------'}</Text>
|
style={{width: '200px', height: '200px'}}
|
||||||
|
className="mx-auto"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View className="bg-gray-100 rounded flex items-center justify-center mx-auto"
|
||||||
|
style={{width: '200px', height: '200px'}}>
|
||||||
|
<View className="text-center">
|
||||||
|
<QrCode size="48" className="text-gray-400 mb-2"/>
|
||||||
|
<Text className="text-gray-500 text-sm">生成中...</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {Button} from '@nutui/nutui-react-taro'
|
import {Button} from '@nutui/nutui-react-taro'
|
||||||
import {Avatar, Tag} from '@nutui/nutui-react-taro'
|
import {Avatar, Tag} from '@nutui/nutui-react-taro'
|
||||||
|
import {Scan} from '@nutui/icons-react-taro';
|
||||||
import {getUserInfo, getWxOpenId} from '@/api/layout';
|
import {getUserInfo, getWxOpenId} from '@/api/layout';
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
@@ -10,6 +11,9 @@ import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
|
|||||||
import {useUser} from "@/hooks/useUser";
|
import {useUser} from "@/hooks/useUser";
|
||||||
|
|
||||||
function UserCard() {
|
function UserCard() {
|
||||||
|
const {
|
||||||
|
isAdmin
|
||||||
|
} = useUser();
|
||||||
const {getDisplayName, getRoleName} = useUser();
|
const {getDisplayName, getRoleName} = useUser();
|
||||||
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||||
const [userInfo, setUserInfo] = useState<User>()
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
@@ -46,6 +50,7 @@ function UserCard() {
|
|||||||
|
|
||||||
// 加载积分数量
|
// 加载积分数量
|
||||||
console.log(userId)
|
console.log(userId)
|
||||||
|
setPointsCount(0)
|
||||||
// getUserPointsStats(userId)
|
// getUserPointsStats(userId)
|
||||||
// .then((res: any) => {
|
// .then((res: any) => {
|
||||||
// setPointsCount(res.currentPoints || 0)
|
// setPointsCount(res.currentPoints || 0)
|
||||||
@@ -61,7 +66,6 @@ function UserCard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reload = () => {
|
const reload = () => {
|
||||||
setPointsCount(0)
|
|
||||||
Taro.getUserInfo({
|
Taro.getUserInfo({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const avatar = res.userInfo.avatarUrl;
|
const avatar = res.userInfo.avatarUrl;
|
||||||
@@ -214,7 +218,8 @@ function UserCard() {
|
|||||||
) : ''}
|
) : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'mx-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
|
{isAdmin() && <Scan onClick={() => navTo('/user/store/verification', true)} />}
|
||||||
|
<div className={'mr-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
|
||||||
onClick={() => navTo('/user/profile/profile', true)}>
|
onClick={() => navTo('/user/profile/profile', true)}>
|
||||||
{'个人资料'}
|
{'个人资料'}
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +228,11 @@ function UserCard() {
|
|||||||
<div className={'item flex justify-center flex-col items-center'}
|
<div className={'item flex justify-center flex-col items-center'}
|
||||||
onClick={() => navTo('/user/wallet/wallet', true)}>
|
onClick={() => navTo('/user/wallet/wallet', true)}>
|
||||||
<span className={'text-sm text-gray-500'}>余额</span>
|
<span className={'text-sm text-gray-500'}>余额</span>
|
||||||
<span className={'text-xl'}>¥ {userInfo?.balance || '0.00'}</span>
|
<span className={'text-xl'}>{userInfo?.balance || '0.00'}</span>
|
||||||
|
</div>
|
||||||
|
<div className={'item flex justify-center flex-col items-center'}>
|
||||||
|
<span className={'text-sm text-gray-500'}>积分</span>
|
||||||
|
<span className={'text-xl'}>{pointsCount}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={'item flex justify-center flex-col items-center'}
|
<div className={'item flex justify-center flex-col items-center'}
|
||||||
onClick={() => navTo('/user/coupon/index', true)}>
|
onClick={() => navTo('/user/coupon/index', true)}>
|
||||||
@@ -235,10 +244,6 @@ function UserCard() {
|
|||||||
<span className={'text-sm text-gray-500'}>礼品卡</span>
|
<span className={'text-sm text-gray-500'}>礼品卡</span>
|
||||||
<span className={'text-xl'}>{giftCount}</span>
|
<span className={'text-xl'}>{giftCount}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={'item flex justify-center flex-col items-center'}>
|
|
||||||
<span className={'text-sm text-gray-500'}>积分</span>
|
|
||||||
<span className={'text-xl'}>{pointsCount}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,20 +2,42 @@ import {useEffect} from 'react'
|
|||||||
import UserCard from "./components/UserCard";
|
import UserCard from "./components/UserCard";
|
||||||
import UserOrder from "./components/UserOrder";
|
import UserOrder from "./components/UserOrder";
|
||||||
import UserCell from "./components/UserCell";
|
import UserCell from "./components/UserCell";
|
||||||
import './user.scss'
|
|
||||||
import UserFooter from "./components/UserFooter";
|
import UserFooter from "./components/UserFooter";
|
||||||
|
import {useUser} from "@/hooks/useUser";
|
||||||
|
import './user.scss'
|
||||||
|
|
||||||
function User() {
|
function User() {
|
||||||
|
const {
|
||||||
|
isAdmin
|
||||||
|
} = useUser();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门店核销管理
|
||||||
|
*/
|
||||||
|
if (isAdmin()) {
|
||||||
|
return <>
|
||||||
|
<div className={'w-full'} style={{
|
||||||
|
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
|
||||||
|
}}>
|
||||||
|
<UserCard/>
|
||||||
|
<UserOrder/>
|
||||||
|
<UserCell/>
|
||||||
|
<UserFooter/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={'w-full'} style={{
|
<div className={'w-full'} style={{
|
||||||
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
|
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
|
||||||
}}>
|
}}>
|
||||||
<UserCard />
|
<UserCard/>
|
||||||
<UserOrder />
|
<UserOrder/>
|
||||||
<UserCell />
|
<UserCell/>
|
||||||
<UserFooter/>
|
<UserFooter/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ import Taro from '@tarojs/taro'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import {getShopGiftByCode, updateShopGift} from "@/api/shop/shopGift";
|
import {getShopGiftByCode, updateShopGift} from "@/api/shop/shopGift";
|
||||||
|
|
||||||
interface VerificationData {
|
// interface VerificationData {
|
||||||
type: string
|
// type: string
|
||||||
giftId: number
|
// giftId: number
|
||||||
giftCode: string
|
// giftCode: string
|
||||||
verificationCode: string
|
// verificationCode: string
|
||||||
faceValue: string
|
// faceValue: string
|
||||||
timestamp: number
|
// timestamp: number
|
||||||
expireTime?: string
|
// expireTime?: string
|
||||||
}
|
// }
|
||||||
|
|
||||||
interface GiftCardInfo {
|
interface GiftCardInfo {
|
||||||
id: number
|
id: number
|
||||||
@@ -40,7 +40,8 @@ const StoreVerification: React.FC = () => {
|
|||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('扫码结果:', res.result)
|
console.log('扫码结果:', res.result)
|
||||||
setScanResult(res.result)
|
setScanResult(res.result)
|
||||||
parseQRCode(res.result)
|
verificationQRCode(res.result)
|
||||||
|
// parseQRCode(res.result)
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('扫码失败:', err)
|
console.error('扫码失败:', err)
|
||||||
@@ -53,41 +54,57 @@ const StoreVerification: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析二维码数据
|
// 解析二维码数据
|
||||||
const parseQRCode = (qrData: string) => {
|
// const parseQRCode = (qrData: string) => {
|
||||||
try {
|
// try {
|
||||||
const data: VerificationData = JSON.parse(qrData)
|
// const data: VerificationData = JSON.parse(qrData)
|
||||||
|
//
|
||||||
|
// if (data.type === 'gift_card_verification') {
|
||||||
|
// setVerificationCode(data.verificationCode)
|
||||||
|
// console.log(data.verificationCode,'...vaerrr')
|
||||||
|
// } else {
|
||||||
|
// throw new Error('无效的二维码格式')
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('解析二维码失败:', error)
|
||||||
|
// Taro.showToast({
|
||||||
|
// title: '无效的二维码',
|
||||||
|
// icon: 'error'
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (data.type === 'gift_card_verification') {
|
// 扫码核销操作
|
||||||
setVerificationCode(data.verificationCode)
|
const verificationQRCode = async (code:string) => {
|
||||||
// 模拟获取礼品卡信息
|
const gift = await getShopGiftByCode(code)
|
||||||
mockGetGiftInfo(data)
|
|
||||||
} else {
|
if(gift.status == 1){
|
||||||
throw new Error('无效的二维码格式')
|
return Taro.showToast({
|
||||||
}
|
title: '此礼品码已使用',
|
||||||
} catch (error) {
|
|
||||||
console.error('解析二维码失败:', error)
|
|
||||||
Taro.showToast({
|
|
||||||
title: '无效的二维码',
|
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
if(gift.status == 2){
|
||||||
|
return Taro.showToast({
|
||||||
// 模拟获取礼品卡信息(实际应该调用API)
|
title: '此礼品码已失效',
|
||||||
const mockGetGiftInfo = (data: VerificationData) => {
|
icon: 'error'
|
||||||
// 这里应该调用后端API验证礼品卡信息
|
})
|
||||||
const mockGiftInfo: GiftCardInfo = {
|
|
||||||
id: data.giftId,
|
|
||||||
name: '礼品卡',
|
|
||||||
goodsName: '星巴克咖啡券',
|
|
||||||
faceValue: data.faceValue,
|
|
||||||
type: 20,
|
|
||||||
status: 0,
|
|
||||||
expireTime: data.expireTime,
|
|
||||||
code: data.giftCode
|
|
||||||
}
|
}
|
||||||
|
if(gift.userId == 0){
|
||||||
setGiftInfo(mockGiftInfo)
|
return Taro.showToast({
|
||||||
|
title: '此礼品码未认领',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
updateShopGift({
|
||||||
|
...gift,
|
||||||
|
status: 1,
|
||||||
|
takeTime: dayjs.unix(Date.now() / 1000).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
}).then(() => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '核销成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手动输入核销码验证
|
// 手动输入核销码验证
|
||||||
@@ -106,7 +123,8 @@ const StoreVerification: React.FC = () => {
|
|||||||
const giftCard = await getShopGiftByCode(verificationCode.trim())
|
const giftCard = await getShopGiftByCode(verificationCode.trim())
|
||||||
await updateShopGift({
|
await updateShopGift({
|
||||||
...giftCard,
|
...giftCard,
|
||||||
status: 1
|
status: 1,
|
||||||
|
takeTime: dayjs.unix(Date.now() / 1000).format('YYYY-MM-DD HH:mm:ss')
|
||||||
})
|
})
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '核销成功',
|
title: '核销成功',
|
||||||
|
|||||||
Reference in New Issue
Block a user