feat: 前端 UI 精修 + 后端视图调整 + 资源预置

前端:
- world.html 游戏内聊天组件重构:表情选择器改固定方格+自适应列+去滚动条;
  输入框清空占位文字+隐藏滚动条;聊天面板整体高度缩短约 1/3;
  顶部用户区改为纯文字并点开个人信息弹窗(头像/头像框/性别/说明/称号/注册)
- chat.html 聊天输入框压扁(46→34px)
- codex.html 新增神话品质、天赋/技能书/书籍分类与预置数据
- rules.html 规则文档更新

后端(Odoo):
- game_base 附件改为看板视图(图片/名称/备注/大小)
- yt_world 玩家菜单直接指向 hr.employee
- game_base 员工表单隐藏原生联系人/组织架构页签
- chat 模型/接口/视图与规则数据扩充

资源:生成 4x4 资源精灵表(树木/石材/芦苇/宝箱)
This commit is contained in:
李鹏宇
2026-08-14 14:27:15 +08:00
parent d713221581
commit 73d764938d
16 changed files with 1713 additions and 388 deletions
+12 -4
View File
@@ -6,9 +6,10 @@
let channels = [];
let activeChannel = null; // {id, name}
let lastId = 0; // 已渲染的最大消息 id(增量轮询基准)
let renderedIds = new Set(); // 已渲染消息 id,避免即时渲染与轮询重复
let pollTimer = null;
const TYPE_ICON = { world: '🌐', guild: '🛡️', pm: '💬' };
const TYPE_ICON = { world: '🌐', guild: '🛡️', pm: '💬', guide: '🤖' };
// ---------- 工具 ----------
function fmtTime(s) {
@@ -54,9 +55,12 @@
}
function renderMessage(m, prepend) {
if (renderedIds.has(m.id)) return; // 去重:即时渲染与轮询可能重复拉到同一消息
renderedIds.add(m.id);
const isGuide = !!m.is_guide;
const row = document.createElement('div');
row.className = 'row' + (m.is_self ? ' self' : '');
const initial = (m.author_name || '?').slice(0, 1);
row.className = 'row' + (m.is_self ? ' self' : '') + (isGuide ? ' guide' : '');
const initial = isGuide ? '🤖' : (m.author_name || '?').slice(0, 1);
row.innerHTML = `
<div class="ava">${escapeHtml(initial)}</div>
<div class="bubble-wrap">
@@ -99,8 +103,12 @@
async function selectChannel(ch) {
activeChannel = ch;
lastId = 0;
renderedIds.clear();
$('messages').innerHTML = '';
$('chTitle').textContent = ch.name;
$('chSub').textContent = ch.type === 'guide'
? '向宇森向导提问,了解属性 / 等级 / 货币 / 制度'
: '和大家聊聊宇森世界吧';
renderChannels();
setStatus('加载中…');
const data = await api(`/game/api/chat/messages?channel_id=${ch.id}&limit=50`);
@@ -138,8 +146,8 @@
btn.disabled = false;
if (data.ok) {
ta.value = '';
// 本地立即渲染(轮询也会拉到,靠 id 去重:renderMessage 只更新 lastId,不重复插入)
renderMessage(data.message, false);
if (data.guide_reply) renderMessage(data.guide_reply, false);
scrollBottom();
} else {
setStatus(data.error || '发送失败');