YYPOST群发软件 发表于 2026-9-25 06:44:07

AIWROK软件双窗口悬浮日志系统

AIWROK软件双窗口悬浮日志系统






/**
* AIWROK 双窗口悬浮日志系统(极简版)
* - 窗口 A:主日志面板(大,左侧),滚动显示
* - 窗口 B:实时统计面板(小,右上),计数/耗时/状态
* 适用:安卓 Rhino ES5 · AIWROK
*/

importClass(java.lang.Thread);

// ======================== 基础 ========================
function doSleep(ms) { try { Thread.sleep(ms); } catch (e) {} }

// ======================== 主日志窗口 A ========================
var winA = {
    floatUI: null,
    tvLog: null,
    tvTitle: null,
    lines: [],          // 用数组存所有日志行,setText 时 join 全部输出
    count: 0,
    MAX_LINES: 100,   // 最多保留 100 行,超出裁掉旧的

    init: function() {
      this.floatUI = new floatUI();
      var sw = screen.getScreenWidth();
      var sh = screen.getScreenHeight();
      printl("[屏幕] " + sw + "x" + sh);
      // 窗口 A:宽度 40%,高度 22%,贴顶部 8%
      var w = Math.floor(sw * 0.40);
      var h = Math.floor(sh * 0.22);
      var x = 8;
      var y = Math.floor(sh * 0.08);

      var xml = '<LinearLayout orientation="vertical" w="' + w + '" h="' + h +
                  '" background="#881e293b">' +
                  '<TextView id="title" text="[ 运行日志 ] 共 0 条" ' +
                  'textColor="#7dd3fc" textSize="13" padding="6" background="#441e40af"/>' +
                  '<ScrollView w="fill_parent" h="fill_parent">' +
                  '    <TextView id="logBox" w="fill_parent" h="fill_parent" text="" ' +
                  'textColor="#f1f5f9" textSize="11" padding="6"/>' +
                  '</ScrollView>' +
                  '</LinearLayout>';

      try {
            this.floatUI.loadXML(xml);
            this.floatUI.setPosition(x, y);
            this.tvTitle = this.floatUI.findViewById("title");
            this.tvLog   = this.floatUI.findViewById("logBox");
            printl("[窗口A] OKpos=(" + x + "," + y + ")size=" + w + "x" + h);
            return true;
      } catch (e) {
            printl("[窗口A] 失败: " + e);
            return false;
      }
    },

    log: function(msg, color) {
      this.count++;
      var colorHex = color || "#E0E0E0";
      if (msg.length > 240) msg = msg.substring(0, 240) + "...";

      var d = new Date();
      var ts = (d.getHours() < 10 ? "0" : "") + d.getHours() + ":" +
               (d.getMinutes() < 10 ? "0" : "") + d.getMinutes() + ":" +
               (d.getSeconds() < 10 ? "0" : "") + d.getSeconds();

      // 前缀标记颜色含义(虽然文本颜色无法区分,但前缀能看懂)
      var prefix = colorHex == "#4ADE80" ? "✓ " :
                     colorHex == "#F87171" ? "✗ " :
                     colorHex == "#FFD166" ? "⚡ " : "• ";

      this.lines.push(ts + "" + prefix + msg);
      if (this.lines.length > this.MAX_LINES) this.lines.shift();

      try {
            this.tvLog.setText(this.lines.join("\n"));
            this.tvTitle.setText("[ 运行日志 ] 共 " + this.count + " 条");
      } catch (e) {}
    },

    destroy: function() {
      try { if (this.floatUI) { this.floatUI.close(); this.floatUI = null; } } catch (e) {}
    }
};

// ======================== 统计窗口 B ========================
var winB = {
    floatUI: null,
    tvTask: null,
    tvOk: null,
    tvErr: null,
    tvCost: null,
    startTs: 0,
    totalTasks: 0,
    okCount: 0,
    errCount: 0,

    init: function() {
      this.floatUI = new floatUI();
      var sw = screen.getScreenWidth();
      var sh = screen.getScreenHeight();
      // 窗口 B:宽度 25%,高度 10%,和 A 顶部对齐
      var w = Math.floor(sw * 0.25);
      var h = Math.floor(sh * 0.10);
      var x = sw - w - 8;
      var y = Math.floor(sh * 0.08);

      var xml = '<LinearLayout orientation="vertical" w="' + w + '" h="' + h +
                  '" background="#88f8fafc" padding="6">' +
                  '<TextView text="&#128202; 实时统计" textColor="#0ea5e9" textSize="13" gravity="center"/>' +
                  '<TextView text="────────────────" w="fill_parent" textColor="#330ea5e9" textSize="8" padding="2"/>' +
                  '<TextView id="tv_task" text="任务: 0" textColor="#334155" textSize="11"/>' +
                  '<TextView id="tv_ok"   text="✓ 成功: 0" textColor="#16a34a" textSize="11"/>' +
                  '<TextView id="tv_err"text="✗ 失败: 0" textColor="#dc2626" textSize="11"/>' +
                  '<TextView id="tv_cost" text="⏱ 00:00" textColor="#d97706" textSize="11"/>' +
                  '</LinearLayout>';

      try {
            this.floatUI.loadXML(xml);
            this.floatUI.setPosition(x, y);
            this.tvTask = this.floatUI.findViewById("tv_task");
            this.tvOk   = this.floatUI.findViewById("tv_ok");
            this.tvErr= this.floatUI.findViewById("tv_err");
            this.tvCost = this.floatUI.findViewById("tv_cost");
            this.startTs = new Date().getTime();
            printl("[窗口B] 统计面板 OKpos=(" + x + "," + y + ") size=" + w + "x" + h);
            return true;
      } catch (e) {
            printl("[窗口B] 失败: " + e);
            return false;
      }
    },

    update: function(taskName, success) {
      this.totalTasks++;
      if (success) this.okCount++; else this.errCount++;

      var elapsed = new Date().getTime() - this.startTs;
      var mm = Math.floor(elapsed / 60000);
      var ss = Math.floor((elapsed % 60000) / 1000);
      var timeStr = (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss;

      try {
            this.tvTask.setText("任务: " + this.totalTasks + " · " + taskName);
            this.tvOk.setText("✓ 成功: " + this.okCount);
            this.tvErr.setText("✗ 失败: " + this.errCount);
            this.tvCost.setText("⏱ " + timeStr);
      } catch (e) {}
    },

    tick: function() {
      var elapsed = new Date().getTime() - this.startTs;
      var mm = Math.floor(elapsed / 60000);
      var ss = Math.floor((elapsed % 60000) / 1000);
      var timeStr = (mm < 10 ? "0" : "") + mm + ":" + (ss < 10 ? "0" : "") + ss;
      try { this.tvCost.setText("⏱ " + timeStr); } catch (e) {}
    },

    destroy: function() {
      try { if (this.floatUI) { this.floatUI.close(); this.floatUI = null; } } catch (e) {}
    }
};

// ======================== 后台计时线程 ========================
var stopTick = false;
new thread().runJsCode(function() {
    while (!stopTick) {
      doSleep(1000);
      if (!stopTick) winB.tick();
    }
}, "计时线程");

// ======================== 演示(录像友好版,约 40 秒) ========================
function runDemo() {
    printl("╔══════════════════════════════════════════════╗");
    printl("║   AIWROK 双窗口悬浮日志系统(录像演示)   ║");
    printl("╚══════════════════════════════════════════════╝");

    // ── 第 1 幕:启动(给观众看清两个窗布局,停 3 秒)──
    winA.init();
    doSleep(500);
    winB.init();
    doSleep(1000);

    winA.log("&#127916; 演示开始,请开始录像...", "#FFD166");
    winB.update("系统启动", true);
    doSleep(2000);

    // ── 第 2 幕:跑任务(间隔 800ms,观众能看清每条)──
    var tasks = [
      ["检测设备权限", true],
      ["连接远程服务器", true],
      ["登录鉴权", true],
      ["拉取配置文件", true],
      ["OCR 图像识别", false],   // 故意失败一个
      ["重试 OCR", true],
      ["解析识别结果", true],
      ["写入本地数据库", true],
      ["上传云端同步", true],
      ["校验数据完整性", false],// 再失败一个
      ["自动修复", true],
      ["推送通知到手机", true],
      ["生成运行报告", true]
    ];

    winA.log("═══════ 任务开始 ═══════", "#FFD166");
    winB.update("准备就绪", true);
    doSleep(1200);

    for (var i = 0; i < tasks.length; i++) {
      var name = tasks;
      var success = tasks;

      if (success) {
            winA.log("✓ [ " + (i + 1) + "/" + tasks.length + " ] " + name, "#4ADE80");
      } else {
            winA.log("✗ [ " + (i + 1) + "/" + tasks.length + " ] " + name + " → 重试中...", "#F87171");
      }
      winB.update(name, success);
      doSleep(800);

      // 失败后自动重试一条,展示 winB 持续更新
      if (!success) {
            winA.log("↳ 重试成功 ✓", "#4ADE80");
            winB.update(name + "(重试)", true);
            doSleep(800);
      }
    }

    // ── 第 3 幕:收尾展示结果(停 5 秒,观众能看清统计面板)──
    winA.log("═══════ 任务全部结束 ═══════", "#FFD166");
    winA.log("成功 " + winB.okCount + "/失败 " + winB.errCount + "(失败已自动重试)", "#4ADE80");
    doSleep(5000);

    // ── 第 4 幕:展示日志滚动到最新、计时继续跑(停 3 秒)──
    winA.log("窗口保持开启中... 可以继续录统计面板和计时", "#CCCCCC");
    doSleep(3000);

    printl("");
    printl("╔══════════════════════════════════════════════╗");
    printl("║演示结束!窗口将在 5 秒后自动关闭         ║");
    printl("║如需更长时间录制,注释掉最后的 destroy      ║");
    printl("╚══════════════════════════════════════════════╝");

    doSleep(5000);

    stopTick = true;
    winA.destroy();
    winB.destroy();
    printl("&#129529; 双窗口已关闭");
}

runDemo();


页: [1]
查看完整版本: AIWROK软件双窗口悬浮日志系统