第58批续24: PHP端信誉积分(credit_score)接收+管理界面
1. core.php: users表加 credit_score 列 INTEGER DEFAULT 100
2. api/sync.php: syncLogin INSERT加credit_score字段,bindValue默认100
3. api/credit.php(新): 信誉积分API
- ensureCreditTables建credit_records/credit_rules表+users加列
- 用户端: submit_good/submit_bad上报, my_score/user_score查积分, my_records查记录
- 管理员: pending_list/approve/reject/approved_list审批流程, rules/update_rule规则管理
- approve审批通过更新users.credit_score
4. player.php: 侧边栏加⭐信誉积分入口+renderCredit页面+creditSubmitFlow上报引导
This commit is contained in:
+240
@@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
// ★ 信誉积分API - 用户上报 / 管理员审批 / 查询积分
|
||||||
|
// ★ Version: 2026-10-28
|
||||||
|
ob_start();
|
||||||
|
if (function_exists('opcache_invalidate')) { @opcache_invalidate(__FILE__); }
|
||||||
|
while (ob_get_level() > 0) ob_end_clean();
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
$coreFile = __DIR__ . '/../core.php';
|
||||||
|
if (!file_exists($coreFile)) {
|
||||||
|
while (ob_get_level() > 0) ob_end_clean();
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'core.php not found'], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
set_error_handler(function($errno, $errstr, $errfile, $errline) {
|
||||||
|
@error_log("[credit.php PRE-LOAD] $errstr in $errfile on line $errline");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
require_once $coreFile;
|
||||||
|
|
||||||
|
$action = $_GET['action'] ?? '';
|
||||||
|
$db = getDB();
|
||||||
|
|
||||||
|
// ====== 初始化表结构 ======
|
||||||
|
function ensureCreditTables($db) {
|
||||||
|
$db->exec("CREATE TABLE IF NOT EXISTS credit_records (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
target_player TEXT NOT NULL,
|
||||||
|
reporter_player TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
content TEXT DEFAULT '',
|
||||||
|
status TEXT DEFAULT 'pending',
|
||||||
|
score_change INTEGER DEFAULT 0,
|
||||||
|
created_at INTEGER DEFAULT 0,
|
||||||
|
approved_at INTEGER DEFAULT 0,
|
||||||
|
approve_reason TEXT DEFAULT '',
|
||||||
|
source TEXT DEFAULT 'web'
|
||||||
|
)");
|
||||||
|
$db->exec("CREATE TABLE IF NOT EXISTS credit_rules (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
rule_name TEXT NOT NULL UNIQUE,
|
||||||
|
min_score INTEGER DEFAULT 0,
|
||||||
|
max_score INTEGER DEFAULT 0,
|
||||||
|
restriction_type TEXT DEFAULT '',
|
||||||
|
restriction_duration INTEGER DEFAULT 0,
|
||||||
|
enabled INTEGER DEFAULT 1,
|
||||||
|
description TEXT DEFAULT ''
|
||||||
|
)");
|
||||||
|
// users 表加 credit_score 列(如果不存在)
|
||||||
|
$ci = $db->query("PRAGMA table_info(users)");
|
||||||
|
$cols = [];
|
||||||
|
while ($c = $ci->fetchArray(SQLITE3_ASSOC)) $cols[] = $c['name'];
|
||||||
|
if (!in_array('credit_score', $cols)) {
|
||||||
|
$db->exec("ALTER TABLE users ADD COLUMN credit_score INTEGER DEFAULT 100");
|
||||||
|
}
|
||||||
|
// 默认规则
|
||||||
|
$db->exec("INSERT OR IGNORE INTO credit_rules (rule_name, min_score, max_score, restriction_type, restriction_duration, description) VALUES ('禁止交易', 60, 0, 'trade', 0, '低于60分时禁止与NPC交易')");
|
||||||
|
$db->exec("INSERT OR IGNORE INTO credit_rules (rule_name, min_score, max_score, restriction_type, restriction_duration, description) VALUES ('禁止PVP', 40, 0, 'pvp', 0, '低于40分时禁止进入PVP竞技场')");
|
||||||
|
$db->exec("INSERT OR IGNORE INTO credit_rules (rule_name, min_score, max_score, restriction_type, restriction_duration, description) VALUES ('禁止传送', 20, 0, 'teleport', 0, '低于20分时禁止使用传送功能')");
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureCreditTables($db);
|
||||||
|
|
||||||
|
/** 玩家端 token 校验,返回玩家名 */
|
||||||
|
function creditRequirePlayer() {
|
||||||
|
$token = getParam('token', '');
|
||||||
|
if (empty($token)) {
|
||||||
|
error('未登录', 401);
|
||||||
|
}
|
||||||
|
$info = validateTokenSilent($token);
|
||||||
|
if (!$info || empty($info['player'])) {
|
||||||
|
error('Token 无效或已过期', 401);
|
||||||
|
}
|
||||||
|
return $info['player'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 管理端 session 校验 */
|
||||||
|
function creditRequireAdmin() {
|
||||||
|
if (empty($_SESSION['admin_logged_in']) || !$_SESSION['admin_logged_in']) {
|
||||||
|
error('需要管理员权限', 401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
// ====== 用户端:上报好人好事 ======
|
||||||
|
case 'submit_good':
|
||||||
|
$player = creditRequirePlayer();
|
||||||
|
$target = getParam('target', '');
|
||||||
|
$content = getParam('content', '');
|
||||||
|
if (!$target) error('缺少目标玩家名');
|
||||||
|
$stmt = $db->prepare("INSERT INTO credit_records (target_player, reporter_player, type, content, created_at, source) VALUES (?, ?, 'good', ?, ?, 'web')");
|
||||||
|
$stmt->bindValue(1, $target, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(2, $player, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(3, $content, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(4, time(), SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
success(['id' => $db->lastInsertRowID()], '好人好事上报成功,等待管理员审批');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 用户端:举报违规 ======
|
||||||
|
case 'submit_bad':
|
||||||
|
$player = creditRequirePlayer();
|
||||||
|
$target = getParam('target', '');
|
||||||
|
$content = getParam('content', '');
|
||||||
|
if (!$target) error('缺少目标玩家名');
|
||||||
|
$stmt = $db->prepare("INSERT INTO credit_records (target_player, reporter_player, type, content, created_at, source) VALUES (?, ?, 'bad', ?, ?, 'web')");
|
||||||
|
$stmt->bindValue(1, $target, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(2, $player, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(3, $content, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(4, time(), SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
success(['id' => $db->lastInsertRowID()], '举报已提交,等待管理员审批');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 用户端:查询自己的积分 ======
|
||||||
|
case 'my_score':
|
||||||
|
$player = creditRequirePlayer();
|
||||||
|
$stmt = $db->prepare("SELECT credit_score FROM users WHERE player_name = ?");
|
||||||
|
$stmt->bindValue(1, $player, SQLITE3_TEXT);
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$score = 100;
|
||||||
|
if ($row = $res->fetchArray(SQLITE3_ASSOC)) $score = (int)$row['credit_score'];
|
||||||
|
success(['player' => $player, 'credit_score' => $score]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 用户端:查询他人积分 ======
|
||||||
|
case 'user_score':
|
||||||
|
$player = creditRequirePlayer();
|
||||||
|
$target = getParam('target', '');
|
||||||
|
if (!$target) error('缺少目标玩家名');
|
||||||
|
$stmt = $db->prepare("SELECT credit_score FROM users WHERE player_name = ?");
|
||||||
|
$stmt->bindValue(1, $target, SQLITE3_TEXT);
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$score = 100;
|
||||||
|
if ($row = $res->fetchArray(SQLITE3_ASSOC)) $score = (int)$row['credit_score'];
|
||||||
|
success(['player' => $target, 'credit_score' => $score]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 用户端:查看自己的积分记录 ======
|
||||||
|
case 'my_records':
|
||||||
|
$player = creditRequirePlayer();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM credit_records WHERE target_player = ? ORDER BY created_at DESC LIMIT 50");
|
||||||
|
$stmt->bindValue(1, $player, SQLITE3_TEXT);
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$records = [];
|
||||||
|
while ($row = $res->fetchArray(SQLITE3_ASSOC)) $records[] = $row;
|
||||||
|
success(['records' => $records]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:查看待审批列表 ======
|
||||||
|
case 'pending_list':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM credit_records WHERE status = 'pending' ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$records = [];
|
||||||
|
while ($row = $res->fetchArray(SQLITE3_ASSOC)) $records[] = $row;
|
||||||
|
success(['records' => $records]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:审批通过 ======
|
||||||
|
case 'approve':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$recordId = (int)getParam('id', 0);
|
||||||
|
$amount = (int)getParam('amount', 10);
|
||||||
|
$reason = getParam('reason', '信誉积分审批通过');
|
||||||
|
if ($recordId <= 0) error('缺少记录ID');
|
||||||
|
// 查目标玩家
|
||||||
|
$stmt = $db->prepare("SELECT target_player FROM credit_records WHERE id = ?");
|
||||||
|
$stmt->bindValue(1, $recordId, SQLITE3_INTEGER);
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$target = '';
|
||||||
|
if ($row = $res->fetchArray(SQLITE3_ASSOC)) $target = $row['target_player'];
|
||||||
|
if (!$target) error('记录不存在');
|
||||||
|
// 更新记录
|
||||||
|
$stmt = $db->prepare("UPDATE credit_records SET status = 'approved', score_change = ?, approved_at = ?, approve_reason = ? WHERE id = ?");
|
||||||
|
$stmt->bindValue(1, $amount, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(2, time(), SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(3, $reason, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(4, $recordId, SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
// 更新积分
|
||||||
|
$stmt = $db->prepare("UPDATE users SET credit_score = credit_score + ? WHERE player_name = ?");
|
||||||
|
$stmt->bindValue(1, $amount, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(2, $target, SQLITE3_TEXT);
|
||||||
|
$stmt->execute();
|
||||||
|
success(['target' => $target, 'amount' => $amount], '审批通过,积分已更新');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:审批驳回 ======
|
||||||
|
case 'reject':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$recordId = (int)getParam('id', 0);
|
||||||
|
$reason = getParam('reason', '管理员驳回');
|
||||||
|
if ($recordId <= 0) error('缺少记录ID');
|
||||||
|
$stmt = $db->prepare("UPDATE credit_records SET status = 'rejected', approved_at = ?, approve_reason = ? WHERE id = ?");
|
||||||
|
$stmt->bindValue(1, time(), SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(2, $reason, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(3, $recordId, SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
success(null, '已驳回,不扣分不加分');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:查看审批记录 ======
|
||||||
|
case 'approved_list':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM credit_records ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$records = [];
|
||||||
|
while ($row = $res->fetchArray(SQLITE3_ASSOC)) $records[] = $row;
|
||||||
|
success(['records' => $records]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:查看所有规则 ======
|
||||||
|
case 'rules':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM credit_rules ORDER BY min_score ASC");
|
||||||
|
$res = $stmt->execute();
|
||||||
|
$rules = [];
|
||||||
|
while ($row = $res->fetchArray(SQLITE3_ASSOC)) $rules[] = $row;
|
||||||
|
success(['rules' => $rules]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ====== 管理员:更新规则 ======
|
||||||
|
case 'update_rule':
|
||||||
|
creditRequireAdmin();
|
||||||
|
$ruleId = (int)getParam('id', 0);
|
||||||
|
$enabled = (int)getParam('enabled', 1);
|
||||||
|
if ($ruleId <= 0) error('缺少规则ID');
|
||||||
|
$stmt = $db->prepare("UPDATE credit_rules SET enabled = ? WHERE id = ?");
|
||||||
|
$stmt->bindValue(1, $enabled, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(2, $ruleId, SQLITE3_INTEGER);
|
||||||
|
$stmt->execute();
|
||||||
|
success(null, '规则已更新');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error('未知操作: ' . $action);
|
||||||
|
}
|
||||||
+2
-1
@@ -1848,7 +1848,7 @@ function syncLogin() {
|
|||||||
if ($registerTime > 1000000000000) { $registerTime = intval($registerTime / 1000); }
|
if ($registerTime > 1000000000000) { $registerTime = intval($registerTime / 1000); }
|
||||||
if ($lastLoginTime > 1000000000000) { $lastLoginTime = intval($lastLoginTime / 1000); }
|
if ($lastLoginTime > 1000000000000) { $lastLoginTime = intval($lastLoginTime / 1000); }
|
||||||
|
|
||||||
$stmt = $db->prepare("INSERT OR REPLACE INTO users (player_name, register_time, last_login_time, email, points, gift_stage, total_online_time) VALUES (:name, :rt, :lt, :email, :pts, :gs, :tot)");
|
$stmt = $db->prepare("INSERT OR REPLACE INTO users (player_name, register_time, last_login_time, email, points, gift_stage, total_online_time, credit_score) VALUES (:name, :rt, :lt, :email, :pts, :gs, :tot, :cs)");
|
||||||
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
|
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
|
||||||
$stmt->bindValue(':rt', $registerTime, SQLITE3_INTEGER);
|
$stmt->bindValue(':rt', $registerTime, SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':lt', $lastLoginTime, SQLITE3_INTEGER);
|
$stmt->bindValue(':lt', $lastLoginTime, SQLITE3_INTEGER);
|
||||||
@@ -1856,6 +1856,7 @@ function syncLogin() {
|
|||||||
$stmt->bindValue(':pts', (int)($user['points'] ?? 0), SQLITE3_INTEGER);
|
$stmt->bindValue(':pts', (int)($user['points'] ?? 0), SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':gs', (int)($user['gift_stage'] ?? 0), SQLITE3_INTEGER);
|
$stmt->bindValue(':gs', (int)($user['gift_stage'] ?? 0), SQLITE3_INTEGER);
|
||||||
$stmt->bindValue(':tot', (int)($user['total_online_time'] ?? 0), SQLITE3_INTEGER);
|
$stmt->bindValue(':tot', (int)($user['total_online_time'] ?? 0), SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(':cs', (int)($user['credit_score'] ?? 100), SQLITE3_INTEGER);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$count++;
|
$count++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1920,6 +1920,7 @@ function initTables($db, $side = null) {
|
|||||||
'last_login_ip' => "TEXT DEFAULT ''",
|
'last_login_ip' => "TEXT DEFAULT ''",
|
||||||
'frozen' => "INTEGER DEFAULT 0",
|
'frozen' => "INTEGER DEFAULT 0",
|
||||||
'freeze_token' => "TEXT DEFAULT ''",
|
'freeze_token' => "TEXT DEFAULT ''",
|
||||||
|
'credit_score' => "INTEGER DEFAULT 100",
|
||||||
];
|
];
|
||||||
foreach ($addCols as $col => $type) {
|
foreach ($addCols as $col => $type) {
|
||||||
if (!in_array($col, $userCols)) {
|
if (!in_array($col, $userCols)) {
|
||||||
|
|||||||
+89
@@ -380,6 +380,7 @@ if ($currentVersion !== $BUILD_VERSION) {
|
|||||||
<div class="sidebar-item" data-page="ticket" onclick="switchPage('ticket')">📋 工单系统</div>
|
<div class="sidebar-item" data-page="ticket" onclick="switchPage('ticket')">📋 工单系统</div>
|
||||||
<div class="sidebar-item" data-page="sn" onclick="switchPage('sn')">🔢 SN设备管理</div>
|
<div class="sidebar-item" data-page="sn" onclick="switchPage('sn')">🔢 SN设备管理</div>
|
||||||
<div class="sidebar-item" data-page="account" onclick="switchPage('account')">🛡️ 账号安全</div>
|
<div class="sidebar-item" data-page="account" onclick="switchPage('account')">🛡️ 账号安全</div>
|
||||||
|
<div class="sidebar-item" data-page="credit" onclick="switchPage('credit')">⭐ 信誉积分</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content" id="content">
|
<div class="content" id="content">
|
||||||
@@ -1269,6 +1270,7 @@ if ($currentVersion !== $BUILD_VERSION) {
|
|||||||
else if (page === 'account') renderAccount(c);
|
else if (page === 'account') renderAccount(c);
|
||||||
else if (page === 'ticket') renderTicket(c);
|
else if (page === 'ticket') renderTicket(c);
|
||||||
else if (page === 'sn') renderSn(c);
|
else if (page === 'sn') renderSn(c);
|
||||||
|
else if (page === 'credit') renderCredit(c);
|
||||||
else if (page === 'recharge') renderRecharge(c);
|
else if (page === 'recharge') renderRecharge(c);
|
||||||
else if (page === 'orders') renderMyOrders(c);
|
else if (page === 'orders') renderMyOrders(c);
|
||||||
}
|
}
|
||||||
@@ -5463,6 +5465,93 @@ async function clearAllMemberPerms(landName, targetPlayer) {
|
|||||||
p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds());
|
p(d.getHours()) + ':' + p(d.getMinutes()) + ':' + p(d.getSeconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 信誉积分 =====
|
||||||
|
function creditTypeBadge(t) {
|
||||||
|
const m = { good: ['好人好事', 'var(--green)'], bad: ['举报', 'var(--red)'] };
|
||||||
|
const v = m[t] || [t, 'var(--dim)'];
|
||||||
|
return '<span style="color:' + v[1] + '">' + v[0] + '</span>';
|
||||||
|
}
|
||||||
|
function creditStatusBadge(s) {
|
||||||
|
const m = {
|
||||||
|
pending: ['待审批', 'var(--yellow)'],
|
||||||
|
approved: ['已通过', 'var(--green)'],
|
||||||
|
rejected: ['已驳回', 'var(--red)']
|
||||||
|
};
|
||||||
|
const v = m[s] || [s, 'var(--dim)'];
|
||||||
|
return '<span style="color:' + v[1] + '">' + v[0] + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderCredit(el) {
|
||||||
|
el.innerHTML = '<div class="card"><h2>⭐ 信誉积分</h2><p style="color:var(--dim)">加载中...</p></div>';
|
||||||
|
try {
|
||||||
|
const [scoreRes, recRes] = await Promise.all([
|
||||||
|
api('credit.php', { action: 'my_score' }),
|
||||||
|
api('credit.php', { action: 'my_records' })
|
||||||
|
]);
|
||||||
|
const score = (scoreRes.success && scoreRes.data) ? scoreRes.data.credit_score : 100;
|
||||||
|
const records = (recRes.success && recRes.data) ? (recRes.data.records || []) : [];
|
||||||
|
|
||||||
|
let html = '<div class="card">' +
|
||||||
|
'<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:14px">' +
|
||||||
|
'<h2 style="margin:0">⭐ 我的信誉积分</h2>' +
|
||||||
|
'<div style="font-size:28px;font-weight:bold;color:' + (score >= 60 ? 'var(--green)' : score >= 40 ? 'var(--yellow)' : 'var(--red)') + '">' + score + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// 上报按钮区
|
||||||
|
html += '<div style="display:flex;gap:8px;margin-bottom:16px;flex-wrap:wrap">' +
|
||||||
|
'<button class="btn btn-green" onclick="creditSubmitFlow(\'good\')">🌱 上报好人好事</button>' +
|
||||||
|
'<button class="btn btn-red" onclick="creditSubmitFlow(\'bad\')">🚩 举报违规违纪</button>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// 积分规则
|
||||||
|
html += '<div style="background:rgba(88,166,255,0.05);border:1px solid var(--border);border-radius:8px;padding:10px 12px;margin-bottom:14px;font-size:13px;color:var(--dim);line-height:1.7">' +
|
||||||
|
'初始积分 100 · 低于 60 禁止交易 · 低于 40 禁止PVP · 低于 20 禁止传送' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// 记录列表
|
||||||
|
html += '<h3 style="margin:0 0 8px;font-size:14px">📋 积分变动记录 (' + records.length + ')</h3>';
|
||||||
|
if (!records.length) {
|
||||||
|
html += '<p style="color:var(--dim);font-size:13px">暂无记录</p>';
|
||||||
|
} else {
|
||||||
|
html += '<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-size:13px">' +
|
||||||
|
'<tr style="color:var(--dim);text-align:left">' +
|
||||||
|
'<th style="padding:6px">类型</th><th style="padding:6px">上报人</th><th style="padding:6px">状态</th>' +
|
||||||
|
'<th style="padding:6px">变动</th><th style="padding:6px">内容</th><th style="padding:6px">来源</th></tr>';
|
||||||
|
for (const r of records) {
|
||||||
|
html += '<tr style="border-top:1px solid var(--border)">' +
|
||||||
|
'<td style="padding:6px">' + creditTypeBadge(r.type) + '</td>' +
|
||||||
|
'<td style="padding:6px">' + escHtml(r.reporter_player) + '</td>' +
|
||||||
|
'<td style="padding:6px">' + creditStatusBadge(r.status) + '</td>' +
|
||||||
|
'<td style="padding:6px;font-weight:bold;color:' + (r.score_change > 0 ? 'var(--green)' : r.score_change < 0 ? 'var(--red)' : 'var(--dim)') + '">' +
|
||||||
|
(r.score_change > 0 ? '+' : '') + r.score_change + '</td>' +
|
||||||
|
'<td style="padding:6px;color:var(--dim);word-break:break-all">' + escHtml(r.content || '') + '</td>' +
|
||||||
|
'<td style="padding:6px;color:var(--dim)">' + escHtml(r.source || '') + '</td></tr>';
|
||||||
|
}
|
||||||
|
html += '</table></div>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
el.innerHTML = html;
|
||||||
|
} catch (e) {
|
||||||
|
el.innerHTML = '<div class="card"><p style="color:var(--red)">加载失败: ' + escHtml(e.message) + '</p></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function creditSubmitFlow(type) {
|
||||||
|
const target = await glassPrompt('请输入目标玩家名');
|
||||||
|
if (!target) return;
|
||||||
|
const content = await glassPrompt('请输入上报理由');
|
||||||
|
if (!content) return;
|
||||||
|
try {
|
||||||
|
const action = type === 'good' ? 'submit_good' : 'submit_bad';
|
||||||
|
const r = await api('credit.php', { action: action, target: target, content: content });
|
||||||
|
if (!r.success) { toast(r.message || '上报失败', 'error'); return; }
|
||||||
|
toast(r.message || '上报成功');
|
||||||
|
renderCredit(document.getElementById('content'));
|
||||||
|
} catch (e) {
|
||||||
|
toast('上报失败: ' + e.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function escHtml(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
function escHtml(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||||||
|
|
||||||
// ===== 第十六批:封禁常态化公示卡片(独立卡片,每15秒一个窗口期,从下往上滚动替换) =====
|
// ===== 第十六批:封禁常态化公示卡片(独立卡片,每15秒一个窗口期,从下往上滚动替换) =====
|
||||||
|
|||||||
Reference in New Issue
Block a user