feat(tender): 阶段3c 我的订单 + 企业资质中心 + 登录态导航
- /tender/orders:我的订单列表(状态/金额/下单时间) - /tender/qualification:企业资质(信息+证件URL+提交审核+审核状态/驳回原因) - HjcUserBar:登录/注册或 我的订单/企业资质/退出(列表/详情页已接入) - 代理:tender/my-orders.get, enterprise.get, enterprise/save.post
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-10">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-gray-900">我的订单</h1>
|
||||
<HjcUserBar />
|
||||
</div>
|
||||
|
||||
<div v-if="!loggedIn" class="py-20 text-center">
|
||||
<p class="text-gray-500">请先登录后查看订单</p>
|
||||
<NuxtLink to="/login?redirect=/tender/orders" class="mt-4 inline-block rounded-md bg-blue-600 px-6 py-2 text-white hover:bg-blue-700">去登录</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div v-else-if="orders.length" class="space-y-4">
|
||||
<div v-for="o in orders" :key="o.id" class="rounded-lg border border-gray-200 p-5">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<p class="font-semibold text-gray-900">{{ o.projectName }}</p>
|
||||
<p class="mt-1 text-sm text-gray-500">订单号:{{ o.orderNo }} · 项目编号:{{ o.projectNo }}</p>
|
||||
</div>
|
||||
<span class="rounded px-2 py-0.5 text-xs" :class="payStatusClass(o.payStatus)">{{ payStatusText(o.payStatus) }}</span>
|
||||
</div>
|
||||
<div class="mt-4 flex items-center justify-between text-sm">
|
||||
<span class="text-gray-500">{{ fmtTime(o.createTime) }}</span>
|
||||
<span class="text-blue-600 font-bold">¥{{ formatMoney(o.totalAmount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="loaded" class="py-20 text-center text-gray-500">暂无订单</div>
|
||||
<SiteLoading v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { isLoggedIn } = useHjcAuth()
|
||||
const loggedIn = ref(false)
|
||||
const orders = shallowRef<any[]>([])
|
||||
const loaded = ref(false)
|
||||
|
||||
function formatMoney(n?: number | string) {
|
||||
return Number(n ?? 0).toFixed(2)
|
||||
}
|
||||
function fmtTime(s?: string) {
|
||||
return s ? s.slice(0, 19).replace('T', ' ') : '-'
|
||||
}
|
||||
function payStatusText(s?: number) {
|
||||
return ({ 0: '待支付', 1: '支付成功', 2: '支付失败', 3: '已退款' } as any)[s ?? 0] ?? '-'
|
||||
}
|
||||
function payStatusClass(s?: number) {
|
||||
if (s === 1) return 'bg-green-100 text-green-700'
|
||||
if (s === 3) return 'bg-gray-100 text-gray-500'
|
||||
return 'bg-amber-100 text-amber-700'
|
||||
}
|
||||
|
||||
loggedIn.value = isLoggedIn()
|
||||
if (loggedIn.value) {
|
||||
const res: any = await $fetch('/api/tender/my-orders')
|
||||
orders.value = Array.isArray(res) ? res : []
|
||||
loaded.value = true
|
||||
} else {
|
||||
loaded.value = true
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-10">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-gray-900">企业资质</h1>
|
||||
<HjcUserBar />
|
||||
</div>
|
||||
|
||||
<div v-if="!loggedIn" class="py-20 text-center">
|
||||
<p class="text-gray-500">请先登录后管理企业资质</p>
|
||||
<NuxtLink to="/login?redirect=/tender/qualification" class="mt-4 inline-block rounded-md bg-blue-600 px-6 py-2 text-white hover:bg-blue-700">去登录</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div v-else class="mx-auto max-w-2xl rounded-lg border border-gray-200 p-6">
|
||||
<!-- 审核状态 -->
|
||||
<div class="mb-6 rounded-md bg-gray-50 p-4 text-sm">
|
||||
<span class="text-gray-600">审核状态:</span>
|
||||
<span :class="authStatusClass" class="rounded px-2 py-0.5 font-medium">{{ authStatusText }}</span>
|
||||
<p v-if="authStatus === 2 && rejectReason" class="mt-2 text-red-500">驳回原因:{{ rejectReason }}</p>
|
||||
<p class="mt-1 text-xs text-gray-400">资质审核通过后方可购买标书</p>
|
||||
</div>
|
||||
|
||||
<form class="space-y-4" @submit.prevent="submit">
|
||||
<Field label="企业名称"><input v-model="form.name" class="field" /></Field>
|
||||
<Field label="纳税人识别号"><input v-model="form.creditCode" class="field" /></Field>
|
||||
<Field label="企业联系电话"><input v-model="form.contactPhone" class="field" /></Field>
|
||||
<Field label="企业邮箱"><input v-model="form.contactEmail" type="email" class="field" /></Field>
|
||||
<Field label="企业地址"><input v-model="form.address" class="field" /></Field>
|
||||
<Field label="经办人姓名"><input v-model="form.agentName" class="field" /></Field>
|
||||
<Field label="经办人邮箱"><input v-model="form.agentEmail" type="email" class="field" /></Field>
|
||||
<Field label="经办人手机号"><input v-model="form.agentPhone" class="field" /></Field>
|
||||
<Field label="授权到期时间"><input v-model="form.authorizeExpire" type="datetime-local" class="field" /></Field>
|
||||
|
||||
<div class="space-y-3">
|
||||
<Field label="经办人身份证正面 URL"><input v-model="materials.idcard_front" class="field" placeholder="/upload/xxx.jpg" /></Field>
|
||||
<Field label="经办人身份证反面 URL"><input v-model="materials.idcard_back" class="field" placeholder="/upload/xxx.jpg" /></Field>
|
||||
<Field label="授权委托书 URL"><input v-model="materials.handbook" class="field" placeholder="/upload/xxx.pdf" /></Field>
|
||||
<Field label="营业执照 URL"><input v-model="materials.license" class="field" placeholder="/upload/xxx.jpg" /></Field>
|
||||
</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 ? '提交中...' : '提交审核' }}
|
||||
</button>
|
||||
<p v-if="error" class="text-center text-sm text-red-500">{{ error }}</p>
|
||||
<p v-if="tip" class="text-center text-sm text-green-600">{{ tip }}</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, h } from 'vue'
|
||||
|
||||
const Field = defineComponent({
|
||||
props: { label: String },
|
||||
setup(props, { slots }) {
|
||||
return () => h('div', { class: 'space-y-1' }, [
|
||||
h('label', { class: 'block text-sm text-gray-600' }, props.label),
|
||||
slots.default?.()
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
const { isLoggedIn } = useHjcAuth()
|
||||
const loggedIn = ref(false)
|
||||
const authStatus = ref(0)
|
||||
const rejectReason = ref('')
|
||||
const submitting = ref(false)
|
||||
const error = ref('')
|
||||
const tip = ref('')
|
||||
|
||||
const form = reactive({
|
||||
name: '', creditCode: '', contactPhone: '', contactEmail: '', address: '',
|
||||
agentName: '', agentEmail: '', agentPhone: '', authorizeExpire: ''
|
||||
})
|
||||
const materials = reactive({ idcard_front: '', idcard_back: '', handbook: '', license: '' })
|
||||
|
||||
const authStatusText = computed(() => ({ 0: '待审核', 1: '已通过', 2: '已驳回' } as any)[authStatus.value] ?? '待审核')
|
||||
const authStatusClass = computed(() => {
|
||||
if (authStatus.value === 1) return 'bg-green-100 text-green-700'
|
||||
if (authStatus.value === 2) return 'bg-red-100 text-red-600'
|
||||
return 'bg-amber-100 text-amber-700'
|
||||
})
|
||||
|
||||
async function load() {
|
||||
const res: any = await $fetch('/api/tender/enterprise')
|
||||
if (res) {
|
||||
authStatus.value = res.authStatus ?? 0
|
||||
rejectReason.value = res.rejectReason || ''
|
||||
form.name = res.name || ''
|
||||
form.creditCode = res.creditCode || ''
|
||||
form.contactPhone = res.contactPhone || ''
|
||||
form.contactEmail = res.contactEmail || ''
|
||||
form.address = res.address || ''
|
||||
form.agentName = res.agentName || ''
|
||||
form.agentEmail = res.agentEmail || ''
|
||||
form.agentPhone = res.agentPhone || ''
|
||||
form.authorizeExpire = res.authorizeExpire ? res.authorizeExpire.slice(0, 16) : ''
|
||||
const ms: any[] = res.materials || []
|
||||
for (const m of ms) {
|
||||
if (m.materialType && (materials as any)[m.materialType] === '') {
|
||||
(materials as any)[m.materialType] = m.fileUrl || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
error.value = ''
|
||||
tip.value = ''
|
||||
submitting.value = true
|
||||
const materialList = Object.entries(materials)
|
||||
.filter(([, url]) => url)
|
||||
.map(([type, url]) => ({ materialType: type, materialName: type, fileUrl: url }))
|
||||
try {
|
||||
await $fetch('/api/tender/enterprise/save', {
|
||||
method: 'POST',
|
||||
body: { ...form, materials: materialList }
|
||||
})
|
||||
tip.value = '提交成功,等待平台审核'
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
error.value = e?.data?.message || e?.message || '提交失败'
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
loggedIn.value = isLoggedIn()
|
||||
if (loggedIn.value) {
|
||||
await load()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field {
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #d1d5db;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user