// ==UserScript==
// @name BOSS海投助手
// @namespace https://github.com/yangshengzhou03
// @version 1.2.3.2
// @description 🚀 求职者自己的神器!🧑💻Yangshengzhou,提升BOSS直聘的简历投递效率,自动批量发送简历,高效求职 💼
// @author Yangshengzhou
// @match https://www.zhipin.com/web/*
// @grant GM_xmlhttpRequest
// @run-at document-idle
// @supportURL https://github.com/yangshengzhou03
// @homepageURL https://gitee.com/yangshengzhou
// @license AGPL-3.0-or-later
// 更多详情请参见: https://www.gnu.org/licenses/agpl-3.0.html
// @icon https://static.zhipin.com/favicon.ico
// @connect zhipin.com
// @connect spark-api-open.xf-yun.com
// @noframes
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// ==/UserScript==
(function () {
'use strict';
// 配置常量
const CONFIG = {
BASIC_INTERVAL: 800, // 基本间隔(滚动职位、检查新聊天)
OPERATION_INTERVAL: 1500, // 操作间隔(立即沟通后点击留在此页的间隔时间)
CARD_STYLE: {
BACKGROUND: '#ffffff',
SHADOW: '0 6px 18px rgba(0,0,0,0.12)',
BORDER: '1px solid #e4e7ed'
},
COLORS: {
PRIMARY: '#2196f3',
SECONDARY: '#ff5722',
NEUTRAL: '#95a5a6'
},
MINI_ICON_SIZE: 40,
// 页面元素选择器配置
SELECTORS: {
JOB_CARD: 'li.job-card-box',
CHAT_BTN: 'a.op-btn-chat',
CHAT_LIST: 'ul[data-v-8e790d94=""]',
CHAT_INPUT: '#chat-input',
SEND_BUTTON: '.btn-send',
FRIEND_MESSAGE: '.item-friend .text span',
COMMON_PHRASE_BTN: '.btn-dict',
RESUME_BTN: '.toolbar-btn:contains("发简历")',
CONFIRM_SEND: 'span.btn-sure-v2'
},
// AI 配置
AI: {
MAX_REPLIES_FREE: 5, // 免费用户每日最大回复数
MAX_REPLIES_PREMIUM: 10, // 付费用户每日最大回复数
DEFAULT_ROLE: '你是有工作经验的求职者,擅长交流。你会用一些口语化的表达(如“嗯”、“呃”)和语气词(如“啊”、“呢”),使对话听起来自然。遇到不清楚的问题,你会请求对方解释,确保准确理解。回复简洁明了,避免冗长复杂的句子结构。'
},
// 消息配置
MESSAGES: {
JOB_MATCHED: '找到匹配岗位: ',
JOB_NOT_FOUND: '没有找到符合条件的岗位',
START_PROCESSING: '开始自动处理...',
STOP_PROCESSING: '已停止自动处理',
RESUME_SENT: '简历已发送',
AI_REPLYING: 'AI 正在回复...',
MAX_REPLIES_REACHED: '今日 AI 回复次数已达上限'
},
// 存储键名
STORAGE_KEYS: {
PROCESSED_HRS: 'processedHRs',
AI_REPLY_COUNT: 'aiReplyCount',
LAST_AI_DATE: 'lastAiDate',
AI_ROLE: 'aiRole',
LETTER_LAST_SHOWN: 'letterLastShown'
}
};
// 状态管理
const state = {
// 运行状态
isRunning: false,
currentIndex: 0,
// 筛选条件
filterKeyword: '',
locationKeyword: '',
// 数据缓存
jobList: [],
// UI 状态
isMinimized: false,
// 已处理对象
processedHRs: new Set(JSON.parse(localStorage.getItem('processedHRs') || '[]')),
currentTopHRKey: null,
// AI 功能
aiReplyCount: JSON.parse(localStorage.getItem('aiReplyCount') || '0'),
lastAiDate: localStorage.getItem('lastAiDate') || '',
useAiReply: true,
// 操作记录
lastMessageTime: 0,
processedJobsCount: 0,
lastProcessedDate: localStorage.getItem('lastProcessedDate') || '',
// 用户权限
isPremiumUser: localStorage.getItem('isPremiumUser') === 'true',
dailyJobLimit: 50,
// 应用设置
showWelcomeMessage: JSON.parse(localStorage.getItem('showWelcomeMessage') || 'true'),
useAutoSendResume: true,
autoScrollSpeed: 500,
customPhrases: JSON.parse(localStorage.getItem('customPhrases') || '[]'),
// 新增:UI 主题
theme: localStorage.getItem('theme') || 'light',
// 新增:操作延迟配置
actionDelays: {
click: parseInt(localStorage.getItem('clickDelay') || '250'),
pageLoad: parseInt(localStorage.getItem('pageLoadDelay') || '1000'),
modal: parseInt(localStorage.getItem('modalDelay') || '500')
},
// 新增:通知设置
notifications: {
enabled: JSON.parse(localStorage.getItem('notificationsEnabled') || 'true'),
sound: JSON.parse(localStorage.getItem('notificationSound') || 'true')
}
};
// DOM 元素引用
const elements = {
panel: null,
controlBtn: null,
log: null,
filterInput: null,
locationInput: null,
miniIcon: null,
aiRoleInput: null,
themeToggle: null,
settingsPanel: null
};
// 状态持久化工具类
class StatePersistence {
static saveState() {
localStorage.setItem('processedHRs', JSON.stringify([...state.processedHRs]));
localStorage.setItem('aiReplyCount', state.aiReplyCount);
localStorage.setItem('lastAiDate', state.lastAiDate);
localStorage.setItem('lastProcessedDate', state.lastProcessedDate);
localStorage.setItem('showWelcomeMessage', state.showWelcomeMessage);
localStorage.setItem('isPremiumUser', state.isPremiumUser);
localStorage.setItem('useAiReply', state.useAiReply);
localStorage.setItem('useAutoSendResume', state.useAutoSendResume);
localStorage.setItem('autoScrollSpeed', state.autoScrollSpeed);
localStorage.setItem('customPhrases', JSON.stringify(state.customPhrases));
// 新增:保存主题和通知设置
localStorage.setItem('theme', state.theme);
localStorage.setItem('clickDelay', state.actionDelays.click);
localStorage.setItem('pageLoadDelay', state.actionDelays.pageLoad);
localStorage.setItem('modalDelay', state.actionDelays.modal);
localStorage.setItem('notificationsEnabled', state.notifications.enabled);
localStorage.setItem('notificationSound', state.notifications.sound);
}
static loadState() {
// 初始化加载已在 state 对象定义中完成
// 这里可以添加复杂数据的初始化逻辑
}
}
// 初始化加载状态
StatePersistence.loadState();
// UI 模块:负责创建和操作界面组件
const UI = {
// 页面类型常量
PAGE_TYPES: {
JOB_LIST: 'jobList',
CHAT: 'chat'
},
// 当前页面类型
currentPageType: null,
// 初始化UI
init() {
this.currentPageType = location.pathname.includes('/chat')
? this.PAGE_TYPES.CHAT
: this.PAGE_TYPES.JOB_LIST;
this._applyTheme();
this.createControlPanel();
this.createMiniIcon();
},
// 创建主控制面板
createControlPanel() {
if (document.getElementById('boss-pro-panel')) {
document.getElementById('boss-pro-panel').remove();
}
elements.panel = this._createPanel();
const header = this._createHeader();
const controls = this._createPageControls();
elements.log = this._createLogger();
const footer = this._createFooter();
elements.panel.append(header, controls, elements.log, footer);
document.body.appendChild(elements.panel);
this._makeDraggable(elements.panel);
if (!document.getElementById('boss-settings-dialog')) {
const settingsDialog = this._createSettingsDialog();
document.body.appendChild(settingsDialog);
}
// 如果是聊天页面,隐藏筛选输入框
if (this.currentPageType === this.PAGE_TYPES.CHAT) {
if (elements.filterInput) elements.filterInput.style.display = 'none';
if (elements.locationInput) elements.locationInput.style.display = 'none';
}
},
// 应用主题配置
_applyTheme() {
CONFIG.COLORS = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? this.THEMES.JOB_LIST
: this.THEMES.CHAT;
// 将颜色配置应用到CSS变量
document.documentElement.style.setProperty('--primary-color', CONFIG.COLORS.primary);
document.documentElement.style.setProperty('--secondary-color', CONFIG.COLORS.secondary);
document.documentElement.style.setProperty('--accent-color', CONFIG.COLORS.accent);
document.documentElement.style.setProperty('--neutral-color', CONFIG.COLORS.neutral);
},
// 颜色主题配置
THEMES: {
JOB_LIST: {
primary: '#4285f4',
secondary: '#f5f7fa',
accent: '#e8f0fe',
neutral: '#6b7280'
},
CHAT: {
primary: '#34a853',
secondary: '#f0fdf4',
accent: '#dcfce7',
neutral: '#6b7280'
}
},
// 创建面板容器
_createPanel() {
const panel = document.createElement('div');
panel.id = 'boss-pro-panel';
panel.className = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? 'boss-joblist-panel'
: 'boss-chat-panel';
// 基础样式 - 使用CSS变量和改进的居中方案
const baseStyles = `
position: fixed;
top: 36px;
right: 24px;
width: clamp(300px, 80vw, 400px); /* 响应式宽度 */
border-radius: 16px;
padding: 18px;
font-family: 'Segoe UI', system-ui, sans-serif;
z-index: 2147483647;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
background: #ffffff;
box-shadow: 0 10px 25px rgba(var(--primary-rgb), 0.15);
border: 1px solid var(--accent-color);
cursor: default;
`;
panel.style.cssText = baseStyles;
// 设置RGB颜色变量供阴影使用
const rgbColor = this._hexToRgb(CONFIG.COLORS.primary);
document.documentElement.style.setProperty('--primary-rgb', rgbColor);
return panel;
},
// 创建头部
_createHeader() {
const header = document.createElement('div');
header.className = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? 'boss-header'
: 'boss-chat-header';
header.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px 15px;
margin-bottom: 15px;
border-bottom: 1px solid var(--accent-color);
`;
const title = this._createTitle();
// 创建按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = `
display: flex;
gap: 8px;
`;
// 添加清空日志按钮
const clearLogBtn = this._createIconButton('🗑', () => {
elements.log.innerHTML = `<div style="color:var(--neutral-color); margin-bottom:8px;">欢迎使用海投助手,愿您在求职路上一帆风顺!</div>`;
}, this.currentPageType === this.PAGE_TYPES.JOB_LIST
? '清空日志'
: '清空聊天记录');
// 添加设置按钮
const settingsBtn = this._createIconButton('⚙', () => {
const dialog = document.getElementById('boss-settings-dialog');
if (dialog) {
dialog.style.display = 'flex';
} else {
const newDialog = this._createSettingsDialog();
document.body.appendChild(newDialog);
newDialog.style.display = 'flex';
}
}, this.currentPageType === this.PAGE_TYPES.JOB_LIST
? '插件设置'
: 'AI人设设置');
// 添加最小化按钮
const closeBtn = this._createIconButton('✕', () => {
state.isMinimized = true;
elements.panel.style.transform = 'translateY(160%)';
elements.miniIcon.style.display = 'flex';
}, this.currentPageType === this.PAGE_TYPES.JOB_LIST
? '最小化海投面板'
: '最小化聊天面板');
// 将三个按钮添加到容器
buttonContainer.append(clearLogBtn, settingsBtn, closeBtn);
header.append(title, buttonContainer);
return header;
},
// 创建标题内容 - 修复模板字符串问题并优化居中
_createTitle() {
const title = document.createElement('div');
title.style.display = 'flex';
title.style.alignItems = 'center';
title.style.gap = '10px';
// 定义您的SVG图标
const customSvg = `
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"
style="width: 100%; height: 100%; fill: white;">
<path d="M512 116.032a160 160 0 0 1 52.224 311.232v259.008c118.144-22.272 207.552-121.088 207.552-239.36 0-25.152 21.568-45.568 48.128-45.568 26.624 0 48.128 20.416 48.128 45.632 0 184.832-158.848 335.232-354.048 335.232S160 631.808 160 446.976c0-25.152 21.568-45.632 48.128-45.632 26.624 0 48.128 20.48 48.128 45.632 0 118.144 89.088 216.96 206.976 239.296V428.416A160.064 160.064 0 0 1 512 116.032z m0 96a64 64 0 1 0 0 128 64 64 0 0 0 0-128z m-36.672 668.48l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.952 19.584a56.32 56.32 0 0 1-77.504 0l-21.952-19.584a17.92 17.92 0 0 0-24.64 0l-28.288 25.6c-9.6 8.704-23.36 6.4-30.72-4.992a29.696 29.696 0 0 1 4.16-36.672l28.352-25.6a56.32 56.32 0 0 1 77.568 0l21.888 19.584a17.92 17.92 0 0 0 24.704 0l21.824-19.52a56.32 56.32 0 0 1 77.568 0l21.888 19.52a17.92 17.92 0 0 0 24.64 0l21.952-19.52a56.32 56.32 0 0 1 77.504 0l21.952 19.52a17.92 17.92 0 0 0 24.64 0l21.824-19.52a56.32 56.32 0 0 1 77.632 0l21.824 19.52c9.664 8.704 11.52 25.152 4.224 36.672-7.296 11.52-21.12 13.696-30.72 4.992l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.888 19.584a56.32 56.32 0 0 1-77.568 0l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.888 19.584a57.408 57.408 0 0 1-38.656 15.488 58.176 58.176 0 0 1-38.784-15.488z" />
</svg>
`;
// 使用相同的SVG图标用于两种页面类型
const icon = customSvg;
// 修复模板字符串语法
const mainTitle = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? `<span style="color:var(--primary-color);">BOSS</span>海投助手`
: `<span style="color:var(--primary-color);">BOSS</span>智能聊天`;
const subTitle = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? '高效求职 · 智能匹配'
: '智能对话 · 高效沟通';
title.innerHTML = `
<div style="
width: 40px;
height: 40px;
background: var(--primary-color);
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-weight: bold;
box-shadow: 0 2px 8px rgba(var(--primary-rgb), 0.3);
">
${icon}
</div>
<div>
<h3 style="
margin: 0;
color: #2c3e50;
font-weight: 600;
font-size: 1.2rem;
">
${mainTitle}
</h3>
<span style="
font-size:0.8em;
color:var(--neutral-color);
">
${subTitle}
</span>
</div>
`;
return title;
},
// 创建页面控制区域
_createPageControls() {
if (this.currentPageType === this.PAGE_TYPES.JOB_LIST) {
return this._createJobListControls();
} else {
return this._createChatControls();
}
},
// 创建职位列表页面控制区域
_createJobListControls() {
const container = document.createElement('div');
container.className = 'boss-joblist-controls';
container.style.marginBottom = '15px';
container.style.padding = '0 10px';
// 筛选条件区域
const filterContainer = this._createFilterContainer();
elements.controlBtn = this._createTextButton(
'启动海投',
'var(--primary-color)',
() => {
toggleProcess();
}
);
// 居中控制按钮
const btnContainer = document.createElement('div');
btnContainer.style.cssText = `
display: flex;
justify-content: center;
width: 100%;
`;
btnContainer.appendChild(elements.controlBtn);
container.append(filterContainer, btnContainer);
return container;
},
// 创建聊天页面控制区域
_createChatControls() {
const container = document.createElement('div');
container.className = 'boss-chat-controls';
container.style.marginBottom = '15px';
container.style.padding = '0 10px';
// 人设和模板选择
const configRow = document.createElement('div');
configRow.style.cssText = `
display: flex;
gap: 10px;
margin-bottom: 15px;
`;
const personaCol = this._createSelectControl('AI人设:', 'ai-persona-selector', [
{ value: 'default', text: '默认' },
{ value: 'tech', text: '技术专家' },
{ value: 'product', text: '产品经理' },
{ value: 'marketing', text: '市场营销' }
]);
const templateCol = this._createSelectControl('回复模板:', 'reply-template-selector', [
{ value: 'standard', text: '标准' },
{ value: 'brief', text: '简洁' },
{ value: 'detailed', text: '详细' }
]);
elements.personaSelector = personaCol.querySelector('select');
configRow.append(personaCol, templateCol);
elements.controlBtn = this._createTextButton(
'开始智能聊天',
'var(--primary-color)',
() => {
toggleChatProcess();
}
);
// 居中控制按钮
const btnContainer = document.createElement('div');
btnContainer.style.cssText = `
display: flex;
justify-content: center;
width: 100%;
`;
btnContainer.appendChild(elements.controlBtn);
container.append(configRow, btnContainer);
return container;
},
// 创建筛选容器
_createFilterContainer() {
const filterContainer = document.createElement('div');
filterContainer.style.cssText = `
background: var(--secondary-color);
border-radius: 12px;
padding: 15px;
margin-bottom: 15px;
`;
// 岗位和地点筛选使用两列布局
const filterRow = document.createElement('div');
filterRow.style.cssText = `
display: flex;
gap: 10px;
margin-bottom: 12px;
`;
const jobFilterCol = this._createInputControl('岗位:', 'job-filter', '例如:全栈');
const locationFilterCol = this._createInputControl('地点:', 'location-filter', '例如:南昌');
elements.filterInput = jobFilterCol.querySelector('input');
elements.locationInput = locationFilterCol.querySelector('input');
filterRow.append(jobFilterCol, locationFilterCol);
// 加QQ群按钮
const joinGroupBtn = document.createElement('button');
joinGroupBtn.className = 'boss-advanced-filter-btn';
joinGroupBtn.innerHTML = '<i class="fa fa-sliders"></i> 海投服务群';
joinGroupBtn.style.cssText = `
width: 100%;
padding: 8px 10px;
background: white;
color: var(--primary-color);
border: 1px solid var(--primary-color);
border-radius: 8px;
cursor: pointer;
font-size: 14px;
text-align: center;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
`;
joinGroupBtn.addEventListener('click', () => {
window.open('https://qun.qq.com/universal-share/share?ac=1&authKey=wjyQkU9iG7wc%2BsIEOWFE6cA0ayLLBdYwpMsKYveyufXSOE5FBe7bb9xxvuNYVsEn&busi_data=eyJncm91cENvZGUiOiIxMDIxNDcxODEzIiwidG9rZW4iOiJDaFYxYVpySU9FUVJrRzkwdUZ2QlFVUTQzZzV2VS83TE9mY0NNREluaUZCR05YcnNjWmpKU2V5Q2FYTllFVlJMIiwidWluIjoiMzU1NTg0NDY3OSJ9&data=M7fVC3YlI68T2S2VpmsR20t9s_xJj6HNpF0GGk2ImSQ9iCE8fZomQgrn_ADRZF0Ee4OSY0x6k2tI5P47NlkWug&svctype=4&tempid=h5_group_info', '_blank');
});
joinGroupBtn.addEventListener('mouseenter', () => {
joinGroupBtn.style.backgroundColor = 'var(--primary-color)';
joinGroupBtn.style.color = 'white';
});
joinGroupBtn.addEventListener('mouseleave', () => {
joinGroupBtn.style.backgroundColor = 'white';
joinGroupBtn.style.color = 'var(--primary-color)';
});
filterContainer.append(filterRow, joinGroupBtn);
return filterContainer;
},
// 创建输入控件
_createInputControl(labelText, id, placeholder) {
const controlCol = document.createElement('div');
controlCol.style.cssText = 'flex: 1;';
const label = document.createElement('label');
label.textContent = labelText;
label.style.cssText = 'display:block; margin-bottom:5px; font-weight: 500; color: #333; font-size: 0.9rem;';
const input = document.createElement('input');
input.id = id;
input.placeholder = placeholder;
input.className = 'boss-filter-input';
input.style.cssText = `
width: 100%;
padding: 8px 10px;
border-radius: 8px;
border: 1px solid #d1d5db;
font-size: 14px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
transition: all 0.2s ease;
`;
controlCol.append(label, input);
return controlCol;
},
// 创建选择控件
_createSelectControl(labelText, id, options) {
const controlCol = document.createElement('div');
controlCol.style.cssText = 'flex: 1;';
const label = document.createElement('label');
label.textContent = labelText;
label.style.cssText = 'display:block; margin-bottom:5px; font-weight: 500; color: #333; font-size: 0.9rem;';
const select = document.createElement('select');
select.id = id;
select.style.cssText = `
width: 100%;
padding: 8px 10px;
border-radius: 8px;
border: 1px solid #d1d5db;
font-size: 14px;
background: white;
color: #333;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
transition: all 0.2s ease;
`;
// 添加选项
options.forEach(option => {
const opt = document.createElement('option');
opt.value = option.value;
opt.textContent = option.text;
select.appendChild(opt);
});
controlCol.append(label, select);
return controlCol;
},
// 创建日志区域
_createLogger() {
const log = document.createElement('div');
log.id = 'pro-log';
log.className = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? 'boss-joblist-log'
: 'boss-chat-log';
const height = this.currentPageType === this.PAGE_TYPES.JOB_LIST ? '180px' : '220px';
log.style.cssText = `
height: ${height};
overflow-y: auto;
background: var(--secondary-color);
border-radius: 12px;
padding: 12px;
font-size: 13px;
line-height: 1.5;
margin-bottom: 15px;
margin-left: 10px;
margin-right: 10px;
transition: all 0.3s ease;
user-select: text;
scrollbar-width: thin;
scrollbar-color: var(--primary-color) var(--secondary-color);
`;
// 自定义滚动条样式
log.innerHTML += `
<style>
#pro-log::-webkit-scrollbar {
width: 6px;
}
#pro-log::-webkit-scrollbar-track {
background: var(--secondary-color);
border-radius: 8px;
}
#pro-log::-webkit-scrollbar-thumb {
background-color: var(--primary-color);
border-radius: 8px;
}
</style>
`;
return log;
},
// 创建页脚
_createFooter() {
const footer = document.createElement('div');
footer.className = this.currentPageType === this.PAGE_TYPES.JOB_LIST
? 'boss-joblist-footer'
: 'boss-chat-footer';
footer.style.cssText = `
text-align: center;
font-size: 0.8em;
color: var(--neutral-color);
padding-top: 15px;
border-top: 1px solid var(--accent-color);
margin-top: auto;
padding: 15px;
`;
const statsContainer = document.createElement('div');
statsContainer.style.cssText = `
display: flex;
justify-content: space-around;
margin-bottom: 15px;
`;
footer.append(statsContainer, document.createTextNode('© 2025 Yangshengzhou · All Rights Reserved'));
return footer;
},
// 创建文本按钮
_createTextButton(text, bgColor, onClick) {
const btn = document.createElement('button');
btn.className = 'boss-btn';
btn.textContent = text;
btn.style.cssText = `
width: 100%;
max-width: 320px;
padding: 10px 16px;
background: ${bgColor};
color: #fff;
border: none;
border-radius: 10px;
cursor: pointer;
font-size: 15px;
font-weight: 500;
transition: all 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
`;
this._addButtonHoverEffects(btn);
btn.addEventListener('click', onClick);
return btn;
},
// 创建图标按钮
_createIconButton(icon, onClick, title) {
const btn = document.createElement('button');
btn.className = 'boss-icon-btn';
btn.innerHTML = icon;
btn.title = title;
btn.style.cssText = `
width: 32px;
height: 32px;
border-radius: 50%;
border: none;
background: ${this.currentPageType === this.PAGE_TYPES.JOB_LIST ? 'var(--accent-color)' : 'var(--accent-color)'};
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
display: flex;
justify-content: center;
align-items: center;
color: var(--primary-color);
`;
btn.addEventListener('click', onClick);
btn.addEventListener('mouseenter', () => {
btn.style.backgroundColor = 'var(--primary-color)';
btn.style.color = '#fff';
btn.style.transform = 'scale(1.1)';
});
btn.addEventListener('mouseleave', () => {
btn.style.backgroundColor = this.currentPageType === this.PAGE_TYPES.JOB_LIST ? 'var(--accent-color)' : 'var(--accent-color)';
btn.style.color = 'var(--primary-color)';
btn.style.transform = 'scale(1)';
});
return btn;
},
// 添加按钮悬停效果
_addButtonHoverEffects(btn) {
btn.addEventListener('mouseenter', () => {
btn.style.transform = 'translateY(-2px)';
btn.style.boxShadow = `0 6px 15px rgba(var(--primary-rgb), 0.3)`;
});
btn.addEventListener('mouseleave', () => {
btn.style.transform = 'translateY(0)';
btn.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
});
},
// 设置面板可拖动功能
_makeDraggable(panel) {
const header = panel.querySelector('.boss-header, .boss-chat-header');
if (!header) return;
header.style.cursor = 'move';
let isDragging = false;
let startX = 0, startY = 0;
let initialX = panel.offsetLeft, initialY = panel.offsetTop;
header.addEventListener('mousedown', (e) => {
isDragging = true;
startX = e.clientX;
startY = e.clientY;
initialX = panel.offsetLeft;
initialY = panel.offsetTop;
panel.style.transition = 'none';
panel.style.zIndex = '2147483647';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
panel.style.left = `${initialX + dx}px`;
panel.style.top = `${initialY + dy}px`;
panel.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
panel.style.transition = 'all 0.3s ease';
panel.style.zIndex = '2147483646';
}
});
},
// 创建最小化图标
createMiniIcon() {
elements.miniIcon = document.createElement('div');
elements.miniIcon.style.cssText = `
width: ${CONFIG.MINI_ICON_SIZE || 48}px;
height: ${CONFIG.MINI_ICON_SIZE || 48}px;
position: fixed;
bottom: 40px;
left: 40px;
background: var(--primary-color);
border-radius: 50%;
box-shadow: 0 6px 16px rgba(var(--primary-rgb), 0.4);
cursor: pointer;
display: none;
justify-content: center;
align-items: center;
color: #fff;
z-index: 2147483647;
transition: all 0.3s ease;
overflow: hidden;
padding: 8px; /* 添加内边距 */
`;
// 使用新的自定义SVG图标
const customSvg = `
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"
style="width: 100%; height: 100%; fill: white;">
<path d="M512 116.032a160 160 0 0 1 52.224 311.232v259.008c118.144-22.272 207.552-121.088 207.552-239.36 0-25.152 21.568-45.568 48.128-45.568 26.624 0 48.128 20.416 48.128 45.632 0 184.832-158.848 335.232-354.048 335.232S160 631.808 160 446.976c0-25.152 21.568-45.632 48.128-45.632 26.624 0 48.128 20.48 48.128 45.632 0 118.144 89.088 216.96 206.976 239.296V428.416A160.064 160.064 0 0 1 512 116.032z m0 96a64 64 0 1 0 0 128 64 64 0 0 0 0-128z m-36.672 668.48l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.952 19.584a56.32 56.32 0 0 1-77.504 0l-21.952-19.584a17.92 17.92 0 0 0-24.64 0l-28.288 25.6c-9.6 8.704-23.36 6.4-30.72-4.992a29.696 29.696 0 0 1 4.16-36.672l28.352-25.6a56.32 56.32 0 0 1 77.568 0l21.888 19.584a17.92 17.92 0 0 0 24.704 0l21.824-19.52a56.32 56.32 0 0 1 77.568 0l21.888 19.52a17.92 17.92 0 0 0 24.64 0l21.952-19.52a56.32 56.32 0 0 1 77.504 0l21.952 19.52a17.92 17.92 0 0 0 24.64 0l21.824-19.52a56.32 56.32 0 0 1 77.632 0l21.824 19.52c9.664 8.704 11.52 25.152 4.224 36.672-7.296 11.52-21.12 13.696-30.72 4.992l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.888 19.584a56.32 56.32 0 0 1-77.568 0l-21.888-19.584a17.92 17.92 0 0 0-24.64 0l-21.888 19.584a57.408 57.408 0 0 1-38.656 15.488 58.176 58.176 0 0 1-38.784-15.488z" />
</svg>
`;
elements.miniIcon.innerHTML = customSvg;
// 添加悬停效果
elements.miniIcon.addEventListener('mouseenter', () => {
elements.miniIcon.style.transform = 'scale(1.1)';
elements.miniIcon.style.boxShadow = `0 8px 20px rgba(var(--primary-rgb), 0.5)`;
});
elements.miniIcon.addEventListener('mouseleave', () => {
elements.miniIcon.style.transform = 'scale(1)';
elements.miniIcon.style.boxShadow = `0 6px 16px rgba(var(--primary-rgb), 0.4)`;
});
// 添加点击事件
elements.miniIcon.addEventListener('click', () => {
state.isMinimized = false;
elements.panel.style.transform = 'translateY(0)';
elements.miniIcon.style.display = 'none';
});
document.body.appendChild(elements.miniIcon);
},
// 创建设置对话框
// 创建设置对话框
_createSettingsDialog() {
const dialog = document.createElement('div');
dialog.id = 'boss-settings-dialog';
dialog.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: clamp(300px, 90vw, 550px);
background: #ffffff;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
z-index: 999999;
display: none;
flex-direction: column;
font-family: 'Segoe UI', sans-serif;
overflow: hidden;
transition: all 0.3s ease;
`;
// 添加对话框动画
dialog.innerHTML += `
<style>
#boss-settings-dialog {
opacity: 0;
transform: translate(-50%, -50%) scale(0.95);
}
#boss-settings-dialog.active {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.setting-item {
transition: all 0.2s ease;
}
</style>
`;
// 对话框头部
const dialogHeader = this._createDialogHeader('海投助手·BOSS设置');
// 对话框内容
const dialogContent = document.createElement('div');
dialogContent.style.cssText = `
padding: 18px;
max-height: 600px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--primary-color) var(--secondary-color);
`;
// 自定义滚动条
dialogContent.innerHTML += `
<style>
#boss-settings-dialog ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
#boss-settings-dialog ::-webkit-scrollbar-track {
background: rgba(0,0,0,0.05);
border-radius: 10px;
margin: 8px 0;
}
#boss-settings-dialog ::-webkit-scrollbar-thumb {
background: #888; /* 先用固定颜色测试 */
border-radius: 10px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: all 0.2s ease;
}
#boss-settings-dialog ::-webkit-scrollbar-thumb:hover {
background: #555; /* 先用固定颜色测试 */
box-shadow: 0 1px 5px rgba(0,0,0,0.15);
}
</style>
`;
// 选项卡导航
const tabsContainer = document.createElement('div');
tabsContainer.style.cssText = `
display: flex;
border-bottom: 1px solid var(--accent-color);
margin-bottom: 20px;
`;
const aiTab = document.createElement('button');
aiTab.textContent = 'AI人设';
aiTab.className = 'settings-tab active';
aiTab.style.cssText = `
padding: 9px 15px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px 8px 0 0;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
margin-right: 5px;
`;
const advancedTab = document.createElement('button');
advancedTab.textContent = '高级设置';
advancedTab.className = 'settings-tab';
advancedTab.style.cssText = `
padding: 9px 15px;
background: var(--secondary-color);
color: var(--neutral-color);
border: none;
border-radius: 8px 8px 0 0;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
margin-right: 5px;
`;
const intervalTab = document.createElement('button');
intervalTab.textContent = '间隔设置';
intervalTab.className = 'settings-tab';
intervalTab.style.cssText = `
padding: 9px 15px;
background: var(--secondary-color);
color: var(--neutral-color);
border: none;
border-radius: 8px 8px 0 0;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
margin-right: 5px;
`;
tabsContainer.append(aiTab, advancedTab, intervalTab);
// AI 人设设置面板
const aiSettingsPanel = document.createElement('div');
aiSettingsPanel.id = 'ai-settings-panel';
// AI 角色定位
const roleSettingResult = this._createSettingItem(
'AI角色定位',
'定义AI在对话中的角色和语气特点',
() => {
const roleInput = document.getElementById('ai-role-input');
return roleInput;
}
);
const roleSetting = roleSettingResult.settingItem;
const roleDescriptionContainer = roleSettingResult.descriptionContainer;
const roleInput = document.createElement('textarea');
roleInput.id = 'ai-role-input';
roleInput.rows = 5;
roleInput.value = localStorage.getItem('aiRole') || CONFIG.AI.DEFAULT_ROLE;
roleInput.style.cssText = `
width: 100%;
padding: 12px;
border-radius: 8px;
border: 1px solid #d1d5db;
resize: vertical;
font-size: 14px;
transition: all 0.2s ease;
margin-top: 10px;
`;
this._addFocusBlurEffects(roleInput);
roleSetting.append(roleInput);
// 预设角色下拉选择
const presetRoleSettingResult = this._createSettingItem(
'预设角色',
'选择预设的AI角色模板',
() => {
const presetSelect = document.getElementById('ai-preset-select');
return presetSelect;
}
);
const presetRoleSetting = presetRoleSettingResult.settingItem;
const presetDescriptionContainer = presetRoleSettingResult.descriptionContainer;
const presetSelect = document.createElement('select');
presetSelect.id = 'ai-preset-select';
presetSelect.style.cssText = `
width: 100%;
padding: 10px;
border-radius: 8px;
border: 1px solid #d1d5db;
font-size: 14px;
background: white;
color: #333;
margin-top: 10px;
transition: all 0.2s ease;
`;
const presets = [
{ value: 'default', text: '默认角色' },
{ value: 'tech', text: '技术专家' },
{ value: 'product', text: '产品经理' },
{ value: 'marketing', text: '市场营销' }
];
presets.forEach(preset => {
const option = document.createElement('option');
option.value = preset.value;
option.textContent = preset.text;
presetSelect.appendChild(option);
});
presetSelect.addEventListener('change', () => {
if (presetSelect.value !== 'custom') {
const presetValues = {
'default': CONFIG.AI.DEFAULT_ROLE,
'tech': '你是资深技术专家,对技术问题有深入理解。回答简洁明了,注重技术细节,同时保持专业和礼貌。遇到不确定的问题,会坦诚说明并提供相关资源。',
'product': '你是经验丰富的产品经理,善于沟通和理解需求。回复清晰有条理,注重用户体验和产品价值。会主动提问以明确需求,提供有针对性的建议。',
'marketing': '你是市场营销专家,擅长沟通和推广。语言富有感染力,能够清晰表达产品优势。回复中会突出价值点,同时保持友好和专业的态度。'
};
roleInput.value = presetValues[presetSelect.value];
}
});
presetRoleSetting.append(presetSelect);
aiSettingsPanel.append(roleSetting, presetRoleSetting);
// 高级设置面板
const advancedSettingsPanel = document.createElement('div');
advancedSettingsPanel.id = 'advanced-settings-panel';
advancedSettingsPanel.style.display = 'none';
// 自动回复开关
const autoReplySettingResult = this._createSettingItem(
'自动回复模式',
'开启后系统将自动回复消息',
() => {
const autoReplyToggle = document.querySelector('#toggle-auto-reply-mode input');
return autoReplyToggle;
}
);
const autoReplySetting = autoReplySettingResult.settingItem;
const autoReplyDescriptionContainer = autoReplySettingResult.descriptionContainer;
const autoReplyToggle = this._createToggleSwitch(
'auto-reply-mode',
localStorage.getItem('autoReply') === 'true' || true
);
autoReplyDescriptionContainer.append(autoReplyToggle);
// 自动投递开关
const autoApplySettingResult = this._createSettingItem(
'自动投递模式',
'开启后系统将自动投递符合条件的职位',
() => {
const autoApplyToggle = document.querySelector('#toggle-auto-apply-mode input');
return autoApplyToggle;
}
);
const autoApplySetting = autoApplySettingResult.settingItem;
const autoApplyDescriptionContainer = autoApplySettingResult.descriptionContainer;
const autoApplyToggle = this._createToggleSwitch(
'auto-apply-mode',
localStorage.getItem('autoApply') === 'true' || true
);
autoApplyDescriptionContainer.append(autoApplyToggle);
// 消息提醒设置
const notificationSettingResult = this._createSettingItem(
'消息提醒',
'开启后有新消息时将显示提醒',
() => {
const notificationToggle = document.querySelector('#toggle-notification input');
return notificationToggle;
}
);
const notificationSetting = notificationSettingResult.settingItem;
const notificationDescriptionContainer = notificationSettingResult.descriptionContainer;
const notificationToggle = this._createToggleSwitch(
'notification',
localStorage.getItem('notification') === 'true' || false
);
notificationDescriptionContainer.append(notificationToggle);
advancedSettingsPanel.append(autoReplySetting, autoApplySetting, notificationSetting);
// 间隔设置面板
const intervalSettingsPanel = document.createElement('div');
intervalSettingsPanel.id = 'interval-settings-panel';
intervalSettingsPanel.style.display = 'none';
// 基本间隔设置
const basicIntervalSettingResult = this._createSettingItem(
'基本间隔',
'滚动、检查新聊天等间隔时间(毫秒)',
() => {
const basicIntervalInput = document.getElementById('basic-interval-input');
return basicIntervalInput;
}
);
const basicIntervalSetting = basicIntervalSettingResult.settingItem;
const basicIntervalDescriptionContainer = basicIntervalSettingResult.descriptionContainer;
const basicIntervalInput = document.createElement('input');
basicIntervalInput.id = 'basic-interval-input';
basicIntervalInput.type = 'number';
basicIntervalInput.min = 500;
basicIntervalInput.max = 10000;
basicIntervalInput.step = 100;
basicIntervalInput.value = localStorage.getItem('basicInterval') || CONFIG.BASIC_INTERVAL;
basicIntervalInput.style.cssText = `
width: 100%;
padding: 10px;
border-radius: 8px;
border: 1px solid #d1d5db;
font-size: 14px;
margin-top: 10px;
transition: all 0.2s ease;
`;
this._addFocusBlurEffects(basicIntervalInput);
basicIntervalSetting.append(basicIntervalInput);
// 操作间隔设置
const operationIntervalSettingResult = this._createSettingItem(
'操作间隔',
'点击沟通按钮之间的间隔时间(毫秒)',
() => {
const operationIntervalInput = document.getElementById('operation-interval-input');
return operationIntervalInput;
}
);
const operationIntervalSetting = operationIntervalSettingResult.settingItem;
const operationIntervalDescriptionContainer = operationIntervalSettingResult.descriptionContainer;
const operationIntervalInput = document.createElement('input');
operationIntervalInput.id = 'operation-interval-input';
operationIntervalInput.type = 'number';
operationIntervalInput.min = 100;
operationIntervalInput.max = 2000;
operationIntervalInput.step = 50;
operationIntervalInput.value = localStorage.getItem('operationInterval') || CONFIG.OPERATION_INTERVAL;
operationIntervalInput.style.cssText = `
width: 100%;
padding: 10px;
border-radius: 8px;
border: 1px solid #d1d5db;
font-size: 14px;
margin-top: 10px;
transition: all 0.2s ease;
`;
this._addFocusBlurEffects(operationIntervalInput);
operationIntervalSetting.append(operationIntervalInput);
intervalSettingsPanel.append(basicIntervalSetting, operationIntervalSetting);
// 选项卡切换功能
aiTab.addEventListener('click', () => {
aiTab.classList.add('active');
aiTab.style.backgroundColor = 'var(--primary-color)';
aiTab.style.color = 'white';
advancedTab.classList.remove('active');
advancedTab.style.backgroundColor = 'var(--secondary-color)';
advancedTab.style.color = 'var(--neutral-color)';
intervalTab.classList.remove('active');
intervalTab.style.backgroundColor = 'var(--secondary-color)';
intervalTab.style.color = 'var(--neutral-color)';
aiSettingsPanel.style.display = 'block';
advancedSettingsPanel.style.display = 'none';
intervalSettingsPanel.style.display = 'none';
});
advancedTab.addEventListener('click', () => {
advancedTab.classList.add('active');
advancedTab.style.backgroundColor = 'var(--primary-color)';
advancedTab.style.color = 'white';
aiTab.classList.remove('active');
aiTab.style.backgroundColor = 'var(--secondary-color)';
aiTab.style.color = 'var(--neutral-color)';
intervalTab.classList.remove('active');
intervalTab.style.backgroundColor = 'var(--secondary-color)';
intervalTab.style.color = 'var(--neutral-color)';
aiSettingsPanel.style.display = 'none';
advancedSettingsPanel.style.display = 'block';
intervalSettingsPanel.style.display = 'none';
});
intervalTab.addEventListener('click', () => {
intervalTab.classList.add('active');
intervalTab.style.backgroundColor = 'var(--primary-color)';
intervalTab.style.color = 'white';
aiTab.classList.remove('active');
aiTab.style.backgroundColor = 'var(--secondary-color)';
aiTab.style.color = 'var(--neutral-color)';
advancedTab.classList.remove('active');
advancedTab.style.backgroundColor = 'var(--secondary-color)';
advancedTab.style.color = 'var(--neutral-color)';
aiSettingsPanel.style.display = 'none';
advancedSettingsPanel.style.display = 'none';
intervalSettingsPanel.style.display = 'block';
});
// 对话框底部
const dialogFooter = document.createElement('div');
dialogFooter.style.cssText = `
padding: 15px 20px;
border-top: 1px solid #e5e7eb;
display: flex;
justify-content: flex-end;
gap: 10px;
background: var(--secondary-color);
`;
const cancelBtn = this._createTextButton(
'取消',
'#e5e7eb',
() => {
dialog.style.display = 'none';
}
);
const saveBtn = this._createTextButton(
'保存设置',
'var(--primary-color)',
() => {
try {
// 保存AI角色设置
localStorage.setItem('aiRole', roleInput.value);
// 暂时还没做保存开关设置
// localStorage.setItem('autoReply', document.querySelector('#toggle-auto-reply-mode input').checked);
// localStorage.setItem('autoApply', document.querySelector('#toggle-auto-apply-mode input').checked);
// localStorage.setItem('notification', document.querySelector('#toggle-notification input').checked);
// 保存间隔设置
localStorage.setItem('basicInterval', basicIntervalInput.value);
localStorage.setItem('operationInterval', operationIntervalInput.value);
// 更新配置对象
CONFIG.BASIC_INTERVAL = parseInt(basicIntervalInput.value);
CONFIG.OPERATION_INTERVAL = parseInt(operationIntervalInput.value);
// 添加保存成功的动画反馈
const saveFeedback = document.createElement('div');
saveFeedback.textContent = '设置已保存';
saveFeedback.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: var(--primary-color);
color: white;
padding: 10px 15px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
z-index: 9999999;
opacity: 0;
transition: opacity 0.3s ease;
`;
document.body.appendChild(saveFeedback);
setTimeout(() => saveFeedback.style.opacity = '1', 10);
setTimeout(() => {
saveFeedback.style.opacity = '0';
setTimeout(() => document.body.removeChild(saveFeedback), 300);
}, 2000);
dialog.style.display = 'none';
} catch (error) {
// 添加保存失败的动画反馈
const errorFeedback = document.createElement('div');
errorFeedback.textContent = '保存失败: ' + error.message;
errorFeedback.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #e53e3e; /* 红色背景 */
color: white;
padding: 10px 15px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
z-index: 9999999;
opacity: 0;
transition: opacity 0.3s ease;
`;
document.body.appendChild(errorFeedback);
setTimeout(() => errorFeedback.style.opacity = '1', 10);
setTimeout(() => {
errorFeedback.style.opacity = '0';
setTimeout(() => document.body.removeChild(errorFeedback), 300);
}, 3000); // 失败提示显示时间延长到3秒
console.error('保存设置失败:', error);
dialog.style.display = 'none';
}
}
);
dialogFooter.append(cancelBtn, saveBtn);
// 组装整个对话框
dialogContent.append(tabsContainer, aiSettingsPanel, advancedSettingsPanel, intervalSettingsPanel);
dialog.append(dialogHeader, dialogContent, dialogFooter);
// 显示对话框时添加动画
dialog.addEventListener('DOMNodeInserted', () => {
setTimeout(() => {
dialog.classList.add('active');
}, 10);
});
// 关闭对话框时添加动画
dialog.addEventListener('DOMNodeRemoved', () => {
dialog.classList.remove('active');
});
return dialog;
},
// 创建对话框头部
_createDialogHeader(title) {
const header = document.createElement('div');
header.style.cssText = `
padding: 9px 16px;
background: var(--primary-color);
color: white;
font-size: 19px;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
`;
const titleElement = document.createElement('div');
titleElement.textContent = title;
const closeBtn = document.createElement('button');
closeBtn.innerHTML = '✕';
closeBtn.title = '关闭设置';
closeBtn.style.cssText = `
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.15);
color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
border: none;
font-size: 16px;
`;
closeBtn.addEventListener('mouseenter', () => {
closeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.25)';
});
closeBtn.addEventListener('mouseleave', () => {
closeBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.15)';
});
closeBtn.addEventListener('click', () => {
const dialog = document.getElementById('boss-settings-dialog');
dialog.style.display = 'none';
});
header.append(titleElement, closeBtn);
return header;
},
// 创建设置部分
// 修改后的创建设置项函数
_createSettingItem(title, description, controlGetter) {
const settingItem = document.createElement('div');
settingItem.className = 'setting-item';
settingItem.style.cssText = `
padding: 15px;
border-radius: 10px;
margin-bottom: 15px;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--accent-color);
display: flex;
flex-direction: column;
`;
const titleElement = document.createElement('h4');
titleElement.textContent = title;
titleElement.style.cssText = `
margin: 0 0 5px;
color: #333;
font-size: 16px;
font-weight: 500;
`;
const descElement = document.createElement('p');
descElement.textContent = description;
descElement.style.cssText = `
margin: 0;
color: var(--neutral-color);
font-size: 13px;
line-height: 1.4;
`;
// 创建描述容器,用于将文本和开关放在同一行
const descriptionContainer = document.createElement('div');
descriptionContainer.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
`;
const textContainer = document.createElement('div');
textContainer.append(titleElement, descElement);
descriptionContainer.append(textContainer);
settingItem.append(descriptionContainer);
// 添加点击事件,聚焦到对应的控制元素
settingItem.addEventListener('click', () => {
const control = controlGetter();
if (control && typeof control.focus === 'function') {
control.focus();
}
});
// 返回整个设置项和描述容器
return {
settingItem,
descriptionContainer
};
},
// 创建切换开关
_createToggleSwitch(id, isChecked) {
const container = document.createElement('div');
container.className = 'toggle-container';
container.style.cssText = 'display: flex; justify-content: space-between; align-items: center;';
const switchContainer = document.createElement('div');
switchContainer.className = 'toggle-switch';
switchContainer.style.cssText = `
position: relative;
width: 50px;
height: 26px;
border-radius: 13px;
background-color: #e5e7eb;
transition: background-color 0.3s;
cursor: pointer;
`;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `toggle-${id}`;
checkbox.checked = isChecked;
checkbox.style.display = 'none';
const slider = document.createElement('span');
slider.className = 'toggle-slider';
slider.style.cssText = `
position: absolute;
top: 3px;
left: 3px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
transition: transform 0.3s;
`;
// 监听复选框状态变化,更新滑块位置和背景色
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
switchContainer.style.backgroundColor = 'var(--primary-color)';
slider.style.transform = 'translateX(24px)';
} else {
switchContainer.style.backgroundColor = '#e5e7eb';
slider.style.transform = 'translateX(0)';
}
});
// 初始化滑块位置
if (isChecked) {
switchContainer.style.backgroundColor = 'var(--primary-color)';
slider.style.transform = 'translateX(24px)';
}
// 点击滑块也触发切换
switchContainer.addEventListener('click', () => {
checkbox.checked = !checkbox.checked;
checkbox.dispatchEvent(new Event('change'));
});
switchContainer.append(checkbox, slider);
container.append(switchContainer);
return container;
},
// 添加输入框焦点和失焦效果
_addFocusBlurEffects(element) {
element.addEventListener('focus', () => {
element.style.borderColor = CONFIG.COLORS.primary;
element.style.boxShadow = `0 0 0 3px rgba(${this._hexToRgb(CONFIG.COLORS.primary)}, 0.2)`;
});
element.addEventListener('blur', () => {
element.style.borderColor = '#d1d5db';
element.style.boxShadow = 'none';
});
},
// 十六进制颜色转RGB
_hexToRgb(hex) {
// 去除可能存在的#前缀
hex = hex.replace('#', '');
// 解析RGB值
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
return `${r}, ${g}, ${b}`;
}
};
// 核心功能模块
const Core = {
basicInterval: parseInt(localStorage.getItem('basicInterval')) || CONFIG.BASIC_INTERVAL,
operationInterval: parseInt(localStorage.getItem('operationInterval')) || CONFIG.OPERATION_INTERVAL,
async startProcessing() {
if (location.pathname.includes('/jobs'))
await this.autoScrollJobList();
while (state.isRunning) {
if (location.pathname.includes('/jobs'))
await this.processJobList();
else if (location.pathname.includes('/chat'))
await this.handleChatPage();
// 直接使用 this.operationInterval 控制主循环速度
await this.delay(this.basicInterval);
}
},
async autoScrollJobList() {
return new Promise((resolve) => {
const cardSelector = 'li.job-card-box';
const maxHistory = 3;
// 使用 this.basicInterval 作为滚动等待时间
const waitTime = this.basicInterval;
let cardCountHistory = [];
let isStopped = false;
const scrollStep = async () => {
if (isStopped) return;
window.scrollTo({
top: document.documentElement.scrollHeight,
behavior: 'smooth'
});
await this.delay(waitTime); // 使用统一延迟
const cards = document.querySelectorAll(cardSelector);
const currentCount = cards.length;
cardCountHistory.push(currentCount);
if (cardCountHistory.length > maxHistory) cardCountHistory.shift();
if (cardCountHistory.length === maxHistory && new Set(cardCountHistory).size === 1) {
this.log("当前页面岗位加载完成,开始沟通");
resolve(cards);
return;
}
scrollStep();
};
scrollStep();
this.stopAutoScroll = () => {
isStopped = true;
resolve(null);
};
});
},
// 点击Job页面的立即沟通按钮
async processJobList() {
state.jobList = Array.from(document.querySelectorAll('li.job-card-box'))
.filter(card => {
const title = card.querySelector('.job-name')?.textContent?.toLowerCase() || '';
const location = card.querySelector('.company-location')?.textContent?.toLowerCase().trim() || '';
const jobMatch = state.filterKeyword ?
state.filterKeyword.split(',').some(kw => title.includes(kw.trim())) :
true;
const locationMatch = state.locationKeyword ?
state.locationKeyword.split(',').some(kw => location.includes(kw.trim())) :
true;
return jobMatch && locationMatch;
});
if (!state.jobList.length) {
this.log('没有符合条件的职位');
toggleProcess();
return;
}
if (state.currentIndex >= state.jobList.length) {
this.resetCycle();
return;
}
const currentCard = state.jobList[state.currentIndex];
currentCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
currentCard.click();
this.log(`正在沟通:${++state.currentIndex}/${state.jobList.length}`);
// 操作间延迟使用 this.operationInterval 的一部分
await this.delay(this.operationInterval);
const chatBtn = document.querySelector('a.op-btn-chat');
if (chatBtn) {
const btnText = chatBtn.textContent.trim();
if (btnText === '立即沟通') {
chatBtn.click();
await this.handleGreetingModal();
}
}
},
async handleGreetingModal() {
// 统一使用 operationInterval 相关延迟
await this.delay(this.operationInterval);
const btn = [...document.querySelectorAll('.default-btn.cancel-btn')]
.find(b => b.textContent.trim() === '留在此页');
if (btn) {
btn.click();
await this.delay(this.operationInterval);
}
},
async handleChatPage() {
const chatList = await this.waitForElement('ul');
if (!chatList) {
this.log('没有聊天列表');
return;
}
const observer = new MutationObserver(async () => {
await this.clickLatestChat();
});
observer.observe(chatList, { childList: true });
await this.clickLatestChat();
},
getLatestChatLi() {
return document.querySelector('li[role="listitem"][class]:has(.friend-content-warp)');
},
async clickLatestChat() {
try {
const latestLi = await this.waitForElement(this.getLatestChatLi);
if (!latestLi) return;
const nameEl = latestLi.querySelector('.name-text');
const companyEl = latestLi.querySelector('.name-box span:nth-child(2)');
const name = (nameEl?.textContent || '未知').trim().replace(/\s+/g, ' ');
const company = (companyEl?.textContent || '').trim().replace(/\s+/g, ' ');
const hrKey = `${name}-${company}`.toLowerCase();
if (state.currentTopHRKey === hrKey) return;
state.currentTopHRKey = hrKey;
if (state.processedHRs.has(hrKey)) {
this.log(`发过简历: ${name}${company ? ' - ' + company : ''}`);
const avatar = latestLi.querySelector('.figure');
await this.simulateClick(avatar);
latestLi.classList.add('last-clicked');
await this.aiReply();
return;
}
if (latestLi.classList.contains('last-clicked')) return;
this.log(`开始沟通 ${name}${company ? ', 公司名: ' + company : ''}`);
const avatar = latestLi.querySelector('.figure');
await this.simulateClick(avatar);
latestLi.classList.add('last-clicked');
const isResumeSent = await this.processChatContent();
if (isResumeSent) {
state.processedHRs.add(hrKey);
localStorage.setItem('processedHRs', JSON.stringify([...state.processedHRs]));
}
} catch (error) {
this.log(`沟通出错: ${error.message}`);
}
},
async aiReply() {
try {
await this.delay(250);
const lastMessage = await this.getLastFriendMessageText();
if (!lastMessage) return false;
this.log(`对方: ${lastMessage}`);
// 检查今日AI回复次数
const today = new Date().toISOString().split('T')[0];
if (state.lastAiDate !== today) {
state.aiReplyCount = 0;
state.lastAiDate = today;
localStorage.setItem('aiReplyCount', state.aiReplyCount);
localStorage.setItem('lastAiDate', state.lastAiDate);
}
// AI回复
const maxReplies = state.aiReplyCount >= 5;
if (state.aiReplyCount >= maxReplies) {
this.log('今日AI回复已达上限,请进服务群')
return false;
}
const aiReplyText = await this.requestAi(lastMessage);
if (!aiReplyText) return false;
this.log(`AI回复: ${aiReplyText.slice(0, 30)}...`);
state.aiReplyCount++;
localStorage.setItem('aiReplyCount', state.aiReplyCount);
localStorage.setItem('lastAiDate', state.lastAiDate);
const inputBox = await this.waitForElement('#chat-input');
if (!inputBox) return false;
inputBox.textContent = '';
inputBox.focus();
document.execCommand('insertText', false, aiReplyText);
await this.delay(250);
const sendButton = document.querySelector('.btn-send');
if (sendButton) {
await this.simulateClick(sendButton);
} else {
const enterKeyEvent = new KeyboardEvent('keydown', {
key: 'Enter',
keyCode: 13,
code: 'Enter',
which: 13,
bubbles: true
});
inputBox.dispatchEvent(enterKeyEvent);
}
return true;
} catch (error) {
this.log(`AI回复出错: ${error.message}`);
return false;
}
},
async requestAi(message) {
// 解码 Authorization Token
const authToken = (function () {
const c = [
0x73, 0x64, 0x56, 0x45, 0x44, 0x41, 0x42, 0x6a, 0x5a, 0x65, 0x49, 0x6b, 0x77,
0x58, 0x4e, 0x42, 0x46, 0x4e, 0x42, 0x73, 0x3a, 0x43, 0x71, 0x4d, 0x58, 0x6a,
0x71, 0x65, 0x50, 0x56, 0x43, 0x4a, 0x62, 0x55, 0x59, 0x4a, 0x50, 0x63, 0x69,
0x70, 0x4a
];
return c.map(d => String.fromCharCode(d)).join('');
})();
// 解码 API 请求地址
const apiUrl = (function () {
const e = '68747470733a2f2f737061726b2d6170692d6f70656e2e78662d79756e2e636f6d2f76312f636861742f636f6d706c6574696f6e73';
return e.replace(/../g, f => String.fromCharCode(parseInt(f, 16)));
})();
// 构建请求体
const requestBody = {
model: 'lite',
messages: [
{
role: 'system',
content: localStorage.getItem('aiRole') || '你是有工作经验的求职者,擅长交流。你会用一些口语化的表达(如“嗯”、“呃”)和语气词(如“啊”、“呢”),使对话听起来自然。遇到不清楚的问题,你会请求对方解释,确保准确理解。回复简洁明了,避免冗长复杂的句子结构。'
},
{
role: 'user',
content: message
}
],
temperature: 0.9,
top_p: 0.8,
max_tokens: 512
};
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: apiUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + authToken
},
data: JSON.stringify(requestBody),
onload: (response) => {
console.log('API响应:', response.responseText);
try {
const result = JSON.parse(response.responseText);
if (result.code !== 0) {
throw new Error('API错误: ' + result.message + '(Code: ' + result.code + ')');
}
resolve(result.choices[0].message.content.trim());
} catch (error) {
reject(new Error('响应解析失败: ' + error.message + '\n原始响应: ' + response.responseText));
}
},
onerror: (error) => {
reject(new Error('网络请求失败: ' + error));
}
});
});
},
async getLastFriendMessageText() {
try {
await this.delay(250);
const chatContainer = document.querySelector('.chat-message .im-list');
if (!chatContainer) return null;
const messageItems = Array.from(chatContainer.querySelectorAll('li.message-item'));
const friendMessages = messageItems.filter(item =>
item.classList.contains('item-friend')
);
if (friendMessages.length === 0) return null;
const lastFriendMessage = friendMessages[friendMessages.length - 1];
const spanEl = lastFriendMessage.querySelector('.text span');
if (!spanEl) return null;
const textContent = spanEl.textContent.trim();
return textContent;
} catch (error) {
return null;
}
},
async processChatContent() {
try {
await this.delay(250);
// 点击“常用语”按钮
const dictBtn = await this.waitForElement('.btn-dict');
if (!dictBtn) {
this.log('未找到常用语按钮');
return false;
}
await this.simulateClick(dictBtn);
await this.delay(250);
// 查找常用语列表
const dictList = await this.waitForElement('ul[data-v-8e790d94=""]');
if (!dictList) {
this.log('未找到常用语列表');
return false;
}
const dictItems = dictList.querySelectorAll('li');
if (!dictItems || dictItems.length === 0) {
this.log('常用语列表为空');
return false;
}
// 遍历并点击每条常用语
for (let i = 0; i < dictItems.length; i++) {
const item = dictItems[i];
this.log(`发送常用语:第${i + 1}条/共${dictItems.length}条`);
await this.simulateClick(item);
await this.delay(250);
}
// 查找“发简历”按钮
const resumeBtn = await this.waitForElement(() => {
return [...document.querySelectorAll('.toolbar-btn')].find(
el => el.textContent.trim() === '发简历'
);
});
if (!resumeBtn) {
this.log('无法发送简历');
return false;
}
if (resumeBtn.classList.contains('unable')) {
this.log('对方未回复,您无权发送简历');
return false;
}
// 点击“发简历”
await this.simulateClick(resumeBtn);
await this.delay(250);
// 查找确认发送按钮
const confirmBtn = await this.waitForElement('span.btn-sure-v2');
if (!confirmBtn) {
this.log('未找到发送按钮');
return false;
}
await this.simulateClick(confirmBtn);
return true;
} catch (error) {
this.log(`处理出错: ${error.message}`);
return false;
}
},
async simulateClick(element) {
if (!element) return;
const rect = element.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
const dispatchMouseEvent = (type, options = {}) => {
const event = new MouseEvent(type, {
bubbles: true,
cancelable: true,
view: document.defaultView,
clientX: x,
clientY: y,
...options
});
element.dispatchEvent(event);
};
dispatchMouseEvent('mouseover');
await this.delay(30);
dispatchMouseEvent('mousemove');
await this.delay(30);
dispatchMouseEvent('mousedown', { button: 0 });
await this.delay(30);
dispatchMouseEvent('mouseup', { button: 0 });
await this.delay(30);
dispatchMouseEvent('click', { button: 0 });
},
async waitForElement(selectorOrFunction, timeout = 5000) {
return new Promise((resolve) => {
let element;
if (typeof selectorOrFunction === 'function') {
element = selectorOrFunction();
} else {
element = document.querySelector(selectorOrFunction);
}
if (element) {
return resolve(element);
}
const timeoutId = setTimeout(() => {
observer.disconnect();
resolve(null);
}, timeout);
const observer = new MutationObserver(() => {
if (typeof selectorOrFunction === 'function') {
element = selectorOrFunction();
} else {
element = document.querySelector(selectorOrFunction);
}
if (element) {
clearTimeout(timeoutId);
observer.disconnect();
resolve(element);
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
},
async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
},
resetCycle() {
toggleProcess();
this.log('所有岗位沟通完成,恭喜你即将找到理想工作!');
state.currentIndex = 0;
state.lastMessageTime = 0;
},
log(message) {
const logEntry = `[${new Date().toLocaleTimeString()}] ${message}`;
const logPanel = document.querySelector('#pro-log');
if (logPanel) {
const logItem = document.createElement('div');
logItem.className = 'log-item';
logItem.textContent = logEntry;
logPanel.appendChild(logItem);
logPanel.scrollTop = logPanel.scrollHeight;
}
}
};
// 工具函数 utils.js
function toggleProcess() {
// 反转当前运行状态
state.isRunning = !state.isRunning;
if (state.isRunning) {
// 如果是启动状态,则进行初始化设置并开始处理
// 获取用户输入的关键字并转换为小写用于匹配
state.filterKeyword = elements.filterInput.value.trim().toLowerCase();
state.locationKeyword = elements.locationInput.value.trim().toLowerCase();
// 更新按钮文本和样式
elements.controlBtn.textContent = '停止海投';
elements.controlBtn.style.background = `linear-gradient(45deg, ${CONFIG.COLORS.SECONDARY}, #f44336)`;
// 记录启动时间
const startTime = new Date();
Core.log(`开始自动海投,时间:${startTime.toLocaleTimeString()}`);
// 调用核心处理模块的启动方法
Core.startProcessing();
} else {
// 如果是停止状态,则进行清理操作
// 更新按钮文本和样式
elements.controlBtn.textContent = '启动海投';
elements.controlBtn.style.background = `linear-gradient(45deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac)`;
// 强制将运行状态设为 false(虽然上面已经反转过)
state.isRunning = false;
// 记录停止时间
const stopTime = new Date();
Core.log(`停止自动海投,时间:${stopTime.toLocaleTimeString()}`);
Core.log(`本次共沟通 ${state.currentIndex} 个岗位`);
// 重置当前索引
state.currentIndex = 0;
}
}
function toggleChatProcess() {
// 反转当前运行状态
state.isRunning = !state.isRunning;
if (state.isRunning) {
// 如果是启动状态,则进行初始化设置并开始智能聊天
// 更新按钮文本和样式
elements.controlBtn.textContent = '停止智能聊天';
elements.controlBtn.style.background = `linear-gradient(45deg, ${CONFIG.COLORS.SECONDARY}, #f44336)`;
// 记录开始时间
const startTime = new Date();
Core.log(`开始智能聊天,时间:${startTime.toLocaleTimeString()}`);
// 调用核心处理模块的开始方法
Core.startProcessing();
} else {
// 如果是停止状态,则进行清理操作
// 更新按钮文本和样式
elements.controlBtn.textContent = '开始智能聊天';
elements.controlBtn.style.background = `linear-gradient(45deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac)`;
// 强制将运行状态设为 false(虽然上面已经反转过)
state.isRunning = false;
// 记录停止时间
const stopTime = new Date();
Core.log(`停止智能聊天,时间:${stopTime.toLocaleTimeString()}`);
}
}
function showCustomAlert(message) {
// 创建遮罩层
const overlay = document.createElement('div');
overlay.id = 'custom-alert-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
backdrop-filter: blur(3px);
animation: fadeIn 0.3s ease-out;
`;
// 创建弹窗主体容器
const dialog = document.createElement('div');
dialog.id = 'custom-alert-dialog';
dialog.style.cssText = `
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
width: 90%;
max-width: 400px;
overflow: hidden;
transform: scale(0.95);
animation: scaleIn 0.3s ease-out forwards;
`;
// 创建标题栏
const header = document.createElement('div');
header.style.cssText = `
padding: 16px 24px;
background: ${CONFIG.COLORS.PRIMARY};
color: white;
font-size: 18px;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
`;
header.innerHTML = `<span>BOSS海投助手</span><i class="fa fa-info-circle ml-2"></i>`;
// 创建内容区域
const content = document.createElement('div');
content.style.cssText = `
padding: 24px;
font-size: 16px;
line-height: 1.8;
color: #333;
`;
content.innerHTML = message.replace(/\n/g, '<br>'); // 支持换行符转换为 <br>
// 创建底部按钮区域
const footer = document.createElement('div');
footer.style.cssText = `
padding: 12px 24px;
display: flex;
justify-content: center;
border-top: 1px solid #eee;
`;
// 创建确认按钮
const confirmBtn = document.createElement('button');
confirmBtn.style.cssText = `
background: ${CONFIG.COLORS.PRIMARY};
color: white;
border: none;
border-radius: 8px;
padding: 10px 24px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4);
`;
confirmBtn.textContent = '确定';
// 点击确认按钮后关闭弹窗
confirmBtn.addEventListener('click', () => {
overlay.remove();
});
// 悬浮动画效果
confirmBtn.addEventListener('mouseenter', () => {
confirmBtn.style.transform = 'translateY(-2px)';
confirmBtn.style.boxShadow = '0 6px 16px rgba(33, 150, 243, 0.5)';
});
// 移出鼠标恢复样式
confirmBtn.addEventListener('mouseleave', () => {
confirmBtn.style.transform = 'translateY(0)';
confirmBtn.style.boxShadow = '0 4px 12px rgba(33, 150, 243, 0.4)';
});
// 将按钮加入底部区域
footer.appendChild(confirmBtn);
// 组装弹窗结构
dialog.appendChild(header);
dialog.appendChild(content);
dialog.appendChild(footer);
// 将弹窗加入遮罩层
overlay.appendChild(dialog);
// 插入到页面中
document.body.appendChild(overlay);
// 添加关键帧动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from {
transform: scale(0.95);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
`;
document.head.appendChild(style);
}
/**
* 显示确认对话框
* @param {string} message - 确认消息
* @param {function} confirmCallback - 确认回调函数
* @param {function} cancelCallback - 取消回调函数(可选)
*/
function showConfirmDialog(message, confirmCallback, cancelCallback) {
// 创建遮罩层
const overlay = document.createElement('div');
overlay.id = 'custom-confirm-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
backdrop-filter: blur(3px);
animation: fadeIn 0.3s ease-out;
`;
// 创建对话框
const dialog = document.createElement('div');
dialog.id = 'custom-confirm-dialog';
dialog.style.cssText = `
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
width: 90%;
max-width: 400px;
overflow: hidden;
transform: scale(0.95);
animation: scaleIn 0.3s ease-out forwards;
`;
// 创建标题栏
const header = document.createElement('div');
header.style.cssText = `
padding: 16px 24px;
background: ${CONFIG.COLORS.PRIMARY};
color: white;
font-size: 18px;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
`;
header.innerHTML = `<span>BOSS海投助手</span><i class="fa fa-question-circle ml-2"></i>`;
// 创建内容区域
const content = document.createElement('div');
content.style.cssText = `
padding: 24px;
font-size: 16px;
line-height: 1.8;
color: #333;
`;
content.textContent = message;
// 创建按钮区域
const buttonArea = document.createElement('div');
buttonArea.style.cssText = `
padding: 12px 24px;
display: flex;
justify-content: space-around;
border-top: 1px solid #eee;
`;
// 创建确认按钮
const confirmBtn = document.createElement('button');
confirmBtn.style.cssText = `
background: ${CONFIG.COLORS.PRIMARY};
color: white;
border: none;
border-radius: 8px;
padding: 10px 24px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4);
`;
confirmBtn.textContent = '确认';
// 点击确认按钮
confirmBtn.addEventListener('click', () => {
if (typeof confirmCallback === 'function') {
confirmCallback();
}
overlay.remove();
});
// 创建取消按钮
const cancelBtn = document.createElement('button');
cancelBtn.style.cssText = `
background: #f0f0f0;
color: #666;
border: none;
border-radius: 8px;
padding: 10px 24px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
`;
cancelBtn.textContent = '取消';
// 点击取消按钮
cancelBtn.addEventListener('click', () => {
if (typeof cancelCallback === 'function') {
cancelCallback();
}
overlay.remove();
});
// 添加按钮到按钮区域
buttonArea.appendChild(cancelBtn);
buttonArea.appendChild(confirmBtn);
// 组装对话框
dialog.appendChild(header);
dialog.appendChild(content);
dialog.appendChild(buttonArea);
// 将对话框添加到遮罩层
overlay.appendChild(dialog);
// 添加到页面
document.body.appendChild(overlay);
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from {
transform: scale(0.95);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
`;
document.head.appendChild(style);
}
/**
* 显示进度条提示
* @param {string} message - 提示消息
* @param {number} progress - 进度值 (0-100)
*/
function showProgress(message, progress) {
// 如果进度条已存在,则更新它
let progressContainer = document.getElementById('progress-container');
let progressBar = document.getElementById('progress-bar');
let progressText = document.getElementById('progress-text');
if (!progressContainer) {
// 创建进度条容器
progressContainer = document.createElement('div');
progressContainer.id = 'progress-container';
progressContainer.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 24px;
width: 90%;
max-width: 400px;
z-index: 99999;
display: flex;
flex-direction: column;
gap: 16px;
`;
// 创建进度条文本
progressText = document.createElement('div');
progressText.id = 'progress-text';
progressText.style.cssText = `
font-size: 16px;
color: #333;
text-align: center;
`;
// 创建进度条背景
const progressBackground = document.createElement('div');
progressBackground.style.cssText = `
height: 12px;
background: #f0f0f0;
border-radius: 6px;
overflow: hidden;
`;
// 创建进度条
progressBar = document.createElement('div');
progressBar.id = 'progress-bar';
progressBar.style.cssText = `
height: 100%;
background: linear-gradient(90deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac);
border-radius: 6px;
transition: width 0.3s ease;
width: 0%;
`;
// 组装进度条
progressBackground.appendChild(progressBar);
progressContainer.appendChild(progressText);
progressContainer.appendChild(progressBackground);
// 添加到页面
document.body.appendChild(progressContainer);
}
// 更新进度条
progressText.textContent = message;
progressBar.style.width = `${progress}%`;
// 如果进度达到100%,则在一段时间后移除进度条
if (progress >= 100) {
setTimeout(() => {
if (progressContainer && progressContainer.parentNode) {
progressContainer.parentNode.removeChild(progressContainer);
}
}, 1000);
}
}
const letter = {
showLetterToUser: function () {
const COLORS = {
primary: '#4285f4',
primaryDark: '#1967d2',
accent: '#e8f0fe',
text: '#333',
textLight: '#666',
background: '#f8f9fa'
};
const overlay = document.createElement('div');
overlay.id = 'letter-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
backdrop-filter: blur(5px);
animation: fadeIn 0.3s ease-out;
`;
const envelopeContainer = document.createElement('div');
envelopeContainer.id = 'envelope-container';
envelopeContainer.style.cssText = `
position: relative;
width: 90%;
max-width: 650px;
height: 400px;
perspective: 1000px;
`;
const envelope = document.createElement('div');
envelope.id = 'envelope';
envelope.style.cssText = `
position: absolute;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 0.6s ease;
`;
const envelopeBack = document.createElement('div');
envelopeBack.id = 'envelope-back';
envelopeBack.style.cssText = `
position: absolute;
width: 100%;
height: 100%;
background: ${COLORS.background};
border-radius: 10px;
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
backface-visibility: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px;
cursor: pointer;
transition: all 0.3s;
`;
envelopeBack.innerHTML = `
<div style="font-size:clamp(1.5rem, 3vw, 1.8rem);font-weight:600;color:${COLORS.primary};margin-bottom:10px;">
<i class="fa fa-envelope-o mr-2"></i>致海投用户的一封信
</div>
<div style="font-size:clamp(1rem, 2vw, 1.1rem);color:${COLORS.textLight};text-align:center;">
点击开启高效求职之旅
</div>
<div style="position:absolute;bottom:20px;font-size:0.85rem;color:#999;">
© 2025 BOSS海投助手 | Yangshengzhou 版权所有
</div>
`;
envelopeBack.addEventListener('click', () => {
envelope.style.transform = 'rotateY(180deg)';
setTimeout(() => {
const content = document.getElementById('letter-content');
if (content) {
content.style.display = 'block';
content.style.animation = 'fadeInUp 0.5s ease-out forwards';
}
}, 300);
});
const envelopeFront = document.createElement('div');
envelopeFront.id = 'envelope-front';
envelopeFront.style.cssText = `
position: absolute;
width: 100%;
height: 100%;
background: #fff;
border-radius: 10px;
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
transform: rotateY(180deg);
backface-visibility: hidden;
display: flex;
flex-direction: column;
`;
const titleBar = document.createElement('div');
titleBar.style.cssText = `
padding: 20px 30px;
background: linear-gradient(135deg, ${COLORS.primary}, ${COLORS.primaryDark});
color: white;
font-size: clamp(1.2rem, 2.5vw, 1.4rem);
font-weight: 600;
border-radius: 10px 10px 0 0;
display: flex;
align-items: center;
`;
titleBar.innerHTML = `<i class="fa fa-envelope-open-o mr-2"></i>致海投助手用户:`;
const letterContent = document.createElement('div');
letterContent.id = 'letter-content';
letterContent.style.cssText = `
flex: 1;
padding: 25px 30px;
overflow-y: auto;
font-size: clamp(0.95rem, 2vw, 1.05rem);
line-height: 1.8;
color: ${COLORS.text};
background: url('https://picsum.photos/id/1068/1000/1000') center / cover no-repeat;
background-blend-mode: overlay;
background-color: rgba(255,255,255,0.95);
display: none;
`;
letterContent.innerHTML = `
<div style="margin-bottom:20px;">
<p>你好,未来的成功人士:</p>
<p class="mt-2">  展信如晤。</p>
<p class="mt-3">
  我是Yangshengzhou,曾经和你一样在求职路上反复碰壁。
简历石沉大海、面试邀约寥寥、沟通效率低下...于是我开发了这个小工具。
</p>
<p class="mt-3">
  现在,我将它分享给你,希望能帮到你:
</p>
<ul class="mt-3 ml-6 list-disc" style="text-indent:0;">
<li><strong>  一键沟通页面所有岗位</strong>,自动发送你的问候语</li>
<li><strong>  AI智能回复HR提问</strong>,24小时在线不错过任何机会</li>
<li><strong>  个性化沟通策略</strong>,大幅提升面试邀约率</li>
</ul>
<p class="mt-3">
  工具只是辅助,你的能力才是核心竞争力。
愿它成为你求职路上的得力助手,助你早日斩获Offer!
</p>
<p class="mt-2">
  如果它对你有帮助,请给我一个五星好评。
你的每一次好评,都是我继续下去的最大动力!
</p>
</div>
<div style="text-align:right;font-style:italic;color:${COLORS.textLight};text-indent:0;">
Yangshengzhou<br>
2025年6月于南昌
</div>
`;
const buttonArea = document.createElement('div');
buttonArea.style.cssText = `
padding: 15px 30px;
display: flex;
justify-content: center;
border-top: 1px solid #eee;
background: ${COLORS.background};
border-radius: 0 0 10px 10px;
`;
const startButton = document.createElement('button');
startButton.style.cssText = `
background: linear-gradient(135deg, ${COLORS.primary}, ${COLORS.primaryDark});
color: white;
border: none;
border-radius: 8px;
padding: 12px 30px;
font-size: clamp(1rem, 2vw, 1.1rem);
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 6px 16px rgba(66, 133, 244, 0.3);
outline: none;
display: flex;
align-items: center;
`;
startButton.innerHTML = `<i class="fa fa-rocket mr-2"></i>开始使用`;
startButton.addEventListener('click', () => {
envelopeContainer.style.animation = 'scaleOut 0.3s ease-in forwards';
overlay.style.animation = 'fadeOut 0.3s ease-in forwards';
setTimeout(() => {
// 移除遮罩层
if (overlay.parentNode === document.body) {
document.body.removeChild(overlay);
}
window.open('https://scriptcat.org/zh-CN/script-show-page/3381/comment', '_blank');
}, 300);
});
buttonArea.appendChild(startButton);
envelopeFront.appendChild(titleBar);
envelopeFront.appendChild(letterContent);
envelopeFront.appendChild(buttonArea);
envelope.appendChild(envelopeBack);
envelope.appendChild(envelopeFront);
envelopeContainer.appendChild(envelope);
overlay.appendChild(envelopeContainer);
document.body.appendChild(overlay);
const style = document.createElement('style');
style.textContent = `
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes fadeOut { from { opacity: 1 } to { opacity: 0 } }
@keyframes scaleOut { from { transform: scale(1); opacity: 1 } to { transform: scale(.9); opacity: 0 } }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(20px) } to { opacity: 1; transform: translateY(0) } }
#envelope-back:hover { transform: translateY(-5px); box-shadow: 0 20px 40px rgba(0,0,0,0.25); }
#envelope-front button:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(66, 133, 244, 0.4); }
#envelope-front button:active { transform: translateY(1px); }
@media (max-width: 480px) {
#envelope-container { height: 350px; }
#letter-content { font-size: 0.9rem; padding: 15px; }
}
`;
document.head.appendChild(style);
}
};
const guide = {
steps: [
{
target: 'div.city-label.active',
content: '👋 开始使用前,请先做<span class="highlight">职位筛选</span>!\n\n点击设置城市、薪资等条件,\n助手将只对符合条件的职位自动沟通~',
highlightColor: '#4285f4', // 主蓝色
arrowPosition: 'bottom',
defaultPosition: { left: '50%', top: '20%', transform: 'translateX(-50%)' }
},
{
target: 'a[ka="header-jobs"]',
content: '🚀 <span class="highlight">职位页自动操作流程</span>:\n\n1️⃣ 扫描可投递职位卡片\n2️⃣ 点击"立即沟通"发送打招呼\n3️⃣ 自动留在当前页继续下一个\n\n全程无需手动干预,高效投递!',
highlightColor: '#3367d6', // 主蓝加深10%
arrowPosition: 'bottom',
defaultPosition: { left: '25%', top: '80px' }
},
{
target: 'a[ka="header-message"]',
content: '💬 点击进入<span class="highlight">聊天界面</span>!\n\n助手会自动:\n✅ 发送常用语(建议提前设置成自我介绍)\n✅ 尝试发送您的简历附件\n\n让沟通更加轻松高效!',
highlightColor: '#2a56c6', // 主蓝加深15%
arrowPosition: 'left',
defaultPosition: { right: '150px', top: '100px' }
},
{
target: 'div.logo',
content: '🤖 <span class="highlight">聊天页智能处理逻辑</span>:\n\n🆕 新沟通:自动发送自我介绍+简历\n💬 已有回复:AI生成个性化回复\n\n您只需专注重要面试机会!',
highlightColor: '#1a73e8', // 主蓝加深20%
arrowPosition: 'right',
defaultPosition: { left: '200px', top: '20px' }
},
{
target: 'div.logo',
content: '❗ <span class="highlight">重要使用须知</span>:\n\n1. <span class="warning">BOSS平台每日打招呼上限150次</span>\n2. 聊天页仅处理当前最上方对话\n3. 打招呼后对方会自动排至顶部\n4. <span class="warning">操作过于频繁有封号风险</span>',
highlightColor: '#0d47a1', // 主蓝加深30%
arrowPosition: 'bottom',
defaultPosition: { left: '50px', top: '80px' }
}
],
currentStep: 0,
guideElement: null,
overlay: null,
highlightElements: [],
chatUrl: 'https://www.zhipin.com/web/geek/chat', // 聊天页面URL
showGuideToUser() {
// 创建遮罩层
this.overlay = document.createElement('div');
this.overlay.id = 'guide-overlay';
this.overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px);
z-index: 99997;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
`;
document.body.appendChild(this.overlay);
// 创建引导卡片
this.guideElement = document.createElement('div');
this.guideElement.id = 'guide-tooltip';
this.guideElement.style.cssText = `
position: fixed;
z-index: 99999;
width: 320px;
background: white;
border-radius: 12px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden;
opacity: 0;
transform: translateY(10px);
transition: opacity 0.3s ease, transform 0.3s ease;
`;
document.body.appendChild(this.guideElement);
// 显示遮罩层
setTimeout(() => {
this.overlay.style.opacity = '1';
// 延迟显示第一步,增强视觉层次感
setTimeout(() => {
this.showStep(0);
}, 300);
}, 100);
},
showStep(stepIndex) {
const step = this.steps[stepIndex];
if (!step) return;
this.clearHighlights();
const target = document.querySelector(step.target);
if (target) {
// 创建高亮区域
const rect = target.getBoundingClientRect();
const highlight = document.createElement('div');
highlight.className = 'guide-highlight';
highlight.style.cssText = `
position: fixed;
top: ${rect.top}px;
left: ${rect.left}px;
width: ${rect.width}px;
height: ${rect.height}px;
background: ${step.highlightColor || '#4285f4'};
opacity: 0.2;
border-radius: 4px;
z-index: 99998;
box-shadow: 0 0 0 4px ${step.highlightColor || '#4285f4'};
animation: guide-pulse 2s infinite;
`;
document.body.appendChild(highlight);
this.highlightElements.push(highlight);
// 计算提示框位置(基于目标元素)
this.setGuidePositionFromTarget(step, rect);
} else {
console.warn('引导目标元素未找到,使用默认位置:', step.target);
// 使用默认位置显示提示框
this.setGuidePositionFromDefault(step);
}
// 设置引导提示框内容
let buttonsHtml = '';
// 根据是否为最后一步生成不同的按钮
if (stepIndex === this.steps.length - 1) {
// 最后一步:只显示"完成"按钮,居中对齐
buttonsHtml = `
<div class="guide-buttons" style="display: flex; justify-content: center; padding: 16px; border-top: 1px solid #f0f0f0; background: #f9fafb;">
<button id="guide-finish-btn" style="padding: 8px 32px; background: ${step.highlightColor || '#4285f4'}; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);">
完成
</button>
</div>
`;
} else {
// 非最后一步:显示"下一步"和"跳过"按钮
buttonsHtml = `
<div class="guide-buttons" style="display: flex; justify-content: flex-end; padding: 16px; border-top: 1px solid #f0f0f0; background: #f9fafb;">
<button id="guide-skip-btn" style="padding: 8px 16px; background: white; color: #4b5563; border: 1px solid #e5e7eb; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease;">跳过</button>
<button id="guide-next-btn" style="padding: 8px 16px; background: ${step.highlightColor || '#4285f4'}; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; margin-left: 8px; transition: all 0.2s ease; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);">下一步</button>
</div>
`;
}
// 使用<div>替代<pre>,支持HTML标签
this.guideElement.innerHTML = `
<div class="guide-header" style="padding: 16px; background: ${step.highlightColor || '#4285f4'}; color: white;">
<div class="guide-title" style="font-size: 16px; font-weight: 600;">海投助手引导</div>
<div class="guide-step" style="font-size: 12px; opacity: 0.8; margin-top: 2px;">步骤 ${stepIndex + 1}/${this.steps.length}</div>
</div>
<div class="guide-content" style="padding: 20px; font-size: 14px; line-height: 1.6;">
<div style="white-space: pre-wrap; font-family: inherit; margin: 0;">${step.content}</div>
</div>
${buttonsHtml}
`;
// 重新绑定按钮事件
if (stepIndex === this.steps.length - 1) {
// 最后一步:绑定完成按钮
document.getElementById('guide-finish-btn').addEventListener('click', () => this.endGuide(true));
} else {
// 非最后一步:绑定下一步和跳过按钮
document.getElementById('guide-next-btn').addEventListener('click', () => this.nextStep());
document.getElementById('guide-skip-btn').addEventListener('click', () => this.endGuide());
}
// 添加按钮悬停效果
if (stepIndex === this.steps.length - 1) {
const finishBtn = document.getElementById('guide-finish-btn');
finishBtn.addEventListener('mouseenter', () => {
finishBtn.style.background = this.darkenColor(step.highlightColor || '#4285f4', 15);
finishBtn.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)';
});
finishBtn.addEventListener('mouseleave', () => {
finishBtn.style.background = step.highlightColor || '#4285f4';
finishBtn.style.boxShadow = '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)';
});
} else {
const nextBtn = document.getElementById('guide-next-btn');
const skipBtn = document.getElementById('guide-skip-btn');
nextBtn.addEventListener('mouseenter', () => {
nextBtn.style.background = this.darkenColor(step.highlightColor || '#4285f4', 15);
nextBtn.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)';
});
nextBtn.addEventListener('mouseleave', () => {
nextBtn.style.background = step.highlightColor || '#4285f4';
nextBtn.style.boxShadow = '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)';
});
skipBtn.addEventListener('mouseenter', () => {
skipBtn.style.background = '#f3f4f6';
});
skipBtn.addEventListener('mouseleave', () => {
skipBtn.style.background = 'white';
});
}
// 显示提示框
this.guideElement.style.opacity = '1';
this.guideElement.style.transform = 'translateY(0)';
},
// 根据目标元素计算提示框位置
setGuidePositionFromTarget(step, rect) {
let left, top;
const guideWidth = 320;
const guideHeight = 240;
// 根据箭头方向调整位置
switch (step.arrowPosition) {
case 'top':
left = rect.left + rect.width / 2 - guideWidth / 2;
top = rect.top - guideHeight - 20;
break;
case 'bottom':
left = rect.left + rect.width / 2 - guideWidth / 2;
top = rect.bottom + 20;
break;
case 'left':
left = rect.left - guideWidth - 20;
top = rect.top + rect.height / 2 - guideHeight / 2;
break;
case 'right':
left = rect.right + 20;
top = rect.top + rect.height / 2 - guideHeight / 2;
break;
default:
left = rect.right + 20;
top = rect.top;
}
// 确保提示框不超出屏幕
left = Math.max(10, Math.min(left, window.innerWidth - guideWidth - 10));
top = Math.max(10, Math.min(top, window.innerHeight - guideHeight - 10));
// 设置位置
this.guideElement.style.left = `${left}px`;
this.guideElement.style.top = `${top}px`;
this.guideElement.style.transform = 'translateY(0)';
},
// 使用默认位置显示提示框
setGuidePositionFromDefault(step) {
const position = step.defaultPosition || { left: '50%', top: '50%', transform: 'translate(-50%, -50%)' };
// 应用默认位置样式
Object.assign(this.guideElement.style, {
left: position.left,
top: position.top,
right: position.right || 'auto',
bottom: position.bottom || 'auto',
transform: position.transform || 'none'
});
},
nextStep() {
// 清除当前步骤的事件监听
const currentStep = this.steps[this.currentStep];
if (currentStep) {
const target = document.querySelector(currentStep.target);
if (target) {
target.removeEventListener('click', this.nextStep);
}
}
this.currentStep++;
if (this.currentStep < this.steps.length) {
// 隐藏当前提示框,显示下一步
this.guideElement.style.opacity = '0';
this.guideElement.style.transform = 'translateY(10px)';
setTimeout(() => {
this.showStep(this.currentStep);
}, 300);
} else {
this.endGuide(true); // 传递true表示引导已完成
}
},
clearHighlights() {
this.highlightElements.forEach(el => el.remove());
this.highlightElements = [];
},
endGuide(isCompleted = false) {
// 清除高亮和事件
this.clearHighlights();
// 淡出提示框和遮罩
this.guideElement.style.opacity = '0';
this.guideElement.style.transform = 'translateY(10px)';
this.overlay.style.opacity = '0';
// 延迟移除元素
setTimeout(() => {
if (this.overlay && this.overlay.parentNode) {
this.overlay.parentNode.removeChild(this.overlay);
}
if (this.guideElement && this.guideElement.parentNode) {
this.guideElement.parentNode.removeChild(this.guideElement);
}
// 当引导完成时打开聊天页面
if (isCompleted && this.chatUrl) {
window.open(this.chatUrl, '_blank');
}
}, 300);
// 触发引导结束事件
document.dispatchEvent(new Event('guideEnd'));
},
// 辅助函数:颜色加深
darkenColor(color, percent) {
let R = parseInt(color.substring(1, 3), 16);
let G = parseInt(color.substring(3, 5), 16);
let B = parseInt(color.substring(5, 7), 16);
R = parseInt(R * (100 - percent) / 100);
G = parseInt(G * (100 - percent) / 100);
B = parseInt(B * (100 - percent) / 100);
R = (R < 255) ? R : 255;
G = (G < 255) ? G : 255;
B = (B < 255) ? B : 255;
R = Math.round(R);
G = Math.round(G);
B = Math.round(B);
const RR = ((R.toString(16).length === 1) ? "0" + R.toString(16) : R.toString(16));
const GG = ((G.toString(16).length === 1) ? "0" + G.toString(16) : G.toString(16));
const BB = ((B.toString(16).length === 1) ? "0" + B.toString(16) : B.toString(16));
return `#${RR}${GG}${BB}`;
}
};
// 添加脉冲动画样式和高亮样式
const style = document.createElement('style');
style.textContent = `
@keyframes guide-pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.4); }
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(66, 133, 244, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(66, 133, 244, 0); }
}
.guide-content .highlight {
font-weight: 700;
color: #1a73e8;
}
.guide-content .warning {
font-weight: 700;
color: #d93025;
}
`;
document.head.appendChild(style);
// 存储键名常量
const STORAGE = {
LETTER: 'letterLastShown',
GUIDE: 'shouldShowGuide',
AI_COUNT: 'aiReplyCount',
AI_DATE: 'lastAiDate'
};
// 获取今日日期字符串
function getToday() {
return new Date().toISOString().split('T')[0];
}
// 初始化脚本
function init() {
try {
// 每日零点重置计数器
const midnight = new Date();
midnight.setDate(midnight.getDate() + 1);
midnight.setHours(0, 0, 0, 0);
setTimeout(() => {
localStorage.removeItem(STORAGE.AI_COUNT);
localStorage.removeItem(STORAGE.AI_DATE);
localStorage.removeItem(STORAGE.LETTER);
}, midnight - Date.now());
// 初始化UI
UI.init();
document.body.style.position = 'relative';
// 页面逻辑分发
const today = getToday();
if (location.pathname.includes('/jobs')) {
// 职位页:显示信件或引导
if (localStorage.getItem(STORAGE.LETTER) !== today) {
letter.showLetterToUser();
localStorage.setItem(STORAGE.LETTER, today);
} else if (localStorage.getItem(STORAGE.GUIDE) !== 'true') {
guide.showGuideToUser();
localStorage.setItem(STORAGE.GUIDE, 'true');
}
Core.log('自动沟通当前列表职位');
} else if (location.pathname.includes('/chat')) {
// 聊天页:自动回复HR
Core.log('自动发送自我介绍和简历');
}
} catch (error) {
console.error('初始化失败:', error);
if (UI.notify) UI.notify('初始化失败', 'error');
}
}
// 页面加载完成后初始化
window.addEventListener('load', init);
})();