Merge branch 'feat/13-order-invoice-frontends' into feat/client-feedback-2026-10
This commit is contained in:
@@ -39,6 +39,24 @@
|
||||
<label class="text-sm text-gray-600">购买数量</label>
|
||||
<input v-model.number="form.quantity" type="number" min="1" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-sm text-gray-600">发票类型</span>
|
||||
<!--
|
||||
预选值来自**企业档案**里的 invoiceType(注册时已必填);买家改了以买家为准,
|
||||
企业档案不因下单而改变(订单是快照)。档案取不到或值非法时**不预选**。
|
||||
-->
|
||||
<div class="mt-2 flex flex-wrap items-center gap-6">
|
||||
<label
|
||||
v-for="t in INVOICE_TYPES"
|
||||
:key="t.value"
|
||||
class="flex items-center gap-2 text-sm text-gray-700"
|
||||
>
|
||||
<input v-model="form.invoiceType" type="radio" :value="t.value" :disabled="submitting" />
|
||||
{{ t.label }}
|
||||
</label>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-400">默认取企业档案里的发票类型,可改;按下单时选的类型开票</p>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50" :disabled="submitting">
|
||||
{{ submitting ? '提交中...' : '提交订单并支付' }}
|
||||
@@ -118,15 +136,33 @@ const payDeadline = ref<string | null>(null)
|
||||
/** 支付时限已到:进支付步就过期、或面板挂着挂着归零(面板的 `timeout` 事件) */
|
||||
const deadlineExpired = ref(false)
|
||||
|
||||
const form = reactive({ contactName: '', contactPhone: '', contactEmail: '', quantity: 1 })
|
||||
const form = reactive({ contactName: '', contactPhone: '', contactEmail: '', quantity: 1, invoiceType: '' })
|
||||
const redirect = computed(() => `/buy?id=${id}`)
|
||||
|
||||
/**
|
||||
* 发票类型:单选、必填,取值与后端 `hjc_invoice.invoice_type` 口径一致。
|
||||
* 初始值取企业档案里的 invoiceType(见下方 setup 取数),买家可改。
|
||||
*/
|
||||
const INVOICE_TYPES = [
|
||||
{ value: 'VAT_SPECIAL', label: '电子专票' },
|
||||
{ value: 'VAT_NORMAL', label: '电子普票' }
|
||||
]
|
||||
|
||||
function isValidInvoiceType(v: unknown): v is string {
|
||||
return INVOICE_TYPES.some((t) => t.value === v)
|
||||
}
|
||||
|
||||
function formatMoney(n?: number | string) {
|
||||
return Number(n ?? 0).toFixed(2)
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
error.value = ''
|
||||
if (!isValidInvoiceType(form.invoiceType)) {
|
||||
// 必填、点名到项:没有值就拦在发请求之前
|
||||
error.value = '请选择发票类型'
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
// 代理原样透传 ApiResult:按 body.code 判定(HTTP 状态码恒为 200,不可用于判定)
|
||||
@@ -137,7 +173,9 @@ async function submit() {
|
||||
quantity: form.quantity || 1,
|
||||
contactName: form.contactName,
|
||||
contactPhone: form.contactPhone,
|
||||
contactEmail: form.contactEmail
|
||||
contactEmail: form.contactEmail,
|
||||
// 下单那一刻的发票类型(订单是快照,不回写企业档案)
|
||||
invoiceType: form.invoiceType
|
||||
}
|
||||
})
|
||||
const orderAuth = await handleAuthCode(order, redirect.value)
|
||||
@@ -205,4 +243,22 @@ try {
|
||||
} catch {
|
||||
notFound.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 预选发票类型 = 企业档案里的 `invoiceType`(注册时已必填,正常不为空)。
|
||||
*
|
||||
* 与上面同理必须用 `useRequestFetch`:裸 `$fetch` 在 SSR 不转发浏览器原始请求头,
|
||||
* 代理会当成未登录/空租户,服务端取不到档案而客户端取得到 → hydration mismatch。
|
||||
* 档案取不到、或值不在枚举内 → **不预选**,由提交那道的必选提示兜住:
|
||||
* 拿不到档案不该阻断下单(接口失败时买家仍可自己选一个继续)。
|
||||
*/
|
||||
if (loggedIn.value) {
|
||||
try {
|
||||
const ent: any = await requestFetch('/api/tender/enterprise')
|
||||
const archiveType = ent?.code === 0 ? ent?.data?.invoiceType : ''
|
||||
if (isValidInvoiceType(archiveType)) form.invoiceType = archiveType
|
||||
} catch {
|
||||
// 取不到档案不影响下单:不预选,提交时必选兜底
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,9 +5,12 @@ import { getTenantFromContext } from '../../utils/tenant'
|
||||
|
||||
/**
|
||||
* 创建标书订单
|
||||
* POST /api/tender/order { projectId, quantity, contactName, contactPhone, contactEmail }
|
||||
* POST /api/tender/order { projectId, quantity, contactName, contactPhone, contactEmail, invoiceType }
|
||||
* 代理到后端 /api/hjc/order/create;需登录态(从 cookie hjc_token 或 Authorization 透传)。
|
||||
*
|
||||
* `invoiceType`(VAT_SPECIAL 电子专票 / VAT_NORMAL 电子普票)由下单页必选;本代理
|
||||
* **body 整体透传**(不做字段白名单/裁剪),校验在后端。
|
||||
*
|
||||
* 约定:原样透传 ApiResult{code,message,data},由前端按 code 判定
|
||||
* (401 未登录/token 失效、403 无权限、其余为业务失败)。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user