Files
hjc-web/app/components/HjcTenderFiles.vue
T
weicw1996 1ff4dfffb9 feat(hjc-web): 订单页展示并下载电子发票
发票由一站式平台开具后推给后端(官方网不开票,见工作区根目录 docs/adr/0006),
订单响应里新增 invoices;文件是本平台副本,下载走与附件同一条链路(点击时取票)。

- 抽出 composable useAttachmentDownload:把「先换票据、再取文件」的逻辑从
  HjcTenderFiles 里提出来,附件与发票共用(各写一份必然分叉);同时把资源标识解析
  扩到 orderNo/projectNo/invoiceId 三种。
- 新增 HjcInvoiceFiles.vue:发票号/类型/开票日期/金额 + 下载;作废(红冲)的也列出来
  且仍可下载——它是留档凭证。
- HjcTenderFiles.vue 改用 composable,行为不变。
- 订单详情加发票区块,订单列表加「已开票」标记与发票列表。
- 不判"是否已支付":有没有发票取决于甲方开没开票,与订单当前状态无关。

校验:npm run build 通过。
2026-09-18 17:33:01 +08:00

82 lines
2.6 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.
<template>
<ul v-if="files.length" class="space-y-2">
<li
v-for="(file, idx) in files"
:key="file.url + idx"
class="flex items-center gap-3 rounded-lg border border-gray-200 px-4 py-2.5"
>
<!-- 用内联 SVG避免为一个小图标引入图标库依赖 PageAttachments.vue -->
<span class="shrink-0 text-blue-600" aria-hidden="true">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14 2v6h6" />
</svg>
</span>
<button
type="button"
class="min-w-0 flex-1 truncate text-left text-sm text-blue-600 hover:underline disabled:opacity-60"
:title="nameOf(file)"
:disabled="busyKey === file.url"
@click="download(file, file.url)"
>
{{ nameOf(file) }}
</button>
<button
type="button"
class="shrink-0 text-sm text-blue-600 hover:underline disabled:opacity-60"
:disabled="busyKey === file.url"
@click="download(file, file.url)"
>
{{ busyKey === file.url ? '正在准备…' : '下载' }}
</button>
</li>
</ul>
<p v-if="errorText" class="mt-2 text-sm text-red-500">{{ errorText }}</p>
</template>
<script setup lang="ts">
import type { HjcTenderFile } from '~/types/tender'
/**
* 附件列表(订单列表页、订单详情页、项目详情的公告附件共用)。
*
* 只渲染后端下发的 `tenderFiles` / `bulletinFiles`**发不发由后端定**
* (标书附件仅 `payStatus === 1` 的订单下发、公告附件随项目上下架),
* 所以这里不做任何权限判断,也不从项目字段里自己拼地址。
*
* 「点击下载」走 `useAttachmentDownload`:先换一张一次性票据、再取文件
* (为什么要绕这一圈见该 composable 的注释)。
*/
const props = withDefaults(
defineProps<{
files?: HjcTenderFile[] | null
}>(),
{
files: () => []
}
)
/** 过滤掉没有地址的脏数据(后端已保证,前端再兜一层,避免渲染出死链) */
const files = computed<HjcTenderFile[]>(() =>
(props.files || []).filter((f) => !!f && typeof f.url === 'string' && f.url.length > 0)
)
const { busyKey, errorText, download } = useAttachmentDownload()
function nameOf(file: HjcTenderFile) {
return file.name || '标书附件'
}
</script>