Files
hjc-web/app/pages/tender/qualification.vue
T
weicw1996 5e7372d0c7 feat(tender): 企业注册与 h5 对齐(一站式表单 + 证件上传 + OCR 回填);修复登录/注册代理路由 404
- 新增 app/components/HjcEnterpriseForm.vue:与 h5 EnterpriseForm 行为一致的共享表单,
  字段集 / 必填规则 / OCR 回填 / 提示文案逐条对齐
- register.vue:一站式注册,一次性提交企业 + 经办人 + 授权 + 证件材料;
  企业联系电话、企业地址选填,其余(含企业邮箱)必填,4 类证件必传
- tender/qualification.vue:改用共享组件,补「经办人身份证号」,
  以真实文件上传 + OCR 取代原「证件 URL 文本框」;
  审核中(0)/已通过(1) 只读,驳回(2)/未提交(-1) 可编辑重提
- 新增 server/api/tender/upload.post.ts(multipart 原样转发 → /hjc/auth/upload,匿名,
  登录前后均可用)与 server/api/tender/ocr/recognize.post.ts(→ /hjc/ocr/recognize)
- 修正 datetime-local(YYYY-MM-DDTHH:mm)与后端 yyyy-MM-dd HH:mm:ss 的格式转换
- fix: server/api/tender/auth/{login,register}.post.ts 实际路由为 /api/tender/auth/*,
  而客户端调用 /api/tender/{login,register},导致登录与注册此前均 404;
  已按文件注释与同级接口约定移至扁平路径 server/api/tender/{login,register}.post.ts
- register / login / enterprise-save 三个代理改为原样透传 ApiResult{code,message,data},
  useHjcAuth 与资质页按 code===0 判定,避免后端校验失败信息被吞掉
2026-09-10 13:12:21 +08:00

130 lines
4.3 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>
<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">
<HjcEnterpriseForm ref="entForm" v-model:form="form" v-model:materials="materials" :readonly="readonly" />
<p v-if="readonly" class="text-center text-xs text-gray-500">
资质审核中 / 已通过时不可修改被驳回后可修改并重新提交
</p>
<button
v-if="!readonly"
type="submit"
class="w-full rounded-md bg-blue-600 py-3 font-medium text-white 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">
const { isLoggedIn } = useHjcAuth()
const loggedIn = ref(false)
const authStatus = ref(-1)
const rejectReason = ref('')
const submitting = ref(false)
const error = ref('')
const tip = ref('')
const entForm = ref<any>(null)
const form = ref<Record<string, any>>({
name: '',
creditCode: '',
contactPhone: '',
contactEmail: '',
address: '',
agentName: '',
idCardNo: '',
agentEmail: '',
agentPhone: '',
authorizeExpire: ''
})
const materials = ref<any[]>([
{ materialType: 'idcard_front', label: '身份证人像面', materialName: '身份证人像面', fileUrl: '' },
{ materialType: 'idcard_back', label: '身份证国徽面', materialName: '身份证国徽面', fileUrl: '' },
{ materialType: 'handbook', label: '授权委托书', materialName: '授权委托书', fileUrl: '' },
{ materialType: 'license', label: '营业执照', materialName: '营业执照', fileUrl: '' }
])
const authStatusText = computed(
() => ({ 0: '待审核', 1: '已通过', 2: '已驳回' } as Record<number, string>)[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'
})
/** 审核中(0)/已通过(1) 只读;驳回(2)/未提交(-1) 可编辑重提(与 h5 一致) */
const readonly = computed(() => authStatus.value === 0 || authStatus.value === 1)
async function load() {
const res: any = await $fetch('/api/tender/enterprise')
if (res) {
authStatus.value = res.authStatus ?? -1
rejectReason.value = res.rejectReason || ''
entForm.value?.fill(res)
}
}
async function submit() {
error.value = ''
tip.value = ''
if (!entForm.value?.validate()) return
submitting.value = true
try {
const res: any = await $fetch('/api/tender/enterprise/save', {
method: 'POST',
body: entForm.value.buildPayload()
})
if (res?.code !== 0) {
error.value = res?.message || '提交失败'
return
}
tip.value = res?.message || '提交成功,等待平台审核'
await load()
} catch (e: any) {
error.value = e?.data?.statusMessage || e?.data?.message || e?.message || '提交失败'
} finally {
submitting.value = false
}
}
loggedIn.value = isLoggedIn()
onMounted(async () => {
loggedIn.value = isLoggedIn()
if (loggedIn.value) {
await load()
}
})
</script>