feat(pages): 添加管理页面功能和配置
- 创建 .editorconfig 文件统一代码风格配置 - 配置 .eslintrc 使用 taro/react 规则集 - 完善 .gitignore 忽略编译产物和敏感文件 - 添加 admin/article/add 页面实现文章管理功能 - 添加 dealer/apply/add 页面实现经销商申请功能 - 添加 dealer/bank/add 页面实现银行卡管理功能 - 添加 dealer/customer/add 页面实现客户管理功能 - 添加 user/address/add 页面实现用户地址管理功能 - 添加 user/chat/message/add 页面实现消息功能 - 添加 user/gift/add 页面实现礼品管理功能 - 配置各页面导航栏标题和样式 - 实现表单验证和数据提交功能 - 集成图片上传和头像选择功能 - 添加日期选择和数据校验逻辑 - 实现编辑和新增模式切换 - 集成用户权限和角色管理功能
This commit is contained in:
4
src/pages/cart/cart.config.ts
Normal file
4
src/pages/cart/cart.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '购物车',
|
||||
navigationStyle: 'custom'
|
||||
})
|
||||
31
src/pages/cart/cart.scss
Normal file
31
src/pages/cart/cart.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
// 购物车页面样式
|
||||
.cart-page {
|
||||
// 当购物车为空时,设置透明背景
|
||||
&.empty {
|
||||
page {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.cart-empty-container {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 空购物车容器样式
|
||||
.cart-empty-container {
|
||||
background-color: transparent !important;
|
||||
|
||||
// 确保 Empty 组件及其子元素也是透明的
|
||||
.nut-empty {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.nut-empty__image {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.nut-empty__description {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
356
src/pages/cart/cart.tsx
Normal file
356
src/pages/cart/cart.tsx
Normal file
@@ -0,0 +1,356 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro, {useShareAppMessage, useShareTimeline, useDidShow} from '@tarojs/taro';
|
||||
import {
|
||||
NavBar,
|
||||
Checkbox,
|
||||
Image,
|
||||
InputNumber,
|
||||
Button,
|
||||
Empty,
|
||||
Divider,
|
||||
ConfigProvider
|
||||
} from '@nutui/nutui-react-taro';
|
||||
import {ArrowLeft, Del} from '@nutui/icons-react-taro';
|
||||
import {View} from '@tarojs/components';
|
||||
import {CartItem, useCart} from "@/hooks/useCart";
|
||||
import './cart.scss';
|
||||
|
||||
function Cart() {
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>(0);
|
||||
const [selectedItems, setSelectedItems] = useState<number[]>([]);
|
||||
const [isAllSelected, setIsAllSelected] = useState(false);
|
||||
|
||||
const {
|
||||
cartItems,
|
||||
cartCount,
|
||||
updateQuantity,
|
||||
removeFromCart,
|
||||
clearCart,
|
||||
loadCartFromStorage
|
||||
} = useCart();
|
||||
|
||||
// InputNumber 主题配置
|
||||
const customTheme = {
|
||||
nutuiInputnumberButtonWidth: '28px',
|
||||
nutuiInputnumberButtonHeight: '28px',
|
||||
nutuiInputnumberInputWidth: '40px',
|
||||
nutuiInputnumberInputHeight: '28px',
|
||||
nutuiInputnumberInputBorderRadius: '4px',
|
||||
nutuiInputnumberButtonBorderRadius: '4px',
|
||||
}
|
||||
|
||||
useShareTimeline(() => {
|
||||
return {
|
||||
title: '购物车 - 易赊宝'
|
||||
};
|
||||
});
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: '购物车 - 易赊宝',
|
||||
success: function () {
|
||||
console.log('分享成功');
|
||||
},
|
||||
fail: function () {
|
||||
console.log('分享失败');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 页面显示时刷新购物车数据
|
||||
useDidShow(() => {
|
||||
loadCartFromStorage();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
Taro.getSystemInfo({
|
||||
success: (res) => {
|
||||
setStatusBarHeight(res.statusBarHeight || 0);
|
||||
},
|
||||
});
|
||||
|
||||
// 设置导航栏背景色
|
||||
Taro.setNavigationBarColor({
|
||||
backgroundColor: '#ffffff',
|
||||
frontColor: 'black',
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 根据购物车状态动态设置页面背景色
|
||||
useEffect(() => {
|
||||
if (cartItems.length === 0) {
|
||||
// 购物车为空时设置透明背景
|
||||
Taro.setBackgroundColor({
|
||||
backgroundColor: 'transparent'
|
||||
});
|
||||
} else {
|
||||
// 有商品时恢复默认背景
|
||||
Taro.setBackgroundColor({
|
||||
backgroundColor: '#f5f5f5'
|
||||
});
|
||||
}
|
||||
}, [cartItems.length]);
|
||||
|
||||
// 处理单个商品选择
|
||||
const handleItemSelect = (goodsId: number, checked: boolean) => {
|
||||
if (checked) {
|
||||
setSelectedItems([...selectedItems, goodsId]);
|
||||
} else {
|
||||
setSelectedItems(selectedItems.filter(id => id !== goodsId));
|
||||
setIsAllSelected(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理全选
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
setIsAllSelected(checked);
|
||||
if (checked) {
|
||||
setSelectedItems(cartItems.map((item: CartItem) => item.goodsId));
|
||||
} else {
|
||||
setSelectedItems([]);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新商品数量
|
||||
const handleQuantityChange = (goodsId: number, value: number) => {
|
||||
updateQuantity(goodsId, value);
|
||||
};
|
||||
|
||||
// 删除商品
|
||||
const handleRemoveItem = (goodsId: number) => {
|
||||
Taro.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除这个商品吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
removeFromCart(goodsId);
|
||||
setSelectedItems(selectedItems.filter(id => id !== goodsId));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 计算选中商品的总价
|
||||
const getSelectedTotalPrice = () => {
|
||||
return cartItems
|
||||
.filter((item: CartItem) => selectedItems.includes(item.goodsId))
|
||||
.reduce((total: number, item: CartItem) => total + (parseFloat(item.price) * item.quantity), 0)
|
||||
.toFixed(2);
|
||||
};
|
||||
|
||||
// 去结算
|
||||
const handleCheckout = () => {
|
||||
if (selectedItems.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '请选择要结算的商品',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取选中的商品
|
||||
const selectedCartItems = cartItems.filter((item: CartItem) =>
|
||||
selectedItems.includes(item.goodsId)
|
||||
);
|
||||
|
||||
// 将选中的商品信息存储到本地,供结算页面使用
|
||||
Taro.setStorageSync('checkout_items', JSON.stringify(selectedCartItems));
|
||||
|
||||
// 跳转到购物车结算页面
|
||||
Taro.navigateTo({
|
||||
url: '/shop/orderConfirmCart/index'
|
||||
});
|
||||
};
|
||||
|
||||
// 检查是否全选
|
||||
useEffect(() => {
|
||||
if (cartItems.length > 0 && selectedItems.length === cartItems.length) {
|
||||
setIsAllSelected(true);
|
||||
} else {
|
||||
setIsAllSelected(false);
|
||||
}
|
||||
}, [selectedItems, cartItems]);
|
||||
|
||||
if (cartItems.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<NavBar
|
||||
fixed={true}
|
||||
style={{marginTop: `${statusBarHeight}px`}}
|
||||
left={<ArrowLeft onClick={() => Taro.navigateBack()}/>}
|
||||
right={
|
||||
cartItems.length > 0 && (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
fill="none"
|
||||
onClick={() => {
|
||||
Taro.showModal({
|
||||
title: '确认清空',
|
||||
content: '确定要清空购物车吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
clearCart();
|
||||
setSelectedItems([]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="text-lg">购物车({cartCount})</span>
|
||||
</NavBar>
|
||||
|
||||
{/* 垂直居中的空状态容器 */}
|
||||
<View
|
||||
className="flex items-center justify-center"
|
||||
style={{
|
||||
height: `calc(100vh - ${statusBarHeight + 150}px)`,
|
||||
paddingTop: `${statusBarHeight + 50}px`,
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
<Empty
|
||||
description="购物车空空如也"
|
||||
actions={[{ text: '去逛逛' }]}
|
||||
style={{
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
onClick={() => Taro.switchTab({ url: '/pages/index/index' })}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={{backgroundColor: '#f6f6f6', height: `${statusBarHeight}px`}}
|
||||
className="fixed z-10 top-0 w-full"></View>
|
||||
<NavBar
|
||||
fixed={true}
|
||||
style={{marginTop: `${statusBarHeight}px`}}
|
||||
left={<ArrowLeft onClick={() => Taro.navigateBack()}/>}
|
||||
right={
|
||||
cartItems.length > 0 && (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
fill="none"
|
||||
onClick={() => {
|
||||
Taro.showModal({
|
||||
title: '确认清空',
|
||||
content: '确定要清空购物车吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
clearCart();
|
||||
setSelectedItems([]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="text-lg">购物车({cartCount})</span>
|
||||
</NavBar>
|
||||
|
||||
{/* 购物车内容 */}
|
||||
<View className="pt-24">
|
||||
{/* 商品列表 */}
|
||||
<View className="bg-white">
|
||||
{cartItems.map((item: CartItem, index: number) => (
|
||||
<View key={item.goodsId}>
|
||||
<View className="bg-white px-4 py-3 flex items-center gap-3">
|
||||
{/* 选择框 */}
|
||||
<Checkbox
|
||||
checked={selectedItems.includes(item.goodsId)}
|
||||
onChange={(checked) => handleItemSelect(item.goodsId, checked)}
|
||||
/>
|
||||
|
||||
{/* 商品图片 */}
|
||||
<Image
|
||||
src={item.image}
|
||||
width="80"
|
||||
height="80"
|
||||
lazyLoad={false}
|
||||
radius="8"
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
|
||||
{/* 商品信息 */}
|
||||
<View className="flex-1 min-w-0">
|
||||
<View className="text-lg font-bold text-gray-900 truncate mb-1">
|
||||
{item.name}
|
||||
</View>
|
||||
<View className="flex items-center justify-between">
|
||||
<View className={'flex text-red-500 text-xl items-baseline'}>
|
||||
<span className={'text-xs'}>¥</span>
|
||||
<span className={'font-bold text-lg'}>{item.price}</span>
|
||||
</View>
|
||||
<ConfigProvider theme={customTheme}>
|
||||
<InputNumber
|
||||
value={item.quantity}
|
||||
min={1}
|
||||
onChange={(value) => handleQuantityChange(item.goodsId, Number(value))}
|
||||
/>
|
||||
</ConfigProvider>
|
||||
<Del className={'text-red-500'} size={14} onClick={() => handleRemoveItem(item.goodsId)}/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{index < cartItems.length - 1 && <Divider/>}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* 底部结算栏 */}
|
||||
<View
|
||||
className="fixed z-50 bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3 safe-area-bottom">
|
||||
<View className="flex items-center justify-between">
|
||||
<View className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
checked={isAllSelected}
|
||||
onChange={handleSelectAll}
|
||||
>
|
||||
全选
|
||||
</Checkbox>
|
||||
<View className="text-sm text-gray-600">
|
||||
已选 {selectedItems.length} 件
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="flex items-center gap-4">
|
||||
<View className="text-right">
|
||||
<View className="text-xs text-gray-500">合计:</View>
|
||||
<div className={'flex text-red-500 text-xl items-baseline'}>
|
||||
<span className={'text-xs'}>¥</span>
|
||||
<span className={'font-bold text-lg'}>{getSelectedTotalPrice()}</span>
|
||||
</div>
|
||||
</View>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
disabled={selectedItems.length === 0}
|
||||
onClick={handleCheckout}
|
||||
className="px-6"
|
||||
>
|
||||
结算({selectedItems.length})
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 底部安全区域占位 */}
|
||||
<View className="h-20"></View>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Cart;
|
||||
5
src/pages/find/find.config.ts
Normal file
5
src/pages/find/find.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '网点',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#ffffff'
|
||||
})
|
||||
144
src/pages/find/find.scss
Normal file
144
src/pages/find/find.scss
Normal file
@@ -0,0 +1,144 @@
|
||||
page {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.sitePage {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #fde8ea 0%, #f7f7f7 320rpx, #f7f7f7 100%);
|
||||
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.searchArea {
|
||||
padding: 22rpx 24rpx 18rpx;
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
height: 86rpx;
|
||||
background: #fff;
|
||||
border: 2rpx solid #b51616;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
flex: 1;
|
||||
height: 86rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 30rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.searchPlaceholder {
|
||||
color: #9e9e9e;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.searchIconWrap {
|
||||
width: 88rpx;
|
||||
height: 86rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.siteList {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.siteCard {
|
||||
background: #fff;
|
||||
border-radius: 18rpx;
|
||||
padding: 22rpx 22rpx 18rpx;
|
||||
margin-top: 18rpx;
|
||||
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.siteCardInner {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.siteInfo {
|
||||
flex: 1;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.siteRow {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
.siteRowTop {
|
||||
padding-top: 2rpx;
|
||||
padding-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.siteLabel {
|
||||
width: 170rpx;
|
||||
flex: 0 0 170rpx;
|
||||
color: #9a9a9a;
|
||||
font-size: 30rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.siteValue {
|
||||
flex: 1;
|
||||
color: #222;
|
||||
font-size: 30rpx;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.siteValueStrong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.siteDivider {
|
||||
height: 2rpx;
|
||||
background: #ededed;
|
||||
}
|
||||
|
||||
.siteSide {
|
||||
width: 160rpx;
|
||||
flex: 0 0 160rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding-left: 12rpx;
|
||||
}
|
||||
|
||||
.navArrow {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
border-top: 8rpx solid #e60012;
|
||||
border-right: 8rpx solid #e60012;
|
||||
border-radius: 4rpx;
|
||||
transform: rotate(45deg);
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.distanceText {
|
||||
margin-top: 18rpx;
|
||||
font-size: 28rpx;
|
||||
color: #e60012;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.emptyWrap {
|
||||
padding: 40rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.emptyText {
|
||||
font-size: 28rpx;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
.bottomSafe {
|
||||
height: 20rpx;
|
||||
}
|
||||
130
src/pages/find/find.tsx
Normal file
130
src/pages/find/find.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import {useMemo, useState} from 'react'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {Input, Text, View} from '@tarojs/components'
|
||||
import {Search} from '@nutui/icons-react-taro'
|
||||
import './find.scss'
|
||||
|
||||
type SiteItem = {
|
||||
id: string
|
||||
cityName: string
|
||||
address: string
|
||||
phone: string
|
||||
contact: string
|
||||
distanceMeter: number
|
||||
}
|
||||
|
||||
const MOCK_SITES: SiteItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
cityName: '北京朝阳区网点',
|
||||
address: '地安门西大街(南门)',
|
||||
phone: '15878179339',
|
||||
contact: '刘先生',
|
||||
distanceMeter: 100
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
cityName: '兰州某某区网点',
|
||||
address: '地安门西大街(南门)',
|
||||
phone: '15878179339',
|
||||
contact: '黄先生',
|
||||
distanceMeter: 150
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
cityName: '合肥市某某区网点',
|
||||
address: '地安门西大街(南门)',
|
||||
phone: '15878179339',
|
||||
contact: '黄先生',
|
||||
distanceMeter: 250
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
cityName: '南宁市某某区网点',
|
||||
address: '广西壮族自治区南宁市良庆区五象新区五象大道403号富雅国际金融中心G1栋高层6006',
|
||||
phone: '15878179339',
|
||||
contact: '柳先生',
|
||||
distanceMeter: 1250
|
||||
}
|
||||
]
|
||||
|
||||
const Find = () => {
|
||||
const [keyword, setKeyword] = useState<string>('')
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const key = keyword.trim()
|
||||
if (!key) return MOCK_SITES
|
||||
return MOCK_SITES.filter((it) => it.cityName.includes(key))
|
||||
}, [keyword])
|
||||
|
||||
const onNavigate = (item: SiteItem) => {
|
||||
Taro.showToast({title: `导航至:${item.cityName}(示例)`, icon: 'none'})
|
||||
}
|
||||
|
||||
const onSearch = () => {
|
||||
Taro.showToast({title: '查询(示例)', icon: 'none'})
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='sitePage'>
|
||||
<View className='searchArea'>
|
||||
<View className='searchBox'>
|
||||
<Input
|
||||
className='searchInput'
|
||||
value={keyword}
|
||||
placeholder='请输入城市名称查询'
|
||||
placeholderClass='searchPlaceholder'
|
||||
confirmType='search'
|
||||
onInput={(e) => setKeyword(e.detail.value)}
|
||||
onConfirm={onSearch}
|
||||
/>
|
||||
<View className='searchIconWrap' onClick={onSearch}>
|
||||
<Search size={18} color='#b51616' />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='siteList'>
|
||||
{filtered.map((item) => (
|
||||
<View key={item.id} className='siteCard'>
|
||||
<View className='siteCardInner'>
|
||||
<View className='siteInfo'>
|
||||
<View className='siteRow siteRowTop'>
|
||||
<Text className='siteLabel'>城市名称:</Text>
|
||||
<Text className='siteValue siteValueStrong'>{item.cityName}</Text>
|
||||
</View>
|
||||
<View className='siteDivider' />
|
||||
<View className='siteRow'>
|
||||
<Text className='siteLabel'>网点地址:</Text>
|
||||
<Text className='siteValue'>{item.address}</Text>
|
||||
</View>
|
||||
<View className='siteRow'>
|
||||
<Text className='siteLabel'>联系电话:</Text>
|
||||
<Text className='siteValue'>{item.phone}</Text>
|
||||
</View>
|
||||
<View className='siteRow'>
|
||||
<Text className='siteLabel'>联系人:</Text>
|
||||
<Text className='siteValue'>{item.contact}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='siteSide' onClick={() => onNavigate(item)}>
|
||||
<View className='navArrow' />
|
||||
<Text className='distanceText'>距离{item.distanceMeter}米</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{filtered.length === 0 && (
|
||||
<View className='emptyWrap'>
|
||||
<Text className='emptyText'>暂无网点</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View className='bottomSafe' />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
export default Find
|
||||
31
src/pages/index/Banner.tsx
Normal file
31
src/pages/index/Banner.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Swiper } from '@nutui/nutui-react-taro'
|
||||
import {CmsAd} from "@/api/cms/cmsAd/model";
|
||||
import {Image} from '@nutui/nutui-react-taro'
|
||||
import {getCmsAdByCode} from "@/api/cms/cmsAd";
|
||||
|
||||
const MyPage = () => {
|
||||
const [item, setItem] = useState<CmsAd>()
|
||||
const reload = async () => {
|
||||
const flash = await getCmsAdByCode('flash')
|
||||
console.log(flash)
|
||||
setItem(flash)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Swiper defaultValue={0} height={item?.height} indicator style={{ height: item?.height + 'px' }}>
|
||||
{item?.imageList?.map((item) => (
|
||||
<Swiper.Item key={item}>
|
||||
<Image width="100%" height="100%" src={item.url} mode={'scaleToFill'} lazyLoad={false} style={{ height: item.height + 'px' }} />
|
||||
</Swiper.Item>
|
||||
))}
|
||||
</Swiper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default MyPage
|
||||
0
src/pages/index/BestSellers.scss
Normal file
0
src/pages/index/BestSellers.scss
Normal file
141
src/pages/index/BestSellers.tsx
Normal file
141
src/pages/index/BestSellers.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Image} from '@nutui/nutui-react-taro'
|
||||
import {Share} from '@nutui/icons-react-taro'
|
||||
import {View, Text} from '@tarojs/components';
|
||||
import Taro, {useShareAppMessage, useShareTimeline} from "@tarojs/taro";
|
||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {pageShopGoods} from "@/api/shop/shopGoods";
|
||||
import './BestSellers.scss'
|
||||
|
||||
|
||||
const BestSellers = () => {
|
||||
const [list, setList] = useState<ShopGoods[]>([])
|
||||
const [goods, setGoods] = useState<ShopGoods>()
|
||||
|
||||
const reload = () => {
|
||||
pageShopGoods({}).then(res => {
|
||||
setList(res?.list || []);
|
||||
})
|
||||
}
|
||||
|
||||
// 处理分享点击
|
||||
const handleShare = (item: ShopGoods) => {
|
||||
setGoods(item);
|
||||
|
||||
// 显示分享选项菜单
|
||||
Taro.showActionSheet({
|
||||
itemList: ['分享给好友', '分享到朋友圈'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
// 分享给好友 - 触发转发
|
||||
Taro.showShareMenu({
|
||||
withShareTicket: true,
|
||||
success: () => {
|
||||
// 提示用户点击右上角分享
|
||||
Taro.showToast({
|
||||
title: '请点击右上角分享给好友',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (res.tapIndex === 1) {
|
||||
// 分享到朋友圈
|
||||
Taro.showToast({
|
||||
title: '请点击右上角分享到朋友圈',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('显示分享菜单失败', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
// 分享给好友
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: goods?.name || '精选商品',
|
||||
path: `/shop/goodsDetail/index?id=${goods?.goodsId}`,
|
||||
imageUrl: goods?.image, // 分享图片
|
||||
success: function (res: any) {
|
||||
console.log('分享成功', res);
|
||||
Taro.showToast({
|
||||
title: '分享成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
},
|
||||
fail: function (res: any) {
|
||||
console.log('分享失败', res);
|
||||
Taro.showToast({
|
||||
title: '分享失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 分享到朋友圈
|
||||
useShareTimeline(() => {
|
||||
return {
|
||||
title: `${goods?.name || '精选商品'} - 易赊宝`,
|
||||
path: `/shop/goodsDetail/index?id=${goods?.goodsId}`,
|
||||
imageUrl: goods?.image
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<View className={'py-1'}>
|
||||
<View className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
||||
{list?.map((item, index) => {
|
||||
return (
|
||||
<View key={index} className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
|
||||
<Image src={item.image} mode={'aspectFit'} lazyLoad={false}
|
||||
radius="10px 10px 0 0" height="180"
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
|
||||
<View className={'flex flex-col p-2 rounded-lg'}>
|
||||
<View>
|
||||
<View className={'car-no text-sm'}>{item.name}</View>
|
||||
<View className={'flex justify-between text-xs py-1'}>
|
||||
<Text className={'text-orange-500'}>{item.comments}</Text>
|
||||
<Text className={'text-gray-400'}>已售 {item.sales}</Text>
|
||||
</View>
|
||||
<View className={'flex justify-between items-center py-2'}>
|
||||
<View className={'flex text-red-500 text-xl items-baseline'}>
|
||||
<Text className={'text-xs'}>¥</Text>
|
||||
<Text className={'font-bold text-2xl'}>{item.price}</Text>
|
||||
</View>
|
||||
<View className={'buy-btn'}>
|
||||
<View className={'cart-icon flex items-center'}>
|
||||
<View
|
||||
className={'flex flex-col justify-center items-center text-white px-3 gap-1 text-nowrap whitespace-nowrap cursor-pointer'}
|
||||
onClick={() => handleShare(item)}
|
||||
>
|
||||
<Share size={20}/>
|
||||
</View>
|
||||
</View>
|
||||
<Text className={'text-white pl-4 pr-5'}
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>购买
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default BestSellers
|
||||
69
src/pages/index/Chart.tsx
Normal file
69
src/pages/index/Chart.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Tabs, TabPane} from '@nutui/nutui-react-taro'
|
||||
|
||||
const list = [
|
||||
{
|
||||
title: '今天',
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
title: '昨天',
|
||||
id: 2
|
||||
},
|
||||
{
|
||||
title: '过去7天',
|
||||
id: 3
|
||||
},
|
||||
{
|
||||
title: '过去30天',
|
||||
id: 4
|
||||
}
|
||||
]
|
||||
const Chart = () => {
|
||||
const [tapIndex, setTapIndex] = useState<string | number>('0')
|
||||
const reload = () => {
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
align={'left'}
|
||||
tabStyle={{position: 'sticky', top: '0px'}}
|
||||
value={tapIndex}
|
||||
onChange={(paneKey) => {
|
||||
setTapIndex(paneKey)
|
||||
}}
|
||||
>
|
||||
{
|
||||
list?.map((item, index) => {
|
||||
return (
|
||||
<TabPane key={index} title={item.title}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Tabs>
|
||||
{
|
||||
list?.map((item, index) => {
|
||||
console.log(item.title)
|
||||
return (
|
||||
<div key={index} className={'px-3'}>
|
||||
{
|
||||
tapIndex != index ? null :
|
||||
<div className={'bg-white rounded-lg p-4 flex justify-center items-center text-center text-gray-300'} style={{height: '200px'}}>
|
||||
线状图
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
export default Chart
|
||||
83
src/pages/index/ExpirationTime.tsx
Normal file
83
src/pages/index/ExpirationTime.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {Button} from '@nutui/nutui-react-taro'
|
||||
import {Target, Scan, Truck} from '@nutui/icons-react-taro'
|
||||
import {getUserInfo} from "@/api/layout";
|
||||
import navTo from "@/utils/common";
|
||||
|
||||
const ExpirationTime = () => {
|
||||
const [isAdmin, setIsAdmin] = useState<boolean>(false)
|
||||
const [roleName, setRoleName] = useState<string>()
|
||||
const onScanCode = () => {
|
||||
Taro.scanCode({
|
||||
onlyFromCamera: true,
|
||||
scanType: ['qrCode'],
|
||||
success: (res) => {
|
||||
console.log(res, 'qrcode...')
|
||||
Taro.navigateTo({url: '/hjm/query?id=' + res.result})
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log(res, '扫码失败')
|
||||
Taro.showToast({
|
||||
title: '扫码失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const navToCarList = () => {
|
||||
if (isAdmin) {
|
||||
navTo('/hjm/list', true)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getUserInfo().then((data) => {
|
||||
if (data) {
|
||||
if(data.certification){
|
||||
setIsAdmin( true)
|
||||
}
|
||||
data.roles?.map((item, index) => {
|
||||
if (index == 0) {
|
||||
setRoleName(item.roleCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'mb-3 fixed top-36 z-20'} style={{width: '96%', marginLeft: '3%'}}>
|
||||
<div className={'w-full flex justify-around items-center py-3 rounded-lg'}>
|
||||
<>
|
||||
<Button size={'large'}
|
||||
style={{background: 'linear-gradient(to right, #f3f2f7, #805de1)', borderColor: '#f3f2f7'}}
|
||||
icon={<Truck/>} onClick={navToCarList}>车辆列表</Button>
|
||||
<Button size={'large'}
|
||||
style={{background: 'linear-gradient(to right, #fffbe6, #ffc53d)', borderColor: '#f3f2f7'}}
|
||||
icon={<Scan/>}
|
||||
onClick={onScanCode}>扫一扫
|
||||
</Button>
|
||||
</>
|
||||
|
||||
{
|
||||
roleName == 'youzheng' && <Button size={'large'} style={{
|
||||
background: 'linear-gradient(to right, #eaff8f, #7cb305)',
|
||||
borderColor: '#f3f2f7'
|
||||
}} icon={<Target/>} onClick={() => Taro.navigateTo({url: '/hjm/fence'})}>电子围栏</Button>
|
||||
}
|
||||
|
||||
{
|
||||
roleName == 'kuaidiyuan' && <Button size={'large'} style={{
|
||||
background: 'linear-gradient(to right, #ffa39e, #ff4d4f)',
|
||||
borderColor: '#f3f2f7'
|
||||
}} icon={<Target/>} onClick={() => Taro.navigateTo({url: '/hjm/bx/bx-add'})}>一键报险</Button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ExpirationTime
|
||||
0
src/pages/index/GoodsList.scss
Normal file
0
src/pages/index/GoodsList.scss
Normal file
67
src/pages/index/GoodsList.tsx
Normal file
67
src/pages/index/GoodsList.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Image} from '@nutui/nutui-react-taro'
|
||||
import {Share} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {pageShopGoods} from "@/api/shop/shopGoods";
|
||||
import './GoodsList.scss'
|
||||
|
||||
|
||||
const BestSellers = () => {
|
||||
const [list, setList] = useState<ShopGoods[]>([])
|
||||
|
||||
const reload = () => {
|
||||
pageShopGoods({}).then(res => {
|
||||
setList(res?.list || []);
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'py-3'}>
|
||||
<div className={'flex flex-wrap justify-between items-start rounded-lg px-2'}>
|
||||
{list?.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={'flex flex-col rounded-lg bg-white shadow-sm mb-5'} style={{
|
||||
width: '48%'
|
||||
}}>
|
||||
<Image src={item.image} mode={'scaleToFill'} lazyLoad={false}
|
||||
radius="10px 10px 0 0" height="180"
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
|
||||
<div className={'flex flex-col p-2 rounded-lg'}>
|
||||
<div>
|
||||
<div className={'car-no text-sm'}>{item.name}</div>
|
||||
<div className={'flex justify-between text-xs py-1'}>
|
||||
<span className={'text-orange-500'}>{item.comments}</span>
|
||||
<span className={'text-gray-400'}>已售 {item.sales}</span>
|
||||
</div>
|
||||
<div className={'flex justify-between items-center py-2'}>
|
||||
<div className={'flex text-red-500 text-xl items-baseline'}>
|
||||
<span className={'text-xs'}>¥</span>
|
||||
<span className={'font-bold text-2xl'}>{item.price}</span>
|
||||
</div>
|
||||
<div className={'buy-btn'}>
|
||||
<div className={'cart-icon'}>
|
||||
<Share size={20} className={'mx-4 mt-2'}
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
|
||||
</div>
|
||||
<div className={'text-white pl-4 pr-5'}
|
||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>购买
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default BestSellers
|
||||
58
src/pages/index/Grid.tsx
Normal file
58
src/pages/index/Grid.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Grid} from '@nutui/nutui-react-taro'
|
||||
import {Avatar, Divider} from '@nutui/nutui-react-taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
||||
import navTo from "@/utils/common";
|
||||
|
||||
const MyGrid = () => {
|
||||
const [list, setList] = useState<CmsNavigation[]>([])
|
||||
const reload = async () => {
|
||||
// 读取首页菜单
|
||||
const home = await listCmsNavigation({model: 'index'});
|
||||
const homeId = home[0].navigationId;
|
||||
if(homeId){
|
||||
const menu = await listCmsNavigation({home: 0, parentId: homeId, hide: 0})
|
||||
setList(menu)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then()
|
||||
}, [])
|
||||
|
||||
if (list.length == 0) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return (
|
||||
<>
|
||||
<View className={'p-4'}>
|
||||
<View className={' bg-white rounded-2xl py-4'}>
|
||||
<View className={'title font-medium px-4'}>功能菜单</View>
|
||||
<Divider />
|
||||
<Grid columns={3} square style={{
|
||||
// @ts-ignore
|
||||
'--nutui-grid-border-color': 'transparent',
|
||||
}}>
|
||||
{
|
||||
list.map((item) => (
|
||||
<Grid.Item key={item.navigationId} onClick={() => navTo(`${item.path}`,true)}>
|
||||
<Avatar src={item.icon} className={'mb-2'} shape="square" style={{
|
||||
backgroundColor: 'transparent',
|
||||
}}/>
|
||||
<Text className={'text-gray-600'} style={{
|
||||
fontSize: '16px'
|
||||
}}>{item.title}</Text>
|
||||
</Grid.Item>
|
||||
))
|
||||
}
|
||||
</Grid>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default MyGrid
|
||||
16
src/pages/index/Header.scss
Normal file
16
src/pages/index/Header.scss
Normal file
@@ -0,0 +1,16 @@
|
||||
.header-bg{
|
||||
background: linear-gradient(to bottom, #03605c, #18ae4f);
|
||||
height: 335px;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
.header-bg2{
|
||||
background: linear-gradient(to bottom, #03605c, #18ae4f);
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
275
src/pages/index/Header.tsx
Normal file
275
src/pages/index/Header.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
import {Space} from '@nutui/nutui-react-taro'
|
||||
import {TriangleDown} from '@nutui/icons-react-taro'
|
||||
import {Avatar, NavBar} from '@nutui/nutui-react-taro'
|
||||
import {getWxOpenId} from "@/api/layout";
|
||||
// import {TenantId} from "@/config/app";
|
||||
import {getOrganization} from "@/api/system/organization";
|
||||
import {myUserVerify} from "@/api/system/userVerify";
|
||||
import {useShopInfo} from '@/hooks/useShopInfo';
|
||||
import {useUser} from '@/hooks/useUser';
|
||||
// import {handleInviteRelation} from "@/utils/invite";
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import MySearch from "./MySearch";
|
||||
import './Header.scss';
|
||||
import navTo from "@/utils/common";
|
||||
import UnifiedQRButton from "@/components/UnifiedQRButton";
|
||||
import {getShopDealerRefereeByUserId} from "@/api/shop/shopDealerReferee";
|
||||
|
||||
const Header = (props: any) => {
|
||||
// 使用新的useShopInfo Hook
|
||||
const {
|
||||
getWebsiteLogo,
|
||||
getWebsiteName
|
||||
} = useShopInfo();
|
||||
|
||||
// 使用useUser Hook管理用户状态
|
||||
const {
|
||||
user,
|
||||
isLoggedIn,
|
||||
fetchUserInfo
|
||||
} = useUser();
|
||||
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>()
|
||||
|
||||
const reload = async () => {
|
||||
Taro.getSystemInfo({
|
||||
success: (res) => {
|
||||
setStatusBarHeight(res.statusBarHeight)
|
||||
},
|
||||
})
|
||||
|
||||
// 检查用户是否已登录并且有头像和昵称
|
||||
if (isLoggedIn) {
|
||||
const hasAvatar = user?.avatar || Taro.getStorageSync('Avatar');
|
||||
const hasNickname = user?.nickname || Taro.getStorageSync('Nickname');
|
||||
|
||||
if (!hasAvatar || !hasNickname) {
|
||||
Taro.showToast({
|
||||
title: '您还没有上传头像和昵称',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({
|
||||
url: '/user/profile/profile'
|
||||
})
|
||||
}, 3000)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果已登录,获取最新用户信息
|
||||
if (isLoggedIn) {
|
||||
try {
|
||||
const data = await fetchUserInfo();
|
||||
if (data) {
|
||||
console.log('用户信息>>>', data.phone)
|
||||
// 获取openId
|
||||
if (!data.openid) {
|
||||
Taro.login({
|
||||
success: (res) => {
|
||||
getWxOpenId({code: res.code}).then(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// 是否已认证
|
||||
if (data.certification) {
|
||||
Taro.setStorageSync('Certification', '1')
|
||||
}
|
||||
// 机构ID
|
||||
Taro.setStorageSync('OrganizationId', data.organizationId)
|
||||
// 父级机构ID
|
||||
if (Number(data.organizationId) > 0) {
|
||||
getOrganization(Number(data.organizationId)).then(res => {
|
||||
Taro.setStorageSync('OrganizationParentId', res.parentId)
|
||||
})
|
||||
}
|
||||
// 管理员
|
||||
const isKdy = data.roles?.findIndex(item => item.roleCode == 'admin')
|
||||
if (isKdy != -1) {
|
||||
Taro.setStorageSync('RoleName', '管理')
|
||||
Taro.setStorageSync('RoleCode', 'admin')
|
||||
return false;
|
||||
}
|
||||
// 注册用户
|
||||
const isUser = data.roles?.findIndex(item => item.roleCode == 'user')
|
||||
if (isUser != -1) {
|
||||
Taro.setStorageSync('RoleName', '注册用户')
|
||||
Taro.setStorageSync('RoleCode', 'user')
|
||||
return false;
|
||||
}
|
||||
// 认证信息
|
||||
myUserVerify({status: 1}).then(data => {
|
||||
if (data?.realName) {
|
||||
Taro.setStorageSync('RealName', data.realName)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
}
|
||||
// 查找上级推荐人
|
||||
if(Taro.getStorageSync('UserId')){
|
||||
const dealer = await getShopDealerRefereeByUserId(Taro.getStorageSync('UserId'))
|
||||
if(dealer){
|
||||
Taro.setStorageSync('DealerId', dealer.dealerId)
|
||||
Taro.setStorageSync('Dealer', dealer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 获取用户手机号 */
|
||||
// const handleGetPhoneNumber = ({detail}: { detail: { code?: string, encryptedData?: string, iv?: string } }) => {
|
||||
// const {code, encryptedData, iv} = detail
|
||||
// Taro.login({
|
||||
// success: (loginRes) => {
|
||||
// if (code) {
|
||||
// Taro.request({
|
||||
// url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||
// method: 'POST',
|
||||
// data: {
|
||||
// authCode: loginRes.code,
|
||||
// code,
|
||||
// encryptedData,
|
||||
// iv,
|
||||
// notVerifyPhone: true,
|
||||
// refereeId: 0,
|
||||
// sceneType: 'save_referee',
|
||||
// tenantId: TenantId
|
||||
// },
|
||||
// header: {
|
||||
// 'content-type': 'application/json',
|
||||
// TenantId
|
||||
// },
|
||||
// success: async function (res) {
|
||||
// if (res.data.code == 1) {
|
||||
// Taro.showToast({
|
||||
// title: res.data.message,
|
||||
// icon: 'error',
|
||||
// duration: 2000
|
||||
// })
|
||||
// return false;
|
||||
// }
|
||||
// // 登录成功
|
||||
// const token = res.data.data.access_token;
|
||||
// const userData = res.data.data.user;
|
||||
//
|
||||
// // 使用useUser Hook的loginUser方法更新状态
|
||||
// loginUser(token, userData);
|
||||
//
|
||||
// // 处理邀请关系
|
||||
// if (userData?.userId) {
|
||||
// try {
|
||||
// const inviteSuccess = await handleInviteRelation(userData.userId)
|
||||
// if (inviteSuccess) {
|
||||
// Taro.showToast({
|
||||
// title: '邀请关系建立成功',
|
||||
// icon: 'success',
|
||||
// duration: 2000
|
||||
// })
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('处理邀请关系失败:', error)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 显示登录成功提示
|
||||
// Taro.showToast({
|
||||
// title: '登录成功',
|
||||
// icon: 'success',
|
||||
// duration: 1500
|
||||
// })
|
||||
//
|
||||
// // 不需要重新启动小程序,状态已经通过useUser更新
|
||||
// // 可以选择性地刷新当前页面数据
|
||||
// reload();
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// console.log('登录失败!')
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
reload().then()
|
||||
}, [])
|
||||
|
||||
// 监听用户信息变化,当用户信息更新后重新检查
|
||||
useEffect(() => {
|
||||
if (isLoggedIn && user) {
|
||||
console.log('用户信息已更新:', user);
|
||||
// 检查是否设置头像和昵称
|
||||
if (user.nickname === '微信用户') {
|
||||
Taro.showToast({
|
||||
title: '请设置头像和昵称',
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateTo({
|
||||
url: '/user/profile/profile'
|
||||
});
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
}, [user, isLoggedIn])
|
||||
|
||||
return (
|
||||
<>
|
||||
<View className={'fixed top-0 header-bg'} style={{
|
||||
height: !props.stickyStatus ? '180px' : '148px',
|
||||
}}>
|
||||
<MySearch/>
|
||||
{/*{!props.stickyStatus && <MySearch done={reload}/>}*/}
|
||||
</View>
|
||||
<NavBar
|
||||
style={{marginTop: `${statusBarHeight}px`, marginBottom: '0px', backgroundColor: 'transparent'}}
|
||||
onBackClick={() => {
|
||||
}}
|
||||
left={
|
||||
<Space>
|
||||
{/*统一扫码入口 - 支持登录和核销*/}
|
||||
<UnifiedQRButton
|
||||
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>
|
||||
}
|
||||
>
|
||||
{isLoggedIn ? (
|
||||
<Space onClick={() => navTo(`/user/profile/profile`, true)}>
|
||||
<Text className={'text-white'}>{getWebsiteName()}</Text>
|
||||
</Space>
|
||||
) : (
|
||||
<View style={{display: 'flex', alignItems: 'center'}}>
|
||||
<Avatar
|
||||
size="22"
|
||||
src={getWebsiteLogo()}
|
||||
/>
|
||||
<Text className={'text-xs'} style={{color: '#ffffff'}}>{getWebsiteName()}</Text>
|
||||
<TriangleDown size={9} className={'text-white'}/>
|
||||
</View>
|
||||
)}
|
||||
</NavBar>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Header
|
||||
221
src/pages/index/HeaderWithHook.tsx
Normal file
221
src/pages/index/HeaderWithHook.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
import {Button, Space} from '@nutui/nutui-react-taro'
|
||||
import {TriangleDown} from '@nutui/icons-react-taro'
|
||||
import {Popup, Avatar, NavBar} from '@nutui/nutui-react-taro'
|
||||
import {getWxOpenId} from "@/api/layout";
|
||||
import {TenantId} from "@/config/app";
|
||||
import {getOrganization} from "@/api/system/organization";
|
||||
import {myUserVerify} from "@/api/system/userVerify";
|
||||
import { useShopInfo } from '@/hooks/useShopInfo';
|
||||
import { useUser } from '@/hooks/useUser';
|
||||
import {handleInviteRelation} from "@/utils/invite";
|
||||
import MySearch from "./MySearch";
|
||||
import './Header.scss';
|
||||
|
||||
const Header = (props: any) => {
|
||||
// 使用新的hooks
|
||||
const {
|
||||
loading: shopLoading,
|
||||
getWebsiteName,
|
||||
getWebsiteLogo
|
||||
} = useShopInfo();
|
||||
|
||||
const {
|
||||
user,
|
||||
isLoggedIn,
|
||||
loading: userLoading
|
||||
} = useUser();
|
||||
|
||||
const [showBasic, setShowBasic] = useState(false)
|
||||
const [statusBarHeight, setStatusBarHeight] = useState<number>()
|
||||
|
||||
const reload = async () => {
|
||||
Taro.getSystemInfo({
|
||||
success: (res) => {
|
||||
setStatusBarHeight(res.statusBarHeight)
|
||||
},
|
||||
})
|
||||
|
||||
// 注意:商店信息现在通过useShopInfo自动管理,不需要手动获取
|
||||
// 用户信息现在通过useUser自动管理,不需要手动获取
|
||||
|
||||
// 如果需要获取openId,可以在用户登录后处理
|
||||
if (user && !user.openid) {
|
||||
Taro.login({
|
||||
success: (res) => {
|
||||
getWxOpenId({code: res.code}).then(() => {
|
||||
console.log('OpenId获取成功');
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 检查用户认证状态
|
||||
if (user?.userId) {
|
||||
// 获取组织信息
|
||||
getOrganization(user.userId).then((data) => {
|
||||
console.log('组织信息>>>', data)
|
||||
}).catch(() => {
|
||||
console.log('获取组织信息失败')
|
||||
});
|
||||
|
||||
// 检查用户认证
|
||||
myUserVerify({id: user.userId}).then((data) => {
|
||||
console.log('认证信息>>>', data)
|
||||
}).catch(() => {
|
||||
console.log('获取认证信息失败')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取手机号授权
|
||||
const handleGetPhoneNumber = ({detail}: {detail: {code?: string, encryptedData?: string, iv?: string}}) => {
|
||||
const {code, encryptedData, iv} = detail
|
||||
Taro.login({
|
||||
success: function (loginRes) {
|
||||
if (code) {
|
||||
Taro.request({
|
||||
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||
method: 'POST',
|
||||
data: {
|
||||
authCode: loginRes.code,
|
||||
code,
|
||||
encryptedData,
|
||||
iv,
|
||||
notVerifyPhone: true,
|
||||
refereeId: 0,
|
||||
sceneType: 'save_referee',
|
||||
tenantId: TenantId
|
||||
},
|
||||
success: async 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)
|
||||
|
||||
// 处理邀请关系
|
||||
if (res.data.data.user?.userId) {
|
||||
try {
|
||||
const inviteSuccess = await handleInviteRelation(res.data.data.user.userId)
|
||||
if (inviteSuccess) {
|
||||
Taro.showToast({
|
||||
title: '邀请关系建立成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理邀请关系失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载小程序
|
||||
Taro.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('登录失败!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then()
|
||||
}, [])
|
||||
|
||||
// 显示加载状态
|
||||
if (shopLoading || userLoading) {
|
||||
return (
|
||||
<div className={'fixed top-0 header-bg'} style={{
|
||||
height: !props.stickyStatus ? '180px' : '148px',
|
||||
}}>
|
||||
<div style={{padding: '20px', textAlign: 'center', color: '#fff'}}>
|
||||
加载中...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'fixed top-0 header-bg'} style={{
|
||||
height: !props.stickyStatus ? '180px' : '148px',
|
||||
}}>
|
||||
<MySearch/>
|
||||
</div>
|
||||
<NavBar
|
||||
style={{marginTop: `${statusBarHeight}px`, marginBottom: '0px', backgroundColor: 'transparent'}}
|
||||
onBackClick={() => {
|
||||
}}
|
||||
left={
|
||||
!isLoggedIn ? (
|
||||
<div style={{display: 'flex', alignItems: 'center'}}>
|
||||
<Button style={{color: '#000'}} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||
<Space>
|
||||
<Avatar
|
||||
size="22"
|
||||
src={getWebsiteLogo()}
|
||||
/>
|
||||
<span style={{color: '#000'}}>{getWebsiteName()}</span>
|
||||
</Space>
|
||||
</Button>
|
||||
<TriangleDown size={9}/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{display: 'flex', alignItems: 'center', gap: '8px'}}>
|
||||
<Avatar
|
||||
size="22"
|
||||
src={getWebsiteLogo()}
|
||||
/>
|
||||
<span className={'text-white'}>{getWebsiteName()}</span>
|
||||
<TriangleDown className={'text-white'} size={9}/>
|
||||
</div>
|
||||
)}>
|
||||
</NavBar>
|
||||
<Popup
|
||||
visible={showBasic}
|
||||
position="bottom"
|
||||
style={{width: '100%', height: '100%'}}
|
||||
onClose={() => {
|
||||
setShowBasic(false)
|
||||
}}
|
||||
>
|
||||
<div style={{padding: '20px'}}>
|
||||
<h3>商店信息</h3>
|
||||
<div>网站名称: {getWebsiteName()}</div>
|
||||
<div>Logo: <img src={getWebsiteLogo()} alt="logo" style={{width: '50px', height: '50px'}} /></div>
|
||||
|
||||
<h3>用户信息</h3>
|
||||
<div>登录状态: {isLoggedIn ? '已登录' : '未登录'}</div>
|
||||
{user && (
|
||||
<>
|
||||
<div>用户ID: {user.userId}</div>
|
||||
<div>手机号: {user.phone}</div>
|
||||
<div>昵称: {user.nickname}</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => setShowBasic(false)}
|
||||
style={{marginTop: '20px', padding: '10px 20px'}}
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</Popup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header;
|
||||
68
src/pages/index/Help.tsx
Normal file
68
src/pages/index/Help.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {ArrowRight} from '@nutui/icons-react-taro'
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {BaseUrl} from "@/config/app";
|
||||
import {TEMPLATE_ID} from "@/utils/server";
|
||||
|
||||
/**
|
||||
* 帮助中心
|
||||
* @constructor
|
||||
*/
|
||||
const Help = () => {
|
||||
const {params} = useRouter();
|
||||
const [categoryId, setCategoryId] = useState<number>(3494)
|
||||
const [list, setList] = useState<CmsArticle[]>([])
|
||||
|
||||
const reload = () => {
|
||||
if (params.id) {
|
||||
setCategoryId(Number(params.id))
|
||||
}
|
||||
Taro.request({
|
||||
url: BaseUrl + '/cms/cms-article/page',
|
||||
method: 'GET',
|
||||
data: {
|
||||
categoryId
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
TenantId: TEMPLATE_ID
|
||||
},
|
||||
success: function (res) {
|
||||
const data = res.data.data;
|
||||
if (data?.list) {
|
||||
setList(data?.list)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={'px-3 mb-10'}>
|
||||
<div className={'flex flex-col justify-between items-center bg-white rounded-lg p-4'}>
|
||||
<div className={'title-bar flex justify-between items-center w-full mb-2'}>
|
||||
<div className={'font-bold text-lg flex text-gray-800 justify-center items-center'}>帮助中心</div>
|
||||
<a className={'text-gray-400 text-sm'} onClick={() => Taro.navigateTo({url: `/cms/article?id=${categoryId}`})}>查看全部</a>
|
||||
</div>
|
||||
<div className={'bg-white min-h-36 w-full'}>
|
||||
{
|
||||
list.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={'flex justify-between items-center py-2'} onClick={() => Taro.navigateTo({url: `/cms/help?id=${item.articleId}`}) }>
|
||||
<div className={'text-sm'}>{item.title}</div>
|
||||
<ArrowRight color={'#cccccc'} size={18} />
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Help
|
||||
152
src/pages/index/Login.tsx
Normal file
152
src/pages/index/Login.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {Input, Radio, Button} from '@nutui/nutui-react-taro'
|
||||
import {TenantId} from "@/config/app";
|
||||
import './login.scss';
|
||||
import {saveStorageByLoginUser} from "@/utils/server";
|
||||
import {handleInviteRelation} from "@/utils/invite";
|
||||
|
||||
// 微信获取手机号回调参数类型
|
||||
interface GetPhoneNumberDetail {
|
||||
code?: string;
|
||||
encryptedData?: string;
|
||||
iv?: string;
|
||||
errMsg: string;
|
||||
}
|
||||
|
||||
interface GetPhoneNumberEvent {
|
||||
detail: GetPhoneNumberDetail;
|
||||
}
|
||||
|
||||
interface LoginProps {
|
||||
done?: (user: any) => void;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 登录接口返回数据类型
|
||||
interface LoginResponse {
|
||||
data: {
|
||||
data: {
|
||||
access_token: string;
|
||||
user: any;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const Login = (props: LoginProps) => {
|
||||
const [isAgree, setIsAgree] = useState(false)
|
||||
const [env, setEnv] = useState<string>()
|
||||
|
||||
/* 获取用户手机号 */
|
||||
const handleGetPhoneNumber = ({detail}: GetPhoneNumberEvent) => {
|
||||
const {code, encryptedData, iv} = detail
|
||||
Taro.login({
|
||||
success: function (loginRes) {
|
||||
if (code) {
|
||||
Taro.request({
|
||||
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||
method: 'POST',
|
||||
data: {
|
||||
authCode: loginRes.code,
|
||||
code,
|
||||
encryptedData,
|
||||
iv,
|
||||
notVerifyPhone: true,
|
||||
refereeId: 0,
|
||||
sceneType: 'save_referee',
|
||||
tenantId: TenantId
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
TenantId
|
||||
},
|
||||
success: async function (res: LoginResponse) {
|
||||
saveStorageByLoginUser(res.data.data.access_token, res.data.data.user)
|
||||
|
||||
// 处理邀请关系
|
||||
if (res.data.data.user?.userId) {
|
||||
try {
|
||||
const inviteSuccess = await handleInviteRelation(res.data.data.user.userId)
|
||||
if (inviteSuccess) {
|
||||
Taro.showToast({
|
||||
title: '邀请关系建立成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理邀请关系失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
props.done?.(res.data.data.user);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('登录失败!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const reload = () => {
|
||||
Taro.hideTabBar()
|
||||
setEnv(Taro.getEnv())
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{height: '80vh'}} className={'flex flex-col justify-center px-5'}>
|
||||
<div className={'text-3xl text-center py-5 font-normal mb-10 '}>登录</div>
|
||||
{
|
||||
env === 'WEAPP' && (
|
||||
<>
|
||||
<div className={'flex flex-col w-full text-white rounded-full justify-between items-center my-2'} style={{ background: 'linear-gradient(to right, #7e22ce, #9333ea)'}}>
|
||||
<Button open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||
授权手机号登录
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
env === 'WEB' && (
|
||||
<>
|
||||
<div className={'flex flex-col justify-between items-center my-2'}>
|
||||
<Input type="text" placeholder="手机号" maxLength={11}
|
||||
style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
|
||||
</div>
|
||||
<div className={'flex flex-col justify-between items-center my-2'}>
|
||||
<Input type="password" placeholder="密码" style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
|
||||
</div>
|
||||
<div className={'flex justify-between my-2 text-left px-1'}>
|
||||
<a href={'#'} className={'text-blue-600 text-sm'}
|
||||
onClick={() => Taro.navigateTo({url: '/passport/forget'})}>忘记密码</a>
|
||||
<a href={'#'} className={'text-blue-600 text-sm'}
|
||||
onClick={() => Taro.navigateTo({url: '/passport/setting'})}>服务配置</a>
|
||||
</div>
|
||||
<div className={'flex justify-center my-5'}>
|
||||
<Button type="info" size={'large'} className={'w-full rounded-lg p-2'} disabled={!isAgree}>登录</Button>
|
||||
</div>
|
||||
<div className={'w-full bottom-20 my-2 flex justify-center text-sm items-center text-center'}>
|
||||
没有账号?<a href={''} onClick={() => Taro.navigateTo({url: '/passport/register'})}
|
||||
className={'text-blue-600'}>立即注册</a>
|
||||
</div>
|
||||
<div className={'my-2 flex fixed bottom-20 text-sm items-center px-1'}>
|
||||
<Radio style={{color: '#333333'}} checked={isAgree} onClick={() => setIsAgree(!isAgree)}></Radio>
|
||||
<span className={'text-gray-400'} onClick={() => setIsAgree(!isAgree)}>登录表示您已阅读并同意</span><a
|
||||
onClick={() => Taro.navigateTo({url: '/passport/agreement'})}
|
||||
className={'text-blue-600'}>《服务协议及隐私政策》</a>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Login
|
||||
73
src/pages/index/Menu.tsx
Normal file
73
src/pages/index/Menu.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {Image} from '@nutui/nutui-react-taro'
|
||||
import {Loading} from '@nutui/nutui-react-taro'
|
||||
import {listCmsNavigation} from "@/api/cms/cmsNavigation"
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model"
|
||||
|
||||
const Page = () => {
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [navItems, setNavItems] = useState<CmsNavigation[]>([])
|
||||
|
||||
const reload = async () => {
|
||||
// 读取首页菜单
|
||||
const home = await listCmsNavigation({model: 'index'});
|
||||
const homeId = home[0].navigationId;
|
||||
if (homeId) {
|
||||
// 读取首页导航条
|
||||
const menus = await listCmsNavigation({parentId: homeId, hide: 0});
|
||||
setNavItems(menus || [])
|
||||
}
|
||||
};
|
||||
|
||||
const onNav = (row: CmsNavigation) => {
|
||||
console.log('nav = ', row)
|
||||
console.log('path = ', `/${row.model}${row.path}`)
|
||||
if (row.model == 'goods') {
|
||||
return Taro.navigateTo({url: `/shop/category/index?id=${row.navigationId}`})
|
||||
}
|
||||
if (row.model == 'article') {
|
||||
return Taro.navigateTo({url: `/cms/category/index?id=${row.navigationId}`})
|
||||
}
|
||||
return Taro.navigateTo({url: `${row.path}`})
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading>加载中</Loading>
|
||||
)
|
||||
}
|
||||
|
||||
if (navItems.length === 0) {
|
||||
return <View className={'hidden'}></View>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={'p-2 z-50 mt-1 hidden'}>
|
||||
<View className={'flex justify-between pb-2 p-2 bg-white rounded-xl shadow-sm'}>
|
||||
{
|
||||
navItems.map((item, index) => (
|
||||
<View key={index} className={'text-center'} onClick={() => onNav(item)}>
|
||||
<View className={'flex flex-col justify-center items-center p-1'}>
|
||||
<Image src={item.icon} height={36} width={36} lazyLoad={false}/>
|
||||
<View className={'mt-1'}>
|
||||
<Text className={'text-gray-600'} style={{fontSize: '14px'}}>{item?.title}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
export default Page
|
||||
70
src/pages/index/MySearch.tsx
Normal file
70
src/pages/index/MySearch.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import {Search} from '@nutui/icons-react-taro'
|
||||
import {Button, Input} from '@nutui/nutui-react-taro'
|
||||
import {useState} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
|
||||
function MySearch() {
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
|
||||
const onKeywords = (keywords: string) => {
|
||||
setKeywords(keywords)
|
||||
}
|
||||
|
||||
const onQuery = () => {
|
||||
if(!keywords.trim()){
|
||||
Taro.showToast({
|
||||
title: '请输入关键字',
|
||||
icon: 'none'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// 跳转到搜索页面
|
||||
Taro.navigateTo({
|
||||
url: `/shop/search/index?keywords=${encodeURIComponent(keywords.trim())}`
|
||||
});
|
||||
}
|
||||
|
||||
// 点击搜索框跳转到搜索页面
|
||||
const onInputFocus = () => {
|
||||
Taro.navigateTo({
|
||||
url: '/shop/search/index'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={'z-50 left-0 w-full'}>
|
||||
<div className={'px-2 hidden'}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
background: '#ffffff',
|
||||
padding: '0 5px',
|
||||
borderRadius: '20px',
|
||||
marginTop: '100px',
|
||||
}}
|
||||
>
|
||||
<Search size={18} className={'ml-2 text-gray-400'}/>
|
||||
<Input
|
||||
placeholder="搜索商品"
|
||||
value={keywords}
|
||||
onChange={onKeywords}
|
||||
onConfirm={onQuery}
|
||||
onFocus={onInputFocus}
|
||||
style={{ padding: '9px 8px'}}
|
||||
/>
|
||||
<div
|
||||
className={'flex items-center'}
|
||||
>
|
||||
<Button type="success" style={{background: 'linear-gradient(to bottom, #1cd98a, #24ca94)'}} onClick={onQuery}>
|
||||
搜索
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MySearch;
|
||||
47
src/pages/index/PopUpAd.tsx
Normal file
47
src/pages/index/PopUpAd.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import { Dialog } from '@nutui/nutui-react-taro'
|
||||
import {getCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
||||
import {RichText} from '@tarojs/components'
|
||||
|
||||
const PopUpAd = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [item, setItem] = useState<CmsNavigation>()
|
||||
const reload = async () => {
|
||||
const navigation = await getCmsNavigation(4426)
|
||||
if(navigation && navigation.hide == 0){
|
||||
setItem(navigation)
|
||||
setVisible(true)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={
|
||||
<div className={'font-bold mb-3'}>重要通知</div>
|
||||
}
|
||||
footer={null}
|
||||
closeIcon
|
||||
closeIconPosition="top-right"
|
||||
style={{
|
||||
// @ts-ignore
|
||||
'--nutui-dialog-close-color': '#8c8c8c',
|
||||
}}
|
||||
onConfirm={() => setVisible(false)}
|
||||
onCancel={() => setVisible(false)}
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setVisible(false)
|
||||
}}
|
||||
>
|
||||
<RichText nodes={item?.design?.content}/>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default PopUpAd
|
||||
29
src/pages/index/SiteUrl.tsx
Normal file
29
src/pages/index/SiteUrl.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Input, Button} from '@nutui/nutui-react-taro'
|
||||
import {copyText} from "@/utils/common";
|
||||
import Taro from '@tarojs/taro'
|
||||
|
||||
const SiteUrl = (props: any) => {
|
||||
const [siteUrl, setSiteUrl] = useState<string>('')
|
||||
const reload = () => {
|
||||
if(props.tenantId){
|
||||
setSiteUrl(`https://${props.tenantId}.shoplnk.cn`)
|
||||
}else {
|
||||
setSiteUrl(`https://${Taro.getStorageSync('TenantId')}.shoplnk.cn`)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [props])
|
||||
|
||||
return (
|
||||
<div className={'px-3 mt-1 mb-4'}>
|
||||
<div className={'flex justify-between items-center bg-gray-300 rounded-lg pr-2'}>
|
||||
<Input type="text" value={siteUrl} disabled style={{backgroundColor: '#d1d5db', borderRadius: '8px'}}/>
|
||||
<Button type={'info'} onClick={() => copyText(siteUrl)}>复制</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default SiteUrl
|
||||
38
src/pages/index/chart/DemoLine.tsx
Normal file
38
src/pages/index/chart/DemoLine.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { View } from '@tarojs/components'
|
||||
import { EChart } from "echarts-taro3-react";
|
||||
import './index.scss'
|
||||
|
||||
export default function Index() {
|
||||
const refBarChart = useRef<any>()
|
||||
const defautOption = {
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [120, 200, 150, 80, 70, 110, 130],
|
||||
type: "line",
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: "rgba(220, 220, 220, 0.8)",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
useEffect(() => {
|
||||
if(refBarChart.current) {
|
||||
refBarChart.current?.refresh(defautOption);
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<View className='index'>
|
||||
<EChart ref={refBarChart} canvasId='line-canvas' />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
7
src/pages/index/chart/index.scss
Normal file
7
src/pages/index/chart/index.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.index {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #F3F3F3;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
5
src/pages/index/index.config.ts
Normal file
5
src/pages/index/index.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '易赊宝',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#f5f5f5'
|
||||
})
|
||||
243
src/pages/index/index.scss
Normal file
243
src/pages/index/index.scss
Normal file
@@ -0,0 +1,243 @@
|
||||
page {
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
.home {
|
||||
padding: 24rpx 24rpx calc(40rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.welcomeCard {
|
||||
padding: 18rpx 0 6rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcomeText {
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bannerCard {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid #e7e7e7;
|
||||
}
|
||||
|
||||
.bannerSwiper {
|
||||
height: 280rpx;
|
||||
}
|
||||
|
||||
.bannerSlide {
|
||||
height: 280rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.bannerSlide1 {
|
||||
background: linear-gradient(135deg, #c61b1b, #e43a3a);
|
||||
}
|
||||
|
||||
.bannerSlide2 {
|
||||
background: linear-gradient(135deg, #b01414, #d63636);
|
||||
}
|
||||
|
||||
.bannerLogo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 180rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.bannerLogoText {
|
||||
color: #ffd34a;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.bannerSlogan {
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
padding: 0 20rpx;
|
||||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.actionCard {
|
||||
margin-top: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx 10rpx 18rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actionLine {
|
||||
position: absolute;
|
||||
left: 36rpx;
|
||||
right: 36rpx;
|
||||
top: 62rpx;
|
||||
height: 4rpx;
|
||||
background: #d6b25a;
|
||||
border-radius: 999rpx;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.actionLineDot {
|
||||
position: absolute;
|
||||
left: 36rpx;
|
||||
top: 56rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #d6b25a;
|
||||
}
|
||||
|
||||
.actionRow {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.actionItem {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.actionIcon {
|
||||
width: 112rpx;
|
||||
height: 112rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.actionIconImg {
|
||||
width: 58rpx;
|
||||
height: 58rpx;
|
||||
}
|
||||
|
||||
.actionLabel {
|
||||
margin-top: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.sectionCard {
|
||||
margin-top: 22rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 18rpx 18rpx 22rpx;
|
||||
}
|
||||
|
||||
.sectionHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sectionAccent {
|
||||
width: 10rpx;
|
||||
height: 34rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #b01f1f;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.sectionMore {
|
||||
margin-left: auto;
|
||||
font-size: 26rpx;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
|
||||
.introBox {
|
||||
margin-top: 16rpx;
|
||||
background: #fdeeee;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.introText {
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.ctaWrap {
|
||||
margin-top: 22rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ctaBtn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(180deg, #ff1a1a, #b51616);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ctaBtnText {
|
||||
color: #fff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.ctaHint {
|
||||
margin-top: 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.faqList {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.faqItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 22rpx 6rpx;
|
||||
border-bottom: 2rpx solid #efefef;
|
||||
}
|
||||
|
||||
.faqItemLast {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.faqTitle {
|
||||
font-size: 30rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.faqDate {
|
||||
margin-left: auto;
|
||||
font-size: 26rpx;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
|
||||
.bottomSpacer {
|
||||
height: 24rpx;
|
||||
}
|
||||
156
src/pages/index/index.tsx
Normal file
156
src/pages/index/index.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import Taro, {useShareAppMessage, useShareTimeline} from '@tarojs/taro'
|
||||
import {Image, Swiper, SwiperItem, Text, View} from '@tarojs/components'
|
||||
import iconShop from '@/assets/tabbar/shop.png'
|
||||
import iconFind from '@/assets/tabbar/find.png'
|
||||
import iconKefu from '@/assets/tabbar/kefu.png'
|
||||
import './index.scss'
|
||||
|
||||
function Home() {
|
||||
useShareTimeline(() => {
|
||||
return {
|
||||
title: '易赊宝',
|
||||
path: `/pages/index/index`
|
||||
}
|
||||
})
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: '易赊宝',
|
||||
path: `/pages/index/index`,
|
||||
success: function () {
|
||||
console.log('分享成功');
|
||||
},
|
||||
fail: function () {
|
||||
console.log('分享失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const onAction = (type: 'progress' | 'guide' | 'kefu') => {
|
||||
const textMap = {
|
||||
progress: '查询进度',
|
||||
guide: '业务指南',
|
||||
kefu: '在线客服'
|
||||
} as const
|
||||
|
||||
Taro.showToast({title: `${textMap[type]}(示例)`, icon: 'none'})
|
||||
}
|
||||
|
||||
const onDemand = () => {
|
||||
Taro.showToast({title: '发需求(示例)', icon: 'none'})
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='home'>
|
||||
<View className='welcomeCard'>
|
||||
<Text className='welcomeText'>欢迎来到易赊宝小程序~</Text>
|
||||
</View>
|
||||
|
||||
<View className='bannerCard'>
|
||||
<Swiper
|
||||
className='bannerSwiper'
|
||||
circular
|
||||
autoplay
|
||||
interval={3500}
|
||||
duration={400}
|
||||
indicatorDots
|
||||
indicatorColor='rgba(255,255,255,0.55)'
|
||||
indicatorActiveColor='#ffffff'
|
||||
>
|
||||
<SwiperItem>
|
||||
<View className='bannerSlide bannerSlide1'>
|
||||
<View className='bannerLogo'>
|
||||
<Text className='bannerLogoText'>易赊宝</Text>
|
||||
</View>
|
||||
<Text className='bannerSlogan'>咨询加速回款,助力经济循环</Text>
|
||||
</View>
|
||||
</SwiperItem>
|
||||
<SwiperItem>
|
||||
<View className='bannerSlide bannerSlide2'>
|
||||
<View className='bannerLogo'>
|
||||
<Text className='bannerLogoText'>易赊宝</Text>
|
||||
</View>
|
||||
<Text className='bannerSlogan'>专业回款咨询服务,团队全国覆盖</Text>
|
||||
</View>
|
||||
</SwiperItem>
|
||||
</Swiper>
|
||||
</View>
|
||||
|
||||
<View className='actionCard'>
|
||||
<View className='actionRow'>
|
||||
<View className='actionItem' onClick={() => onAction('progress')}>
|
||||
<View className='actionIcon'>
|
||||
<Image className='actionIconImg' src={iconShop} mode='aspectFit' />
|
||||
</View>
|
||||
<Text className='actionLabel'>查询进度</Text>
|
||||
</View>
|
||||
|
||||
<View className='actionItem' onClick={() => onAction('guide')}>
|
||||
<View className='actionIcon'>
|
||||
<Image className='actionIconImg' src={iconFind} mode='aspectFit' />
|
||||
</View>
|
||||
<Text className='actionLabel'>业务指南</Text>
|
||||
</View>
|
||||
|
||||
<View className='actionItem' onClick={() => onAction('kefu')}>
|
||||
<View className='actionIcon'>
|
||||
<Image className='actionIconImg' src={iconKefu} mode='aspectFit' />
|
||||
</View>
|
||||
<Text className='actionLabel'>在线客服</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='sectionCard'>
|
||||
<View className='sectionHeader'>
|
||||
<View className='sectionAccent' />
|
||||
<Text className='sectionTitle'>简介</Text>
|
||||
<Text className='sectionMore'>更多</Text>
|
||||
</View>
|
||||
|
||||
<View className='introBox'>
|
||||
<Text className='introText'>
|
||||
易赊宝是提供专业回款咨询服务的小程序!我们重视企业需求,加速回款咨询服务能力助力企业资金流动畅通循环,服务团队全国覆盖。
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View className='ctaWrap'>
|
||||
<View className='ctaBtn' onClick={onDemand}>
|
||||
<Text className='ctaBtnText'>发需求</Text>
|
||||
</View>
|
||||
<Text className='ctaHint'>提出您的述求,免费获取解决方案</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='sectionCard'>
|
||||
<View className='sectionHeader'>
|
||||
<View className='sectionAccent' />
|
||||
<Text className='sectionTitle'>常见问题解答</Text>
|
||||
<Text className='sectionMore'>更多>></Text>
|
||||
</View>
|
||||
|
||||
<View className='faqList'>
|
||||
{[
|
||||
{title: '常见问题一', date: '2025-11-12'},
|
||||
{title: '常见问题二', date: '2025-11-12'},
|
||||
{title: '常见问题三', date: '2025-11-12'},
|
||||
{title: '常见问题四', date: '2025-11-12'},
|
||||
{title: '常见问题五', date: '2025-11-12'}
|
||||
].map((item, idx, arr) => (
|
||||
<View
|
||||
key={`${item.title}-${idx}`}
|
||||
className={`faqItem ${idx === arr.length - 1 ? 'faqItemLast' : ''}`}
|
||||
>
|
||||
<Text className='faqTitle'>{item.title}</Text>
|
||||
<Text className='faqDate'>{item.date}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className='bottomSpacer' />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
10
src/pages/index/login.scss
Normal file
10
src/pages/index/login.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
// 微信授权按钮的特殊样式
|
||||
button[open-type="getPhoneNumber"] {
|
||||
width: 100%;
|
||||
padding: 8px 0 !important;
|
||||
height: 80px;
|
||||
color: #ffffff !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 50px !important;
|
||||
}
|
||||
4
src/pages/order/order.scss
Normal file
4
src/pages/order/order.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
// 订单页面样式
|
||||
.order-page {
|
||||
// 订单相关样式
|
||||
}
|
||||
76
src/pages/user/components/IsDealer.tsx
Normal file
76
src/pages/user/components/IsDealer.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
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} from "react";
|
||||
import {useDealerUser} from "@/hooks/useDealerUser";
|
||||
|
||||
const UserCell = () => {
|
||||
const {isSuperAdmin} = useUser();
|
||||
const {dealerUser} = useDealerUser()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* 管理中心
|
||||
*/
|
||||
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 null
|
||||
}
|
||||
export default UserCell
|
||||
206
src/pages/user/components/UserCard.tsx
Normal file
206
src/pages/user/components/UserCard.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import {Button} from '@nutui/nutui-react-taro'
|
||||
import {Avatar, Tag} from '@nutui/nutui-react-taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {Scan} from '@nutui/icons-react-taro';
|
||||
import {getWxOpenId} from '@/api/layout';
|
||||
import Taro from '@tarojs/taro';
|
||||
import {useEffect} from "react";
|
||||
import navTo from "@/utils/common";
|
||||
import {TenantId} from "@/config/app";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
|
||||
function UserCard() {
|
||||
const {
|
||||
user,
|
||||
isAdmin,
|
||||
isLoggedIn,
|
||||
loginUser,
|
||||
fetchUserInfo,
|
||||
getDisplayName,
|
||||
getRoleName
|
||||
} = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
||||
Taro.getSetting({
|
||||
success: (res) => {
|
||||
if (res.authSetting['scope.userInfo']) {
|
||||
// 用户已经授权过,可以直接获取用户信息
|
||||
console.log('用户已经授权过,可以直接获取用户信息')
|
||||
reload();
|
||||
} else {
|
||||
// 用户未授权,需要弹出授权窗口
|
||||
console.log('用户未授权,需要弹出授权窗口')
|
||||
showAuthModal();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
const reload = async () => {
|
||||
// 如果已登录,获取最新用户信息
|
||||
if (isLoggedIn) {
|
||||
try {
|
||||
const data = await fetchUserInfo();
|
||||
if (data) {
|
||||
// 获取openId
|
||||
if (!data.openid) {
|
||||
Taro.login({
|
||||
success: (res) => {
|
||||
getWxOpenId({code: res.code}).then(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 (loginRes) {
|
||||
if (code) {
|
||||
Taro.request({
|
||||
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||
method: 'POST',
|
||||
data: {
|
||||
authCode: loginRes.code,
|
||||
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;
|
||||
}
|
||||
// 登录成功
|
||||
const token = res.data.data.access_token;
|
||||
const userData = res.data.data.user;
|
||||
|
||||
// 使用useUser Hook的loginUser方法更新状态
|
||||
loginUser(token, userData);
|
||||
|
||||
// 显示登录成功提示
|
||||
Taro.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 刷新页面数据
|
||||
reload();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('登录失败!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={'header-bg pt-20'}>
|
||||
<View className={'p-4'}>
|
||||
<View
|
||||
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',
|
||||
minHeight: '120px',
|
||||
// borderRadius: '22px 22px 0 0',
|
||||
}}
|
||||
>
|
||||
<View className={'user-card-header flex w-full justify-between items-center pt-4'}>
|
||||
<View className={'flex items-center mx-4'}>
|
||||
{
|
||||
isLoggedIn ? (
|
||||
<Avatar size="large" src={user?.avatar} shape="round"/>
|
||||
) : (
|
||||
<Button className={'text-black'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||
<Avatar size="large" src={user?.avatar} shape="round"/>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<View className={'user-info flex flex-col px-2'}>
|
||||
<View className={'py-1 text-black font-bold max-w-28'}>{getDisplayName()}</View>
|
||||
{isLoggedIn ? (
|
||||
<View className={'grade text-xs py-1'}>
|
||||
<Tag type="success" round>
|
||||
<Text className={'p-1'}>
|
||||
{getRoleName()}
|
||||
</Text>
|
||||
</Tag>
|
||||
</View>
|
||||
) : ''}
|
||||
</View>
|
||||
</View>
|
||||
<View className={'gap-2 flex items-center'}>
|
||||
{isAdmin() && <Scan className={'text-gray-900'} size={24} onClick={() => navTo('/user/store/verification', true)} />}
|
||||
<View 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)}>
|
||||
{'个人资料'}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default UserCard;
|
||||
144
src/pages/user/components/UserCell.tsx
Normal file
144
src/pages/user/components/UserCell.tsx
Normal file
@@ -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, LogisticsError, Location} 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={'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
|
||||
102
src/pages/user/components/UserFooter.tsx
Normal file
102
src/pages/user/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
|
||||
122
src/pages/user/components/UserOrder.tsx
Normal file
122
src/pages/user/components/UserOrder.tsx
Normal file
@@ -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;
|
||||
5
src/pages/user/user.config.ts
Normal file
5
src/pages/user/user.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '我的',
|
||||
navigationStyle: 'custom',
|
||||
navigationBarBackgroundColor: '#e9fff2'
|
||||
})
|
||||
10
src/pages/user/user.scss
Normal file
10
src/pages/user/user.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
.header-bg{
|
||||
/* Replace top background image with a gradient + subtle decoration */
|
||||
background:
|
||||
radial-gradient(circle at 18% 22%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 55%),
|
||||
radial-gradient(circle at 82% 30%, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 52%),
|
||||
radial-gradient(circle at 60% 8%, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 45%),
|
||||
linear-gradient(180deg, #ff0000 0%, #ffeee9 45%, #f9fafb 100%);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
42
src/pages/user/user.tsx
Normal file
42
src/pages/user/user.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import UserCard from "./components/UserCard";
|
||||
import UserOrder from "./components/UserOrder";
|
||||
import UserCell from "./components/UserCell";
|
||||
import UserFooter from "./components/UserFooter";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
import './user.scss'
|
||||
import IsDealer from "./components/IsDealer";
|
||||
|
||||
function User() {
|
||||
const {
|
||||
isAdmin
|
||||
} = useUser();
|
||||
|
||||
/**
|
||||
* 门店核销管理
|
||||
*/
|
||||
if (isAdmin()) {
|
||||
return <>
|
||||
<div className={'w-full'}>
|
||||
<UserCard/>
|
||||
<UserOrder/>
|
||||
<IsDealer/>
|
||||
<UserCell/>
|
||||
<UserFooter/>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'w-full'}>
|
||||
<UserCard/>
|
||||
{/*<UserOrder/>*/}
|
||||
<IsDealer/>
|
||||
<UserCell/>
|
||||
<UserFooter/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default User
|
||||
Reference in New Issue
Block a user