🎨 新增讲师申请审核页,新增vip管理页面

This commit is contained in:
2025-09-02 20:32:51 +08:00
parent f3ac0bb511
commit 5f470478cd
5 changed files with 716 additions and 4 deletions

View File

@@ -0,0 +1,337 @@
<template>
<div>
<div class="gva-table-box">
<div class="gva-btn-list">
<el-button type="primary" @click="refreshList">刷新列表</el-button>
</div>
<!-- 统计信息 -->
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<el-row :gutter="20">
<el-col :span="6">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #409eff;">{{ getStats().total }}</div>
<div style="color: #666;">总申请数</div>
</div>
</el-col>
<el-col :span="6">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #e6a23c;">{{ getStats().pending }}</div>
<div style="color: #666;">待审核</div>
</div>
</el-col>
<el-col :span="6">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #67c23a;">{{ getStats().approved }}</div>
<div style="color: #666;">已通过</div>
</div>
</el-col>
<el-col :span="6">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #f56c6c;">{{ getStats().rejected }}</div>
<div style="color: #666;">已拒绝</div>
</div>
</el-col>
</el-row>
</div>
<!-- 讲师申请列表 -->
<el-table
:data="tableData"
v-loading="tableLoading"
border
stripe
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="nickname" label="申请人" align="center" width="100" />
<el-table-column prop="phone" label="手机号" align="center" width="120" />
<el-table-column prop="reason" label="申请理由" align="center" min-width="150" show-overflow-tooltip />
<el-table-column prop="expectRate" label="期望分成比例" align="center" width="120">
<template #default="{ row }">
<span class="text-blue-600 font-bold">{{ row.expectRate }}%</span>
</template>
</el-table-column>
<el-table-column label="申请状态" align="center" width="100">
<template #default="{ row }">
<el-tag :type="getStatusTag(row.status).type">
{{ getStatusTag(row.status).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="CreatedAt" label="申请时间" align="center" width="180">
<template #default="{ row }">
{{ formatDate(row.CreatedAt) }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200">
<template #default="{ row }">
<el-button
v-if="row.status === 0"
type="success"
size="small"
@click="handleReview(row, 1)"
>
通过
</el-button>
<el-button
v-if="row.status === 0"
type="danger"
size="small"
@click="handleReview(row, 2)"
>
拒绝
</el-button>
<el-button
type="primary"
size="small"
@click="handleDetail(row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div style="margin-top: 20px; text-align: right;">
<el-pagination
v-model:current-page="queryParams.page"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="changePage"
/>
</div>
</div>
<!-- 审核弹窗 -->
<el-dialog
v-model="reviewVisible"
:title="reviewData.status === 1 ? '通过申请' : '拒绝申请'"
width="500px"
destroy-on-close
>
<el-form :model="reviewForm" label-width="100px">
<el-form-item label="申请人">
<span>{{ reviewData.nickname }}</span>
</el-form-item>
<el-form-item label="申请理由">
<span>{{ reviewData.reason }}</span>
</el-form-item>
<el-form-item label="期望分成">
<span>{{ reviewData.expectRate }}%</span>
</el-form-item>
<el-form-item label="审核备注" v-if="reviewData.status === 2">
<el-input
v-model="reviewForm.note"
type="textarea"
:rows="3"
placeholder="请输入拒绝理由"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="reviewVisible = false">取消</el-button>
<el-button
:type="reviewData.status === 1 ? 'success' : 'danger'"
@click="confirmReview"
>
{{ reviewData.status === 1 ? '确认通过' : '确认拒绝' }}
</el-button>
</span>
</template>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog
v-model="detailVisible"
title="申请详情"
width="600px"
destroy-on-close
>
<el-descriptions :column="2" border>
<el-descriptions-item label="申请人">{{ detailData.nickname || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailData.phone || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="申请理由" :span="2">{{ detailData.reason || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="期望分成比例">{{ (detailData.expectRate || 0) }}%</el-descriptions-item>
<el-descriptions-item label="申请状态">
<el-tag :type="getStatusTag(detailData.status).type">
{{ getStatusTag(detailData.status).label }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ formatDate(detailData.CreatedAt) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDate(detailData.UpdatedAt) }}</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2">{{ detailData.note || '无' }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script setup>
import { getTeacherApplyList, reviewTeacherApply } from '@/api/user/index.js'
import { ref, onMounted } from 'vue'
import { formatDate } from '@/utils/format'
import { ElMessage } from 'element-plus'
const tableLoading = ref(true)
const tableData = ref([])
const detailVisible = ref(false)
const detailData = ref({})
const reviewVisible = ref(false)
const reviewData = ref({})
const queryParams = ref({
page: 1,
pageSize: 10
})
const total = ref(0)
const EMPTY_STR = '- -'
// 获取状态标签
const getStatusTag = (status) => {
const map = {
0: { type: 'warning', label: '待审核' },
1: { type: 'success', label: '已通过' },
2: { type: 'danger', label: '已拒绝' }
}
return map[status] || { type: '', label: EMPTY_STR }
}
// 获取统计数据
const getStats = () => {
const stats = {
total: total.value,
pending: tableData.value.filter(item => item.status === 0).length,
approved: tableData.value.filter(item => item.status === 1).length,
rejected: tableData.value.filter(item => item.status === 2).length
}
return stats
}
// 获取列表数据
const getList = async () => {
tableLoading.value = true
try {
const res = await getTeacherApplyList(queryParams.value)
if (res.code === 0) {
tableData.value = res.data.list
total.value = res.data.total
console.log('讲师申请数据获取成功:', {
total: res.data.total,
list: res.data.list
})
} else {
ElMessage.error(res.msg || '获取讲师申请列表失败')
}
} catch (error) {
console.error('获取讲师申请列表失败:', error)
ElMessage.error('获取讲师申请列表失败')
} finally {
tableLoading.value = false
}
}
// 查看详情
const handleDetail = (row) => {
detailData.value = { ...row }
detailVisible.value = true
}
// 审核操作
const handleReview = (row, status) => {
reviewData.value = { ...row, status }
reviewVisible.value = true
}
// 确认审核
const confirmReview = async () => {
try {
const data = {
ID: reviewData.value.ID,
status: reviewData.value.status,
note: reviewData.value.status === 2 ? reviewForm.value.note : ''
}
const res = await reviewTeacherApply(data)
if (res.code === 0) {
ElMessage.success(reviewData.value.status === 1 ? '审核通过成功' : '审核拒绝成功')
reviewVisible.value = false
getList() // 刷新列表
} else {
ElMessage.error(res.msg || '审核操作失败')
}
} catch (error) {
console.error('审核操作失败:', error)
ElMessage.error('审核操作失败')
}
}
// 分页相关
const changePage = (page) => {
queryParams.value.page = page
getList()
}
const handleSizeChange = (size) => {
queryParams.value.pageSize = size
queryParams.value.page = 1
getList()
}
// 刷新列表
const refreshList = () => {
getList()
}
// 审核表单
const reviewForm = ref({
note: ''
})
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
.gva-table-box {
.gva-btn-list {
margin-bottom: 20px;
}
.el-row {
.el-col {
.el-statistic {
text-align: center;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.el-statistic__number {
font-size: 24px;
font-weight: bold;
}
.el-statistic__label {
color: #666;
margin-top: 8px;
}
}
}
}
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>