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 (
);
}
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([]);
const [loading, setLoading] = useState(true);
const [sheetOpen, setSheetOpen] = useState(false);
const [draft, setDraft] = useState(EMPTY_DRAFT);
const [saving, setSaving] = useState(false);
const load = useCallback(() => {
setLoading(true);
return request('/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 (
{isH5 ? (
Taro.navigateBack().catch(() => Taro.switchTab({ url: '/pages/mine/index' }))}
/>
) : null}
{loading ? 加载中… : null}
{!loading && titles.length === 0 ? (
暂无发票抬头,点击右上角添加
) : null}
{!loading &&
titles.map((t) => (
{t.titleName}
{INVOICE_TITLE_TYPE_LABELS[t.titleType] ?? t.titleType}
{t.isDefault ? 默认 : null}
{t.taxNo ? 税号:{t.taxNo} : null}
{t.email ? 邮箱:{t.email} : null}
{t.phone ? 电话:{t.phone} : null}
{t.addressPhone ? (
地址电话:{t.addressPhone}
) : null}
{t.bankAccount ? (
开户行账号:{t.bankAccount}
) : null}
openEdit(t)} />
{!t.isDefault ? (
void setDefault(t)} />
) : null}
void remove(t.id)} />
))}
+ 添加抬头
{sheetOpen ? (
!saving && setSheetOpen(false)} />
{draft.id ? '编辑发票抬头' : '添加发票抬头'}
抬头类型
{(['PERSONAL', 'ENTERPRISE'] as InvoiceTitleType[]).map((tp) => (
setDraft({ ...draft, titleType: tp })}
>
{INVOICE_TITLE_TYPE_LABELS[tp]}
))}
抬头名称
setDraft({ ...draft, titleName: e.detail.value })}
/>
{draft.titleType === 'ENTERPRISE' ? (
税号
setDraft({ ...draft, taxNo: e.detail.value })}
/>
) : null}
接收邮箱
setDraft({ ...draft, email: e.detail.value })}
/>
联系电话
setDraft({ ...draft, phone: e.detail.value })}
/>
{draft.titleType === 'ENTERPRISE' ? (
<>
地址电话
setDraft({ ...draft, addressPhone: e.detail.value })}
/>
开户行账号
setDraft({ ...draft, bankAccount: e.detail.value })}
/>
>
) : null}
设为默认抬头
setDraft({ ...draft, isDefault: !!e.detail.value })}
/>
!saving && setSheetOpen(false)}
>
取消
{ if (!saving) void save(); }}
>
{saving ? '保存中…' : '保存'}
) : null}
);
}