feat: multi-module iteration

This commit is contained in:
2026-08-04 21:38:49 +08:00
parent 9d96c73246
commit 71f508e02b
1366 changed files with 202004 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
import { useEffect, useState } from 'react';
import { View, Text, ScrollView, Button } from '@tarojs/components';
import { useDidShow } from '@tarojs/taro';
import HqHeader from '../../components/HqHeader';
import HqTabBar from '../../components/HqTabBar';
import { request, type Paginated, toast } from '../../lib/api';
import { useHqSession } from '../../lib/session';
import { badgeClass, fmtTime } from '../../lib/constants';
type TicketRow = {
id: string;
ticketNo: string;
ticketType?: string;
refType?: string;
status: string;
remark?: string;
createdAt: string;
};
const STATUS_LABELS: Record<string, string> = {
PENDING: '待处理',
PROCESSING: '处理中',
RESOLVED: '已解决',
COMPLETED: '已完成',
CLOSED: '已关闭',
};
const TABS = [
{ key: '', label: '全部' },
{ key: 'PENDING', label: '待处理' },
{ key: 'PROCESSING', label: '处理中' },
{ key: 'COMPLETED', label: '已完成' },
];
export default function TicketsPage() {
useHqSession();
const [status, setStatus] = useState('');
const [rows, setRows] = useState<TicketRow[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(true);
function load() {
setLoading(true);
const qs = new URLSearchParams({ pageSize: '50' });
if (status) qs.set('status', status);
request<Paginated<TicketRow>>(`/common/tickets?${qs.toString()}`)
.then((d) => {
setRows(d.items);
setTotal(d.total);
})
.catch(() => setRows([]))
.finally(() => setLoading(false));
}
useEffect(load, [status]);
useDidShow(load);
async function markProcessing(id: string) {
try {
await request(`/common/tickets/${id}/status`, { method: 'PUT', data: { status: 'PROCESSING' } });
toast('已受理', 'success');
load();
} catch (e) {
toast(e instanceof Error ? e.message : '操作失败');
}
}
return (
<View className="hq-page hq-page--tab">
<HqHeader title="客服中心" />
<ScrollView scrollX enhanced showScrollbar={false} className="hq-tabs">
{TABS.map((t) => (
<View
key={t.key}
className={`hq-tab${status === t.key ? ' hq-tab--active' : ''}`}
onClick={() => setStatus(t.key)}
>
<Text>{t.label}</Text>
</View>
))}
</ScrollView>
<Text className="hq-section-title">
<Text></Text>
<Text className="hq-muted" style="font-size:12px;font-weight:400"> {total} </Text>
</Text>
{loading && <View className="hq-empty"></View>}
{!loading && rows.length === 0 && <View className="hq-empty"></View>}
{rows.map((t) => (
<View key={t.id} className="hq-card" style="margin-top:8px;margin-bottom:0">
<View className="hq-row">
<Text style="font-size:13px;color:var(--hq-muted)">{t.ticketNo}</Text>
<Text className={`hq-badge ${badgeClass(t.status)}`}>{STATUS_LABELS[t.status] || t.status}</Text>
</View>
<Text style="display:block;margin-top:8px;font-size:14px">
{t.ticketType || '工单'} · {t.refType || ''}
</Text>
{t.remark ? <Text className="hq-muted" style="display:block;margin-top:4px;font-size:13px">{t.remark}</Text> : null}
<View className="hq-row" style="margin-top:10px">
<Text className="hq-muted" style="font-size:11px">{fmtTime(t.createdAt)}</Text>
{t.status === 'PENDING' && (
<Button className="hq-btn hq-btn--ghost" style="padding:6px 14px;font-size:13px" onClick={() => markProcessing(t.id)}>
</Button>
)}
</View>
</View>
))}
<HqTabBar selected={3} />
</View>
);
}