NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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());
}
})();