This commit is contained in:
qsc
2026-09-04 22:38:16 +08:00
parent 3845950862
commit 9c9808adf7
30 changed files with 658 additions and 510 deletions
+14
View File
@@ -1,5 +1,19 @@
## 更新记录
### version: 3.4.22026-09-04):
图片模板整体调整为浅色背景、深色高对比文本和更大的字号,缩小内容与金色边框之间的留白并扩大主体区域;同步检查并调整多个独立页面的字号、间距及表格布局,提高长图阅读体验。
插件配置页新增“图片渲染质量”分组,可选择 `1.0``1.3``1.8` 三档渲染清晰度以及 JPEG/PNG 输出格式,并可设置 JPEG 质量;默认使用 `1.3` 倍设备像素比和 JPEG 质量 `100`
将 17 个独立排行榜指令合并为 `帮会排行``阵营排行``其他排行` 三个两轮会话入口;首轮按分类展示固定序号菜单,第二轮复用排行榜图片流程输出所选榜单,并统一只保留接口数据的前 50 条。
修复两轮会话已经取得有效选择后等待计时器仍继续运行,导致耗时查询或图片渲染期间触发超时分支并重复输出第二张图片的问题。
修复 aiocqhttp 已成功发送图片但返回 `retcode=1200` 超时回执时,插件继续补发“猪脑过载”错误提示的问题;该回执现在只记录警告。
优化插件 WebUI 的别名管理:区服页展示全部标准区服并移除删除操作,心法页隐藏不可修改的配装 ID;标准区服名和标准心法名均保持只读,点击“编辑”后直接在对应别名单元格内修改并保存。
### version: 3.4.12026-09-04):
带区服参数的查询新增 `全区` 保留值;即使当前会话已绑定区服,也会向接口传入空区服以查询全区数据。
+25 -34
View File
@@ -15,7 +15,8 @@
- 中文触发词覆盖活动、名剑、排行、交易、阵营、角色、奇遇、副本、家园、社区等场景。
- 支持纯文本、远程图片、HTML/Jinja2 渲染图片和图文消息链。
- 支持 `宏``资历` 两类 10 秒两轮交互,超时默认选择第一项。
- 支持 `宏``资历` 以及帮会、阵营、其他排行榜的 10 秒两轮交互,超时默认选择第一项。
- 查询图片统一使用浅色高对比主题,并可在插件配置页调整渲染清晰度、输出格式和 JPEG 质量。
- 支持本地 SQLite 避雷记录的增删改查。
- 支持按 AstrBot 会话分别开启总开关和具体 JX3API 实时事件订阅。
- 复用 `aiohttp.ClientSession`,统一处理 GET、POST、JSON、图片和分页请求。
@@ -78,12 +79,15 @@ pip install -r data/plugins/astrbot_plugin_jx3/requirements.txt
| 配置项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `prefix.enable` | `bool` | `true` | 是否启用指令前缀检查 |
| `prefix.enable` | `bool` | `false` | 是否启用指令前缀检查 |
| `prefix.text` | `string` | `剑三` | 指令前缀内容 |
| `jx3api_token` | `string` | 空 | JX3API Token |
| `jx3api_ticket` | `string` | 空 | 部分名剑和心法接口需要的推栏 Ticket |
| `jx3api_wss` | `string` | `wss://socket.nicemoe.cn` | JX3API 事件通道地址 |
| `jx3api_wss_token` | `string` | 空 | 事件版令牌;免费事件无需填写 |
| `image_render_quality.device_scale_factor` | `string` | `1.3` | 渲染清晰度,可选 `1.0``1.3``1.8` |
| `image_render_quality.format` | `string` | `jpeg` | 图片输出格式,可选 `jpeg``png` |
| `image_render_quality.jpeg_quality` | `int` | `100` | JPEG 图片质量,范围 `1-100`PNG 下不生效 |
会话区服绑定、区服别名和事件订阅均保存在本地 SQLite。修改 WebSocket 地址或事件版令牌后需要重新加载插件。
@@ -100,7 +104,7 @@ JX3API Token 可从 [JX3API](https://www.jx3api.com/) 获取。推栏 Ticket 通
## 使用方式
默认启用前缀,因此推荐发送:
如已启用指令前缀,可发送:
```text
剑三 功能
@@ -108,7 +112,7 @@ JX3API Token 可从 [JX3API](https://www.jx3api.com/) 获取。推栏 Ticket 通
剑三 战绩 梦江南 角色名 33
```
前缀与指令之间的空格可以省略,例如 `剑三日常` 也能触发。关闭前缀检查可直接发送 `日常`
前缀与指令之间的空格可以省略,例如 `剑三日常` 也能触发。默认关闭前缀检查可直接发送 `日常`;启用后才要求使用配置的前缀
参数规则:
@@ -161,29 +165,12 @@ JX3API Token 可从 [JX3API](https://www.jx3api.com/) 获取。推栏 Ticket 通
| `战绩 服务器 角色 [模式]` | 角色名剑战绩,模式默认 `33`;图片 | Token + Ticket |
| `名剑排行 [模式] [数量]` | 名剑大会排行,默认 `33`、50 条;图片 | Token + Ticket |
| `名剑统计 [模式]` | 名剑门派统计,模式默认 `33`;图片 | Token + Ticket |
| `帮会排行 服务器` | 回复序号选择神兵宝甲或爱心帮会榜单,最多保留前 50 条;图片 | Token |
| `阵营排行 服务器` | 回复序号选择赛季、上周或本周阵营榜单,最多保留前 50 条;图片 | Token |
| `其他排行 服务器` | 回复序号选择名士、老江湖、名师等其他榜单,最多保留前 50 条;图片 | Token |
| `试炼排行 服务器 心法` | 试炼之地排行;图片 | Token |
以下排行榜指令均使用 `指令 服务器` 格式,输出图片并需要 Token
```text
名士五十强
老江湖五十强
兵甲藏家五十强
名师五十强
阵营英雄五十强
薪火相传五十强
庐园广记一百强
浩气神兵宝甲五十强
恶人神兵宝甲五十强
浩气爱心帮会五十强
恶人爱心帮会五十强
赛季恶人五十强
赛季浩气五十强
上周恶人五十强
上周浩气五十强
本周恶人五十强
本周浩气五十强
```
三个聚合排行榜指令都会先发送可选榜单菜单;回复对应序号后才查询并渲染结果。已绑定区服的会话可以省略服务器参数。
### 物价与交易
@@ -304,7 +291,7 @@ JX3API Token 可从 [JX3API](https://www.jx3api.com/) 获取。推栏 Ticket 通
### WebUI 插件管理
AstrBot 插件详情页中的“剑网三插件管理”页面提供四个页签:会话绑定、事件订阅、区服别名和心法别名。页面可以新增、修改或解除任意会话绑定,查看所有会话的事件总开关及订阅编号,为每个标准区服配置多个别名,并维护心法的 JX3BOX 配装 ID标准名称和最多 5 个别名。管理页通过 AstrBot Plugin Pages 桥接调用插件 Web API,不直接访问 Dashboard 凭据。
AstrBot 插件详情页中的“剑网三插件管理”页面提供四个页签:会话绑定、事件订阅、区服别名和心法别名。页面可以新增、修改或解除任意会话绑定,查看所有会话的事件总开关及订阅编号。区服别名页展示完整标准区服目录,标准名称只读,只能行内编辑别名;心法别名页隐藏 JX3BOX 配装 ID标准心法名称只读,同样只允许行内编辑最多 5 个别名。管理页通过 AstrBot Plugin Pages 桥接调用插件 Web API,不直接访问 Dashboard 凭据。
## 业务流程
@@ -400,13 +387,13 @@ flowchart LR
`core/message.py` 根据业务结果选择输出方式:
- `plain_msg()`:纯文本。
- `T2I_image_msg()`:向模板注入业务数据和本地图标,再调用 AstrBot HTML 渲染器生成 JPEG
- `T2I_image_msg()`:向模板注入业务数据和本地图标,再调用 AstrBot HTML 渲染器按插件配置生成图片
- `image_msg()`:直接发送远程图片 URL 或图片数据。
- `plain_chain()`:发送文本与图片组成的富媒体消息链;结果包含 HTML 正文时,会将其渲染为图片组件追加到消息链。
- `handler_plain_image_msg()`:通用两轮会话;首轮函数直接返回 `{"1": "总览", "2": "杂闻"}` 形式的数字键值对(无 `code``msg``data` 外层),处理器据此生成序号文本;选择后裁剪为只含所选项的新字典交给次轮函数,再由注入的输出器发送结果;超时固定选择第一项。
- `zili()`:在指令入口直接调用上述通用两轮会话处理器,并通过标准图片流程输出结果。
- `hong()``zili()` 及三类聚合排行榜入口:保持统一调用结构接入上述两轮会话处理器,并按各自需要选择图文消息链或标准图片流程输出结果。
图片默认使用质量 `100`、完整页面截图和普通设备缩放级别。模板可通过 `icons.img``icons.sect``icons.serendipity` 访问通用、门派/心法和奇遇图标。
图片默认使用 JPEG 质量 `100`、完整页面截图和 `1.3` 倍设备像素比。可以在插件配置页的“图片渲染质量”分组切换 JPEG/PNG、调整 JPEG 质量,并选择 `1.0``1.3``1.8` 设置渲染清晰度。模板可通过 `icons.img``icons.sect``icons.serendipity` 访问通用、门派/心法和奇遇图标。
AstrBot 的渲染接口接收完整 HTML 字符串,因此插件不会依赖渲染端读取本地 CSS 文件。`core/template.py` 会异步读取并缓存公共布局、设计变量、基础样式和组件样式,再按需读取页面专属样式及页面片段,组装完成后把单个完整字符串交给渲染器。共享资源只在首次请求时读取一次;标准页面没有同名 CSS 文件也可以正常组合。
@@ -525,12 +512,16 @@ git diff --check
## 当前版本状态
以下内容是对 v3.4.1 当前源码的静态核对结果,部署和二次开发前应注意:
以下内容是对 v3.4.2 当前源码的静态核对结果,部署和二次开发前应注意:
1. 带区服参数的指令在入口层统一进行会话绑定补齐和别名解析;新增此类指令时参数名应继续使用 `server`
2. 资历分布使用固定的“数字序号 → 大类名称”首轮菜单,通用会话裁剪出单项字典后交给 JX3API `/tuilan/achievement` 次轮查询;模板兼容总览与指定 `subclass` 的不同 `data.total` 层级,并直接使用接口角色字段、`pieces``seniority``score/totalScore` 渲染,JX3BOX 不再参与资历查询
3. `APIClient` 当前默认 `ssl_verify=False`,即外部 HTTPS 请求不校验证书。对传输安全有要求的部署应先评估并调整该设置
4. JX3API 服务初始化时会把 Token 和 Ticket 写入 debug 日志。不要公开调试日志,建议二次开发时移除敏感值输出
1. 查询图片使用浅色高对比主题和放大的内容区域;渲染清晰度、JPEG/PNG 格式及 JPEG 质量由 `image_render_quality` 配置组控制,提高清晰度或使用 PNG 会增加图片体积与渲染耗时
2. `帮会排行``阵营排行``其他排行` 使用固定的“数字序号 → 榜单名称”首轮菜单,次轮查询统一截取接口返回的前 50 条;取得有效选择后会先停止超时计时器,避免渲染期间重复输出
3. 区服别名 WebUI 展示完整标准区服目录并只允许行内编辑别名;心法别名 WebUI 隐藏配装 ID,后端保存接口也只更新已有心法的别名
4. aiocqhttp 图片发送返回 `retcode=1200` 时只记录警告,不再追加“猪脑过载”提示;该回执代表发送结果不确定,仍建议结合平台日志确认实际送达情况
5. 带区服参数的指令在入口层统一进行会话绑定补齐和别名解析;新增此类指令时参数名应继续使用 `server`
6. 资历分布使用固定的“数字序号 → 大类名称”首轮菜单,通用会话裁剪出单项字典后交给 JX3API `/tuilan/achievement` 次轮查询;模板兼容总览与指定 `subclass` 的不同 `data.total` 层级,并直接使用接口角色字段、`pieces``seniority``score/totalScore` 渲染,JX3BOX 不再参与资历查询。
7. `APIClient` 当前默认 `ssl_verify=False`,即外部 HTTPS 请求不校验证书。对传输安全有要求的部署应先评估并调整该设置。
8. JX3API 服务初始化时会把 Token 和 Ticket 写入 debug 日志。不要公开调试日志,建议二次开发时移除敏感值输出。
## 注意事项
+38
View File
@@ -40,5 +40,43 @@
"type": "string",
"default": "",
"hint": "用于付费事件推送,不填写只推送免费事件。"
},
"image_render_quality": {
"description": "图片渲染质量",
"type": "object",
"items": {
"device_scale_factor": {
"description": "渲染清晰度",
"type": "string",
"options": [
"1.0",
"1.3",
"1.8"
],
"default": "1.3",
"hint": "数值越高越清晰,同时图片更大、渲染耗时更多。"
},
"format": {
"description": "输出格式",
"type": "string",
"options": [
"jpeg",
"png"
],
"default": "jpeg",
"hint": "JPEG 文件通常更小;PNG 为无损格式但文件更大。透明背景的特殊页面仍会自动使用 PNG。"
},
"jpeg_quality": {
"description": "JPEG 质量",
"type": "int",
"default": 100,
"slider": {
"min": 1,
"max": 100,
"step": 1
},
"hint": "仅输出格式为 JPEG 时生效;数值越高细节越完整,文件也越大。"
}
}
}
}
+88 -26
View File
@@ -15,7 +15,62 @@ from .sqlite import AsyncSQLiteDB
from .fun_basic import load_template,gold_to_parts,week_to_num,compare_date_str,format_time,format_remaining
ROLE_RANK_NAMES = {
"名士五十强",
"老江湖五十强",
"兵甲藏家五十强",
"名师五十强",
"阵营英雄五十强",
"薪火相传五十强",
"庐园广记一百强",
}
TONG_RANK_NAMES0 = {
"赛季恶人五十强",
"赛季浩气五十强",
"本周恶人五十强",
"本周浩气五十强",
}
TONG_RANK_NAMES1 = {
"浩气神兵宝甲五十强",
"恶人神兵宝甲五十强",
"浩气爱心帮会五十强",
"恶人爱心帮会五十强",
}
TONG_RANK_NAMES2 = {
"上周恶人五十强",
"上周浩气五十强",
}
GUILD_RANK_OPTIONS = {
"1": "浩气神兵宝甲五十强",
"2": "恶人神兵宝甲五十强",
"3": "浩气爱心帮会五十强",
"4": "恶人爱心帮会五十强",
}
CAMP_RANK_OPTIONS = {
"1": "赛季恶人五十强",
"2": "赛季浩气五十强",
"3": "上周恶人五十强",
"4": "上周浩气五十强",
"5": "本周恶人五十强",
"6": "本周浩气五十强",
}
OTHER_RANK_OPTIONS = {
"1": "名士五十强",
"2": "老江湖五十强",
"3": "兵甲藏家五十强",
"4": "名师五十强",
"5": "阵营英雄五十强",
"6": "薪火相传五十强",
"7": "庐园广记一百强",
}
RANK_NAMES = frozenset().union(
ROLE_RANK_NAMES,
TONG_RANK_NAMES0,
TONG_RANK_NAMES1,
TONG_RANK_NAMES2,
)
class JX3APIService:
@@ -456,33 +511,36 @@ class JX3APIService:
)
async def banghui_rank_menu(self) -> Dict[str, str]:
"""帮会排行榜选择内容。"""
return GUILD_RANK_OPTIONS.copy()
async def zhenying_rank_menu(self) -> Dict[str, str]:
"""阵营排行榜选择内容。"""
return CAMP_RANK_OPTIONS.copy()
async def qita_rank_menu(self) -> Dict[str, str]:
"""其他排行榜选择内容。"""
return OTHER_RANK_OPTIONS.copy()
async def rank_statistical_select(self, server: str, selected: Dict[str, Any],) -> Dict[str, Any]:
"""排行榜次轮:根据单项选择数据查询对应榜单。"""
if not isinstance(selected, dict) or len(selected) != 1:
return_data = self._init_return_data()
return_data["msg"] = "排行榜选项数据格式异常"
return return_data
rank_name = str(next(iter(selected.values())) or "").strip()
if rank_name not in RANK_NAMES:
return_data = self._init_return_data()
return_data["msg"] = "无效排行榜选项"
return return_data
return await self.rank_statistical(rank_name, server)
async def rank_statistical(self, name: str, server: str) -> Dict[str, Any]:
"""排行榜单"""
ROLE_RANK_NAMES = {
"名士五十强",
"老江湖五十强",
"兵甲藏家五十强",
"名师五十强",
"阵营英雄五十强",
"薪火相传五十强",
"庐园广记一百强",
}
TONG_RANK_NAMES0 = {
"赛季恶人五十强",
"赛季浩气五十强",
"本周恶人五十强",
"本周浩气五十强",
}
TONG_RANK_NAMES1 = {
"浩气神兵宝甲五十强",
"恶人神兵宝甲五十强",
"浩气爱心帮会五十强",
"恶人爱心帮会五十强",
}
TONG_RANK_NAMES2 = {
"上周恶人五十强",
"上周浩气五十强",
}
if name in ROLE_RANK_NAMES:
template_name = "rank_role.html"
elif name in TONG_RANK_NAMES0:
@@ -494,7 +552,11 @@ class JX3APIService:
# 数据处理
async def processor(data: Any, return_data: Dict[str, Any]) -> None:
items = data.get("data", [])
items = data.get("data") or []
if isinstance(items, list):
items = items[:50]
else:
items = []
return_data["data"] = {
"items": items,
+15 -17
View File
@@ -73,17 +73,21 @@ class KungfuAliasService:
for row in rows
]
async def save(self, pzid: Any, name: Any, aliases: Iterable[Any]):
async def save_aliases(self, pzid: Any, aliases: Iterable[Any]):
"""只更新已有心法的别名,不允许修改 ID 和标准名称。"""
normalized_pzid = self._parse_pzid(pzid)
normalized_name = self._clean(name)
if not normalized_name:
raise ValueError("标准心法名不能为空")
if len(normalized_name) > 64:
raise ValueError("标准心法名过长")
rows = await self.list_kungfu()
current = next(
(row for row in rows if row["pzid"] == normalized_pzid),
None,
)
if current is None:
raise ValueError("心法不存在")
normalized_name = current["name"]
normalized_aliases = self._normalize_aliases(normalized_name, aliases)
occupied: dict[str, str] = {}
for row in await self.list_kungfu():
for row in rows:
if row["pzid"] == normalized_pzid:
continue
for value in [row["name"], *row["aliases"]]:
@@ -100,17 +104,11 @@ class KungfuAliasService:
]
await self.sql.execute(
"""
INSERT INTO kungfu (pzid, name, name1, name2, name3, name4, name5)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(pzid) DO UPDATE SET
name=excluded.name,
name1=excluded.name1,
name2=excluded.name2,
name3=excluded.name3,
name4=excluded.name4,
name5=excluded.name5
UPDATE kungfu
SET name1=?, name2=?, name3=?, name4=?, name5=?
WHERE pzid=?
""",
(normalized_pzid, normalized_name, *values),
(*values, normalized_pzid),
)
def _normalize_aliases(self, name: str, aliases: Iterable[Any]) -> list[str]:
+91 -81
View File
@@ -1,5 +1,6 @@
from typing import Any
from aiocqhttp.exceptions import ActionFailed
from astrbot.core import html_renderer
from astrbot.api import logger
from astrbot.api.event import AstrMessageEvent, MessageChain
@@ -16,18 +17,67 @@ from .bilei_data import BiLeidata
class MessageBuilder:
"""回复消息构建"""
_RENDER_FORMATS = {"jpeg", "png"}
_DEVICE_SCALE_FACTOR_LEVELS = {
1.0: "normal",
1.3: "high",
1.8: "ultra",
}
def __init__(self,
jx3api: JX3APIService,
jx3box: JX3BOXService,
bilei: BiLeidata,
event_push: EventPushService,
icons: dict[str, dict[str, str]]
icons: dict[str, dict[str, str]],
render_config: dict[str, Any] | None = None,
):
self.jx3api = jx3api
self.jx3box = jx3box
self.bilei = bilei
self.event_push = event_push
self.icons = icons
self.render_config = render_config if isinstance(render_config, dict) else {}
def _build_render_options(
self,
overrides: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""根据插件配置构造 AstrBot HTML 截图参数。"""
image_format = str(self.render_config.get("format", "jpeg")).lower()
if image_format not in self._RENDER_FORMATS:
image_format = "jpeg"
try:
device_scale_factor = float(
self.render_config.get("device_scale_factor", 1.3)
)
except (TypeError, ValueError):
device_scale_factor = 1.3
scale_factor_level = self._DEVICE_SCALE_FACTOR_LEVELS.get(
device_scale_factor,
"high",
)
try:
jpeg_quality = int(self.render_config.get("jpeg_quality", 100))
except (TypeError, ValueError):
jpeg_quality = 100
jpeg_quality = max(1, min(100, jpeg_quality))
options: dict[str, Any] = {
"quality": jpeg_quality,
"device_scale_factor_level": scale_factor_level,
"scale": "device",
"full_page": True,
"omit_background": False,
"type": image_format,
}
options.update(overrides or {})
if options.get("type") == "png":
options.pop("quality", None)
return options
async def html_render(
@@ -42,7 +92,7 @@ class MessageBuilder:
tmpl,
data,
return_url=return_url,
options=options,
options=self._build_render_options(options),
)
@@ -69,22 +119,25 @@ class MessageBuilder:
data = await action()
try:
if data["code"] == 200:
options = {
"quality": 100,
"device_scale_factor_level": "normal",
"full_page": True,
"omit_background": False,
"type": "jpeg"
}
options.update(render_options or {})
if options.get("type") == "png":
options.pop("quality", None)
data["data"]["icons"] = self.icons
url = await self.html_render(data["temp"], data["data"], options=options)
url = await self.html_render(
data["temp"],
data["data"],
options=render_options,
)
await event.send(event.image_result(url))
else:
await event.send(event.plain_result(data["msg"]))
except ActionFailed as e:
if e.retcode == 1200:
logger.warning(
"图片消息发送回执超时,消息可能已经成功送达,"
"不再发送错误提示。"
)
return
logger.error(f"功能函数执行错误: {e}")
await event.send(event.plain_result("猪脑过载,请稍后再试"))
except Exception as e:
logger.error(f"功能函数执行错误: {e}")
await event.send(event.plain_result("猪脑过载,请稍后再试"))
@@ -131,7 +184,7 @@ class MessageBuilder:
if data.get("data"):
chain.message(str(data["data"]))
if data.get("temp"):
url = await self.html_render(data["temp"], {}, options={})
url = await self.html_render(data["temp"], {})
chain.url_image(url)
await event.send(chain)
except Exception as e:
@@ -191,6 +244,9 @@ class MessageBuilder:
controller.stop()
return
# 已取得有效选择,先结束等待计时,再执行可能超过 timeout 的
# API 请求与图片渲染,避免超时分支重复发送默认第一项。
controller.stop()
try:
await send_selected(
new_event,
@@ -200,8 +256,6 @@ class MessageBuilder:
logger.error(f"二轮查询执行错误: {e}")
await new_event.send(MessageChain().message("猪脑过载,请稍后再试"))
controller.stop()
try:
await option_select_waiter(event)
except TimeoutError:
@@ -291,73 +345,29 @@ class MessageBuilder:
""" 名剑统计 模式"""
return await self.T2I_image_msg(event, lambda: self.jx3api.mingjiantongji(mode))
async def mingshiwushiqiang(self, event: AstrMessageEvent, server: str):
""" 名士五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("名士五十强", server))
async def banghuipaihang(self, event: AstrMessageEvent, server: str):
"""帮会排行 服务器"""
return await self.handler_plain_image_msg(
event,
lambda: self.jx3api.banghui_rank_menu(),
lambda selected: self.jx3api.rank_statistical_select(server,selected,),
)
async def laojianghuwushiqiang(self, event: AstrMessageEvent, server: str):
""" 老江湖五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("老江湖五十强", server))
async def zhenyingpaihang(self, event: AstrMessageEvent, server: str):
"""阵营排行 服务器"""
return await self.handler_plain_image_msg(
event,
lambda: self.jx3api.zhenying_rank_menu(),
lambda selected: self.jx3api.rank_statistical_select(server,selected,),
)
async def bingjiacangjiawushiqiang(self, event: AstrMessageEvent, server: str):
""" 兵甲藏家五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("兵甲藏家五十强", server))
async def mingshiwushiqiang_mentor(self, event: AstrMessageEvent, server: str):
""" 名师五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("名师五十强", server))
async def zhengyingyingxiongwushiqiang(self, event: AstrMessageEvent, server: str):
""" 阵营英雄五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("阵营英雄五十强", server))
async def xinhuoxiangchuanwushiqiang(self, event: AstrMessageEvent, server: str):
""" 薪火相传五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("薪火相传五十强", server))
async def luyuanguangjiyibaiqiang(self, event: AstrMessageEvent, server: str):
""" 庐园广记一百强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("庐园广记一百强", server))
async def haoqishenbingbaojiawushiqiang(self, event: AstrMessageEvent, server: str):
""" 浩气神兵宝甲五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("浩气神兵宝甲五十强", server))
async def erenshenbingbaojiawushiqiang(self, event: AstrMessageEvent, server: str ):
""" 恶人神兵宝甲五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("恶人神兵宝甲五十强", server))
async def haoqiaixinbanghuiwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 浩气爱心帮会五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("浩气爱心帮会五十强", server))
async def erenaixinbanghuiwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 恶人爱心帮会五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("恶人爱心帮会五十强", server))
async def saijierenwushiqiang(self, event: AstrMessageEvent, server: str):
""" 赛季恶人五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("赛季恶人五十强", server))
async def saijihaoqiwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 赛季浩气五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("赛季浩气五十强", server))
async def shangzhouerenwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 上周恶人五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("上周恶人五十强", server))
async def shangzhouhaoqiwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 上周浩气五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("上周浩气五十强", server))
async def benzhouerenwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 本周恶人五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("本周恶人五十强", server))
async def benzhouhaoqiwushiqiang(self, event: AstrMessageEvent, server: str ):
""" 本周浩气五十强 服务器"""
return await self.T2I_image_msg(event, lambda: self.jx3api.rank_statistical("本周浩气五十强", server))
async def qitapaihang(self, event: AstrMessageEvent, server: str):
"""其他排行 服务器"""
return await self.handler_plain_image_msg(
event,
lambda: self.jx3api.qita_rank_menu(),
lambda selected: self.jx3api.rank_statistical_select(server,selected,),
)
async def shilianpaixing(self, event: AstrMessageEvent, server: str , name: str):
""" 试炼排行 服务器 心法 """
+11 -1
View File
@@ -3,5 +3,15 @@
{"server": "唯我独尊", "aliases": ["唯满侠"]},
{"server": "乾坤一掷", "aliases": ["华乾"]},
{"server": "剑胆琴心", "aliases": ["煎蛋"]},
{"server": "斗转星移", "aliases": ["姨妈"]}
{"server": "斗转星移", "aliases": ["姨妈"]},
{"server": "蝶恋花", "aliases": []},
{"server": "飞龙在天", "aliases": []},
{"server": "绝代天骄", "aliases": []},
{"server": "龙争虎斗", "aliases": []},
{"server": "眉间雪", "aliases": []},
{"server": "破阵子", "aliases": []},
{"server": "山海相逢", "aliases": []},
{"server": "天鹅坪", "aliases": []},
{"server": "幽月轮", "aliases": []},
{"server": "长安城", "aliases": []}
]
+9 -25
View File
@@ -26,7 +26,7 @@ PLUGIN_NAME = "astrbot_plugin_jx3"
@register("astrbot_plugin_jx3",
"fxdyz",
"聚合剑网三游戏数据,提供查询、图片渲染、本地避雷和实时事件推送。",
"3.4.1",
"3.4.2",
"https://github.com/qsc20001102/astrbot_plugin_jx3"
)
class Jx3ApiPlugin(Star):
@@ -177,6 +177,7 @@ class Jx3ApiPlugin(Star):
self.bilei,
self.event_push,
self.icons,
self.conf.get("image_render_quality", {}),
)
@@ -244,24 +245,11 @@ class Jx3ApiPlugin(Star):
"战绩": self. jx3cmd.zhanji,
"名剑排行": self. jx3cmd.mingjianpaihang,
"名剑统计": self. jx3cmd.mingjiantongji,
"名士五十强": self. jx3cmd.mingshiwushiqiang,
"老江湖五十强": self. jx3cmd.laojianghuwushiqiang,
"兵甲藏家五十强": self. jx3cmd.bingjiacangjiawushiqiang,
"名师五十强": self. jx3cmd.mingshiwushiqiang_mentor,
"阵营英雄五十强": self. jx3cmd.zhengyingyingxiongwushiqiang,
"薪火相传五十强": self. jx3cmd.xinhuoxiangchuanwushiqiang,
"庐园广记一百强": self. jx3cmd.luyuanguangjiyibaiqiang,
"浩气神兵宝甲五十强": self. jx3cmd.haoqishenbingbaojiawushiqiang,
"恶人神兵宝甲五十强": self. jx3cmd.erenshenbingbaojiawushiqiang,
"浩气爱心帮会五十强": self. jx3cmd.haoqiaixinbanghuiwushiqiang,
"恶人爱心帮会五十强": self. jx3cmd.erenaixinbanghuiwushiqiang,
"赛季恶人五十强": self. jx3cmd.saijierenwushiqiang,
"赛季浩气五十强": self. jx3cmd.saijihaoqiwushiqiang,
"上周恶人五十强": self. jx3cmd.shangzhouerenwushiqiang,
"上周浩气五十强": self. jx3cmd.shangzhouhaoqiwushiqiang,
"本周恶人五十强": self. jx3cmd.benzhouerenwushiqiang,
"本周浩气五十强": self. jx3cmd.benzhouhaoqiwushiqiang,
"帮会排行": self. jx3cmd.banghuipaihang,
"阵营排行": self. jx3cmd.zhenyingpaihang,
"其他排行": self. jx3cmd.qitapaihang,
"试炼排行": self. jx3cmd.shilianpaixing,
"资历": self. jx3cmd.zili,
"阵营拍卖": self. jx3cmd.zhengyingpaimai,
"的卢": self. jx3cmd.dilujilu,
"金价": self. jx3cmd.jinjia,
@@ -321,16 +309,16 @@ class Jx3ApiPlugin(Star):
"开服": self. jx3cmd.kaifu,
"技改": self. jx3cmd.jigai,
"解密": self. jx3cmd.jiemi,
"副本": self. jx3cmd.fubeng,
"掉落": self. jx3cmd.diaoluo,
"": self. jx3cmd.hong,
"资历": self. jx3cmd.zili,
"交易行": self. jx3cmd.jiaoyihang,
"绑定区服": self.bind_server,
"解绑区服": self.unbind_server,
"事件推送": self.jx3cmd.shijian_tuisong,
"避雷添加": self.jx3cmd.bilei_add,
"避雷查看": self.jx3cmd.bilei_all,
"避雷查询": self.jx3cmd.bilei_select,
@@ -634,9 +622,8 @@ class Jx3ApiPlugin(Star):
else:
return error_response("别名必须是字符串或数组", status_code=400)
try:
await self.kungfu_alias.save(
await self.kungfu_alias.save_aliases(
payload.get("pzid"),
payload.get("name"),
aliases,
)
except ValueError as exc:
@@ -653,6 +640,3 @@ class Jx3ApiPlugin(Star):
+1 -1
View File
@@ -1,7 +1,7 @@
name: astrbot_plugin_jx3
display_name: 剑网三游戏数据查询工具
desc: 聚合剑网三游戏数据,提供查询、图片渲染、本地避雷和实时事件推送。
version: 3.4.1
version: 3.4.2
author: 飞翔大野猪
repo: https://github.com/qsc20001102/astrbot_plugin_jx3
astrbot_version: ">=4.24.1"
+119 -46
View File
@@ -1,5 +1,6 @@
const bridge = window.AstrBotPluginPage;
const state = { bindings: [], subscriptions: [], aliases: [], kungfu: [], servers: [], events: {} };
const editing = { aliasServer: null, kungfuPzid: null };
let toastTimer;
const byId = (id) => document.getElementById(id);
@@ -32,6 +33,42 @@ function emptyRow(columnCount, message) {
return row;
}
function aliasText(aliases) {
return aliases.length ? aliases.join("、") : "无";
}
function inlineAliasEditor(aliases, label, onSave, onCancel) {
const input = document.createElement("input");
input.className = "inline-editor";
input.value = aliases.join(", ");
input.placeholder = "多个别名用逗号分隔";
input.setAttribute("aria-label", label);
const saveButton = button("保存", "", async () => {
saveButton.disabled = true;
cancelButton.disabled = true;
const saved = await onSave(input.value);
if (!saved) {
saveButton.disabled = false;
cancelButton.disabled = false;
input.focus();
}
});
const cancelButton = button("取消", "", onCancel);
input.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault();
saveButton.click();
} else if (event.key === "Escape") {
event.preventDefault();
cancelButton.click();
}
});
queueMicrotask(() => input.focus());
return { input, controls: [saveButton, cancelButton] };
}
function bindingMap() {
return new Map(state.bindings.map((item) => [item.session_id, item.server]));
}
@@ -128,11 +165,22 @@ function renderSubscriptions() {
function renderAliases() {
const body = byId("aliases-body");
if (!state.aliases.length) {
body.replaceChildren(emptyRow(3, "暂无区服别名配置"));
const aliasesByServer = new Map(
state.aliases.map((item) => [item.server, item.aliases]),
);
const servers = [...new Set([
...state.servers,
...aliasesByServer.keys(),
])].sort((left, right) => left.localeCompare(right, "zh-CN"));
if (!servers.length) {
body.replaceChildren(emptyRow(3, "暂无标准区服数据"));
return;
}
body.replaceChildren(...state.aliases.map((item) => {
body.replaceChildren(...servers.map((serverName) => {
const item = {
server: serverName,
aliases: aliasesByServer.get(serverName) || [],
};
const row = document.createElement("tr");
const server = document.createElement("td");
const aliases = document.createElement("td");
@@ -141,19 +189,40 @@ function renderAliases() {
aliases.dataset.label = "别名";
actions.dataset.label = "操作";
server.textContent = item.server;
aliases.textContent = item.aliases.length ? item.aliases.join("、") : "无";
aliases.className = "alias-cell";
actions.className = "actions";
actions.append(
button("编辑", "", () => {
byId("alias-server").value = item.server;
byId("alias-values").value = item.aliases.join(", ");
byId("alias-values").focus();
}),
button("删除", "link-button--danger", async () => {
if (!window.confirm(`确认删除 ${item.server} 的全部别名?`)) return;
await mutate("aliases/delete", { server: item.server }, "别名已删除");
}),
);
if (editing.aliasServer === item.server) {
row.classList.add("is-editing");
const editor = inlineAliasEditor(
item.aliases,
`${item.server}的区服别名`,
async (value) => {
editing.aliasServer = null;
const saved = await mutate(
"aliases/save",
{ server: item.server, aliases: value },
"区服别名已保存",
);
if (!saved) {
editing.aliasServer = item.server;
renderAliases();
}
return saved;
},
() => {
editing.aliasServer = null;
renderAliases();
},
);
aliases.append(editor.input);
actions.append(...editor.controls);
} else {
aliases.textContent = aliasText(item.aliases);
actions.append(button("编辑", "", () => {
editing.aliasServer = item.server;
renderAliases();
}));
}
row.append(server, aliases, actions);
return row;
}));
@@ -162,30 +231,53 @@ function renderAliases() {
function renderKungfu() {
const body = byId("kungfu-body");
if (!state.kungfu.length) {
body.replaceChildren(emptyRow(4, "暂无心法配置"));
body.replaceChildren(emptyRow(3, "暂无心法配置"));
return;
}
body.replaceChildren(...state.kungfu.map((item) => {
const row = document.createElement("tr");
const pzid = document.createElement("td");
const name = document.createElement("td");
const aliases = document.createElement("td");
const actions = document.createElement("td");
pzid.dataset.label = "心法 ID";
name.dataset.label = "标准心法名";
aliases.dataset.label = "别名";
actions.dataset.label = "操作";
pzid.textContent = String(item.pzid);
name.textContent = item.name;
aliases.textContent = item.aliases.length ? item.aliases.join("、") : "无";
aliases.className = "alias-cell";
actions.className = "actions";
actions.append(button("编辑", "", () => {
byId("kungfu-pzid").value = String(item.pzid);
byId("kungfu-name").value = item.name;
byId("kungfu-aliases").value = item.aliases.join(", ");
byId("kungfu-aliases").focus();
}));
row.append(pzid, name, aliases, actions);
if (editing.kungfuPzid === item.pzid) {
row.classList.add("is-editing");
const editor = inlineAliasEditor(
item.aliases,
`${item.name}的心法别名`,
async (value) => {
editing.kungfuPzid = null;
const saved = await mutate(
"kungfu/save",
{ pzid: item.pzid, aliases: value },
"心法别名已保存",
);
if (!saved) {
editing.kungfuPzid = item.pzid;
renderKungfu();
}
return saved;
},
() => {
editing.kungfuPzid = null;
renderKungfu();
},
);
aliases.append(editor.input);
actions.append(...editor.controls);
} else {
aliases.textContent = aliasText(item.aliases);
actions.append(button("编辑", "", () => {
editing.kungfuPzid = item.pzid;
renderKungfu();
}));
}
row.append(name, aliases, actions);
return row;
}));
}
@@ -241,25 +333,6 @@ byId("binding-form").addEventListener("submit", async (event) => {
if (saved) event.currentTarget.reset();
});
byId("alias-form").addEventListener("submit", async (event) => {
event.preventDefault();
const saved = await mutate("aliases/save", {
server: byId("alias-server").value,
aliases: byId("alias-values").value,
}, "区服别名已保存");
if (saved) event.currentTarget.reset();
});
byId("kungfu-form").addEventListener("submit", async (event) => {
event.preventDefault();
const saved = await mutate("kungfu/save", {
pzid: byId("kungfu-pzid").value,
name: byId("kungfu-name").value,
aliases: byId("kungfu-aliases").value,
}, "心法配置已保存");
if (saved) event.currentTarget.reset();
});
byId("refresh").addEventListener("click", async (event) => {
const control = event.currentTarget;
control.disabled = true;
+7 -27
View File
@@ -76,17 +76,9 @@
</section>
<section class="panel" id="aliases-panel" role="tabpanel" hidden>
<form class="editor editor--aliases" id="alias-form">
<label>
<span>标准区服名</span>
<input id="alias-server" name="server" maxlength="64" list="server-options" placeholder="例如:梦江南" required />
</label>
<label>
<span>别名(多个别名用逗号分隔)</span>
<input id="alias-values" name="aliases" placeholder="例如:梦江、梦江南服、MJ" />
</label>
<button class="button button--primary" type="submit">保存别名</button>
</form>
<div class="section-intro">
<div><h2>区服别名</h2><p>标准区服名称为只读;点击对应行的“编辑”可修改别名。</p></div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>标准区服名</th><th>别名</th><th class="actions">操作</th></tr></thead>
@@ -96,24 +88,12 @@
</section>
<section class="panel" id="kungfu-panel" role="tabpanel" hidden>
<form class="editor editor--kungfu" id="kungfu-form">
<label>
<span>心法 IDJX3BOX 配装 ID</span>
<input id="kungfu-pzid" name="pzid" type="number" min="1" step="1" placeholder="例如:10014" required />
</label>
<label>
<span>标准心法名</span>
<input id="kungfu-name" name="name" maxlength="64" placeholder="例如:紫霞功" required />
</label>
<label>
<span>别名(最多 5 个,用逗号分隔)</span>
<input id="kungfu-aliases" name="aliases" placeholder="例如:气纯、紫霞" />
</label>
<button class="button button--primary" type="submit">保存心法</button>
</form>
<div class="section-intro">
<div><h2>心法别名</h2><p>标准心法名称为只读;点击对应行的“编辑”可修改最多 5 个别名。</p></div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>心法 ID</th><th>标准心法名</th><th>别名</th><th class="actions">操作</th></tr></thead>
<thead><tr><th>标准心法名</th><th>别名</th><th class="actions">操作</th></tr></thead>
<tbody id="kungfu-body"></tbody>
</table>
</div>
+6 -3
View File
@@ -59,8 +59,6 @@ h1 { margin: 0; font-size: clamp(26px, 3vw, 36px); line-height: 1.2; letter-spac
.panel { margin-top: 18px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); overflow: hidden; }
.panel[hidden] { display: none; }
.editor { display: grid; grid-template-columns: minmax(260px, 1.35fr) minmax(210px, 1fr) auto; align-items: end; gap: 16px; padding: 22px; background: var(--surface-subtle); border-bottom: 1px solid var(--border); }
.editor--aliases { grid-template-columns: minmax(220px, .8fr) minmax(320px, 1.5fr) auto; }
.editor--kungfu { grid-template-columns: minmax(210px, .65fr) minmax(210px, .8fr) minmax(300px, 1.35fr) auto; }
label { display: grid; gap: 7px; color: var(--text); font-size: 13px; font-weight: 650; }
input { width: 100%; height: 42px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); transition: border 160ms ease, box-shadow 160ms ease; }
input::placeholder { color: color-mix(in srgb, var(--muted) 76%, transparent); }
@@ -73,10 +71,14 @@ th, td { padding: 15px 20px; text-align: center; vertical-align: middle; border-
th { background: var(--surface-subtle); color: var(--muted); font-size: 13px; font-weight: 700; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transparent); }
tbody tr.is-editing { background: color-mix(in srgb, var(--accent-soft) 62%, transparent); }
.alias-cell { min-width: 320px; }
.inline-editor { min-width: 260px; }
.actions { white-space: nowrap; }
.link-button { padding: 4px 7px; border: 0; border-radius: 5px; background: transparent; color: #3568ae; cursor: pointer; font-weight: 650; }
[data-theme="dark"] .link-button { color: #8db9f4; }
.link-button:hover { background: color-mix(in srgb, currentColor 10%, transparent); }
.link-button:disabled { cursor: wait; opacity: .55; }
.link-button--danger { color: var(--danger); }
.state { display: inline-flex; align-items: center; gap: 7px; font-weight: 650; }
.state::before { width: 8px; height: 8px; border-radius: 50%; background: currentColor; content: ""; }
@@ -97,7 +99,7 @@ tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transpar
.summary__item + .summary__item { border-top: 1px solid var(--border); border-left: 0; }
.tabs { gap: 16px; overflow-x: auto; }
.tab { flex: 0 0 auto; }
.editor, .editor--aliases, .editor--kungfu { grid-template-columns: 1fr; }
.editor { grid-template-columns: 1fr; }
.table-wrap { overflow: visible; }
table, tbody, tr, td { display: block; width: 100%; }
thead { display: none; }
@@ -106,6 +108,7 @@ tbody tr:hover { background: color-mix(in srgb, var(--accent-soft) 42%, transpar
td { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 12px; padding: 8px 16px; border: 0; overflow-wrap: anywhere; }
td::before { color: var(--muted); content: attr(data-label); font-size: 12px; font-weight: 700; }
td.actions { grid-template-columns: max-content auto auto; align-items: center; width: 100%; white-space: normal; }
.alias-cell, .inline-editor { min-width: 0; }
td.empty { display: block; padding: 36px 16px; }
td.empty::before { content: none; }
}
+5 -19
View File
@@ -3,7 +3,7 @@
<header class="top-info help-header">
<div>
<div class="top-name">剑网三综合查询插件</div>
<div class="top-summary">共 17 个功能分类 · 69 项功能 · 105 条指令</div>
<div class="top-summary">共 17 个功能分类 · 69 项功能 · 93 个触发词</div>
</div>
<div class="top-desc">
指令与参数之间使用空格分隔<br>
@@ -38,25 +38,11 @@
<div class="section-title">排行榜单</div>
<div class="section-body">
<div class="feature ranking-feature">
<div class="feature-name">排行统计</div>
<div class="feature-name">排行统计(二轮选择)</div>
<div class="ranking-list">
<span class="command">名士五十强 服务器</span>
<span class="command">老江湖五十强 服务器</span>
<span class="command">兵甲藏家五十强 服务器</span>
<span class="command">名师五十强 服务器</span>
<span class="command">阵营英雄五十强 服务器</span>
<span class="command">薪火相传五十强 服务器</span>
<span class="command">庐园广记一百强 服务器</span>
<span class="command">浩气神兵宝甲五十强 服务器</span>
<span class="command">恶人神兵宝甲五十强 服务器</span>
<span class="command">浩气爱心帮会五十强 服务器</span>
<span class="command">恶人爱心帮会五十强 服务器</span>
<span class="command">赛季恶人五十强 服务器</span>
<span class="command">赛季浩气五十强 服务器</span>
<span class="command">上周恶人五十强 服务器</span>
<span class="command">上周浩气五十强 服务器</span>
<span class="command">本周恶人五十强 服务器</span>
<span class="command">本周浩气五十强 服务器</span>
<span class="command">帮会排行 服务器</span>
<span class="command">阵营排行 服务器</span>
<span class="command">其他排行 服务器</span>
</div>
</div>
<div class="feature trial-feature"><div class="feature-name">试炼排行</div><div class="commands"><span class="command">试炼排行 服务器 心法</span></div></div>
+16 -18
View File
@@ -11,20 +11,18 @@ body {
}
body.jx3-template {
min-width: calc(var(--jx3-page-width) + 94px);
padding: 20px;
min-width: calc(var(--jx3-page-width) + 64px);
padding: 12px;
color: var(--jx3-text);
font-family: var(--jx3-font);
font-size: 16px;
line-height: 1.6;
font-size: var(--jx3-font-size);
line-height: 1.65;
background-color: var(--jx3-bg);
background-image:
radial-gradient(circle at 50% -10%, rgba(67, 111, 150, 0.34), transparent 38%),
linear-gradient(180deg, rgba(4, 17, 29, 0.18), rgba(4, 17, 29, 0.72)),
url("{{ icons.img.get('bg', '') }}");
background-repeat: no-repeat, no-repeat, repeat;
background-size: 100% 560px, 100% 100%, 540px 316px;
background-blend-mode: screen, normal, multiply;
radial-gradient(circle at 50% -10%, rgba(190, 145, 63, 0.18), transparent 40%),
linear-gradient(180deg, #f8fafb 0, var(--jx3-bg) 520px);
background-repeat: no-repeat, no-repeat;
background-size: 100% 560px, 100% 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
@@ -32,13 +30,13 @@ body.jx3-template {
.jx3-frame {
position: relative;
width: 100%;
min-height: calc(100vh - 40px);
padding: 26px;
min-height: calc(100vh - 24px);
padding: 18px;
border: 1px solid var(--jx3-border-strong);
background: rgba(4, 17, 29, 0.2);
background: rgba(255, 255, 255, 0.72);
box-shadow:
inset 0 0 0 3px rgba(4, 17, 29, 0.9),
inset 0 0 0 4px rgba(203, 160, 78, 0.2),
inset 0 0 0 3px rgba(255, 255, 255, 0.9),
inset 0 0 0 4px rgba(126, 83, 27, 0.18),
var(--jx3-shadow-md);
}
@@ -74,7 +72,7 @@ body.jx3-template {
.data-header,
.collection-section
) {
width: var(--jx3-page-width);
width: min(100%, calc(var(--jx3-page-width) + 240px));
max-width: none;
margin-right: auto;
margin-left: auto;
@@ -88,12 +86,12 @@ body.jx3-template {
.jx3-template h1 {
margin: 0 0 var(--jx3-space-6);
color: var(--jx3-text);
font-size: 34px;
font-size: 42px;
line-height: 1.25;
font-weight: 800;
letter-spacing: 0.06em;
text-align: center;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.32);
text-shadow: none;
}
.jx3-template h2,
+51 -51
View File
@@ -20,7 +20,7 @@
) {
color: var(--jx3-text);
border: 1px solid var(--jx3-border-soft);
background: linear-gradient(180deg, rgba(17, 52, 81, 0.96), rgba(9, 35, 57, 0.96));
background: linear-gradient(180deg, var(--jx3-surface), var(--jx3-surface-strong));
border-radius: var(--jx3-radius-md);
box-shadow: var(--jx3-shadow-sm);
}
@@ -43,7 +43,7 @@
) {
color: var(--jx3-text-secondary);
border-color: var(--jx3-border-faint);
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface-soft);
}
.jx3-template :where(.header, .page-header, .top-info, .info-bar) {
@@ -74,11 +74,11 @@
}
.jx3-template .rank-2 {
color: #c8d2dc;
color: #506578;
}
.jx3-template .rank-3 {
color: #c88a62;
color: #9a542e;
}
.jx3-template :where(.winrate, .success, .unfinished, .status-unfinished) {
@@ -95,8 +95,8 @@
.jx3-template :where(.progress, .map-progress) {
overflow: hidden;
background: rgba(135, 157, 178, 0.2);
border: 1px solid rgba(135, 157, 178, 0.16);
background: #dce4e9;
border: 1px solid rgba(39, 57, 73, 0.13);
}
.jx3-template :where(.progress-fill, .map-progress > span) {
@@ -105,12 +105,12 @@
.jx3-template :where(.avatar, .force-icon, .school-icon, .item-icon, .material-icon, .main-icon) {
border: 1px solid var(--jx3-border-soft);
background: rgba(7, 26, 43, 0.6);
background: var(--jx3-surface-muted);
}
.jx3-template :where(.tag, .category-tag, .channel-tag, .status-badge, .quality-badge, .material-tag) {
border: 1px solid var(--jx3-border-soft);
background: rgba(225, 183, 102, 0.1);
background: #fff4dc;
color: var(--jx3-gold-bright);
}
@@ -169,13 +169,13 @@
.jx3-template .command {
color: var(--jx3-text-secondary);
background: rgba(114, 169, 213, 0.1);
background: #eaf3fa;
}
.jx3-template .command:hover {
color: var(--jx3-gold-bright);
border-color: var(--jx3-border-soft);
background: rgba(225, 183, 102, 0.1);
background: #fff4dc;
}
.jx3-template :where(.stat-label, .price-box-label, .material-price-label, .store-price-label, .point-label) {
@@ -189,7 +189,7 @@
.jx3-template .data-page__meta {
margin-bottom: var(--jx3-space-6);
color: var(--jx3-text-muted);
font-size: 16px;
font-size: 20px;
text-align: center;
}
@@ -199,7 +199,7 @@
overflow: hidden;
border: 1px solid var(--jx3-border-soft);
border-radius: var(--jx3-radius-md);
background: rgba(7, 26, 43, 0.76);
background: var(--jx3-surface);
box-shadow: var(--jx3-shadow-sm);
}
@@ -210,7 +210,7 @@
border-collapse: collapse;
border-radius: var(--jx3-radius-md);
color: var(--jx3-text-secondary);
background: rgba(7, 26, 43, 0.64);
background: var(--jx3-surface);
border-color: var(--jx3-border-soft);
text-align: center;
box-shadow: none;
@@ -218,7 +218,7 @@
.jx3-template thead {
color: var(--jx3-gold-bright);
background: #102f4a;
background: var(--jx3-surface-muted);
border-top: 1px solid var(--jx3-border-strong);
border-bottom: 1px solid var(--jx3-border-strong);
}
@@ -227,7 +227,7 @@
padding: 13px 14px;
color: inherit;
border-color: var(--jx3-border-faint);
font-size: 15px;
font-size: 19px;
text-align: center;
vertical-align: middle;
}
@@ -247,11 +247,11 @@
}
.jx3-template tbody tr:nth-child(even) {
background: rgba(31, 74, 107, 0.18);
background: #f3f6f8;
}
.jx3-template tbody tr:hover {
background: rgba(225, 183, 102, 0.09);
background: #fff4df;
}
.jx3-template tbody tr:last-child td {
@@ -325,11 +325,11 @@
}
.jx3-template .data-cell--rank.rank-2 {
color: #c8d2dc;
color: #506578;
}
.jx3-template .data-cell--rank.rank-3 {
color: #c88a62;
color: #9a542e;
}
.jx3-template .icon-label {
@@ -350,7 +350,7 @@
border-radius: 50%;
object-fit: contain;
flex: 0 0 auto;
background: rgba(7, 26, 43, 0.6);
background: var(--jx3-surface-muted);
}
.jx3-template .data-table tr.camp-haoqi {
@@ -362,11 +362,11 @@
}
.jx3-template .data-table--camp-tinted tr.camp-haoqi {
background: rgba(114, 169, 213, 0.1);
background: #eaf3fa;
}
.jx3-template .data-table--camp-tinted tr.camp-eren {
background: rgba(213, 106, 99, 0.1);
background: #fcedeb;
}
.jx3-template .data-table--camp-tinted tr.camp-haoqi .data-cell--name,
@@ -394,7 +394,7 @@
gap: 10px;
margin: 0 0 var(--jx3-space-3);
color: var(--jx3-gold-bright);
font-size: 24px;
font-size: 28px;
line-height: 1.3;
font-weight: 800;
}
@@ -426,7 +426,7 @@
padding: 18px 14px;
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-md);
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
text-align: center;
box-shadow: var(--jx3-shadow-sm);
}
@@ -434,12 +434,12 @@
.jx3-template .stat-card__label {
margin-bottom: var(--jx3-space-2);
color: var(--jx3-text-muted);
font-size: 16px;
font-size: 20px;
}
.jx3-template .stat-card__value {
color: var(--jx3-gold-bright);
font-size: 30px;
font-size: 34px;
line-height: 1;
font-weight: 800;
font-variant-numeric: tabular-nums;
@@ -455,7 +455,7 @@
padding: 14px;
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-md);
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
box-shadow: var(--jx3-shadow-sm);
}
@@ -465,7 +465,7 @@
border: 1px solid var(--jx3-border-soft);
border-radius: 8px;
object-fit: cover;
background: rgba(7, 26, 43, 0.6);
background: var(--jx3-surface-muted);
}
.jx3-template .ability-card__head {
@@ -478,7 +478,7 @@
.jx3-template .ability-card__name {
color: var(--jx3-text);
font-size: 18px;
font-size: 21px;
line-height: 1.25;
font-weight: 800;
}
@@ -488,8 +488,8 @@
border: 1px solid var(--jx3-border-soft);
border-radius: 999px;
color: var(--jx3-gold-bright);
background: rgba(225, 183, 102, 0.1);
font-size: 13px;
background: #fff4dc;
font-size: 18px;
white-space: nowrap;
}
@@ -501,7 +501,7 @@
.jx3-template .ability-card__desc {
color: var(--jx3-text-secondary);
font-size: 15px;
font-size: 19px;
line-height: 1.55;
word-break: break-word;
}
@@ -515,7 +515,7 @@
.jx3-template .collection-heading {
margin-bottom: var(--jx3-space-4);
font-size: 32px;
font-size: 36px;
}
.jx3-template .collection-card {
@@ -527,7 +527,7 @@
justify-content: center;
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-md);
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
text-align: center;
box-shadow: var(--jx3-shadow-sm);
}
@@ -543,13 +543,13 @@
.jx3-template .collection-card__name {
margin-bottom: 4px;
color: var(--jx3-text);
font-size: 22px;
font-size: 24px;
font-weight: 600;
}
.jx3-template .collection-card__meta {
color: var(--jx3-text-muted);
font-size: 20px;
font-size: 22px;
}
.jx3-template .collection-empty {
@@ -557,7 +557,7 @@
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-md);
color: var(--jx3-text-muted);
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
}
.jx3-template .data-header {
@@ -567,20 +567,20 @@
.jx3-template .data-header__title {
margin-bottom: var(--jx3-space-2);
color: var(--jx3-text);
font-size: 34px;
font-size: 38px;
font-weight: 800;
}
.jx3-template .data-header__meta {
color: var(--jx3-text-muted);
font-size: 20px;
font-size: 22px;
}
.jx3-template .server-status-card {
padding: 30px 12px;
border-radius: var(--jx3-radius-md);
color: #fff;
font-size: 28px;
font-size: 30px;
font-weight: 600;
text-align: center;
}
@@ -612,7 +612,7 @@
overflow: hidden;
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-lg);
background: rgba(13, 41, 66, 0.9);
background: var(--jx3-surface);
box-shadow: var(--jx3-shadow-sm);
}
@@ -621,7 +621,7 @@
height: 220px;
padding: 16px;
object-fit: contain;
background: rgba(7, 26, 43, 0.72);
background: var(--jx3-surface-muted);
}
.jx3-template .object-card__content {
@@ -632,7 +632,7 @@
.jx3-template .object-card__title {
margin-bottom: 6px;
font-size: 26px;
font-size: 30px;
font-weight: 800;
}
@@ -645,7 +645,7 @@
.jx3-template .object-card__meta {
margin-bottom: 14px;
color: var(--jx3-text-muted);
font-size: 15px;
font-size: 19px;
}
.jx3-template .object-card__meta span {
@@ -657,8 +657,8 @@
padding: 10px 14px;
border-left: 4px solid var(--jx3-gold);
color: var(--jx3-text-secondary);
background: rgba(114, 169, 213, 0.08);
font-size: 14px;
background: #edf5fa;
font-size: 18px;
}
.jx3-template .object-stats {
@@ -671,26 +671,26 @@
padding: 10px;
border: 1px solid var(--jx3-border-faint);
border-radius: var(--jx3-radius-md);
background: rgba(7, 26, 43, 0.58);
background: var(--jx3-surface-soft);
text-align: center;
}
.jx3-template .object-stat__label {
margin-bottom: 6px;
color: var(--jx3-text-muted);
font-size: 13px;
font-size: 18px;
}
.jx3-template .object-stat__value {
color: var(--jx3-text);
font-size: 18px;
font-size: 20px;
font-weight: 700;
}
.jx3-template .object-card__footer {
margin-top: 12px;
color: var(--jx3-text-muted);
font-size: 14px;
font-size: 18px;
}
/* @endcomponent */
@@ -698,7 +698,7 @@
.jx3-template .data-subtitle {
margin: 40px 0 12px;
color: var(--jx3-gold-bright);
font-size: 26px;
font-size: 30px;
font-weight: 700;
}
+5 -5
View File
@@ -11,13 +11,13 @@
}
.time {
font-size: 18px;
font-size: 20px;
font-weight: 600;
color: var(--jx3-text-secondary);
}
.boss-final {
font-size: 22px;
font-size: 24px;
font-weight: 800;
color: #d63031;
}
@@ -47,7 +47,7 @@
.index {
font-weight: bold;
font-size: 18px;
font-size: 20px;
color: var(--jx3-text);
margin-bottom: 4px;
}
@@ -55,13 +55,13 @@
.boss {
color: #d63031;
font-weight: 700;
font-size: 16px;
font-size: 18px;
margin-bottom: 6px;
}
.buff {
color: var(--jx3-blue);
font-size: 14px;
font-size: 18px;
line-height: 1.5em;
word-break: break-word;
}
+29 -29
View File
@@ -9,7 +9,7 @@
.page-title {
margin: 0 0 10px;
font-size: 40px;
font-size: 44px;
line-height: 1.3;
font-weight: 800;
color: #222;
@@ -18,7 +18,7 @@
.page-subtitle {
margin: 0;
color: #777;
font-size: 19px;
font-size: 21px;
line-height: 1.7;
}
@@ -84,7 +84,7 @@
.main-name {
margin: 0;
font-size: 36px;
font-size: 40px;
line-height: 1.35;
font-weight: 800;
color: #222;
@@ -97,7 +97,7 @@
justify-content: center;
padding: 7px 16px;
border-radius: 999px;
font-size: 17px;
font-size: 19px;
font-weight: 700;
white-space: nowrap;
}
@@ -113,7 +113,7 @@
margin-bottom: 9px;
padding-left: 22px;
color: var(--jx3-text-secondary);
font-size: 19px;
font-size: 21px;
line-height: 1.8;
}
@@ -149,12 +149,12 @@
.stat-label {
margin-bottom: 8px;
color: #777;
font-size: 16px;
font-size: 18px;
}
.stat-value {
color: #333;
font-size: 22px;
font-size: 25px;
line-height: 1.4;
font-weight: 800;
}
@@ -184,7 +184,7 @@
flex-wrap: wrap;
gap: 12px;
margin-bottom: 20px;
font-size: 25px;
font-size: 28px;
font-weight: 800;
}
@@ -200,7 +200,7 @@
.summary-count {
color: #888;
font-size: 16px;
font-size: 18px;
font-weight: 500;
}
@@ -220,12 +220,12 @@
.price-box-label {
margin-bottom: 10px;
color: #777;
font-size: 16px;
font-size: 18px;
}
.price-box-value {
color: #333;
font-size: 21px;
font-size: 24px;
font-weight: 800;
line-height: 1.55;
}
@@ -241,7 +241,7 @@
gap: 12px 22px;
margin-top: 18px;
color: #777;
font-size: 16px;
font-size: 18px;
line-height: 1.7;
}
@@ -259,12 +259,12 @@
.subtotal-label {
color: #555;
font-size: 18px;
font-size: 20px;
}
.subtotal-value {
color: #2e8b57;
font-size: 23px;
font-size: 26px;
font-weight: 800;
}
@@ -296,7 +296,7 @@
display: block;
margin-top: 5px;
color: #999;
font-size: 14px;
font-size: 18px;
font-weight: 500;
}
@@ -318,14 +318,14 @@
.section-title {
margin: 0;
font-size: 31px;
font-size: 35px;
font-weight: 800;
color: #222;
}
.section-desc {
color: #777;
font-size: 18px;
font-size: 20px;
line-height: 1.6;
}
@@ -379,7 +379,7 @@
.material-name {
margin: 0 0 10px;
font-size: 26px;
font-size: 30px;
line-height: 1.35;
font-weight: 800;
color: #222;
@@ -400,7 +400,7 @@
border-radius: 999px;
color: #666;
background: #f0f0f3;
font-size: 15px;
font-size: 18px;
line-height: 1.3;
font-weight: 600;
}
@@ -453,12 +453,12 @@
.quantity-label {
margin-bottom: 4px;
color: #888;
font-size: 15px;
font-size: 18px;
}
.quantity-value {
color: #333;
font-size: 29px;
font-size: 33px;
font-weight: 800;
}
@@ -482,12 +482,12 @@
.material-price-label {
margin-bottom: 8px;
color: #777;
font-size: 15px;
font-size: 18px;
}
.material-price-value {
color: #333;
font-size: 18px;
font-size: 20px;
font-weight: 700;
line-height: 1.55;
}
@@ -506,12 +506,12 @@
.material-total-label {
color: #555;
font-size: 17px;
font-size: 19px;
}
.material-total-value {
color: #d93939;
font-size: 21px;
font-size: 24px;
font-weight: 800;
}
@@ -522,7 +522,7 @@
gap: 10px 18px;
margin-top: 15px;
color: #888;
font-size: 15px;
font-size: 18px;
line-height: 1.6;
}
@@ -546,12 +546,12 @@
.store-price-label {
margin-bottom: 9px;
color: #777;
font-size: 16px;
font-size: 18px;
}
.store-price-value {
color: #2e8b57;
font-size: 22px;
font-size: 25px;
font-weight: 800;
line-height: 1.55;
}
@@ -566,5 +566,5 @@
border: 2px dashed #ccc;
border-radius: 16px;
color: #888;
font-size: 20px;
font-size: 22px;
}
+25 -25
View File
@@ -46,20 +46,20 @@
.header-label {
margin-bottom: 9px;
color: #98a7d9;
font-size: 14px;
color: var(--jx3-text-muted);
font-size: 18px;
font-weight: 600;
letter-spacing: 3px;
}
.role-name {
margin: 0;
color: #ffffff;
font-size: 34px;
color: var(--jx3-text);
font-size: 42px;
font-weight: 800;
line-height: 1.3;
letter-spacing: 1px;
text-shadow: 0 3px 12px rgba(0, 0, 0, 0.3);
text-shadow: none;
}
.header-line {
@@ -136,8 +136,8 @@
.achievement-name {
margin: 0 0 8px 0;
color: #ffffff;
font-size: 20px;
color: var(--jx3-text);
font-size: 25px;
font-weight: 750;
line-height: 1.45;
word-break: break-word;
@@ -156,15 +156,15 @@
padding: 4px 10px;
border: 1px solid rgba(114, 139, 230, 0.24);
border-radius: 999px;
color: #b9c6ed;
background: rgba(92, 115, 192, 0.11);
font-size: 12px;
color: #345a85;
background: #eaf1fa;
font-size: 18px;
font-weight: 600;
line-height: 1.3;
}
.category-tag.detail {
color: #d1bbf4;
color: #694297;
border-color: rgba(168, 114, 230, 0.24);
background: rgba(156, 92, 203, 0.1);
}
@@ -184,19 +184,19 @@
min-width: 72px;
padding: 6px 11px;
border-radius: 999px;
font-size: 12px;
font-size: 18px;
font-weight: 700;
white-space: nowrap;
}
.status-finished {
color: #7ff0c9;
color: #176541;
border: 1px solid rgba(71, 219, 166, 0.28);
background: rgba(53, 190, 143, 0.12);
}
.status-unfinished {
color: #ffbcbd;
color: #9b2f2f;
border: 1px solid rgba(255, 116, 116, 0.25);
background: rgba(218, 75, 75, 0.11);
}
@@ -205,8 +205,8 @@
display: inline-flex;
align-items: center;
gap: 5px;
color: #f4c969;
font-size: 12px;
color: var(--jx3-gold-bright);
font-size: 18px;
font-weight: 650;
white-space: nowrap;
}
@@ -218,8 +218,8 @@
.description {
min-height: 46px;
margin: 0 0 18px 0;
color: #aab5d1;
font-size: 14px;
color: var(--jx3-text-secondary);
font-size: 19px;
line-height: 1.75;
word-break: break-word;
}
@@ -234,8 +234,8 @@
}
.point-label {
color: #76819e;
font-size: 13px;
color: var(--jx3-text-muted);
font-size: 18px;
}
.point-value {
@@ -247,12 +247,12 @@
}
.point-number {
font-size: 22px;
font-size: 25px;
line-height: 1;
}
.point-unit {
font-size: 12px;
font-size: 18px;
font-weight: 600;
}
@@ -261,8 +261,8 @@
padding: 60px 20px;
border: 1px dashed rgba(135, 153, 207, 0.24);
border-radius: 16px;
color: #8d98b6;
background: rgba(27, 34, 55, 0.75);
color: var(--jx3-text-muted);
background: var(--jx3-surface);
text-align: center;
font-size: 16px;
font-size: 20px;
}
+17 -17
View File
@@ -4,7 +4,7 @@
align-items: center;
gap: 24px;
margin-bottom: 20px;
font-size: 14px;
font-size: 18px;
color: var(--jx3-text-secondary);
}
@@ -30,7 +30,7 @@
.map-name {
color: var(--jx3-text);
font-size: 18px;
font-size: 20px;
font-weight: 700;
line-height: 1.5;
}
@@ -38,7 +38,7 @@
.map-progress {
margin-top: 7px;
color: var(--jx3-text-muted);
font-size: 13px;
font-size: 18px;
font-weight: 500;
}
@@ -71,9 +71,9 @@
* 奖励已经获取,显示灰色
*/
.boss-item.finished {
color: #d5e0e9;
background: #24384b;
border: 1px solid #60788f;
color: #3f5263;
background: #e5eaee;
border: 1px solid #8696a4;
}
.boss-item.finished:hover {
@@ -85,8 +85,8 @@
* 奖励尚未获取,显示绿色
*/
.boss-item.unfinished {
color: #c8f3dc;
background: #183f34;
color: #165b3b;
background: #e1f2e9;
border: 1px solid #438d68;
}
@@ -98,7 +98,7 @@
position: absolute;
top: 6px;
right: 8px;
font-size: 11px;
font-size: 17px;
font-weight: 700;
opacity: 0.65;
}
@@ -106,7 +106,7 @@
.boss-name {
padding-right: 18px;
overflow: hidden;
font-size: 15px;
font-size: 19px;
font-weight: 700;
line-height: 1.4;
text-overflow: ellipsis;
@@ -119,7 +119,7 @@
justify-content: center;
gap: 5px;
margin-top: 6px;
font-size: 12px;
font-size: 18px;
font-weight: 500;
}
@@ -130,25 +130,25 @@
width: 16px;
height: 16px;
border-radius: 50%;
font-size: 11px;
font-size: 17px;
font-weight: 800;
}
.boss-item.finished .status-icon {
color: #e7eef4;
background: rgba(192, 211, 226, 0.16);
color: #304352;
background: #d2dbe2;
}
.boss-item.unfinished .status-icon {
color: #d7f8e6;
background: rgba(98, 188, 146, 0.18);
color: #155b3a;
background: #c8e7d7;
}
.empty-data {
padding: 50px 20px;
color: var(--jx3-text-muted);
text-align: center;
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
border: 1px solid var(--jx3-border-faint);
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+16 -18
View File
@@ -2,12 +2,10 @@
position: relative;
padding: 22px;
overflow: hidden;
border: 1px solid rgba(224, 200, 141, 0.72);
border: 1px solid var(--jx3-border-soft);
border-radius: 20px;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.035), transparent 24%),
rgba(8, 27, 49, 0.92);
box-shadow: inset 0 0 32px rgba(126, 161, 216, 0.06);
background: linear-gradient(135deg, #ffffff, #f5f8fa);
box-shadow: var(--jx3-shadow-sm);
}
.help-header {
@@ -19,19 +17,19 @@
}
.top-name {
color: #fff0c8;
font-size: 24px;
color: var(--jx3-gold-bright);
font-size: 32px;
font-weight: 800;
letter-spacing: 0.08em;
}
.top-summary {
margin-top: 5px;
font-size: 14px;
font-size: 18px;
}
.top-desc {
font-size: 14px;
font-size: 18px;
line-height: 1.75;
text-align: right;
}
@@ -61,7 +59,7 @@
padding: 11px 15px;
border-bottom: 1px solid rgba(220, 199, 145, 0.3);
background: linear-gradient(90deg, rgba(198, 165, 93, 0.24), rgba(105, 138, 194, 0.08));
font-size: 19px;
font-size: 22px;
font-weight: 800;
letter-spacing: 0.05em;
}
@@ -89,7 +87,7 @@
.feature-name {
padding-top: 3px;
font-size: 14px;
font-size: 18px;
line-height: 1.5;
font-weight: 700;
}
@@ -106,7 +104,7 @@
padding: 3px 7px;
border: 1px solid rgba(157, 184, 228, 0.22);
border-radius: 5px;
font-size: 12px;
font-size: 18px;
line-height: 1.5;
word-break: break-all;
}
@@ -142,8 +140,8 @@
}
.section--danger .section-title {
color: #ffd6d3;
background: linear-gradient(90deg, rgba(176, 65, 65, 0.3), rgba(105, 138, 194, 0.06));
color: #872d29;
background: linear-gradient(90deg, #f9dfdc, #f9f3ee);
}
.section--success {
@@ -151,19 +149,19 @@
}
.section--success .section-title {
color: #d8ffeb;
background: linear-gradient(90deg, rgba(39, 148, 103, 0.28), rgba(105, 138, 194, 0.06));
color: #176541;
background: linear-gradient(90deg, #dff2e8, #f3f7f4);
}
.help-footer {
margin-top: 17px;
padding: 13px 16px 2px;
border-top: 1px solid rgba(216, 195, 140, 0.24);
font-size: 13px;
font-size: 18px;
line-height: 1.8;
text-align: center;
}
.help-footer strong {
color: #e6d4a4;
color: var(--jx3-gold-bright);
}
+3 -3
View File
@@ -6,7 +6,7 @@
display: flex;
gap: 18px;
margin-top: 10px;
font-size: 15px;
font-size: 19px;
color: var(--jx3-text-muted);
}
@@ -15,7 +15,7 @@
height: 34px;
border-radius: 6px;
flex: 0 0 auto;
background: rgba(7, 26, 43, 0.6);
background: var(--jx3-surface-muted);
}
.item-name {
@@ -56,6 +56,6 @@
.time {
color: var(--jx3-text-muted);
font-size: 14px;
font-size: 18px;
white-space: nowrap;
}
+2 -2
View File
@@ -25,7 +25,7 @@
min-width: 58px;
padding: 5px 10px;
border-radius: 6px;
font-size: 14px;
font-size: 18px;
font-weight: 700;
text-align: center;
}
@@ -63,5 +63,5 @@
.empty-row td {
padding: 50px 20px;
color: #8a8fa3;
font-size: 17px;
font-size: 19px;
}
+8 -7
View File
@@ -8,7 +8,7 @@
/* 表头 */
.week {
text-align: center;
font-size: 18px;
font-size: 20px;
font-weight: 700;
color: var(--jx3-text-secondary);
}
@@ -24,17 +24,18 @@
/* 状态样式 */
.card.past {
background: rgba(71, 91, 108, 0.72);
color: var(--jx3-text-secondary);
background: #e2e7eb;
color: #4d5d6b;
}
.card.today {
background: rgba(53, 113, 165, 0.9);
color: var(--jx3-text);
background: #d9ebf8;
color: #123e60;
border: 2px solid #397aab;
}
.card.future {
background: rgba(13, 41, 66, 0.82);
background: var(--jx3-surface);
color: var(--jx3-text);
border: 1px solid var(--jx3-border-faint);
}
@@ -46,6 +47,6 @@
}
.line {
font-size: 16px;
font-size: 20px;
margin-top: 6px;
}
+2 -2
View File
@@ -88,7 +88,7 @@ body.jx3-template--shapan {
width: 1em;
color: #fffcd0;
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
font-size: 15px;
font-size: 17px;
line-height: 20px;
text-shadow: 1px 1px 1px #000;
}
@@ -99,7 +99,7 @@ body.jx3-template--shapan {
width: 110px;
color: #fffcd0;
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
font-size: 14px;
font-size: 17px;
font-weight: 600;
line-height: 18px;
text-align: center;
+6 -6
View File
@@ -7,7 +7,7 @@
}
.goods-desc {
font-size: 16px;
font-size: 20px;
line-height: 1.8;
color: var(--jx3-text-secondary);
margin: 0 0 10px;
@@ -20,7 +20,7 @@
align-items: center;
flex-wrap: wrap;
gap: 20px 40px;
font-size: 18px;
font-size: 20px;
margin-bottom: 30px;
color: var(--jx3-text-muted);
}
@@ -43,7 +43,7 @@
}
.table-title {
font-size: 20px;
font-size: 22px;
font-weight: 700;
margin-bottom: 8px;
text-align: center;
@@ -75,7 +75,7 @@
}
.table-card.notice thead {
background: rgba(63, 111, 153, 0.78);
background: #dcebf6;
}
/* ===== 万宝楼在售(绿色) ===== */
@@ -86,7 +86,7 @@
}
.table-card.sale thead {
background: rgba(43, 111, 82, 0.78);
background: #dcefe5;
}
/* ===== 查询区服(紫色) ===== */
@@ -97,5 +97,5 @@
}
.table-card.query thead {
background: rgba(90, 67, 126, 0.8);
background: #e9e0f3;
}
+5 -5
View File
@@ -19,7 +19,7 @@
.time {
width: 70px;
text-align: center;
font-size: 26px;
font-size: 30px;
font-weight: 700;
color: #2563eb;
flex-shrink: 0;
@@ -32,19 +32,19 @@
}
.stage {
font-size: 22px;
font-size: 25px;
font-weight: 700;
margin-bottom: 6px;
}
.meta {
font-size: 18px;
font-size: 20px;
color: #666;
margin-bottom: 6px;
}
.desc {
font-size: 20px;
font-size: 22px;
color: var(--jx3-text-secondary);
line-height: 1.5;
}
@@ -58,7 +58,7 @@
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-size: 20px;
font-weight: 700;
color: var(--jx3-blue);
}
+5 -5
View File
@@ -9,13 +9,13 @@
}
.role-name {
font-size: 40px;
font-size: 44px;
font-weight: bold;
}
.sub-info {
margin-top: 6px;
font-size: 22px;
font-size: 25px;
}
/* ===== 头像 + 核心数据 ===== */
@@ -57,12 +57,12 @@
}
.stat-title {
font-size: 26px;
font-size: 30px;
color: var(--jx3-text-muted);
}
.stat-value {
font-size: 36px;
font-size: 40px;
margin-top: 8px;
font-weight: bold;
}
@@ -89,6 +89,6 @@
}
.time-col {
font-size: 16px;
font-size: 18px;
font-variant-numeric: tabular-nums;
}
+8 -8
View File
@@ -14,7 +14,7 @@
align-items: center;
gap: 10px;
color: #565d70;
font-size: 16px;
font-size: 18px;
white-space: nowrap;
}
@@ -28,7 +28,7 @@
.meta {
margin-top: 12px;
color: #7a7f99;
font-size: 15px;
font-size: 18px;
line-height: 1.7;
}
@@ -46,13 +46,13 @@
}
.summary-name {
font-size: 24px;
font-size: 28px;
font-weight: 800;
color: #222;
}
.summary-percent {
font-size: 34px;
font-size: 38px;
line-height: 1;
font-weight: 900;
color: #d93939;
@@ -62,7 +62,7 @@
.score {
margin-top: 8px;
color: #656d80;
font-size: 15px;
font-size: 18px;
font-variant-numeric: tabular-nums;
}
@@ -96,7 +96,7 @@
.item-name {
min-width: 0;
font-size: 17px;
font-size: 19px;
font-weight: 800;
color: #303543;
word-break: break-word;
@@ -104,12 +104,12 @@
.item-percent {
color: #d93939;
font-size: 18px;
font-size: 20px;
font-weight: 900;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.item .score {
font-size: 14px;
font-size: 18px;
}
+5 -5
View File
@@ -3,7 +3,7 @@
*/
.rank-col {
color: #d93939;
font-size: 18px;
font-size: 20px;
font-weight: 800;
white-space: nowrap;
}
@@ -38,7 +38,7 @@
width: 100%;
min-width: 0;
color: var(--jx3-text);
font-size: 17px;
font-size: 19px;
font-weight: 700;
white-space: nowrap;
}
@@ -68,7 +68,7 @@
padding-left: 10px;
padding-right: 10px;
color: var(--jx3-text);
font-size: 17px;
font-size: 19px;
font-weight: 600;
}
@@ -79,7 +79,7 @@
padding-left: 8px;
padding-right: 8px;
color: var(--jx3-text-secondary);
font-size: 17px;
font-size: 19px;
}
/*
@@ -89,7 +89,7 @@
padding-left: 6px;
padding-right: 10px;
color: #d93939;
font-size: 18px;
font-size: 20px;
font-weight: 800;
white-space: nowrap;
font-variant-numeric: tabular-nums;
+25 -23
View File
@@ -1,27 +1,27 @@
:root {
--jx3-bg-deep: #04111d;
--jx3-bg: #071a2b;
--jx3-bg-raised: #0a2238;
--jx3-surface: #0d2942;
--jx3-surface-strong: #113451;
--jx3-surface-soft: #173d5d;
--jx3-surface-muted: #1b4567;
--jx3-border: #9a7432;
--jx3-border-strong: #c09647;
--jx3-border-soft: rgba(203, 160, 78, 0.36);
--jx3-border-faint: rgba(141, 175, 207, 0.18);
--jx3-gold: #e1b766;
--jx3-gold-bright: #f2cf82;
--jx3-gold-muted: #b89655;
--jx3-text: #f5f0e6;
--jx3-text-secondary: #c4d2df;
--jx3-text-muted: #879db2;
--jx3-blue: #72a9d5;
--jx3-green: #62bc92;
--jx3-red: #d56a63;
--jx3-purple: #a98bd2;
--jx3-shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.18);
--jx3-shadow-md: 0 12px 30px rgba(0, 0, 0, 0.24);
--jx3-bg-deep: #dfe6eb;
--jx3-bg: #edf2f5;
--jx3-bg-raised: #f7f9fa;
--jx3-surface: #ffffff;
--jx3-surface-strong: #f8fafb;
--jx3-surface-soft: #f1f5f7;
--jx3-surface-muted: #e8eef2;
--jx3-border: #9a6a25;
--jx3-border-strong: #7e531b;
--jx3-border-soft: rgba(126, 83, 27, 0.34);
--jx3-border-faint: rgba(39, 57, 73, 0.16);
--jx3-gold: #9a640f;
--jx3-gold-bright: #704505;
--jx3-gold-muted: #c8973e;
--jx3-text: #17212b;
--jx3-text-secondary: #304254;
--jx3-text-muted: #596a79;
--jx3-blue: #185f99;
--jx3-green: #20754d;
--jx3-red: #b23832;
--jx3-purple: #694297;
--jx3-shadow-sm: 0 4px 14px rgba(34, 48, 61, 0.09);
--jx3-shadow-md: 0 12px 30px rgba(34, 48, 61, 0.13);
--jx3-radius-sm: 6px;
--jx3-radius-md: 10px;
--jx3-radius-lg: 14px;
@@ -33,6 +33,8 @@
--jx3-space-6: 24px;
--jx3-space-8: 32px;
--jx3-page-width: 1200px;
--jx3-font-size: 20px;
--jx3-font-size-small: 18px;
--jx3-font: "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", Arial, sans-serif;
}