文件

626 行
27 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>骑手认证审核</title>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/css/menu.css?v=1.0.5"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/css/minimal-admin.css?v=1.0.0"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/Retail/css/myscroll.css?v=1.0.2"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/layui-v2.4.3/layui/css/layui.css"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/Retail/css/home.css"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/Retail/css/goodsList.css?v=1.0.2"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/vue/element-ui.css?v=2.13.2">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/vue/js/vue2.5.16.js"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/vue/element-ui.js?v=2.13.2"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/js/common.js?v=1.0.2"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/jquery/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/layui-v2.4.3/layui/layui.js"></script>
</head>
<body>
<div id="RiderAuthManagement" v-cloak>
<div class="goodsBox">
<div class="goods_title">
<div class="goods_title_inner">
<i class="fas fa-user-check" style="margin-right: 8px; color: #409EFF;"></i>
骑手认证审核
</div>
</div>
</div>
<!-- 统计卡片区域 -->
<div class="auth-stats-container" style="padding: 20px; background: #f5f7fa;">
<el-row :gutter="20">
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fas fa-users"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.total_count }}</div>
<div class="stat-label">总申请数</div>
</div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<i class="fas fa-clock"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.pending_auth_count }}</div>
<div class="stat-label">待审核实名</div>
</div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.approved_auth_count }}</div>
<div class="stat-label">实名已通过</div>
</div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
<i class="fas fa-heartbeat"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.pending_health_count }}</div>
<div class="stat-label">待审核健康证</div>
</div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);">
<i class="fas fa-certificate"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.approved_health_count }}</div>
<div class="stat-label">健康证已通过</div>
</div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);">
<i class="fas fa-user-shield"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ statistics.fully_certified_count }}</div>
<div class="stat-label">完全认证</div>
</div>
</div>
</el-col>
</el-row>
</div>
<!-- 搜索筛选区域 -->
<div class="search-container" style="padding: 20px; background: white; margin: 20px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);">
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="骑手姓名/手机号/身份证" clearable style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="实名状态">
<el-select v-model="searchForm.auth_status" placeholder="请选择实名状态" clearable style="width: 150px;">
<el-option label="待审核" value="0"></el-option>
<el-option label="已通过" value="1"></el-option>
<el-option label="已拒绝" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="健康证状态">
<el-select v-model="searchForm.health_status" placeholder="请选择健康证状态" clearable style="width: 150px;">
<el-option label="待审核" value="0"></el-option>
<el-option label="已通过" value="1"></el-option>
<el-option label="已拒绝" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker
v-model="searchForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 240px;">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchAuth" icon="el-icon-search">搜索</el-button>
<el-button @click="resetSearch" icon="el-icon-refresh">重置</el-button>
<el-button type="success" @click="exportAuth" icon="el-icon-download">导出</el-button>
</el-form-item>
</el-form>
</div>
<!-- 认证列表 -->
<div class="table-container" style="margin: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);">
<el-table :data="authList" v-loading="loading" style="width: 100%;" stripe>
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="id" label="ID" width="80" align="center"></el-table-column>
<el-table-column label="骑手信息" width="200">
<template slot-scope="scope">
<div style="display: flex; align-items: center;">
<el-avatar :src="scope.row.avatar" size="small" style="margin-right: 10px;">
<i class="el-icon-user-solid"></i>
</el-avatar>
<div>
<div style="font-weight: bold;">{{ scope.row.nickname }}</div>
<div style="color: #999; font-size: 12px;">{{ scope.row.phone }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="实名认证" width="150">
<template slot-scope="scope">
<div>
<div style="font-weight: bold;">{{ scope.row.real_name || '未填写' }}</div>
<div style="color: #999; font-size: 12px;">{{ scope.row.id_card || '未填写' }}</div>
<el-tag :type="getAuthStatusType(scope.row.auth_status)" size="mini">
{{ scope.row.auth_status_text }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="健康证" width="120" align="center">
<template slot-scope="scope">
<div>
<el-tag :type="getHealthStatusType(scope.row.health_status)" size="mini">
{{ scope.row.health_status_text }}
</el-tag>
<div v-if="scope.row.health_expire_time" style="color: #999; font-size: 12px; margin-top: 5px;">
到期:{{ scope.row.health_expire_time }}
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="create_time_format" label="申请时间" width="160" align="center"></el-table-column>
<el-table-column prop="update_time_format" label="更新时间" width="160" align="center"></el-table-column>
<el-table-column label="操作" width="250" align="center">
<template slot-scope="scope">
<el-button size="mini" @click="viewDetail(scope.row)" icon="el-icon-view">详情</el-button>
<el-button v-if="scope.row.auth_status == 0" size="mini" type="success" @click="auditAuth(scope.row, 1)" icon="el-icon-check">通过实名</el-button>
<el-button v-if="scope.row.auth_status == 0" size="mini" type="danger" @click="auditAuth(scope.row, 2)" icon="el-icon-close">拒绝实名</el-button>
<el-button v-if="scope.row.health_status == 0" size="mini" type="success" @click="auditHealth(scope.row, 1)" icon="el-icon-check">通过健康证</el-button>
<el-button v-if="scope.row.health_status == 0" size="mini" type="danger" @click="auditHealth(scope.row, 2)" icon="el-icon-close">拒绝健康证</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作 -->
<div style="padding: 20px; border-top: 1px solid #ebeef5;">
<el-button type="success" @click="batchAudit('auth', 1)" icon="el-icon-check">批量通过实名</el-button>
<el-button type="danger" @click="batchAudit('auth', 2)" icon="el-icon-close">批量拒绝实名</el-button>
<el-button type="success" @click="batchAudit('health', 1)" icon="el-icon-check">批量通过健康证</el-button>
<el-button type="danger" @click="batchAudit('health', 2)" icon="el-icon-close">批量拒绝健康证</el-button>
</div>
<!-- 分页 -->
<div style="padding: 20px; text-align: center;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pagination.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="pagination.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
</div>
<!-- 认证详情弹窗 -->
<el-dialog title="认证详情" :visible.sync="detailDialog.visible" width="800px">
<div v-if="detailDialog.data">
<el-tabs v-model="detailDialog.activeTab">
<el-tab-pane label="基本信息" name="basic">
<el-descriptions :column="2" border>
<el-descriptions-item label="骑手ID">{{ detailDialog.data.id }}</el-descriptions-item>
<el-descriptions-item label="昵称">{{ detailDialog.data.nickname }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailDialog.data.phone }}</el-descriptions-item>
<el-descriptions-item label="注册时间">{{ detailDialog.data.create_time_format }}</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="实名认证" name="auth">
<el-descriptions :column="2" border>
<el-descriptions-item label="真实姓名">{{ detailDialog.data.real_name || '未填写' }}</el-descriptions-item>
<el-descriptions-item label="身份证号">{{ detailDialog.data.id_card || '未填写' }}</el-descriptions-item>
<el-descriptions-item label="认证状态">
<el-tag :type="getAuthStatusType(detailDialog.data.auth_status)">
{{ detailDialog.data.auth_status_text }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="审核时间">{{ detailDialog.data.auth_audit_time || '未审核' }}</el-descriptions-item>
<el-descriptions-item label="审核管理员">{{ detailDialog.data.auth_audit_admin || '未审核' }}</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2">{{ detailDialog.data.auth_remark || '无' }}</el-descriptions-item>
</el-descriptions>
<div v-if="detailDialog.data.id_card_front || detailDialog.data.id_card_back" style="margin-top: 20px;">
<h4>身份证照片</h4>
<el-row :gutter="20">
<el-col :span="12" v-if="detailDialog.data.id_card_front">
<div style="text-align: center;">
<p>身份证正面</p>
<el-image :src="detailDialog.data.id_card_front" style="width: 200px; height: 120px;" fit="cover" :preview-src-list="[detailDialog.data.id_card_front]"></el-image>
</div>
</el-col>
<el-col :span="12" v-if="detailDialog.data.id_card_back">
<div style="text-align: center;">
<p>身份证反面</p>
<el-image :src="detailDialog.data.id_card_back" style="width: 200px; height: 120px;" fit="cover" :preview-src-list="[detailDialog.data.id_card_back]"></el-image>
</div>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane label="健康证" name="health">
<el-descriptions :column="2" border>
<el-descriptions-item label="健康证状态">
<el-tag :type="getHealthStatusType(detailDialog.data.health_status)">
{{ detailDialog.data.health_status_text }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="有效期至">{{ detailDialog.data.health_expire_time || '未设置' }}</el-descriptions-item>
<el-descriptions-item label="审核时间">{{ detailDialog.data.health_audit_time || '未审核' }}</el-descriptions-item>
<el-descriptions-item label="审核管理员">{{ detailDialog.data.health_audit_admin || '未审核' }}</el-descriptions-item>
<el-descriptions-item label="审核备注" :span="2">{{ detailDialog.data.health_remark || '无' }}</el-descriptions-item>
</el-descriptions>
<div v-if="detailDialog.data.health_certificate" style="margin-top: 20px; text-align: center;">
<h4>健康证照片</h4>
<el-image :src="detailDialog.data.health_certificate" style="width: 300px; height: 200px;" fit="cover" :preview-src-list="[detailDialog.data.health_certificate]"></el-image>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
<!-- 审核弹窗 -->
<el-dialog :title="auditDialog.title" :visible.sync="auditDialog.visible" width="500px">
<el-form :model="auditDialog.form" label-width="80px">
<el-form-item label="审核结果">
<el-tag :type="auditDialog.status == 1 ? 'success' : 'danger'">
{{ auditDialog.status == 1 ? '通过' : '拒绝' }}
</el-tag>
</el-form-item>
<el-form-item v-if="auditDialog.type === 'health' && auditDialog.status == 1" label="有效期至">
<el-date-picker
v-model="auditDialog.form.expire_time"
type="date"
placeholder="选择有效期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="auditDialog.form.remark" type="textarea" :rows="4" placeholder="请输入审核备注"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="auditDialog.visible = false">取消</el-button>
<el-button type="primary" @click="confirmAudit" :loading="auditDialog.loading">确定</el-button>
</div>
</el-dialog>
</div>
<style>
.stat-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
transition: transform 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
}
.stat-icon i {
font-size: 24px;
color: white;
}
.stat-content {
flex: 1;
}
.stat-number {
font-size: 24px;
font-weight: bold;
color: #303133;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
.search-form .el-form-item {
margin-bottom: 0;
}
[v-cloak] {
display: none;
}
</style>
<script>
new Vue({
el: '#RiderAuthManagement',
data() {
return {
loading: false,
authList: [],
statistics: {
total_count: 0,
pending_auth_count: 0,
approved_auth_count: 0,
pending_health_count: 0,
approved_health_count: 0,
fully_certified_count: 0
},
searchForm: {
keyword: '',
auth_status: '',
health_status: '',
dateRange: []
},
pagination: {
page: 1,
pageSize: 20,
total: 0
},
detailDialog: {
visible: false,
data: null,
activeTab: 'basic'
},
auditDialog: {
visible: false,
title: '',
type: '',
status: 0,
riderId: 0,
form: {
remark: '',
expire_time: ''
},
loading: false
}
}
},
mounted() {
this.loadAuthList();
this.loadStatistics();
},
methods: {
// 加载认证列表
loadAuthList() {
this.loading = true;
let params = {
page: this.pagination.page,
pageSize: this.pagination.pageSize,
keyword: this.searchForm.keyword,
auth_status: this.searchForm.auth_status,
health_status: this.searchForm.health_status
};
if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
params.start_time = this.searchForm.dateRange[0];
params.end_time = this.searchForm.dateRange[1];
}
$.get('/index.php/Retail/RiderAuth/getAuthList', params, (res) => {
this.loading = false;
if (res.code === 0) {
this.authList = res.data.list;
this.pagination.total = res.data.total;
} else {
this.$message.error(res.msg);
}
});
},
// 加载统计数据
loadStatistics() {
let params = {};
if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
params.start_time = this.searchForm.dateRange[0];
params.end_time = this.searchForm.dateRange[1];
}
$.get('/index.php/Retail/RiderAuth/getAuthStats', params, (res) => {
if (res.code === 0) {
this.statistics = res.data;
}
});
},
// 搜索
searchAuth() {
this.pagination.page = 1;
this.loadAuthList();
this.loadStatistics();
},
// 重置搜索
resetSearch() {
this.searchForm = {
keyword: '',
auth_status: '',
health_status: '',
dateRange: []
};
this.pagination.page = 1;
this.loadAuthList();
this.loadStatistics();
},
// 导出
exportAuth() {
let params = {
keyword: this.searchForm.keyword,
auth_status: this.searchForm.auth_status,
health_status: this.searchForm.health_status
};
if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
params.start_time = this.searchForm.dateRange[0];
params.end_time = this.searchForm.dateRange[1];
}
let url = '/index.php/Retail/RiderAuth/exportAuth?' + $.param(params);
window.open(url);
},
// 查看详情
viewDetail(row) {
$.get('/index.php/Retail/RiderAuth/getAuthDetail', {id: row.id}, (res) => {
if (res.code === 0) {
this.detailDialog.data = res.data;
this.detailDialog.visible = true;
this.detailDialog.activeTab = 'basic';
} else {
this.$message.error(res.msg);
}
});
},
// 审核实名认证
auditAuth(row, status) {
this.auditDialog.title = status === 1 ? '通过实名认证' : '拒绝实名认证';
this.auditDialog.type = 'auth';
this.auditDialog.status = status;
this.auditDialog.riderId = row.id;
this.auditDialog.form.remark = '';
this.auditDialog.form.expire_time = '';
this.auditDialog.visible = true;
},
// 审核健康证
auditHealth(row, status) {
this.auditDialog.title = status === 1 ? '通过健康证' : '拒绝健康证';
this.auditDialog.type = 'health';
this.auditDialog.status = status;
this.auditDialog.riderId = row.id;
this.auditDialog.form.remark = '';
this.auditDialog.form.expire_time = '';
this.auditDialog.visible = true;
},
// 确认审核
confirmAudit() {
this.auditDialog.loading = true;
let url = this.auditDialog.type === 'auth' ? '/index.php/Retail/RiderAuth/auditAuth' : '/index.php/Retail/RiderAuth/auditHealth';
let params = {
id: this.auditDialog.riderId,
status: this.auditDialog.status,
remark: this.auditDialog.form.remark
};
if (this.auditDialog.type === 'health' && this.auditDialog.status == 1) {
params.expire_time = this.auditDialog.form.expire_time;
}
$.post(url, params, (res) => {
this.auditDialog.loading = false;
if (res.code === 0) {
this.$message.success('操作成功');
this.auditDialog.visible = false;
this.loadAuthList();
this.loadStatistics();
} else {
this.$message.error(res.msg);
}
});
},
// 批量审核
batchAudit(type, status) {
let selectedRows = this.$refs.authTable ? this.$refs.authTable.selection : [];
if (selectedRows.length === 0) {
this.$message.warning('请选择要操作的记录');
return;
}
let ids = selectedRows.map(row => row.id);
let actionText = status === 1 ? '通过' : '拒绝';
let typeText = type === 'auth' ? '实名认证' : '健康证';
this.$confirm(`确定要批量${actionText}选中的${typeText}吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$.post('/index.php/Retail/RiderAuth/batchAudit', {
ids: ids.join(','),
type: type,
status: status
}, (res) => {
if (res.code === 0) {
this.$message.success('操作成功');
this.loadAuthList();
this.loadStatistics();
} else {
this.$message.error(res.msg);
}
});
});
},
// 获取实名认证状态类型
getAuthStatusType(status) {
const typeMap = {
0: 'warning',
1: 'success',
2: 'danger'
};
return typeMap[status] || 'info';
},
// 获取健康证状态类型
getHealthStatusType(status) {
const typeMap = {
0: 'warning',
1: 'success',
2: 'danger'
};
return typeMap[status] || 'info';
},
// 分页大小改变
handleSizeChange(val) {
this.pagination.pageSize = val;
this.pagination.page = 1;
this.loadAuthList();
},
// 当前页改变
handleCurrentChange(val) {
this.pagination.page = val;
this.loadAuthList();
}
}
});
</script>
</body>
</html>