Last active
March 19, 2026 04:33
-
-
Save codexss/e8328f58982c029742d00da8d99cd436 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Server Monitor Widget (Final Version) | |
| // @description Streamlined UI, left-aligned progress bar, and top-left update time. | |
| // ==/UserScript== | |
| // ========================================================= | |
| // 1. 配置区域:在这里填入你的 API 地址 | |
| // ========================================================= | |
| const API_URL = "http://192.168.1.1:43210/index.php?action=get_status"; | |
| const USE_MOCK_DATA = false; // 测试时保持 true,对接真实 API 时改为 false | |
| // 定义全局颜色 (支持暗黑模式自动切换) | |
| const Colors = { | |
| bg: Color.dynamic(new Color("#FFFFFF"), new Color("#1C1C1E")), // 背景色 | |
| primaryText: Color.dynamic(new Color("#1A202C"), new Color("#FFFFFF")), // 主文本色 (黑/白) | |
| secondaryText: Color.dynamic(new Color("#8A8A8E"), new Color("#8E8E93")),// 次要文本色 (灰) | |
| barBg: Color.dynamic(new Color("#E5E5EA"), new Color("#3A3A3C")), // 进度条底色 | |
| barFill: Color.dynamic(new Color("#1A202C"), new Color("#FFFFFF")), // 进度条填充色 | |
| statusGreen: new Color("#34C759"), // 运行正常 (绿) | |
| statusRed: new Color("#FF3B30") // 运行异常 (红) | |
| }; | |
| // ========================================================= | |
| // 2. 主逻辑 | |
| // ========================================================= | |
| async function main() { | |
| const data = await fetchData(); | |
| const family = config.widgetFamily || "small"; | |
| const widget = createWidget(data, family); | |
| if (config.runsInWidget) { | |
| Script.setWidget(widget); | |
| } else { | |
| if (family === "small") { | |
| widget.presentSmall(); | |
| } else { | |
| widget.presentMedium(); | |
| } | |
| } | |
| Script.complete(); | |
| } | |
| async function fetchData() { | |
| if (USE_MOCK_DATA) { | |
| return { | |
| "data": [{ | |
| "id": 1, "account": "LTAI5tS***", "flow_total": 200, "flow_used": 61.1, | |
| "percentageOfUse": 30.55, "region": "cn-hongkong", "regionName": "中国香港", | |
| "rate95": false, "threshold": 95, "instanceStatus": "Running", | |
| "lastUpdated": "2026-03-19 11:21:27", "remark": "", | |
| "cost": { "enabled": true, "monthly_cost": 0.02, "balance": "0.00", "currency": "USD", "last_updated": "2026-03-19 11:22:43", "error": null } | |
| }], | |
| "system_last_run": 1773890521 | |
| }; | |
| } else { | |
| const req = new Request(API_URL); | |
| return await req.loadJSON(); | |
| } | |
| } | |
| // ========================================================= | |
| // 3. UI 构建 | |
| // ========================================================= | |
| function createWidget(apiResponse, family) { | |
| const widget = new ListWidget(); | |
| widget.backgroundColor = Colors.bg; | |
| const padding = family === "small" ? 16 : 20; | |
| widget.setPadding(padding, padding, padding, padding); | |
| const info = apiResponse.data[0]; | |
| // 1. 顶部行:左侧刷新时间 + 右侧运行状态指示灯 | |
| const topRow = widget.addStack(); | |
| topRow.centerAlignContent(); // 垂直居中对齐,保证文字和圆点在同一水平线 | |
| // 提取时间部分 (截取空格后的部分,即 11:21:27) | |
| const timeString = info.lastUpdated.split(" ")[1] || ""; | |
| const updateText = topRow.addText(timeString); | |
| updateText.font = Font.systemFont(11); // 灰色小字 | |
| updateText.textColor = Colors.secondaryText; | |
| topRow.addSpacer(); // 将中间撑开,把红绿点推到最右侧 | |
| const isRunning = info.instanceStatus.toLowerCase().includes("run"); | |
| const dot = topRow.addImage(SFSymbol.named("circle.fill").image); | |
| dot.tintColor = isRunning ? Colors.statusGreen : Colors.statusRed; | |
| dot.imageSize = new Size(10, 10); | |
| widget.addSpacer(12); | |
| // 2. 核心区:流量大字 (严格靠左对齐) | |
| const flowRow = widget.addStack(); | |
| flowRow.bottomAlignContent(); | |
| const mainNum = flowRow.addText(info.flow_used.toString()); | |
| mainNum.font = Font.blackSystemFont(family === "small" ? 38 : 42); | |
| mainNum.textColor = Colors.primaryText; | |
| flowRow.addSpacer(4); | |
| const unitStack = flowRow.addStack(); | |
| unitStack.setPadding(0, 0, family === "small" ? 6 : 8, 0); | |
| const unitText = unitStack.addText("GB"); | |
| unitText.font = Font.boldSystemFont(family === "small" ? 18 : 20); | |
| unitText.textColor = Colors.secondaryText; | |
| flowRow.addSpacer(); // 将整行文字推到左边 | |
| widget.addSpacer(6); | |
| // 3. 流量占比标签 (严格靠左对齐) | |
| const percentRow = widget.addStack(); | |
| const percentLabel = percentRow.addText(`${info.percentageOfUse}% / ${info.flow_total}G`); | |
| percentLabel.font = Font.boldSystemFont(15); | |
| percentLabel.textColor = Colors.secondaryText; | |
| percentRow.addSpacer(); // 将标签推到左边 | |
| widget.addSpacer(12); | |
| // 4. 进度条 (强制从左到右填充) | |
| const barRow = widget.addStack(); | |
| const barWidth = family === "small" ? 126 : 280; | |
| const barHeight = 8; | |
| const percentage = info.percentageOfUse / 100; | |
| // 进度条灰底轨道 | |
| const progressBgStack = barRow.addStack(); | |
| progressBgStack.layoutHorizontally(); | |
| progressBgStack.size = new Size(barWidth, barHeight); | |
| progressBgStack.backgroundColor = Colors.barBg; | |
| progressBgStack.cornerRadius = barHeight / 2; | |
| // 进度条黑色填充 | |
| if (percentage > 0) { | |
| const progressFillStack = progressBgStack.addStack(); | |
| const fillWidth = Math.max(barWidth * percentage, barHeight); // 保证最起码是个圆点 | |
| progressFillStack.size = new Size(fillWidth, barHeight); | |
| progressFillStack.backgroundColor = Colors.barFill; | |
| progressFillStack.cornerRadius = barHeight / 2; | |
| } | |
| progressBgStack.addSpacer(); // 关键:在填充部分右侧加 Spacer,把填充挤到左边 | |
| barRow.addSpacer(); // 确保进度条外部也靠左对齐 | |
| widget.addSpacer(); // 弹性空间,把底部的“本月费用”压到最下面 | |
| // 5. 底部:本月费用 | |
| const costRow = widget.addStack(); | |
| costRow.centerAlignContent(); | |
| const sym = info.cost.currency === "USD" ? "$" : "¥"; | |
| const leftText = costRow.addText("本月费用"); | |
| leftText.font = Font.systemFont(14); | |
| leftText.textColor = Colors.secondaryText; | |
| costRow.addSpacer(); // 将左右两块文本撑开 | |
| const rightText = costRow.addText(`${sym}${info.cost.monthly_cost}`); | |
| rightText.font = Font.boldSystemFont(16); | |
| rightText.textColor = Colors.primaryText; | |
| return widget; | |
| } | |
| // 执行 | |
| await main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment