Files
console-dev/app/pages/invoices/invoice.vue
T
cneicy 42dfead9cd
前端验证 / verify (push) Failing after 15m3s
feat(ui): 优化控制台响应式布局与视觉体验
- 统一页面标题、圆角、字体和响应式断点,调整暗色主题及图表配色
- 新增表格列标签插件,在窄屏下将表格转换为卡片布局,优化实例、订单、工单等列表展示
- 支持移动端点击订单行进入详情,并完善订单凭据的打印布局
- 在财务中心展示本月及累计消费,补充充值关闭时的提示
- 优化工单详情布局,将附件上传入口整合至富文本编辑器工具栏
- 调整实例操作按钮、SSH 密钥操作及安全菜单图标
- 为多个页面显式导入 definePageMeta,完善页面元数据声明
2026-11-07 00:09:57 +08:00

633 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
/**
* 发票详情:按查询 ID 加载发票,区分历史登记与正式电子文件;下载链接必须通过协议与字符校验。
*/
import { definePageMeta } from "#imports";
import "~/assets/styles/invoice.B6sYFYlb.css";
definePageMeta({
key: (e) => e.fullPath,
});
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { normalizeClass } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { createElementBlock } from "vue";
import { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { useRoute } from "#app";
import { NuxtLink } from "#components";
import { sanitizeMessage } from "~/lib/auth.js";
import { useApi } from "~/lib/auth.js";
import button from "primevue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import message from "primevue/message";
import { n as dict_n } from "~/lib/dict.js";
import skeleton from "primevue/skeleton";
import { t as composables_t } from "~/lib/composables-2.js";
var T = {
class: `invoice-detail-page`,
},
E = {
key: 1,
class: `invoice-error`,
},
D = {
key: 2,
class: `invoice-paper`,
},
O = {
class: `invoice-number`,
},
k = {
key: 0,
},
A = {
key: 1,
},
j = {
key: 2,
},
M = {
key: 3,
},
N = {
key: 4,
},
P = {
class: `invoice-total`,
},
F = {
class: `invoice-actions`,
},
I = [`href`],
L = {
key: 2,
class: `invoice-file-hint`,
},
R = _plugin_vue_export_helper_t(
defineComponent({
__name: `invoice`,
setup(f) {
composables_t({
key: (e) => e.fullPath,
});
let b = useRoute(),
R = useApi(),
z = ref(null),
B = ref(!0),
V = ref(``),
H = ref(!1),
U = ref(``),
W = {
pending: `待审核`,
unpaid: `待支付`,
wait_create: `待开具`,
creating: `开具中`,
create_fail: `开具失败`,
approved: `已审核`,
reject: `已驳回`,
rejected: `已驳回`,
issued: `已开具`,
wait_send: `待寄出`,
sent: `已寄出`,
cancel: `已取消`,
flushed: `已冲红`,
},
G = computed(() => z.value?.invoice_project === `历史已开票登记`),
K = computed(() =>
G.value
? `历史已开票登记`
: W[String(z.value?.status || ``).toLowerCase()] ||
String(z.value?.status || `—`),
);
// 下载地址仅允许 HTTP(S) 或站内绝对路径,拒绝反斜杠、空白控制字符和协议相对地址。
function q(e) {
return typeof e != `string` || /[\u0000-\u0020\\]/.test(e)
? ``
: /^https?:\/\//i.test(e) || /^\/(?!\/)/.test(e)
? e
: ``;
}
// 从详情的候选字段中选择第一个非空值,用于兼容不同发票字段展示。
function J(...e) {
for (let t of e) {
let e = z.value?.[t];
if (e != null && String(e) !== ``) return e;
}
return ``;
}
// 普通发票尚无有效下载地址时单独请求文件地址;历史已开票登记不伪造下载文件。
async function Y(e) {
if (!(G.value || q(z.value?.invoice_url))) {
((H.value = !0), (U.value = ``));
try {
let t = await R.get(`/invoice/${e}/invoice_filename`),
n = t.data && typeof t.data == `object` ? t.data : t,
r = q(n.url ?? n.invoice_url);
if (r && z.value)
z.value = {
...z.value,
invoice_url: r,
};
else throw Error(`正式发票文件尚未提供`);
} catch (e) {
U.value = e instanceof Error ? e.message : `获取发票下载地址失败`;
} finally {
H.value = !1;
}
}
}
// 校验查询 ID 并确认响应含发票资料,再按需要获取正式文件地址。
async function X() {
((B.value = !0), (V.value = ``));
try {
let e = String(b.query.id || ``);
if (!/^\d+$/.test(e)) throw Error(`请选择要查看的发票`);
let t = await R.get(`/invoice/${e}`),
n = t.invoice && typeof t.invoice == `object` ? t.invoice : t;
if (!n.id && !n.title && !n.invoice_project)
throw Error(`发票不存在或暂不可用`);
((z.value = n), G.value || Y(String(n.id || e)));
} catch (e) {
V.value = e instanceof Error ? e.message : `加载发票失败`;
} finally {
B.value = !1;
}
}
let Z = computed(() => q(z.value?.invoice_url));
return (
onMounted(X),
// 历史开票登记与正式文件下载分开展示,仅使用经过 q 校验的 URL 作为下载入口。
(t, i) => {
let s = message,
f = skeleton,
h = button,
v = NuxtLink;
return (
openBlock(),
createElementBlock(`div`, T, [
createVNode(
s,
{
severity: `info`,
closable: !1,
},
{
default: withCtx(() => [
...(i[1] ||= [
createTextVNode(
`发票内容以开票机构出具的正式文件为准,请下载后查看完整票面信息。`,
-1,
),
]),
]),
_: 1,
},
),
unref(B)
? (openBlock(),
createBlock(f, {
key: 0,
height: `480px`,
}))
: unref(V)
? (openBlock(),
createElementBlock(`div`, E, [
createVNode(
s,
{
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(V)), 1),
]),
_: 1,
},
),
createVNode(h, {
label: `重新加载`,
icon: `pi pi-refresh`,
severity: `secondary`,
outlined: ``,
onClick: X,
}),
]))
: unref(z)
? (openBlock(),
createElementBlock(`article`, D, [
(i[16] ||= createElementVNode(
`h1`,
null,
`发票详情`,
-1,
)),
createElementVNode(
`p`,
O,
`发票 ID:` +
toDisplayString(unref(z).id || unref(b).query.id),
1,
),
createElementVNode(`dl`, null, [
createElementVNode(`div`, null, [
(i[2] ||= createElementVNode(
`dt`,
null,
`发票抬头`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(z).title || `—`),
1,
),
]),
createElementVNode(`div`, null, [
(i[3] ||= createElementVNode(
`dt`,
null,
`开票项目`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).invoice_project || `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[4] ||= createElementVNode(
`dt`,
null,
`收件地址`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).email || unref(z).address || `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[5] ||= createElementVNode(
`dt`,
null,
`申请日期`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
unref(z).create_time
? (`formatDate` in t
? t.formatDate
: unref(dict_n))(
Number(unref(z).create_time),
)
: `—`,
),
1,
),
]),
createElementVNode(`div`, null, [
(i[6] ||= createElementVNode(
`dt`,
null,
`格式`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(z).invoice_format || `—`),
1,
),
]),
createElementVNode(`div`, null, [
(i[7] ||= createElementVNode(
`dt`,
null,
`状态`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(unref(K)),
1,
),
]),
J(`tax_price`, `tax_amount`) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, k, [
(i[8] ||= createElementVNode(
`dt`,
null,
`税费`,
-1,
)),
createElementVNode(
`dd`,
null,
`¥` +
toDisplayString(
J(`tax_price`, `tax_amount`),
),
1,
),
])),
J(`parcel_price`, `shipping_fee`, `express_fee`) ===
``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, A, [
(i[9] ||= createElementVNode(
`dt`,
null,
`快递费`,
-1,
)),
createElementVNode(
`dd`,
null,
`¥` +
toDisplayString(
J(
`parcel_price`,
`shipping_fee`,
`express_fee`,
),
),
1,
),
])),
J(
`express_company`,
`logistics_company`,
`parcel_name`,
) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, j, [
(i[10] ||= createElementVNode(
`dt`,
null,
`物流公司`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
J(
`express_company`,
`logistics_company`,
`parcel_name`,
),
),
1,
),
])),
J(
`express_number`,
`tracking_number`,
`parcel_number`,
) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, M, [
(i[11] ||= createElementVNode(
`dt`,
null,
`物流单号`,
-1,
)),
createElementVNode(
`dd`,
null,
toDisplayString(
J(
`express_number`,
`tracking_number`,
`parcel_number`,
),
),
1,
),
])),
J(`invoice_order_id`, `fee_order_id`) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`div`, N, [
(i[12] ||= createElementVNode(
`dt`,
null,
`费用订单`,
-1,
)),
createElementVNode(
`dd`,
null,
`#` +
toDisplayString(
J(`invoice_order_id`, `fee_order_id`),
),
1,
),
])),
]),
createElementVNode(`div`, P, [
(i[13] ||= createElementVNode(
`span`,
null,
`价税合计`,
-1,
)),
createElementVNode(
`strong`,
null,
`¥` + toDisplayString(unref(z).price ?? `—`),
1,
),
]),
unref(z).reject_reason
? (openBlock(),
createBlock(
s,
{
key: 0,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(
unref(sanitizeMessage)(
unref(z).reject_reason,
),
),
1,
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createElementVNode(`div`, F, [
createVNode(
h,
{
"as-child": ``,
severity: `secondary`,
outlined: ``,
},
{
default: withCtx((e) => [
createVNode(
v,
{
to: `/invoices`,
class: normalizeClass(e.class),
},
{
default: withCtx(() => [
...(i[14] ||= [
createTextVNode(`返回发票列表`, -1),
]),
]),
_: 1,
},
8,
[`class`],
),
]),
_: 1,
},
),
unref(Z)
? (openBlock(),
createBlock(
h,
{
key: 0,
"as-child": ``,
},
{
default: withCtx((e) => [
createElementVNode(
`a`,
{
href: unref(Z),
class: normalizeClass(e.class),
target: `_blank`,
rel: `noopener noreferrer`,
},
[
...(i[15] ||= [
createElementVNode(
`i`,
{
class: `pi pi-download`,
},
null,
-1,
),
createTextVNode(` 下载发票`, -1),
]),
],
10,
I,
),
]),
_: 1,
},
))
: unref(G)
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
h,
{
key: 1,
label: `获取发票文件`,
icon: `pi pi-download`,
severity: `secondary`,
outlined: ``,
loading: unref(H),
disabled: unref(H),
onClick: (i[0] ||= (e) =>
Y(
String(
unref(z).id ||
unref(b).query.id ||
``,
),
)),
},
null,
8,
[`loading`, `disabled`],
)),
]),
unref(U)
? (openBlock(),
createBlock(
s,
{
key: 1,
severity: `warn`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(U)) +
`。可稍后重试获取。`,
1,
),
]),
_: 1,
},
))
: !unref(Z) && !unref(G)
? (openBlock(),
createElementBlock(
`p`,
L,
`正式发票文件尚未提供,可在交付后查看或下载。`,
))
: createCommentVNode(``, !0),
]))
: createCommentVNode(``, !0),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-3b8f6704`]],
);
export {};
export default R;
</script>