<script setup lang="ts">
import {
  ProLayout,
  getMenuData,
  clearMenuItem,
  MenuDataItem,
} from 'antdv-pro-layout';
import RightContent from './components/RightContent.vue';
import Tabs from './components/Tabs.vue';
import GlobalMask from '@/components/GlobalMask/index.vue';
import ForcePasswdChange from '@/components/ForcePasswdChange/index.vue';
import { scriptUrl } from '@/assets/js/icon_font_8d5l8fzk5b87iudi';
import {
  computed,
  nextTick,
  onMounted,
  onUnmounted,
  reactive,
  watch,
} from 'vue';
import { useRouter } from 'vue-router';
import useLayoutStore from '@/store/modules/layout';
import useAppStore from '@/store/modules/app';
import useNeListStore from '@/store/modules/ne_list';
import useRouterStore from '@/store/modules/router';
import useTabsStore from '@/store/modules/tabs';
import useI18n from '@/hooks/useI18n';
import useAlarmStore from '@/store/modules/alarm';
import { getServerTime } from '@/api';
import { MENU_PATH_INLINE } from '@/constants/menu-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { getTimezoneOffset, parseDateUTCToStr } from '@/utils/date-utils';
import { parseUrlPath } from '@/plugins/file-static-url';
const { proConfig, waterMarkContent } = useLayoutStore();
const { t, currentLocale } = useI18n();
const routerStore = useRouterStore();
const neListStore = useNeListStore();
const tabsStore = useTabsStore();
const appStore = useAppStore();
const router = useRouter();

/**菜单面板 */
const layoutState = reactive({
  collapsed: false, // 是否展开菜单面板
  openKeys: ['/'], // 打开菜单key
  selectedKeys: ['/index'], // 选中高亮菜单key
});

/**监听路由变化改变菜单面板选项 */
watch(
  router.currentRoute,
  v => {
    const matched = v.matched.concat();
    layoutState.openKeys = matched
      .filter(r => r.path !== v.path)
      .map(r => r.path);
    layoutState.selectedKeys = matched
      .filter(r => r.name !== 'Root')
      .map(r => r.path);
    // 路由地址含有内嵌地址标识又是隐藏菜单需要处理打开高亮菜单栏
    if (v.path.includes(MENU_PATH_INLINE) && v.meta.hideInMenu) {
      const idx = v.path.lastIndexOf(MENU_PATH_INLINE);
      layoutState.openKeys = layoutState.selectedKeys.slice(0, -1);
      layoutState.selectedKeys[matched.length - 1] = v.path.slice(0, idx);
    }
  },
  { immediate: true }
);

// 动态路由添加到菜单面板
// const rootRoute = router.getRoutes().find(r => r.name === 'Root');
// if (rootRoute) {
//   const children = routerStore.setRootRouterData(rootRoute.children);
//   const buildRouterData = routerStore.buildRouterData;
//   if (buildRouterData.length > 0) {
//     rootRoute.children = children.concat(buildRouterData);
//   } else {
//     rootRoute.children = children;
//   }
// }
//
// const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));
//
const menuData = computed(() => {
  const rootRoute = router.getRoutes().find(r => r.name === 'Root');
  if (rootRoute) {
    const children = routerStore.setRootRouterData(rootRoute.children);
    const buildRouterData = routerStore.buildRouterData;
    if (buildRouterData.length > 0) {
      rootRoute.children = children.concat(buildRouterData);
    } else {
      rootRoute.children = children;
    }
  }
  const neTypes = neListStore.getNeSelectOtions.map(v => v.value);
  let routes = clearMenuItem(router.getRoutes());
  routes = routerStore.clearMenuItemByNeList(routes, neTypes);
  const { menuData } = getMenuData(routes);
  return menuData;
});

/**面包屑数据对象，排除根节点和首页不显示 */
const breadcrumb = computed(() => {
  const matched = router.currentRoute.value.matched.concat();
  // 菜单中隐藏子节点不显示面包屑
  if (matched.length == 2) {
    const hideChildInMenu = matched[0].meta?.hideChildInMenu || false;
    if (hideChildInMenu) {
      return [];
    }
  }
  return matched
    .filter(r => !['Root', 'Index'].includes(r.name as string))
    .map(item => {
      return {
        path: item.path,
        breadcrumbName: fnLocale(item),
      };
    });
});

/**
 * 给页面组件设置路由名称
 *
 * 路由名称设为缓存key
 * @param component 页面组件
 * @param name 路由名称
 */
function fnComponentSetName(component: any, to: any) {
  if (component && component.type) {
    // 通过路由取最后匹配的，确认是缓存的才处理
    const matched = to.matched.concat();
    const lastRoute = matched[matched.length - 1];
    if (!lastRoute.meta.cache) return component;
    const routeName = lastRoute.name;
    const routeDef = lastRoute.components.default;
    // 有命名但不是跳转的路由文件
    const __name = component.type.__name;
    if (__name && __name !== routeName) {
      routeDef.name = routeName;
      routeDef.__name = routeName;
      Reflect.set(component, 'type', routeDef);
      return component;
    }
  }
  return component;
}

// 清空导航栏标签
tabsStore.clear();

// LOGO地址
const logoUrl = computed(() => {
  let url =
    appStore.logoType === 'brand'
      ? parseUrlPath(appStore.filePathBrand)
      : parseUrlPath(appStore.filePathIcon);

  if (url.indexOf('{language}') === -1) {
    return url;
  }
  // 语言参数替换
  const local = currentLocale.value;
  const lang = local.split('_')[0];
  return url.replace('{language}', lang);
});

/**系统使用手册跳转 */
function fnClickHelpDoc(language?: string) {
  const routeData = router.resolve({ name: 'HelpDoc' });
  let href = routeData.href;
  if (language) {
    href = `${routeData.href}?language=${language}`;
  }
  window.open(href, '_blank');
}

/**
 * 国际化翻译转换
 */
function fnLocale(m: MenuDataItem) {
  if (!m.meta) return;
  let title = m.meta?.title ?? '';
  if (title.indexOf('router.') !== -1) {
    title = t(title);
  }
  return title;
}

/**检查系统名称是否超出范围进行滚动 */
function fnCheckAppNameOverflow() {
  const container: HTMLDivElement | null = document.querySelector('.app-name');
  if (!container) return;
  const text: HTMLDivElement | null = container.querySelector('.marquee');
  if (!text) return;
  if (text.offsetWidth > container.offsetWidth) {
    text.classList.add('app-name_scrollable');
    text.setAttribute('data-content', text.innerText);
  } else {
    text.classList.remove('app-name_scrollable');
  }
}

watch(
  () => appStore.appName,
  () => nextTick(fnCheckAppNameOverflow)
);

//
onMounted(() => {
  fnCheckAppNameOverflow();
  fnGetServerTime();
  useAlarmStore().fnGetActiveAlarmInfo();
});

// ==== 服务器时间显示 start
let serverTimeInterval: any = null;
let serverTime = 0;
let serverTimeZone = getTimezoneOffset();
let activeAlarmRefresh = 0;

// 获取服务器时间
function fnGetServerTime() {
  getServerTime().then(res => {
    if (res.code === RESULT_CODE_SUCCESS && res.data) {
      clearInterval(serverTimeInterval);
      serverTimeInterval = null;
      serverTime = res.data.timestamp;
      serverTimeZone = res.data.timezone;
      // 用DOM直接修改
      const serverTimeDom = document.getElementById('serverTimeDom');
      serverTimeInterval = setInterval(() => {
        serverTime += 1000;
        activeAlarmRefresh += 1;
        if (serverTimeDom) {
          serverTimeDom.innerText = parseDateUTCToStr(
            serverTime,
            serverTimeZone
          );
        } else {
          clearInterval(serverTimeInterval);
          serverTimeInterval = null;
        }
        if (activeAlarmRefresh === 5) {
          useAlarmStore().fnGetActiveAlarmInfo();
          activeAlarmRefresh = 0;
        }
      }, 1000);
    }
  });
}

// 监听可视改变
document.addEventListener('visibilitychange', function () {
  if (document.visibilityState == 'hidden') {
    //切离该页面时执行
    clearInterval(serverTimeInterval);
    serverTimeInterval = null;
  }
  if (document.visibilityState == 'visible') {
    //切换到该页面时执行
    clearInterval(serverTimeInterval);
    serverTimeInterval = null;
    fnGetServerTime();
    useAlarmStore().fnGetActiveAlarmInfo();
  }
});

onUnmounted(() => {
  clearInterval(serverTimeInterval);
  serverTimeInterval = null;
});
// ==== 服务器时间显示 end
</script>

<template>
  <a-watermark :content="waterMarkContent" :z-index="100">
    <ProLayout
      v-model:collapsed="layoutState.collapsed"
      v-model:selectedKeys="layoutState.selectedKeys"
      v-model:openKeys="layoutState.openKeys"
      :menu-data="menuData"
      :breadcrumb="{ routes: breadcrumb }"
      v-bind="proConfig"
      :iconfont-url="scriptUrl"
      :locale="fnLocale"
    >
      <!--插槽-菜单头-->
      <template #menuHeaderRender>
        <RouterLink :to="{ name: 'Index' }" :replace="true">
          <template
            v-if="
              appStore.logoType === 'icon' ||
              (proConfig.layout === 'side' && layoutState.collapsed)
            "
          >
            <img
              class="logo-icon"
              :src="logoUrl"
              :alt="appStore.appName"
              :title="appStore.appName"
            />
            <h1 class="app-name" :title="appStore.appName">
              <span class="marquee app-name_scrollable">
                {{ appStore.appName }}
              </span>
            </h1>
          </template>
          <template v-if="appStore.logoType === 'brand'">
            <img
              class="logo-brand"
              :src="logoUrl"
              :alt="appStore.appName"
              :title="appStore.appName"
            />
          </template>
        </RouterLink>
      </template>

      <!--插槽-渲染顶部内容区域，仅布局side有效-->
      <template #headerContentRender></template>

      <!--插槽-渲染顶部内容右端区域-->
      <template #headerContentRightRender>
        <RightContent />
      </template>

      <!--插槽-导航标签项-->
      <template #tabRender="{ width, fixedHeader, headerRender }">
        <Tabs
          :width="width"
          :fixed-header="fixedHeader"
          :header-render="headerRender"
        />
      </template>

      <!--插槽-页面路由导航面包屑-->
      <template #breadcrumbRender="{ route, params, routes }">
        <span v-if="routes.indexOf(route) === routes.length - 1">
          {{ route.breadcrumbName }}
        </span>
        <RouterLink v-else :to="{ path: route.path, params }">
          {{ route.breadcrumbName }}
        </RouterLink>
      </template>

      <!--内容页面视图-->
      <RouterView v-slot="{ Component, route }">
        <transition name="slide-left" mode="out-in">
          <KeepAlive :include="tabsStore.getCaches">
            <component
              :is="fnComponentSetName(Component, route)"
              :key="route.path"
            />
          </KeepAlive>
        </transition>
      </RouterView>

      <!--插槽-内容底部-->
      <template #footerRender="{ width }">
        <footer class="footer">
          <div class="footer-fixed" :style="{ width }">
            <div style="flex: 1">
              <span>{{ appStore.copyright }}</span>
            </div>
            <a-space direction="horizontal" :size="8">
              <span id="serverTimeDom"></span>
              <a-button
                type="link"
                :href="appStore.officialUrl"
                target="_blank"
                size="small"
                v-perms:has="['system:setting:official']"
                v-if="appStore.officialUrl !== '#'"
              >
                {{ t('loayouts.basic.officialUrl') }}
              </a-button>
              <a-button
                type="link"
                size="small"
                v-perms:has="['system:setting:doc']"
                @click="fnClickHelpDoc()"
              >
                {{ t('loayouts.basic.helpDoc') }}
              </a-button>
            </a-space>
          </div>
        </footer>
      </template>
    </ProLayout>

    <!-- 全局遮罩 -->
    <GlobalMask />
    <!-- 强制密码修改 -->
    <ForcePasswdChange />
  </a-watermark>
</template>

<style lang="less" scoped>
.logo-icon {
  height: 32px;
  width: 32px;
  vertical-align: middle;
  border-style: none;
  border-radius: 6.66px;
}

.logo-brand {
  height: 48px;
  width: 174px;
  vertical-align: middle;
  border-style: none;
  margin-right: 16px;
}

.app-name {
  overflow: hidden;
  // text-overflow: ellipsis;
  white-space: nowrap;
  width: 148px;
  > .app-name_scrollable {
    padding-right: 12px;
    display: inline-block;
    animation: scrollable-animation linear 6s infinite both;
    &::after {
      content: attr(data-content);
      position: absolute;
      top: 0;
      right: -100%;
      transition: right 0.3s ease;
    }

    @keyframes scrollable-animation {
      0% {
        transform: translateX(0);
      }

      100% {
        transform: translateX(calc(-100% - 12px));
      }
    }
  }
}

.theme-light.theme-menu-light .app-name {
  color: #141414 !important;
}
.theme-light.theme-menu-dark .app-name {
  color: #fff !important;
}
.theme-dark.theme-menu-light .app-name {
  color: #fff !important;
}
.theme-dark.theme-menu-dark .app-name {
  color: #fff !important;
}

.footer {
  z-index: 16;
  margin: 0px;
  width: auto;
  margin-top: 32px;
  &-fixed {
    position: fixed;
    bottom: 0;
    right: 0;
    width: auto;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: space-between;
    align-items: center;
    padding: 4px 16px;
    background: #fff;
    box-shadow: 0 1px 4px #0015291f;
    transition: background 0.3s, width 0.2s;
    height: 32px;
  }

  [data-theme='dark'] &-fixed {
    background: #141414;
  }

  & #serverTimeDom {
    color: inherit;
    opacity: 0.85;
    transition: all 0.3s;
  }
}
</style>
