文件

467 行
22 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);
}
.status-tag {
font-size: 12px;
}
.reward-type-tag {
font-size: 12px;
}
</style>
</head>
<body>
<div id="app" class="page-container">
<div class="stats-cards">
<div class="stats-card">
<div class="stats-number">{{ stats.total_activities || 0 }}</div>
<div class="stats-label">总活动数</div>
</div>
<div class="stats-card">
<div class="stats-number">{{ stats.active_activities || 0 }}</div>
<div class="stats-label">进行中活动</div>
</div>
<div class="stats-card">
<div class="stats-number">{{ stats.total_participants || 0 }}</div>
<div class="stats-label">参与骑手数</div>
</div>
<div class="stats-card">
<div class="stats-number">¥{{ stats.total_rewards || 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.reward_type" placeholder="请选择" clearable style="width: 150px;">
<el-option label="全部" value=""></el-option>
<el-option label="高峰奖励" value="peak_hour"></el-option>
<el-option label="天气补贴" value="weather"></el-option>
<el-option label="满单奖励" value="order_count"></el-option>
<el-option label="距离奖励" value="distance"></el-option>
<el-option label="新手奖励" value="newbie"></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.status" placeholder="请选择" clearable style="width: 120px;">
<el-option label="全部" value=""></el-option>
<el-option label="进行中" value="1"></el-option>
<el-option label="已停止" value="0"></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">
</el-date-picker>
</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="showAddDialog">新增活动</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="id" label="ID" width="80"></el-table-column>
<el-table-column prop="activity_name" label="活动名称" min-width="150"></el-table-column>
<el-table-column prop="reward_type" label="活动类型" width="120">
<template slot-scope="scope">
<el-tag :type="getRewardTypeColor(scope.row.reward_type)" class="reward-type-tag">
{{ getRewardTypeText(scope.row.reward_type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="reward_amount" label="奖励金额" width="100">
<template slot-scope="scope">
¥{{ scope.row.reward_amount }}
</template>
</el-table-column>
<el-table-column prop="condition_desc" label="触发条件" min-width="200"></el-table-column>
<el-table-column prop="start_time" label="开始时间" width="180">
<template slot-scope="scope">
{{ formatDateTime(scope.row.start_time) }}
</template>
</el-table-column>
<el-table-column prop="end_time" label="结束时间" width="180">
<template slot-scope="scope">
{{ formatDateTime(scope.row.end_time) }}
</template>
</el-table-column>
<el-table-column prop="participant_count" label="参与人数" width="100"></el-table-column>
<el-table-column prop="status" label="状态" width="80">
<template slot-scope="scope">
<el-tag :type="scope.row.status == 1 ? 'success' : 'danger'" class="status-tag">
{{ scope.row.status == 1 ? '进行中' : '已停止' }}
</el-tag>
</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="editActivity(scope.row)">编辑</el-button>
<el-button
size="mini"
:type="scope.row.status == 1 ? 'warning' : 'success'"
@click="toggleStatus(scope.row)">
{{ scope.row.status == 1 ? '停止' : '启用' }}
</el-button>
<el-button size="mini" type="danger" @click="deleteActivity(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="dialogTitle" :visible.sync="dialogVisible" width="600px">
<el-form :model="activityForm" :rules="activityRules" ref="activityForm" label-width="100px">
<el-form-item label="活动名称" prop="activity_name">
<el-input v-model="activityForm.activity_name" placeholder="请输入活动名称"></el-input>
</el-form-item>
<el-form-item label="活动类型" prop="reward_type">
<el-select v-model="activityForm.reward_type" placeholder="请选择活动类型" style="width: 100%;">
<el-option label="高峰奖励" value="peak_hour"></el-option>
<el-option label="天气补贴" value="weather"></el-option>
<el-option label="满单奖励" value="order_count"></el-option>
<el-option label="距离奖励" value="distance"></el-option>
<el-option label="新手奖励" value="newbie"></el-option>
</el-select>
</el-form-item>
<el-form-item label="奖励金额" prop="reward_amount">
<el-input-number v-model="activityForm.reward_amount" :min="0" :precision="2" style="width: 100%;"></el-input-number>
</el-form-item>
<el-form-item label="触发条件" prop="condition_desc">
<el-input type="textarea" v-model="activityForm.condition_desc" placeholder="请描述活动触发条件" :rows="3"></el-input>
</el-form-item>
<el-form-item label="活动时间" prop="dateRange">
<el-date-picker
v-model="activityForm.dateRange"
type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item label="活动描述">
<el-input type="textarea" v-model="activityForm.description" placeholder="请输入活动描述" :rows="3"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveActivity">确定</el-button>
</div>
</el-dialog>
<!-- 活动详情对话框 -->
<el-dialog title="活动详情" :visible.sync="detailDialogVisible" width="800px">
<div v-if="currentActivity">
<el-descriptions :column="2" border>
<el-descriptions-item label="活动名称">{{ currentActivity.activity_name }}</el-descriptions-item>
<el-descriptions-item label="活动类型">{{ getRewardTypeText(currentActivity.reward_type) }}</el-descriptions-item>
<el-descriptions-item label="奖励金额">¥{{ currentActivity.reward_amount }}</el-descriptions-item>
<el-descriptions-item label="参与人数">{{ currentActivity.participant_count }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ formatDateTime(currentActivity.start_time) }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ formatDateTime(currentActivity.end_time) }}</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="currentActivity.status == 1 ? 'success' : 'danger'">
{{ currentActivity.status == 1 ? '进行中' : '已停止' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDateTime(currentActivity.create_time) }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="1" border style="margin-top: 20px;">
<el-descriptions-item label="触发条件">{{ currentActivity.condition_desc }}</el-descriptions-item>
<el-descriptions-item label="活动描述">{{ currentActivity.description || '暂无描述' }}</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
loading: false,
tableData: [],
stats: {},
searchForm: {
keyword: '',
reward_type: '',
status: '',
dateRange: []
},
pagination: {
page: 1,
pageSize: 20,
total: 0
},
dialogVisible: false,
detailDialogVisible: false,
dialogTitle: '新增活动',
currentActivity: null,
activityForm: {
id: null,
activity_name: '',
reward_type: '',
reward_amount: 0,
condition_desc: '',
description: '',
dateRange: []
},
activityRules: {
activity_name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' }
],
reward_type: [
{ required: true, message: '请选择活动类型', trigger: 'change' }
],
reward_amount: [
{ required: true, message: '请输入奖励金额', trigger: 'blur' }
],
condition_desc: [
{ required: true, message: '请输入触发条件', trigger: 'blur' }
],
dateRange: [
{ required: true, message: '请选择活动时间', trigger: 'change' }
]
}
}
},
mounted() {
this.loadData();
this.loadStats();
},
methods: {
loadData() {
this.loading = true;
const params = {
page: this.pagination.page,
pageSize: this.pagination.pageSize,
...this.searchForm
};
// 模拟API调用
setTimeout(() => {
this.tableData = [
{
id: 1,
activity_name: '午高峰奖励活动',
reward_type: 'peak_hour',
reward_amount: 5.00,
condition_desc: '11:00-14:00时段完成订单',
start_time: '2024-01-01 00:00:00',
end_time: '2024-12-31 23:59:59',
participant_count: 156,
status: 1,
create_time: '2024-01-01 10:00:00',
description: '午餐高峰期配送奖励'
},
{
id: 2,
activity_name: '雨天补贴',
reward_type: 'weather',
reward_amount: 3.00,
condition_desc: '雨天配送订单',
start_time: '2024-01-01 00:00:00',
end_time: '2024-12-31 23:59:59',
participant_count: 89,
status: 1,
create_time: '2024-01-01 10:00:00',
description: '恶劣天气配送补贴'
}
];
this.pagination.total = 2;
this.loading = false;
}, 500);
},
loadStats() {
// 模拟统计数据
this.stats = {
total_activities: 8,
active_activities: 5,
total_participants: 245,
total_rewards: 12580.50
};
},
handleSizeChange(val) {
this.pagination.pageSize = val;
this.loadData();
},
handleCurrentChange(val) {
this.pagination.page = val;
this.loadData();
},
resetSearch() {
this.searchForm = {
keyword: '',
reward_type: '',
status: '',
dateRange: []
};
this.pagination.page = 1;
this.loadData();
},
showAddDialog() {
this.dialogTitle = '新增活动';
this.activityForm = {
id: null,
activity_name: '',
reward_type: '',
reward_amount: 0,
condition_desc: '',
description: '',
dateRange: []
};
this.dialogVisible = true;
},
editActivity(row) {
this.dialogTitle = '编辑活动';
this.activityForm = {
id: row.id,
activity_name: row.activity_name,
reward_type: row.reward_type,
reward_amount: row.reward_amount,
condition_desc: row.condition_desc,
description: row.description || '',
dateRange: [row.start_time, row.end_time]
};
this.dialogVisible = true;
},
saveActivity() {
this.$refs.activityForm.validate((valid) => {
if (valid) {
// 模拟保存操作
this.$message.success('保存成功');
this.dialogVisible = false;
this.loadData();
}
});
},
viewDetail(row) {
this.currentActivity = row;
this.detailDialogVisible = true;
},
toggleStatus(row) {
const action = row.status == 1 ? '停止' : '启用';
this.$confirm(`确定要${action}该活动吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 模拟状态切换
this.$message.success(`${action}成功`);
this.loadData();
});
},
deleteActivity(row) {
this.$confirm('确定要删除该活动吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 模拟删除操作
this.$message.success('删除成功');
this.loadData();
});
},
getRewardTypeText(type) {
const typeMap = {
'peak_hour': '高峰奖励',
'weather': '天气补贴',
'order_count': '满单奖励',
'distance': '距离奖励',
'newbie': '新手奖励'
};
return typeMap[type] || type;
},
getRewardTypeColor(type) {
const colorMap = {
'peak_hour': 'warning',
'weather': 'info',
'order_count': 'success',
'distance': 'primary',
'newbie': 'danger'
};
return colorMap[type] || '';
},
formatDateTime(timestamp) {
if (!timestamp) return '';
return new Date(timestamp).toLocaleString('zh-CN');
}
}
});
</script>
</body>
</html>