Browse Source

处理 图片过大, 对接登陆,但是不通

master
DU 4 months ago
parent
commit
ae443b7fc7
  1. 110
      MIGRATION_CHECKLIST.md
  2. 116
      src/api/apidoc/login.md
  3. 19
      src/api/login.ts
  4. 5
      src/components/EmptyData/index.tsx
  5. 6
      src/config/index.ts
  6. 6
      src/pageStore/storeDetail/index.tsx
  7. 15
      src/pages/index/index.tsx
  8. 10
      src/pages/message/index.tsx
  9. 24
      src/pages/user/index.scss
  10. 127
      src/pages/user/index.tsx
  11. 4
      src/pagesOrder/accountBalance/components/BalanceList.tsx
  12. 4
      src/pagesOrder/accountBalance/components/OpenRecordList.tsx
  13. 4
      src/pagesOrder/accountBalance/components/RechargeRecordList.tsx
  14. 4
      src/pagesOrder/rechargeDetail/index.tsx
  15. 4
      src/pagesUser/orderList/index.tsx
  16. 60
      src/store/user.ts
  17. 47
      src/types/login.ts
  18. 176
      src/utils/README_imageHelper.md
  19. 108
      src/utils/imageHelper.ts
  20. 36
      src/utils/testImageHelper.ts

110
MIGRATION_CHECKLIST.md

@ -0,0 +1,110 @@
# 图片迁移检查清单
## ✅ 已完成的工作
### 1. 创建图片工具
- ✅ 创建 `src/utils/imageHelper.ts` 工具类
- ✅ 配置预定义图片映射 `BIG_IMAGES`
- ✅ 提供多种使用方式:`getBigImage()`, `getBigImageUrl()`, `getBigImagesUrl()`
- ✅ 添加图片预加载功能 `preloadImages()`
### 2. 更新代码引用
- ✅ `src/components/EmptyData/index.tsx` - 空数据组件
- ✅ `src/pages/index/index.tsx` - 首页轮播图和卡片
- ✅ `src/pages/user/index.tsx` - 用户页面背景
- ✅ `src/pageStore/storeDetail/index.tsx` - 门店详情轮播图
- ✅ `src/pages/message/index.tsx` - 消息页面图片和空数据
- ✅ `src/pagesUser/orderList/index.tsx` - 订单列表空数据
- ✅ `src/pagesOrder/rechargeDetail/index.tsx` - 充值详情空数据
- ✅ `src/pagesOrder/accountBalance/components/OpenRecordList.tsx` - 开台记录空数据
- ✅ `src/pagesOrder/accountBalance/components/RechargeRecordList.tsx` - 充值记录空数据
- ✅ `src/pagesOrder/accountBalance/components/BalanceList.tsx` - 余额记录空数据
### 3. 构建测试
- ✅ 修复所有语法错误
- ✅ 成功构建微信小程序
- ✅ 生成的代码正确使用图片工具
## 🔄 需要你完成的工作
### 1. 服务器图片检查
确保以下图片已上传到服务器 `http://39.101.75.71:8888/img/MiniBgImage/`
- [ ] `noData.png` - 空数据占位图
- [ ] `swBG.png` - 轮播背景图
- [ ] `user_bg.png` - 用户页面背景
- [ ] `fjmd.png` - 附近门店功能图
- [ ] `smkt.png` - 扫码开台功能图
- [ ] `tghx.png` - 团购核销功能图
### 2. 图片访问测试
在浏览器中测试以下 URL 是否可以正常访问:
- [ ] http://39.101.75.71:8888/img/MiniBgImage/noData.png
- [ ] http://39.101.75.71:8888/img/MiniBgImage/swBG.png
- [ ] http://39.101.75.71:8888/img/MiniBgImage/user_bg.png
- [ ] http://39.101.75.71:8888/img/MiniBgImage/fjmd.png
- [ ] http://39.101.75.71:8888/img/MiniBgImage/smkt.png
- [ ] http://39.101.75.71:8888/img/MiniBgImage/tghx.png
### 3. 小程序测试
- [ ] 在微信开发者工具中预览小程序
- [ ] 检查所有页面的图片是否正常显示
- [ ] 测试网络较慢时的图片加载体验
- [ ] 检查图片加载失败时的表现
### 4. 性能优化(可选)
- [ ] 在 `app.tsx` 中添加关键图片预加载
- [ ] 为图片组件添加 `onError` 错误处理
- [ ] 考虑添加图片懒加载
## 📝 使用示例
### 基础用法
```typescript
import { getBigImage } from '@/utils/imageHelper'
// 在组件中使用
<Image src={getBigImage('NO_DATA')} mode="aspectFit" />
```
### 预加载关键图片
```typescript
import { preloadImages, getBigImage } from '@/utils/imageHelper'
// 在应用启动时预加载
const keyImages = [
getBigImage('NO_DATA'),
getBigImage('SW_BG'),
getBigImage('USER_BG')
]
preloadImages(keyImages).then(() => {
console.log('关键图片预加载完成')
})
```
### 添加新图片
1. 上传图片到服务器:`http://39.101.75.71:8888/img/MiniBgImage/新图片.png`
2. 在 `BIG_IMAGES` 中添加映射:
```typescript
export const BIG_IMAGES = {
// 现有图片...
NEW_IMAGE: '新图片.png',
} as const
```
3. 在代码中使用:`getBigImage('NEW_IMAGE')`
## ⚠ 注意事项
1. **网络依赖**:图片加载现在依赖网络,首次加载会有延迟
2. **缓存机制**:小程序会自动缓存网络图片
3. **错误处理**:建议为重要图片添加加载失败的兜底处理
4. **包体积**:本地大图已移除,小程序包体积显著减少
## 🚀 预期收益
- ✅ **包体积减少**:移除本地大图,预计减少包体积 500KB+
- ✅ **便于管理**:图片统一在服务器管理,更新无需重新发布
- ✅ **加载灵活**:可以动态更新图片内容
- ✅ **代码整洁**:统一的图片引用方式,便于维护

116
src/api/apidoc/login.md

@ -0,0 +1,116 @@
## 微信登录
**接口地址**:`/api/v2/WechatAuth/login-wechat`
**请求方式**:`POST`
**请求数据类型**:`application/x-www-form-urlencoded,application/json,text/json,application/*+json`
**响应数据类型**:`text/plain`
**接口描述**:
**请求示例**:
```javascript
{
"code": ""
}
```
**请求参数**:
**请求参数**:
| 参数名称 | 参数说明 | 请求类型 | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|wechatLoginRequest|微信授权请求模型\n\n用于微信登录|body|true|WechatLoginRequest|WechatLoginRequest|
|&emsp;&emsp;code|微信登录凭证code\n\n通过 wx.login() 获取的临时登录凭证||true|string||
**响应状态**:
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
|200|OK|WechatAuthResultDtoOpResult|
**响应参数**:
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|message|描述消息|string||
|code|返回值|integer(int32)|integer(int32)|
|success|是否成功|boolean||
|data||WechatAuthResultDto|WechatAuthResultDto|
|&emsp;&emsp;success|是否成功|boolean||
|&emsp;&emsp;customer|客户Dto|CustomerDto|CustomerDto|
|&emsp;&emsp;&emsp;&emsp;id|id|integer||
|&emsp;&emsp;&emsp;&emsp;sn|客户编码|string||
|&emsp;&emsp;&emsp;&emsp;wxOpenId|OpenId|string||
|&emsp;&emsp;&emsp;&emsp;name|名称|string||
|&emsp;&emsp;&emsp;&emsp;type|类型 0会员 1VIP|integer||
|&emsp;&emsp;&emsp;&emsp;typeName|类型描述|string||
|&emsp;&emsp;&emsp;&emsp;amount|余额|number||
|&emsp;&emsp;&emsp;&emsp;wchart|微信号|string||
|&emsp;&emsp;&emsp;&emsp;phone|手机号|string||
|&emsp;&emsp;&emsp;&emsp;bpSn|店铺编码|string||
|&emsp;&emsp;&emsp;&emsp;bpInfoName|店铺名称|string||
|&emsp;&emsp;&emsp;&emsp;instSn|机构编码|string||
|&emsp;&emsp;&emsp;&emsp;ownerName|机构名称|string||
|&emsp;&emsp;&emsp;&emsp;createTime|创建时间|string||
|&emsp;&emsp;&emsp;&emsp;lastLoginTime||string||
|&emsp;&emsp;&emsp;&emsp;wxNickName|微信昵称|string||
|&emsp;&emsp;&emsp;&emsp;wxAvatarUrl|头像URL|string||
|&emsp;&emsp;operationResult|操作结果|||
|&emsp;&emsp;isNewCustomer|是否是新用户|boolean||
|&emsp;&emsp;message|消息|string||
**响应示例**:
```javascript
{
"message": "",
"code": 0,
"success": true,
"data": {
"success": true,
"customer": {
"id": 0,
"sn": "",
"wxOpenId": "",
"name": "",
"type": 0,
"typeName": "",
"amount": 0,
"wchart": "",
"phone": "",
"bpSn": "",
"bpInfoName": "",
"instSn": "",
"ownerName": "",
"createTime": "",
"lastLoginTime": "",
"wxNickName": "",
"wxAvatarUrl": ""
},
"operationResult": {},
"isNewCustomer": true,
"message": ""
}
}
```

19
src/api/login.ts

@ -0,0 +1,19 @@
/**
*
*/
import request from '../services/request'
import { WechatLoginRequest, WechatAuthResponse } from '../types/login'
/**
*
* @param code
*/
export const wechatLogin = (code: string, grpSn=''): Promise<WechatAuthResponse> => {
const data: WechatLoginRequest = { code,grpSn }
return request({
url: '/WechatAuth/login-wechat',
method: 'POST',
data
})
}

5
src/components/EmptyData/index.tsx

@ -2,10 +2,9 @@
* *
*/ */
import { View, Text, Image } from '@tarojs/components' import { View, Text, Image } from '@tarojs/components'
import { getBigImage } from '@/utils/imageHelper'
import './index.scss' import './index.scss'
const noDataImg = require('../../assets/big/noData.png')
interface EmptyDataProps { interface EmptyDataProps {
text?: string text?: string
imageSize?: 'small' | 'medium' | 'large' imageSize?: 'small' | 'medium' | 'large'
@ -19,7 +18,7 @@ const EmptyData: React.FC<EmptyDataProps> = ({
<View className="empty-data-component"> <View className="empty-data-component">
<Image <Image
className={`empty-image ${imageSize}`} className={`empty-image ${imageSize}`}
src={noDataImg} src={getBigImage('NO_DATA')}
mode="aspectFit" mode="aspectFit"
/> />
<Text className="empty-text">{text}</Text> <Text className="empty-text">{text}</Text>

6
src/config/index.ts

@ -7,6 +7,9 @@
// 直接配置,如需修改请更改此处 // 直接配置,如需修改请更改此处
const API_BASE_URL = 'https://39.101.75.71:8889' const API_BASE_URL = 'https://39.101.75.71:8889'
// 大图服务器地址 http://39.101.75.71:8888/img/MiniBgImage/swBG.png
const BIG_IMAGE_URL = 'http://39.101.75.71:8888/img/MiniBgImage'
// API 版本 // API 版本
const API_VERSION = 'v2' const API_VERSION = 'v2'
@ -20,6 +23,9 @@ export const config = {
// API 基础地址 // API 基础地址
apiBaseUrl: API_BASE_URL, apiBaseUrl: API_BASE_URL,
// 大图服务器地址
bigImageUrl: BIG_IMAGE_URL,
// API 版本 // API 版本
apiVersion: API_VERSION, apiVersion: API_VERSION,

6
src/pageStore/storeDetail/index.tsx

@ -22,7 +22,7 @@ import payVipBg from '../images/payVip.png'
import billiardTabBg from '../images/billiardtab.png' import billiardTabBg from '../images/billiardtab.png'
import paiTabBg from '../images/paitab.png' import paiTabBg from '../images/paitab.png'
import storeBoxBg from '../images/storeBox.png' import storeBoxBg from '../images/storeBox.png'
import swBG from '../../assets/big/swBG.png' import { getBigImage } from '@/utils/imageHelper'
// 台球桌状态类型 // 台球桌状态类型
type TableStatus = 'available' | 'using' | 'waiting' | 'reserved' type TableStatus = 'available' | 'using' | 'waiting' | 'reserved'
@ -280,14 +280,14 @@ const StoreDetail = () => {
<Swiper.Item> <Swiper.Item>
<Image <Image
className="swiper_image" className="swiper_image"
src={swBG} src={getBigImage('SW_BG')}
mode="aspectFill" mode="aspectFill"
/> />
</Swiper.Item> </Swiper.Item>
<Swiper.Item> <Swiper.Item>
<Image <Image
className="swiper_image" className="swiper_image"
src={swBG} src={getBigImage('SW_BG')}
mode="aspectFill" mode="aspectFill"
/> />
</Swiper.Item> </Swiper.Item>

15
src/pages/index/index.tsx

@ -9,10 +9,7 @@ import { Swiper } from '@taroify/core'
import { LocationOutlined } from '@taroify/icons' import { LocationOutlined } from '@taroify/icons'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { userStore } from '../../store' import { userStore } from '../../store'
import swBG from '../../assets/big/swBG.png' import { getBigImage, BIG_IMAGES } from '@/utils/imageHelper'
import smkt from '../../assets/big/smkt.png'
import fjmd from '../../assets/big/fjmd.png'
import tghx from '../../assets/big/tghx.png'
import './index.scss' import './index.scss'
const Index = observer(() => { const Index = observer(() => {
@ -105,14 +102,14 @@ const Index = observer(() => {
<Swiper.Item> <Swiper.Item>
<Image <Image
className="swiper-image" className="swiper-image"
src={swBG} src={getBigImage('SW_BG')}
mode="aspectFill" mode="aspectFill"
/> />
</Swiper.Item> </Swiper.Item>
<Swiper.Item> <Swiper.Item>
<Image <Image
className="swiper-image" className="swiper-image"
src={swBG} src={getBigImage('SW_BG')}
mode="aspectFill" mode="aspectFill"
/> />
</Swiper.Item> </Swiper.Item>
@ -126,7 +123,7 @@ const Index = observer(() => {
<View className="scan-card" onClick={handleScanClick}> <View className="scan-card" onClick={handleScanClick}>
<Image <Image
className="card-bg" className="card-bg"
src={smkt} src={getBigImage('SMKT')}
mode="aspectFill" mode="aspectFill"
/> />
<View className="card-text-content"> <View className="card-text-content">
@ -141,7 +138,7 @@ const Index = observer(() => {
<View className="nearby-store-card" onClick={handleNearbyStoreClick}> <View className="nearby-store-card" onClick={handleNearbyStoreClick}>
<Image <Image
className="card-bg" className="card-bg"
src={fjmd} src={getBigImage('FJMD')}
mode="aspectFill" mode="aspectFill"
/> />
<View className="card-content"> <View className="card-content">
@ -158,7 +155,7 @@ const Index = observer(() => {
<View className="group-buy-card" onClick={handleGroupBuyClick}> <View className="group-buy-card" onClick={handleGroupBuyClick}>
<Image <Image
className="card-bg" className="card-bg"
src={tghx} src={getBigImage('TGHX')}
mode="aspectFill" mode="aspectFill"
/> />
<View className="card-text-content"> <View className="card-text-content">

10
src/pages/message/index.tsx

@ -6,7 +6,7 @@ import { useLoad } from '@tarojs/taro'
import { observer } from 'mobx-react' import { observer } from 'mobx-react'
import { useState } from 'react' import { useState } from 'react'
import { Tabs, Badge } from '@taroify/core' import { Tabs, Badge } from '@taroify/core'
import noDataImg from '../../assets/big/noData.png' import { getBigImage } from '@/utils/imageHelper'
import './index.scss' import './index.scss'
// 消息数据接口 // 消息数据接口
@ -85,7 +85,7 @@ const MessageIndex = observer(() => {
content: '金风科技上课福建省咖啡机看得见风可视对讲开发机十大费晶...', content: '金风科技上课福建省咖啡机看得见风可视对讲开发机十大费晶...',
time: '2025-01-10', time: '2025-01-10',
createTime: currentTime - 1 * 60 * 60 * 1000, createTime: currentTime - 1 * 60 * 60 * 1000,
image: require('../../assets/big/fjmd.png'), image: getBigImage('FJMD'),
}, },
{ {
id: '5', id: '5',
@ -100,7 +100,7 @@ const MessageIndex = observer(() => {
content: '来力台球厅新店盛大开业,前100名顾客可享受5折优惠,还有精美礼品相送!', content: '来力台球厅新店盛大开业,前100名顾客可享受5折优惠,还有精美礼品相送!',
time: '2025-01-09 20:00:00', time: '2025-01-09 20:00:00',
createTime: currentTime - 4 * 60 * 60 * 1000, createTime: currentTime - 4 * 60 * 60 * 1000,
image: require('../../assets/big/smkt.png'), image: getBigImage('SMKT'),
}, },
{ {
id: '12', id: '12',
@ -115,7 +115,7 @@ const MessageIndex = observer(() => {
content: '本周末会员专享8折优惠,非会员可享受9折优惠,欢迎预约!', content: '本周末会员专享8折优惠,非会员可享受9折优惠,欢迎预约!',
time: '2025-01-09 10:00:00', time: '2025-01-09 10:00:00',
createTime: currentTime - 14 * 60 * 60 * 1000, createTime: currentTime - 14 * 60 * 60 * 1000,
image: require('../../assets/big/tghx.png'), image: getBigImage('TGHX'),
}, },
]) ])
@ -183,7 +183,7 @@ const MessageIndex = observer(() => {
// 渲染空状态 // 渲染空状态
const renderEmptyState = () => ( const renderEmptyState = () => (
<View className="empty-state"> <View className="empty-state">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
<Text className="empty-text">~</Text> <Text className="empty-text">~</Text>
</View> </View>
) )

24
src/pages/user/index.scss

@ -97,6 +97,25 @@
color: #000; color: #000;
flex-shrink: 0; flex-shrink: 0;
} }
// 登录按钮样式
.login-btn {
margin-left: 20px;
padding: 8px 16px;
font-size: 24rpx;
border-radius: 20px;
flex-shrink: 0;
min-width: 80px;
height: 36px;
:global {
.taroify-button {
height: 100%;
font-size: 24rpx;
border-radius: 20px;
}
}
}
} }
// 成长值 // 成长值
@ -117,6 +136,11 @@
margin-right: 10px; margin-right: 10px;
flex-shrink: 0; flex-shrink: 0;
} }
.no-data {
font-size: 25rpx;
color: #666666;
}
} }
} }
} }

127
src/pages/user/index.tsx

@ -5,12 +5,13 @@ import { View, Text, Image } from '@tarojs/components'
import { useLoad } from '@tarojs/taro' import { useLoad } from '@tarojs/taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import { observer } from 'mobx-react' import { observer } from 'mobx-react'
import { Image as TaroifyImage } from '@taroify/core' import { Image as TaroifyImage, Button } from '@taroify/core'
import { userStore } from '../../store' import { userStore } from '../../store'
import { wechatLogin } from '../../api/login'
import { getBigImage } from '@/utils/imageHelper'
import './index.scss' import './index.scss'
// 导入图片 // 导入小图标(保留本地引用)
import userBg from '../../assets/big/user_bg.png'
import userEdite from '../../assets/icon/user_edite.png' import userEdite from '../../assets/icon/user_edite.png'
import zuanIcon from '../../assets/icon/zuan.png' import zuanIcon from '../../assets/icon/zuan.png'
import sunIcon from '../../assets/icon/sun.png' import sunIcon from '../../assets/icon/sun.png'
@ -70,10 +71,47 @@ const UserIndex = observer(() => {
}) })
} }
// 微信登录
const handleWechatLogin = async () => {
let grpSn = '212'
try {
userStore.setLoading(true)
// 调用微信登录获取code
const loginResult = await Taro.login()
if (!loginResult.code) {
throw new Error('获取微信登录凭证失败')
}
// 调用后端接口
const response = await wechatLogin(loginResult.code,grpSn)
if (response.success && response.data.success) {
// 保存用户信息到store
userStore.setUserInfoFromWechat(response.data.customer)
Taro.showToast({
title: response.data.isNewCustomer ? '注册成功' : '登录成功',
icon: 'success'
})
} else {
throw new Error(response.data.message || response.message || '登录失败')
}
} catch (error: any) {
console.error('微信登录失败:', error)
Taro.showToast({
title: error.message || '登录失败,请重试',
icon: 'error'
})
} finally {
userStore.setLoading(false)
}
}
return ( return (
<View className="user-page"> <View className="user-page">
{/* 上部分:背景图和用户信息 */} {/* 上部分:背景图和用户信息 */}
<View className="user-header" style={{ backgroundImage: `url(${userBg})` }}> <View className="user-header" style={{ backgroundImage: `url(${getBigImage('USER_BG')})` }}>
<View className="user-content"> <View className="user-content">
{/* 用户信息模块 */} {/* 用户信息模块 */}
<View className="user-info"> <View className="user-info">
@ -83,7 +121,7 @@ const UserIndex = observer(() => {
<View className="avatar-wrapper"> <View className="avatar-wrapper">
<TaroifyImage <TaroifyImage
className="avatar" className="avatar"
src={userStore.userInfo?.avatar || myAvatar} src={userStore.isLoggedIn && userStore.userInfo?.avatar ? userStore.userInfo.avatar : myAvatar}
round round
mode="aspectFill" mode="aspectFill"
/> />
@ -91,30 +129,61 @@ const UserIndex = observer(() => {
{/* 右侧信息 */} {/* 右侧信息 */}
<View className="profile-right"> <View className="profile-right">
{/* 用户名称和编辑按钮 */} {userStore.isLoggedIn ? (
<View className="name-row"> <>
<View className="name-edit-wrapper"> {/* 已登录状态 */}
<Text className="username">{userStore.userInfo?.nickname || '旋转跳跃的球圣'}</Text> {/* 用户名称和编辑按钮 */}
<Image <View className="name-row">
className="edit-icon" <View className="name-edit-wrapper">
src={userEdite} <Text className="username">{userStore.userInfo?.nickname || '微信用户'}</Text>
onClick={handleEdit} <Image
/> className="edit-icon"
</View> src={userEdite}
<View className="vip-badge" onClick={handleVipClick}>VIP</View> onClick={handleEdit}
</View> />
</View>
{/* 成长值 */} <View className="vip-badge" onClick={handleVipClick}>
<View className="growth-row"> {userStore.userInfo?.typeName || 'VIP'}
<Text className="growth-label"></Text> </View>
{growthIcons.map((icon, index) => ( </View>
<Image
key={index} {/* 成长值 */}
className="growth-icon" <View className="growth-row">
src={icon} <Text className="growth-label"></Text>
/> {growthIcons.map((icon, index) => (
))} <Image
</View> key={index}
className="growth-icon"
src={icon}
/>
))}
</View>
</>
) : (
<>
{/* 未登录状态 */}
<View className="name-row">
<View className="name-edit-wrapper">
<Text className="username"></Text>
</View>
<Button
className="login-btn"
size="small"
color="primary"
loading={userStore.isLoading}
onClick={handleWechatLogin}
>
{userStore.isLoading ? '登录中...' : '微信登录'}
</Button>
</View>
{/* 成长值显示为暂无 */}
<View className="growth-row">
<Text className="growth-label"></Text>
<Text className="no-data"></Text>
</View>
</>
)}
</View> </View>
</View> </View>

4
src/pagesOrder/accountBalance/components/BalanceList.tsx

@ -5,7 +5,7 @@ import { View, Text, Image } from '@tarojs/components'
import { FC } from 'react' import { FC } from 'react'
import './BalanceList.scss' import './BalanceList.scss'
const noDataImg = require('../../../assets/big/noData.png') import { getBigImage } from '@/utils/imageHelper'
interface StoreBalanceItem { interface StoreBalanceItem {
id: string id: string
@ -33,7 +33,7 @@ const BalanceList: FC<BalanceListProps> = ({ balanceList, onRecharge, onViewCabi
<View className="balance-list"> <View className="balance-list">
{balanceList.length === 0 ? ( {balanceList.length === 0 ? (
<View className="empty-data"> <View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
</View> </View>
) : ( ) : (
<> <>

4
src/pagesOrder/accountBalance/components/OpenRecordList.tsx

@ -8,7 +8,7 @@ import { Popup, DatetimePicker } from '@taroify/core'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import './OpenRecordList.scss' import './OpenRecordList.scss'
const noDataImg = require('../../../assets/big/noData.png') import { getBigImage } from '@/utils/imageHelper'
interface OpenRecordItem { interface OpenRecordItem {
id: string id: string
@ -135,7 +135,7 @@ const OpenRecordList: FC<OpenRecordListProps> = ({ recordList }) => {
<View className="open-record-list"> <View className="open-record-list">
{dataList.length === 0 ? ( {dataList.length === 0 ? (
<View className="empty-data"> <View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
</View> </View>
) : ( ) : (
<> <>

4
src/pagesOrder/accountBalance/components/RechargeRecordList.tsx

@ -8,7 +8,7 @@ import { Popup, DatetimePicker } from '@taroify/core'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import './RechargeRecordList.scss' import './RechargeRecordList.scss'
const noDataImg = require('../../../assets/big/noData.png') import { getBigImage } from '@/utils/imageHelper'
interface RechargeRecordItem { interface RechargeRecordItem {
id: string id: string
@ -114,7 +114,7 @@ const RechargeRecordList: FC<RechargeRecordListProps> = ({ recordList }) => {
<View className="recharge-record-list"> <View className="recharge-record-list">
{dataList.length === 0 ? ( {dataList.length === 0 ? (
<View className="empty-data"> <View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
</View> </View>
) : ( ) : (
<> <>

4
src/pagesOrder/rechargeDetail/index.tsx

@ -8,7 +8,7 @@ import { Popup, DatetimePicker } from '@taroify/core'
import Taro, { useLoad } from '@tarojs/taro' import Taro, { useLoad } from '@tarojs/taro'
import './index.scss' import './index.scss'
const noDataImg = require('../../assets/big/noData.png') import { getBigImage } from '@/utils/imageHelper'
interface RechargeRecordItem { interface RechargeRecordItem {
id: string id: string
@ -133,7 +133,7 @@ const RechargeDetail = () => {
<View className="recharge-detail-page"> <View className="recharge-detail-page">
{dataList.length === 0 ? ( {dataList.length === 0 ? (
<View className="empty-data"> <View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
</View> </View>
) : ( ) : (
<> <>

4
src/pagesUser/orderList/index.tsx

@ -9,7 +9,7 @@ import { ArrowDown } from '@taroify/icons'
import FilterPopup from './components/FilterPopup' import FilterPopup from './components/FilterPopup'
import './index.scss' import './index.scss'
const noDataImg = require('../../assets/big/noData.png') import { getBigImage } from '@/utils/imageHelper'
// 订单数据接口 // 订单数据接口
interface OrderItem { interface OrderItem {
@ -141,7 +141,7 @@ const OrderList = () => {
<View className="order-list"> <View className="order-list">
{filteredOrders.length === 0 ? ( {filteredOrders.length === 0 ? (
<View className="empty-data"> <View className="empty-data">
<Image className="empty-image" src={noDataImg} mode="aspectFit" /> <Image className="empty-image" src={getBigImage('NO_DATA')} mode="aspectFit" />
<Text className="empty-text">~</Text> <Text className="empty-text">~</Text>
</View> </View>
) : ( ) : (

60
src/store/user.ts

@ -3,21 +3,49 @@
*/ */
import { makeAutoObservable } from 'mobx' import { makeAutoObservable } from 'mobx'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import { CustomerDto } from '../types/login'
export interface UserInfo { export interface UserInfo {
id: string id: number
nickname: string nickname: string
avatar?: string avatar?: string
phone?: string phone?: string
wxOpenId?: string
name?: string
type?: number
typeName?: string
amount?: number
wchart?: string
bpSn?: string
bpInfoName?: string
instSn?: string
ownerName?: string
createTime?: string
lastLoginTime?: string
} }
class UserStore { class UserStore {
userInfo: UserInfo | null = null userInfo: UserInfo | null = null
isLoggedIn: boolean = false isLoggedIn: boolean = false
balance: number = 0 balance: number = 0
isLoading: boolean = false
constructor() { constructor() {
makeAutoObservable(this) makeAutoObservable(this)
this.initUserInfo()
}
// 初始化用户信息(从本地存储读取)
initUserInfo = () => {
try {
const userInfo = Taro.getStorageSync('userInfo')
if (userInfo) {
this.userInfo = userInfo
this.isLoggedIn = true
}
} catch (error) {
console.error('读取用户信息失败:', error)
}
} }
setUserInfo = (userInfo: UserInfo | null) => { setUserInfo = (userInfo: UserInfo | null) => {
@ -31,12 +59,42 @@ class UserStore {
} }
} }
// 从微信登录结果设置用户信息
setUserInfoFromWechat = (customer: CustomerDto) => {
const userInfo: UserInfo = {
id: customer.id,
nickname: customer.wxNickName || customer.name || '微信用户',
avatar: customer.wxAvatarUrl,
phone: customer.phone,
wxOpenId: customer.wxOpenId,
name: customer.name,
type: customer.type,
typeName: customer.typeName,
amount: customer.amount,
wchart: customer.wchart,
bpSn: customer.bpSn,
bpInfoName: customer.bpInfoName,
instSn: customer.instSn,
ownerName: customer.ownerName,
createTime: customer.createTime,
lastLoginTime: customer.lastLoginTime
}
this.setUserInfo(userInfo)
this.setBalance(customer.amount || 0)
}
setBalance = (balance: number) => { setBalance = (balance: number) => {
this.balance = balance this.balance = balance
} }
setLoading = (loading: boolean) => {
this.isLoading = loading
}
logout = () => { logout = () => {
this.setUserInfo(null) this.setUserInfo(null)
this.balance = 0
Taro.removeStorageSync('token') Taro.removeStorageSync('token')
Taro.removeStorageSync('userInfo') Taro.removeStorageSync('userInfo')
} }

47
src/types/login.ts

@ -0,0 +1,47 @@
/**
*
*/
// 微信登录请求参数
export interface WechatLoginRequest {
code: string
grpSn?: string
}
// 客户信息
export interface CustomerDto {
id: number
sn: string
wxOpenId: string
name: string
type: number
typeName: string
amount: number
wchart: string
phone: string
bpSn: string
bpInfoName: string
instSn: string
ownerName: string
createTime: string
lastLoginTime: string
wxNickName: string
wxAvatarUrl: string
}
// 微信登录结果数据
export interface WechatAuthResultDto {
success: boolean
customer: CustomerDto
operationResult: any
isNewCustomer: boolean
message: string
}
// 微信登录响应
export interface WechatAuthResponse {
message: string
code: number
success: boolean
data: WechatAuthResultDto
}

176
src/utils/README_imageHelper.md

@ -0,0 +1,176 @@
# 图片工具使用说明
## 概述
`imageHelper.ts` 是一个用于管理服务器端大图资源的工具类,可以将本地大图迁移到服务器,减少小程序包体积。
## 配置
图片服务器地址在 `src/config/index.ts` 中配置:
```typescript
const BIG_IMAGE_URL = 'http://39.101.75.71:8888/img/MiniBgImage'
```
## 使用方法
### 1. 基础用法 - 通过文件名获取
```typescript
import { getBigImageUrl } from '@/utils/imageHelper'
// 直接传入文件名
const imageUrl = getBigImageUrl('noData.png')
// 结果: http://39.101.75.71:8888/img/MiniBgImage/noData.png
```
### 2. 推荐用法 - 使用预定义常量
```typescript
import { getBigImage, BIG_IMAGES } from '@/utils/imageHelper'
// 使用预定义的图片键
const noDataUrl = getBigImage('NO_DATA')
const backgroundUrl = getBigImage('SW_BG')
// 查看所有可用的图片键
console.log(BIG_IMAGES)
// {
// NO_DATA: 'noData.png',
// SW_BG: 'swBG.png',
// USER_BG: 'user_bg.png',
// FJMD: 'fjmd.png',
// SMKT: 'smkt.png',
// TGHX: 'tghx.png'
// }
```
### 3. 批量获取
```typescript
import { getBigImagesUrl } from '@/utils/imageHelper'
const urls = getBigImagesUrl(['noData.png', 'swBG.png'])
// 结果: [
// 'http://39.101.75.71:8888/img/MiniBgImage/noData.png',
// 'http://39.101.75.71:8888/img/MiniBgImage/swBG.png'
// ]
```
### 4. 图片预加载
```typescript
import { preloadImages, getBigImage } from '@/utils/imageHelper'
// 预加载关键图片
const keyImages = [
getBigImage('NO_DATA'),
getBigImage('SW_BG'),
getBigImage('USER_BG')
]
preloadImages(keyImages).then(() => {
console.log('关键图片预加载完成')
})
```
## 迁移指南
### 原来的写法:
```typescript
// ❌ 旧写法 - 本地图片
import noDataImg from '../../assets/big/noData.png'
const noDataImg = require('../../assets/big/noData.png')
<Image src={noDataImg} />
```
### 现在的写法:
```typescript
// ✅ 新写法 - 服务器图片
import { getBigImage } from '@/utils/imageHelper'
<Image src={getBigImage('NO_DATA')} />
```
## 在组件中的使用示例
### 空数据组件
```typescript
import { getBigImage } from '@/utils/imageHelper'
const EmptyData = () => {
return (
<View className="empty-data">
<Image
src={getBigImage('NO_DATA')}
mode="aspectFit"
/>
<Text>没有数据哦~</Text>
</View>
)
}
```
### 轮播图组件
```typescript
import { getBigImage } from '@/utils/imageHelper'
const Banner = () => {
return (
<Swiper>
<Swiper.Item>
<Image
src={getBigImage('SW_BG')}
mode="aspectFill"
/>
</Swiper.Item>
</Swiper>
)
}
```
## 添加新图片
1. **将图片上传到服务器**:`http://39.101.75.71:8888/img/MiniBgImage/新图片.png`
2. **在 `BIG_IMAGES` 中添加映射**
```typescript
export const BIG_IMAGES = {
// 现有图片...
NEW_IMAGE: '新图片.png', // 添加新图片
} as const
```
3. **在代码中使用**
```typescript
<Image src={getBigImage('NEW_IMAGE')} />
```
## 注意事项
1. **文件名规范**:确保服务器上的文件名与代码中定义的一致
2. **网络依赖**:图片加载依赖网络,建议添加加载失败的兜底处理
3. **缓存策略**:小程序会自动缓存网络图片,但首次加载会有延迟
4. **预加载**:对于关键图片,建议在应用启动时预加载
## 错误处理
```typescript
// 添加图片加载失败的处理
<Image
src={getBigImage('NO_DATA')}
mode="aspectFit"
onError={() => {
console.warn('图片加载失败')
// 可以设置默认图片或隐藏图片
}}
/>
```
## 性能优化建议
1. **按需加载**:只在需要时获取图片 URL
2. **预加载关键图片**:在应用启动时预加载常用图片
3. **图片压缩**:确保服务器上的图片已经过压缩优化
4. **CDN 加速**:考虑使用 CDN 加速图片访问

108
src/utils/imageHelper.ts

@ -0,0 +1,108 @@
/**
*
*
*/
import { config } from '@/config'
/**
* URL
* @param fileName
* @returns URL
*
* @example
* getBigImageUrl('noData.png') // 返回: http://39.101.75.71:8888/img/MiniBgImage/noData.png
* getBigImageUrl('swBG.png') // 返回: http://39.101.75.71:8888/img/MiniBgImage/swBG.png
*/
export const getBigImageUrl = (fileName: string): string => {
// 确保文件名不为空
if (!fileName) {
console.warn('图片文件名不能为空')
return ''
}
// 移除可能的前导斜杠
const cleanFileName = fileName.startsWith('/') ? fileName.slice(1) : fileName
return `${config.bigImageUrl}/${cleanFileName}`
}
/**
*
* 便使
*/
export const BIG_IMAGES = {
// 空数据占位图
NO_DATA: 'noData.png',
// 背景图片
SW_BG: 'swBG.png',
USER_BG: 'user_bg.png',
// 功能图片
FJMD: 'fjmd.png',
SMKT: 'smkt.png',
TGHX: 'tghx.png',
} as const
/**
* URL
* @param imageKey
* @returns URL
*
* @example
* getBigImage('NO_DATA') // 返回: http://39.101.75.71:8888/img/MiniBgImage/noData.png
* getBigImage('SW_BG') // 返回: http://39.101.75.71:8888/img/MiniBgImage/swBG.png
*/
export const getBigImage = (imageKey: keyof typeof BIG_IMAGES): string => {
const fileName = BIG_IMAGES[imageKey]
if (!fileName) {
console.warn(`未找到图片键: ${imageKey}`)
return ''
}
return getBigImageUrl(fileName)
}
/**
* URL
* @param fileNames
* @returns URL
*
* @example
* getBigImagesUrl(['noData.png', 'swBG.png'])
* // 返回: ['http://39.101.75.71:8888/img/MiniBgImage/noData.png', 'http://39.101.75.71:8888/img/MiniBgImage/swBG.png']
*/
export const getBigImagesUrl = (fileNames: string[]): string[] => {
return fileNames.map(fileName => getBigImageUrl(fileName))
}
/**
*
* @param imageUrls URL
* @returns Promise resolve
*/
export const preloadImages = (imageUrls: string[]): Promise<void[]> => {
const promises = imageUrls.map(url => {
return new Promise<void>((resolve, reject) => {
const img = new Image()
img.onload = () => resolve()
img.onerror = () => {
console.warn(`图片预加载失败: ${url}`)
resolve() // 即使失败也 resolve,避免阻塞其他图片
}
img.src = url
})
})
return Promise.all(promises)
}
// 默认导出主要方法
export default {
getBigImageUrl,
getBigImage,
getBigImagesUrl,
preloadImages,
BIG_IMAGES
}

36
src/utils/testImageHelper.ts

@ -0,0 +1,36 @@
/**
*
* 访
*/
import { getBigImage, getBigImageUrl, BIG_IMAGES } from './imageHelper'
// 测试所有预定义图片的 URL 生成
export const testImageUrls = () => {
console.log('=== 图片工具测试 ===')
// 测试预定义图片
Object.keys(BIG_IMAGES).forEach(key => {
const url = getBigImage(key as keyof typeof BIG_IMAGES)
console.log(`${key}: ${url}`)
})
// 测试直接文件名
console.log('\n=== 直接文件名测试 ===')
console.log('noData.png:', getBigImageUrl('noData.png'))
console.log('swBG.png:', getBigImageUrl('swBG.png'))
return {
NO_DATA: getBigImage('NO_DATA'),
SW_BG: getBigImage('SW_BG'),
USER_BG: getBigImage('USER_BG'),
FJMD: getBigImage('FJMD'),
SMKT: getBigImage('SMKT'),
TGHX: getBigImage('TGHX'),
}
}
// 在开发环境下自动运行测试
if (process.env.NODE_ENV === 'development') {
testImageUrls()
}
Loading…
Cancel
Save