新版官网模板

This commit is contained in:
2026-04-29 01:33:33 +08:00
commit 0d82386f8f
341 changed files with 64526 additions and 0 deletions

View File

@@ -0,0 +1,324 @@
<template>
<div class="admin-members-review">
<div class="page-header">
<h3>会员审核</h3>
<span class="pending-count">待审核{{ pendingCount }} </span>
</div>
<!-- 搜索过滤 -->
<div class="filter-bar">
<a-space wrap>
<a-input v-model:value="filters.keyword" allow-clear placeholder="搜索申请人姓名/单位" style="width: 200px" @press-enter="loadData" />
<a-select v-model:value="filters.type" style="width: 130px" @change="loadData">
<a-select-option value="">全部类型</a-select-option>
<a-select-option value="enterprise">企业会员</a-select-option>
<a-select-option value="personal">个人会员</a-select-option>
</a-select>
<a-select v-model:value="filters.status" style="width: 130px" @change="loadData">
<a-select-option value="">全部状态</a-select-option>
<a-select-option value="pending">待审核</a-select-option>
<a-select-option value="approved">已通过</a-select-option>
<a-select-option value="rejected">已拒绝</a-select-option>
</a-select>
<a-button type="primary" @click="loadData">搜索</a-button>
</a-space>
</div>
<div class="table-card">
<a-table
:columns="columns"
:data-source="dataSource"
:loading="loading"
:pagination="{ total, pageSize, current: currentPage, onChange: handlePageChange, showTotal: (t: number) => `共 ${t} 条` }"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag :color="record.memberType === 'enterprise' ? 'blue' : 'green'">
{{ record.memberType === 'enterprise' ? '企业会员' : '个人会员' }}
</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag :color="getStatusColor(record.status)">{{ getStatusText(record.status) }}</a-tag>
</template>
<template v-if="column.key === 'materials'">
<a-button size="small" @click="viewMaterials(record)">查看材料</a-button>
</template>
<template v-if="column.key === 'action'">
<a-space v-if="record.status === 'pending'">
<a-button size="small" type="primary" @click="handleApprove(record)">通过</a-button>
<a-button danger size="small" @click="handleReject(record)">拒绝</a-button>
<a-button size="small" @click="viewDetail(record)">详情</a-button>
</a-space>
<a-button v-else size="small" @click="viewDetail(record)">详情</a-button>
</template>
</template>
</a-table>
</div>
<!-- 材料预览弹窗 -->
<a-modal v-model:open="materialsModal" :footer="null" :title="`${currentRecord?.applicantName} 的申请材料`" width="700px">
<div v-if="currentRecord">
<!-- 企业会员材料 -->
<div v-if="currentRecord.memberType === 'enterprise'">
<h4>企业会员申请材料</h4>
<div class="materials-list">
<div class="material-item">
<span class="material-icon">📄</span>
<span class="material-name">入会申请表盖章</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">🏢</span>
<span class="material-name">营业执照副本</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">🪪</span>
<span class="material-name">法人身份证</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">📝</span>
<span class="material-name">单位简介</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
</div>
</div>
<!-- 个人会员材料 -->
<div v-else>
<h4>个人会员申请材料</h4>
<div class="materials-list">
<div class="material-item">
<span class="material-icon">📄</span>
<span class="material-name">入会申请表签字</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">📖</span>
<span class="material-name">个人简介</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">🎓</span>
<span class="material-name">职称证书/学历证书</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">🪪</span>
<span class="material-name">身份证复印件</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
<div class="material-item">
<span class="material-icon">🏆</span>
<span class="material-name">研究成果/获奖证明</span>
<a-button ghost size="small" type="primary">预览/下载</a-button>
</div>
</div>
</div>
<div v-if="currentRecord.status === 'pending'" class="action-area">
<a-divider />
<a-space>
<a-button type="primary" @click="handleApprove(currentRecord); materialsModal = false">通过申请</a-button>
<a-button danger @click="handleReject(currentRecord); materialsModal = false">拒绝申请</a-button>
</a-space>
</div>
</div>
</a-modal>
<!-- 详情弹窗 -->
<a-modal v-model:open="detailModal" :footer="null" :title="`会员申请详情`" width="700px">
<a-descriptions v-if="currentRecord" :column="2" bordered>
<a-descriptions-item label="申请人">{{ currentRecord.applicantName }}</a-descriptions-item>
<a-descriptions-item label="会员类型">
<a-tag :color="currentRecord.memberType === 'enterprise' ? 'blue' : 'green'">
{{ currentRecord.memberType === 'enterprise' ? '企业会员' : '个人会员' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item v-if="currentRecord.memberType === 'enterprise'" label="单位/组织">{{ currentRecord.organization }}</a-descriptions-item>
<a-descriptions-item label="联系方式">{{ currentRecord.phone }}</a-descriptions-item>
<a-descriptions-item :span="2" label="电子邮箱">{{ currentRecord.email }}</a-descriptions-item>
<a-descriptions-item label="申请时间">{{ currentRecord.applyTime }}</a-descriptions-item>
<a-descriptions-item label="状态">
<a-tag :color="getStatusColor(currentRecord.status)">{{ getStatusText(currentRecord.status) }}</a-tag>
</a-descriptions-item>
<a-descriptions-item v-if="currentRecord.rejectReason" :span="2" label="拒绝原因">{{ currentRecord.rejectReason }}</a-descriptions-item>
</a-descriptions>
</a-modal>
<!-- 拒绝弹窗 -->
<a-modal v-model:open="rejectModal" :confirm-loading="saving" title="填写拒绝原因" @ok="confirmReject">
<a-textarea v-model:value="rejectReason" :rows="4" placeholder="请说明拒绝原因" />
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { message } from 'ant-design-vue'
definePageMeta({ layout: 'admin' })
useHead({ title: '会员审核' })
const loading = ref(false)
const saving = ref(false)
const materialsModal = ref(false)
const detailModal = ref(false)
const rejectModal = ref(false)
const rejectReason = ref('')
const currentRecord = ref<any>(null)
const pendingCount = ref(5)
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(15)
const filters = reactive({ keyword: '', type: '', status: '' })
const columns = [
{ title: '申请人', dataIndex: 'applicantName', key: 'applicantName' },
{ title: '会员类型', key: 'type', width: 110 },
{ title: '单位/联系方式', dataIndex: 'orgOrContact', key: 'orgOrContact' },
{ title: '申请时间', dataIndex: 'applyTime', key: 'applyTime', width: 150 },
{ title: '状态', key: 'status', width: 100 },
{ title: '材料', key: 'materials', width: 100 },
{ title: '操作', key: 'action', width: 180 },
]
const dataSource = ref([
{ id: 1, applicantName: '广西某科技公司', memberType: 'enterprise', orgOrContact: '王总 139****0001', phone: '139****0001', email: 'enterprise@xx.com', organization: '广西某科技有限公司', applyTime: '2024-12-19 10:00', status: 'pending' },
{ id: 2, applicantName: '张某某', memberType: 'personal', orgOrContact: '广西大学', phone: '138****0001', email: 'zhang@gxu.edu.cn', applyTime: '2024-12-18 14:30', status: 'pending' },
{ id: 3, applicantName: '南宁某咨询机构', memberType: 'enterprise', orgOrContact: '李经理 137****0002', phone: '137****0002', email: 'nn@xx.com', organization: '南宁某咨询有限公司', applyTime: '2024-12-15 09:20', status: 'approved' },
])
function getStatusColor(status: string) {
const map: Record<string, string> = { pending: 'orange', approved: 'green', rejected: 'red' }
return map[status] || 'default'
}
function getStatusText(status: string) {
const map: Record<string, string> = { pending: '待审核', approved: '已通过', rejected: '已拒绝' }
return map[status] || status
}
function viewMaterials(record: any) {
currentRecord.value = record
materialsModal.value = true
}
function viewDetail(record: any) {
currentRecord.value = record
detailModal.value = true
}
async function handleApprove(record: any) {
// TODO: 调用API
record.status = 'approved'
pendingCount.value = Math.max(0, pendingCount.value - 1)
message.success(`已通过 ${record.applicantName} 的会员申请`)
}
function handleReject(record: any) {
currentRecord.value = record
rejectReason.value = ''
rejectModal.value = true
}
async function confirmReject() {
if (!rejectReason.value.trim()) { message.warning('请填写拒绝原因'); return }
saving.value = true
try {
// TODO: 调用API
currentRecord.value.status = 'rejected'
currentRecord.value.rejectReason = rejectReason.value
pendingCount.value = Math.max(0, pendingCount.value - 1)
message.success('已拒绝申请并通知申请人')
rejectModal.value = false
} finally {
saving.value = false
}
}
function handlePageChange(page: number) {
currentPage.value = page
loadData()
}
async function loadData() {
// TODO: 接入实际API
}
onMounted(() => {
total.value = dataSource.value.length
})
</script>
<style scoped>
.admin-members-review {
display: flex;
flex-direction: column;
gap: 16px;
}
.page-header {
display: flex;
align-items: center;
gap: 12px;
background: #fff;
border-radius: 10px;
padding: 14px 20px;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
.page-header h3 {
font-size: 16px;
font-weight: 700;
color: #1f2937;
margin: 0;
}
.pending-count {
padding: 4px 12px;
background: #fef3c7;
color: #b45309;
border-radius: 20px;
font-size: 13px;
font-weight: 500;
}
.filter-bar, .table-card {
background: #fff;
border-radius: 12px;
padding: 16px 20px;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
.materials-list {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 12px;
}
.material-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: #f9fafb;
border-radius: 8px;
}
.material-icon {
font-size: 18px;
}
.material-name {
flex: 1;
font-size: 14px;
color: #374151;
}
.action-area {
margin-top: 8px;
}
</style>