feat(dealer): 添加微信客服功能并优化订单和客户页面
- 在 app.config.ts 中添加微信客服页面路由 - 修改订单页面标题为"电费收益" - 新增微信客服页面组件和样式 - 优化客户交易页面,添加搜索功能 - 更新二维码页面文案- 新增微信二维码图片说明文档
This commit is contained in:
@@ -65,7 +65,8 @@ export default defineAppConfig({
|
|||||||
"info",
|
"info",
|
||||||
"customer/index",
|
"customer/index",
|
||||||
"customer/add",
|
"customer/add",
|
||||||
"customer/trading"
|
"customer/trading",
|
||||||
|
"wechat/index"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|||||||
22
src/assets/images/README.md
Normal file
22
src/assets/images/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 微信二维码图片说明
|
||||||
|
|
||||||
|
请将以下微信二维码图片放置在此目录中:
|
||||||
|
|
||||||
|
## 需要的图片文件
|
||||||
|
|
||||||
|
1. `wechat-service-qr.png` - 客服微信二维码
|
||||||
|
2. `wechat-tech-qr.png` - 技术支持微信二维码
|
||||||
|
|
||||||
|
## 图片要求
|
||||||
|
|
||||||
|
- 格式:PNG 或 JPG
|
||||||
|
- 尺寸:建议 200x200 像素或更高分辨率
|
||||||
|
- 背景:建议白色背景,确保二维码清晰可见
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
这些图片将在 `/dealer/wechat` 页面中显示,用户可以长按保存并扫码添加微信好友。
|
||||||
|
|
||||||
|
## 自定义
|
||||||
|
|
||||||
|
如需修改图片路径或添加更多二维码,请编辑 `src/dealer/wechat/index.tsx` 文件中的 `qrCodeData` 数组。
|
||||||
BIN
src/assets/images/wechat-service-qr.png
Normal file
BIN
src/assets/images/wechat-service-qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
src/assets/images/wechat-tech-qr.png
Normal file
BIN
src/assets/images/wechat-tech-qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
@@ -1,12 +1,11 @@
|
|||||||
import {useState, useEffect, useCallback} from 'react'
|
import {useState, useEffect, useCallback} from 'react'
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import {Loading, Space} from '@nutui/nutui-react-taro'
|
import {Loading, Space, SearchBar} from '@nutui/nutui-react-taro'
|
||||||
import type {ShopDealerApply as UserType} from "@/api/shop/shopDealerApply/model";
|
import type {ShopDealerApply as UserType} from "@/api/shop/shopDealerApply/model";
|
||||||
import {
|
import {
|
||||||
CustomerStatus,
|
CustomerStatus,
|
||||||
mapApplyStatusToCustomerStatus,
|
mapApplyStatusToCustomerStatus,
|
||||||
} from '@/utils/customerStatus';
|
} from '@/utils/customerStatus';
|
||||||
import navTo from "@/utils/common";
|
|
||||||
import {pageShopDealerApply} from "@/api/shop/shopDealerApply";
|
import {pageShopDealerApply} from "@/api/shop/shopDealerApply";
|
||||||
|
|
||||||
// 扩展User类型,添加客户状态
|
// 扩展User类型,添加客户状态
|
||||||
@@ -17,7 +16,7 @@ interface CustomerUser extends UserType {
|
|||||||
const CustomerTrading = () => {
|
const CustomerTrading = () => {
|
||||||
const [list, setList] = useState<CustomerUser[]>([])
|
const [list, setList] = useState<CustomerUser[]>([])
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
const [searchValue, _] = useState<string>('')
|
const [searchValue, setSearchValue] = useState<string>('')
|
||||||
|
|
||||||
// 获取客户数据
|
// 获取客户数据
|
||||||
const fetchCustomerData = useCallback(async () => {
|
const fetchCustomerData = useCallback(async () => {
|
||||||
@@ -75,8 +74,7 @@ const CustomerTrading = () => {
|
|||||||
// 渲染客户项
|
// 渲染客户项
|
||||||
const renderCustomerItem = (customer: CustomerUser) => (
|
const renderCustomerItem = (customer: CustomerUser) => (
|
||||||
<View key={customer.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
<View key={customer.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||||
<View className="flex items-center"
|
<View className="flex items-center">
|
||||||
onClick={() => navTo(`/dealer/customer/add?id=${customer.applyId}`, true)}>
|
|
||||||
<View className="flex-1">
|
<View className="flex-1">
|
||||||
<View className="flex items-center justify-between mb-1">
|
<View className="flex items-center justify-between mb-1">
|
||||||
<Text className="font-semibold text-gray-800 mr-2">
|
<Text className="font-semibold text-gray-800 mr-2">
|
||||||
@@ -125,6 +123,16 @@ const CustomerTrading = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="min-h-screen bg-gray-50">
|
<View className="min-h-screen bg-gray-50">
|
||||||
|
{/* 搜索栏 */}
|
||||||
|
<View className="bg-white shadow-sm">
|
||||||
|
<SearchBar
|
||||||
|
placeholder="请输入搜索关键词"
|
||||||
|
value={searchValue}
|
||||||
|
onChange={(value) => setSearchValue(value)}
|
||||||
|
onSearch={(value) => setSearchValue(value)}
|
||||||
|
onClear={() => setSearchValue('')}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* 客户列表 */}
|
{/* 客户列表 */}
|
||||||
{renderCustomerList()}
|
{renderCustomerList()}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '分销订单'
|
navigationBarTitleText: '电费收益'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ const DealerOrders: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Empty description="暂无分销订单"/>
|
<Empty description="暂无收益"/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ const DealerQrcode: React.FC = () => {
|
|||||||
|
|
||||||
const inviteText = `🎉 邀请您加入我的团队!
|
const inviteText = `🎉 邀请您加入我的团队!
|
||||||
|
|
||||||
扫描小程序码或搜索"时里院子市集"小程序,即可享受优质商品和服务!
|
扫描小程序码或搜索"九云售电云"小程序,即可享受优质商品和服务!
|
||||||
|
|
||||||
💰 成为我的团队成员,一起赚取丰厚佣金
|
💰 成为我的团队成员,一起赚取丰厚佣金
|
||||||
🎁 新用户专享优惠等你来拿
|
🎁 新用户专享优惠等你来拿
|
||||||
|
|||||||
3
src/dealer/wechat/index.config.ts
Normal file
3
src/dealer/wechat/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '微信客服'
|
||||||
|
})
|
||||||
176
src/dealer/wechat/index.scss
Normal file
176
src/dealer/wechat/index.scss
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
.wechat-service-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
|
||||||
|
.service-tabs {
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.nut-tabs__titles {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nut-tabs__content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-container {
|
||||||
|
padding: 20px;
|
||||||
|
min-height: calc(100vh - 100px);
|
||||||
|
|
||||||
|
.qr-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.qr-title {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-description {
|
||||||
|
display: block;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 30px;
|
||||||
|
|
||||||
|
.qr-code-wrapper {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 30px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.qr-code-image {
|
||||||
|
width: 360px;
|
||||||
|
height: 360px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wechat-id {
|
||||||
|
display: block;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-tips {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.tip-title {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-item {
|
||||||
|
display: block;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.8;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-left: 10px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '•';
|
||||||
|
color: #07c160;
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式适配
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.wechat-service-page {
|
||||||
|
.qr-container {
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
.qr-content {
|
||||||
|
.qr-code-wrapper {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.qr-code-image {
|
||||||
|
width: 180px;
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深色模式适配
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.wechat-service-page {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
|
||||||
|
.service-tabs {
|
||||||
|
.nut-tabs__titles {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
border-bottom-color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-container {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
|
||||||
|
.qr-header {
|
||||||
|
.qr-title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-description {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-content {
|
||||||
|
.qr-code-wrapper {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
|
||||||
|
.qr-code-image {
|
||||||
|
border-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wechat-id {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-tips {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
|
||||||
|
.tip-title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-item {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/dealer/wechat/index.tsx
Normal file
68
src/dealer/wechat/index.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import {useEffect, useState} from 'react'
|
||||||
|
import {View, Text, Image} from '@tarojs/components'
|
||||||
|
import {Tabs} from '@nutui/nutui-react-taro'
|
||||||
|
import './index.scss'
|
||||||
|
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||||
|
import {CmsWebsiteField} from "@/api/cms/cmsWebsiteField/model";
|
||||||
|
|
||||||
|
interface QrCodeData {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
qrCode: string
|
||||||
|
wechatId: string
|
||||||
|
}
|
||||||
|
const WechatService = () => {
|
||||||
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
|
const [codes, setCodes] = useState<CmsWebsiteField[]>([])
|
||||||
|
|
||||||
|
const renderQRCode = (data: typeof codes[0]) => (
|
||||||
|
<View className="qr-container">
|
||||||
|
|
||||||
|
<View className="qr-content">
|
||||||
|
<View className="qr-code-wrapper">
|
||||||
|
<Image
|
||||||
|
src={`${data.value}`}
|
||||||
|
className="qr-code-image"
|
||||||
|
mode="aspectFit"
|
||||||
|
/>
|
||||||
|
<Text className="wechat-id">微信号:{data.comments}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="qr-tips">
|
||||||
|
<Text className="tip-title">使用说明:</Text>
|
||||||
|
<Text className="tip-item">1. 长按二维码保存到相册</Text>
|
||||||
|
<Text className="tip-item">2. 打开微信扫一扫</Text>
|
||||||
|
<Text className="tip-item">3. 选择相册中的二维码图片</Text>
|
||||||
|
<Text className="tip-item">4. 添加好友并发送验证消息</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
listCmsWebsiteField({ name: 'kefu'}).then(data => {
|
||||||
|
if(data){
|
||||||
|
setCodes(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="wechat-service-page">
|
||||||
|
<Tabs
|
||||||
|
value={activeTab}
|
||||||
|
onChange={(value) => setActiveTab(`${value}`)}
|
||||||
|
className="service-tabs"
|
||||||
|
>
|
||||||
|
{codes.map((item) => (
|
||||||
|
<Tabs.TabPane key={item.id} title={item.comments} value={item.id}>
|
||||||
|
{renderQRCode(item)}
|
||||||
|
</Tabs.TabPane>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WechatService
|
||||||
Reference in New Issue
Block a user