Compare commits

..

12 Commits

Author SHA1 Message Date
431a095e87 refactor(dealer): 移除未使用的导入和代码
- 移除了未使用的 Button 组件导入
- 移除了未使用的 gradientUtils 导入
- 移除了未使用的 error 和 refresh 变量
- 注释掉了
2026-01-08 13:28:13 +08:00
adc5c46184 feat(config): 更新开发环境API基础URL配置
- 将开发环境API基础URL从本地地址更新为线上测试地址
- 注释掉请求工具中的本地开发环境URL配置
- 统一开发环境API访问地址,便于调试和测试
2025-12-02 13:39:39 +08:00
77d9687cef feat(dealer): 优化经销商相关功能与代码细节
- 移除客户添加表单中公司名称输入框的maxLength限制
- 更新联系方式1标签为联系方式
- 在提现管理中引入Image组件并替换原生img标签
- 实现上传打款凭证的新逻辑,支持选择和上传图片
- 配置开发环境API基础URL为本地地址
- 修改订单查询条件,管理员可查看所有用户订单
- 注释掉不再使用的getResourceId方法
- 启用request.ts中的本地开发环境baseUrl配置
2025-12-02 08:51:26 +08:00
a38d1229e7 feat(dealer): 更新报备人字段并调整登录页面配置
- 报备人字段标签增加 "(ID)" 标识
- 设置报备人输入框类型为数字
- 禁用编辑模式下报备人字段
- 注释掉注册、忘记密码和服务配置相关路由及界面元素
- 移除登录页底部服务配置按钮
- 隐藏忘记密码与立即注册链接
2025-11-21 10:35:10 +08:00
365f4de585 feat(dealer): 更新报备人字段并调整登录页面配置
- 报备人字段标签增加 "(ID)" 标识
- 设置报备人输入框类型为数字
- 禁用编辑模式下报备人字段
- 注释掉注册、忘记密码和服务配置相关路由及界面元素
- 移除登录页底部服务配置按钮
- 隐藏忘记密码与立即注册链接
2025-11-21 10:25:03 +08:00
c67b2d4863 feat(admin): 添加实名审核管理页面及提现审核功能
- 新增实名审核管理页面,支持审核通过和驳回操作
- 实现审核记录的分页查询和状态展示
- 添加身份证照片预览功能
- 集成审核状态变更的通知推送
- 在经销商首页添加实名审核入口
- 用户提交实名认证后自动发送审核提醒给管理员
- 经销商提现申请时发送提现审核提醒
- 提现成功后发送到账通知给申请人
- 更新API基础URL配置
- 修复企业认证选项显示问题
- 优化审核流程用户体验
2025-11-20 17:54:09 +08:00
d9cd206da8 chore(config): 更新开发环境API基础URL
- 将开发环境的API基础URL从'http://127.0.0.1:9200/api'更改为'https://cms-api.websoft.top/api'

fix(withdraw): 修复提现页面表单重置逻辑

- 在提现成功后添加了await关键字以确保异步操作完成后再重置表单
2025-11-20 13:52:46 +08:00
c3044c6ea7 feat(dealer): 更新订单页面用户选择逻辑
- 修改开发环境 API 地址为本地地址
- 在 shopDealerReferee 模型中新增 roleId 和 isAdmin 字段
- 新增 users2 状态用于存储渠道员数据
- 调整 fetchUsers 方法,区分业务员和渠道员的数据获取逻辑
- 优化用户选择器,将渠道员选择绑定到 users2 数据源
- 修复渠道员选择按钮的显示逻辑和点击事件绑定问题
2025-11-20 10:27:11 +08:00
d1c205d41a Merge remote-tracking branch 'origin/master' 2025-11-20 09:13:28 +08:00
3d8c8146bc chore(config): 更新所有环境的API基础URL
- 将开发环境API基础URL从mp-api.websoft.top更改为cms-api.websoft.top
- 将生产环境API基础URL从mp-api.websoft.top更改为cms-api.websoft.top
- 将测试环境API基础URL从mp-api.websoft.top更改为cms-api.websoft.top
2025-11-20 09:13:16 +08:00
527aa9bf76 1 2025-11-17 20:26:45 +08:00
26a1689f5a 1 2025-11-17 18:15:11 +08:00
14 changed files with 674 additions and 102 deletions

View File

@@ -2,19 +2,19 @@
export const ENV_CONFIG = { export const ENV_CONFIG = {
// 开发环境 // 开发环境
development: { development: {
API_BASE_URL: 'https://mp-api.websoft.top/api', API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '开发环境', APP_NAME: '开发环境',
DEBUG: 'true', DEBUG: 'true',
}, },
// 生产环境 // 生产环境
production: { production: {
API_BASE_URL: 'https://mp-api.websoft.top/api', API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '九运售电云', APP_NAME: '九运售电云',
DEBUG: 'false', DEBUG: 'false',
}, },
// 测试环境 // 测试环境
test: { test: {
API_BASE_URL: 'https://mp-api.websoft.top/api', API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '测试环境', APP_NAME: '测试环境',
DEBUG: 'true', DEBUG: 'true',
} }

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '实名审核'
})

View File

@@ -0,0 +1,319 @@
import React, {useState, useEffect, useCallback} from 'react'
import {View, Text} from '@tarojs/components'
import {
Space,
Tabs,
Tag,
Empty,
Loading,
PullToRefresh,
Button,
Dialog,
Image,
ImagePreview,
TextArea
} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {useDealerUser} from '@/hooks/useDealerUser'
import {pageUserVerify, updateUserVerify} from '@/api/system/userVerify'
import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model'
import {UserVerify} from "@/api/system/userVerify/model";
const UserVeirfyAdmin: React.FC = () => {
const [activeTab, setActiveTab] = useState<string | number>(0)
const [loading, setLoading] = useState<boolean>(false)
const [refreshing, setRefreshing] = useState<boolean>(false)
const [list, setList] = useState<UserVerify[]>([])
const [rejectDialogVisible, setRejectDialogVisible] = useState<boolean>(false)
const [rejectReason, setRejectReason] = useState<string>('')
const [currentRecord, setCurrentRecord] = useState<ShopDealerWithdraw | null>(null)
const [showPreview, setShowPreview] = useState(false)
const [showPreview2, setShowPreview2] = useState(false)
const {dealerUser} = useDealerUser()
// Tab 切换处理函数
const handleTabChange = (value: string | number) => {
console.log('Tab切换到:', value)
setActiveTab(value)
// activeTab变化会自动触发useEffect重新获取数据无需手动调用
}
// 获取审核记录
const fetchWithdrawRecords = useCallback(async () => {
if (!dealerUser?.userId) return
try {
setLoading(true)
const currentStatus = Number(activeTab)
const result = await pageUserVerify({
page: 1,
limit: 100,
status: currentStatus // 后端筛选,提高性能
})
if (result?.list) {
const processedRecords = result.list.map(record => ({
...record
}))
setList(processedRecords)
}
} catch (error) {
console.error('获取审核记录失败:', error)
Taro.showToast({
title: '获取审核记录失败',
icon: 'none'
})
} finally {
setLoading(false)
}
}, [dealerUser?.userId, activeTab])
// 刷新数据
const handleRefresh = async () => {
setRefreshing(true)
await Promise.all([fetchWithdrawRecords()])
setRefreshing(false)
}
// 审核通过
const handleApprove = async (record: ShopDealerWithdraw) => {
try {
await updateUserVerify({
...record,
status: 1, // 审核通过
})
Taro.showToast({
title: '审核通过',
icon: 'success'
})
await fetchWithdrawRecords()
} catch (error: any) {
if (error !== 'cancel') {
console.error('审核通过失败:', error)
Taro.showToast({
title: error.message || '操作失败',
icon: 'none'
})
}
}
}
// 驳回申请
const handleReject = (record: ShopDealerWithdraw) => {
setCurrentRecord(record)
setRejectReason('')
setRejectDialogVisible(true)
}
// 确认驳回
const confirmReject = async () => {
if (!rejectReason.trim()) {
Taro.showToast({
title: '请输入驳回原因',
icon: 'none'
})
return
}
try {
await updateUserVerify({
...currentRecord!,
status: 2, // 驳回
comments: rejectReason.trim()
})
Taro.showToast({
title: '已驳回',
icon: 'success'
})
setRejectDialogVisible(false)
setCurrentRecord(null)
setRejectReason('')
await fetchWithdrawRecords()
} catch (error: any) {
console.error('驳回失败:', error)
Taro.showToast({
title: error.message || '操作失败',
icon: 'none'
})
}
}
// 初始化加载数据
useEffect(() => {
if (dealerUser?.userId) {
fetchWithdrawRecords().then()
}
}, [fetchWithdrawRecords])
const getStatusText = (status?: number) => {
switch (status) {
case 0:
return '待审核'
case 1:
return '审核通过'
case 2:
return '已驳回'
default:
return '未知'
}
}
const getStatusColor = (status?: number) => {
switch (status) {
case 0:
return 'warning'
case 1:
return 'success'
case 2:
return 'danger'
default:
return 'default'
}
}
const renderWithdrawRecords = () => {
console.log('渲染审核记录:', {loading, recordsCount: list.length, dealerUserId: dealerUser?.userId})
return (
<PullToRefresh
disabled={refreshing}
onRefresh={handleRefresh}
>
<View>
{loading ? (
<View className="text-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
) : list.length > 0 ? (
list.map(record => (
<View key={record.id} className="rounded-lg bg-gray-50 p-4 mb-3 shadow-sm">
<View className="flex justify-between items-start mb-3">
<Space direction={'vertical'}>
<Text className="font-semibold text-gray-800">
{record.realName}
</Text>
<Text className="font-normal text-sm text-gray-500">
{record.phone}
</Text>
<Text className="font-normal text-sm text-gray-500">
{record.idCard}
</Text>
</Space>
<Tag type={getStatusColor(record.status)}>
{getStatusText(record.status)}
</Tag>
</View>
<View className="flex gap-2 mb-2">
<Image src={record.sfz1} height={100} onClick={() => setShowPreview(true)}/>
<Image src={record.sfz2} height={100} onClick={() => setShowPreview2(true)}/>
</View>
<ImagePreview
autoPlay
images={[{src: `${record.sfz1}`}]}
visible={showPreview}
onClose={() => setShowPreview(false)}
/>
<ImagePreview
autoPlay
images={[{src: `${record.sfz1}`}]}
visible={showPreview2}
onClose={() => setShowPreview2(false)}
/>
<View className="text-xs text-gray-400">
<Text>{record.createTime}</Text>
{record.status == 1 && (
<Text className="block mt-1">
{record.updateTime}
</Text>
)}
{record.status == 2 && (
<Text className="block mt-1 text-red-500">
{record.comments}
</Text>
)}
</View>
{/* 操作按钮 */}
{record.status === 0 && (
<View className="flex gap-2 mt-3">
<Button
type="success"
size="small"
className="flex-1"
onClick={() => handleApprove(record)}
>
</Button>
<Button
type="danger"
size="small"
className="flex-1"
onClick={() => handleReject(record)}
>
</Button>
</View>
)}
</View>
))
) : (
<Empty description="暂无申请记录"/>
)}
</View>
</PullToRefresh>
)
}
return (
<View className="bg-gray-50 min-h-screen">
<Tabs value={activeTab} onChange={handleTabChange}>
<Tabs.TabPane title="待审核" value="0">
{renderWithdrawRecords()}
</Tabs.TabPane>
<Tabs.TabPane title="已通过" value="1">
{renderWithdrawRecords()}
</Tabs.TabPane>
<Tabs.TabPane title="已驳回" value="2">
{renderWithdrawRecords()}
</Tabs.TabPane>
</Tabs>
{/* 驳回原因对话框 */}
<Dialog
visible={rejectDialogVisible}
title="驳回原因"
onCancel={() => {
setRejectDialogVisible(false)
setCurrentRecord(null)
setRejectReason('')
}}
onConfirm={confirmReject}
>
<View className="p-4">
<TextArea
placeholder="请输入驳回原因"
value={rejectReason}
onChange={(value) => setRejectReason(value)}
maxLength={200}
rows={4}
/>
</View>
</Dialog>
</View>
)
}
export default UserVeirfyAdmin

View File

@@ -1,5 +1,7 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { ApiResult } from '@/api'; import type { ApiResult } from '@/api';
import {UserVerify} from "@/api/system/userVerify/model";
import {ShopDealerWithdraw} from "@/api/shop/shopDealerWithdraw/model";
/** /**
* 升级为管理员 * 升级为管理员
@@ -14,3 +16,46 @@ export async function pushByUpdateAdmin(userId: number) {
} }
return Promise.reject(new Error(res.message)); return Promise.reject(new Error(res.message));
} }
/**
* 通知管理员审核操作提醒
*/
export async function pushReviewReminder(data: UserVerify) {
const res = await request.post<ApiResult<unknown>>(
'/sdy/sdy-template-message/pushReviewReminder',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 通知管理员去提现审核操作提醒
*/
export async function pushWithdrawalReviewReminder(data: ShopDealerWithdraw) {
const res = await request.post<ApiResult<unknown>>(
'/sdy/sdy-template-message/pushWithdrawalReviewReminder',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* 提现成功通知
*/
export async function pushNoticeOfWithdrawalToAccount(data: ShopDealerWithdraw) {
const res = await request.post<ApiResult<unknown>>(
'/sdy/sdy-template-message/pushNoticeOfWithdrawalToAccount',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}

View File

@@ -39,5 +39,7 @@ export interface ShopDealerRefereeParam extends PageParam {
id?: number; id?: number;
dealerId?: number; dealerId?: number;
deleted?: number; deleted?: number;
roleId?: number;
isAdmin?: boolean;
keywords?: string; keywords?: string;
} }

View File

@@ -30,6 +30,8 @@ export interface ShopDealerWithdraw {
rejectReason?: string; rejectReason?: string;
// 来源客户端(APP、H5、小程序等) // 来源客户端(APP、H5、小程序等)
platform?: string; platform?: string;
// 手机号
phone?: string;
// 备注 // 备注
comments?: string; comments?: string;
// 租户id // 租户id

View File

@@ -10,9 +10,9 @@ export default defineAppConfig({
"root": "passport", "root": "passport",
"pages": [ "pages": [
"login", "login",
"register", // "register",
"forget", // "forget",
"setting", // "setting",
"agreement", "agreement",
"sms-login" "sms-login"
] ]
@@ -88,6 +88,7 @@ export default defineAppConfig({
"pages": [ "pages": [
"index", "index",
"article/index", "article/index",
"userVerify/index"
] ]
} }
], ],

View File

@@ -313,12 +313,12 @@ const AddShopDealerApply = () => {
<View className={'bg-gray-100 h-3'}></View> <View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}> <CellGroup style={{padding: '4px 0'}}>
<Form.Item name="dealerName" label="公司名称" initialValue={FormData?.dealerName} required> <Form.Item name="dealerName" label="公司名称" initialValue={FormData?.dealerName} required>
<Input placeholder="公司名称" maxLength={10} disabled={isEditMode}/> <Input placeholder="公司名称" disabled={isEditMode}/>
</Form.Item> </Form.Item>
<Form.Item name="realName" label="联系人" initialValue={FormData?.realName} required> <Form.Item name="realName" label="联系人" initialValue={FormData?.realName} required>
<Input placeholder="请输入联系人" disabled={isEditMode}/> <Input placeholder="请输入联系人" disabled={isEditMode}/>
</Form.Item> </Form.Item>
<Form.Item name="mobile" label="联系方式1" initialValue={FormData?.mobile} required> <Form.Item name="mobile" label="联系方式" initialValue={FormData?.mobile} required>
<Input placeholder="请输入手机号" disabled={isEditMode} maxLength={11}/> <Input placeholder="请输入手机号" disabled={isEditMode} maxLength={11}/>
</Form.Item> </Form.Item>
<Form.Item name="address" label="公司地址" initialValue={FormData?.address} required> <Form.Item name="address" label="公司地址" initialValue={FormData?.address} required>
@@ -363,10 +363,11 @@ const AddShopDealerApply = () => {
{/*</Form.Item>*/} {/*</Form.Item>*/}
</> </>
)} )}
<Form.Item name="userId" label="报备人" initialValue={FormData?.userId}> <Form.Item name="userId" label="报备人(ID)" initialValue={FormData?.userId}>
<Input <Input
placeholder="自己报备请留空" placeholder="自己报备请留空"
disabled={isEditMode} disabled={isEditMode}
type="number"
value={(FormData?.userId)?.toString() || ''} value={(FormData?.userId)?.toString() || ''}
/> />
</Form.Item> </Form.Item>

View File

@@ -1,6 +1,6 @@
import React, {useState, useEffect} from 'react' import React, {useState, useEffect} from 'react'
import {View, Text} from '@tarojs/components' import {View, Text} from '@tarojs/components'
import {ConfigProvider, Button, Grid, Avatar} from '@nutui/nutui-react-taro' import {ConfigProvider, Grid, Avatar} from '@nutui/nutui-react-taro'
import { import {
User, User,
Shopping, Shopping,
@@ -11,16 +11,14 @@ import {
} from '@nutui/icons-react-taro' } from '@nutui/icons-react-taro'
import {useDealerUser} from '@/hooks/useDealerUser' import {useDealerUser} from '@/hooks/useDealerUser'
import {useThemeStyles} from '@/hooks/useTheme' import {useThemeStyles} from '@/hooks/useTheme'
import {businessGradients, cardGradients, gradientUtils} from '@/styles/gradients' import {businessGradients, cardGradients} from '@/styles/gradients'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {getShopDealerRefereeByUserId} from "@/api/shop/shopDealerReferee"; import {getShopDealerRefereeByUserId} from "@/api/shop/shopDealerReferee";
import {ShopDealerUser} from "@/api/shop/shopDealerUser/model"; import {ShopDealerUser} from "@/api/shop/shopDealerUser/model";
const DealerIndex: React.FC = () => { const DealerIndex: React.FC = () => {
const { const {
dealerUser, dealerUser
error,
refresh,
} = useDealerUser() } = useDealerUser()
const [dealer, setDealer] = useState<ShopDealerUser>() const [dealer, setDealer] = useState<ShopDealerUser>()
@@ -45,16 +43,16 @@ const DealerIndex: React.FC = () => {
} }
// 获取用户主题 // 获取用户主题
const userTheme = gradientUtils.getThemeByUserId(dealerUser?.userId) // const userTheme = gradientUtils.getThemeByUserId(dealerUser?.userId)
// 获取渐变背景 // 获取渐变背景
const getGradientBackground = (themeColor?: string) => { // const getGradientBackground = (themeColor?: string) => {
if (themeColor) { // if (themeColor) {
const darkerColor = gradientUtils.adjustColorBrightness(themeColor, -30) // const darkerColor = gradientUtils.adjustColorBrightness(themeColor, -30)
return gradientUtils.createGradient(themeColor, darkerColor) // return gradientUtils.createGradient(themeColor, darkerColor)
} // }
return userTheme.background // return userTheme.background
} // }
// 初始化当前用户名称 // 初始化当前用户名称
@@ -65,20 +63,20 @@ const DealerIndex: React.FC = () => {
}, [dealerUser]) }, [dealerUser])
console.log(getGradientBackground(), 'getGradientBackground()') // console.log(getGradientBackground(), 'getGradientBackground()')
//
if (error) { // if (error) {
return ( // return (
<View className="p-4"> // <View className="p-4">
<View className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4"> // <View className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
<Text className="text-red-600">{error}</Text> // <Text className="text-red-600">{error}</Text>
</View> // </View>
<Button type="primary" onClick={refresh}> // <Button type="primary" onClick={refresh}>
// 重试
</Button> // </Button>
</View> // </View>
) // )
} // }
return ( return (
<View className="bg-gray-100 min-h-screen"> <View className="bg-gray-100 min-h-screen">
@@ -271,6 +269,18 @@ const DealerIndex: React.FC = () => {
) )
} }
{
(dealerUser?.userId == 33658 || dealerUser?.userId == 33677) && (
<Grid.Item text={'实名审核'} onClick={() => navigateToPage('/admin/userVerify/index')}>
<View className="text-center">
<View className="w-12 h-12 bg-red-50 rounded-xl flex items-center justify-center mx-auto mb-2">
<People color="#10b981" size="20"/>
</View>
</View>
</Grid.Item>
)
}
</Grid> </Grid>
{/* 第二行功能 */} {/* 第二行功能 */}

View File

@@ -7,7 +7,7 @@ import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder'
import {useDealerUser} from '@/hooks/useDealerUser' import {useDealerUser} from '@/hooks/useDealerUser'
import type {ShopDealerOrder} from '@/api/shop/shopDealerOrder/model' import type {ShopDealerOrder} from '@/api/shop/shopDealerOrder/model'
import {useUser} from "@/hooks/useUser"; import {useUser} from "@/hooks/useUser";
import {pageUsers} from "@/api/system/user"; import {pageShopDealerReferee} from "@/api/shop/shopDealerReferee";
interface OrderWithDetails extends ShopDealerOrder { interface OrderWithDetails extends ShopDealerOrder {
orderNo?: string orderNo?: string
@@ -36,19 +36,23 @@ const DealerOrder: React.FC = () => {
const [currentPage, setCurrentPage] = useState<number>(1) const [currentPage, setCurrentPage] = useState<number>(1)
const [hasMore, setHasMore] = useState<boolean>(true) const [hasMore, setHasMore] = useState<boolean>(true)
const [users, setUsers] = useState<any[]>([]) const [users, setUsers] = useState<any[]>([])
const [users2, setUsers2] = useState<any[]>([])
const [visible1, setVisible1] = useState(false) const [visible1, setVisible1] = useState(false)
const [text1, setText1] = useState('') const [text1, setText1] = useState('')
const [selectedUserId, setSelectedUserId] = useState<number | undefined>(undefined)
const [visible2, setVisible2] = useState(false) const [visible2, setVisible2] = useState(false)
const [text2, setText2] = useState('') const [text2, setText2] = useState('')
const [selectedFirstUserId, setSelectedFirstUserId] = useState<number | undefined>(undefined)
const [visible3, setVisible3] = useState(false) const [visible3, setVisible3] = useState(false)
const [text3, setText3] = useState('') // const [text3, setText3] = useState('')
const [selectedSecondUserId, setSelectedSecondUserId] = useState<number | undefined>(undefined)
const {dealerUser} = useDealerUser() const {dealerUser} = useDealerUser()
const {user} = useUser() const {user} = useUser()
// 获取订单数据 // 获取订单数据
const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false,userId?: number) => { const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
if (!dealerUser?.userId) return if (!dealerUser?.userId) return
try { try {
@@ -59,15 +63,30 @@ const DealerOrder: React.FC = () => {
} else { } else {
setLoadingMore(true) setLoadingMore(true)
} }
const result = await pageShopDealerOrder({
userId: userId || undefined, let where = {
userId: selectedUserId,
firstUserId: selectedSecondUserId,
secondUserId: selectedSecondUserId,
isInvalid: 0, isInvalid: 0,
isSettled: 1, isSettled: 1,
resourceId: getResourceId(), resourceId: user?.userId,
month: date, month: date,
page, page,
limit: 10 limit: 10
}) };
if (hasRole('superAdmin') || hasRole('admin')) {
console.log('>>>>>>>>>>>>是管理员')
where = {...where, resourceId: undefined}
}
if(selectedUserId){
where = {...where,userId: selectedUserId}
}
if(selectedFirstUserId){
where = {...where,secondUserId: selectedFirstUserId}
}
const result = await pageShopDealerOrder(where)
if (result?.list) { if (result?.list) {
const newOrders = result.list.map(order => ({ const newOrders = result.list.map(order => ({
@@ -98,7 +117,7 @@ const DealerOrder: React.FC = () => {
setRefreshing(false) setRefreshing(false)
setLoadingMore(false) setLoadingMore(false)
} }
}, [dealerUser?.userId, date]) }, [dealerUser?.userId, date, selectedUserId, selectedFirstUserId, selectedSecondUserId])
// 下拉刷新 // 下拉刷新
const handleRefresh = async () => { const handleRefresh = async () => {
@@ -112,14 +131,15 @@ const DealerOrder: React.FC = () => {
} }
} }
const getResourceId = () => { // const getResourceId = () => {
if (hasRole('superAdmin')) { // if (hasRole('superAdmin')) {
return undefined // return undefined
} // }
if (hasRole('admin')) { // if (hasRole('admin')) {
return user?.userId // return user?.userId
} // }
} // return user?.userId;
// }
// 检查是否有特定角色 // 检查是否有特定角色
const hasRole = (roleCode: string) => { const hasRole = (roleCode: string) => {
@@ -133,15 +153,20 @@ const DealerOrder: React.FC = () => {
options: PickerOption[], options: PickerOption[],
values: (string | number)[] values: (string | number)[]
) => { ) => {
// let description = ''
// options.forEach((option: any) => {
// description += ` ${option.text}`
// })
if(values && values.length > 0){ if(values && values.length > 0){
Number(values[0]) const userId = Number(values[0])
options.forEach((option: any) => { options.forEach((option: any) => {
setText1(`${option.text}`) setText1(`${option.text}`)
}) })
// 清空其他两个筛选条件
setSelectedFirstUserId(undefined)
setSelectedSecondUserId(undefined)
setText2('')
// setText3('')
// 设置业务员筛选条件
setSelectedUserId(userId)
// 关闭选择器
setVisible1(false)
} }
} }
const confirmPicker2 = ( const confirmPicker2 = (
@@ -149,21 +174,39 @@ const DealerOrder: React.FC = () => {
values: (string | number)[] values: (string | number)[]
) => { ) => {
if(values && values.length > 0){ if(values && values.length > 0){
Number(values[0]) const firstUserId = Number(values[0])
options.forEach((option: any) => { options.forEach((option: any) => {
setText2(`${option.text}`) setText2(`${option.text}`)
}) })
// 清空其他两个筛选条件
// setSelectedUserId(undefined)
// setSelectedSecondUserId(undefined)
// setText1('')
// setText3('')
// 设置渠道一筛选条件
setSelectedFirstUserId(firstUserId)
// 关闭选择器
setVisible2(false)
} }
} }
const confirmPicker3 = ( const confirmPicker3 = (
options: PickerOption[], _: PickerOption[],
values: (string | number)[] values: (string | number)[]
) => { ) => {
if(values && values.length > 0){ if(values && values.length > 0){
Number(values[0]) const secondUserId = Number(values[0])
options.forEach((option: any) => { // options.forEach((option: any) => {
setText3(`${option.text}`) // setText3(`${option.text}`)
}) // })
// 清空其他两个筛选条件
setSelectedUserId(undefined)
setSelectedFirstUserId(undefined)
setText1('')
setText2('')
// 设置渠道二筛选条件
setSelectedSecondUserId(secondUserId)
// 关闭选择器
setVisible3(false)
} }
} }
@@ -174,10 +217,12 @@ const DealerOrder: React.FC = () => {
} }
function fetchUsers() { function fetchUsers() {
pageShopDealerReferee({
pageUsers({}).then(data => { dealerId: selectedFirstUserId || Taro.getStorageSync('UserId'),
console.log(data,'datadatadatadatadatadata') isAdmin: true,
const userList = data?.list.map(d => { limit: 100
}).then(res => {
const data = res?.list.map(d => {
return { return {
text: d.nickname, text: d.nickname,
value: d.userId, value: d.userId,
@@ -186,7 +231,22 @@ const DealerOrder: React.FC = () => {
className: '' className: ''
} }
}) })
setUsers(userList || []) setUsers(data || [])
})
pageShopDealerReferee({
dealerId: selectedUserId || Taro.getStorageSync('UserId'),
limit: 100
}).then(res => {
const data = res?.list.map(d => {
return {
text: d.nickname,
value: d.userId,
disabled: false,
children: [],
className: ''
}
})
setUsers2(data || [])
}) })
} }
@@ -195,10 +255,10 @@ const DealerOrder: React.FC = () => {
if (dealerUser?.userId) { if (dealerUser?.userId) {
fetchOrders(1).then() fetchOrders(1).then()
} }
fetchUsers()
}, [fetchOrders, date]) }, [fetchOrders, date])
useEffect(() => { useEffect(() => {
fetchUsers()
},[]) },[])
const renderOrderItem = (order: OrderWithDetails) => ( const renderOrderItem = (order: OrderWithDetails) => (
@@ -316,8 +376,8 @@ const DealerOrder: React.FC = () => {
</View> </View>
<Space className={'select-user'}> <Space className={'select-user'}>
<Button size={'mini'} onClick={() => setVisible1(!visible1)}>{text1 || '业务员'}</Button> <Button size={'mini'} onClick={() => setVisible1(!visible1)}>{text1 || '业务员'}</Button>
<Button size={'mini'} onClick={() => setVisible2(!visible2)}>{text2 || '渠道一'}</Button> {/*{selectedUserId && <Button size={'mini'} onClick={() => setVisible2(!visible2)}>{text2 || '渠道员'}</Button>}*/}
<Button size={'mini'} onClick={() => setVisible3(!visible3)}>{text3 || '渠道'}</Button> <Button size={'mini'} onClick={() => setVisible2(!visible3)}>{text2 || '渠道'}</Button>
</Space> </Space>
</View> </View>
{/*账单列表*/} {/*账单列表*/}
@@ -375,7 +435,7 @@ const DealerOrder: React.FC = () => {
<Picker <Picker
title="请选择" title="请选择"
visible={visible2} visible={visible2}
options={users} options={users2}
onConfirm={(list, values) => confirmPicker2(list, values)} onConfirm={(list, values) => confirmPicker2(list, values)}
onClose={() => setVisible2(false)} onClose={() => setVisible2(false)}
/> />

View File

@@ -10,6 +10,7 @@ import {
PullToRefresh, PullToRefresh,
Button, Button,
Dialog, Dialog,
Image,
TextArea TextArea
} from '@nutui/nutui-react-taro' } from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
@@ -18,6 +19,8 @@ import {pageShopDealerWithdraw, updateShopDealerWithdraw} from '@/api/shop/shopD
import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model' import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model'
import {ShopDealerBank} from "@/api/shop/shopDealerBank/model"; import {ShopDealerBank} from "@/api/shop/shopDealerBank/model";
import {listShopDealerBank} from "@/api/shop/shopDealerBank"; import {listShopDealerBank} from "@/api/shop/shopDealerBank";
import {pushNoticeOfWithdrawalToAccount} from "@/api/sdy/sdyTemplateMessage";
import {uploadFile} from "@/api/system/file";
interface WithdrawRecordWithDetails extends ShopDealerWithdraw { interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
accountDisplay?: string accountDisplay?: string
@@ -195,18 +198,27 @@ const DealerWithdraw: React.FC = () => {
// 上传打款凭证 // 上传打款凭证
const handleUploadPaymentImage = async () => { const handleUploadPaymentImage = async () => {
try { try {
const res = await Taro.chooseImage({ // 直接调用uploadFile它内部会处理图片选择和上传
count: 3 - paymentImages.length, // 最多3张 const data = await uploadFile();
sizeType: ['compressed'], console.log(data.url, 'uploaded image url');
sourceType: ['album', 'camera']
}) // 确保url存在再添加到状态中
if (data.url) {
// 这里应该上传到服务器获取图片URL // 将返回的图片URL添加到状态中
// 暂时使用本地路径演示 const newImages = [...paymentImages, data.url];
const newImages = [...paymentImages, ...res.tempFilePaths] setPaymentImages(newImages.slice(0, 3));
setPaymentImages(newImages.slice(0, 3)) } else {
Taro.showToast({
title: '图片上传失败未返回有效URL',
icon: 'none'
});
}
} catch (error) { } catch (error) {
console.error('选择图片失败:', error) console.error('上传图片失败:', error);
Taro.showToast({
title: '上传失败: ' + (error instanceof Error ? error.message : '未知错误'),
icon: 'none'
});
} }
} }
@@ -230,6 +242,10 @@ const DealerWithdraw: React.FC = () => {
icon: 'success' icon: 'success'
}) })
if(currentRecord){
await pushNoticeOfWithdrawalToAccount(currentRecord).then()
}
setPayDialogVisible(false) setPayDialogVisible(false)
setCurrentRecord(null) setCurrentRecord(null)
setPaymentImages([]) setPaymentImages([])
@@ -306,6 +322,9 @@ const DealerWithdraw: React.FC = () => {
<Text className="text-sm text-gray-500"> <Text className="text-sm text-gray-500">
{record.comments} {record.comments}
</Text> </Text>
<Text className={'text-sm text-gray-500'}>
{record.bankAccount} {record.phone}
</Text>
</Space> </Space>
<Tag type={getStatusColor(record.applyStatus)}> <Tag type={getStatusColor(record.applyStatus)}>
{getStatusText(record.applyStatus)} {getStatusText(record.applyStatus)}
@@ -430,9 +449,18 @@ const DealerWithdraw: React.FC = () => {
onConfirm={confirmPayment} onConfirm={confirmPayment}
> >
<View className="p-4"> <View className="p-4">
<View className="mb-3"> <View className="mb-3 flex flex-col">
<Text className="text-sm text-gray-600"> <Text className="text-sm text-gray-600">
¥{Number(currentRecord?.money || 0).toFixed(2)} ¥{(Number(currentRecord?.money) - 3).toFixed(2)}
</Text>
<Text className="text-sm">
{currentRecord?.bankName}
</Text>
<Text className="text-sm">
{currentRecord?.bankAccount}
</Text>
<Text className="text-sm">
{currentRecord?.bankCard}
</Text> </Text>
</View> </View>
<View className="mb-3"> <View className="mb-3">
@@ -442,7 +470,7 @@ const DealerWithdraw: React.FC = () => {
<View className="flex flex-wrap gap-2"> <View className="flex flex-wrap gap-2">
{paymentImages.map((img, index) => ( {paymentImages.map((img, index) => (
<View key={index} className="relative w-20 h-20"> <View key={index} className="relative w-20 h-20">
<img src={img} className="w-full h-full object-cover rounded" /> <Image src={img} className="w-full h-full object-cover rounded" />
<View <View
className="absolute top-0 right-0 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs" className="absolute top-0 right-0 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs"
onClick={() => handleRemovePaymentImage(index)} onClick={() => handleRemovePaymentImage(index)}

View File

@@ -24,6 +24,7 @@ import {listShopDealerBank} from "@/api/shop/shopDealerBank";
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField"; import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
import {myUserVerify} from "@/api/system/userVerify"; import {myUserVerify} from "@/api/system/userVerify";
import navTo from "@/utils/common"; import navTo from "@/utils/common";
import {pushWithdrawalReviewReminder} from "@/api/sdy/sdyTemplateMessage";
interface WithdrawRecordWithDetails extends ShopDealerWithdraw { interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
accountDisplay?: string accountDisplay?: string
@@ -278,6 +279,8 @@ const DealerWithdraw: React.FC = () => {
icon: 'success' icon: 'success'
}) })
await pushWithdrawalReviewReminder(withdrawData)
// 重置表单 // 重置表单
setWithdrawAmount('') setWithdrawAmount('')

View File

@@ -1,13 +1,83 @@
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {Input, Radio, Button} from '@nutui/nutui-react-taro' import {Input, Radio, Button} from '@nutui/nutui-react-taro'
import {loginBySms} from '@/api/passport/login'
const Login = () => { const Login = () => {
const [isAgree, setIsAgree] = useState(false) const [isAgree, setIsAgree] = useState(false)
const [phone, setPhone] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const reload = () => { const reload = () => {
Taro.hideTabBar() Taro.hideTabBar()
} }
// 处理登录
const handleLogin = async () => {
if (!isAgree) {
Taro.showToast({
title: '请先同意服务协议',
icon: 'none'
})
return
}
if (!phone || phone.trim() === '') {
Taro.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
if (!password || password.trim() === '') {
Taro.showToast({
title: '请输入密码',
icon: 'none'
})
return
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(phone)) {
Taro.showToast({
title: '请输入正确的手机号',
icon: 'none'
})
return
}
try {
setLoading(true)
await loginBySms({
phone: phone,
code: password
})
Taro.showToast({
title: '登录成功',
icon: 'success'
})
// 延迟跳转到首页
setTimeout(() => {
Taro.reLaunch({
url: '/pages/index/index'
})
}, 1500)
} catch (error: any) {
console.error('登录失败:', error)
Taro.showToast({
title: error.message || '登录失败,请重试',
icon: 'none'
})
} finally {
setLoading(false)
}
}
useEffect(() => { useEffect(() => {
reload() reload()
}, []) }, [])
@@ -19,24 +89,45 @@ const Login = () => {
<> <>
<div className={'flex flex-col justify-between items-center my-2'}> <div className={'flex flex-col justify-between items-center my-2'}>
<Input type="text" placeholder="手机号" maxLength={11} <Input
style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/> type="text"
placeholder="手机号"
maxLength={11}
value={phone}
onChange={(val) => setPhone(val)}
style={{backgroundColor: '#ffffff', borderRadius: '8px'}}
/>
</div> </div>
<div className={'flex flex-col justify-between items-center my-2'}> <div className={'flex flex-col justify-between items-center my-2'}>
<Input type="password" placeholder="密码" style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/> <Input
</div> type="password"
<div className={'flex justify-between my-2 text-left px-1'}> placeholder="密码"
<a href={'#'} className={'text-blue-600 text-sm'} value={password}
onClick={() => Taro.navigateTo({url: '/passport/forget'})}></a> onChange={(val) => setPassword(val)}
<a href={'#'} className={'text-blue-600 text-sm'} style={{backgroundColor: '#ffffff', borderRadius: '8px'}}
onClick={() => Taro.navigateTo({url: '/passport/register'})}></a> />
</div> </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/register'})}>立即注册</a>*/}
{/*</div>*/}
<div className={'flex justify-center my-5'}> <div className={'flex justify-center my-5'}>
<Button type="info" size={'large'} className={'w-full rounded-lg p-2'} disabled={!isAgree}></Button> <Button
</div> type="info"
<div className={'my-2 flex fixed justify-center bottom-20 left-0 text-sm items-center text-center w-full'}> size={'large'}
<Button onClick={() => Taro.navigateTo({url: '/passport/setting'})}></Button> className={'w-full rounded-lg p-2'}
disabled={!isAgree}
loading={loading}
onClick={handleLogin}
>
{loading ? '登录中...' : '登录'}
</Button>
</div> </div>
{/*<div className={'my-2 flex fixed justify-center bottom-20 left-0 text-sm items-center text-center w-full'}>*/}
{/* <Button onClick={() => Taro.navigateTo({url: '/passport/setting'})}>服务配置</Button>*/}
{/*</div>*/}
{/*<div className={'w-full fixed bottom-20 my-2 flex justify-center text-sm items-center text-center'}>*/} {/*<div className={'w-full fixed bottom-20 my-2 flex justify-center text-sm items-center text-center'}>*/}
{/* 没有账号?<a href={''} onClick={() => Taro.navigateTo({url: '/passport/register'})}*/} {/* 没有账号?<a href={''} onClick={() => Taro.navigateTo({url: '/passport/register'})}*/}
{/* className={'text-blue-600'}>立即注册</a>*/} {/* className={'text-blue-600'}>立即注册</a>*/}

View File

@@ -12,6 +12,7 @@ import {
import {UserVerify} from "@/api/system/userVerify/model"; import {UserVerify} from "@/api/system/userVerify/model";
import {addUserVerify, myUserVerify, updateUserVerify} from "@/api/system/userVerify"; import {addUserVerify, myUserVerify, updateUserVerify} from "@/api/system/userVerify";
import {uploadFile} from "@/api/system/file"; import {uploadFile} from "@/api/system/file";
import {pushReviewReminder} from "@/api/sdy/sdyTemplateMessage";
function Index() { function Index() {
const [isUpdate, setIsUpdate] = useState<boolean>(false) const [isUpdate, setIsUpdate] = useState<boolean>(false)
@@ -95,6 +96,12 @@ function Index() {
const saveOrUpdate = isUpdate ? updateUserVerify : addUserVerify; const saveOrUpdate = isUpdate ? updateUserVerify : addUserVerify;
saveOrUpdate({...FormData, status: 0}).then(() => { saveOrUpdate({...FormData, status: 0}).then(() => {
Taro.showToast({title: `提交成功`, icon: 'success'}) Taro.showToast({title: `提交成功`, icon: 'success'})
if(!isUpdate){
// 发送派单成功提醒 0FBKFCWXe8WyjReYXwSDEXf1-pxYKQXE0quZre3GYIM
pushReviewReminder({
realName: FormData.realName,
}).then()
}
setTimeout(() => { setTimeout(() => {
return Taro.navigateBack() return Taro.navigateBack()
}, 1000) }, 1000)
@@ -185,9 +192,9 @@ function Index() {
<Radio key={0} value={0}> <Radio key={0} value={0}>
</Radio> </Radio>
<Radio key={1} value={1}> {/*<Radio key={1} value={1}>*/}
{/* 企业*/}
</Radio> {/*</Radio>*/}
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
{ {