GM 页改版:加回满疲劳/赞助页,去掉背包与邮件页和重复标题
- 角色页新增「回满疲劳」,走 POST /dnf/gm/characters/{id}/fatigue/refill(需角色下线)
- 新增「赞助」页:支付宝 / 微信收款码占位,图放 gmweb/qr/ 下即显示
- 删除背包页与邮件页(发到邮箱按钮保留在发物品页)
- 去掉页头「GM 控制台」文字(窗口标题栏已画标题,重复),页头只留连接状态
- serve.py 补 jpg/jpeg/gif/webp/bmp/svg 的 MIME
- 控制台宽度 1040 -> 920
This commit is contained in:
1 parent
7f41f9797a
commit
06558415da
6 files changed
+89
-101
No files matched your search
@@ -15,7 +15,7 @@ minimize_game=0
|
|||||||
; (推荐,原面板不会残留、也不会再露面);0 = 把面板放回去(老行为,会看到旧 IE 页面)
|
; (推荐,原面板不会残留、也不会再露面);0 = 把面板放回去(老行为,会看到旧 IE 页面)
|
||||||
close_action=1
|
close_action=1
|
||||||
; 我们自己的窗口尺寸
|
; 我们自己的窗口尺寸
|
||||||
width=1040
|
width=920
|
||||||
height=720
|
height=720
|
||||||
; 目标页面。必须是本地 http,避免浏览器弹证书提示。
|
; 目标页面。必须是本地 http,避免浏览器弹证书提示。
|
||||||
url=http://127.0.0.1:18112/
|
url=http://127.0.0.1:18112/
|
||||||
|
|||||||
+31
-79
@@ -4,7 +4,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
charId: '', charName: '', chars: [], bagRows: [],
|
charId: '', charName: '', chars: [],
|
||||||
itemId: 0, itemName: '', picked: null, items: [],
|
itemId: 0, itemName: '', picked: null, items: [],
|
||||||
page: 1, pageSize: 20, total: 0
|
page: 1, pageSize: 20, total: 0
|
||||||
};
|
};
|
||||||
@@ -66,7 +66,6 @@
|
|||||||
for (var j = 0; j < panes.length; j++) {
|
for (var j = 0; j < panes.length; j++) {
|
||||||
panes[j].className = 'pane' + (panes[j].id === 'pane-' + name ? ' on' : '');
|
panes[j].className = 'pane' + (panes[j].id === 'pane-' + name ? ' on' : '');
|
||||||
}
|
}
|
||||||
if (name === 'bag') { loadBag(0); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindTabs() {
|
function bindTabs() {
|
||||||
@@ -156,6 +155,17 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 一键回满疲劳:直接把库里 fatigue 写回上限。
|
||||||
|
服务端要求角色已下线(在线时写库会被下一个存档覆盖),所以在线角色会报错。 */
|
||||||
|
function refillFatigue() {
|
||||||
|
if (!requireChar()) { return; }
|
||||||
|
api('/dnf/gm/characters/' + S.charId + '/fatigue/refill', 'POST', {}, function (d) {
|
||||||
|
if (!d) { return; }
|
||||||
|
log('已回满 ' + S.charName + ' 的疲劳值', 'ok');
|
||||||
|
loadChars();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- 物品 */
|
/* ---------------------------------------------------------------- 物品 */
|
||||||
|
|
||||||
/* 装备类型标签:PVF 里是 "[weapon]" 这种,去掉括号后映射成中文。 */
|
/* 装备类型标签:PVF 里是 "[weapon]" 这种,去掉括号后映射成中文。 */
|
||||||
@@ -384,10 +394,7 @@
|
|||||||
var data = d.data || {};
|
var data = d.data || {};
|
||||||
log('已发 ' + S.itemName + ' ×' + n + optText(o) +
|
log('已发 ' + S.itemName + ' ×' + n + optText(o) +
|
||||||
(data.slot != null ? ',格位 ' + data.list_type + ':' + data.slot : ''), 'ok');
|
(data.slot != null ? ',格位 ' + data.list_type + ':' + data.slot : ''), 'ok');
|
||||||
// 发完立刻切到背包页并高亮这一行:到底有没有进包,玩家要看得见
|
// 回游戏里看背包即可(客户端会自动刷新),这里不再切页打断操作
|
||||||
switchTab('bag');
|
|
||||||
loadBag(S.itemId);
|
|
||||||
setTimeout(function () { loadBag(S.itemId); }, 600);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,78 +413,22 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- 背包 */
|
/* ---------------------------------------------------------------- 赞助 */
|
||||||
|
|
||||||
/* 背包 / 穿戴的实时快照。GM 发完东西,玩家第一件事就是看它到底进没进包,
|
/* 收款码图片是可选的:放了就显示,没放(404)就把占位提示顶上来。 */
|
||||||
所以这里按服务端实际存的行来列,不做任何本地猜测。 */
|
function bindQr(id) {
|
||||||
function loadBag(highlightItemId) {
|
var img = $(id);
|
||||||
if (!S.charId) {
|
if (!img) { return; }
|
||||||
$('bag-list').innerHTML = '<div class="empty">先在「角色」页选一个角色</div>';
|
img.onerror = function () {
|
||||||
$('bag-info').innerHTML = '-';
|
var box = this.parentNode;
|
||||||
return;
|
if (box) { box.className = 'qr-box miss'; }
|
||||||
}
|
this.style.display = 'none';
|
||||||
api('/local/dnf-gm/inventory/list?character_id=' + encodeURIComponent(S.charId),
|
};
|
||||||
'GET', null, function (d) {
|
img.onload = function () {
|
||||||
if (!d) { return; }
|
var box = this.parentNode;
|
||||||
var data = d.data || {};
|
if (box) { box.className = 'qr-box'; }
|
||||||
S.bagRows = data.rows || [];
|
this.style.display = '';
|
||||||
renderBag(highlightItemId || 0);
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderBag(highlightItemId) {
|
|
||||||
var box = $('bag-list');
|
|
||||||
var rows = S.bagRows || [];
|
|
||||||
var bagCount = 0, wearCount = 0, i;
|
|
||||||
for (i = 0; i < rows.length; i++) {
|
|
||||||
if (rows[i].collection === 'wear') { wearCount++; } else { bagCount++; }
|
|
||||||
}
|
|
||||||
$('bag-info').innerHTML = '背包 ' + bagCount + ' 件 · 穿戴 ' + wearCount + ' 件';
|
|
||||||
|
|
||||||
if (!rows.length) {
|
|
||||||
box.innerHTML = '<div class="empty">背包是空的</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刚发进去的排在最前面,其余保持服务端顺序
|
|
||||||
var ordered = [];
|
|
||||||
if (highlightItemId) {
|
|
||||||
for (i = 0; i < rows.length; i++) {
|
|
||||||
if (rows[i].item_id === highlightItemId) { ordered.push(rows[i]); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (i = 0; i < rows.length; i++) {
|
|
||||||
if (!highlightItemId || rows[i].item_id !== highlightItemId) { ordered.push(rows[i]); }
|
|
||||||
}
|
|
||||||
|
|
||||||
var h = [];
|
|
||||||
for (i = 0; i < ordered.length; i++) {
|
|
||||||
var r = ordered[i];
|
|
||||||
var isNew = highlightItemId && r.item_id === highlightItemId;
|
|
||||||
h.push('<div class="li' + (isNew ? ' on' : '') + '">' +
|
|
||||||
'<span class="nm">' + esc(r.item_name || ('物品 ' + r.item_id)) +
|
|
||||||
(isNew ? ' <em class="new">新</em>' : '') + '</span>' +
|
|
||||||
'<span class="meta">' + (r.collection === 'wear' ? '穿戴' : '背包') +
|
|
||||||
' · ' + esc(r.entry_key) + ' · id ' + esc(r.item_id) +
|
|
||||||
(r.count > 1 ? ' ×' + esc(r.count) : '') + '</span></div>');
|
|
||||||
}
|
|
||||||
box.innerHTML = h.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- 邮件 */
|
|
||||||
|
|
||||||
function sendMail() {
|
|
||||||
if (!requireChar()) { return; }
|
|
||||||
var gold = parseInt($('in-goldmail').value, 10) || 0;
|
|
||||||
api('/local/dnf-gm/mail', 'POST', {
|
|
||||||
character_name: S.charName,
|
|
||||||
title: $('in-title').value || 'GM 邮件',
|
|
||||||
body: $('in-body').value || '',
|
|
||||||
gold: gold,
|
|
||||||
attachments: []
|
|
||||||
}, function (d) {
|
|
||||||
if (d) { log('邮件已发送给 ' + S.charName, 'ok'); }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- 启动 */
|
/* ---------------------------------------------------------------- 启动 */
|
||||||
@@ -515,9 +466,8 @@
|
|||||||
bind('btn-prev', function () { if (S.page > 1) { loadItems(S.page - 1); } });
|
bind('btn-prev', function () { if (S.page > 1) { loadItems(S.page - 1); } });
|
||||||
bind('btn-next', function () { loadItems(S.page + 1); });
|
bind('btn-next', function () { loadItems(S.page + 1); });
|
||||||
bind('btn-grant', grantItem);
|
bind('btn-grant', grantItem);
|
||||||
bind('btn-bag', function () { loadBag(0); });
|
|
||||||
bind('btn-mailitem', mailItem);
|
bind('btn-mailitem', mailItem);
|
||||||
bind('btn-mail', sendMail);
|
bind('btn-fatigue', refillFatigue);
|
||||||
|
|
||||||
$('in-page-size').onchange = function () { loadItems(1); };
|
$('in-page-size').onchange = function () { loadItems(1); };
|
||||||
$('in-group').onchange = function () { fillTypeOptions(); loadItems(1); };
|
$('in-group').onchange = function () { fillTypeOptions(); loadItems(1); };
|
||||||
@@ -549,6 +499,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
fillTypeOptions();
|
fillTypeOptions();
|
||||||
|
bindQr('qr-alipay');
|
||||||
|
bindQr('qr-wechat');
|
||||||
|
|
||||||
renderPicked();
|
renderPicked();
|
||||||
forceIME();
|
forceIME();
|
||||||
|
|||||||
+21
-17
@@ -8,7 +8,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="hd">
|
<div id="hd">
|
||||||
<div id="hd-t">GM 控制台</div>
|
|
||||||
<div id="hd-s"><span id="dot" class="dot off"></span><span id="stat">连接中…</span></div>
|
<div id="hd-s"><span id="dot" class="dot off"></span><span id="stat">连接中…</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -16,8 +15,7 @@
|
|||||||
<a class="tab on" data-tab="home" href="javascript:void(0)">状态</a>
|
<a class="tab on" data-tab="home" href="javascript:void(0)">状态</a>
|
||||||
<a class="tab" data-tab="char" href="javascript:void(0)">角色</a>
|
<a class="tab" data-tab="char" href="javascript:void(0)">角色</a>
|
||||||
<a class="tab" data-tab="item" href="javascript:void(0)">发物品</a>
|
<a class="tab" data-tab="item" href="javascript:void(0)">发物品</a>
|
||||||
<a class="tab" data-tab="bag" href="javascript:void(0)">背包</a>
|
<a class="tab" data-tab="donate" href="javascript:void(0)">赞助</a>
|
||||||
<a class="tab" data-tab="mail" href="javascript:void(0)">邮件</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="body">
|
<div id="body">
|
||||||
@@ -53,6 +51,11 @@
|
|||||||
<label class="lb">等级</label><input id="in-level" class="inp sm" value="">
|
<label class="lb">等级</label><input id="in-level" class="inp sm" value="">
|
||||||
<button id="btn-level" class="btn">写入</button>
|
<button id="btn-level" class="btn">写入</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sec">疲劳</div>
|
||||||
|
<div class="row">
|
||||||
|
<button id="btn-fatigue" class="btn pri">回满疲劳</button>
|
||||||
|
<span class="dim">角色需先在游戏里下线(回到角色选择)才能写库</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pane" id="pane-item">
|
<div class="pane" id="pane-item">
|
||||||
@@ -127,23 +130,24 @@
|
|||||||
<div class="hint">强化 0-31、增幅等级 0-31、锻造 0-8。增幅填了就直接覆盖强化(红字装不能同时白字强化),非装备行忽略这些值。</div>
|
<div class="hint">强化 0-31、增幅等级 0-31、锻造 0-8。增幅填了就直接覆盖强化(红字装不能同时白字强化),非装备行忽略这些值。</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pane" id="pane-bag">
|
<div class="pane" id="pane-donate">
|
||||||
<div class="row">
|
<div class="sec">赞助支持</div>
|
||||||
<button id="btn-bag" class="btn">刷新背包</button>
|
<div class="hint">服务器长期免费维护。如果玩得开心,可以扫码赞助一点,金额随意,感谢支持。</div>
|
||||||
<span id="bag-info" class="pg">-</span>
|
<div class="qr-wrap">
|
||||||
|
<div class="qr-card">
|
||||||
|
<div class="qr-title">支付宝</div>
|
||||||
|
<div class="qr-box"><img id="qr-alipay" src="/qr/alipay.png" alt="支付宝收款码">
|
||||||
|
<div class="qr-miss">二维码未配置<div class="qr-path">gmweb\qr\alipay.png</div></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="bag-list" class="list fixed"></div>
|
|
||||||
<div class="hint">发到背包的物品会自动跳到这一页并高亮(标「新」)。列表里没有,就说明没进背包:可能是格位满了,或者这个物品类型不能直接进背包(该走邮箱)。</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="qr-card">
|
||||||
<div class="pane" id="pane-mail">
|
<div class="qr-title">微信</div>
|
||||||
<div class="row"><label class="lb">标题</label><input id="in-title" class="inp" value="GM 邮件"></div>
|
<div class="qr-box"><img id="qr-wechat" src="/qr/wechat.png" alt="微信收款码">
|
||||||
<div class="row"><label class="lb">内容</label><input id="in-body" class="inp" value="来自 GM 的问候"></div>
|
<div class="qr-miss">二维码未配置<div class="qr-path">gmweb\qr\wechat.png</div></div>
|
||||||
<div class="row"><label class="lb">附金币</label><input id="in-goldmail" class="inp sm" value="0"></div>
|
|
||||||
<div class="row">
|
|
||||||
<button id="btn-mail" class="btn pri">发送到当前角色</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="hint">收件人取「角色」页选中的角色;未选中会提示。</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hint">把收款码图片按上面的文件名放进 gmweb\qr\ 目录,刷新这一页就会显示;换成 jpg 也可以,改一下上面的文件名后缀即可。</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
把收款码图片放到这个目录,文件名固定为:
|
||||||
|
|
||||||
|
alipay.png 支付宝收款码
|
||||||
|
wechat.png 微信收款码
|
||||||
|
|
||||||
|
说明:
|
||||||
|
- 建议正方形图片(页面按 194x194 显示,非正方形会被拉伸)
|
||||||
|
- 支持 png / jpg / jpeg / gif / bmp / webp / svg
|
||||||
|
- 用 jpg 的话,把 gmweb\index.html 里对应 <img src="/qr/alipay.png"> 的后缀改成 .jpg 即可
|
||||||
|
- 换图后刷新控制台页面(标题栏的刷新按钮)就能看到,不用重启服务
|
||||||
|
- 图片没放时,赞助页显示「二维码未配置」占位,不影响其他功能
|
||||||
@@ -32,6 +32,12 @@ MIME = {
|
|||||||
".js": "application/javascript; charset=utf-8",
|
".js": "application/javascript; charset=utf-8",
|
||||||
".css": "text/css; charset=utf-8",
|
".css": "text/css; charset=utf-8",
|
||||||
".png": "image/png",
|
".png": "image/png",
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".webp": "image/webp",
|
||||||
|
".bmp": "image/bmp",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
".ico": "image/x-icon",
|
".ico": "image/x-icon",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-4
@@ -6,11 +6,10 @@ html, body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#hd {
|
#hd {
|
||||||
height: 30px; padding: 0 10px;
|
height: 26px; padding: 0 10px;
|
||||||
background: #1d2029; border-bottom: 1px solid #2b3040;
|
background: #1d2029; border-bottom: 1px solid #2b3040;
|
||||||
line-height: 30px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
#hd-t { float: left; font-weight: bold; color: #e8c979; letter-spacing: 1px; }
|
|
||||||
#hd-s { float: right; color: #8d94a6; }
|
#hd-s { float: right; color: #8d94a6; }
|
||||||
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 4px; margin-right: 6px; }
|
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 4px; margin-right: 6px; }
|
||||||
.dot.on { background: #4ec46a; }
|
.dot.on { background: #4ec46a; }
|
||||||
@@ -24,7 +23,7 @@ html, body {
|
|||||||
.tab:hover { color: #d8dbe2; }
|
.tab:hover { color: #d8dbe2; }
|
||||||
.tab.on { color: #e8c979; border-bottom-color: #e8c979; background: #20242e; }
|
.tab.on { color: #e8c979; border-bottom-color: #e8c979; background: #20242e; }
|
||||||
|
|
||||||
#body { position: absolute; top: 59px; bottom: 62px; left: 0; right: 0; overflow: auto; }
|
#body { position: absolute; top: 55px; bottom: 62px; left: 0; right: 0; overflow: auto; }
|
||||||
.pane { display: none; padding: 10px; }
|
.pane { display: none; padding: 10px; }
|
||||||
.pane.on { display: block; }
|
.pane.on { display: block; }
|
||||||
|
|
||||||
@@ -111,3 +110,19 @@ select.inp { padding: 3px 2px; }
|
|||||||
font-style: normal; font-size: 11px; padding: 0 4px; margin-left: 4px;
|
font-style: normal; font-size: 11px; padding: 0 4px; margin-left: 4px;
|
||||||
background: #e8c979; color: #1a1e27; border-radius: 2px;
|
background: #e8c979; color: #1a1e27; border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 赞助页:收款码(IE11 不支持 flex,用 float 排两列) */
|
||||||
|
.qr-wrap { overflow: hidden; margin-top: 10px; }
|
||||||
|
.qr-card {
|
||||||
|
float: left; width: 216px; margin: 0 14px 10px 0;
|
||||||
|
background: #1a1e27; border: 1px solid #2b3040; padding: 10px;
|
||||||
|
}
|
||||||
|
.qr-title { color: #e8c979; letter-spacing: 1px; margin-bottom: 8px; }
|
||||||
|
.qr-box {
|
||||||
|
width: 194px; height: 194px; position: relative; overflow: hidden;
|
||||||
|
background: #0f1116; border: 1px dashed #3a4154; text-align: center;
|
||||||
|
}
|
||||||
|
.qr-box img { display: block; width: 194px; height: 194px; }
|
||||||
|
.qr-miss { display: none; padding-top: 72px; color: #666e80; line-height: 1.8; }
|
||||||
|
.qr-box.miss .qr-miss { display: block; }
|
||||||
|
.qr-path { color: #4c5365; font-size: 11px; margin-top: 2px; }
|
||||||
Reference in new issue
Block a user