桌子生成二维码
This commit is contained in:
parent
adbe039e3d
commit
64f44d0736
@ -3,3 +3,6 @@ import os
|
|||||||
class Config:
|
class Config:
|
||||||
SECRET_KEY = os.getenv('SECRET_KEY', 'jagxor-xokwis-dekqE6')
|
SECRET_KEY = os.getenv('SECRET_KEY', 'jagxor-xokwis-dekqE6')
|
||||||
BASE_API_URL = os.getenv('BASE_API_URL', 'http://127.0.0.1:8000')
|
BASE_API_URL = os.getenv('BASE_API_URL', 'http://127.0.0.1:8000')
|
||||||
|
WX_APP_ID = os.getenv('WX_APP_ID', 'wx2c0e64b724b6dec4')
|
||||||
|
WX_APP_SECRET = os.getenv('WX_APP_SECRET', 'b5330aece22ce5cf5df3048cb28d1558')
|
||||||
|
WX_QRCODE_API = 'https://api.weixin.qq.com/wxa/getwxacode'
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, session
|
from flask import Blueprint, render_template, request, redirect, url_for, flash, session
|
||||||
import requests
|
import requests
|
||||||
from frontend.config import Config
|
from frontend.config import Config
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
|
||||||
|
wx_token_cache = {
|
||||||
|
'access_token': None,
|
||||||
|
'expires_at': 0 # 过期时间戳
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
tables_bp = Blueprint('tables', __name__)
|
tables_bp = Blueprint('tables', __name__)
|
||||||
|
|
||||||
@ -162,3 +170,50 @@ def list_all_tables():
|
|||||||
return resp.json()
|
return resp.json()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"detail": str(e)}, 500
|
return {"detail": str(e)}, 500
|
||||||
|
|
||||||
|
|
||||||
|
@tables_bp.route('/tables/qrcode/<int:table_id>')
|
||||||
|
def get_table_qrcode(table_id):
|
||||||
|
if not session.get('token'):
|
||||||
|
return {"detail": "未认证"}, 401
|
||||||
|
|
||||||
|
# 获取access_token
|
||||||
|
try:
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time > wx_token_cache['expires_at']:
|
||||||
|
resp = requests.get(
|
||||||
|
"https://api.weixin.qq.com/cgi-bin/token",
|
||||||
|
params={
|
||||||
|
"grant_type": "client_credential",
|
||||||
|
"appid": Config.WX_APP_ID,
|
||||||
|
"secret": Config.WX_APP_SECRET
|
||||||
|
}
|
||||||
|
)
|
||||||
|
token_data = resp.json()
|
||||||
|
if 'access_token' not in token_data:
|
||||||
|
return {"detail": "获取token失败"}, 500
|
||||||
|
|
||||||
|
wx_token_cache['access_token'] = token_data['access_token']
|
||||||
|
wx_token_cache['expires_at'] = current_time + token_data['expires_in'] - 300 # 提前5分钟过期
|
||||||
|
|
||||||
|
# 生成小程序码
|
||||||
|
qr_resp = requests.post(
|
||||||
|
Config.WX_QRCODE_API,
|
||||||
|
params={"access_token": wx_token_cache['access_token']},
|
||||||
|
json={
|
||||||
|
"path": f"/pages/timer/timer?table_id={table_id}",
|
||||||
|
"env_version": "trial",
|
||||||
|
"width": 280
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if qr_resp.status_code != 200 or 'image' not in qr_resp.headers.get('Content-Type', ''):
|
||||||
|
return {"detail": "生成二维码失败"}, 500
|
||||||
|
|
||||||
|
return {
|
||||||
|
"image": f"data:image/png;base64,{base64.b64encode(qr_resp.content).decode('utf-8')}",
|
||||||
|
"table_id": table_id
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"detail": str(e)}, 500
|
||||||
@ -4,130 +4,172 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<h1>桌台列表</h1>
|
<h1>桌台列表</h1>
|
||||||
<a href="{{ url_for('tables.add_table') }}" class="btn btn-primary mb-3">添加桌台</a>
|
<a href="{{ url_for('tables.add_table') }}" class="btn btn-primary mb-3">添加桌台</a>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>桌号</th>
|
<th>桌号</th>
|
||||||
<th>容量</th>
|
<th>容量</th>
|
||||||
<th>收款策略</th>
|
<th>收款策略</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for table in tables %}
|
{% for table in tables %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ table.table_id }}</td>
|
<td>{{ table.table_id }}</td>
|
||||||
<td>{{ table.game_table_number }}</td>
|
<td>{{ table.game_table_number }}</td>
|
||||||
<td>{{ table.capacity }}</td>
|
<td>{{ table.capacity }}</td>
|
||||||
<td>{{ table.strategy_name }}</td>
|
<td>{{ table.strategy_name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('tables.edit_table', table_id=table.table_id) }}"
|
<a href="{{ url_for('tables.edit_table', table_id=table.table_id) }}"
|
||||||
class="btn btn-warning btn-sm">编辑</a>
|
class="btn btn-warning btn-sm">编辑</a>
|
||||||
<form action="{{ url_for('tables.delete_table', table_id=table.table_id) }}" method="post"
|
<button class="btn btn-sm btn-info"
|
||||||
class="d-inline">
|
onclick="showQRCode({{ table.table_id }}, '{{ table.game_table_number }}')">
|
||||||
<button type="submit" class="btn btn-danger btn-sm"
|
生成二维码
|
||||||
onclick="return confirm('确认删除该桌台吗?')">删除</button>
|
</button>
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 选择策略模态框 -->
|
<form action="{{ url_for('tables.delete_table', table_id=table.table_id) }}" method="post"
|
||||||
<div class="modal fade" id="selectStrategyModal" tabindex="-1" aria-labelledby="selectStrategyModalLabel"
|
class="d-inline">
|
||||||
aria-hidden="true">
|
<button type="submit" class="btn btn-danger btn-sm"
|
||||||
<div class="modal-dialog">
|
onclick="return confirm('确认删除该桌台吗?')">删除
|
||||||
<div class="modal-content">
|
</button>
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="selectStrategyModalLabel">选择收款策略</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<form onsubmit="selectStrategy(event)">
|
|
||||||
<input type="hidden" id="selectedTableId">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="strategySelect">收款策略</label>
|
|
||||||
<select class="form-control" id="strategySelect" required>
|
|
||||||
<!-- 动态填充策略选项 -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
||||||
<button type="submit" class="btn btn-primary">保存</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="qrcodeModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">桌台二维码 - 桌号<span id="tableNumber"></span></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-center">
|
||||||
|
<img id="qrcodeImage" class="img-fluid mb-3" style="max-width: 300px;">
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-primary" onclick="downloadQRCode()">下载二维码</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<!-- 选择策略模态框 -->
|
||||||
<script>
|
<div class="modal fade" id="selectStrategyModal" tabindex="-1" aria-labelledby="selectStrategyModalLabel"
|
||||||
async function openSelectStrategyModal(tableId) {
|
aria-hidden="true">
|
||||||
try {
|
<div class="modal-dialog">
|
||||||
const token = "{{ session['token'] }}";
|
<div class="modal-content">
|
||||||
const response = await fetch('/admin/strategies/list', {
|
<div class="modal-header">
|
||||||
headers: {
|
<h5 class="modal-title" id="selectStrategyModalLabel">选择收款策略</h5>
|
||||||
'Authorization': `Bearer ${token}`
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
}
|
</div>
|
||||||
});
|
<form onsubmit="selectStrategy(event)">
|
||||||
const strategies = await response.json();
|
<input type="hidden" id="selectedTableId">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="strategySelect">收款策略</label>
|
||||||
|
<select class="form-control" id="strategySelect" required>
|
||||||
|
<!-- 动态填充策略选项 -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||||
|
<button type="submit" class="btn btn-primary">保存</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
const strategySelect = document.getElementById('strategySelect');
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
strategySelect.innerHTML = '';
|
<script>
|
||||||
|
function showQRCode(tableId, tableNumber) {
|
||||||
strategies.forEach(strategy => {
|
fetch(`/tables/qrcode/${tableId}`)
|
||||||
const option = document.createElement('option');
|
.then(res => res.json())
|
||||||
option.value = strategy.strategy_id;
|
.then(data => {
|
||||||
option.textContent = strategy.strategy_name;
|
if (data.image) {
|
||||||
strategySelect.appendChild(option);
|
document.getElementById('qrcodeImage').src = data.image;
|
||||||
});
|
document.getElementById('tableNumber').textContent = tableNumber;
|
||||||
|
new bootstrap.Modal(document.getElementById('qrcodeModal')).show();
|
||||||
document.getElementById('selectedTableId').value = tableId;
|
|
||||||
const myModal = new bootstrap.Modal(document.getElementById('selectStrategyModal'));
|
|
||||||
myModal.show();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取策略列表失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function selectStrategy(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const tableId = document.getElementById('selectedTableId').value;
|
|
||||||
const strategyId = document.getElementById('strategySelect').value;
|
|
||||||
const token = "{{ session['token'] }}";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`/admin/tables/${tableId}/strategy`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
strategy_id: strategyId
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const myModal = bootstrap.Modal.getInstance(document.getElementById('selectStrategyModal'));
|
|
||||||
myModal.hide();
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
const error = await response.json();
|
|
||||||
alert(`更新失败: ${error.detail}`);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
});
|
||||||
console.error('更新策略失败:', error);
|
}
|
||||||
}
|
|
||||||
|
function downloadQRCode() {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.download = `桌台二维码-${document.getElementById('tableNumber').textContent}.png`;
|
||||||
|
link.href = document.getElementById('qrcodeImage').src;
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSelectStrategyModal(tableId) {
|
||||||
|
try {
|
||||||
|
const token = "{{ session['token'] }}";
|
||||||
|
const response = await fetch('/admin/strategies/list', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const strategies = await response.json();
|
||||||
|
|
||||||
|
const strategySelect = document.getElementById('strategySelect');
|
||||||
|
strategySelect.innerHTML = '';
|
||||||
|
|
||||||
|
strategies.forEach(strategy => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = strategy.strategy_id;
|
||||||
|
option.textContent = strategy.strategy_name;
|
||||||
|
strategySelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('selectedTableId').value = tableId;
|
||||||
|
const myModal = new bootstrap.Modal(document.getElementById('selectStrategyModal'));
|
||||||
|
myModal.show();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取策略列表失败:', error);
|
||||||
}
|
}
|
||||||
</script>
|
}
|
||||||
|
|
||||||
|
async function selectStrategy(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const tableId = document.getElementById('selectedTableId').value;
|
||||||
|
const strategyId = document.getElementById('strategySelect').value;
|
||||||
|
const token = "{{ session['token'] }}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/admin/tables/${tableId}/strategy`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
strategy_id: strategyId
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const myModal = bootstrap.Modal.getInstance(document.getElementById('selectStrategyModal'));
|
||||||
|
myModal.hide();
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
const error = await response.json();
|
||||||
|
alert(`更新失败: ${error.detail}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新策略失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user