文件

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>