架构说明:
1. 前端:实时计算单价×数量、展示总价,不刷新页面
2. 点击下单用 fetch 无刷新提交到 TP8 接口
3. 后端TP8二次核算价格,防止前端篡改,返回订单结果
4. 使用 JSON 传参,控制器返回 json 格式
一、控制器代码 app/controller/Order.php
php
<?php
namespace app\controller;
use think\Request;
class Order
{
// 商品下单接口(AJAX提交地址)
public function submit(Request $request)
{
// 接收前端JSON数据
$data = $request->json();
$goods_price = floatval($data['goods_price']);
$goods_num = intval($data['goods_num']);
$front_total = floatval($data['total_price']);
// 后端真实计算总价(安全核心,以服务器为准)
$server_total = round($goods_price * $goods_num, 2);
// 校验前端是否篡改价格
if (abs($server_total - $front_total) > 0.01) {
return json([
'code' => 400,
'msg' => '价格校验失败,请勿篡改数据'
]);
}
// ======================
// 此处写业务:写入订单数据库、库存扣减等
// ======================
return json([
'code' => 200,
'msg' => '订单提交成功',
'total' => $server_total
]);
}
}
二、路由配置 route/app.php(可选,简化URL)
php
<?php
use think\facade\Route;
// 订单提交接口
Route::post('order/submit', 'Order/submit');
三、前端模板 view/order/index.html
功能:
- 输入数量实时自动计算总价
- 点击按钮fetch无刷新请求TP8接口
- 提交时禁用按钮防重复提交
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>TP8商品无刷新计算价格</title>
<style>
.box{margin:30px;line-height:2.5;font-size:16px;}
#msg{margin-top:10px;color:#f00;}
#submitBtn{padding:6px 18px;cursor:pointer;}
</style>
</head>
<body>
<div class="box">
<p>商品单价:<span id="price">128.00</span> 元</p>
<p>购买数量:
<input type="number" id="num" value="1" min="1" style="width:80px;padding:4px;">
</p>
<p>应付总价:<strong id="total">128.00</strong> 元</p>
<button id="submitBtn">立即下单</button>
<div id="msg"></div>
</div>
<script>
// 元素
const priceBox = document.getElementById('price');
const numInput = document.getElementById('num');
const totalBox = document.getElementById('total');
const submitBtn = document.getElementById('submitBtn');
const msgBox = document.getElementById('msg');
// 商品单价
const basePrice = parseFloat(priceBox.innerText);
// 实时计算总价
function calcTotal() {
let num = parseInt(numInput.value) || 1;
if(num < 1) num = 1;
let total = (basePrice * num).toFixed(2);
totalBox.innerText = total;
return total;
}
// 数量变化自动重算
numInput.addEventListener('input', calcTotal);
// 无刷新提交订单
submitBtn.addEventListener('click', async function(){
let num = parseInt(numInput.value);
let total = calcTotal();
msgBox.innerText = '提交中,请稍候...';
this.disabled = true;
try {
// 请求TP8后端接口
const res = await fetch('/order/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// TP8防CSRF令牌,必须携带,否则403报错
'X-CSRF-Token': "{:token()}"
},
body: JSON.stringify({
goods_price: basePrice,
goods_num: num,
total_price: total
})
});
const result = await res.json();
if(result.code === 200){
msgBox.style.color = 'green';
msgBox.innerText = result.msg + ',实付金额:' + result.total + '元';
} else {
msgBox.style.color = 'red';
msgBox.innerText = result.msg;
}
} catch (err) {
msgBox.style.color = 'red';
msgBox.innerText = '网络异常,请求失败';
}
// 恢复按钮
this.disabled = false;
});
// 页面加载初始化计算
calcTotal();
</script>
</body>
</html>

被折叠的 条评论
为什么被折叠?



