// ==UserScript==
// @name 蝉妈妈产品历史KOC助手
// @namespace https://github.com/scriptCat/
// @version 1.0.0
// @description 蝉妈妈平台产品历史KOC数据分析和导出助手
// @author wangxi
// @match https://www.chanmama.com/promotionRank/*
// @grant GM_xmlhttpRequest
// @grant GM_notification
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_log
// @grant GM_addStyle
// @grant GM_setClipboard
// @connect chanmama.com
// @connect api-service.chanmama.com
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @license MIT
// ==/UserScript==
/* ==UserConfig==
settings:
enabled:
title: 启用功能
description: 是否启用蝉妈妈产品历史KOC助手功能
type: checkbox
default: true
timeout:
title: 超时时间
description: 网络请求超时时间
type: number
default: 30
min: 10
max: 120
unit: 秒
debug:
title: 调试模式
description: 启用详细日志输出
type: checkbox
default: false
==/UserConfig== */
(function() {
'use strict';
// 配置常量
const CONFIG = {
debug: true, // 强制开启调试模式
enabled: GM_getValue('settings.enabled', true),
timeout: GM_getValue('settings.timeout', 30) * 1000,
api: {
baseUrl: 'https://api-service.chanmama.com',
endpoint: '/v1/product/author/analysis'
},
pagination: {
pageSize: 30,
amountThreshold: 10000 // 金额门槛,低于此值停止翻页
}
};
// 工具函数
function log(message, level = 'info') {
const logMessage = `[${GM_info.script.name}] ${message}`;
// 强制输出所有日志到GM日志和控制台,便于调试
GM_log(logMessage, level);
console.log(logMessage);
}
function showNotification(title, text, type = 'info') {
GM_notification({
title: title,
text: text,
timeout: 5000,
onclick: () => log('通知被点击')
});
}
function makeRequest(url, options = {}) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: options.method || 'GET',
url: url,
headers: {
'Content-Type': 'application/json',
...options.headers
},
data: options.data,
timeout: CONFIG.timeout,
anonymous: false,
onload: (response) => {
if (response.status >= 200 && response.status < 300) {
try {
const data = JSON.parse(response.responseText);
resolve(data);
} catch (error) {
resolve(response.responseText);
}
} else {
reject(new Error(`HTTP ${response.status}: ${response.statusText}`));
}
},
onerror: () => reject(new Error('网络请求失败')),
ontimeout: () => reject(new Error('请求超时'))
});
});
}
// SweetAlert2 弹窗函数
function showInfo(title, text) {
Swal.fire({
title: title,
text: text,
icon: 'info',
confirmButtonText: '确定'
});
}
function showSuccess(title, text) {
Swal.fire({
title: title,
text: text,
icon: 'success',
timer: 3000,
showConfirmButton: false
});
}
function showError(title, text) {
Swal.fire({
title: title,
text: text,
icon: 'error',
confirmButtonText: '确定'
});
}
async function showConfirm(title, text) {
const result = await Swal.fire({
title: title,
text: text,
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
});
return result.isConfirmed;
}
// 数据导出函数
function exportToJSON(data, filename = 'koc_history') {
// JSON导出逻辑
log('导出JSON格式数据');
const jsonStr = JSON.stringify(data, null, 2);
GM_setClipboard(jsonStr);
showSuccess('导出成功', 'JSON数据已复制到剪贴板');
}
// KOC数据处理函数
async function fetchKOCHistory(productId) {
try {
log(`获取产品 ${productId} 的KOC历史数据`);
const response = await makeRequest(`${CONFIG.api.baseUrl}${CONFIG.api.endpoint}`, {
method: 'POST',
data: JSON.stringify({ productId })
});
return response;
} catch (error) {
log(`获取KOC历史数据失败: ${error.message}`, 'error');
throw error;
}
}
/**
* 解析区间文本为平均值
* 例如: "750~1000" -> 875, "1w~2.5w" -> 17500
*/
function parseRangeText(rangeText) {
if (!rangeText || rangeText === '0' || rangeText === '') return 0;
// 正确处理万单位:先解析每个数值,再转换单位
const split = rangeText.split('~');
const values = split.map(s => {
const trimmed = s.trim();
if (trimmed.includes('w')) {
// 提取数字部分,乘以10000
const numPart = parseFloat(trimmed.replace('w', '')) || 0;
return numPart * 10000;
} else {
return parseFloat(trimmed) || 0;
}
});
const result = values.reduce((sum, val) => sum + val, 0) / values.length;
return result;
}
/**
* 解析KOC数据
*/
function parseKOCData(apiResponse) {
if (!apiResponse || !apiResponse.data || !apiResponse.data.list) {
log('数据格式错误', 'error');
return [];
}
const results = [];
const kocList = apiResponse.data.list;
kocList.forEach((koc, index) => {
// 基础信息
const baseInfo = {
昵称: koc.nickname || '',
达人UID: koc.unique_id || koc.short_id || '',
粉丝数: koc.follower_count || 0,
类型: koc.label || '',
口碑分: (koc.reputation && koc.reputation.score) || 0,
关联商品销售量区间: koc.volume_text || '',
关联商品销售金额区间: koc.amount_text || '',
预估关联商品销售量: parseRangeText(koc.volume_text),
预估关联商品销售金额: parseRangeText(koc.amount_text)
};
// 计算关联商品视频的总销售量和金额
let totalVideoVolume = 0;
let totalVideoAmount = 0;
const videoProductIds = new Set();
if (koc.a && Array.isArray(koc.a)) {
koc.a.forEach((video, videoIndex) => {
if (video.product_id) {
videoProductIds.add(video.product_id);
}
const volumeValue = parseRangeText(video.volume_text);
const amountValue = parseRangeText(video.amount_text);
totalVideoVolume += volumeValue;
totalVideoAmount += amountValue;
});
}
baseInfo.预估关联商品视频销售量 = totalVideoVolume;
baseInfo.预估关联商品视频销售金额 = totalVideoAmount;
baseInfo.关联商品蝉妈妈ID = Array.from(videoProductIds).join(',');
results.push(baseInfo);
});
return results;
}
// UI相关函数 - 创建简洁美观的悬浮窗
function createFloatingWindow() {
// 检查悬浮窗是否已存在
if (document.querySelector('.koc-floating-window')) {
log('悬浮窗已存在');
return true;
}
// 创建简洁的悬浮窗
const floatingWindow = document.createElement('div');
floatingWindow.className = 'koc-floating-window';
floatingWindow.innerHTML = `
`;
// 设置悬浮窗样式
floatingWindow.style.cssText = `
position: fixed;
bottom: 30px;
right: 30px;
width: 180px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
user-select: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
opacity: 0;
transform: translateY(20px) scale(0.95);
`;
// 添加美观的样式
GM_addStyle(`
.koc-floating-window {
animation: kocSlideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes kocSlideIn {
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.koc-window-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
cursor: move;
user-select: none;
}
.koc-window-title {
font-size: 13px;
font-weight: 600;
color: #374151;
letter-spacing: 0.025em;
}
.koc-close-btn {
width: 20px;
height: 20px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: #6b7280;
transition: all 0.2s ease;
font-weight: 300;
}
.koc-close-btn:hover {
background: rgba(239, 68, 68, 0.1);
color: #ef4444;
transform: scale(1.1);
}
.koc-window-content {
padding: 16px;
}
.koc-api-status {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
background: rgba(16, 185, 129, 0.05);
border-radius: 6px;
border: 1px solid rgba(16, 185, 129, 0.1);
margin-bottom: 12px;
}
.koc-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
.koc-download-btn {
width: 100%;
padding: 12px 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
position: relative;
overflow: hidden;
}
.koc-download-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.koc-download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
.koc-download-btn:hover::before {
left: 100%;
}
.koc-download-btn:active {
transform: translateY(0);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.koc-download-btn:disabled {
background: linear-gradient(135deg, #d1d5db 0%, #9ca3af 100%);
cursor: not-allowed;
transform: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.koc-btn-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.koc-status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background: #10b981;
animation: kocPulse 2s infinite;
}
@keyframes kocPulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.5;
transform: scale(1.2);
}
}
.koc-status-text {
font-size: 11px;
color: #6b7280;
font-weight: 500;
}
.koc-floating-window.dragging {
transition: none !important;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
transform: scale(1.02);
}
/* 状态颜色 */
.koc-status.waiting .koc-status-indicator {
background: #10b981;
}
.koc-status.processing .koc-status-indicator {
background: #f59e0b;
animation: kocSpin 1s linear infinite;
}
.koc-status.success .koc-status-indicator {
background: #10b981;
}
.koc-status.error .koc-status-indicator {
background: #ef4444;
}
@keyframes kocSpin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.koc-floating-window {
bottom: 20px;
right: 20px;
width: 160px;
}
}
`);
// 添加到页面
document.body.appendChild(floatingWindow);
// 设置拖拽功能
setupSimpleDrag(floatingWindow);
// 设置事件监听器
setupFloatingWindowEvents(floatingWindow);
// 显示动画
setTimeout(() => {
floatingWindow.style.opacity = '1';
floatingWindow.style.transform = 'translateY(0) scale(1)';
}, 100);
return true;
}
// 更新悬浮窗状态
function updateFloatingWindowStatus(message, type = 'info') {
const floatingWindow = document.querySelector('.koc-floating-window');
if (!floatingWindow) return;
const statusText = floatingWindow.querySelector('.koc-status-text');
const statusIndicator = floatingWindow.querySelector('.koc-status-indicator');
if (statusText) {
statusText.textContent = message;
}
if (statusIndicator) {
statusIndicator.className = 'koc-status-indicator';
statusIndicator.classList.add(`koc-status-${type}`);
}
}
// 处理下载按钮点击
async function handleDownloadClick() {
const floatingWindow = document.querySelector('.koc-floating-window');
if (!floatingWindow) {
log('悬浮窗不存在', 'error');
return;
}
const downloadBtn = floatingWindow.querySelector('.koc-download-btn');
if (!downloadBtn) {
log('下载按钮不存在', 'error');
return;
}
if (downloadBtn.disabled) {
log('按钮已禁用,请等待当前操作完成', 'warn');
return;
}
try {
log('=== 开始KOC数据采集 ===', 'info');
// 设置按钮状态为处理中
setStatus(floatingWindow, 'processing', '正在采集数据...');
downloadBtn.disabled = true;
downloadBtn.innerHTML = `
采集中...
`;
// 从当前页面URL提取product_id
function extractProductIdFromUrl() {
const url = window.location.href;
const match = url.match(/promotionRank\/([^\.]+)\.html/);
return match ? match[1] : "MUJ0VtooAuIRKA33vMWQN2QUo4A016WL";
}
// 计算时间范围(近365天,T-1结束)
function getDateRange() {
const endDate = new Date();
endDate.setDate(endDate.getDate() - 1); // T-1
const startDate = new Date();
startDate.setDate(startDate.getDate() - 365); // 近365天
return {
start_date: startDate.toISOString().split('T')[0],
end_date: endDate.toISOString().split('T')[0]
};
}
const dateRange = getDateRange();
const productId = extractProductIdFromUrl();
const apiUrl = `${CONFIG.api.baseUrl}${CONFIG.api.endpoint}`;
log(`产品ID: ${productId}`, 'debug');
log(`时间范围: ${dateRange.start_date} 到 ${dateRange.end_date}`, 'debug');
let allData = [];
let currentPage = 1;
let shouldContinue = true;
while (shouldContinue) {
log(`请求第 ${currentPage} 页数据`, 'info');
// 构造请求参数
const requestParams = new URLSearchParams({
product_id: productId,
sort: 'amount',
order_by: 'desc',
has_contact: '0',
is_new_corp: '0',
self_play: '0',
shop_play: '0',
author_play: '1',
live_room_status: '0',
category: '',
reputation_level: '-1',
follower_count: '',
keyword: '',
page: currentPage.toString(),
size: CONFIG.pagination.pageSize.toString(),
take_product_method: '1',
start_date: dateRange.start_date,
end_date: dateRange.end_date
}).toString();
log(`请求参数: ${requestParams}`, 'debug');
// 发起API请求
const response = await makeRequest(`${apiUrl}?${requestParams}`, {
method: 'GET'
});
// 解析数据
const parsedData = parseKOCData(response);
if (parsedData.length === 0) {
log(`第 ${currentPage} 页无数据,停止采集`, 'info');
break;
}
// 检查金额门槛 - 使用预估关联商品视频销售金额的最小值
const amountValues = parsedData.map(author => author.预估关联商品视频销售金额 || 0);
const minAmount = Math.min(...amountValues);
log(`第 ${currentPage} 页最小金额: ${minAmount}`, 'debug');
if (minAmount < CONFIG.pagination.amountThreshold) {
log(`达到金额门槛 ${CONFIG.pagination.amountThreshold},停止采集`, 'info');
// 过滤掉低于门槛的数据
const filteredData = parsedData.filter(author => author.预估关联商品视频销售金额 >= CONFIG.pagination.amountThreshold);
allData = allData.concat(filteredData);
shouldContinue = false;
} else {
allData = allData.concat(parsedData);
currentPage++;
// 防止无限循环,最多采集100页
if (currentPage > 100) {
log('达到最大页数限制,停止采集', 'warn');
shouldContinue = false;
}
}
// 更新状态
setStatus(floatingWindow, 'processing', `已采集 ${allData.length} 条数据`);
// 延迟1秒,避免请求过于频繁
await new Promise(resolve => setTimeout(resolve, 1000));
}
log(`=== 数据采集完成 ===`, 'info');
log(`总共采集 ${allData.length} 条符合条件的达人数据`, 'info');
// 打印完整数据到日志
log('📊 KOC数据采集结果:', 'info');
log(`📈 共采集 ${allData.length} 条达人数据`, 'info');
log('📋 数据详情:', 'info');
log(JSON.stringify(allData, null, 2), 'info');
// 显示成功提示
setStatus(floatingWindow, 'success', `已采集 ${allData.length} 条数据`);
showSuccess('采集成功', `已采集 ${allData.length} 条符合条件的达人数据,请查看控制台`);
// 5秒后恢复按钮状态
setTimeout(() => {
resetButton(floatingWindow);
}, 5000);
} catch (error) {
log(`=== 数据采集失败 ===`, 'error');
log(`错误详情: ${error.message}`, 'error');
log(`错误堆栈: ${error.stack}`, 'error');
setStatus(floatingWindow, 'error', '采集失败');
showError('采集失败', error.message);
// 5秒后恢复按钮状态
setTimeout(() => {
resetButton(floatingWindow);
}, 5000);
}
}
function getCurrentProductId() {
// 从页面URL或DOM元素中获取产品ID
const urlMatch = window.location.pathname.match(/\/product\/(\d+)/);
if (urlMatch) {
return urlMatch[1];
}
// 尝试从页面元素获取
const productElement = document.querySelector('[data-product-id]');
if (productElement) {
return productElement.getAttribute('data-product-id');
}
return null;
}
// 主逻辑
async function init() {
if (!CONFIG.enabled) {
log('脚本已禁用');
return;
}
try {
// 输出启动信息
log('🎯 KOC数据采集工具启动', 'info');
// 等待页面加载完成
await waitForPageLoad();
// 检查是否在正确的页面
if (!isValidPage()) {
log('当前页面不支持此功能');
return;
}
// 创建悬浮窗
let windowCreated = createFloatingWindow();
if (!windowCreated) {
log('首次创建悬浮窗失败,延迟重试...', 'warn');
setTimeout(() => {
windowCreated = createFloatingWindow();
if (!windowCreated) {
log('延迟重试失败,设置DOM观察器', 'warn');
setupWindowObserver();
}
}, 2000);
}
showToast('KOC数据采集工具已就绪', 'success');
} catch (error) {
log(`初始化失败: ${error.message}`, 'error');
showToast('初始化失败', 'error');
}
}
function setupWindowObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
// 检查是否已经有悬浮窗
if (document.querySelector('.koc-floating-window')) {
observer.disconnect();
return;
}
// 简单创建悬浮窗
createFloatingWindow();
observer.disconnect();
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
log('DOM观察器已设置');
}
function waitForPageLoad() {
return new Promise((resolve) => {
if (document.readyState === 'complete') {
resolve();
} else {
window.addEventListener('load', resolve);
}
});
}
function isValidPage() {
// 检查是否在蝉妈妈的推广排名页面
return window.location.hostname.includes('chanmama.com') &&
window.location.pathname.includes('/promotionRank');
}
// 备用拖拽方案
function setupSimpleDrag(floatingWindow) {
const header = floatingWindow.querySelector('.koc-window-header');
let isDragging = false;
let startX, startY, startLeft, startTop;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.koc-close-btn')) return;
isDragging = true;
startX = e.clientX;
startY = e.clientY;
// 获取当前位置,正确处理bottom/right定位
const rect = floatingWindow.getBoundingClientRect();
startLeft = rect.left;
startTop = rect.top;
floatingWindow.classList.add('dragging');
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
let newLeft = startLeft + deltaX;
let newTop = startTop + deltaY;
// 限制在视窗内
const maxX = window.innerWidth - floatingWindow.offsetWidth;
const maxY = window.innerHeight - floatingWindow.offsetHeight;
newLeft = Math.max(0, Math.min(newLeft, maxX));
newTop = Math.max(0, Math.min(newTop, maxY));
// 设置新位置,清除bottom/right定位
floatingWindow.style.left = newLeft + 'px';
floatingWindow.style.top = newTop + 'px';
floatingWindow.style.right = 'auto';
floatingWindow.style.bottom = 'auto';
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
floatingWindow.classList.remove('dragging');
}
});
}
// 设置悬浮窗事件
function setupFloatingWindowEvents(floatingWindow) {
const downloadBtn = floatingWindow.querySelector('.koc-download-btn');
const closeBtn = floatingWindow.querySelector('.koc-close-btn');
// 关闭功能
closeBtn.addEventListener('click', () => {
floatingWindow.style.opacity = '0';
floatingWindow.style.transform = 'translateY(20px) scale(0.95)';
setTimeout(() => {
floatingWindow.remove();
}, 300);
});
// 下载按钮点击 - 直接调用handleDownloadClick
downloadBtn.addEventListener('click', handleDownloadClick);
}
// 设置状态
function setStatus(floatingWindow, status, text) {
const apiStatusContainer = floatingWindow.querySelector('.koc-api-status');
const apiStatusText = floatingWindow.querySelector('.koc-api-status .koc-status-text');
if (apiStatusContainer && apiStatusText) {
// 移除所有状态类
apiStatusContainer.classList.remove('waiting', 'processing', 'success', 'error');
// 添加新状态类
apiStatusContainer.classList.add(status);
apiStatusText.textContent = text;
// 根据状态更新样式
switch (status) {
case 'processing':
apiStatusContainer.style.background = 'rgba(245, 158, 11, 0.1)';
apiStatusContainer.style.borderColor = 'rgba(245, 158, 11, 0.2)';
break;
case 'success':
apiStatusContainer.style.background = 'rgba(16, 185, 129, 0.1)';
apiStatusContainer.style.borderColor = 'rgba(16, 185, 129, 0.2)';
break;
case 'error':
apiStatusContainer.style.background = 'rgba(239, 68, 68, 0.1)';
apiStatusContainer.style.borderColor = 'rgba(239, 68, 68, 0.2)';
break;
default:
apiStatusContainer.style.background = 'rgba(16, 185, 129, 0.05)';
apiStatusContainer.style.borderColor = 'rgba(16, 185, 129, 0.1)';
}
}
}
// 重置按钮
function resetButton(floatingWindow) {
const downloadBtn = floatingWindow.querySelector('.koc-download-btn');
if (downloadBtn) {
downloadBtn.disabled = false;
downloadBtn.innerHTML = `
开始采集
`;
}
// 恢复状态显示
const apiStatusText = floatingWindow.querySelector('.koc-api-status .koc-status-text');
if (apiStatusText) {
apiStatusText.textContent = '准备采集...';
}
setStatus(floatingWindow, 'waiting', '准备采集...');
}
// 创建简洁的toast提示
function showToast(message, type = 'info') {
// 移除已存在的toast
const existingToast = document.querySelector('.koc-toast');
if (existingToast) {
existingToast.remove();
}
const toast = document.createElement('div');
toast.className = `koc-toast koc-toast-${type}`;
toast.innerHTML = `
${getToastIcon(type)}
${message}
`;
// 添加toast样式
GM_addStyle(`
.koc-toast {
position: fixed;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
padding: 16px 20px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
z-index: 10000;
display: flex;
align-items: center;
gap: 12px;
font-size: 15px;
font-weight: 600;
max-width: 350px;
min-width: 280px;
animation: kocToastSlideIn 0.3s ease;
transition: all 0.3s ease;
}
@keyframes kocToastSlideIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.koc-toast-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
.koc-toast-message {
color: #374151;
line-height: 1.4;
}
.koc-toast-success {
border-left: 5px solid #10b981;
}
.koc-toast-error {
border-left: 5px solid #ef4444;
}
.koc-toast-info {
border-left: 5px solid #3b82f6;
}
.koc-toast-warning {
border-left: 5px solid #f59e0b;
}
.koc-toast.fade-out {
opacity: 0;
transform: translateX(100%);
}
`);
document.body.appendChild(toast);
// 3秒后自动消失
setTimeout(() => {
toast.classList.add('fade-out');
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
}, 300);
}, 3000);
// 点击关闭
toast.addEventListener('click', () => {
toast.classList.add('fade-out');
setTimeout(() => {
if (toast.parentNode) {
toast.remove();
}
}, 300);
});
}
// 获取toast图标
function getToastIcon(type) {
switch (type) {
case 'success':
return ``;
case 'error':
return ``;
case 'warning':
return ``;
default:
return ``;
}
}
// 页面加载完成后运行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();