159 lines
6.0 KiB
TypeScript
159 lines
6.0 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
import {
|
|
INVOICE_KIND_LABELS,
|
|
INVOICE_TITLE_TYPE_LABELS,
|
|
type InvoiceKind,
|
|
type InvoiceTitleType,
|
|
} from '@dukang/shared-types';
|
|
import SubPageHeader from '../components/SubPageHeader';
|
|
import { request } from '../lib/api';
|
|
|
|
type OrderRow = { id: string; orderNo: string; payAmount: number | string; productName?: string };
|
|
|
|
export default function InvoiceApplyPage() {
|
|
const navigate = useNavigate();
|
|
const [params] = useSearchParams();
|
|
const presetOrderId = params.get('orderId') || '';
|
|
|
|
const [orders, setOrders] = useState<OrderRow[]>([]);
|
|
const [orderId, setOrderId] = useState(presetOrderId);
|
|
const [titleType, setTitleType] = useState<InvoiceTitleType>('PERSONAL');
|
|
const [invoiceKind, setInvoiceKind] = useState<InvoiceKind>('NORMAL');
|
|
const [titleName, setTitleName] = useState('');
|
|
const [taxNo, setTaxNo] = useState('');
|
|
const [addressPhone, setAddressPhone] = useState('');
|
|
const [bankAccount, setBankAccount] = useState('');
|
|
const [email, setEmail] = useState('');
|
|
const [phone, setPhone] = useState('');
|
|
const [submitting, setSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
request<{ list?: OrderRow[] }>('USER_H5', '/trade/orders?tab=completed&pageSize=50')
|
|
.then((res) => setOrders(res.list ?? []))
|
|
.catch(() => setOrders([]));
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (invoiceKind === 'SPECIAL') setTitleType('ENTERPRISE');
|
|
}, [invoiceKind]);
|
|
|
|
async function submit() {
|
|
if (!orderId) {
|
|
window.alert('请选择订单');
|
|
return;
|
|
}
|
|
if (!titleName.trim() || !email.trim() || !phone.trim()) {
|
|
window.alert('请填写抬头名称、邮箱与手机号');
|
|
return;
|
|
}
|
|
setSubmitting(true);
|
|
try {
|
|
await request('USER_H5', `/trade/orders/${orderId}/invoices`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
titleType,
|
|
invoiceKind,
|
|
titleName: titleName.trim(),
|
|
taxNo: taxNo.trim() || undefined,
|
|
addressPhone: addressPhone.trim() || undefined,
|
|
bankAccount: bankAccount.trim() || undefined,
|
|
email: email.trim(),
|
|
phone: phone.trim(),
|
|
}),
|
|
});
|
|
window.alert('发票申请已提交,总部将在 2 个工作日内开具');
|
|
navigate('/invoices');
|
|
} catch (e) {
|
|
window.alert(e instanceof Error ? e.message : '申请失败');
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="after-sale-page">
|
|
<SubPageHeader title="申请发票" onBack={() => navigate(-1)} />
|
|
<main className="after-sale-body after-sale-form">
|
|
<label className="after-sale-label">选择已完成订单</label>
|
|
<div className="after-sale-order-list" style={{ marginBottom: 16 }}>
|
|
{orders.length === 0 ? (
|
|
<p className="after-sale-empty">暂无已完成订单</p>
|
|
) : (
|
|
orders.map((o) => (
|
|
<button
|
|
key={o.id}
|
|
type="button"
|
|
className={`after-sale-order-item${orderId === o.id ? ' is-selected' : ''}`}
|
|
onClick={() => setOrderId(o.id)}
|
|
>
|
|
<p className="after-sale-order-no">{o.orderNo}</p>
|
|
<p className="after-sale-order-meta">
|
|
{o.productName || '商品'} · ¥{Number(o.payAmount).toFixed(2)}
|
|
</p>
|
|
</button>
|
|
))
|
|
)}
|
|
</div>
|
|
|
|
<label className="after-sale-label">发票类型</label>
|
|
<div className="invoice-chip-row">
|
|
{(Object.keys(INVOICE_KIND_LABELS) as InvoiceKind[]).map((k) => (
|
|
<button
|
|
key={k}
|
|
type="button"
|
|
className={`invoice-chip${invoiceKind === k ? ' is-selected' : ''}`}
|
|
onClick={() => setInvoiceKind(k)}
|
|
>
|
|
{INVOICE_KIND_LABELS[k]}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<label className="after-sale-label">抬头类型</label>
|
|
<div className="invoice-chip-row">
|
|
{(Object.keys(INVOICE_TITLE_TYPE_LABELS) as InvoiceTitleType[]).map((t) => (
|
|
<button
|
|
key={t}
|
|
type="button"
|
|
className={`invoice-chip${titleType === t ? ' is-selected' : ''}`}
|
|
disabled={invoiceKind === 'SPECIAL' && t === 'PERSONAL'}
|
|
onClick={() => setTitleType(t)}
|
|
>
|
|
{INVOICE_TITLE_TYPE_LABELS[t]}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<label className="after-sale-label">抬头名称</label>
|
|
<input className="after-sale-input" value={titleName} onChange={(e) => setTitleName(e.target.value)} placeholder="个人姓名或公司全称" />
|
|
|
|
{(titleType === 'ENTERPRISE' || invoiceKind === 'SPECIAL') && (
|
|
<>
|
|
<label className="after-sale-label">税号</label>
|
|
<input className="after-sale-input" value={taxNo} onChange={(e) => setTaxNo(e.target.value)} placeholder="纳税人识别号" />
|
|
</>
|
|
)}
|
|
|
|
{invoiceKind === 'SPECIAL' && (
|
|
<>
|
|
<label className="after-sale-label">地址与电话</label>
|
|
<input className="after-sale-input" value={addressPhone} onChange={(e) => setAddressPhone(e.target.value)} />
|
|
<label className="after-sale-label">开户行与账号</label>
|
|
<input className="after-sale-input" value={bankAccount} onChange={(e) => setBankAccount(e.target.value)} />
|
|
</>
|
|
)}
|
|
|
|
<label className="after-sale-label">接收邮箱</label>
|
|
<input className="after-sale-input" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
<label className="after-sale-label">手机号</label>
|
|
<input className="after-sale-input" value={phone} onChange={(e) => setPhone(e.target.value)} />
|
|
|
|
<button type="button" className="after-sale-primary" disabled={submitting} onClick={() => void submit()}>
|
|
{submitting ? '提交中…' : '提交申请'}
|
|
</button>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|