v3.5.1 版本更新
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-08-19 15:54:51 +08:00
parent 7dd5fdfb12
commit 233ed0af3b
103 changed files with 5764 additions and 185 deletions
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { View, Text, Input, Textarea, Switch } from '@tarojs/components';
import '../../styles/address.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useCallback, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/address.css';
import Taro, { useDidShow, usePullDownRefresh, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/redeem.css';
import Taro from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/redeem.css';
import Taro from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '申请发票',
});
@@ -0,0 +1,296 @@
import { useCallback, useEffect, useState } from 'react';
import { View, Text, Input, Radio } from '@tarojs/components';
import '../../styles/invoice.css';
import Taro, { useDidShow, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
import { request, toast } from '../../lib/api';
import { usePageView } from '../../lib/usePageView';
import {
INVOICE_KIND_LABELS,
INVOICE_TITLE_TYPE_LABELS,
type InvoiceKind,
type UserInvoiceTitleDto,
} from '@dukang/shared-types';
const isH5 = process.env.TARO_ENV === 'h5';
type InvoiceStatus = {
exists: boolean;
status?: string | null;
};
type OrderBrief = {
orderNo?: string;
productName?: string;
productSpec?: string;
quantity?: number;
payAmount?: number;
status?: string;
};
const INVOICE_STATUS_LABELS: Record<string, string> = {
PENDING: '开票中',
ISSUED: '已开票',
REJECTED: '已驳回',
};
export default function InvoiceApplyPage() {
const router = useRouter();
const orderId = router.params.orderId ?? '';
usePageView('user_invoice_apply_view', orderId ? { orderId } : undefined);
const [titles, setTitles] = useState<UserInvoiceTitleDto[]>([]);
const [selectedId, setSelectedId] = useState('');
const [invoiceKind, setInvoiceKind] = useState<InvoiceKind>('NORMAL');
const [emailOverride, setEmailOverride] = useState('');
const [phoneOverride, setPhoneOverride] = useState('');
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [invoiceStatus, setInvoiceStatus] = useState<InvoiceStatus | null>(null);
const [order, setOrder] = useState<OrderBrief | null>(null);
const load = useCallback(() => {
if (!orderId) return;
setLoading(true);
Promise.all([
request<UserInvoiceTitleDto[]>(`/trade/orders/${orderId}/invoice-titles`).catch(() => []),
request<InvoiceStatus>(`/trade/orders/${orderId}/invoice-status`).catch(() => ({ exists: false })),
request<OrderBrief>(`/trade/orders/${orderId}`).catch(() => ({})),
])
.then(([list, status, orderData]) => {
const titlesList = Array.isArray(list) ? list : [];
setTitles(titlesList);
setInvoiceStatus(status);
setOrder(orderData || {});
const def = titlesList.find((t) => t.isDefault);
setSelectedId(def?.id || (titlesList[0]?.id ?? ''));
})
.catch(() => {
setTitles([]);
setInvoiceStatus({ exists: false });
})
.finally(() => setLoading(false));
}, [orderId]);
useEffect(() => {
void load();
}, [load]);
useDidShow(() => {
void load();
});
const selected = titles.find((t) => t.id === selectedId);
async function submit() {
if (!orderId || submitting) return;
if (order?.status && order.status !== 'COMPLETED') {
toast('仅已完成订单可申请发票');
return;
}
if (!selected) {
toast('请先选择发票抬头');
return;
}
const email = (selected.email || emailOverride).trim();
const phone = (selected.phone || phoneOverride).trim();
if (!email) {
toast('请填写接收邮箱');
return;
}
if (!phone) {
toast('请填写联系电话');
return;
}
if (invoiceKind === 'SPECIAL' && selected.titleType !== 'ENTERPRISE') {
toast('专用发票仅支持企业抬头');
return;
}
setSubmitting(true);
try {
await request(`/trade/orders/${orderId}/invoices`, {
method: 'POST',
data: {
titleId: selectedId,
invoiceKind,
email,
phone,
},
});
toast('发票申请已提交', 'success');
setTimeout(() => {
Taro.navigateBack().catch(() => {
Taro.redirectTo({ url: '/pages/orders/index?tab=completed' });
});
}, 1200);
} catch (e) {
toast(e instanceof Error ? e.message : '申请失败');
} finally {
setSubmitting(false);
}
}
function goAddTitle() {
Taro.navigateTo({
url: '/pages/invoice-titles/index',
});
}
const alreadyApplied = invoiceStatus?.exists;
const orderCompletable = !order?.status || order.status === 'COMPLETED';
const canApply = !alreadyApplied && orderCompletable;
const productLine = order
? [order.productName, order.productSpec, order.quantity ? `×${order.quantity}` : '']
.filter(Boolean)
.join(' ')
: '';
return (
<PageShell variant="sub" className="invoice-apply-page">
{isH5 ? (
<SubPageHeader
title="申请发票"
onBack={() => Taro.navigateBack().catch(() => Taro.switchTab({ url: '/pages/mine/index' }))}
/>
) : null}
<View className="invoice-apply-body">
{order?.orderNo ? (
<View className="invoice-apply-order">
<Text className="invoice-apply-order-label"></Text>
<Text className="invoice-apply-order-no">{order.orderNo}</Text>
{productLine ? (
<Text className="invoice-apply-order-meta">{productLine}</Text>
) : null}
{order.payAmount != null ? (
<Text className="invoice-apply-order-meta">
¥{Number(order.payAmount).toFixed(2)}
</Text>
) : null}
</View>
) : null}
{alreadyApplied ? (
<View className="invoice-apply-status-card">
<Text className="invoice-apply-status-icon"></Text>
<View>
<Text className="invoice-apply-status-title"></Text>
<Text className="invoice-apply-status-desc">
{INVOICE_STATUS_LABELS[invoiceStatus?.status ?? ''] || invoiceStatus?.status || '处理中'}
</Text>
</View>
</View>
) : null}
{!alreadyApplied && !orderCompletable ? (
<View className="invoice-apply-status-card">
<View>
<Text className="invoice-apply-status-title"></Text>
<Text className="invoice-apply-status-desc"></Text>
</View>
</View>
) : null}
{canApply ? (
<View className="invoice-title-field" style={{ marginBottom: 16 }}>
<Text className="invoice-title-label"></Text>
<View className="invoice-title-type-row">
{(['NORMAL', 'SPECIAL'] as InvoiceKind[]).map((k) => (
<Text
key={k}
className={`invoice-title-type-chip${invoiceKind === k ? ' active' : ''}`}
onClick={() => setInvoiceKind(k)}
>
{INVOICE_KIND_LABELS[k]}
</Text>
))}
</View>
</View>
) : null}
{canApply ? (
<View className="invoice-apply-section-title">
<Text></Text>
<Text className="invoice-apply-add-link" onClick={goAddTitle}>
+
</Text>
</View>
) : null}
{loading ? <View className="u-empty"></View> : null}
{canApply && !loading && titles.length === 0 ? (
<View className="invoice-apply-empty-titles">
<Text className="u-empty"></Text>
<View className="invoice-apply-empty-cta" onClick={goAddTitle}>
</View>
</View>
) : null}
{canApply && !loading && titles.length > 0 ? (
<View className="invoice-apply-title-list">
{titles.map((t) => (
<View
key={t.id}
className={`invoice-apply-title-item${selectedId === t.id ? ' is-selected' : ''}`}
onClick={() => setSelectedId(t.id)}
>
<View className="invoice-apply-title-info">
<View className="invoice-apply-title-head">
<Text className="invoice-apply-title-name">{t.titleName}</Text>
<Text className="invoice-apply-title-type">
{INVOICE_TITLE_TYPE_LABELS[t.titleType] ?? t.titleType}
</Text>
{t.isDefault ? <Text className="invoice-apply-title-default"></Text> : null}
</View>
{t.taxNo ? <Text className="invoice-apply-title-sub">{t.taxNo}</Text> : null}
{t.email ? <Text className="invoice-apply-title-sub">{t.email}</Text> : null}
</View>
<Radio
checked={selectedId === t.id}
color="#A61D24"
onClick={() => setSelectedId(t.id)}
/>
</View>
))}
</View>
) : null}
{canApply && selected && !selected.email ? (
<View className="invoice-title-field" style={{ marginTop: 16 }}>
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
placeholder="电子发票将发送至此邮箱"
value={emailOverride}
onInput={(e) => setEmailOverride(e.detail.value)}
/>
</View>
) : null}
{canApply && selected && !selected.phone ? (
<View className="invoice-title-field" style={{ marginTop: 12 }}>
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
type="number"
placeholder="请填写联系电话"
value={phoneOverride}
onInput={(e) => setPhoneOverride(e.detail.value)}
/>
</View>
) : null}
</View>
{canApply && titles.length > 0 ? (
<View className="invoice-apply-footer">
<View
className={`invoice-apply-submit${submitting ? ' is-disabled' : ''}`}
onClick={() => { if (!submitting) void submit(); }}
>
{submitting ? '提交中…' : '提交申请'}
</View>
</View>
) : null}
</PageShell>
);
}
@@ -0,0 +1,5 @@
export default definePageConfig({
navigationBarTitleText: '发票抬头',
enablePullDownRefresh: true,
backgroundTextStyle: 'dark',
});
@@ -0,0 +1,360 @@
import { useCallback, useEffect, useState } from 'react';
import { View, Text, Input, Switch, Image } from '@tarojs/components';
import '../../styles/invoice.css';
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
import { request, toast } from '../../lib/api';
import { usePageView } from '../../lib/usePageView';
import {
INVOICE_TITLE_TYPE_LABELS,
type InvoiceTitleType,
type UpsertInvoiceTitleRequest,
type UserInvoiceTitleDto,
} from '@dukang/shared-types';
import iconEdit from '../../assets/icons/编辑.png';
import iconDefault from '../../assets/icons/默认.png';
import iconDelete from '../../assets/icons/删除.png';
const isH5 = process.env.TARO_ENV === 'h5';
const ACTION_ICONS = {
edit: iconEdit,
star: iconDefault,
trash: iconDelete,
} as const;
type EditDraft = UpsertInvoiceTitleRequest & { id?: string };
function TitleActionIcon({
kind,
label,
onClick,
}: {
kind: keyof typeof ACTION_ICONS;
label: string;
onClick: () => void;
}) {
return (
<View
className="invoice-title-icon-btn"
hoverClass="invoice-title-icon-btn--hover"
aria-label={label}
onClick={onClick}
>
<Image className="invoice-title-icon-img" src={ACTION_ICONS[kind]} mode="aspectFit" />
</View>
);
}
const EMPTY_DRAFT: EditDraft = {
titleType: 'PERSONAL',
titleName: '',
taxNo: '',
email: '',
phone: '',
addressPhone: '',
bankAccount: '',
isDefault: false,
};
export default function InvoiceTitlesPage() {
usePageView('user_invoice_titles_view');
const [titles, setTitles] = useState<UserInvoiceTitleDto[]>([]);
const [loading, setLoading] = useState(true);
const [sheetOpen, setSheetOpen] = useState(false);
const [draft, setDraft] = useState<EditDraft>(EMPTY_DRAFT);
const [saving, setSaving] = useState(false);
const load = useCallback(() => {
setLoading(true);
return request<UserInvoiceTitleDto[]>('/trade/invoice-titles')
.then((data) => setTitles(Array.isArray(data) ? data : []))
.catch((e) => {
toast(e instanceof Error ? e.message : '加载失败');
setTitles([]);
})
.finally(() => setLoading(false));
}, []);
useEffect(() => {
void load();
}, [load]);
useDidShow(() => {
void load();
});
usePullDownRefresh(() => {
void load().finally(() => Taro.stopPullDownRefresh());
});
function openCreate() {
setDraft({ ...EMPTY_DRAFT });
setSheetOpen(true);
}
function openEdit(t: UserInvoiceTitleDto) {
setDraft({
id: t.id,
titleType: t.titleType,
titleName: t.titleName,
taxNo: t.taxNo ?? '',
email: t.email ?? '',
phone: t.phone ?? '',
addressPhone: t.addressPhone ?? '',
bankAccount: t.bankAccount ?? '',
isDefault: t.isDefault,
});
setSheetOpen(true);
}
async function save() {
if (saving) return;
const titleName = draft.titleName.trim();
if (!titleName) {
toast('请填写发票抬头名称');
return;
}
if (draft.titleType === 'ENTERPRISE' && !draft.taxNo?.trim()) {
toast('企业抬头须填写税号');
return;
}
setSaving(true);
try {
const payload: UpsertInvoiceTitleRequest = {
titleType: draft.titleType,
titleName,
taxNo: draft.taxNo?.trim() || null,
email: draft.email?.trim() || null,
phone: draft.phone?.trim() || null,
addressPhone: draft.addressPhone?.trim() || null,
bankAccount: draft.bankAccount?.trim() || null,
isDefault: !!draft.isDefault,
};
if (draft.id) {
await request(`/trade/invoice-titles/${draft.id}`, {
method: 'PUT',
data: payload,
});
toast('已更新', 'success');
} else {
await request('/trade/invoice-titles', { method: 'POST', data: payload });
toast('已添加', 'success');
}
setSheetOpen(false);
await load();
} catch (e) {
toast(e instanceof Error ? e.message : '保存失败');
} finally {
setSaving(false);
}
}
async function setDefault(t: UserInvoiceTitleDto) {
try {
await request(`/trade/invoice-titles/${t.id}`, {
method: 'PUT',
data: {
titleType: t.titleType,
titleName: t.titleName,
taxNo: t.taxNo,
email: t.email,
phone: t.phone,
addressPhone: t.addressPhone,
bankAccount: t.bankAccount,
isDefault: true,
},
});
toast('已设为默认', 'success');
await load();
} catch (e) {
toast(e instanceof Error ? e.message : '设置失败');
}
}
async function remove(id: string) {
const ok = await Taro.showModal({ title: '提示', content: '确认删除该发票抬头?' });
if (!ok.confirm) return;
try {
await request(`/trade/invoice-titles/${id}`, { method: 'DELETE' });
toast('已删除', 'success');
await load();
} catch (e) {
toast(e instanceof Error ? e.message : '删除失败');
}
}
return (
<PageShell variant="sub" className="invoice-titles-page">
{isH5 ? (
<SubPageHeader
title="发票抬头"
onBack={() => Taro.navigateBack().catch(() => Taro.switchTab({ url: '/pages/mine/index' }))}
/>
) : null}
<View className="invoice-titles-body">
{loading ? <View className="u-empty"></View> : null}
{!loading && titles.length === 0 ? (
<View className="u-empty"></View>
) : null}
{!loading &&
titles.map((t) => (
<View key={t.id} className="invoice-title-card">
<View className="invoice-title-card-head">
<Text className="invoice-title-name">{t.titleName}</Text>
<Text className="invoice-title-type">
{INVOICE_TITLE_TYPE_LABELS[t.titleType] ?? t.titleType}
</Text>
{t.isDefault ? <Text className="invoice-title-default"></Text> : null}
</View>
{t.taxNo ? <Text className="invoice-title-line">{t.taxNo}</Text> : null}
{t.email ? <Text className="invoice-title-line">{t.email}</Text> : null}
{t.phone ? <Text className="invoice-title-line">{t.phone}</Text> : null}
{t.addressPhone ? (
<Text className="invoice-title-line">{t.addressPhone}</Text>
) : null}
{t.bankAccount ? (
<Text className="invoice-title-line">{t.bankAccount}</Text>
) : null}
<View className="invoice-title-actions">
<TitleActionIcon kind="edit" label="编辑" onClick={() => openEdit(t)} />
{!t.isDefault ? (
<TitleActionIcon kind="star" label="设为默认" onClick={() => void setDefault(t)} />
) : null}
<TitleActionIcon kind="trash" label="删除" onClick={() => void remove(t.id)} />
</View>
</View>
))}
</View>
<View className="invoice-titles-fab" onClick={openCreate}>
<Text>+ </Text>
</View>
{sheetOpen ? (
<View className="invoice-title-sheet-mask">
<View className="invoice-title-sheet-mask-backdrop" onClick={() => !saving && setSheetOpen(false)} />
<View className="invoice-title-sheet">
<Text className="invoice-title-sheet-title">
{draft.id ? '编辑发票抬头' : '添加发票抬头'}
</Text>
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<View className="invoice-title-type-row">
{(['PERSONAL', 'ENTERPRISE'] as InvoiceTitleType[]).map((tp) => (
<Text
key={tp}
className={`invoice-title-type-chip${
draft.titleType === tp ? ' active' : ''
}`}
onClick={() => setDraft({ ...draft, titleType: tp })}
>
{INVOICE_TITLE_TYPE_LABELS[tp]}
</Text>
))}
</View>
</View>
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
maxlength={128}
placeholder="个人姓名或企业名称"
value={draft.titleName}
onInput={(e) => setDraft({ ...draft, titleName: e.detail.value })}
/>
</View>
{draft.titleType === 'ENTERPRISE' ? (
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
maxlength={32}
placeholder="企业税号"
value={draft.taxNo || ''}
onInput={(e) => setDraft({ ...draft, taxNo: e.detail.value })}
/>
</View>
) : null}
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
maxlength={128}
placeholder="电子发票将发送至此邮箱"
value={draft.email || ''}
onInput={(e) => setDraft({ ...draft, email: e.detail.value })}
/>
</View>
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
type="number"
maxlength={20}
placeholder="选填"
value={draft.phone || ''}
onInput={(e) => setDraft({ ...draft, phone: e.detail.value })}
/>
</View>
{draft.titleType === 'ENTERPRISE' ? (
<>
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
maxlength={256}
placeholder="专用发票需要,选填"
value={draft.addressPhone || ''}
onInput={(e) => setDraft({ ...draft, addressPhone: e.detail.value })}
/>
</View>
<View className="invoice-title-field">
<Text className="invoice-title-label"></Text>
<Input
className="invoice-title-input"
maxlength={256}
placeholder="专用发票需要,选填"
value={draft.bankAccount || ''}
onInput={(e) => setDraft({ ...draft, bankAccount: e.detail.value })}
/>
</View>
</>
) : null}
<View className="invoice-title-default-row">
<Text className="invoice-title-label"></Text>
<Switch
checked={!!draft.isDefault}
color="#A61D24"
onChange={(e) => setDraft({ ...draft, isDefault: !!e.detail.value })}
/>
</View>
<View className="invoice-title-sheet-actions">
<Text
className="invoice-title-sheet-cancel"
onClick={() => !saving && setSheetOpen(false)}
>
</Text>
<Text
className={`invoice-title-sheet-save${saving ? ' is-disabled' : ''}`}
onClick={() => { if (!saving) void save(); }}
>
{saving ? '保存中…' : '保存'}
</Text>
</View>
</View>
</View>
) : null}
</PageShell>
);
}
+1
View File
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { View, Text, Input, Image } from '@tarojs/components';
import '../../styles/login.css';
import Taro, { useRouter } from '@tarojs/taro';
import {
SmsScene,
+1
View File
@@ -56,6 +56,7 @@ const SERVICES = [
{ icon: iconCs, label: '联系客服', url: '/pages/customer-service/index' },
{ icon: iconQualification, label: '资质公示', action: 'qualification' as const },
{ icon: iconAbout, label: '关于我们', action: 'about' as const },
{ icon: iconAbout, label: '发票管理', url: '/pages/invoice-titles/index' },
] as const;
const isWeapp = process.env.TARO_ENV === 'weapp';
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useDidShow, useRouter, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -27,6 +28,7 @@ import {
toWeappShareTimeline,
} from '../../lib/wechat-share';
import { usePageView } from '../../lib/usePageView';
import type { InvoiceStatus } from '@dukang/shared-types';
const isWeapp = process.env.TARO_ENV === 'weapp';
@@ -58,6 +60,7 @@ type OrderDetail = {
deliveryType?: string;
items?: OrderItem[];
wechatConfirm?: WechatConfirmPayload | null;
invoiceStatus?: InvoiceStatus | null;
delivery?: {
provider?: string;
trackingNo?: string;
@@ -150,6 +153,9 @@ export default function OrderDetailPage() {
const canPay = !!order && order.status === 'PENDING_PAY' && !isReship;
const canConfirmReceive =
!!order && !isReship && !isProxy && ['PENDING_RECEIVE', 'DELIVERED'].includes(order.status || '');
const canInvoice =
!!order && !isReship && order.status === 'COMPLETED' && order.invoiceStatus !== 'PENDING' && order.invoiceStatus !== 'ISSUED';
const invoicePending = !!order && !isReship && order.status === 'COMPLETED' && order.invoiceStatus === 'PENDING';
const canViewLogistics =
!!order &&
order.deliveryType !== 'ON_SITE_PICKUP' &&
@@ -403,6 +409,22 @@ export default function OrderDetailPage() {
{confirming ? '提交中…' : '确认收货'}
</View>
) : null}
{canInvoice ? (
<View
className="order-detail-invoice-btn"
onClick={() => Taro.navigateTo({ url: `/pages/invoice-apply/index?orderId=${order.id}` })}
>
</View>
) : null}
{invoicePending ? (
<View
className="order-detail-invoice-status"
onClick={() => Taro.navigateTo({ url: `/pages/invoice-apply/index?orderId=${order.id}` })}
>
</View>
) : null}
</View>
) : null}
</PageShell>
@@ -1,5 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
+28 -1
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useDidShow, usePullDownRefresh, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -7,7 +8,7 @@ import { request, toast } from '../../lib/api';
import { buildPayUrl } from '../../lib/checkout-nav';
import { usePageView } from '../../lib/usePageView';
import { ORDER_STATUS_LABELS } from '@dukang/shared-types';
import { ORDER_STATUS_LABELS, type InvoiceStatus } from '@dukang/shared-types';
const TABS = [
{ key: 'all', label: '全部订单' },
@@ -50,6 +51,7 @@ type OrderRow = {
isProxyOrder?: boolean;
proxyPartnerName?: string | null;
items?: OrderItem[];
invoiceStatus?: InvoiceStatus | null;
};
export default function OrdersPage() {
@@ -125,6 +127,9 @@ export default function OrdersPage() {
const qty = item?.quantity ?? o.quantity ?? o.qty ?? 1;
const unitPrice = Number(item?.unitPrice ?? 0);
const canPay = o.status === 'PENDING_PAY' && !o.originOrderId;
const invoiceBlocking = o.invoiceStatus === 'PENDING' || o.invoiceStatus === 'ISSUED';
const canInvoice = o.status === 'COMPLETED' && !o.originOrderId && !invoiceBlocking;
const invoicePending = o.status === 'COMPLETED' && o.invoiceStatus === 'PENDING';
const isProxy = o.isProxyOrder || o.orderType === 'PROXY';
return (
@@ -177,6 +182,28 @@ export default function OrdersPage() {
</View>
) : null}
{canInvoice ? (
<View
className="order-list-invoice-btn"
onClick={(e) => {
e.stopPropagation();
Taro.navigateTo({ url: `/pages/invoice-apply/index?orderId=${o.id}` });
}}
>
</View>
) : null}
{invoicePending ? (
<View
className="order-list-invoice-status"
onClick={(e) => {
e.stopPropagation();
Taro.navigateTo({ url: `/pages/invoice-apply/index?orderId=${o.id}` });
}}
>
</View>
) : null}
</View>
</View>
);
+1
View File
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useDidShow, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useCallback, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useDidShow, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,4 +1,5 @@
import { View, Text } from '@tarojs/components';
import '../../styles/legal.css';
import { getLegalDocument } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/product-detail.css';
import Taro, {
useDidShow,
usePageScroll,
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/redeem.css';
import Taro, { useRouter } from '@tarojs/taro';
import { REDEEM_TOKEN_TTL_SECONDS } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
@@ -1,5 +1,6 @@
import { useMemo, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/redeem.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { View, Text, Input } from '@tarojs/components';
import '../../styles/redeem.css';
import Taro, { useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import '../../styles/store-detail.css';
import Taro, {
useDidShow,
useLoad,
@@ -1,5 +1,7 @@
import { useCallback, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/store-detail.css';
import '../../styles/product-detail.css';
import Taro, { useLoad, useRouter } from '@tarojs/taro';
import { normalizeStorePackageImageUrls } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
@@ -1,4 +1,5 @@
import { View, Text } from '@tarojs/components';
import '../../styles/legal.css';
import { getLegalDocument } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';