562 行
26 KiB
HTML
562 行
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>骑手考核管理</title>
|
|
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
|
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
|
|
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
|
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
|
<style>
|
|
.page-container {
|
|
padding: 20px;
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
.stats-cards {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.stats-card {
|
|
flex: 1;
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.stats-number {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #409EFF;
|
|
}
|
|
.stats-label {
|
|
color: #666;
|
|
margin-top: 5px;
|
|
}
|
|
.search-form {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.table-container {
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.level-tag {
|
|
font-size: 12px;
|
|
}
|
|
.score-progress {
|
|
width: 100px;
|
|
}
|
|
.metric-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 10px;
|
|
padding: 8px;
|
|
background: #f9f9f9;
|
|
border-radius: 4px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app" class="page-container">
|
|
<div class="stats-cards">
|
|
<div class="stats-card">
|
|
<div class="stats-number">{{ stats.total_riders || 0 }}</div>
|
|
<div class="stats-label">参与考核骑手</div>
|
|
</div>
|
|
<div class="stats-card">
|
|
<div class="stats-number">{{ stats.excellent_count || 0 }}</div>
|
|
<div class="stats-label">优秀骑手</div>
|
|
</div>
|
|
<div class="stats-card">
|
|
<div class="stats-number">{{ stats.good_count || 0 }}</div>
|
|
<div class="stats-label">良好骑手</div>
|
|
</div>
|
|
<div class="stats-card">
|
|
<div class="stats-number">{{ stats.avg_score || 0 }}</div>
|
|
<div class="stats-label">平均考核分数</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="search-form">
|
|
<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.assessment_level" placeholder="请选择" clearable style="width: 120px;">
|
|
<el-option label="全部" value=""></el-option>
|
|
<el-option label="优秀" value="excellent"></el-option>
|
|
<el-option label="良好" value="good"></el-option>
|
|
<el-option label="合格" value="qualified"></el-option>
|
|
<el-option label="不合格" value="unqualified"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="考核月份">
|
|
<el-date-picker
|
|
v-model="searchForm.assessment_month"
|
|
type="month"
|
|
placeholder="选择月份"
|
|
format="yyyy-MM"
|
|
value-format="yyyy-MM"
|
|
style="width: 150px;">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
<el-form-item label="分数范围">
|
|
<el-input-number v-model="searchForm.min_score" :min="0" :max="100" placeholder="最低分" style="width: 100px;"></el-input-number>
|
|
<span style="margin: 0 10px;">-</span>
|
|
<el-input-number v-model="searchForm.max_score" :min="0" :max="100" placeholder="最高分" style="width: 100px;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="loadData">查询</el-button>
|
|
<el-button @click="resetSearch">重置</el-button>
|
|
<el-button type="success" @click="generateAssessment">生成月度考核</el-button>
|
|
<el-button type="info" @click="exportData">导出数据</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<el-table :data="tableData" v-loading="loading" style="width: 100%">
|
|
<el-table-column prop="rider_name" label="骑手姓名" width="100"></el-table-column>
|
|
<el-table-column prop="rider_phone" label="手机号" width="120"></el-table-column>
|
|
<el-table-column prop="assessment_month" label="考核月份" width="100"></el-table-column>
|
|
<el-table-column prop="total_score" label="总分" width="80">
|
|
<template slot-scope="scope">
|
|
<el-progress
|
|
:percentage="scope.row.total_score"
|
|
:color="getScoreColor(scope.row.total_score)"
|
|
class="score-progress">
|
|
</el-progress>
|
|
<div style="text-align: center; margin-top: 5px;">{{ scope.row.total_score }}</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="punctuality_rate" label="准时率" width="80">
|
|
<template slot-scope="scope">
|
|
{{ scope.row.punctuality_rate }}%
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="rating_score" label="好评率" width="80">
|
|
<template slot-scope="scope">
|
|
{{ scope.row.rating_score }}%
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="order_completion_rate" label="完成率" width="80">
|
|
<template slot-scope="scope">
|
|
{{ scope.row.order_completion_rate }}%
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="complaint_count" label="投诉次数" width="80"></el-table-column>
|
|
<el-table-column prop="assessment_level" label="考核等级" width="100">
|
|
<template slot-scope="scope">
|
|
<el-tag :type="getLevelColor(scope.row.assessment_level)" class="level-tag">
|
|
{{ getLevelText(scope.row.assessment_level) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="create_time" label="考核时间" width="180">
|
|
<template slot-scope="scope">
|
|
{{ formatDateTime(scope.row.create_time) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="200" fixed="right">
|
|
<template slot-scope="scope">
|
|
<el-button size="mini" @click="viewDetail(scope.row)">详情</el-button>
|
|
<el-button size="mini" type="primary" @click="editAssessment(scope.row)">编辑</el-button>
|
|
<el-button size="mini" type="danger" @click="deleteAssessment(scope.row)">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div style="padding: 20px; text-align: right;">
|
|
<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="detailDialogVisible" width="800px">
|
|
<div v-if="currentAssessment">
|
|
<el-descriptions :column="2" border>
|
|
<el-descriptions-item label="骑手姓名">{{ currentAssessment.rider_name }}</el-descriptions-item>
|
|
<el-descriptions-item label="手机号">{{ currentAssessment.rider_phone }}</el-descriptions-item>
|
|
<el-descriptions-item label="考核月份">{{ currentAssessment.assessment_month }}</el-descriptions-item>
|
|
<el-descriptions-item label="总分">{{ currentAssessment.total_score }}</el-descriptions-item>
|
|
<el-descriptions-item label="考核等级">
|
|
<el-tag :type="getLevelColor(currentAssessment.assessment_level)">
|
|
{{ getLevelText(currentAssessment.assessment_level) }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="考核时间">{{ formatDateTime(currentAssessment.create_time) }}</el-descriptions-item>
|
|
</el-descriptions>
|
|
|
|
<h4 style="margin-top: 20px;">详细指标</h4>
|
|
<div>
|
|
<div class="metric-item">
|
|
<span>准时率</span>
|
|
<span>{{ currentAssessment.punctuality_rate }}% ({{ currentAssessment.punctuality_score }}分)</span>
|
|
</div>
|
|
<div class="metric-item">
|
|
<span>好评率</span>
|
|
<span>{{ currentAssessment.rating_score }}% ({{ currentAssessment.rating_score_points }}分)</span>
|
|
</div>
|
|
<div class="metric-item">
|
|
<span>订单完成率</span>
|
|
<span>{{ currentAssessment.order_completion_rate }}% ({{ currentAssessment.completion_score }}分)</span>
|
|
</div>
|
|
<div class="metric-item">
|
|
<span>投诉处理</span>
|
|
<span>{{ currentAssessment.complaint_count }}次 ({{ currentAssessment.complaint_score }}分)</span>
|
|
</div>
|
|
<div class="metric-item">
|
|
<span>违规扣分</span>
|
|
<span>{{ currentAssessment.violation_deduction }}分</span>
|
|
</div>
|
|
<div class="metric-item">
|
|
<span>奖励加分</span>
|
|
<span>{{ currentAssessment.bonus_points }}分</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="currentAssessment.remarks" style="margin-top: 20px;">
|
|
<h4>考核备注</h4>
|
|
<p>{{ currentAssessment.remarks }}</p>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 编辑考核对话框 -->
|
|
<el-dialog title="编辑考核" :visible.sync="editDialogVisible" width="600px">
|
|
<el-form :model="assessmentForm" :rules="assessmentRules" ref="assessmentForm" label-width="120px">
|
|
<el-form-item label="准时率分数" prop="punctuality_score">
|
|
<el-input-number v-model="assessmentForm.punctuality_score" :min="0" :max="30" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="好评率分数" prop="rating_score_points">
|
|
<el-input-number v-model="assessmentForm.rating_score_points" :min="0" :max="25" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="完成率分数" prop="completion_score">
|
|
<el-input-number v-model="assessmentForm.completion_score" :min="0" :max="25" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="投诉处理分数" prop="complaint_score">
|
|
<el-input-number v-model="assessmentForm.complaint_score" :min="0" :max="20" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="违规扣分" prop="violation_deduction">
|
|
<el-input-number v-model="assessmentForm.violation_deduction" :min="0" :max="50" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="奖励加分" prop="bonus_points">
|
|
<el-input-number v-model="assessmentForm.bonus_points" :min="0" :max="20" style="width: 100%;"></el-input-number>
|
|
</el-form-item>
|
|
<el-form-item label="考核备注">
|
|
<el-input type="textarea" v-model="assessmentForm.remarks" placeholder="请输入考核备注" :rows="3"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button @click="editDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="saveAssessment">保存</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 生成考核对话框 -->
|
|
<el-dialog title="生成月度考核" :visible.sync="generateDialogVisible" width="500px">
|
|
<el-form :model="generateForm" :rules="generateRules" ref="generateForm" label-width="100px">
|
|
<el-form-item label="考核月份" prop="month">
|
|
<el-date-picker
|
|
v-model="generateForm.month"
|
|
type="month"
|
|
placeholder="选择月份"
|
|
format="yyyy-MM"
|
|
value-format="yyyy-MM"
|
|
style="width: 100%;">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
<el-form-item label="骑手范围">
|
|
<el-radio-group v-model="generateForm.rider_scope">
|
|
<el-radio label="all">全部骑手</el-radio>
|
|
<el-radio label="active">活跃骑手</el-radio>
|
|
<el-radio label="custom">指定骑手</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item v-if="generateForm.rider_scope === 'custom'" label="选择骑手">
|
|
<el-select v-model="generateForm.rider_ids" multiple placeholder="请选择骑手" style="width: 100%;">
|
|
<el-option
|
|
v-for="rider in riderList"
|
|
:key="rider.id"
|
|
:label="rider.name + ' (' + rider.phone + ')'"
|
|
:value="rider.id">
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button @click="generateDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="confirmGenerate">生成</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
|
|
<script>
|
|
new Vue({
|
|
el: '#app',
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
tableData: [],
|
|
stats: {},
|
|
riderList: [],
|
|
searchForm: {
|
|
keyword: '',
|
|
assessment_level: '',
|
|
assessment_month: '',
|
|
min_score: null,
|
|
max_score: null
|
|
},
|
|
pagination: {
|
|
page: 1,
|
|
pageSize: 20,
|
|
total: 0
|
|
},
|
|
detailDialogVisible: false,
|
|
editDialogVisible: false,
|
|
generateDialogVisible: false,
|
|
currentAssessment: null,
|
|
assessmentForm: {
|
|
id: null,
|
|
punctuality_score: 0,
|
|
rating_score_points: 0,
|
|
completion_score: 0,
|
|
complaint_score: 0,
|
|
violation_deduction: 0,
|
|
bonus_points: 0,
|
|
remarks: ''
|
|
},
|
|
assessmentRules: {
|
|
punctuality_score: [
|
|
{ required: true, message: '请输入准时率分数', trigger: 'blur' }
|
|
],
|
|
rating_score_points: [
|
|
{ required: true, message: '请输入好评率分数', trigger: 'blur' }
|
|
],
|
|
completion_score: [
|
|
{ required: true, message: '请输入完成率分数', trigger: 'blur' }
|
|
],
|
|
complaint_score: [
|
|
{ required: true, message: '请输入投诉处理分数', trigger: 'blur' }
|
|
]
|
|
},
|
|
generateForm: {
|
|
month: '',
|
|
rider_scope: 'all',
|
|
rider_ids: []
|
|
},
|
|
generateRules: {
|
|
month: [
|
|
{ required: true, message: '请选择考核月份', trigger: 'change' }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.loadData();
|
|
this.loadStats();
|
|
this.loadRiderList();
|
|
},
|
|
methods: {
|
|
loadData() {
|
|
this.loading = true;
|
|
const params = {
|
|
page: this.pagination.page,
|
|
pageSize: this.pagination.pageSize,
|
|
...this.searchForm
|
|
};
|
|
|
|
// 模拟API调用
|
|
setTimeout(() => {
|
|
this.tableData = [
|
|
{
|
|
id: 1,
|
|
rider_name: '张三',
|
|
rider_phone: '13800138001',
|
|
assessment_month: '2024-01',
|
|
total_score: 85,
|
|
punctuality_rate: 92,
|
|
punctuality_score: 28,
|
|
rating_score: 88,
|
|
rating_score_points: 22,
|
|
order_completion_rate: 95,
|
|
completion_score: 24,
|
|
complaint_count: 2,
|
|
complaint_score: 16,
|
|
violation_deduction: 5,
|
|
bonus_points: 0,
|
|
assessment_level: 'good',
|
|
create_time: '2024-02-01 10:00:00',
|
|
remarks: '表现良好,需要提升准时率'
|
|
},
|
|
{
|
|
id: 2,
|
|
rider_name: '李四',
|
|
rider_phone: '13800138002',
|
|
assessment_month: '2024-01',
|
|
total_score: 92,
|
|
punctuality_rate: 96,
|
|
punctuality_score: 29,
|
|
rating_score: 94,
|
|
rating_score_points: 24,
|
|
order_completion_rate: 98,
|
|
completion_score: 25,
|
|
complaint_count: 0,
|
|
complaint_score: 20,
|
|
violation_deduction: 0,
|
|
bonus_points: 5,
|
|
assessment_level: 'excellent',
|
|
create_time: '2024-02-01 10:00:00',
|
|
remarks: '表现优秀,获得奖励加分'
|
|
}
|
|
];
|
|
this.pagination.total = 2;
|
|
this.loading = false;
|
|
}, 500);
|
|
},
|
|
loadStats() {
|
|
// 模拟统计数据
|
|
this.stats = {
|
|
total_riders: 156,
|
|
excellent_count: 45,
|
|
good_count: 78,
|
|
avg_score: 82.5
|
|
};
|
|
},
|
|
loadRiderList() {
|
|
// 模拟骑手列表
|
|
this.riderList = [
|
|
{ id: 1, name: '张三', phone: '13800138001' },
|
|
{ id: 2, name: '李四', phone: '13800138002' },
|
|
{ id: 3, name: '王五', phone: '13800138003' }
|
|
];
|
|
},
|
|
handleSizeChange(val) {
|
|
this.pagination.pageSize = val;
|
|
this.loadData();
|
|
},
|
|
handleCurrentChange(val) {
|
|
this.pagination.page = val;
|
|
this.loadData();
|
|
},
|
|
resetSearch() {
|
|
this.searchForm = {
|
|
keyword: '',
|
|
assessment_level: '',
|
|
assessment_month: '',
|
|
min_score: null,
|
|
max_score: null
|
|
};
|
|
this.pagination.page = 1;
|
|
this.loadData();
|
|
},
|
|
viewDetail(row) {
|
|
this.currentAssessment = row;
|
|
this.detailDialogVisible = true;
|
|
},
|
|
editAssessment(row) {
|
|
this.currentAssessment = row;
|
|
this.assessmentForm = {
|
|
id: row.id,
|
|
punctuality_score: row.punctuality_score,
|
|
rating_score_points: row.rating_score_points,
|
|
completion_score: row.completion_score,
|
|
complaint_score: row.complaint_score,
|
|
violation_deduction: row.violation_deduction,
|
|
bonus_points: row.bonus_points,
|
|
remarks: row.remarks || ''
|
|
};
|
|
this.editDialogVisible = true;
|
|
},
|
|
saveAssessment() {
|
|
this.$refs.assessmentForm.validate((valid) => {
|
|
if (valid) {
|
|
// 模拟保存操作
|
|
this.$message.success('保存成功');
|
|
this.editDialogVisible = false;
|
|
this.loadData();
|
|
}
|
|
});
|
|
},
|
|
deleteAssessment(row) {
|
|
this.$confirm('确定要删除该考核记录吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
// 模拟删除操作
|
|
this.$message.success('删除成功');
|
|
this.loadData();
|
|
});
|
|
},
|
|
generateAssessment() {
|
|
this.generateForm = {
|
|
month: '',
|
|
rider_scope: 'all',
|
|
rider_ids: []
|
|
};
|
|
this.generateDialogVisible = true;
|
|
},
|
|
confirmGenerate() {
|
|
this.$refs.generateForm.validate((valid) => {
|
|
if (valid) {
|
|
// 模拟生成操作
|
|
this.$message.success('考核生成成功');
|
|
this.generateDialogVisible = false;
|
|
this.loadData();
|
|
}
|
|
});
|
|
},
|
|
exportData() {
|
|
// 模拟导出操作
|
|
this.$message.success('导出成功');
|
|
},
|
|
getLevelText(level) {
|
|
const levelMap = {
|
|
'excellent': '优秀',
|
|
'good': '良好',
|
|
'qualified': '合格',
|
|
'unqualified': '不合格'
|
|
};
|
|
return levelMap[level] || level;
|
|
},
|
|
getLevelColor(level) {
|
|
const colorMap = {
|
|
'excellent': 'success',
|
|
'good': 'primary',
|
|
'qualified': 'warning',
|
|
'unqualified': 'danger'
|
|
};
|
|
return colorMap[level] || '';
|
|
},
|
|
getScoreColor(score) {
|
|
if (score >= 90) return '#67c23a';
|
|
if (score >= 80) return '#409eff';
|
|
if (score >= 70) return '#e6a23c';
|
|
return '#f56c6c';
|
|
},
|
|
formatDateTime(timestamp) {
|
|
if (!timestamp) return '';
|
|
return new Date(timestamp).toLocaleString('zh-CN');
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |