2025-03-20 04:41:04 +08:00

399 lines
15 KiB
HTML

{% extends "base.html" %}
{% block title %}收款策略管理{% endblock %}
{% block content %}
<div class="container mt-4">
<h2>收款策略管理</h2>
<div class="card mb-4">
<div class="card-header">创建新策略</div>
<div class="card-body">
<form id="strategyForm" onsubmit="submitStrategy(event)">
<!-- 策略基本信息 -->
<div class="row mb-3">
<div class="col-md-4">
<div class="form-group">
<label>策略名称 <span class="text-danger">*</span></label>
<input type="text" id="strategyName" class="form-control" required>
</div>
</div>
<div class="col-md-8">
<div class="form-group">
<label>策略描述</label>
<textarea id="description" class="form-control" rows="2"></textarea>
</div>
</div>
</div>
<!-- 价格分段设置 -->
<div class="border-top pt-3">
<h5 class="text-muted mb-3">价格分段设置</h5>
<!-- 第一阶段 -->
<div class="row alert alert-light">
<div class="col-md-3">
<label>第一阶段 <span class="text-danger">*</span></label>
<div class="input-group">
<input type="number" id="segment1Threshold" class="form-control" placeholder="分钟"
required>
<div class="input-group-append">
<span class="input-group-text">分钟内</span>
</div>
</div>
</div>
<div class="col-md-3">
<label>单价 <span class="text-danger">*</span></label>
<div class="input-group">
<input type="number" step="0.01" id="segment1Price" class="form-control" required>
<div class="input-group-append">
<span class="input-group-text">元/分钟</span>
</div>
</div>
</div>
</div>
<!-- 第二阶段 -->
<div class="row alert alert-light">
<div class="col-md-3">
<label>第二阶段 <span class="text-danger">*</span></label>
<div class="input-group">
<input type="number" id="segment2Threshold" class="form-control" placeholder="分钟"
required>
<div class="input-group-append">
<span class="input-group-text">分钟内</span>
</div>
</div>
</div>
<div class="col-md-3">
<label>单价 <span class="text-danger">*</span></label>
<div class="input-group">
<input type="number" step="0.01" id="segment2Price" class="form-control" required>
<div class="input-group-append">
<span class="input-group-text">元/分钟</span>
</div>
</div>
</div>
</div>
<!-- 第三阶段 -->
<div class="row alert alert-light">
<div class="col-md-3">
<label>第三阶段(可选)</label>
<div class="input-group">
<input type="number" id="segment3Threshold" class="form-control" placeholder="分钟">
<div class="input-group-append">
<span class="input-group-text">分钟后</span>
</div>
</div>
<small class="form-text text-muted">填写后超过该部分的价格将不计算</small>
</div>
<div class="col-md-3">
<label>固定单价 <span class="text-danger">*</span></label>
<div class="input-group">
<input type="number" step="0.01" id="segment3Price" class="form-control" required>
<div class="input-group-append">
<span class="input-group-text">元/分钟</span>
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mt-3">创建策略</button>
</form>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>策略ID</th>
<th>策略名称</th>
<th>分段1阈值</th>
<th>分段1价格</th>
<th>分段2阈值</th>
<th>分段2价格</th>
<th>分段3阈值</th>
<th>分段3价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for strategy in strategies %}
<tr>
<td>{{ strategy.strategy_id }}</td>
<td>{{ strategy.strategy_name }}</td>
<td>{{ strategy.segment1_threshold }}分钟</td>
<td>{{ strategy.segment1_price }}元</td>
<td>{{ strategy.segment2_threshold }}分钟</td>
<td>{{ strategy.segment2_price }}元</td>
<td>{{ strategy.segment3_threshold or '无' }}分钟</td>
<td>{{ strategy.segment3_price or '无' }}元</td>
<td>
<button class="btn btn-sm btn-danger"
onclick="deleteStrategy({{ strategy.strategy_id }})">删除
</button>
<button class="btn btn-sm btn-info"
onclick="openBindTableModal({{ strategy.strategy_id }})">绑定桌子
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- 绑定桌子模态框 -->
<!-- 修改后的绑定桌子模态框 -->
<div class="modal fade" id="bindTableModal">
<div class="modal-dialog">
<div class="modal-content">
<form id="bindTableForm" onsubmit="submitBindTable(event)">
<div class="modal-header">
<h5 class="modal-title">绑定桌子</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<div class="table-checkbox-list" style="max-height: 300px; overflow-y: auto;">
<!-- 这里会通过JavaScript动态加载桌子列表 -->
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">绑定选中桌子</button>
</div>
</form>
</div>
</div>
</div>
<script>
let currentStrategyId = null;
async function submitStrategy(event) {
event.preventDefault();
const payload = {
strategy_name: document.getElementById('strategyName').value,
description: document.getElementById('description').value || null,
segment1_threshold: parseInt(document.getElementById('segment1Threshold').value),
segment1_price: parseFloat(document.getElementById('segment1Price').value),
segment2_threshold: parseInt(document.getElementById('segment2Threshold').value),
segment2_price: parseFloat(document.getElementById('segment2Price').value),
segment3_price: parseFloat(document.getElementById('segment3Price').value),
segment3_threshold: document.getElementById('segment3Threshold').value
? parseInt(document.getElementById('segment3Threshold').value)
: null
};
// 添加数据验证
if (payload.segment3_threshold !== null && payload.segment3_threshold <= payload.segment2_threshold) {
alert("第三阶段阈值必须大于第二阶段");
return;
}
try {
const url = '/strategies/create';
const resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: "{{ session.token }}",
strategy_data: payload
})
});
const result = await resp.json();
if (result.status === "success") {
alert("创建成功");
window.location.reload(); // 手动刷新页面
} else {
alert(`失败: ${result.message}`);
}
} catch (error) {
console.error('请求失败:', error);
alert('网络请求异常,请检查连接');
}
}
async function deleteStrategy(strategyId) {
if (!confirm('确定删除该策略?')) return;
try {
const resp = await fetch(`/strategies/delete/${strategyId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${"{{ session.token }}"}`
},
body: JSON.stringify({
token: "{{ session.token }}"
})
});
if (resp.ok) {
location.reload();
} else {
const error = await resp.json();
alert(`删除失败: ${error.detail}`);
}
} catch (error) {
console.error('请求失败:', error);
alert('网络请求异常,请检查连接');
}
}
// ... existing code ...
async function openBindTableModal(strategyId) {
currentStrategyId = strategyId;
try {
const resp = await fetch('/admin/tables/list', {
headers: {
'Authorization': `Bearer ${"{{ session.token }}"}`
}
});
const tables = await resp.json();
// 清空并重新渲染桌子列表
const container = document.querySelector('.table-checkbox-list');
container.innerHTML = tables.map(table => `
<div class="form-check">
<input class="form-check-input" type="checkbox"
name="table_ids"
value="${table.table_id}"
id="table_${table.table_id}">
<label class="form-check-label" for="table_${table.table_id}">
${table.game_table_number}
</label>
</div>
`).join('');
$('#bindTableModal').modal('show');
} catch (error) {
alert('获取桌子列表失败');
}
}
async function submitBindTable(event) {
event.preventDefault();
const tableIds = Array.from(document.querySelectorAll('#bindTableForm input[name="table_ids"]:checked')).map(input => input.value);
try {
const resp = await fetch('/strategies/bind_tables', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
strategy_id: currentStrategyId,
table_ids: tableIds.map(id => parseInt(id))
})
});
const result = await resp.json();
if (resp.ok) {
alert(result.message);
$('#bindTableModal').modal('hide');
} else {
alert(result.message);
}
} catch (error) {
console.error('请求失败:', error);
alert('网络请求异常,请检查连接');
}
}
// 初始化图表
let chart = null;
function generateChartData() {
const s1Threshold = parseInt(document.getElementById('segment1Threshold').value) || 0;
const s1Price = parseFloat(document.getElementById('segment1Price').value) || 0;
const s2Threshold = parseInt(document.getElementById('segment2Threshold').value) || s1Threshold + 30;
const s2Price = parseFloat(document.getElementById('segment2Price').value) || 0;
const s3Threshold = parseInt(document.getElementById('segment3Threshold').value);
const s3Price = parseFloat(document.getElementById('segment3Price').value) || 0;
// 确定X轴范围
const maxX = s3Threshold ? s3Threshold + 10 : s2Threshold + 10;
const dataPoints = [];
// 生成价格点
for (let x = 0; x <= maxX; x++) {
let price;
if (x <= s1Threshold) {
price = s1Price;
} else if (x <= s2Threshold) {
price = s2Price;
} else {
price = s3Price || s2Price;
}
dataPoints.push({x, price});
}
return {
labels: dataPoints.map(p => p.x + '分钟'),
datasets: [{
label: '单价 (元/分钟)',
data: dataPoints.map(p => p.price),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
};
}
function updateChart() {
const data = generateChartData();
if (chart) {
chart.destroy();
}
chart = new Chart(document.getElementById('priceChart'), {
type: 'line',
data: data,
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '价格 (元)'
}
},
x: {
title: {
display: true,
text: '使用时长'
}
}
}
}
});
}
// 添加输入监听
const inputs = document.querySelectorAll('#strategyForm input[type="number"]');
inputs.forEach(input => {
input.addEventListener('input', () => {
if (document.getElementById('strategyName').value) {
updateChart();
}
});
});
// 初始化时创建图表
document.addEventListener('DOMContentLoaded', updateChart);
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
{% endblock %}