373 lines
22 KiB
Vue
373 lines
22 KiB
Vue
<script setup lang="ts">
|
||||
|
|
import { computed, onMounted, reactive, ref } from 'vue'
|
|||
|
|
import { ElMessage } from 'element-plus'
|
|||
|
|
import type { UploadFile, UploadInstance } from 'element-plus'
|
|||
|
|
import { Building2, CheckCircle2, Clock3, FileCheck2, FileText, Pencil, Trash2, Upload, UserRound } from '@lucide/vue'
|
|||
|
|
import { useTenantAdministrationStore, type TenantCompanyProfile, type TenantQualification } from '@/stores/tenantAdministration'
|
|||
|
|
import { useAuthStore } from '@/stores/auth'
|
|||
|
|
import { formatQualificationFileSize, validateQualificationFile } from '@/utils/qualificationFile'
|
|||
|
|
import { downloadQualificationImage, uploadBusinessLicense } from '@/api/merchantIam'
|
|||
|
|
|
|||
|
|
const store = useTenantAdministrationStore()
|
|||
|
|
const auth = useAuthStore()
|
|||
|
|
const companyApproved = computed(() => store.company.auditStatus === 'ACTIVE')
|
|||
|
|
const auditStatusCopy = computed(() => ({
|
|||
|
|
PROFILE_REQUIRED: { label: '待完善资料', title: '请上传营业执照', detail: '营业执照提交审核前不能提交头程订单。' },
|
|||
|
|
PENDING: { label: '审核中', title: '企业资料正在等待平台审核', detail: '审核期间仍可维护资料和保存订单草稿;审核通过前不能提交订单。' },
|
|||
|
|
ACTIVE: { label: '已认证', title: '企业资料已审核,下单资格已开通', detail: '您可以创建、保存并提交头程订单。' },
|
|||
|
|
REJECTED: { label: '审核驳回', title: '企业资料审核未通过', detail: '请根据平台反馈修正资料;当前后端暂不支持同一企业重新上传。' },
|
|||
|
|
DISABLED: { label: '企业已停用', title: '企业账号已被平台停用', detail: '当前不能提交订单,请联系平台客服核实。' },
|
|||
|
|
})[store.company.auditStatus])
|
|||
|
|
const activeTab = ref('basic')
|
|||
|
|
const editing = ref(false)
|
|||
|
|
const qualificationOpen = ref(false)
|
|||
|
|
const previewOpen = ref(false)
|
|||
|
|
const previewRecord = ref<TenantQualification | null>(null)
|
|||
|
|
const previewUrl = ref('')
|
|||
|
|
const previewLoading = ref(false)
|
|||
|
|
const uploadRef = ref<UploadInstance>()
|
|||
|
|
const selectedFileSize = ref(0)
|
|||
|
|
const selectedFileType = ref('')
|
|||
|
|
const qualificationFileError = ref('')
|
|||
|
|
const qualificationSubmitting = ref(false)
|
|||
|
|
const profileSubmitting = ref(false)
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const selectedFile = ref<File | null>(null)
|
|||
|
|
const draft = reactive<TenantCompanyProfile>({ ...store.company })
|
|||
|
|
const qualificationDraft = reactive({ type: '营业执照', fileName: '', certificateNo: '' })
|
|||
|
|
|
|||
|
|
onMounted(async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
await store.loadCompanyContext()
|
|||
|
|
Object.assign(draft, store.company)
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error(error instanceof Error ? error.message : '企业资料加载失败')
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
function startEdit() {
|
|||
|
|
Object.assign(draft, store.company)
|
|||
|
|
editing.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function cancelEdit() {
|
|||
|
|
Object.assign(draft, store.company)
|
|||
|
|
editing.value = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function saveProfile() {
|
|||
|
|
if (profileSubmitting.value) return
|
|||
|
|
if (!draft.companyName || !draft.legalRepresentative || !draft.contactName || !draft.contactPhone) {
|
|||
|
|
ElMessage.warning('请完整填写企业名称、法人及联系人信息')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
profileSubmitting.value = true
|
|||
|
|
try {
|
|||
|
|
await store.updateCompany({ ...draft })
|
|||
|
|
Object.assign(draft, store.company)
|
|||
|
|
editing.value = false
|
|||
|
|
ElMessage.success('企业资料已保存')
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error(error instanceof Error ? error.message : '企业资料保存失败')
|
|||
|
|
} finally {
|
|||
|
|
profileSubmitting.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openQualificationDialog() {
|
|||
|
|
resetQualificationDraft()
|
|||
|
|
qualificationOpen.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function handleFileChange(file: UploadFile) {
|
|||
|
|
const raw = file.raw
|
|||
|
|
if (!raw) {
|
|||
|
|
qualificationFileError.value = '未能读取所选文件,请重新选择'
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
const error = validateQualificationFile({ name: raw.name, size: raw.size, type: raw.type })
|
|||
|
|
if (error) {
|
|||
|
|
qualificationFileError.value = error
|
|||
|
|
qualificationDraft.fileName = ''
|
|||
|
|
selectedFileSize.value = 0
|
|||
|
|
selectedFileType.value = ''
|
|||
|
|
selectedFile.value = null
|
|||
|
|
uploadRef.value?.clearFiles()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
qualificationFileError.value = ''
|
|||
|
|
selectedFile.value = raw
|
|||
|
|
qualificationDraft.fileName = raw.name
|
|||
|
|
selectedFileSize.value = raw.size
|
|||
|
|
selectedFileType.value = raw.type || raw.name.split('.').pop()?.toUpperCase() || '文件'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function removeSelectedFile() {
|
|||
|
|
uploadRef.value?.clearFiles()
|
|||
|
|
qualificationDraft.fileName = ''
|
|||
|
|
selectedFileSize.value = 0
|
|||
|
|
selectedFileType.value = ''
|
|||
|
|
qualificationFileError.value = ''
|
|||
|
|
selectedFile.value = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function resetQualificationDraft() {
|
|||
|
|
uploadRef.value?.clearFiles()
|
|||
|
|
qualificationDraft.type = '营业执照'
|
|||
|
|
qualificationDraft.fileName = ''
|
|||
|
|
qualificationDraft.certificateNo = store.company.creditCode
|
|||
|
|
selectedFileSize.value = 0
|
|||
|
|
selectedFileType.value = ''
|
|||
|
|
qualificationFileError.value = ''
|
|||
|
|
selectedFile.value = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function submitQualification() {
|
|||
|
|
if (qualificationSubmitting.value) return
|
|||
|
|
if (!qualificationDraft.fileName || !selectedFile.value) {
|
|||
|
|
ElMessage.warning('请选择营业执照图片')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
if (qualificationFileError.value) {
|
|||
|
|
ElMessage.warning(qualificationFileError.value)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
qualificationSubmitting.value = true
|
|||
|
|
try {
|
|||
|
|
await uploadBusinessLicense(selectedFile.value, qualificationDraft.certificateNo.trim() || undefined)
|
|||
|
|
await store.loadCompanyContext()
|
|||
|
|
qualificationOpen.value = false
|
|||
|
|
ElMessage.success('营业执照已提交审核')
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error(error instanceof Error ? error.message : '营业执照上传失败')
|
|||
|
|
} finally {
|
|||
|
|
qualificationSubmitting.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function previewQualification(record: TenantQualification) {
|
|||
|
|
previewRecord.value = record
|
|||
|
|
previewOpen.value = true
|
|||
|
|
if (!record.documentUrl) return
|
|||
|
|
previewLoading.value = true
|
|||
|
|
try {
|
|||
|
|
const blob = await downloadQualificationImage(record.documentUrl)
|
|||
|
|
previewUrl.value = URL.createObjectURL(blob)
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error(error instanceof Error ? error.message : '资质文件加载失败')
|
|||
|
|
} finally {
|
|||
|
|
previewLoading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function closeQualificationPreview() {
|
|||
|
|
if (previewUrl.value) URL.revokeObjectURL(previewUrl.value)
|
|||
|
|
previewUrl.value = ''
|
|||
|
|
previewRecord.value = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function qualificationStatus(status: string) {
|
|||
|
|
return status === 'VERIFIED' ? '已验证' : status === 'PENDING' ? '待审核' : status === 'REJECTED' ? '已驳回' : '已过期'
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<section v-loading="loading" class="profile-page">
|
|||
|
|
<div class="profile-summary-card client-card">
|
|||
|
|
<div class="company-mark"><Building2 :size="28" /></div>
|
|||
|
|
<div class="company-summary">
|
|||
|
|
<div><h2>{{ store.company.companyName }}</h2><el-tag :type="companyApproved ? 'success' : store.company.auditStatus === 'REJECTED' || store.company.auditStatus === 'DISABLED' ? 'danger' : 'warning'" effect="light">{{ auditStatusCopy.label }}</el-tag></div>
|
|||
|
|
<p>{{ store.company.creditCode }} · {{ store.company.companyType }}</p>
|
|||
|
|
</div>
|
|||
|
|
<el-button v-if="auth.hasPermission('company:profile:edit')" type="primary" @click="startEdit"><Pencil :size="16" />编辑企业资料</el-button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="company-audit-notice" :class="companyApproved ? 'is-approved' : 'is-pending'" role="status">
|
|||
|
|
<component :is="companyApproved ? CheckCircle2 : Clock3" :size="21" />
|
|||
|
|
<div><strong>{{ auditStatusCopy.title }}</strong><span>{{ auditStatusCopy.detail }}</span></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<el-tabs v-model="activeTab" class="profile-tabs">
|
|||
|
|
<el-tab-pane name="basic">
|
|||
|
|
<template #label><span class="tab-label"><Building2 :size="16" />企业基本信息</span></template>
|
|||
|
|
<div class="client-card info-panel">
|
|||
|
|
<dl class="profile-grid">
|
|||
|
|
<div><dt>公司名称</dt><dd>{{ store.company.companyName }}</dd></div>
|
|||
|
|
<div><dt>统一社会信用代码</dt><dd>{{ store.company.creditCode }}</dd></div>
|
|||
|
|
<div><dt>企业类型</dt><dd>{{ store.company.companyType }}</dd></div>
|
|||
|
|
<div><dt>注册地</dt><dd>{{ store.company.registeredAddress }}</dd></div>
|
|||
|
|
<div><dt>成立日期</dt><dd>{{ store.company.establishedDate }}</dd></div>
|
|||
|
|
<div><dt>注册资本</dt><dd>{{ store.company.registeredCapital }}</dd></div>
|
|||
|
|
<div><dt>法人姓名</dt><dd>{{ store.company.legalRepresentative }}</dd></div>
|
|||
|
|
<div><dt>法人电话</dt><dd>{{ store.company.legalRepresentativePhone }}</dd></div>
|
|||
|
|
</dl>
|
|||
|
|
</div>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
|
|||
|
|
<el-tab-pane name="contact">
|
|||
|
|
<template #label><span class="tab-label"><UserRound :size="16" />联系信息</span></template>
|
|||
|
|
<div class="client-card info-panel">
|
|||
|
|
<dl class="profile-grid">
|
|||
|
|
<div><dt>联系人</dt><dd>{{ store.company.contactName }}</dd></div>
|
|||
|
|
<div><dt>联系电话</dt><dd>{{ store.company.contactPhone }}</dd></div>
|
|||
|
|
<div><dt>联系邮箱</dt><dd>{{ store.company.contactEmail }}</dd></div>
|
|||
|
|
<div><dt>微信</dt><dd>{{ store.company.contactWechat }}</dd></div>
|
|||
|
|
<div class="full-row"><dt>业务地址</dt><dd>{{ store.company.businessAddress }}</dd></div>
|
|||
|
|
</dl>
|
|||
|
|
</div>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
|
|||
|
|
<el-tab-pane name="qualification">
|
|||
|
|
<template #label><span class="tab-label"><FileCheck2 :size="16" />资质文件</span></template>
|
|||
|
|
<div class="client-card qualification-panel">
|
|||
|
|
<header><div><h3>企业资质</h3><p>文件更新后需等待平台重新审核,历史业务不受影响。</p></div><el-button v-if="auth.hasPermission('company:qualification:upload')" type="primary" @click="openQualificationDialog"><Upload :size="16" />更新资质</el-button></header>
|
|||
|
|
<el-table class="desktop-qualification-table" :data="store.qualifications" row-key="id">
|
|||
|
|
<el-table-column prop="type" label="文件类型" min-width="150" />
|
|||
|
|
<el-table-column prop="fileName" label="文件名称" min-width="220" show-overflow-tooltip />
|
|||
|
|
<el-table-column label="状态" width="110"><template #default="{ row }"><el-tag :type="row.status === 'VERIFIED' ? 'success' : row.status === 'PENDING' ? 'warning' : 'danger'" effect="light">{{ qualificationStatus(row.status) }}</el-tag></template></el-table-column>
|
|||
|
|
<el-table-column prop="expiresAt" label="有效期" width="130" />
|
|||
|
|
<el-table-column prop="updatedAt" label="更新时间" min-width="160" />
|
|||
|
|
<el-table-column label="操作" width="90" fixed="right"><template #default="{ row }"><el-button link type="primary" @click="previewQualification(row as TenantQualification)">查看</el-button></template></el-table-column>
|
|||
|
|
</el-table>
|
|||
|
|
<div class="mobile-qualification-list">
|
|||
|
|
<article v-for="record in store.qualifications" :key="record.id">
|
|||
|
|
<header><strong>{{ record.type }}</strong><el-tag :type="record.status === 'VERIFIED' ? 'success' : record.status === 'PENDING' ? 'warning' : 'danger'" effect="light">{{ qualificationStatus(record.status) }}</el-tag></header>
|
|||
|
|
<div class="qualification-file-name"><FileText :size="16" /><span>{{ record.fileName }}</span></div>
|
|||
|
|
<dl><div><dt>有效期</dt><dd>{{ record.expiresAt }}</dd></div><div><dt>更新时间</dt><dd>{{ record.updatedAt }}</dd></div></dl>
|
|||
|
|
<el-button link type="primary" @click="previewQualification(record)">查看文件信息</el-button>
|
|||
|
|
</article>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
</el-tabs>
|
|||
|
|
|
|||
|
|
<el-drawer v-model="editing" title="编辑企业资料" size="min(620px, 94vw)" @closed="cancelEdit">
|
|||
|
|
<el-form label-position="top" class="profile-form">
|
|||
|
|
<h3>企业基本信息</h3>
|
|||
|
|
<div class="form-grid-two">
|
|||
|
|
<el-form-item label="公司名称" required><el-input v-model="draft.companyName" /></el-form-item>
|
|||
|
|
<el-form-item label="统一社会信用代码"><el-input v-model="draft.creditCode" disabled /></el-form-item>
|
|||
|
|
<el-form-item label="企业类型"><el-input v-model="draft.companyType" /></el-form-item>
|
|||
|
|
<el-form-item label="注册地"><el-input v-model="draft.registeredAddress" /></el-form-item>
|
|||
|
|
<el-form-item label="法人姓名" required><el-input v-model="draft.legalRepresentative" /></el-form-item>
|
|||
|
|
<el-form-item label="法人电话"><el-input v-model="draft.legalRepresentativePhone" /></el-form-item>
|
|||
|
|
</div>
|
|||
|
|
<h3>联系信息</h3>
|
|||
|
|
<div class="form-grid-two">
|
|||
|
|
<el-form-item label="联系人" required><el-input v-model="draft.contactName" /></el-form-item>
|
|||
|
|
<el-form-item label="联系电话" required><el-input v-model="draft.contactPhone" /></el-form-item>
|
|||
|
|
<el-form-item label="联系邮箱"><el-input v-model="draft.contactEmail" /></el-form-item>
|
|||
|
|
<el-form-item label="微信"><el-input v-model="draft.contactWechat" /></el-form-item>
|
|||
|
|
<el-form-item label="业务地址" class="full-row"><el-input v-model="draft.businessAddress" /></el-form-item>
|
|||
|
|
</div>
|
|||
|
|
</el-form>
|
|||
|
|
<template #footer><div class="drawer-actions"><el-button :disabled="profileSubmitting" @click="cancelEdit">取消</el-button><el-button type="primary" :loading="profileSubmitting" @click="saveProfile">保存修改</el-button></div></template>
|
|||
|
|
</el-drawer>
|
|||
|
|
|
|||
|
|
<el-dialog v-model="qualificationOpen" title="更新资质文件" width="min(520px, 92vw)" @closed="resetQualificationDraft">
|
|||
|
|
<el-form label-position="top">
|
|||
|
|
<el-form-item label="文件类型"><el-input v-model="qualificationDraft.type" disabled /></el-form-item>
|
|||
|
|
<el-form-item label="选择文件" required :error="qualificationFileError">
|
|||
|
|
<el-upload ref="uploadRef" :auto-upload="false" :limit="1" :show-file-list="false" accept=".jpg,.jpeg,.png" @change="handleFileChange">
|
|||
|
|
<el-button><Upload :size="16" />选择文件</el-button>
|
|||
|
|
<template #tip><div class="upload-tip">仅支持 JPG、PNG,单张图片不超过 5MB。</div></template>
|
|||
|
|
</el-upload>
|
|||
|
|
<div v-if="qualificationDraft.fileName" class="selected-qualification-file" aria-live="polite">
|
|||
|
|
<FileText :size="20" />
|
|||
|
|
<div><strong>{{ qualificationDraft.fileName }}</strong><span>{{ selectedFileType }} · {{ formatQualificationFileSize(selectedFileSize) }}</span></div>
|
|||
|
|
<el-button link type="danger" aria-label="移除所选资质文件" @click="removeSelectedFile"><Trash2 :size="16" />移除</el-button>
|
|||
|
|
</div>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="统一社会信用代码"><el-input v-model="qualificationDraft.certificateNo" maxlength="18" placeholder="建议填写 18 位统一社会信用代码" /></el-form-item>
|
|||
|
|
</el-form>
|
|||
|
|
<template #footer><el-button :disabled="qualificationSubmitting" @click="qualificationOpen = false">取消</el-button><el-button type="primary" :loading="qualificationSubmitting" @click="submitQualification">提交审核</el-button></template>
|
|||
|
|
</el-dialog>
|
|||
|
|
|
|||
|
|
<el-dialog v-model="previewOpen" :title="`${previewRecord?.type || '资质文件'}详情`" width="min(560px, 92vw)" @closed="closeQualificationPreview">
|
|||
|
|
<div v-if="previewRecord" v-loading="previewLoading" class="document-preview">
|
|||
|
|
<img v-if="previewUrl" :src="previewUrl" :alt="`${previewRecord.type}预览`" class="qualification-preview-image" />
|
|||
|
|
<FileCheck2 :size="42" />
|
|||
|
|
<strong>{{ previewRecord.fileName }}</strong>
|
|||
|
|
<el-tag :type="previewRecord.status === 'VERIFIED' ? 'success' : previewRecord.status === 'PENDING' ? 'warning' : 'danger'" effect="light">{{ qualificationStatus(previewRecord.status) }}</el-tag>
|
|||
|
|
<dl><div><dt>文件类型</dt><dd>{{ previewRecord.type }}</dd></div><div><dt>有效期</dt><dd>{{ previewRecord.expiresAt }}</dd></div><div><dt>更新时间</dt><dd>{{ previewRecord.updatedAt }}</dd></div></dl>
|
|||
|
|
<span>{{ previewUrl ? '文件通过当前商户会话临时加载,关闭弹窗后立即释放。' : '当前文件暂不可预览。' }}</span>
|
|||
|
|
</div>
|
|||
|
|
</el-dialog>
|
|||
|
|
</section>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.profile-page { display: flex; flex-direction: column; gap: 16px; }
|
|||
|
|
.company-audit-notice { display: flex; align-items: flex-start; gap: 12px; padding: 15px 17px; border: 1px solid; }
|
|||
|
|
.company-audit-notice svg { flex: 0 0 21px; margin-top: 1px; }
|
|||
|
|
.company-audit-notice div { display: flex; min-width: 0; flex-direction: column; gap: 4px; }
|
|||
|
|
.company-audit-notice strong { color: var(--navy); font-size: 13px; }
|
|||
|
|
.company-audit-notice span { color: var(--muted); font-size: 12px; line-height: 1.6; }
|
|||
|
|
.company-audit-notice.is-approved { border-color: #bcd9d2; background: #eff8f5; color: #20745f; }
|
|||
|
|
.qualification-preview-image { display: block; max-width: 100%; max-height: 360px; margin: 0 auto 14px; object-fit: contain; }
|
|||
|
|
.company-audit-notice.is-pending { border-color: #e5d2a5; background: #fff9eb; color: #9a6a12; }
|
|||
|
|
.profile-summary-card { display: flex; align-items: center; gap: 16px; }
|
|||
|
|
.company-mark { display: grid; width: 58px; height: 58px; flex: 0 0 58px; place-items: center; background: #e7f3f1; color: var(--primary); }
|
|||
|
|
.company-summary { flex: 1; min-width: 0; }
|
|||
|
|
.company-summary > div { display: flex; align-items: center; gap: 10px; }
|
|||
|
|
.company-summary h2 { overflow: hidden; margin: 0; color: var(--navy); font-size: 20px; text-overflow: ellipsis; white-space: nowrap; }
|
|||
|
|
.company-summary p, .qualification-panel header p { margin: 6px 0 0; color: var(--muted); font-size: 12px; }
|
|||
|
|
.profile-tabs :deep(.el-tabs__header) { margin-bottom: 14px; padding: 0 16px; border: 1px solid var(--border); background: #fff; }
|
|||
|
|
.profile-tabs :deep(.el-tabs__nav-wrap::after) { display: none; }
|
|||
|
|
.tab-label { display: inline-flex; align-items: center; gap: 7px; }
|
|||
|
|
.info-panel { padding: 0; }
|
|||
|
|
.profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; }
|
|||
|
|
.profile-grid > div { min-height: 86px; padding: 20px 22px; border-bottom: 1px solid var(--border); }
|
|||
|
|
.profile-grid > div:nth-child(odd) { border-right: 1px solid var(--border); }
|
|||
|
|
.profile-grid dt { color: var(--muted); font-size: 11px; }
|
|||
|
|
.profile-grid dd { margin: 9px 0 0; color: var(--navy); font-size: 14px; font-weight: 650; }
|
|||
|
|
.profile-grid .full-row { grid-column: 1 / -1; border-right: 0; }
|
|||
|
|
.qualification-panel { padding: 0; overflow: hidden; }
|
|||
|
|
.qualification-panel > header { padding: 20px 22px; border-bottom: 1px solid var(--border); }
|
|||
|
|
.qualification-panel h3 { margin: 0; }
|
|||
|
|
.mobile-qualification-list { display: none; }
|
|||
|
|
.profile-form h3 { margin: 4px 0 18px; padding-bottom: 10px; border-bottom: 1px solid var(--border); font-size: 15px; }
|
|||
|
|
.profile-form h3:not(:first-child) { margin-top: 22px; }
|
|||
|
|
.form-grid-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; }
|
|||
|
|
.form-grid-two .full-row { grid-column: 1 / -1; }
|
|||
|
|
.drawer-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|||
|
|
.upload-tip { margin-top: 8px; color: var(--muted); font-size: 11px; line-height: 1.55; }
|
|||
|
|
.selected-qualification-file { display: flex; width: 100%; min-width: 0; align-items: center; gap: 10px; margin-top: 12px; padding: 12px; border: 1px solid #cfe1dd; background: #f3f8f7; color: var(--primary); }
|
|||
|
|
.selected-qualification-file > svg { flex: 0 0 20px; }
|
|||
|
|
.selected-qualification-file > div { display: flex; min-width: 0; flex: 1; flex-direction: column; gap: 3px; }
|
|||
|
|
.selected-qualification-file strong { overflow: hidden; color: var(--navy); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
|
|||
|
|
.selected-qualification-file span { color: var(--muted); font-size: 10px; }
|
|||
|
|
.document-preview { display: grid; min-height: 260px; place-items: center; align-content: center; gap: 12px; border: 1px dashed #b8c8c5; background: #f6faf9; color: var(--primary); }
|
|||
|
|
.document-preview > strong { max-width: min(420px, calc(100% - 32px)); overflow-wrap: anywhere; color: var(--navy); text-align: center; }
|
|||
|
|
.document-preview dl { display: grid; width: min(420px, calc(100% - 32px)); grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 6px 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
|
|||
|
|
.document-preview dl div { min-width: 0; padding: 12px; text-align: center; }
|
|||
|
|
.document-preview dt { color: var(--muted); font-size: 10px; }
|
|||
|
|
.document-preview dd { margin: 5px 0 0; overflow-wrap: anywhere; color: var(--navy); font-size: 12px; font-weight: 650; }
|
|||
|
|
.document-preview > span { max-width: 440px; padding: 0 16px; color: var(--muted); font-size: 11px; line-height: 1.6; text-align: center; }
|
|||
|
|
@media (max-width: 720px) {
|
|||
|
|
.profile-summary-card { align-items: flex-start; flex-wrap: wrap; }
|
|||
|
|
.profile-summary-card > .el-button { width: 100%; }
|
|||
|
|
.profile-grid, .form-grid-two { grid-template-columns: 1fr; }
|
|||
|
|
.profile-grid > div:nth-child(odd) { border-right: 0; }
|
|||
|
|
.qualification-panel > header { align-items: flex-start; gap: 12px; }
|
|||
|
|
.qualification-panel > header .el-button { flex: 0 0 auto; }
|
|||
|
|
.desktop-qualification-table { display: none; }
|
|||
|
|
.mobile-qualification-list { display: grid; gap: 10px; padding: 10px; }
|
|||
|
|
.mobile-qualification-list article { min-width: 0; padding: 14px; border: 1px solid var(--border); background: #fff; }
|
|||
|
|
.mobile-qualification-list article > header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|||
|
|
.qualification-file-name { display: flex; min-width: 0; align-items: center; gap: 8px; margin: 14px 0; color: var(--primary); }
|
|||
|
|
.qualification-file-name svg { flex: 0 0 16px; }
|
|||
|
|
.qualification-file-name span { min-width: 0; overflow: hidden; color: var(--navy); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
|
|||
|
|
.mobile-qualification-list dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 0 0 10px; padding-top: 12px; border-top: 1px solid var(--border); }
|
|||
|
|
.mobile-qualification-list dt { color: var(--muted); font-size: 10px; }
|
|||
|
|
.mobile-qualification-list dd { margin: 4px 0 0; overflow-wrap: anywhere; font-size: 11px; font-weight: 600; }
|
|||
|
|
.document-preview dl { grid-template-columns: 1fr; }
|
|||
|
|
.document-preview dl div + div { border-top: 1px solid var(--border); }
|
|||
|
|
.selected-qualification-file { align-items: flex-start; flex-wrap: wrap; }
|
|||
|
|
.selected-qualification-file > .el-button { margin-left: 30px; }
|
|||
|
|
}
|
|||
|
|
</style>
|