cuipengcheng / 云效助手

// ==UserScript==
// @name         云效助手
// @namespace    http://tampermonkey.net/
// @version      1.2.10
// @description  包含云效项目功能加强、自动登录、快速填写工时、工时提醒、视图列表过滤
// @author       温华
// @match        https://passport.aliyun.com/havanaone/login/login.htm*
// @match        https://devops.aliyun.com/*
// @match        https://codeup.aliyun.com/*
// @match        https://account.aliyun.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=aliyun.com
// @license      MIT
// @grant        GM_addStyle
// @grant        unsafeWindow
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @downloadURL https://openuserjs.org/install/cuipengcheng/云效助手.user.js
// @updateURL https://openuserjs.org/meta/cuipengcheng/云效助手.meta.js
// @copyright 2025, cuipengcheng (https://openuserjs.org/users/cuipengcheng)
// ==/UserScript==

(async function () {
  "use strict";
  let userName = "";
  let password = "";
  var bugIconHtml = `<span class="CardTable--cardIconBox--M9k3BUL"><span class="workitemCategoryIconBox CardTable--cardIcon--39ZirqJ"><i class="teamix-icon teamix-icon-quexian-zhubug-line teamix-medium" style="color: var(--color-error-5, #e84738);"><svg viewBox="0 0 1024 1024"><use xlink:href="#yunxiao-quexian-zhubug-line"></use></svg></i></span></span>`;
  var reqIconHtml = `<span class="CardTable--cardIconBox--M9k3BUL"><span class="workitemCategoryIconBox CardTable--cardIcon--39ZirqJ"><i class="teamix-icon teamix-icon-assign-line teamix-medium" style="color: var(--color-success-5, #23b066);"><svg viewBox="0 0 1024 1024"><use xlink:href="#yunxiao-assign-line"></use></svg></i></span></span>`;
  var taskIconHtml = `<span class="CardTable--cardIconBox--M9k3BUL"><span class="workitemCategoryIconBox CardTable--cardIcon--39ZirqJ"><i class="teamix-icon teamix-icon-file-2-line teamix-medium" style="color: rgb(84, 64, 182);"><svg viewBox="0 0 1024 1024"><use xlink:href="#yunxiao-file-2-line"></use></svg></i></span></span>`;
  var setIconHtml = `<button type="button" class="next-btn next-small next-btn-normal next-btn-text isOnlyIcon isOnlyIcon aone-biz-today-work-setting-line is-yunxiao"><i class="teamix-icon teamix-icon-setting-line teamix-medium"><svg viewBox="0 0 1024 1024"><use xlink:href="#yunxiao-setting-line"></use></svg></i></button>`;

  /**
   * 自动登录
   */
  {
    userName = await GM_getValue("userName", "");
    password = await GM_getValue("password", "");

    if (!userName && location.host !== "passport.aliyun.com") {
      createGetLoginInfo();
    }
    const nameDom = document.querySelector("#fm-login-id");
    const passwordDom = document.querySelector("#fm-login-password");
    if (nameDom && passwordDom) {
      nameDom.value = userName;
      passwordDom.value = password;
      document.querySelector(".password-login").click();
    }
  }

  /**
   * 视图列表过滤功能
   */
  const ViewFilter = {
    // 存储键名
    STORAGE_KEY: 'yunxiao_view_filter_settings',

    // 当前页面类型
    currentPageType: null,

    // 视图数据缓存
    viewsData: {
      personalViews: [],
      publicViews: []
    },

    // 获取当前页面类型
    getCurrentPageType() {
      const path = location.pathname;
      if (path.includes('/req')) return 'Req';
      if (path.includes('/workitem')) return 'Workitem';
      if (path.includes('/task')) return 'Task';
      if (path.includes('/bug')) return 'Bug';
      return null;
    },

    // 获取项目标识符
    getProjectId() {
      const match = location.pathname.match(/\/project\/([^\/]+)/);
      return match ? match[1] : null;
    },

    // 获取视图列表数据
    async fetchViewsData(pageType) {
      const projectId = this.getProjectId();
      if (!projectId || !pageType) return null;

      try {
        // 获取个人视图
        const personalResponse = await fetchApi(
          `https://devops.aliyun.com/projex/api/workitem/view/list/simpleInfo?spaceIdentifier=${projectId}&spaceType=Project&type=personalView&belong=${pageType}&_input_charset=utf-8`
        );

        // 获取公共视图
        const publicResponse = await fetchApi(
          `https://devops.aliyun.com/projex/api/workitem/view/list/simpleInfo?spaceIdentifier=${projectId}&spaceType=Project&type=publicView&belong=${pageType}&_input_charset=utf-8`
        );

        if (personalResponse.code === 200 && publicResponse.code === 200) {
          this.viewsData = {
            personalViews: personalResponse.result || [],
            publicViews: publicResponse.result || []
          };
          return this.viewsData;
        }
      } catch (error) {
        console.error('获取视图数据失败:', error);
      }
      return null;
    },

    // 获取过滤设置
    getFilterSettings() {
      const settings = localStorage.getItem(this.STORAGE_KEY);
      return settings ? JSON.parse(settings) : {};
    },

    // 保存过滤设置
    saveFilterSettings(pageType, hiddenViews) {
      const settings = this.getFilterSettings();
      settings[pageType] = hiddenViews;
      localStorage.setItem(this.STORAGE_KEY, JSON.stringify(settings));
    },

    // 获取滚动条设置
    getRemoveScrollbarSetting() {
      const setting = localStorage.getItem(this.STORAGE_KEY + '_scrollbar');
      return setting === 'true';
    },

    // 保存滚动条设置
    saveRemoveScrollbarSetting(remove) {
      localStorage.setItem(this.STORAGE_KEY + '_scrollbar', remove.toString());
    },

    // 创建过滤按钮
    createFilterButton() {
      // 尝试多个可能的位置
      const possibleHeaders = [
        '.teamix-cloud-sidebar-side-filter-header',
        '.sidebar-header',
        '.filter-header',
        '.view-header'
      ];

      let header = null;
      for (const selector of possibleHeaders) {
        header = document.querySelector(selector);
        if (header) break;
      }

      if (!header || document.querySelector('#view-filter-btn')) {
        return;
      }

      const filterBtn = document.createElement('button');
      filterBtn.id = 'view-filter-btn';
      filterBtn.className = 'next-btn next-small next-btn-normal next-btn-text';
      filterBtn.innerHTML = `
        <i class="teamix-icon teamix-icon-filter-line teamix-small">
          <svg viewBox="0 0 1024 1024" style="width: 14px; height: 14px;">
            <path d="M349.6 840.4c0 37.6 30.4 68 68 68s68-30.4 68-68v-84.8l316.8-380.2c12-14.4 18.4-32.8 18.4-51.6V256c0-17.6-14.4-32-32-32H235.2c-17.6 0-32 14.4-32 32v67.6c0 18.8 6.4 37.2 18.4 51.6l316.8 380.2v84.8l11.2 0.2z"/>
          </svg>
        </i>
        <span>过滤</span>
      `;

      // 添加自定义样式防止挤压
      GM_addStyle(`
        .teamix-cloud-sidebar-side-filter-header,
        .sidebar-header,
        .filter-header,
        .view-header {
          display: flex !important;
          align-items: center !important;
          flex-wrap: nowrap !important;
          gap: 4px !important;
        }

        #view-filter-btn {
          margin-left: 8px !important;
          padding: 0 8px !important;
          min-width: 60px !important;
          height: 24px !important;
          white-space: nowrap !important;
          flex-shrink: 0 !important;
          display: inline-flex !important;
          align-items: center !important;
          justify-content: center !important;
          gap: 4px !important;
          font-size: 12px !important;
          border: 1px solid #d9d9d9 !important;
          border-radius: 4px !important;
          background: #fff !important;
          color: #666 !important;
          cursor: pointer !important;
        }

        #view-filter-btn:hover {
          background: #f5f5f5 !important;
          border-color: #40a9ff !important;
          color: #40a9ff !important;
        }

        #view-filter-btn span {
          font-size: 12px !important;
          line-height: 1 !important;
        }

        #view-filter-btn i {
          flex-shrink: 0 !important;
          width: 14px !important;
          height: 14px !important;
        }

        #view-filter-btn svg {
          fill: currentColor !important;
        }
      `);

      // 添加点击事件
      filterBtn.onclick = (e) => {
        e.stopPropagation();
        this.toggleFilterPanel();
      };

      // 尝试插入到合适的位置
      const existingButtons = header.querySelectorAll('button, .next-btn');
      if (existingButtons.length > 0) {
        // 插入到最后一个按钮后面
        const lastButton = existingButtons[existingButtons.length - 1];
        lastButton.parentNode.insertBefore(filterBtn, lastButton.nextSibling);
      } else {
        header.appendChild(filterBtn);
      }
    },

    // 创建过滤面板
    createFilterPanel() {
      if (document.querySelector('#view-filter-panel')) return;

      const panel = document.createElement('div');
      panel.id = 'view-filter-panel';
      panel.className = 'view-filter-panel';
      panel.style.display = 'none';

      // 添加样式
      GM_addStyle(`
        .view-filter-panel {
          position: absolute;
          top: 100%;
          right: 0;
          width: 180px;
          background: #fff;
          border: 1px solid #d9d9d9;
          border-radius: 6px;
          box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
          z-index: 1000;
          padding: 16px;
          margin-top: 4px;
        }

        .view-filter-section {
          margin-bottom: 16px;
        }

        .view-filter-section:last-child {
          margin-bottom: 0;
        }

        .view-filter-title {
          font-size: 14px;
          font-weight: 500;
          color: #262626;
          margin-bottom: 8px;
          display: flex;
          align-items: center;
        }

        .view-filter-title .icon {
          margin-right: 4px;
          font-size: 14px;
        }

        .view-filter-item {
          display: flex;
          align-items: center;
          padding: 4px 0;
          font-size: 13px;
        }

        .view-filter-item input[type="checkbox"] {
          margin-right: 8px;
          cursor: pointer;
        }

        .view-filter-item label {
          cursor: pointer;
          flex: 1;
          color: #595959;
        }

        .view-filter-empty {
          color: #bfbfbf;
          font-size: 12px;
          padding: 8px 0;
          text-align: center;
        }

        .view-filter-footer {
          margin-top: 12px;
          padding-top: 8px;
          border-top: 1px solid #f0f0f0;
        }

        .view-filter-controls {
          display: flex;
          justify-content: flex-end;
        }

        .view-filter-controls .view-filter-item {
          margin: 0;
          padding: 0;
        }

        .view-filter-controls .view-filter-item input[type="checkbox"] {
          margin-right: 6px;
        }

        .view-filter-controls .view-filter-item label {
          font-size: 12px;
          color: #666;
          font-weight: normal;
        }
      `);

      document.body.appendChild(panel);

      // 点击外部关闭面板
      document.addEventListener('click', (e) => {
        if (!panel.contains(e.target) && !document.querySelector('#view-filter-btn').contains(e.target)) {
          panel.style.display = 'none';
        }
      });
    },

    // 切换过滤面板显示状态
    async toggleFilterPanel() {
      const panel = document.querySelector('#view-filter-panel');
      if (!panel) return;

      if (panel.style.display === 'none') {
        // 获取当前页面类型
        this.currentPageType = this.getCurrentPageType();
        if (!this.currentPageType) return;

        // 获取视图数据
        const viewsData = await this.fetchViewsData(this.currentPageType);
        if (!viewsData) return;

        // 更新面板内容
        this.updatePanelContent(panel);

        // 显示面板
        panel.style.display = 'block';

        // 设置位置
        const btn = document.querySelector('#view-filter-btn');
        const rect = btn.getBoundingClientRect();
        panel.style.position = 'fixed';
        panel.style.top = (rect.bottom + 4) + 'px';
        panel.style.right = (window.innerWidth - rect.right - 100) + 'px';
      } else {
        panel.style.display = 'none';
      }
    },

    // 更新面板内容
    updatePanelContent(panel) {
      const settings = this.getFilterSettings();
      const currentHidden = settings[this.currentPageType] || [];
      const removeScrollbar = this.getRemoveScrollbarSetting();

      let html = '';

      // 个人视图部分
      if (this.viewsData.personalViews.length > 0) {
        html += `
          <div class="view-filter-section">
            <div class="view-filter-title">
              <span class="icon">🔒</span>
              个人视图
            </div>
        `;

        this.viewsData.personalViews.forEach(view => {
          // 尝试多种匹配方式
          const isHidden = currentHidden.includes(view.identifier) ||
                          currentHidden.includes(view.displayName) ||
                          currentHidden.includes(view.name);
          html += `
            <div class="view-filter-item">
              <input type="checkbox" id="view_${view.identifier}" data-view-name="${view.displayName}" data-view-id="${view.identifier}" ${isHidden ? 'checked' : ''}>
              <label for="view_${view.identifier}">${view.displayName}</label>
            </div>
          `;
        });

        html += '</div>';
      }

      // 公共视图部分
      if (this.viewsData.publicViews.length > 0) {
        html += `
          <div class="view-filter-section">
            <div class="view-filter-title">
              <span class="icon">🌐</span>
              公共视图
            </div>
        `;

        this.viewsData.publicViews.forEach(view => {
          // 尝试多种匹配方式
          const isHidden = currentHidden.includes(view.identifier) ||
                          currentHidden.includes(view.displayName) ||
                          currentHidden.includes(view.name);
          html += `
            <div class="view-filter-item">
              <input type="checkbox" id="view_${view.identifier}" data-view-name="${view.displayName}" data-view-id="${view.identifier}" ${isHidden ? 'checked' : ''}>
              <label for="view_${view.identifier}">${view.displayName}</label>
            </div>
          `;
        });

        html += '</div>';
      }

      if (this.viewsData.personalViews.length === 0 && this.viewsData.publicViews.length === 0) {
        html += '<div class="view-filter-empty">暂无可过滤的视图</div>';
      }

      // 底部控制区域
      html += `
        <div class="view-filter-footer">
          <div class="view-filter-controls">
            <div class="view-filter-item">
              <input type="checkbox" id="remove-scrollbar" ${removeScrollbar ? 'checked' : ''}>
              <label for="remove-scrollbar">去除滚动条</label>
            </div>
          </div>
        </div>
      `;

      panel.innerHTML = html;

      // 绑定复选框事件
      panel.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
        checkbox.addEventListener('change', (e) => {
          this.handleFilterChange(e.target);
        });
      });
    },

    // 处理过滤变化
    handleFilterChange(checkbox) {
      // 处理滚动条控制
      if (checkbox.id === 'remove-scrollbar') {
        this.saveRemoveScrollbarSetting(checkbox.checked);
        this.applyScrollbarControl(checkbox.checked);
        return;
      }

      const viewId = checkbox.dataset.viewId || checkbox.id.replace('view_', '');
      const viewName = checkbox.dataset.viewName;
      const settings = this.getFilterSettings();
      let currentHidden = settings[this.currentPageType] || [];

      if (checkbox.checked) {
        // 添加到隐藏列表 - 同时添加ID和名称以提高匹配成功率
        if (!currentHidden.includes(viewId)) {
          currentHidden.push(viewId);
        }
        if (viewName && !currentHidden.includes(viewName)) {
          currentHidden.push(viewName);
        }
      } else {
        // 从隐藏列表移除 - 移除ID和名称
        currentHidden = currentHidden.filter(id => id !== viewId && id !== viewName);

        // 如果是取消选择,先强制显示所有视图,再重新应用过滤
        this.showAllViews();
      }

      // 保存设置
      this.saveFilterSettings(this.currentPageType, currentHidden);

      // 延迟应用过滤,确保显示操作完成
      setTimeout(() => this.applyFilter(), 100);
    },

    // 显示所有视图
    showAllViews() {
      const selectors = [
        '.teamix-cloud-sidebar-side-filter-menu-item-wrap',
        '.sidebar-menu-item',
        '.view-item',
        '[data-view-id]',
        '.next-menu-item',
        '.teamix-cloud-sidebar-side-filter-menu-item',
        '.sidebar-item',
        '.filter-menu-item',
        '.teamix-cloud-sidebar li',
        '.sidebar li',
        '[role="menuitem"]'
      ];

      for (const selector of selectors) {
        const items = document.querySelectorAll(selector);
        if (items.length > 0) {
          items.forEach(item => {
            item.style.display = '';
            item.style.visibility = '';
            item.style.opacity = '';
          });
          break;
        }
      }
    },

    // 应用滚动条控制
    applyScrollbarControl(remove) {
      // 查找所有可能的滚动容器元素
      const selectors = [
        '.teamix-cloud-sidebar-side-filter-menu-body-scroll',
        '.sidebar-menu-body-scroll'
      ];

      selectors.forEach(selector => {
        const elements = document.querySelectorAll(selector);
        elements.forEach(element => {
          if (remove) {
            // 只需要重写max-height就行,添加!important确保覆盖
            element.style.setProperty('max-height', 'none', 'important');
          } else {
            // 移除行内样式,恢复原始max-height
            element.style.removeProperty('max-height');
          }
        });
      });
    },

    // 展开所有视图(点击加载更多按钮)
    expandAllViews() {
      // 在视图列表区域查找"加载更多"按钮
      const sidebarSelectors = [
        '.teamix-cloud-sidebar',
        '.sidebar',
        '.view-list',
        '.filter-menu'
      ];

      let sidebar = null;
      for (const selector of sidebarSelectors) {
        sidebar = document.querySelector(selector);
        if (sidebar) break;
      }

      // 如果没有找到侧边栏,在整个文档中查找
      const searchArea = sidebar || document;

      // 查找所有可能的按钮和链接
      const allButtons = searchArea.querySelectorAll('button, a, span[role="button"], div[role="button"]');

      allButtons.forEach(btn => {
        const text = btn.textContent.trim();
        const isLoadMoreButton = (
          text === '加载更多' ||
          text === '更多' ||
          text.includes('加载更多') ||
          text.includes('展开更多') ||
          text.includes('查看更多') ||
          btn.className.includes('load-more') ||
          btn.className.includes('more-btn') ||
          btn.className.includes('expand-more')
        );

        if (isLoadMoreButton) {
          // 检查按钮是否可见且可点击
          const rect = btn.getBoundingClientRect();
          const isVisible = rect.width > 0 && rect.height > 0 &&
                           btn.offsetParent !== null &&
                           !btn.disabled &&
                           getComputedStyle(btn).display !== 'none' &&
                           getComputedStyle(btn).visibility !== 'hidden';

          if (isVisible) {
            try {
              btn.click();
              return; // 找到并点击了按钮,退出循环
            } catch (e) {
              // 忽略点击错误,继续查找其他按钮
            }
          }
        }
      });
    },

    // 应用过滤
    applyFilter() {
      const settings = this.getFilterSettings();
      const currentHidden = settings[this.currentPageType] || [];

      // 先尝试点击"加载更多"按钮展开所有视图
      this.expandAllViews();

      // 给展开操作一些时间,然后再应用过滤
      setTimeout(() => {
        this.doApplyFilter(currentHidden);
      }, 1000);
    },

    // 执行实际的过滤操作
    doApplyFilter(currentHidden) {
      const selectors = [
        '.teamix-cloud-sidebar-side-filter-menu-item-wrap',
        '.sidebar-menu-item',
        '.view-item',
        '[data-view-id]',
        '.next-menu-item',
        '.teamix-cloud-sidebar-side-filter-menu-item',
        '.sidebar-item',
        '.filter-menu-item',
        '.teamix-cloud-sidebar li',
        '.sidebar li',
        '[role="menuitem"]'
      ];

      let viewItems = [];
      for (const selector of selectors) {
        viewItems = document.querySelectorAll(selector);
        if (viewItems.length > 0) break;
      }

      if (viewItems.length === 0) {
        // 如果找不到视图元素,延迟重试,但限制重试次数
        if (!this.retryCount) this.retryCount = 0;
        if (this.retryCount < 5) {
          this.retryCount++;
          setTimeout(() => this.doApplyFilter(currentHidden), 1000);
        }
        return;
      }

      // 找到元素后重置重试计数
      this.retryCount = 0;

      viewItems.forEach((item, index) => {
        // 尝试从不同的属性中获取视图ID
        const viewId = this.getViewIdFromElement(item);
        const textContent = item.textContent.trim();

        // 检查是否应该隐藏这个视图
        const shouldHide = viewId && (
          currentHidden.includes(viewId) ||
          currentHidden.includes(textContent) ||
          currentHidden.some(hiddenId =>
            textContent.includes(hiddenId) ||
            (viewId && viewId.includes(hiddenId))
          )
        );

        if (shouldHide) {
          item.style.display = 'none';
        } else {
          item.style.display = '';
          // 确保移除任何可能的隐藏样式
          item.style.visibility = '';
          item.style.opacity = '';
        }
      });
    },

    // 从元素中获取视图ID
    getViewIdFromElement(element) {
      // 尝试从data属性获取
      if (element.dataset.viewId) return element.dataset.viewId;
      if (element.dataset.viewIdentifier) return element.dataset.viewIdentifier;
      if (element.dataset.key) return element.dataset.key;

      // 尝试从链接href中获取viewId参数
      const link = element.querySelector('a');
      if (link && link.href) {
        const match = link.href.match(/viewId=([^&]+)/);
        if (match) return match[1];
      }

      // 尝试从其他可能的属性获取
      const viewElement = element.querySelector('[data-view-identifier]');
      if (viewElement) return viewElement.dataset.viewIdentifier;

      // 尝试从元素的文本内容匹配已知视图名称
      const textContent = element.textContent.trim();
      if (textContent && this.viewsData) {
        // 在已加载的视图数据中查找匹配的视图
        const allViews = [...this.viewsData.personalViews, ...this.viewsData.publicViews];
        const matchedView = allViews.find(view => {
          return view.displayName === textContent ||
                 view.name === textContent ||
                 textContent.includes(view.displayName) ||
                 textContent.includes(view.name);
        });
        if (matchedView) {
          return matchedView.identifier;
        }
      }

      // 尝试从元素的其他属性获取
      if (element.id && element.id.includes('view')) {
        return element.id.replace(/^view[_-]?/, '');
      }

      // 尝试从class名称中获取
      if (element.className) {
        const classMatch = element.className.match(/view-([a-zA-Z0-9]+)/);
        if (classMatch) return classMatch[1];
      }

      // 如果是简单的文本匹配,直接返回文本作为ID
      if (textContent && textContent.length > 0 && textContent.length < 50) {
        return textContent;
      }

      return null;
    },

    // 初始化
    init() {
      // 等待页面加载
      const initInterval = setInterval(() => {
        const header = document.querySelector('.teamix-cloud-sidebar-side-filter-header');
        if (header) {
          clearInterval(initInterval);

          // 创建过滤按钮和面板
          this.createFilterButton();
          this.createFilterPanel();

          // 应用已保存的过滤设置
          this.currentPageType = this.getCurrentPageType();

          if (this.currentPageType) {
            // 应用已保存的滚动条设置
            const removeScrollbar = this.getRemoveScrollbarSetting();
            if (removeScrollbar) {
              this.applyScrollbarControl(true);
            }

            // 页面加载后先点击"加载更多"按钮
            setTimeout(() => this.expandAllViews(), 1500);

            // 多次尝试应用过滤,确保页面内容完全加载
            setTimeout(() => this.applyFilter(), 2000);
            setTimeout(() => this.applyFilter(), 3000);
            setTimeout(() => this.applyFilter(), 4000);
          }

          // 设置DOM变化监听器
          this.setupMutationObserver();
        }
      }, 500);

      // 监听页面变化
      let currentUrl = location.href;
      const urlCheckInterval = setInterval(() => {
        if (location.href !== currentUrl) {
          currentUrl = location.href;
          const newPageType = this.getCurrentPageType();
          if (newPageType && newPageType !== this.currentPageType) {
            this.currentPageType = newPageType;

            // 页面切换后先点击"加载更多"按钮
            setTimeout(() => this.expandAllViews(), 1500);

            // 延迟应用过滤,等待页面内容加载
            setTimeout(() => this.applyFilter(), 2000);

            // 重新创建过滤按钮(如果页面结构发生变化)
            setTimeout(() => {
              if (!document.querySelector('#view-filter-btn')) {
                this.createFilterButton();
              }
            }, 2500);
          }
        }
      }, 1000);
    },

    // 设置DOM变化监听器
    setupMutationObserver() {
      // 监听侧边栏的变化
      const sidebar = document.querySelector('.teamix-cloud-sidebar') || document.querySelector('.sidebar');
      if (!sidebar) return;

      const observer = new MutationObserver((mutations) => {
        let shouldReapplyFilter = false;

        mutations.forEach((mutation) => {
          // 检查是否有新的视图元素添加
          if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
            for (const node of mutation.addedNodes) {
              if (node.nodeType === Node.ELEMENT_NODE) {
                // 检查是否包含视图相关的类名
                if (node.classList && (
                  node.classList.contains('teamix-cloud-sidebar-side-filter-menu-item-wrap') ||
                  node.classList.contains('sidebar-menu-item') ||
                  node.querySelector('.teamix-cloud-sidebar-side-filter-menu-item-wrap') ||
                  node.querySelector('.sidebar-menu-item')
                )) {
                  shouldReapplyFilter = true;
                  break;
                }
              }
            }
          }
        });

        if (shouldReapplyFilter) {
          // 延迟应用过滤,等待DOM完全更新
          setTimeout(() => this.applyFilter(), 500);
          // 再次延迟确保稳定
          setTimeout(() => this.applyFilter(), 1500);
        }
      });

      observer.observe(sidebar, {
        childList: true,
        subtree: true
      });
    }
  };

  /**
   * 任务加强
   */
  if (location.host === "devops.aliyun.com") {
    // 初始化视图过滤功能
    ViewFilter.init();

    let isLogin = false;
    const login = () => {
      if (isLogin) return;
      const loginDom = document.querySelector(
        ".next-overlay-wrapper .next-dialog-footer button"
      );
      if (loginDom && loginDom.textContent === "重新登录") {
        loginDom.click();
        isLogin = true;
      }
    };
    // 从其他tab切换回来时,需要重新登录
    window.addEventListener("visibilitychange", () => {
      if (document.visibilityState === "visible") {
        login();
      }
    });

    setInterval(() => {
      login();
      document.querySelectorAll(".next-btn").forEach((btn) => {
        const text = btn.querySelector(".teamix-title")?.innerText;
        if (!text) return;
        switch (text) {
          case "开发中":
            btn.style.cssText = "background-color: #09f !important";
            break;
          case "待修复":
            btn.style.cssText = "background-color: #2091ed !important";
            break;
          case "修复中":
            btn.style.cssText = "background-color: #f0812d !important";
            break;
          case "修复完成":
            btn.style.cssText =
              "background-color: rgb(117, 93, 255) !important";
            break;
          case "测试中":
            btn.style.cssText = "background-color: #2196F3 !important";
            break;
          case "测试完成":
            btn.style.cssText =
              "background-color: rgba(7, 175, 175, 0.65) !important";
            break;
          case "再次打开":
            btn.style.cssText = "background-color: red !important";
            break;
          case "推迟修复":
            btn.style.cssText = "background-color: #b2b8be !important";
            break;
        }

        if (testFilter) {
          filterDom(btn, text);
        }
      });
    }, 1000);

    const closest = (el, selector) => {
      let element = el;
      while (element) {
        if (element.matches(selector)) {
          break;
        }
        element = element.parentElement;
      }
      return element;
    };

    const contianer = document.createElement("div");
    contianer.style.position = "fixed";
    contianer.style.top = "28px";
    contianer.style.right = "6px";
    contianer.style.zIndex = "9999";
    document.body.appendChild(contianer);

    const filterBtn = document.createElement("button");
    setBtnStyle(filterBtn);
    filterBtn.style.backgroundColor = "#999";
    filterBtn.innerHTML = "过滤";
    contianer.appendChild(filterBtn);

    const setBtn = document.createElement("span");
    setBtn.innerHTML = setIconHtml;
    setBtn.onclick = () => {
      createGetLoginInfo();
    };
    contianer.appendChild(setBtn);

    const filterChilds = document.createElement("div");
    filterChilds.style.position = "absolute";
    filterChilds.style.top = "24px";
    filterChilds.style.right = "0";
    filterChilds.style.width = "300px";
    filterChilds.style.textAlign = "right";
    filterChilds.style.display = "none";

    contianer.appendChild(filterChilds);

    // 增加多项checkbox按钮
    const btns = [
      {
        text: "测试完成",
        selected: true,
      },
      {
        text: "已取消",
        selected: true,
      },
      {
        text: "已发布",
        selected: true,
      },
      {
        text: "已完成",
        selected: true,
      },
      {
        text: "走查完成",
        selected: true,
      },
    ];
    btns.forEach((item) => {
      const contianer = document.createElement("span");
      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      checkbox.name = "test";
      checkbox.value = item.text;
      checkbox.checked = item.selected;
      checkbox.style.zoom = "0.8";
      checkbox.style.position = "relative";
      checkbox.style.top = "1px";
      checkbox.onchange = function () {
        item.selected = !item.selected;
      };
      contianer.appendChild(checkbox);
      const span = document.createElement("span");
      span.style.fontSize = "10px";
      span.innerHTML = item.text;
      contianer.appendChild(span);
      contianer.style.marginLeft = "4px";
      filterChilds.appendChild(contianer);
    });

    let testFilter = false;
    const filterDom = (btn, text = "") => {
      if (text === "") {
        text = btn.querySelector(".teamix-title")?.innerText;
      }
      if (!text) return;
      const filterItem = btns.find((item) => item.text === text);
      if (filterItem) {
        const tr = closest(btn, "tr");
        if (!testFilter) {
          tr.style.display = "";
        } else if (filterItem.selected) {
          tr.style.display = "none";
        } else {
          tr.style.display = "";
        }
      }
    };
    filterBtn.onclick = function () {
      testFilter = !testFilter;
      if (!testFilter) {
        filterBtn.style.backgroundColor = "#999";
        filterChilds.style.display = "none";
      } else {
        filterBtn.style.backgroundColor = "rgb(95, 206, 157)";
        filterChilds.style.display = "block";
      }
      document.querySelectorAll(".next-btn").forEach((btn) => {
        filterDom(btn);
      });
    };
  }

  /**
   * 工时管理
   */
  let recordedHours = -1;
  if (location.host === "devops.aliyun.com") {
    const getTaskList = async () => {
      const res = await fetchApi(
        "https://devops.aliyun.com/projex/api/workitem/workitem/list?_input_charset=utf-8",
        `{"spaceType":"User","spaceIdentifier":"${unsafeWindow.AONE_GLOBAL.user.identifier}","category":"","toPage":1,"pageSize":100,"conditions":"{\\"conditionGroups\\":[[{\\"fieldIdentifier\\":\\"statusStage\\",\\"operator\\":\\"CONTAINS\\",\\"value\\":[\\"1\\",\\"2\\",\\"6\\",\\"7\\",\\"11\\",\\"12\\",\\"13\\"],\\"toValue\\":null,\\"className\\":\\"statusStage\\",\\"format\\":\\"multiList\\"},{\\"fieldIdentifier\\":\\"assignedTo\\",\\"operator\\":\\"CONTAINS\\",\\"value\\":[\\"${unsafeWindow.AONE_GLOBAL.user.identifier}\\"],\\"toValue\\":null,\\"className\\":\\"user\\",\\"format\\":\\"list\\"}]]}","searchType":"LIST","orderBy":"{\\"fieldIdentifier\\":\\"workitemType\\",\\"format\\":\\"list\\",\\"order\\":\\"desc\\",\\"className\\":\\"workitemType\\"}","scope":"personal"}`,
        "POST"
      );
      return res.result;
    };
    const showTaskList = async () => {
      if (window.isShowTaskList) return;
      const list = await getTaskList();
      list.sort((a, b) => b.gmtModified - a.gmtModified);
      console.log(list);
      function createTable() {
        var contianer = document.createElement("div");
        contianer.id = "timeContianer";
        GM_addStyle(`
          #timeContianer {
            width: 950px;
            position: fixed;
            background: #fff;
            z-index: 99999;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            padding: 26px;
            box-shadow: 0 0px 0px 2000px rgba(0, 0, 0, 0.3);
            border-radius: 8px;
            overflow-y: auto;
            overflow-x: hidden;
            max-height: 600px;
          }
        `);

        const closeDow = document.createElement("div");
        closeDow.id = "closeDow";
        GM_addStyle(`
          #closeDow {
            position: absolute;
            top: 10px;
            right: 10px;
            color: #000;
            font-size: 20px;
            line-height: 17px;
            padding: 4px 2px;
            cursor: pointer;
            font-family: NextIcon;

            &:before {
              content: var(--icon-content-close, "\e626");
            }
          }
        `);
        closeDow.onclick = () => {
          window.isShowTaskList = false;
          contianer.remove();
        };
        contianer.appendChild(closeDow);

        const titleDom = document.createElement("div");
        const selectType = {
          value: localStorage.getItem("selectType") || "develop",
        };

        // 今日工时
        const timeDisplay = document.createElement("span");
        timeDisplay.id = "timeTitle2";
        timeDisplay.innerHTML = `今日工时:${recordedHours}小时 ${
          recordedHours >= 8 ? `<span style="color:red;">已满</span>` : ""
        }`;
        timeDisplay.style.marginRight = "12px";
        titleDom.appendChild(timeDisplay);

        createSelecter(titleDom, selectType);
        contianer.appendChild(titleDom);

        const table = document.createElement("table");
        table.id = "timeTable";
        GM_addStyle(`
          #timeTable {
            width: 100%;
          }

          .time-tr {
            height: 30px;
            background: #fff;
          }

          .time-tr:hover {
            background: #f2f5f7;
          }
        `);

        const thead = document.createElement("thead");
        const tbody = document.createElement("tbody");

        const headerRow = document.createElement("tr");
        const headerName = document.createElement("th");
        headerName.textContent = "名称";

        const todayWork = document.createElement("th");
        todayWork.textContent = "今日工时";

        const headerButtons = document.createElement("th");
        headerButtons.textContent = "添加工时";
        headerRow.appendChild(headerName);
        headerRow.appendChild(todayWork);
        headerRow.appendChild(headerButtons);
        thead.appendChild(headerRow);

        list.forEach((item) => {
          const name = item.subject;
          const row = document.createElement("tr");
          const nameCell = document.createElement("td");
          const title = document.createElement("div");
          row.className = "time-tr";
          title.style.width = "520px";
          title.style.whiteSpace = "nowrap";
          title.style.textOverflow = "ellipsis";
          title.style.overflow = "hidden";
          title.style.cursor = "pointer";

          title.onclick = () => {
            GM_openInTab(
              `https://devops.aliyun.com/projex/project/${item.spaceIdentifier}/req/${item.identifier}`,
              {
                active: true,
              }
            );
          };

          if (item.category.identifier === "Req") {
            const icon = document.createElement("span");
            icon.innerHTML = reqIconHtml;
            title.textContent = name;
            title.prepend(icon);
          } else if (item.category.identifier === "Task") {
            const icon = document.createElement("span");
            icon.innerHTML = taskIconHtml;
            title.textContent = name;
            title.prepend(icon);
          } else if (item.category.identifier === "Bug") {
            const icon = document.createElement("span");
            icon.innerHTML = bugIconHtml;
            title.textContent = name;
            title.prepend(icon);
          }
          nameCell.appendChild(title);

          const todayCell = document.createElement("td");
          todayCell.id = `td-${item.identifier}`;
          todayCell.innerHTML = `0`;

          const buttonCell = document.createElement("td");
          for (let i = 1; i <= 6; i++) {
            const button = document.createElement("button");
            button.textContent = `+${i}`;
            button.style.marginRight = "5px";
            button.style.cursor = "pointer";
            button.onclick = async function () {
              const t = recordedHours + i > 8 ? 8 - recordedHours : i;
              if (t <= 0) return;
              await addTime(item.identifier, t, selectType.value);
              const newTime = recordedHours + i > 8 ? 8 : recordedHours + i;
              recordedHours = newTime;
              setDayWorkitemTime(newTime);
              setTodayWorkItemTime();
            };
            buttonCell.appendChild(button);
          }

          row.appendChild(nameCell);
          row.appendChild(todayCell);
          row.appendChild(buttonCell);
          tbody.appendChild(row);
        });

        table.appendChild(thead);
        table.appendChild(tbody);
        contianer.appendChild(table);
        return contianer;
      }
      window.isShowTaskList = true;
      // 添加表格到页面
      document.body.appendChild(createTable());
      setTodayWorkItemTime();
    };

    const addTime = async (id, time, type) => {
      fetchApi(
        "https://devops.aliyun.com/projex/api/workitem/workitem/time?_input_charset=utf-8",
        `{"workitemIdentifier":"${id}","type":"${type}","actualTime":${time},"description":"","recordUserIdentifier":"${
          unsafeWindow.AONE_GLOBAL.user.identifier
        }","gmtStart":"${getLocalISOString()}","gmtEnd":"${getLocalISOString()}","containsRestDay":true}`,
        "POST"
      );
    };

    const setDayWorkitemTime = async (newTime = recordedHours) => {
      if (newTime < 0) {
        const res = await getDayWorkitemTime();
        newTime = res?.recordedHours || 0;
      }
      let timeTitle = document.querySelector("#timeTitle");
      let timeTitleMessage = document.querySelector("#timeTitleMessage");
      if (!timeTitle) {
        timeTitle = document.createElement("div");
        timeTitle.id = "timeTitle";
        GM_addStyle(`
          #timeTitle {
            color: #000;
            margin-left: 12px;
            font-weight: 900;
          }
        `);
        const dom = document.querySelector(".system-bar-left");
        dom.append(timeTitle);

        const btn = document.createElement("div");
        btn.id = "timeTitleBtn";
        GM_addStyle(`
          #timeTitleBtn {
            background: var(--color-brand1-6, #1b9aee);
            border-radius: 4px;
            cursor: pointer;
            mask: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik0xMCAyMEM0LjQ3NyAyMCAwIDE1LjUyMyAwIDEwUzQuNDc3IDAgMTAgMHMxMCA0LjQ3NyAxMCAxMC00LjQ3NyAxMC0xMCAxMHptLjYxNS02LjU4NHYtMi44MDFoMi44MDFhLjYxNi42MTYgMCAwMDAtMS4yM2gtMi44MDFWNi41ODVhLjYxNS42MTUgMCAxMC0xLjIzIDB2Mi44bC0yLjguMDAyYS42MTQuNjE0IDAgMTAtLjAwMiAxLjIzaDIuODAydjIuOGEuNjE1LjYxNSAwIDAwMS4yMyAweiIgaWQ9ImEiLz48L2RlZnM+PHVzZSBmaWxsPSIjMDAwIiB4bGluazpocmVmPSIjYSIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+) no-repeat center center;
            width: 20px;
            height: 20px;
            margin-left: 4px;
          }
        `);
        btn.onclick = showTaskList;
        dom.append(btn);

        timeTitleMessage = document.createElement("div");
        timeTitleMessage.id = "timeTitleMessage";
        GM_addStyle(`
          #timeTitleMessage {
            color: red;
            margin-left: 6px;
          }
        `);
        dom.append(timeTitleMessage);
      }
      timeTitle.innerHTML = `今日工时:${newTime}小时 ${
        newTime >= 8 ? `<span style="color:red;">已满</span>` : ""
      }`;
      let timeTitle2 = document.querySelector("#timeTitle2");
      if (timeTitle2)
        timeTitle2.innerHTML = `今日工时:${newTime}小时 ${
          newTime >= 8 ? `<span style="color:red;">已满</span>` : ""
        }`;

      const now = new Date();
      const hours = now.getHours();
      const minutes = now.getMinutes();
      if (newTime < 3.76 && (hours > 20 || (hours === 20 && minutes > 0))) {
        timeTitleMessage.innerText = "今日工时不足,请填写!";
      } else if (newTime > 8) {
        timeTitleMessage.innerText = "今日工时不得大于8小时!";
      } else {
        timeTitleMessage.innerText = "";
      }
    };

    const getDayWorkitemTime = async () => {
      const today = getFormattedDate();
      const { monday, friday } = getWeekStartAndEnd();
      const res = await fetchApi(
        `https://devops.aliyun.com/projex/api/workitem/workitem/time/stats/user/dayWorkitemTime?gmtStart=${monday}&gmtEnd=${friday}`
      );
      const dayWorkitemTime = res.result;
      const todayInfo = dayWorkitemTime.find((item) => item.date === today);
      recordedHours = todayInfo?.recordedHours || 0;
      return todayInfo;
    };

    const getTodayWorkitem = async () => {
      const today = getFormattedDate();
      const res = await fetchApi(
        `https://devops.aliyun.com/projex/api/workitem/workitem/workTable/workitemTime/list?day=${today}`
      );
      return res.result;
    };

    const getDayWorkitemTimeByWorkitems = async (ids) => {
      const today = getFormattedDate();
      const res = await fetchApi(
        `https://devops.aliyun.com/projex/api/workitem/workitem/time/stats/user/getDayWorkitemTimeByWorkitems?day=${today}&workitemIdentifiers=${ids.join()}`
      );
      return res.result;
    };

    const setTodayWorkItemTime = async () => {
      const list = await getTodayWorkitem();
      const ids = [];
      list.forEach((item) => {
        ids.push(item.identifier);
      });
      const timeList = await getDayWorkitemTimeByWorkitems(ids);
      timeList?.forEach((item) => {
        const td = document.querySelector(`#td-${item.workitemIdentifier}`);
        if (td) {
          td.innerHTML = `${item.recordedHours}小时`;
        }
      });
    };

    const removeDom = () => {
      document.querySelector("#tb-navigation-customOperation")?.remove();
    };
    if (location.host === "devops.aliyun.com") {
      setTimeout(async () => {
        removeDom();
        setDayWorkitemTime(-1);
      }, 1000);
    }
    setInterval(() => {
      setDayWorkitemTime(-1);
    }, 1000 * 60 * 10);
    window.addEventListener("visibilitychange", () => {
      setDayWorkitemTime(-1);
    });
  }

  /**
   * components
   */

  function createGetLoginInfo() {
    // 创建容器
    const container = document.createElement("div");
    container.className = "login-container";
    GM_addStyle(`
      .login-container {
        position: fixed;
        top: 40%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 400px;
        padding: 20px;
        border-radius: 8px;
        box-shadow: 0 0px 0px 2000px rgba(0, 0, 0, 0.3);
        background-color: #fff;
        z-index: 9999;
      }
      `);

    // 创建关闭按钮
    const closeButton = document.createElement("button");
    closeButton.textContent = "×"; // 使用 '×' 符号作为关闭按钮
    closeButton.style.position = "absolute";
    closeButton.style.top = "10px";
    closeButton.style.right = "10px";
    closeButton.style.fontSize = "20px";
    closeButton.style.backgroundColor = "transparent";
    closeButton.style.border = "none";
    closeButton.style.color = "#333";
    closeButton.style.cursor = "pointer";
    closeButton.style.padding = "0";
    closeButton.style.fontWeight = "bold";

    // 添加关闭按钮点击事件,隐藏容器
    closeButton.addEventListener("click", function () {
      container.remove();
    });

    // 创建标题
    const title = document.createElement("h2");
    title.textContent = "自动登录需设置账户密码";
    title.style.fontSize = "18px";
    title.style.fontWeight = "bold";
    title.style.marginBottom = "20px";
    title.style.color = "#333";
    title.style.textAlign = "center";

    // 创建说明文本
    const description = document.createElement("p");
    description.textContent = "提示:账号密码只存储到当前设备";
    description.style.fontSize = "12px";
    description.style.color = "#999";
    description.style.textAlign = "center";
    description.style.marginBottom = "30px";

    // 创建用户名输入框
    const usernameLabel = document.createElement("label");
    usernameLabel.textContent = "账号名";
    usernameLabel.style.fontSize = "14px";
    usernameLabel.style.color = "#333";
    usernameLabel.style.marginBottom = "5px";
    const usernameInput = document.createElement("input");
    usernameInput.placeholder = "请输入";
    usernameInput.style.width = "100%";
    usernameInput.style.padding = "12px";
    usernameInput.style.marginBottom = "15px";
    usernameInput.style.border = "1px solid #d9d9d9"; // 更浅的边框颜色
    usernameInput.style.borderRadius = "4px";
    usernameInput.style.fontSize = "14px";
    usernameInput.style.outline = "none";
    usernameInput.style.boxSizing = "border-box";
    usernameInput.style.color = "#333"; // 字体颜色调整

    // 创建密码输入框
    const passwordLabel = document.createElement("label");
    passwordLabel.textContent = "密码";
    passwordLabel.style.fontSize = "14px";
    passwordLabel.style.color = "#333";
    passwordLabel.style.marginBottom = "5px";
    const passwordInput = document.createElement("input");
    passwordInput.placeholder = "请输入";
    passwordInput.type = "password";
    passwordInput.style.width = "100%";
    passwordInput.style.padding = "12px";
    passwordInput.style.marginBottom = "20px";
    passwordInput.style.border = "1px solid #d9d9d9"; // 更浅的边框颜色
    passwordInput.style.borderRadius = "4px";
    passwordInput.style.fontSize = "14px";
    passwordInput.style.outline = "none";
    passwordInput.style.boxSizing = "border-box";
    passwordInput.style.color = "#333"; // 字体颜色调整

    // 创建登录按钮
    const loginButton = document.createElement("button");
    loginButton.textContent = "保存";
    loginButton.style.width = "100%";
    loginButton.style.padding = "12px";
    loginButton.style.backgroundColor = "#1890FF"; // 按钮颜色调整为蓝色
    loginButton.style.color = "#fff";
    loginButton.style.border = "none";
    loginButton.style.borderRadius = "4px";
    loginButton.style.cursor = "pointer";
    loginButton.style.fontSize = "16px";
    loginButton.style.transition = "background-color 0.3s";

    loginButton.addEventListener("click", async function () {
      userName = usernameInput.value;
      password = passwordInput.value;

      await GM_setValue("userName", userName);
      await GM_setValue("password", password);

      if (location.host === "account.aliyun.com") {
        location.href = location.href;
      } else {
        container.remove();
      }
    });

    // 添加按钮 hover 效果
    loginButton.addEventListener("mouseover", function () {
      loginButton.style.backgroundColor = "#0066cc"; // 悬停时的颜色
    });
    loginButton.addEventListener("mouseout", function () {
      loginButton.style.backgroundColor = "#1890FF"; // 悬停恢复颜色
    });

    // 将元素添加到容器中
    container.appendChild(closeButton);
    container.appendChild(title);
    container.appendChild(description);
    container.appendChild(usernameLabel);
    container.appendChild(usernameInput);
    container.appendChild(passwordLabel);
    container.appendChild(passwordInput);
    container.appendChild(loginButton);

    // 将容器添加到页面中
    unsafeWindow.document.body.appendChild(container);
  }

  /**
   * util
   */
  function setBtnStyle(btn) {
    btn.className = "next-btn next-medium next-btn-normal next-menu-btn";
    btn.style.backgroundColor = "rgb(95, 206, 157)";
    btn.style.color = "#fff";
    btn.style.padding = "0px 10px";
    btn.style.fontSize = "12px";
    btn.style.height = "24px";
    btn.style.width = "auto";
    btn.style.minWidth = "auto";
  }

  function getFormattedDate(dateInput = new Date()) {
    const date = new Date(dateInput); // 如果没有传参,则使用当前日期
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始,所以加1
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }

  function createSelecter(container, selectType) {
    const dropdownTitle = document.createElement("span");
    dropdownTitle.innerText = "工作类型:";
    container.appendChild(dropdownTitle);
    const dropdownContainer = document.createElement("div");
    dropdownContainer.classList.add("dropdown");

    const selectedOption = document.createElement("div");
    selectedOption.classList.add("selected");
    const options = [
      {
        name: "设计",
        type: "design",
      },
      {
        name: "研发",
        type: "develop",
      },
      {
        name: "测试",
        type: "test",
      },
      {
        name: "文档",
        type: "document",
      },
      {
        name: "其他",
        type: "others",
      },
    ];
    const name = options.find((item) => item.type === selectType.value).name;
    selectedOption.textContent = name; // Default selected option

    const optionsList = document.createElement("div");
    optionsList.style.display = "none";
    optionsList.classList.add("options");

    options.forEach((option) => {
      const optionItem = document.createElement("div");
      optionItem.classList.add("option");
      optionItem.textContent = option.name;
      optionItem.addEventListener("click", () => {
        selectedOption.textContent = option.name;
        selectType.value = option.type;
        localStorage.setItem("selectType", option.type);
        optionsList.style.display = "none"; // Hide the options after selection
      });
      optionsList.appendChild(optionItem);
    });

    dropdownContainer.appendChild(selectedOption);
    dropdownContainer.appendChild(optionsList);
    container.appendChild(dropdownContainer);

    selectedOption.addEventListener("click", () => {
      optionsList.style.display =
        optionsList.style.display === "none" ? "block" : "none";
    });

    const style = document.createElement("style");
    style.textContent = `
    .dropdown {
      position: relative;
      display: inline-block;
      font-family: Arial, sans-serif;
      width: 150px;
    }
    .selected {
      padding: 3px 10px;
      color: #2a7bf2;
      border: 1px solid #ccc;
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
    }
    .selected:hover {
      background-color: #e3f2fd;
    }
    .options {
      display: none;
      position: absolute;
      top: 100%;
      left: 0;
      background-color: white;
      border: 1px solid #2a7bf2;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
      width: 100%;
      border-radius: 4px;
      z-index: 1;
    }
    .option {
      padding: 10px;
      background-color: white;
      color: #333;
      cursor: pointer;
      font-size: 14px;
    }
    .option:hover {
      background-color: #f0f7ff;
    }
  `;
    container.appendChild(style);
  }

  function getWeekStartAndEnd(dateInput = new Date()) {
    const date = new Date(dateInput);
    const day = date.getDay();
    let diffToMonday = day === 0 ? -6 : 1 - day;
    diffToMonday = diffToMonday === 0 ? -1 : diffToMonday;
    const monday = new Date(date);
    monday.setDate(date.getDate() + diffToMonday);

    const friday = new Date(monday);
    friday.setDate(monday.getDate() + 6);

    const format = (d) => {
      const y = d.getFullYear();
      const m = String(d.getMonth() + 1).padStart(2, "0");
      const day = String(d.getDate()).padStart(2, "0");
      return `${y}-${m}-${day}`;
    };

    return {
      monday: format(monday),
      friday: format(friday),
    };
  }

  function getLocalISOString() {
    const date = new Date();

    let isoString = date.toISOString().split(".")[0];

    const timezoneOffset = date.getTimezoneOffset();
    const offsetHours = Math.floor(Math.abs(timezoneOffset) / 60);
    const offsetMinutes = Math.abs(timezoneOffset) % 60;
    const sign = timezoneOffset > 0 ? "-" : "+";

    const formattedOffset = `${sign}${String(offsetHours).padStart(
      2,
      "0"
    )}:${String(offsetMinutes).padStart(2, "0")}`;

    const formattedDate = `${isoString}${formattedOffset}`;
    return formattedDate;
  }


  function fetchApi(url, body = null, method = "GET") {
    var headers = {
      accept: "application/json, text/plain, */*",
      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
      "cache-control": "no-cache",
      "content-type": "application/json",
      pragma: "no-cache",
      priority: "u=1, i",
      "sec-ch-ua":
        '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": '"macOS"',
      "sec-fetch-dest": "empty",
      "sec-fetch-mode": "cors",
      "sec-fetch-site": "same-origin",
      "x-csrf-token": "8rSfd6u3-6ucnCtsANW1H2wEUWab5kwxYL7E",
      "x-requested-with": "XMLHttpRequest",
    };

    return fetch(url, {
      headers: headers,
      referrerPolicy: "strict-origin-when-cross-origin",
      body: body,
      method: method,
      mode: "cors",
      credentials: "include",
    }).then((res) => res.json());
  }
})();