feat(credit): 新增我的订单查询页面并优化文件上传功能
- 新增 credit/my-order/index 页面用于订单进度查询 - 在 app.config.ts 中注册新的订单查询页面路由 - 修改文件上传逻辑,统一使用 uploadFile 方法替代手动选择图片流程 - 重构 uploadFileByPath 函数,增强错误处理和响应解析逻辑 - 修复STS token过期判断条件,确保及时刷新临时凭证 - 实现订单列表的搜索、筛选、排序和分页加载功能 - 添加日期范围选择器和回款状态过滤功能 - 优化图片上传用户体验,与用户认证页面保持一致
This commit is contained in:
@@ -19,7 +19,7 @@ export async function uploadOssByPath(filePath: string) {
|
|||||||
};
|
};
|
||||||
let sts = Taro.getStorageSync('sts');
|
let sts = Taro.getStorageSync('sts');
|
||||||
let stsExpired = Taro.getStorageSync('stsExpiredAt');
|
let stsExpired = Taro.getStorageSync('stsExpiredAt');
|
||||||
if (!sts || (stsExpired && dayjs().isBefore(dayjs(stsExpired)))) {
|
if (!sts || (stsExpired && dayjs().isAfter(dayjs(stsExpired)))) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const {data: {data: {credentials}}} = await request.get(`https://gle-server.websoft.top/api/oss/getSTSToken`)
|
const {data: {data: {credentials}}} = await request.get(`https://gle-server.websoft.top/api/oss/getSTSToken`)
|
||||||
Taro.setStorageSync('sts', credentials)
|
Taro.setStorageSync('sts', credentials)
|
||||||
@@ -57,20 +57,58 @@ const computeSignature = (accessKeySecret: string, canonicalString: string): str
|
|||||||
* 上传阿里云OSS
|
* 上传阿里云OSS
|
||||||
*/
|
*/
|
||||||
export async function uploadFileByPath(filePath: string) {
|
export async function uploadFileByPath(filePath: string) {
|
||||||
return new Promise((resolve: (result: FileRecord) => void, reject) => {
|
const parseResponseToRecord = (res: any): FileRecord => {
|
||||||
if (!filePath) {
|
const statusCode = Number(res?.statusCode)
|
||||||
reject(new Error('缺少 filePath'))
|
if (Number.isFinite(statusCode) && statusCode !== 200) {
|
||||||
return
|
throw new Error(`上传失败(HTTP ${statusCode})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantId = Taro.getStorageSync('TenantId') || TenantId
|
const raw = res?.data
|
||||||
|
if (raw === null || raw === undefined || raw === '') {
|
||||||
|
throw new Error('上传失败:响应为空')
|
||||||
|
}
|
||||||
|
|
||||||
|
let data: any
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
const cleaned = raw.replace(/^\uFEFF/, '').trim()
|
||||||
|
try {
|
||||||
|
data = JSON.parse(cleaned)
|
||||||
|
} catch (_e) {
|
||||||
|
throw new Error('上传失败:响应格式错误')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data?.code === 0) return data.data as FileRecord
|
||||||
|
|
||||||
|
const codeHint = data?.code !== undefined && data?.code !== null ? `(code=${String(data.code)})` : ''
|
||||||
|
let msg = String(data?.message || data?.msg || data?.error || data?.errMsg || `上传失败${codeHint}`)
|
||||||
|
try {
|
||||||
|
msg = decodeURIComponent(escape(msg))
|
||||||
|
} catch (_e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
throw new Error(msg || '上传失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenantIdInStorage = Taro.getStorageSync('TenantId')
|
||||||
|
const tenantId =
|
||||||
|
tenantIdInStorage && String(tenantIdInStorage) === String(TenantId)
|
||||||
|
? tenantIdInStorage
|
||||||
|
: TenantId
|
||||||
|
|
||||||
const header: Record<string, string> = {
|
const header: Record<string, string> = {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
TenantId: String(tenantId)
|
TenantId: String(tenantId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一走同一个上传接口:既支持图片,也支持文档等文件(由后端决定白名单/大小限制)
|
return new Promise((resolve: (result: FileRecord) => void, reject) => {
|
||||||
|
if (!filePath) {
|
||||||
|
reject(new Error('缺少 filePath'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Taro.uploadFile({
|
Taro.uploadFile({
|
||||||
url: 'https://server.websoft.top/api/oss/upload',
|
url: 'https://server.websoft.top/api/oss/upload',
|
||||||
filePath,
|
filePath,
|
||||||
@@ -78,30 +116,14 @@ export async function uploadFileByPath(filePath: string) {
|
|||||||
header,
|
header,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
try {
|
try {
|
||||||
if ((res as any)?.statusCode && (res as any).statusCode !== 200) {
|
resolve(parseResponseToRecord(res))
|
||||||
reject(new Error(`上传失败(HTTP ${(res as any).statusCode})`))
|
} catch (e) {
|
||||||
return
|
reject(e)
|
||||||
}
|
|
||||||
const raw = (res as any)?.data
|
|
||||||
const data = typeof raw === 'string' ? JSON.parse(raw) : raw
|
|
||||||
if (data.code === 0) {
|
|
||||||
resolve(data.data)
|
|
||||||
} else {
|
|
||||||
let msg = String(data.message || '上传失败')
|
|
||||||
try {
|
|
||||||
msg = decodeURIComponent(escape(msg))
|
|
||||||
} catch (_e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
reject(new Error(msg || '上传失败'))
|
|
||||||
}
|
|
||||||
} catch (_error) {
|
|
||||||
reject(new Error('解析响应数据失败'))
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.log('上传请求失败', err);
|
const msg = String((err as any)?.errMsg || (err as any)?.message || '上传请求失败')
|
||||||
reject(new Error('上传请求失败'))
|
reject(new Error(msg))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "credit",
|
"root": "credit",
|
||||||
"pages": ["data/index",
|
"pages": [
|
||||||
|
"data/index",
|
||||||
"customer/index",
|
"customer/index",
|
||||||
"order/index",
|
"order/index",
|
||||||
"order/add",
|
"order/add",
|
||||||
@@ -128,6 +129,7 @@ export default {
|
|||||||
"company/detail",
|
"company/detail",
|
||||||
"company/follow-step1",
|
"company/follow-step1",
|
||||||
"company/edit",
|
"company/edit",
|
||||||
|
"my-order/index",
|
||||||
'creditMpCustomer/index',
|
'creditMpCustomer/index',
|
||||||
'creditMpCustomer/add',]
|
'creditMpCustomer/add',]
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/credit/my-order/index.config.ts
Normal file
5
src/credit/my-order/index.config.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '查询进度',
|
||||||
|
navigationBarTextStyle: 'black',
|
||||||
|
navigationBarBackgroundColor: '#ffffff'
|
||||||
|
})
|
||||||
348
src/credit/my-order/index.tsx
Normal file
348
src/credit/my-order/index.tsx
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
import { useMemo, useState } from 'react'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import { Button, ConfigProvider, DatePicker, Empty, Input, Popup } from '@nutui/nutui-react-taro'
|
||||||
|
import { Search } from '@nutui/icons-react-taro'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
type PayStatus = '全部回款' | '部分回款' | '未回款'
|
||||||
|
type AmountSort = '默认' | '从高到低' | '从低到高'
|
||||||
|
|
||||||
|
type OrderItem = {
|
||||||
|
id: string
|
||||||
|
orderNo: string
|
||||||
|
companyName: string
|
||||||
|
unifiedCode: string
|
||||||
|
projectName: string
|
||||||
|
follower: string
|
||||||
|
payStatus: PayStatus
|
||||||
|
date: string // YYYY-MM-DD
|
||||||
|
principal: number
|
||||||
|
interest: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatYmd = (d?: Date | null) => {
|
||||||
|
if (!d) return ''
|
||||||
|
return dayjs(d).format('YYYY-MM-DD')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusStyle = (s: PayStatus) => {
|
||||||
|
if (s === '全部回款') return 'bg-green-500'
|
||||||
|
if (s === '部分回款') return 'bg-orange-500'
|
||||||
|
return 'bg-red-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeMockOrders = (page: number): OrderItem[] => {
|
||||||
|
const companies = [
|
||||||
|
{ name: '广西万宇工程建设有限公司', code: '91450100MA00000001' },
|
||||||
|
{ name: '广西远恒建筑有限公司', code: '91450100MA00000002' },
|
||||||
|
{ name: '南宁宏达工程有限公司', code: '91450100MA00000003' },
|
||||||
|
{ name: '桂林鑫盛建设有限公司', code: '91450100MA00000004' }
|
||||||
|
]
|
||||||
|
const followers = ['罗天东', '张三', '李四', '王五']
|
||||||
|
const suffix = ['一期', '二期', '改扩建', '配套', '市政', '装饰', '机电']
|
||||||
|
|
||||||
|
// page=1:给出 16 条示例,统计更直观(项目=订单维度,而非公司维度)
|
||||||
|
const size = page === 1 ? 16 : 6
|
||||||
|
const baseNo = 9099009999 + (page - 1) * 100
|
||||||
|
|
||||||
|
const list: OrderItem[] = []
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
const c = companies[(page + i) % companies.length]
|
||||||
|
const follower = followers[(i + page) % followers.length]
|
||||||
|
const payStatus: PayStatus =
|
||||||
|
page === 1 ? '全部回款' : (['全部回款', '部分回款', '未回款'][(i + page) % 3] as PayStatus)
|
||||||
|
|
||||||
|
const orderNo = String(baseNo + i)
|
||||||
|
const date = dayjs('2025-10-10').subtract((page - 1) * 7 + (i % 6), 'day').format('YYYY-MM-DD')
|
||||||
|
|
||||||
|
// page=1:让本金/利息合计更规整(本金 2000,利息 200)
|
||||||
|
const principal = page === 1 ? (i < 8 ? 100 : 150) : 200 + ((i + page) % 5) * 50
|
||||||
|
const interest = page === 1 ? (i < 8 ? 10 : 15) : 20 + ((i + page) % 4) * 5
|
||||||
|
|
||||||
|
const projectName = `${c.name}${suffix[(i + page) % suffix.length]}项目`
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
id: `${page}-${i}-${orderNo}`,
|
||||||
|
orderNo,
|
||||||
|
companyName: c.name,
|
||||||
|
unifiedCode: c.code,
|
||||||
|
projectName,
|
||||||
|
follower,
|
||||||
|
payStatus,
|
||||||
|
date,
|
||||||
|
principal,
|
||||||
|
interest
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CreditOrderPage() {
|
||||||
|
|
||||||
|
const [rawList, setRawList] = useState<OrderItem[]>(() => makeMockOrders(1))
|
||||||
|
const [mockPage, setMockPage] = useState(1)
|
||||||
|
|
||||||
|
const [searchValue, setSearchValue] = useState('')
|
||||||
|
const [amountSort, setAmountSort] = useState<AmountSort>('默认')
|
||||||
|
const [payFilter, setPayFilter] = useState<PayStatus>('全部回款')
|
||||||
|
|
||||||
|
const [datePopupVisible, setDatePopupVisible] = useState(false)
|
||||||
|
const [datePickerVisible, setDatePickerVisible] = useState(false)
|
||||||
|
const [picking, setPicking] = useState<'start' | 'end'>('start')
|
||||||
|
const [startDate, setStartDate] = useState<Date | null>(null)
|
||||||
|
const [endDate, setEndDate] = useState<Date | null>(null)
|
||||||
|
|
||||||
|
const filteredList = useMemo(() => {
|
||||||
|
let list = rawList.slice()
|
||||||
|
|
||||||
|
const q = searchValue.trim()
|
||||||
|
if (q) {
|
||||||
|
const qq = q.toLowerCase()
|
||||||
|
list = list.filter(o => {
|
||||||
|
return (
|
||||||
|
String(o.companyName || '').toLowerCase().includes(qq) ||
|
||||||
|
String(o.unifiedCode || '').toLowerCase().includes(qq) ||
|
||||||
|
String(o.orderNo || '').toLowerCase().includes(qq)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payFilter) {
|
||||||
|
list = list.filter(o => o.payStatus === payFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startDate || endDate) {
|
||||||
|
const start = startDate ? dayjs(startDate).startOf('day') : null
|
||||||
|
const end = endDate ? dayjs(endDate).endOf('day') : null
|
||||||
|
list = list.filter(o => {
|
||||||
|
const t = dayjs(o.date, 'YYYY-MM-DD')
|
||||||
|
if (start && t.isBefore(start)) return false
|
||||||
|
if (end && t.isAfter(end)) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountSort !== '默认') {
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const av = Number(a.principal || 0)
|
||||||
|
const bv = Number(b.principal || 0)
|
||||||
|
return amountSort === '从高到低' ? bv - av : av - bv
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}, [amountSort, endDate, payFilter, rawList, searchValue, startDate])
|
||||||
|
|
||||||
|
const stats = useMemo(() => {
|
||||||
|
// 业务说明:一个公司可能对应多个项目(多条订单),因此“订单数量”=项目/订单条数,而不是公司数。
|
||||||
|
const total = filteredList.length
|
||||||
|
const principal = filteredList.reduce((sum, o) => sum + Number(o.principal || 0), 0)
|
||||||
|
const interest = filteredList.reduce((sum, o) => sum + Number(o.interest || 0), 0)
|
||||||
|
return { total, principal, interest }
|
||||||
|
}, [filteredList])
|
||||||
|
|
||||||
|
const timeText = useMemo(() => {
|
||||||
|
const s = formatYmd(startDate)
|
||||||
|
const e = formatYmd(endDate)
|
||||||
|
if (!s && !e) return '时间筛选'
|
||||||
|
if (s && e) return `${s}~${e}`
|
||||||
|
return s ? `${s}~` : `~${e}`
|
||||||
|
}, [endDate, startDate])
|
||||||
|
|
||||||
|
const pickAmountSort = async () => {
|
||||||
|
try {
|
||||||
|
const options: AmountSort[] = ['默认', '从高到低', '从低到高']
|
||||||
|
const res = await Taro.showActionSheet({ itemList: options })
|
||||||
|
const next = options[res.tapIndex]
|
||||||
|
if (next) setAmountSort(next)
|
||||||
|
} catch (e) {
|
||||||
|
const msg = String((e as any)?.errMsg || (e as any)?.message || e || '')
|
||||||
|
if (msg.includes('cancel')) return
|
||||||
|
console.error('选择金额排序失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickPayFilter = async () => {
|
||||||
|
try {
|
||||||
|
const options: PayStatus[] = ['全部回款', '部分回款', '未回款']
|
||||||
|
const res = await Taro.showActionSheet({ itemList: options })
|
||||||
|
const next = options[res.tapIndex]
|
||||||
|
if (next) setPayFilter(next)
|
||||||
|
} catch (e) {
|
||||||
|
const msg = String((e as any)?.errMsg || (e as any)?.message || e || '')
|
||||||
|
if (msg.includes('cancel')) return
|
||||||
|
console.error('选择回款筛选失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadMore = () => {
|
||||||
|
const nextPage = mockPage + 1
|
||||||
|
const incoming = makeMockOrders(nextPage)
|
||||||
|
setMockPage(nextPage)
|
||||||
|
setRawList(prev => prev.concat(incoming))
|
||||||
|
console.log('加载更多:', { nextPage, appended: incoming.length })
|
||||||
|
Taro.showToast({ title: '已加载更多', icon: 'none' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="bg-pink-50 min-h-screen">
|
||||||
|
<ConfigProvider>
|
||||||
|
<View className="max-w-md mx-auto">
|
||||||
|
<View className="px-4 pt-2">
|
||||||
|
<View className="bg-white rounded-full border border-pink-100 px-3 py-2 flex items-center gap-2">
|
||||||
|
<Search size={16} className="text-gray-400" />
|
||||||
|
<View className="flex-1">
|
||||||
|
<Input
|
||||||
|
value={searchValue}
|
||||||
|
onChange={setSearchValue}
|
||||||
|
placeholder="请输入公司名称、统一代码查询、订单号查询"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="mt-3 grid grid-cols-3 gap-2">
|
||||||
|
<View
|
||||||
|
className="bg-white rounded-xl border border-pink-100 px-2 py-2 text-xs text-gray-700 flex items-center justify-center"
|
||||||
|
onClick={pickAmountSort}
|
||||||
|
>
|
||||||
|
<Text className="mr-1">金额排序</Text>
|
||||||
|
<Text className="text-gray-400">{amountSort === '从高到低' ? '↓' : amountSort === '从低到高' ? '↑' : '↕'}</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className="bg-white rounded-xl border border-pink-100 px-2 py-2 text-xs text-gray-700 flex items-center justify-center"
|
||||||
|
onClick={pickPayFilter}
|
||||||
|
>
|
||||||
|
<Text>{payFilter}</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className="bg-white rounded-xl border border-pink-100 px-2 py-2 text-xs text-gray-700 flex items-center justify-center"
|
||||||
|
onClick={() => setDatePopupVisible(true)}
|
||||||
|
>
|
||||||
|
<Text className="truncate">{timeText}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="mt-2 text-xs text-gray-400 flex items-center justify-between">
|
||||||
|
<Text>总订单量:{stats.total}个</Text>
|
||||||
|
<Text>订单本金:{stats.principal}元</Text>
|
||||||
|
<Text>利息金额:{stats.interest}元</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="px-4 mt-3 pb-6">
|
||||||
|
{!filteredList.length ? (
|
||||||
|
<View className="bg-white rounded-xl border border-pink-100 py-10">
|
||||||
|
<Empty description="暂无订单" />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
filteredList.map(o => (
|
||||||
|
<View key={o.id} className="bg-white rounded-xl border border-pink-100 p-3 mb-3">
|
||||||
|
<View className="flex items-center justify-between">
|
||||||
|
<Text className="text-xs text-gray-500">订单号:{o.orderNo}</Text>
|
||||||
|
<View className={`px-2 py-1 rounded-full ${getStatusStyle(o.payStatus)}`}>
|
||||||
|
<Text className="text-xs text-white">{o.payStatus}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 项目名称是核心分组/统计维度(一个公司可有多个项目=多条订单),因此需要突出显示 */}
|
||||||
|
<View className="mt-2">
|
||||||
|
<Text className="text-sm font-semibold text-rose-600">{o.projectName}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-gray-600">
|
||||||
|
<Text>公司:{o.companyName}</Text>
|
||||||
|
<Text>跟进人:{o.follower}</Text>
|
||||||
|
<Text>日期:{o.date}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="mt-2 flex items-center gap-6 text-xs text-gray-700">
|
||||||
|
<Text>
|
||||||
|
<Text className="text-gray-400">本金:</Text>
|
||||||
|
{o.principal}元
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
<Text className="text-gray-400">利息:</Text>
|
||||||
|
{o.interest}元
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className="mt-2 flex justify-center">
|
||||||
|
<Button
|
||||||
|
fill="none"
|
||||||
|
size="small"
|
||||||
|
style={{ color: '#bdbdbd' }}
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
|
加载更多
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
visible={datePopupVisible}
|
||||||
|
position="bottom"
|
||||||
|
onClose={() => setDatePopupVisible(false)}
|
||||||
|
style={{ borderTopLeftRadius: '12px', borderTopRightRadius: '12px' }}
|
||||||
|
>
|
||||||
|
<View className="px-4 py-4 bg-white">
|
||||||
|
<View className="text-base font-semibold text-gray-900">时间筛选</View>
|
||||||
|
<View className="mt-3 text-sm text-gray-700">
|
||||||
|
<View className="flex items-center justify-between py-2" onClick={() => { setPicking('start'); setDatePickerVisible(true) }}>
|
||||||
|
<Text>开始日期</Text>
|
||||||
|
<Text className="text-gray-500">{formatYmd(startDate) || '未选择'}</Text>
|
||||||
|
</View>
|
||||||
|
<View className="flex items-center justify-between py-2" onClick={() => { setPicking('end'); setDatePickerVisible(true) }}>
|
||||||
|
<Text>结束日期</Text>
|
||||||
|
<Text className="text-gray-500">{formatYmd(endDate) || '未选择'}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="mt-4 flex items-center justify-between gap-3">
|
||||||
|
<Button
|
||||||
|
fill="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setStartDate(null)
|
||||||
|
setEndDate(null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" onClick={() => setDatePopupVisible(false)}>
|
||||||
|
完成
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Popup>
|
||||||
|
|
||||||
|
<DatePicker
|
||||||
|
visible={datePickerVisible}
|
||||||
|
title={picking === 'start' ? '选择开始日期' : '选择结束日期'}
|
||||||
|
type="date"
|
||||||
|
value={(picking === 'start' ? startDate : endDate) || new Date()}
|
||||||
|
startDate={dayjs('2020-01-01').toDate()}
|
||||||
|
endDate={dayjs('2035-12-31').toDate()}
|
||||||
|
onClose={() => setDatePickerVisible(false)}
|
||||||
|
onCancel={() => setDatePickerVisible(false)}
|
||||||
|
onConfirm={(_options, selectedValue) => {
|
||||||
|
const [y, m, d] = (selectedValue || []).map(v => Number(v))
|
||||||
|
const next = new Date(y, (m || 1) - 1, d || 1, 0, 0, 0)
|
||||||
|
|
||||||
|
if (picking === 'start') {
|
||||||
|
setStartDate(next)
|
||||||
|
if (endDate && dayjs(endDate).isBefore(next, 'day')) setEndDate(null)
|
||||||
|
} else {
|
||||||
|
setEndDate(next)
|
||||||
|
if (startDate && dayjs(next).isBefore(startDate, 'day')) setStartDate(null)
|
||||||
|
}
|
||||||
|
setDatePickerVisible(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ConfigProvider>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import { ArrowRight, Close } from '@nutui/icons-react-taro'
|
|||||||
|
|
||||||
import FixedButton from '@/components/FixedButton'
|
import FixedButton from '@/components/FixedButton'
|
||||||
import RegionData from '@/api/json/regions-data.json'
|
import RegionData from '@/api/json/regions-data.json'
|
||||||
import { uploadFileByPath } from '@/api/system/file'
|
import { uploadFile, uploadFileByPath } from '@/api/system/file'
|
||||||
import { addCreditMpCustomer } from '@/api/credit/creditMpCustomer'
|
import { addCreditMpCustomer } from '@/api/credit/creditMpCustomer'
|
||||||
|
|
||||||
import './add.scss'
|
import './add.scss'
|
||||||
@@ -79,32 +79,11 @@ export default function CreditOrderAddPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chooseAndUploadImages = async () => {
|
const chooseAndUploadImages = async () => {
|
||||||
let res: any
|
|
||||||
try {
|
|
||||||
res = await Taro.chooseImage({
|
|
||||||
count: 9,
|
|
||||||
sizeType: ['compressed'],
|
|
||||||
sourceType: ['album', 'camera']
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
const msg = String((e as any)?.errMsg || (e as any)?.message || e || '')
|
|
||||||
if (msg.includes('cancel')) return
|
|
||||||
console.error('选择图片失败:', e)
|
|
||||||
Taro.showToast({ title: '选择图片失败', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const paths = (res?.tempFilePaths || []).filter(Boolean)
|
|
||||||
if (!paths.length) return
|
|
||||||
|
|
||||||
setUploading(true)
|
setUploading(true)
|
||||||
try {
|
try {
|
||||||
const uploaded = []
|
// 与用户认证页一致:走 uploadFile() 内部的 chooseImage + uploadFileByPath 流程
|
||||||
for (const p of paths) {
|
const record = await uploadFile()
|
||||||
const record = await uploadFileByPath(p)
|
addUploadedRecords([record as any], { isImage: true })
|
||||||
uploaded.push(record as any)
|
|
||||||
}
|
|
||||||
addUploadedRecords(uploaded, { isImage: true })
|
|
||||||
Taro.showToast({ title: '上传成功', icon: 'success' })
|
Taro.showToast({ title: '上传成功', icon: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('上传图片失败:', e)
|
console.error('上传图片失败:', e)
|
||||||
|
|||||||
Reference in New Issue
Block a user