#
Junjie
2026-01-15 6200ba627af8af4045155c1bd7e65220ce59d6ba
#
5个文件已修改
788 ■■■■ 已修改文件
src/main/java/com/zy/common/config/AspectConfig.java 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/system/controller/LicenseCreatorController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/system/timer/LicenseTimer.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/index.html 716 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/config/AspectConfig.java
@@ -47,24 +47,26 @@
    @Around("@within(org.springframework.web.bind.annotation.RestController)" +
            "||@within(org.springframework.stereotype.Controller)")
    public Object after(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = requestAttributes != null ? requestAttributes.getRequest() : null;
        long start = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OpenApiLog annotation = method.isAnnotationPresent(OpenApiLog.class) ? method.getAnnotation(OpenApiLog.class) : null;
        OpenApiLog annotation = method.isAnnotationPresent(OpenApiLog.class) ? method.getAnnotation(OpenApiLog.class)
                : null;
        Object result = null;
        Object errorResponse = null;
        try {
            if (licenseTimer != null && !licenseTimer.getSystemSupport()) {
                Object fail = R.error("许可证失效");
                if (annotation != null && !Cools.isEmpty(annotation.memo())) {
                    saveErrLog(joinPoint, request, fail, new CoolException("许可证失效"), annotation.memo());
                }
                return fail;
            }
            result = joinPoint.proceed(joinPoint.getArgs());
            if (annotation != null && !Cools.isEmpty(annotation.memo())) {
                if (licenseTimer != null && !licenseTimer.getSystemSupport()) {
                    Object fail = R.error("许可证失效");
                    if (annotation != null && !Cools.isEmpty(annotation.memo())) {
                        saveErrLog(joinPoint, request, fail, new CoolException("许可证失效"), annotation.memo());
                    }
                    return fail;
                }
                saveLog(joinPoint, request, result, annotation.memo());
            }
            return result;
@@ -79,13 +81,16 @@
            throw new RuntimeException(ex);
        } finally {
            long end = System.currentTimeMillis();
//            log.info("请求日志的打印");
//            log.info("请求地址:{}", request != null ? Optional.ofNullable(request.getRequestURI()).orElse(null) : null);
//            log.info("请求方式:{}", request != null ? request.getMethod() : null);
//            log.info("请求类方法:{}", joinPoint.getSignature());
//            log.info("请求类方法参数:{}", JSONObject.toJSONString(filterArgs(joinPoint.getArgs())));
//            log.info("请求响应参数{}", JSONObject.toJSONString(result != null ? result : errorResponse));
//            log.info("执行耗时:{}", end - start);
            // log.info("请求日志的打印");
            // log.info("请求地址:{}", request != null ?
            // Optional.ofNullable(request.getRequestURI()).orElse(null) : null);
            // log.info("请求方式:{}", request != null ? request.getMethod() : null);
            // log.info("请求类方法:{}", joinPoint.getSignature());
            // log.info("请求类方法参数:{}",
            // JSONObject.toJSONString(filterArgs(joinPoint.getArgs())));
            // log.info("请求响应参数{}", JSONObject.toJSONString(result != null ? result :
            // errorResponse));
            // log.info("执行耗时:{}", end - start);
        }
    }
@@ -110,11 +115,11 @@
                1,
                new Date(),
                null,
                null
        ));
                null));
    }
    private void saveErrLog(ProceedingJoinPoint joinPoint, HttpServletRequest request, Object response, Throwable ex, String memo) {
    private void saveErrLog(ProceedingJoinPoint joinPoint, HttpServletRequest request, Object response, Throwable ex,
                            String memo) {
        apiLogService.insert(new ApiLog(
                null,
                memo,
@@ -129,8 +134,7 @@
                1,
                new Date(),
                null,
                null
        ));
                null));
    }
    private Object buildErrorResponse(Throwable ex) {
src/main/java/com/zy/system/controller/LicenseCreatorController.java
@@ -62,7 +62,11 @@
     */
    @RequestMapping(value = "/getLicenseDays")
    public R getLicenseDays() {
        return R.ok(licenseTimer.getLicenseDays());
        int licenseDays = licenseTimer.getLicenseDays();
        if (!licenseTimer.getSystemSupport()) {
            licenseDays = -1;
        }
        return R.ok().add(licenseDays);
    }
    @RequestMapping(value = "/updateLicense")
src/main/java/com/zy/system/timer/LicenseTimer.java
@@ -18,9 +18,9 @@
@Component
public class LicenseTimer {
    private static boolean SYSTEM_SUPPORT = false;//系统激活状态,默认关闭
    private static boolean SYSTEM_SUPPORT = false;// 系统激活状态,默认关闭
    private static int LICENSE_DAYS = 0;//许可证天数
    private static int LICENSE_DAYS = 0;// 许可证天数
    /**
     * 证书subject
@@ -55,7 +55,7 @@
    @Autowired
    private LicenseInfosService licenseInfosService;
    //每天晚上11点更新系统激活状态
    // 每天晚上11点更新系统激活状态
    @Scheduled(cron = "0 0 23 * * ? ")
    public void timer() {
        try {
@@ -75,12 +75,12 @@
        try {
            AbstractServerInfos abstractServerInfos = null;
            String osName = System.getProperty("os.name");
            //根据不同操作系统类型选择不同的数据获取方法
            // 根据不同操作系统类型选择不同的数据获取方法
            if (osName.startsWith("windows")) {
                abstractServerInfos = new WindowsServerInfos();
            } else if (osName.startsWith("linux")) {
                abstractServerInfos = new LinuxServerInfos();
            }else{//其他服务器类型
            } else {// 其他服务器类型
                abstractServerInfos = new WindowsServerInfos();
            }
            LicenseCheck serverInfos = abstractServerInfos.getServerInfos();
@@ -123,9 +123,9 @@
        param.setLicensePath(licensePath);
        param.setPublicKeysStorePath(publicKeysStorePath);
        //验证许可证是否有效
        // 验证许可证是否有效
        LicenseVerify licenseVerify = new LicenseVerify();
        //安装证书
        // 安装证书
        LicenseContent install = licenseVerify.install(param, latestLicense.getLicense());
        if (install != null) {
@@ -133,11 +133,11 @@
            Date end = install.getNotAfter();
            Long starTime = start.getTime();
            Long endTime = end.getTime();
            long num = endTime - starTime;//时间戳相差的毫秒数
            long num = endTime - starTime;// 时间戳相差的毫秒数
            int day = (int) (num / 24 / 60 / 60 / 1000);
            setLicenseDays(day);
            setSystemSupport(true);
        }else {
        } else {
            setLicenseDays(0);
            setSystemSupport(false);
        }
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 1.0.0
  version: 1.0.1
  version-type: dev  # stable 或 dev
server:
src/main/webapp/views/index.html
@@ -7,7 +7,7 @@
  <meta name="renderer" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
        content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <link rel="stylesheet" href="../static/layui/css/layui.css" media="all">
  <link rel="stylesheet" href="../static/css/admin.css?v=318" media="all">
  <link rel="stylesheet" href="../static/css/loader.css" media="all">
@@ -83,386 +83,390 @@
</head>
<body class="layui-layout-body">
  <div class="layui-layout layui-layout-admin">
    <!-- 头部 -->
    <div class="layui-header">
      <div class="layui-logo">
        <img src="../static/images/zy-logo-dark.png" style="display: inline-block; width: 60%;height: auto">
        <!--          <span style="margin-top: 0; letter-spacing: 10px">中扬立库</span>-->
        <!--          <img src="../static/image/logo.svg"/>-->
        <!--          <cite>中扬 - Zoneyung</cite>-->
      </div>
<div class="layui-layout layui-layout-admin">
  <!-- 头部 -->
  <div class="layui-header">
    <div class="layui-logo">
      <img src="../static/images/zy-logo-dark.png" style="display: inline-block; width: 60%;height: auto">
      <!--          <span style="margin-top: 0; letter-spacing: 10px">中扬立库</span>-->
      <!--          <img src="../static/image/logo.svg"/>-->
      <!--          <cite>中扬 - Zoneyung</cite>-->
    </div>
      <ul class="layui-nav layui-layout-left">
        <li class="layui-nav-item" lay-unselect>
          <a ew-event="flexible" title="侧边伸缩"><i class="layui-icon layui-icon-shrink-right"></i></a>
        </li>
        <li class="layui-nav-item" lay-unselect>
          <a ew-event="refresh" title="刷新"><i class="layui-icon layui-icon-refresh-3"></i></a>
        </li>
      </ul>
      <ul class="layui-nav layui-layout-right">
        <!--      <li class="layui-nav-item" lay-unselect>-->
        <!--        <a ew-event="note" title="便签"><i class="layui-icon layui-icon-note"></i></a>-->
        <!--      </li>-->
        <li class="layui-nav-item" lay-unselect id="fakeShow"
    <ul class="layui-nav layui-layout-left">
      <li class="layui-nav-item" lay-unselect>
        <a ew-event="flexible" title="侧边伸缩"><i class="layui-icon layui-icon-shrink-right"></i></a>
      </li>
      <li class="layui-nav-item" lay-unselect>
        <a ew-event="refresh" title="刷新"><i class="layui-icon layui-icon-refresh-3"></i></a>
      </li>
    </ul>
    <ul class="layui-nav layui-layout-right">
      <!--      <li class="layui-nav-item" lay-unselect>-->
      <!--        <a ew-event="note" title="便签"><i class="layui-icon layui-icon-note"></i></a>-->
      <!--      </li>-->
      <li class="layui-nav-item" lay-unselect id="fakeShow"
          style="display: none;user-select: none;margin-right: 10px;">
          <div style="color: red;" id="fakeShowText">仿真模拟运行中</div>
        </li>
        <li class="layui-nav-item" lay-unselect id="licenseShow" style="display: none;user-select: none;">
          <div style="color: red;">临时许可证有效期:<span id="licenseDays">29</span>天</div>
        </li>
        <li class="layui-nav-item layui-hide-xs" lay-unselect>
          <a ew-event="fullScreen" title="全屏"><i class="layui-icon layui-icon-screen-full"></i></a>
        </li>
        <li class="layui-nav-item" lay-unselect>
          <a>
            <cite id="username" style="margin-right: 5px">管理员</cite>
          </a>
          <dl class="layui-nav-child">
            <dd lay-unselect><a ew-href="detail.html?resourceId=8">基本资料</a></dd>
            <hr>
            <dd lay-unselect><a id="logout">退出</a></dd>
          </dl>
        </li>
        <li class="layui-nav-item" lay-unselect>
          <a ew-event="theme" title="主题"><i class="layui-icon layui-icon-more-vertical"></i></a>
        </li>
        <div style="color: red;" id="fakeShowText">仿真模拟运行中</div>
      </li>
      <li class="layui-nav-item" lay-unselect id="licenseShow" style="display: none;user-select: none;">
        <div style="color: red;">临时许可证有效期:<span id="licenseDays">29</span>天</div>
      </li>
      <li class="layui-nav-item layui-hide-xs" lay-unselect>
        <a ew-event="fullScreen" title="全屏"><i class="layui-icon layui-icon-screen-full"></i></a>
      </li>
      <li class="layui-nav-item" lay-unselect>
        <a>
          <cite id="username" style="margin-right: 5px">管理员</cite>
        </a>
        <dl class="layui-nav-child">
          <dd lay-unselect><a ew-href="detail.html?resourceId=8">基本资料</a></dd>
          <hr>
          <dd lay-unselect><a id="logout">退出</a></dd>
        </dl>
      </li>
      <li class="layui-nav-item" lay-unselect>
        <a ew-event="theme" title="主题"><i class="layui-icon layui-icon-more-vertical"></i></a>
      </li>
    </ul>
  </div>
  <!-- 侧边栏 -->
  <div class="layui-side">
    <div class="layui-side-scroll">
      <ul id="menu-main" class="layui-nav layui-nav-tree arrow2" lay-filter="admin-side-nav" lay-shrink="_all">
      </ul>
    </div>
    <!-- 侧边栏 -->
    <div class="layui-side">
      <div class="layui-side-scroll">
        <ul id="menu-main" class="layui-nav layui-nav-tree arrow2" lay-filter="admin-side-nav" lay-shrink="_all">
        </ul>
      </div>
    </div>
    <!-- 主体部分 -->
    <div class="layui-body"></div>
    <!-- 底部 -->
    <div class="layui-footer layui-text">
      copyright © 2026 浙江中扬立库技术有限公司 all rights reserved.
      <span class="pull-right" id="system-version">Version loading...</span>
    </div>
  </div>
  <!--初始化加载层-->
  <div class="layuimini-loader">
    <div class="layuimini-loader-inner"></div>
  <!-- 主体部分 -->
  <div class="layui-body"></div>
  <!-- 底部 -->
  <div class="layui-footer layui-text">
    copyright © 2026 浙江中扬立库技术有限公司 all rights reserved.
    <span class="pull-right" id="system-version">Version loading...</span>
  </div>
  <!-- 弹窗内容 -->
  <div class="popup" id="popup">
    <div class="popup-content">
      <h2 style="font-size: 28px;margin-bottom: 10px;">许可证即将过期</h2>
      <div id="popup-text" style="font-size: 28px;color: red"></div>
      <button
        style="background-color: #007bff;color: #fff;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;font-size: 16px;"
        onclick="hidePopup()">关闭</button>
    </div>
</div>
<!--初始化加载层-->
<div class="layuimini-loader">
  <div class="layuimini-loader-inner"></div>
</div>
<!-- 弹窗内容 -->
<div class="popup" id="popup">
  <div class="popup-content">
    <h2 style="font-size: 28px;margin-bottom: 10px;">许可证即将过期</h2>
    <div id="popup-text" style="font-size: 28px;color: red"></div>
    <button
            style="background-color: #007bff;color: #fff;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;font-size: 16px;"
            onclick="hidePopup()">关闭</button>
  </div>
</div>
  <!-- 右下角SVG动画 -->
  <div id="ai-assistant-btn" style="position: fixed; bottom: 40px; right: 20px; z-index: 9999; cursor: pointer;">
  </div>
<!-- 右下角SVG动画 -->
<div id="ai-assistant-btn" style="position: fixed; bottom: 40px; right: 20px; z-index: 9999; cursor: pointer;">
</div>
  <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
  <script type="text/javascript" src="../static/layui/layui.js"></script>
  <script type="text/javascript" src="../static/js/handlebars/handlebars-v4.5.3.js"></script>
  <script type="text/javascript" src="../static/js/common.js"></script>
  <script>
    // 版本信息变量
    var systemVersion = '...';
    var systemVersionType = '';
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/layui/layui.js"></script>
<script type="text/javascript" src="../static/js/handlebars/handlebars-v4.5.3.js"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script>
  // 版本信息变量
  var systemVersion = '...';
  var systemVersionType = '';
    // 加载系统版本信息
    function loadSystemVersion() {
  // 加载系统版本信息
  function loadSystemVersion() {
    $.ajax({
      url: baseUrl + "/openapi/getSystemVersion",
      headers: { 'token': localStorage.getItem('token') },
      method: 'GET',
      success: function (res) {
        if (res.code === 200) {
          systemVersion = res.data.version;
          systemVersionType = res.data.versionType;
          var versionTypeLabel = systemVersionType === 'stable' ? '' : ' (' + systemVersionType + ')';
          var versionTypeColor = systemVersionType === 'stable' ? 'rgb(25,190,107)' : 'rgb(245,166,35)';
          // 更新页脚版本显示
          $('#system-version').html('Version ' + systemVersion + '<span style="margin-left:5px;padding:1px 6px;font-size:12px;border-radius:3px;background-color:' + versionTypeColor + ';color:#fff;">' + systemVersionType + '</span>');
          // 控制台输出版本信息
          console.log('%c 中扬立库平台 %c ' + systemVersion + ' %c ' + systemVersionType + ' ', 'background-color:rgb(53,73,94);color: #fff;border-radius:2px 0 0 2px;padding:2px 4px;', 'background-color:rgb(25,190,107);color: #fff;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;', 'background-color:' + versionTypeColor + ';color: #fff;border-radius:0 2px 2px 0;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;');
        }
      }
    });
  }
  $(function () {
    // 注入AI助手图标
    $('#ai-assistant-btn').html(getAiIconHtml(60, 60));
    if ("" === localStorage.getItem('token')) {
      top.location.href = baseUrl + "/login";
    }
    // 加载版本信息
    loadSystemVersion();
  });
  // 显示弹窗
  function showPopup(res) {
    document.getElementById('popup').style.display = 'block';
    // 获取弹出窗口内容的容器元素
    var popupText = document.getElementById('popup-text');
    // 假设后台返回的字符串为 responseString
    if (res !== "") {
      // 获取当前日期
      const currentDate = new Date();
      // 创建新日期对象并添加天数
      const newDate = new Date();
      newDate.setDate(currentDate.getDate() + res + 1);
      // 将字符串设置为弹窗内容的文本
      popupText.textContent = "许可证将于" + new Intl.DateTimeFormat('zh-CN').format(newDate) + "过期,剩余有效期:" + res + "天!";
    } else {
      document.getElementById('popup').style.display = 'none';
    }
  }
  // 隐藏弹窗
  function hidePopup() {
    document.getElementById('popup').style.display = 'none';
  }
  layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
  }).extend({
    notice: 'notice/notice',
  }).use(['index', 'element', 'layer', 'admin', 'notice'], function () {
    var $ = layui.jquery;
    var index = layui.index;
    var element = layui.element;
    var layer = layui.layer;
    var admin = layui.admin;
    var notice = layui.notice;
    var easywebIframeMsg = localStorage.getItem("easyweb-iframe");
    if (!isEmpty(easywebIframeMsg)) {
      var easywebIframeObj = JSON.parse(easywebIframeMsg);
      if (easywebIframeObj.defaultTheme === undefined) {
        admin.changeTheme("theme-colorful");
      }
    }
    let fakeRunning = false
    let fakeStatusInterval = null
    function checkFakeStatus() {
      $.ajax({
        url: baseUrl + "/openapi/getSystemVersion",
        url: baseUrl + "/openapi/getFakeSystemRunStatus",
        headers: { 'token': localStorage.getItem('token') },
        method: 'GET',
        success: function (res) {
          if (res.code === 200) {
            systemVersion = res.data.version;
            systemVersionType = res.data.versionType;
            var versionTypeLabel = systemVersionType === 'stable' ? '' : ' (' + systemVersionType + ')';
            var versionTypeColor = systemVersionType === 'stable' ? 'rgb(25,190,107)' : 'rgb(245,166,35)';
            // 更新页脚版本显示
            $('#system-version').html('Version ' + systemVersion + '<span style="margin-left:5px;padding:1px 6px;font-size:12px;border-radius:3px;background-color:' + versionTypeColor + ';color:#fff;">' + systemVersionType + '</span>');
            // 控制台输出版本信息
            console.log('%c 中扬立库平台 %c ' + systemVersion + ' %c ' + systemVersionType + ' ', 'background-color:rgb(53,73,94);color: #fff;border-radius:2px 0 0 2px;padding:2px 4px;', 'background-color:rgb(25,190,107);color: #fff;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;', 'background-color:' + versionTypeColor + ';color: #fff;border-radius:0 2px 2px 0;padding:2px 4px;font: 9pt "Apercu Regular", Georgia, "Times New Roman", Times, serif;');
          }
        }
      });
    }
    $(function () {
      // 注入AI助手图标
      $('#ai-assistant-btn').html(getAiIconHtml(60, 60));
      if ("" === localStorage.getItem('token')) {
        top.location.href = baseUrl + "/login";
      }
      // 加载版本信息
      loadSystemVersion();
    });
    // 显示弹窗
    function showPopup(res) {
      document.getElementById('popup').style.display = 'block';
      // 获取弹出窗口内容的容器元素
      var popupText = document.getElementById('popup-text');
      // 假设后台返回的字符串为 responseString
      if (res !== "") {
        // 获取当前日期
        const currentDate = new Date();
        // 创建新日期对象并添加天数
        const newDate = new Date();
        newDate.setDate(currentDate.getDate() + res + 1);
        // 将字符串设置为弹窗内容的文本
        popupText.textContent = "许可证将于" + new Intl.DateTimeFormat('zh-CN').format(newDate) + "过期,剩余有效期:" + res + "天!";
      } else {
        document.getElementById('popup').style.display = 'none';
      }
    }
    // 隐藏弹窗
    function hidePopup() {
      document.getElementById('popup').style.display = 'none';
    }
    layui.config({
      base: baseUrl + "/static/layui/lay/modules/"
    }).extend({
      notice: 'notice/notice',
    }).use(['index', 'element', 'layer', 'admin', 'notice'], function () {
      var $ = layui.jquery;
      var index = layui.index;
      var element = layui.element;
      var layer = layui.layer;
      var admin = layui.admin;
      var notice = layui.notice;
      var easywebIframeMsg = localStorage.getItem("easyweb-iframe");
      if (!isEmpty(easywebIframeMsg)) {
        var easywebIframeObj = JSON.parse(easywebIframeMsg);
        if (easywebIframeObj.defaultTheme === undefined) {
          admin.changeTheme("theme-colorful");
        }
      }
      let fakeRunning = false
      let fakeStatusInterval = null
      function checkFakeStatus() {
        $.ajax({
          url: baseUrl + "/openapi/getFakeSystemRunStatus",
          headers: { 'token': localStorage.getItem('token') },
          method: 'GET',
          success: function (res) {
            if (res.code === 200) {
              if (res.data.isFake) {
                $("#fakeShow").show()
                let running = res.data.running
                if (running) {
                  $("#fakeShowText").text("仿真模拟运行中")
                } else {
                  $("#fakeShowText").text("仿真模拟未运行")
                }
                fakeRunning = running
                if (!fakeStatusInterval) {
                  fakeStatusInterval = setInterval(checkFakeStatus, 1000);
                }
            if (res.data.isFake) {
              $("#fakeShow").show()
              let running = res.data.running
              if (running) {
                $("#fakeShowText").text("仿真模拟运行中")
              } else {
                $("#fakeShow").hide()
                if (fakeStatusInterval) {
                  clearInterval(fakeStatusInterval);
                  fakeStatusInterval = null;
                }
                $("#fakeShowText").text("仿真模拟未运行")
              }
              fakeRunning = running
              if (!fakeStatusInterval) {
                fakeStatusInterval = setInterval(checkFakeStatus, 1000);
              }
            } else {
              top.location.href = baseUrl + "/login";
              $("#fakeShow").hide()
              if (fakeStatusInterval) {
                clearInterval(fakeStatusInterval);
                fakeStatusInterval = null;
              }
            }
          } else {
            top.location.href = baseUrl + "/login";
          }
        }
      });
    }
    checkFakeStatus();
    $("#fakeShow").on("click", function () {
      if (fakeRunning) {
        layer.confirm('确定要停止仿真模拟吗?', function (index) {
          layer.close(index);
          $.ajax({
            url: baseUrl + "/openapi/stopFakeSystem",
            headers: { 'token': localStorage.getItem('token') },
            method: 'POST',
            success: function (res) {
              if (res.code === 200) {
                layer.msg("仿真模拟已停止", { icon: 1 });
                $("#fakeShowText").text("仿真模拟未运行")
              } else {
                layer.msg(res.msg, { icon: 2 });
              }
            }
          });
        });
      } else {
        layer.confirm('确定要启动仿真模拟吗?', function (index) {
          layer.close(index);
          $.ajax({
            url: baseUrl + "/openapi/startFakeSystem",
            headers: { 'token': localStorage.getItem('token') },
            method: 'POST',
            success: function (res) {
              if (res.code === 200) {
                layer.msg("仿真模拟已启动", { icon: 1 });
                $("#fakeShowText").text("仿真模拟运行中")
              } else {
                layer.msg(res.msg, { icon: 2 });
              }
            }
          });
        });
      }
      checkFakeStatus();
      $("#fakeShow").on("click", function () {
        if (fakeRunning) {
          layer.confirm('确定要停止仿真模拟吗?', function (index) {
            layer.close(index);
            $.ajax({
              url: baseUrl + "/openapi/stopFakeSystem",
              headers: { 'token': localStorage.getItem('token') },
              method: 'POST',
              success: function (res) {
                if (res.code === 200) {
                  layer.msg("仿真模拟已停止", { icon: 1 });
                  $("#fakeShowText").text("仿真模拟未运行")
                } else {
                  layer.msg(res.msg, { icon: 2 });
                }
              }
            });
          });
        } else {
          layer.confirm('确定要启动仿真模拟吗?', function (index) {
            layer.close(index);
            $.ajax({
              url: baseUrl + "/openapi/startFakeSystem",
              headers: { 'token': localStorage.getItem('token') },
              method: 'POST',
              success: function (res) {
                if (res.code === 200) {
                  layer.msg("仿真模拟已启动", { icon: 1 });
                  $("#fakeShowText").text("仿真模拟运行中")
                } else {
                  layer.msg(res.msg, { icon: 2 });
                }
              }
            });
          });
        }
      });
      $.ajax({
        url: baseUrl + "/menu/auth",
        headers: { 'token': localStorage.getItem('token') },
        method: 'POST',
        // async: false,
        success: function (res) {
          // 关闭加载动画
          $('.layuimini-loader').fadeOut();
          if (res.code === 200) {
            var tpl = $('#menuTpl').html();
            var template = Handlebars.compile(tpl);
            var html = template(res);
            $("#menu-main").html(html);
            element.init();
          } else if (res.code === 403) {
            top.location.href = baseUrl + "/login";
          } else {
            layer.msg(res.msg, { icon: 2 });
          }
        }
      });
      $.ajax({
        url: baseUrl + "/license/getLicenseDays",
        headers: { 'token': localStorage.getItem('token') },
        method: 'POST',
        success: function (res) {
          if (res.code == 200) {
            let days = res.data
            if (days <= 30) {
              $("#licenseShow").show()
              $("#licenseDays").html(days)
            }
            if (days <= 15) {
              showPopup(days)
            }
          } else {
            top.location.href = baseUrl + "/login";
          }
        }
      });
      // 默认加载主页
      index.loadHome({
        menuPath: baseUrl + '/views/watch/console.html',
        menuName: '<i class="layui-icon layui-icon-home"></i>'
      });
      $('#username').text(localStorage.getItem('username'));
      $(document).on('click', '#logout', function () {
        window.location.href = "login.html";
        localStorage.removeItem('token');
        localStorage.removeItem('username');
        admin.closeAllTabs();
      });
      // 替换退出按钮变量
      var logout = document.getElementById('logout');
      var url = logout.getAttribute('href');
      logout.setAttribute('href', baseUrl + "/login");
      // AI助手弹窗索引
      var aiLayerIndex = null;
      // AI助手图标悬浮提示
      $('#ai-assistant-btn').on('mouseenter', function () {
        this.index = layer.tips('AI助手', this, {
          tips: [1, '#333'], // 上方显示,深色背景
          time: -1 // 不自动关闭
        });
      }).on('mouseleave', function () {
        layer.close(this.index);
      }).on('click', function () {
        // 如果已经打开过且未销毁,直接显示
        if (aiLayerIndex !== null && $('#layui-layer' + aiLayerIndex).length > 0) {
          var $layero = $('#layui-layer' + aiLayerIndex);
          var $shade = $('#layui-layer-shade' + aiLayerIndex);
          // 显示并重置状态
          $shade.show().css('opacity', 0.1);
          $layero.show();
          // 重新触发进入动画
          $layero.removeClass('ai-drawer-layer-close');
          $layero.removeClass('ai-drawer-layer');
          void $layero.get(0).offsetWidth; // 触发重绘
          $layero.addClass('ai-drawer-layer');
          return;
        }
        layer.open({
          type: 2,
          title: false, // 隐藏默认标题栏,更简洁
          closeBtn: 0, // 隐藏关闭按钮,点击遮罩关闭
          shadeClose: false, // 改为手动控制关闭,以便播放动画
          shade: 0.1,
          area: ['600px', '100%'],
          offset: 'r', // 右侧悬浮
          anim: -1, // 禁用默认动画,使用CSS动画
          isOutAnim: false,
          skin: 'ai-drawer-layer', // 自定义皮肤
          content: 'ai/diagnosis.html',
          success: function (layero, index) {
            aiLayerIndex = index; // 记录索引
            // 背景模糊效果
            var shadeId = layero.attr('id').replace('layui-layer', 'layui-layer-shade');
            var $shade = $('#' + shadeId);
            $shade.css({
              'backdrop-filter': 'blur(3px)',
              'transition': 'opacity 0.8s'
            });
            // 点击遮罩关闭(带动画)
            $shade.on('click', function () {
              layero.addClass('ai-drawer-layer-close');
              $shade.css('opacity', 0);
              setTimeout(function () {
                // layer.close(index); // 不销毁,改为隐藏
                layero.hide();
                $shade.hide();
              }, 400);
            });
          }
        });
      });
    });
  </script>
  <script type="text/html" id="menuTpl">
    $.ajax({
      url: baseUrl + "/menu/auth",
      headers: { 'token': localStorage.getItem('token') },
      method: 'POST',
      // async: false,
      success: function (res) {
        // 关闭加载动画
        $('.layuimini-loader').fadeOut();
        if (res.code === 200) {
          var tpl = $('#menuTpl').html();
          var template = Handlebars.compile(tpl);
          var html = template(res);
          $("#menu-main").html(html);
          element.init();
        } else if (res.code === 403) {
          top.location.href = baseUrl + "/login";
        } else {
          layer.msg(res.msg, { icon: 2 });
        }
      }
    });
    $.ajax({
      url: baseUrl + "/license/getLicenseDays",
      headers: { 'token': localStorage.getItem('token') },
      method: 'POST',
      success: function (res) {
        if (res.code == 200) {
          let days = res.data
          if (days <= 30) {
            $("#licenseShow").show()
            $("#licenseDays").html(days)
          }
          if (days <= 15) {
            showPopup(days)
          }
          if (days < 0) {
            top.location.href = baseUrl + "/login";
          }
        } else {
          top.location.href = baseUrl + "/login";
        }
      }
    });
    // 默认加载主页
    index.loadHome({
      menuPath: baseUrl + '/views/watch/console.html',
      menuName: '<i class="layui-icon layui-icon-home"></i>'
    });
    $('#username').text(localStorage.getItem('username'));
    $(document).on('click', '#logout', function () {
      window.location.href = "login.html";
      localStorage.removeItem('token');
      localStorage.removeItem('username');
      admin.closeAllTabs();
    });
    // 替换退出按钮变量
    var logout = document.getElementById('logout');
    var url = logout.getAttribute('href');
    logout.setAttribute('href', baseUrl + "/login");
    // AI助手弹窗索引
    var aiLayerIndex = null;
    // AI助手图标悬浮提示
    $('#ai-assistant-btn').on('mouseenter', function () {
      this.index = layer.tips('AI助手', this, {
        tips: [1, '#333'], // 上方显示,深色背景
        time: -1 // 不自动关闭
      });
    }).on('mouseleave', function () {
      layer.close(this.index);
    }).on('click', function () {
      // 如果已经打开过且未销毁,直接显示
      if (aiLayerIndex !== null && $('#layui-layer' + aiLayerIndex).length > 0) {
        var $layero = $('#layui-layer' + aiLayerIndex);
        var $shade = $('#layui-layer-shade' + aiLayerIndex);
        // 显示并重置状态
        $shade.show().css('opacity', 0.1);
        $layero.show();
        // 重新触发进入动画
        $layero.removeClass('ai-drawer-layer-close');
        $layero.removeClass('ai-drawer-layer');
        void $layero.get(0).offsetWidth; // 触发重绘
        $layero.addClass('ai-drawer-layer');
        return;
      }
      layer.open({
        type: 2,
        title: false, // 隐藏默认标题栏,更简洁
        closeBtn: 0, // 隐藏关闭按钮,点击遮罩关闭
        shadeClose: false, // 改为手动控制关闭,以便播放动画
        shade: 0.1,
        area: ['600px', '100%'],
        offset: 'r', // 右侧悬浮
        anim: -1, // 禁用默认动画,使用CSS动画
        isOutAnim: false,
        skin: 'ai-drawer-layer', // 自定义皮肤
        content: 'ai/diagnosis.html',
        success: function (layero, index) {
          aiLayerIndex = index; // 记录索引
          // 背景模糊效果
          var shadeId = layero.attr('id').replace('layui-layer', 'layui-layer-shade');
          var $shade = $('#' + shadeId);
          $shade.css({
            'backdrop-filter': 'blur(3px)',
            'transition': 'opacity 0.8s'
          });
          // 点击遮罩关闭(带动画)
          $shade.on('click', function () {
            layero.addClass('ai-drawer-layer-close');
            $shade.css('opacity', 0);
            setTimeout(function () {
              // layer.close(index); // 不销毁,改为隐藏
              layero.hide();
              $shade.hide();
            }, 400);
          });
        }
      });
    });
  });
</script>
<script type="text/html" id="menuTpl">
  {{#each data}}
  <li class="layui-nav-item">
    <a><i class="layui-icon {{this.menuIcon}}"></i>&emsp;<cite>{{this.menu}}</cite></a>