|
@@ -1,1377 +0,0 @@
|
|
|
-import Awesome from '../background/awesome.js'
|
|
|
-import MSG_TYPE from '../static/js/common.js';
|
|
|
-import Settings from './settings.js';
|
|
|
-import Statistics from '../background/statistics.js';
|
|
|
-import toolMap from '../background/tools.js';
|
|
|
-
|
|
|
-// 工具分类定义
|
|
|
-const TOOL_CATEGORIES = [
|
|
|
- { key: 'dev', name: '开发工具类', tools: ['json-format', 'json-diff', 'code-beautify', 'code-compress', 'postman', 'websocket', 'regexp','page-timing'] },
|
|
|
- { key: 'encode', name: '编解码转换类', tools: ['en-decode', 'trans-radix', 'timestamp', 'trans-color'] },
|
|
|
- { key: 'image', name: '图像处理类', tools: ['qr-code', 'image-base64', 'svg-converter', 'chart-maker', 'poster-maker' ,'screenshot', 'color-picker'] },
|
|
|
- { key: 'productivity', name: '效率工具类', tools: ['aiagent', 'sticky-notes', 'html2markdown', 'page-monkey'] },
|
|
|
- { key: 'calculator', name: '计算工具类', tools: ['crontab', 'loan-rate', 'password'] },
|
|
|
- { key: 'other', name: '其他工具', tools: [] }
|
|
|
-];
|
|
|
-
|
|
|
-// Vue实例
|
|
|
-new Vue({
|
|
|
- el: '#marketContainer',
|
|
|
- data: {
|
|
|
- manifest: { version: '0.0.0' },
|
|
|
- searchKey: '',
|
|
|
- currentCategory: '',
|
|
|
- sortType: 'default',
|
|
|
- viewMode: 'list', // 默认网格视图
|
|
|
- categories: TOOL_CATEGORIES,
|
|
|
- favorites: new Set(),
|
|
|
- recentUsed: [],
|
|
|
- loading: true,
|
|
|
- originalTools: {}, // 保存原始工具数据
|
|
|
- currentView: 'all', // 当前视图类型(all/installed/favorites/recent)
|
|
|
- activeTools: {}, // 当前显示的工具列表
|
|
|
- installedCount: 0, // 已安装工具数量
|
|
|
-
|
|
|
- // 版本相关
|
|
|
- latestVersion: '', // 最新版本号
|
|
|
- needUpdate: false, // 是否需要更新
|
|
|
-
|
|
|
- // 设置相关
|
|
|
- showSettingsModal: false,
|
|
|
- defaultKey: 'Alt+Shift+J', // 默认快捷键
|
|
|
- countDown: 0, // 夜间模式倒计时
|
|
|
- selectedOpts: [], // 选中的选项
|
|
|
- menuDownloadCrx: false, // 菜单-插件下载
|
|
|
- menuFeHelperSeting: false, // 菜单-FeHelper设置
|
|
|
- isFirefox: false, // 是否Firefox浏览器
|
|
|
-
|
|
|
- // 打赏相关
|
|
|
- showDonateModal: false,
|
|
|
- donate: {
|
|
|
- text: '感谢你对FeHelper的认可和支持!',
|
|
|
- image: './donate.jpeg'
|
|
|
- },
|
|
|
-
|
|
|
- // 确认对话框
|
|
|
- confirmDialog: {
|
|
|
- show: false,
|
|
|
- title: '操作确认',
|
|
|
- message: '',
|
|
|
- callback: null,
|
|
|
- data: null
|
|
|
- },
|
|
|
-
|
|
|
- recentCount: 0,
|
|
|
- showDashboard: false, // 是否显示DashBoard
|
|
|
- dashboardData: null, // DashBoard数据
|
|
|
- },
|
|
|
-
|
|
|
- async created() {
|
|
|
- await this.initData();
|
|
|
- this.recentCount = (await Statistics.getRecentUsedTools(10)).length;
|
|
|
- // 初始化后更新已安装工具数量
|
|
|
- this.updateInstalledCount();
|
|
|
- // 恢复用户的视图模式设置
|
|
|
- this.loadViewMode();
|
|
|
- // 加载设置项
|
|
|
- this.loadSettings();
|
|
|
- // 检查浏览器类型
|
|
|
- this.checkBrowserType();
|
|
|
- // 检查版本更新
|
|
|
- this.checkVersionUpdate();
|
|
|
-
|
|
|
- // 检查URL中是否有donate_from参数
|
|
|
- this.checkDonateParam();
|
|
|
- },
|
|
|
-
|
|
|
- computed: {
|
|
|
- filteredTools() {
|
|
|
- if (this.loading) {
|
|
|
- return [];
|
|
|
- }
|
|
|
-
|
|
|
- // 获取当前工具列表
|
|
|
- let result = Object.values(this.activeTools).map(tool => ({
|
|
|
- ...tool,
|
|
|
- favorite: this.favorites.has(tool.key)
|
|
|
- }));
|
|
|
-
|
|
|
- // 搜索过滤
|
|
|
- if (this.searchKey) {
|
|
|
- const key = this.searchKey.toLowerCase();
|
|
|
- result = result.filter(tool =>
|
|
|
- tool.name.toLowerCase().includes(key) ||
|
|
|
- tool.tips.toLowerCase().includes(key)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // 分类过滤,在所有视图下生效
|
|
|
- if (this.currentCategory) {
|
|
|
- const category = TOOL_CATEGORIES.find(c => c.key === this.currentCategory);
|
|
|
- const categoryTools = category ? category.tools : [];
|
|
|
- result = result.filter(tool => categoryTools.includes(tool.key));
|
|
|
- }
|
|
|
-
|
|
|
- // 排序
|
|
|
- switch (this.sortType) {
|
|
|
- case 'newest':
|
|
|
- result.sort((a, b) => (b.updateTime || 0) - (a.updateTime || 0));
|
|
|
- break;
|
|
|
- case 'hot':
|
|
|
- result.sort((a, b) => (b.updateTime || 0) - (a.updateTime || 0));
|
|
|
- break;
|
|
|
- default:
|
|
|
- const allTools = TOOL_CATEGORIES.reduce((acc, category) => {
|
|
|
- acc.push(...category.tools);
|
|
|
- return acc;
|
|
|
- }, []);
|
|
|
-
|
|
|
- result.sort((a, b) => {
|
|
|
- const indexA = allTools.indexOf(a.key);
|
|
|
- const indexB = allTools.indexOf(b.key);
|
|
|
-
|
|
|
- // 如果工具不在任何类别中,放到最后
|
|
|
- if (indexA === -1 && indexB === -1) {
|
|
|
- return a.key.localeCompare(b.key); // 字母顺序排序
|
|
|
- }
|
|
|
- if (indexA === -1) return 1;
|
|
|
- if (indexB === -1) return -1;
|
|
|
-
|
|
|
- return indexA - indexB;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- methods: {
|
|
|
- async initData() {
|
|
|
- try {
|
|
|
- this.loading = true;
|
|
|
-
|
|
|
- // 获取manifest信息
|
|
|
- const manifest = await chrome.runtime.getManifest();
|
|
|
- this.manifest = manifest;
|
|
|
-
|
|
|
- // 从 Awesome.getAllTools 获取工具列表
|
|
|
- const tools = await Awesome.getAllTools();
|
|
|
-
|
|
|
- // 获取收藏数据
|
|
|
- const favorites = await this.getFavoritesData();
|
|
|
- this.favorites = new Set(favorites);
|
|
|
-
|
|
|
- // 获取最近使用数据
|
|
|
- const recentUsed = await this.getRecentUsedData();
|
|
|
- this.recentUsed = recentUsed;
|
|
|
- this.recentCount = recentUsed.length;
|
|
|
-
|
|
|
- // 获取已安装工具列表
|
|
|
- const installedTools = await Awesome.getInstalledTools();
|
|
|
-
|
|
|
- // 处理工具数据
|
|
|
- const processedTools = {};
|
|
|
- Object.entries(tools).forEach(([key, tool]) => {
|
|
|
- // 检查工具是否已安装
|
|
|
- const isInstalled = installedTools.hasOwnProperty(key);
|
|
|
- // 检查是否有右键菜单
|
|
|
- const hasMenu = tool.menu || false;
|
|
|
-
|
|
|
- processedTools[key] = {
|
|
|
- ...tool,
|
|
|
- key, // 添加key到工具对象中
|
|
|
- updateTime: Date.now() - Math.floor(Math.random() * 30) * 24 * 60 * 60 * 1000,
|
|
|
- installed: isInstalled, // 使用实时安装状态
|
|
|
- inContextMenu: hasMenu, // 使用实时菜单状态
|
|
|
- systemInstalled: tool.systemInstalled || false, // 是否系统预装
|
|
|
- favorite: this.favorites.has(key)
|
|
|
- };
|
|
|
- });
|
|
|
-
|
|
|
- this.originalTools = processedTools;
|
|
|
-
|
|
|
- // 初始化activeTools为所有工具
|
|
|
- this.activeTools = { ...processedTools };
|
|
|
-
|
|
|
- // 更新"其他工具"类别
|
|
|
- this.updateOtherCategory(Object.keys(processedTools));
|
|
|
-
|
|
|
- // 默认选中"全部分类"
|
|
|
- this.currentCategory = '';
|
|
|
- } catch (error) {
|
|
|
- console.error('初始化数据失败:', error);
|
|
|
- } finally {
|
|
|
- this.loading = false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 更新"其他工具"类别,将未分类的工具添加到此类别
|
|
|
- updateOtherCategory(allToolKeys) {
|
|
|
- // 获取所有已分类的工具
|
|
|
- const categorizedTools = new Set();
|
|
|
- TOOL_CATEGORIES.forEach(category => {
|
|
|
- if (category.key !== 'other') {
|
|
|
- category.tools.forEach(tool => categorizedTools.add(tool));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 找出未分类的工具
|
|
|
- const uncategorizedTools = allToolKeys.filter(key => !categorizedTools.has(key));
|
|
|
-
|
|
|
- // 更新"其他工具"类别
|
|
|
- const otherCategory = TOOL_CATEGORIES.find(category => category.key === 'other');
|
|
|
- if (otherCategory) {
|
|
|
- otherCategory.tools = uncategorizedTools;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 检查版本更新
|
|
|
- async checkVersionUpdate() {
|
|
|
- try {
|
|
|
- // 获取已安装的版本号
|
|
|
- const currentVersion = this.manifest.version;
|
|
|
-
|
|
|
- // 尝试从本地存储获取最新版本信息,避免频繁请求
|
|
|
- const cachedData = await new Promise(resolve => {
|
|
|
- chrome.storage.local.get('fehelper_latest_version_data', data => {
|
|
|
- resolve(data.fehelper_latest_version_data || null);
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- // 检查是否需要重新获取版本信息:
|
|
|
- // 1. 缓存不存在
|
|
|
- // 2. 缓存已过期(超过24小时)
|
|
|
- // 3. 缓存的当前版本与实际版本不同(说明插件已更新)
|
|
|
- const now = Date.now();
|
|
|
- const cacheExpired = !cachedData || !cachedData.timestamp || (now - cachedData.timestamp > 24 * 60 * 60 * 1000);
|
|
|
- const versionChanged = cachedData && cachedData.currentVersion !== currentVersion;
|
|
|
-
|
|
|
- if (cacheExpired || versionChanged) {
|
|
|
- try {
|
|
|
- console.log('开始获取最新版本信息...');
|
|
|
- // 使用shields.io的JSON API获取最新版本号
|
|
|
- const response = await fetch('https://img.shields.io/chrome-web-store/v/pkgccpejnmalmdinmhkkfafefagiiiad.json');
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`HTTP错误:${response.status}`);
|
|
|
- }
|
|
|
-
|
|
|
- const data = await response.json();
|
|
|
- // 提取版本号 - shields.io返回的数据中包含版本信息
|
|
|
- let latestVersion = '';
|
|
|
- if (data && data.value) {
|
|
|
- // 去掉版本号前的'v'字符(如果有)
|
|
|
- latestVersion = data.value.replace(/^v/, '');
|
|
|
- console.log('获取到最新版本号:', latestVersion);
|
|
|
- }
|
|
|
-
|
|
|
- // 比较版本号
|
|
|
- const needUpdate = this.compareVersions(currentVersion, latestVersion) < 0;
|
|
|
- console.log('当前版本:', currentVersion, '最新版本:', latestVersion, '需要更新:', needUpdate);
|
|
|
-
|
|
|
- // 保存到本地存储中
|
|
|
- await chrome.storage.local.set({
|
|
|
- 'fehelper_latest_version_data': {
|
|
|
- timestamp: now,
|
|
|
- currentVersion, // 保存当前检查时的版本号
|
|
|
- latestVersion,
|
|
|
- needUpdate
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- this.latestVersion = latestVersion;
|
|
|
- this.needUpdate = needUpdate;
|
|
|
- } catch (fetchError) {
|
|
|
- console.error('获取最新版本信息失败:', fetchError);
|
|
|
- // 获取失败时不显示更新按钮
|
|
|
- this.needUpdate = false;
|
|
|
-
|
|
|
- // 如果是版本变更导致的重新检查,但获取失败,则使用缓存数据
|
|
|
- if (versionChanged && cachedData) {
|
|
|
- this.latestVersion = cachedData.latestVersion || '';
|
|
|
- // 比较新的currentVersion和缓存的latestVersion
|
|
|
- this.needUpdate = this.compareVersions(currentVersion, cachedData.latestVersion) < 0;
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 使用缓存数据
|
|
|
- console.log('使用缓存的版本信息');
|
|
|
- this.latestVersion = cachedData.latestVersion || '';
|
|
|
- this.needUpdate = cachedData.needUpdate || false;
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('检查版本更新失败:', error);
|
|
|
- this.needUpdate = false; // 出错时不显示更新提示
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 比较版本号:如果v1 < v2返回-1,v1 = v2返回0,v1 > v2返回1
|
|
|
- compareVersions(v1, v2) {
|
|
|
- // 将版本号拆分为数字数组
|
|
|
- const v1Parts = v1.split('.').map(Number);
|
|
|
- const v2Parts = v2.split('.').map(Number);
|
|
|
-
|
|
|
- // 计算两个版本号中较长的长度
|
|
|
- const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
|
-
|
|
|
- // 比较每一部分
|
|
|
- for (let i = 0; i < maxLength; i++) {
|
|
|
- // 获取当前部分,如果不存在则视为0
|
|
|
- const part1 = v1Parts[i] || 0;
|
|
|
- const part2 = v2Parts[i] || 0;
|
|
|
-
|
|
|
- // 比较当前部分
|
|
|
- if (part1 < part2) return -1;
|
|
|
- if (part1 > part2) return 1;
|
|
|
- }
|
|
|
-
|
|
|
- // 所有部分都相等
|
|
|
- return 0;
|
|
|
- },
|
|
|
-
|
|
|
- // 打开Chrome商店页面
|
|
|
- openStorePage() {
|
|
|
- try {
|
|
|
- console.log('开始请求检查更新...');
|
|
|
-
|
|
|
- // 使用Chrome Extension API请求检查更新
|
|
|
- // Manifest V3中requestUpdateCheck返回Promise,结果是一个对象而不是数组
|
|
|
- chrome.runtime.requestUpdateCheck().then(result => {
|
|
|
- // 正确获取status和details,它们是result对象的属性
|
|
|
- console.log('更新检查结果:', result);
|
|
|
- const status = result.status;
|
|
|
- const details = result.details;
|
|
|
-
|
|
|
- console.log('更新检查状态:', status, '详情:', details);
|
|
|
- this.handleUpdateStatus(status, details);
|
|
|
- }).catch(error => {
|
|
|
- console.error('更新检查失败:', error);
|
|
|
- this.handleUpdateError(error);
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('请求更新出错:', error);
|
|
|
- this.handleUpdateError(error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 处理更新状态
|
|
|
- handleUpdateStatus(status, details) {
|
|
|
- console.log(`处理更新状态: ${status}`, details);
|
|
|
-
|
|
|
- if (status === 'update_available') {
|
|
|
- console.log('发现更新:', details);
|
|
|
-
|
|
|
- // 显示更新通知
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 更新',
|
|
|
- message: '已发现新版本,正在更新...'
|
|
|
- });
|
|
|
-
|
|
|
- // 重新加载扩展以应用更新
|
|
|
- setTimeout(() => {
|
|
|
- console.log('重新加载扩展...');
|
|
|
- chrome.runtime.reload();
|
|
|
- }, 1000);
|
|
|
- } else if (status === 'no_update') {
|
|
|
- // 如果没有可用更新,但用户点击了更新按钮
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 更新',
|
|
|
- message: '您的FeHelper已经是最新版本。'
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 其他情况,如更新检查失败等
|
|
|
- console.log('其他更新状态:', status);
|
|
|
-
|
|
|
- // 备选方案:跳转到官方网站
|
|
|
- chrome.tabs.create({
|
|
|
- url: 'https://baidufe.com/fehelper'
|
|
|
- });
|
|
|
-
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 更新',
|
|
|
- message: '自动更新失败,请访问FeHelper官网手动获取最新版本。'
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 处理更新错误
|
|
|
- handleUpdateError(error) {
|
|
|
- console.error('更新过程中出错:', error);
|
|
|
-
|
|
|
- // 出错时跳转到官方网站
|
|
|
- chrome.tabs.create({
|
|
|
- url: 'https://baidufe.com/fehelper'
|
|
|
- });
|
|
|
-
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 更新错误',
|
|
|
- message: '更新过程中出现错误,请手动检查更新。'
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 显示通知的统一方法
|
|
|
- showNotification(options) {
|
|
|
- try {
|
|
|
- console.log('准备显示通知:', options);
|
|
|
-
|
|
|
- // 定义通知ID,方便后续关闭
|
|
|
- const notificationId = 'fehelper-update-notification';
|
|
|
- const simpleNotificationId = 'fehelper-simple-notification';
|
|
|
-
|
|
|
- // 直接尝试创建通知,不检查权限
|
|
|
- // Chrome扩展在manifest中已声明notifications权限,应该可以直接使用
|
|
|
- const notificationOptions = {
|
|
|
- type: 'basic',
|
|
|
- iconUrl: chrome.runtime.getURL('static/img/fe-48.png'),
|
|
|
- title: options.title || 'FeHelper',
|
|
|
- message: options.message || '',
|
|
|
- priority: 2,
|
|
|
- requireInteraction: false, // 改为false,因为我们会手动关闭
|
|
|
- silent: false // 播放音效
|
|
|
- };
|
|
|
-
|
|
|
- console.log('通知选项:', notificationOptions);
|
|
|
-
|
|
|
- // 首先尝试直接创建通知
|
|
|
- chrome.notifications.create(notificationId, notificationOptions, (createdId) => {
|
|
|
- const error = chrome.runtime.lastError;
|
|
|
- if (error) {
|
|
|
- console.error('创建通知出错:', error);
|
|
|
-
|
|
|
- // 通知创建失败,尝试使用alert作为备选方案
|
|
|
- alert(`${options.title}: ${options.message}`);
|
|
|
-
|
|
|
- // 再尝试使用不同的选项创建通知
|
|
|
- const simpleOptions = {
|
|
|
- type: 'basic',
|
|
|
- iconUrl: chrome.runtime.getURL('static/img/fe-48.png'),
|
|
|
- title: options.title || 'FeHelper',
|
|
|
- message: options.message || ''
|
|
|
- };
|
|
|
-
|
|
|
- // 使用简化选项再次尝试
|
|
|
- chrome.notifications.create(simpleNotificationId, simpleOptions, (simpleId) => {
|
|
|
- if (chrome.runtime.lastError) {
|
|
|
- console.error('简化通知创建也失败:', chrome.runtime.lastError);
|
|
|
- } else {
|
|
|
- console.log('简化通知已创建,ID:', simpleId);
|
|
|
-
|
|
|
- // 3秒后自动关闭简化通知
|
|
|
- setTimeout(() => {
|
|
|
- chrome.notifications.clear(simpleId, (wasCleared) => {
|
|
|
- console.log('简化通知已关闭:', wasCleared);
|
|
|
- });
|
|
|
- }, 3000);
|
|
|
- }
|
|
|
- });
|
|
|
- } else {
|
|
|
- console.log('通知已成功创建,ID:', createdId);
|
|
|
-
|
|
|
- // 3秒后自动关闭通知
|
|
|
- setTimeout(() => {
|
|
|
- chrome.notifications.clear(createdId, (wasCleared) => {
|
|
|
- console.log('通知已关闭:', wasCleared);
|
|
|
- });
|
|
|
- }, 3000);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 同时使用内置UI显示消息
|
|
|
- this.showInPageNotification(options);
|
|
|
- } catch (error) {
|
|
|
- console.error('显示通知时出错:', error);
|
|
|
- // 降级为alert
|
|
|
- alert(`${options.title}: ${options.message}`);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 在页面内显示通知消息
|
|
|
- showInPageNotification(options) {
|
|
|
- try {
|
|
|
- // 创建一个通知元素
|
|
|
- const notificationEl = document.createElement('div');
|
|
|
- notificationEl.className = 'in-page-notification';
|
|
|
- notificationEl.innerHTML = `
|
|
|
- <div class="notification-content">
|
|
|
- <div class="notification-title">${options.title || 'FeHelper'}</div>
|
|
|
- <div class="notification-message">${options.message || ''}</div>
|
|
|
- </div>
|
|
|
- <button class="notification-close">×</button>
|
|
|
- `;
|
|
|
-
|
|
|
- // 添加样式
|
|
|
- const style = document.createElement('style');
|
|
|
- style.textContent = `
|
|
|
- .in-page-notification {
|
|
|
- position: fixed;
|
|
|
- bottom: 20px;
|
|
|
- right: 20px;
|
|
|
- background-color: #4285f4;
|
|
|
- color: white;
|
|
|
- padding: 15px;
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
- z-index: 9999;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- min-width: 300px;
|
|
|
- animation: slideIn 0.3s ease-out;
|
|
|
- }
|
|
|
- .notification-content {
|
|
|
- flex: 1;
|
|
|
- }
|
|
|
- .notification-title {
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 5px;
|
|
|
- }
|
|
|
- .notification-message {
|
|
|
- font-size: 14px;
|
|
|
- }
|
|
|
- .notification-close {
|
|
|
- background: none;
|
|
|
- border: none;
|
|
|
- color: white;
|
|
|
- font-size: 20px;
|
|
|
- cursor: pointer;
|
|
|
- margin-left: 10px;
|
|
|
- padding: 0 5px;
|
|
|
- }
|
|
|
- @keyframes slideIn {
|
|
|
- from { transform: translateX(100%); opacity: 0; }
|
|
|
- to { transform: translateX(0); opacity: 1; }
|
|
|
- }
|
|
|
- @keyframes slideOut {
|
|
|
- from { transform: translateX(0); opacity: 1; }
|
|
|
- to { transform: translateX(100%); opacity: 0; }
|
|
|
- }
|
|
|
- `;
|
|
|
-
|
|
|
- // 添加到页面
|
|
|
- document.head.appendChild(style);
|
|
|
- document.body.appendChild(notificationEl);
|
|
|
-
|
|
|
- // 点击关闭按钮移除通知
|
|
|
- const closeBtn = notificationEl.querySelector('.notification-close');
|
|
|
- if (closeBtn) {
|
|
|
- closeBtn.addEventListener('click', () => {
|
|
|
- notificationEl.style.animation = 'slideOut 0.3s ease-out forwards';
|
|
|
- notificationEl.addEventListener('animationend', () => {
|
|
|
- notificationEl.remove();
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 3秒后自动移除(从5秒改为3秒)
|
|
|
- setTimeout(() => {
|
|
|
- notificationEl.style.animation = 'slideOut 0.3s ease-out forwards';
|
|
|
- notificationEl.addEventListener('animationend', () => {
|
|
|
- notificationEl.remove();
|
|
|
- });
|
|
|
- }, 3000);
|
|
|
-
|
|
|
- console.log('页内通知已显示,将在3秒后自动关闭');
|
|
|
- } catch (error) {
|
|
|
- console.error('创建页内通知出错:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- async getFavoritesData() {
|
|
|
- return new Promise((resolve) => {
|
|
|
- chrome.storage.local.get('favorites', (result) => {
|
|
|
- resolve(result.favorites || []);
|
|
|
- });
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- async getRecentUsedData() {
|
|
|
- // 直接从Statistics模块获取最近使用的工具
|
|
|
- return await Statistics.getRecentUsedTools(10);
|
|
|
- },
|
|
|
-
|
|
|
- async saveFavorites() {
|
|
|
- try {
|
|
|
- await chrome.storage.local.set({
|
|
|
- favorites: Array.from(this.favorites)
|
|
|
- });
|
|
|
- // 更新工具的收藏状态
|
|
|
- Object.keys(this.originalTools).forEach(key => {
|
|
|
- this.originalTools[key].favorite = this.favorites.has(key);
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('保存收藏失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- handleSearch() {
|
|
|
- // 搜索时不重置视图类型,允许在已过滤的结果中搜索
|
|
|
- },
|
|
|
-
|
|
|
- handleCategoryChange(category) {
|
|
|
- // 切换到全部工具视图
|
|
|
- if (this.currentView !== 'all') {
|
|
|
- this.currentView = 'all';
|
|
|
- this.updateActiveTools('all');
|
|
|
- }
|
|
|
- this.currentCategory = category;
|
|
|
- this.searchKey = '';
|
|
|
- // 确保工具显示正确
|
|
|
- this.activeTools = { ...this.originalTools };
|
|
|
- this.showDashboard = false;
|
|
|
- },
|
|
|
-
|
|
|
- handleSort() {
|
|
|
- // 排序逻辑已在computed中实现
|
|
|
- },
|
|
|
-
|
|
|
- getCategoryCount(categoryKey) {
|
|
|
- const category = TOOL_CATEGORIES.find(c => c.key === categoryKey);
|
|
|
- const categoryTools = category ? category.tools : [];
|
|
|
- return categoryTools.length;
|
|
|
- },
|
|
|
-
|
|
|
- async getInstalledCount() {
|
|
|
- try {
|
|
|
- // 使用Awesome.getInstalledTools实时获取已安装工具数量
|
|
|
- const installedTools = await Awesome.getInstalledTools();
|
|
|
- return Object.keys(installedTools).length;
|
|
|
- } catch (error) {
|
|
|
- console.error('获取已安装工具数量失败:', error);
|
|
|
- // 回退到本地数据
|
|
|
- return Object.values(this.originalTools).filter(tool =>
|
|
|
- tool.installed || tool.systemInstalled || false
|
|
|
- ).length;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- getFavoritesCount() {
|
|
|
- return this.favorites.size;
|
|
|
- },
|
|
|
-
|
|
|
- getToolCategory(toolKey) {
|
|
|
- for (const category of TOOL_CATEGORIES) {
|
|
|
- if (category.tools.includes(toolKey)) {
|
|
|
- return category.key;
|
|
|
- }
|
|
|
- }
|
|
|
- return 'other';
|
|
|
- },
|
|
|
-
|
|
|
- async showMyInstalled() {
|
|
|
- this.currentView = 'installed';
|
|
|
- this.currentCategory = '';
|
|
|
- this.searchKey = '';
|
|
|
- await this.updateActiveTools('installed');
|
|
|
- // 更新已安装工具数量
|
|
|
- await this.updateInstalledCount();
|
|
|
- this.showDashboard = false;
|
|
|
- },
|
|
|
-
|
|
|
- showMyFavorites() {
|
|
|
- this.currentView = 'favorites';
|
|
|
- this.currentCategory = '';
|
|
|
- this.searchKey = '';
|
|
|
- this.updateActiveTools('favorites');
|
|
|
- this.showDashboard = false;
|
|
|
- },
|
|
|
-
|
|
|
- async showRecentUsed() {
|
|
|
- this.currentView = 'recent';
|
|
|
- this.currentCategory = '';
|
|
|
- this.searchKey = '';
|
|
|
- // 拉取DashBoard数据并显示
|
|
|
- this.dashboardData = await Statistics.getDashboardData();
|
|
|
- this.showDashboard = true;
|
|
|
- // 不再更新工具列表
|
|
|
- },
|
|
|
-
|
|
|
- // 关闭DashBoard,恢复工具列表
|
|
|
- closeDashboard() {
|
|
|
- this.showDashboard = false;
|
|
|
- this.currentView = 'all';
|
|
|
- this.updateActiveTools('all');
|
|
|
- },
|
|
|
-
|
|
|
- // 重置工具列表到原始状态
|
|
|
- resetTools() {
|
|
|
- this.currentView = 'all';
|
|
|
- },
|
|
|
-
|
|
|
- // 安装工具
|
|
|
- async installTool(toolKey) {
|
|
|
- try {
|
|
|
- // 查找可能存在的按钮元素
|
|
|
- const btnElement = document.querySelector(`button[data-tool="${toolKey}"]`);
|
|
|
- let elProgress = null;
|
|
|
-
|
|
|
- // 如果是通过按钮点击调用的,获取进度条元素
|
|
|
- if (btnElement) {
|
|
|
- if (btnElement.getAttribute('data-undergoing') === '1') {
|
|
|
- return false;
|
|
|
- }
|
|
|
- btnElement.setAttribute('data-undergoing', '1');
|
|
|
- elProgress = btnElement.querySelector('span.x-progress');
|
|
|
- }
|
|
|
-
|
|
|
- // 显示安装进度
|
|
|
- let pt = 1;
|
|
|
- await Awesome.install(toolKey);
|
|
|
-
|
|
|
- // 只有当进度条元素存在时才更新文本内容
|
|
|
- if (elProgress) {
|
|
|
- elProgress.textContent = `(${pt}%)`;
|
|
|
- let ptInterval = setInterval(() => {
|
|
|
- elProgress.textContent = `(${pt}%)`;
|
|
|
- pt += Math.floor(Math.random() * 20);
|
|
|
- if(pt > 100) {
|
|
|
- clearInterval(ptInterval);
|
|
|
- elProgress.textContent = ``;
|
|
|
-
|
|
|
- // 在进度条完成后显示安装成功的通知
|
|
|
- this.showInPageNotification({
|
|
|
- message: `${this.originalTools[toolKey].name} 安装成功!`,
|
|
|
- type: 'success',
|
|
|
- duration: 3000
|
|
|
- });
|
|
|
- }
|
|
|
- }, 100);
|
|
|
- } else {
|
|
|
- // 如果没有进度条元素,直接显示通知
|
|
|
- this.showInPageNotification({
|
|
|
- message: `${this.originalTools[toolKey].name} 安装成功!`,
|
|
|
- type: 'success',
|
|
|
- duration: 3000
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 更新原始数据和当前活动数据
|
|
|
- this.originalTools[toolKey].installed = true;
|
|
|
- if (this.activeTools[toolKey]) {
|
|
|
- this.activeTools[toolKey].installed = true;
|
|
|
- }
|
|
|
-
|
|
|
- // 更新已安装工具数量
|
|
|
- this.updateInstalledCount();
|
|
|
-
|
|
|
- // 如果按钮存在,更新其状态
|
|
|
- if (btnElement) {
|
|
|
- btnElement.setAttribute('data-undergoing', '0');
|
|
|
- }
|
|
|
-
|
|
|
- // 发送消息通知后台更新
|
|
|
- chrome.runtime.sendMessage({
|
|
|
- type: MSG_TYPE.DYNAMIC_TOOL_INSTALL_OR_OFFLOAD,
|
|
|
- toolName: toolKey,
|
|
|
- action: 'install',
|
|
|
- showTips: true
|
|
|
- });
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- console.error('安装工具失败:', error);
|
|
|
-
|
|
|
- // 显示安装失败的通知
|
|
|
- this.showInPageNotification({
|
|
|
- message: `安装失败:${error.message || '未知错误'}`,
|
|
|
- type: 'error',
|
|
|
- duration: 5000
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 卸载工具
|
|
|
- async uninstallTool(toolKey) {
|
|
|
- try {
|
|
|
- // 使用自定义确认对话框而非浏览器原生的confirm
|
|
|
- this.showConfirm({
|
|
|
- title: '卸载确认',
|
|
|
- message: `确定要卸载"${this.originalTools[toolKey].name}"工具吗?`,
|
|
|
- callback: async (key) => {
|
|
|
- try {
|
|
|
- await chrome.runtime.sendMessage({
|
|
|
- type: MSG_TYPE.DYNAMIC_TOOL_INSTALL_OR_OFFLOAD,
|
|
|
- toolName: key,
|
|
|
- action: 'offload',
|
|
|
- showTips: true
|
|
|
- });
|
|
|
-
|
|
|
- // 调用Awesome.offLoad卸载工具
|
|
|
- await Awesome.offLoad(key);
|
|
|
-
|
|
|
- // 更新原始数据和当前活动数据
|
|
|
- this.originalTools[key].installed = false;
|
|
|
- this.originalTools[key].inContextMenu = false;
|
|
|
-
|
|
|
- if (this.activeTools[key]) {
|
|
|
- this.activeTools[key].installed = false;
|
|
|
- this.activeTools[key].inContextMenu = false;
|
|
|
- }
|
|
|
-
|
|
|
- // 更新已安装工具数量
|
|
|
- this.updateInstalledCount();
|
|
|
-
|
|
|
- // 显示卸载成功的通知
|
|
|
- this.showInPageNotification({
|
|
|
- message: `${this.originalTools[key].name} 已成功卸载!`,
|
|
|
- type: 'success',
|
|
|
- duration: 3000
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('卸载工具失败:', error);
|
|
|
-
|
|
|
- // 显示卸载失败的通知
|
|
|
- this.showInPageNotification({
|
|
|
- message: `卸载失败:${error.message || '未知错误'}`,
|
|
|
- type: 'error',
|
|
|
- duration: 5000
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- data: toolKey
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('准备卸载过程中出错:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 切换右键菜单
|
|
|
- async toggleContextMenu(toolKey) {
|
|
|
- try {
|
|
|
- const tool = this.originalTools[toolKey];
|
|
|
- const newState = !tool.inContextMenu;
|
|
|
-
|
|
|
- // 更新菜单状态
|
|
|
- await Awesome.menuMgr(toolKey, newState ? 'install' : 'offload');
|
|
|
-
|
|
|
- // 更新原始数据和当前活动数据
|
|
|
- tool.inContextMenu = newState;
|
|
|
- if (this.activeTools[toolKey]) {
|
|
|
- this.activeTools[toolKey].inContextMenu = newState;
|
|
|
- }
|
|
|
-
|
|
|
- // 发送消息通知后台更新右键菜单
|
|
|
- chrome.runtime.sendMessage({
|
|
|
- type: MSG_TYPE.DYNAMIC_TOOL_INSTALL_OR_OFFLOAD,
|
|
|
- action: `menu-${newState ? 'install' : 'offload'}`,
|
|
|
- showTips: false,
|
|
|
- menuOnly: true
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('切换右键菜单失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 切换收藏状态
|
|
|
- async toggleFavorite(toolKey) {
|
|
|
- try {
|
|
|
- if (this.favorites.has(toolKey)) {
|
|
|
- this.favorites.delete(toolKey);
|
|
|
- // 更新原始数据和当前活动数据
|
|
|
- this.originalTools[toolKey].favorite = false;
|
|
|
- if (this.activeTools[toolKey]) {
|
|
|
- this.activeTools[toolKey].favorite = false;
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.favorites.add(toolKey);
|
|
|
- // 更新原始数据和当前活动数据
|
|
|
- this.originalTools[toolKey].favorite = true;
|
|
|
- if (this.activeTools[toolKey]) {
|
|
|
- this.activeTools[toolKey].favorite = true;
|
|
|
- }
|
|
|
- }
|
|
|
- await this.saveFavorites();
|
|
|
-
|
|
|
- // 如果是在收藏视图,需要更新视图
|
|
|
- if (this.currentView === 'favorites') {
|
|
|
- this.updateActiveTools('favorites');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('切换收藏状态失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- async updateActiveTools(view) {
|
|
|
- if (this.loading || Object.keys(this.originalTools).length === 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- switch (view) {
|
|
|
- case 'installed':
|
|
|
- // 使用Awesome.getInstalledTools实时获取已安装工具
|
|
|
- try {
|
|
|
- const installedTools = await Awesome.getInstalledTools();
|
|
|
- // 合并installedTools与originalTools的数据
|
|
|
- this.activeTools = Object.fromEntries(
|
|
|
- Object.entries(this.originalTools).filter(([key]) =>
|
|
|
- installedTools.hasOwnProperty(key)
|
|
|
- )
|
|
|
- );
|
|
|
- } catch (error) {
|
|
|
- console.error('获取已安装工具失败:', error);
|
|
|
- // 回退到本地数据
|
|
|
- this.activeTools = Object.fromEntries(
|
|
|
- Object.entries(this.originalTools).filter(([_, tool]) =>
|
|
|
- tool.installed || tool.systemInstalled || false
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
- break;
|
|
|
- case 'favorites':
|
|
|
- this.activeTools = Object.fromEntries(
|
|
|
- Object.entries(this.originalTools).filter(([key]) => this.favorites.has(key))
|
|
|
- );
|
|
|
- break;
|
|
|
- case 'recent':
|
|
|
- // 切换recent时,recentUsed已在showRecentUsed中实时拉取
|
|
|
- this.activeTools = Object.fromEntries(
|
|
|
- Object.entries(this.originalTools).filter(([key]) => this.recentUsed.includes(key))
|
|
|
- );
|
|
|
- break;
|
|
|
- case 'all':
|
|
|
- default:
|
|
|
- this.activeTools = { ...this.originalTools };
|
|
|
- // 分类过滤在computed属性中处理
|
|
|
- break;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 新增更新已安装工具数量的方法
|
|
|
- async updateInstalledCount() {
|
|
|
- this.installedCount = await this.getInstalledCount();
|
|
|
- },
|
|
|
-
|
|
|
- // 加载用户保存的视图模式
|
|
|
- async loadViewMode() {
|
|
|
- try {
|
|
|
- const result = await new Promise(resolve => {
|
|
|
- chrome.storage.local.get('fehelper_view_mode', result => {
|
|
|
- resolve(result.fehelper_view_mode);
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- if (result) {
|
|
|
- this.viewMode = result;
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('加载视图模式失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 保存用户的视图模式选择
|
|
|
- async saveViewMode(mode) {
|
|
|
- try {
|
|
|
- this.viewMode = mode;
|
|
|
- await chrome.storage.local.set({
|
|
|
- 'fehelper_view_mode': mode
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('保存视图模式失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 加载设置项
|
|
|
- async loadSettings() {
|
|
|
- try {
|
|
|
- Settings.getOptions(async (opts) => {
|
|
|
- let selectedOpts = [];
|
|
|
- Object.keys(opts).forEach(key => {
|
|
|
- if(String(opts[key]) === 'true') {
|
|
|
- selectedOpts.push(key);
|
|
|
- }
|
|
|
- });
|
|
|
- this.selectedOpts = selectedOpts;
|
|
|
-
|
|
|
- // 加载右键菜单设置
|
|
|
- this.menuDownloadCrx = await Awesome.menuMgr('download-crx', 'get') === '1';
|
|
|
- this.menuFeHelperSeting = await Awesome.menuMgr('fehelper-setting', 'get') !== '0';
|
|
|
-
|
|
|
- // 获取快捷键
|
|
|
- chrome.commands.getAll((commands) => {
|
|
|
- for (let command of commands) {
|
|
|
- if (command.name === '_execute_action') {
|
|
|
- this.defaultKey = command.shortcut || 'Alt+Shift+J';
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('加载设置项失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 检查浏览器类型
|
|
|
- checkBrowserType() {
|
|
|
- try {
|
|
|
- this.isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
|
|
- } catch (error) {
|
|
|
- console.error('检查浏览器类型失败:', error);
|
|
|
- this.isFirefox = false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 显示设置模态框
|
|
|
- showSettings() {
|
|
|
- this.showSettingsModal = true;
|
|
|
- },
|
|
|
-
|
|
|
- // 关闭设置模态框
|
|
|
- closeSettings() {
|
|
|
- this.showSettingsModal = false;
|
|
|
- },
|
|
|
-
|
|
|
- // 显示打赏模态框
|
|
|
- openDonateModal() {
|
|
|
- this.showDonateModal = true;
|
|
|
- },
|
|
|
-
|
|
|
- // 关闭打赏模态框
|
|
|
- closeDonateModal() {
|
|
|
- this.showDonateModal = false;
|
|
|
- },
|
|
|
-
|
|
|
- // 显示确认对话框
|
|
|
- showConfirm(options) {
|
|
|
- this.confirmDialog = {
|
|
|
- show: true,
|
|
|
- title: options.title || '操作确认',
|
|
|
- message: options.message || '确定要执行此操作吗?',
|
|
|
- callback: options.callback || null,
|
|
|
- data: options.data || null
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- // 确认操作
|
|
|
- confirmAction() {
|
|
|
- if (this.confirmDialog.callback) {
|
|
|
- this.confirmDialog.callback(this.confirmDialog.data);
|
|
|
- }
|
|
|
- this.confirmDialog.show = false;
|
|
|
- },
|
|
|
-
|
|
|
- // 取消确认
|
|
|
- cancelConfirm() {
|
|
|
- this.confirmDialog.show = false;
|
|
|
- },
|
|
|
-
|
|
|
- // 保存设置
|
|
|
- async saveSettings() {
|
|
|
- try {
|
|
|
- // 构建设置对象
|
|
|
- let opts = {};
|
|
|
- ['OPT_ITEM_CONTEXTMENUS', 'FORBID_OPEN_IN_NEW_TAB', 'CONTENT_SCRIPT_ALLOW_ALL_FRAMES',
|
|
|
- 'JSON_PAGE_FORMAT', 'AUTO_DARK_MODE', 'ALWAYS_DARK_MODE'].forEach(key => {
|
|
|
- opts[key] = this.selectedOpts.includes(key).toString();
|
|
|
- });
|
|
|
-
|
|
|
- // 保存设置 - 直接传递对象,settings.js已增加对对象类型的支持
|
|
|
- Settings.setOptions(opts, async () => {
|
|
|
- try {
|
|
|
- // 处理右键菜单
|
|
|
- const crxAction = this.menuDownloadCrx ? 'install' : 'offload';
|
|
|
- const settingAction = this.menuFeHelperSeting ? 'install' : 'offload';
|
|
|
-
|
|
|
- await Promise.all([
|
|
|
- Awesome.menuMgr('download-crx', crxAction),
|
|
|
- Awesome.menuMgr('fehelper-setting', settingAction)
|
|
|
- ]);
|
|
|
-
|
|
|
- // 通知后台更新右键菜单
|
|
|
- chrome.runtime.sendMessage({
|
|
|
- type: MSG_TYPE.DYNAMIC_TOOL_INSTALL_OR_OFFLOAD,
|
|
|
- action: 'menu-change',
|
|
|
- menuOnly: true
|
|
|
- });
|
|
|
-
|
|
|
- // 关闭弹窗
|
|
|
- this.closeSettings();
|
|
|
-
|
|
|
- // 显示提示
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 设置',
|
|
|
- message: '设置已保存!'
|
|
|
- });
|
|
|
- } catch (innerError) {
|
|
|
- console.error('保存菜单设置失败:', innerError);
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 设置错误',
|
|
|
- message: '保存菜单设置失败: ' + innerError.message
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- console.error('保存设置失败:', error);
|
|
|
- this.showNotification({
|
|
|
- title: 'FeHelper 设置错误',
|
|
|
- message: '保存设置失败: ' + error.message
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 设置快捷键
|
|
|
- setShortcuts() {
|
|
|
- chrome.tabs.create({
|
|
|
- url: 'chrome://extensions/shortcuts'
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 体验夜间模式
|
|
|
- turnLight(event) {
|
|
|
- event.preventDefault();
|
|
|
-
|
|
|
- // 获取body元素
|
|
|
- const body = document.body;
|
|
|
-
|
|
|
- // 切换夜间模式
|
|
|
- if (body.classList.contains('dark-mode')) {
|
|
|
- body.classList.remove('dark-mode');
|
|
|
- } else {
|
|
|
- body.classList.add('dark-mode');
|
|
|
-
|
|
|
- // 设置倒计时
|
|
|
- this.countDown = 10;
|
|
|
-
|
|
|
- // 启动倒计时
|
|
|
- const timer = setInterval(() => {
|
|
|
- this.countDown--;
|
|
|
- if (this.countDown <= 0) {
|
|
|
- clearInterval(timer);
|
|
|
- body.classList.remove('dark-mode');
|
|
|
- }
|
|
|
- }, 1000);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 检查URL中的donate_from参数并显示打赏弹窗
|
|
|
- checkDonateParam() {
|
|
|
- try {
|
|
|
- const urlParams = new URLSearchParams(window.location.search);
|
|
|
- const donateFrom = urlParams.get('donate_from');
|
|
|
-
|
|
|
- if (donateFrom) {
|
|
|
- console.log('检测到打赏来源参数:', donateFrom);
|
|
|
-
|
|
|
- // 记录打赏来源
|
|
|
- chrome.storage.local.set({
|
|
|
- 'fehelper_donate_from': donateFrom,
|
|
|
- 'fehelper_donate_time': Date.now()
|
|
|
- });
|
|
|
-
|
|
|
- // 等待工具数据加载完成
|
|
|
- this.$nextTick(() => {
|
|
|
- // 在所有工具中查找匹配项
|
|
|
- let matchedTool = null;
|
|
|
-
|
|
|
- // 首先尝试直接匹配工具key
|
|
|
- if (this.originalTools && this.originalTools[donateFrom]) {
|
|
|
- matchedTool = this.originalTools[donateFrom];
|
|
|
- } else if (this.originalTools) {
|
|
|
- // 如果没有直接匹配,尝试在所有工具中查找部分匹配
|
|
|
- for (const [key, tool] of Object.entries(this.originalTools)) {
|
|
|
- if (key.includes(donateFrom) || donateFrom.includes(key) ||
|
|
|
- (tool.name && tool.name.includes(donateFrom)) ||
|
|
|
- (donateFrom && donateFrom.includes(tool.name))) {
|
|
|
- matchedTool = tool;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 更新打赏文案
|
|
|
- if (matchedTool) {
|
|
|
- this.donate.text = `看起来【${matchedTool.name}】工具帮助到了你,感谢你的认可!`;
|
|
|
- } else {
|
|
|
- // 没有匹配到特定工具,使用通用文案
|
|
|
- this.donate.text = `感谢你对FeHelper的认可和支持!`;
|
|
|
- }
|
|
|
-
|
|
|
- // 显示打赏弹窗
|
|
|
- this.showDonateModal = true;
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('处理打赏参数时出错:', error);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 补充 getRecentCount,保证模板调用不报错,且数据源唯一
|
|
|
- async getRecentCount() {
|
|
|
- const recent = await Statistics.getRecentUsedTools(10);
|
|
|
- return recent.length;
|
|
|
- },
|
|
|
-
|
|
|
- renderDashboard() {
|
|
|
- const dashboardContainerId = 'fh-dashboard-panel';
|
|
|
- let container = document.getElementById(dashboardContainerId);
|
|
|
- // 只在showDashboard且currentView为recent时隐藏工具列表
|
|
|
- const grid = document.querySelector('.tools-grid');
|
|
|
- if (!this.showDashboard || this.currentView !== 'recent') {
|
|
|
- if (container) container.style.display = 'none';
|
|
|
- if (grid) grid.style.display = '';
|
|
|
- return;
|
|
|
- }
|
|
|
- if (grid) grid.style.display = 'none';
|
|
|
- if (!container) {
|
|
|
- container = document.createElement('div');
|
|
|
- container.id = dashboardContainerId;
|
|
|
- container.style = 'padding:32px; background:#fff; border-radius:8px; margin:24px; box-shadow:0 2px 12px #eee; min-width:700px;';
|
|
|
- const main = document.querySelector('.market-main') || document.querySelector('.market-content');
|
|
|
- if (main) main.prepend(container);
|
|
|
- else document.body.appendChild(container);
|
|
|
- }
|
|
|
- container.style.display = 'block';
|
|
|
- const data = this.dashboardData || {};
|
|
|
- // 工具ID转中文名和icon
|
|
|
- const toolName = (key) => (this.originalTools && this.originalTools[key] && this.originalTools[key].name) ? this.originalTools[key].name : key;
|
|
|
- const toolIcon = (key) => {
|
|
|
- if (toolMap[key] && toolMap[key].menuConfig && toolMap[key].menuConfig[0] && toolMap[key].menuConfig[0].icon) {
|
|
|
- return toolMap[key].menuConfig[0].icon;
|
|
|
- }
|
|
|
- return toolName(key).slice(0,1);
|
|
|
- };
|
|
|
- // 插入美观样式
|
|
|
- if (!document.getElementById('fh-dashboard-style')) {
|
|
|
- const style = document.createElement('style');
|
|
|
- style.id = 'fh-dashboard-style';
|
|
|
- style.innerHTML = `
|
|
|
- .fh-dashboard-cards { display: flex; flex-wrap: wrap; gap: 18px; margin-bottom: 24px;}
|
|
|
- .fh-card { background: linear-gradient(135deg,#f7f9fa 60%,#e3eafc 100%); border-radius: 12px; box-shadow:0 2px 8px #f0f0f0; padding:18px 24px; min-width:120px; flex:1; text-align:center; font-size:15px;}
|
|
|
- .fh-card.main { background: linear-gradient(135deg,#e3fcec 60%,#e3eafc 100%);}
|
|
|
- .fh-card-num { font-size:32px; font-weight:bold; margin-bottom:4px;}
|
|
|
- .fh-calendar { display:inline-block; margin-left:12px; }
|
|
|
- .fh-cal-cell { display:inline-block; width:18px; height:18px; line-height:18px; text-align:center; border-radius:3px; margin:1px; background:#eee; color:#888; font-size:12px;}
|
|
|
- .fh-cal-cell.used { background:#4285f4; color:#fff; font-weight:bold;}
|
|
|
- .fh-dashboard-section { background:#fff; border-radius:12px; box-shadow:0 1px 4px #f0f0f0; padding:18px 24px; margin-bottom:24px;}
|
|
|
- .fh-dashboard-header { margin-bottom:24px; }
|
|
|
- .fh-dashboard-header h2 { font-size:22px; margin:0; }
|
|
|
- .fh-tool-bar { display:inline-block; width:18px; height:18px; border-radius:3px; background:#e3eafc; margin-right:6px; vertical-align:middle; }
|
|
|
- .fh-tool-bar-inner { display:inline-block; height:100%; border-radius:3px; background:#4285f4; }
|
|
|
- .fh-tool-list { margin:0; padding:0; list-style:none; }
|
|
|
- .fh-tool-list li { margin-bottom:10px; }
|
|
|
- .fh-tool-icon { display:inline-block; width:18px; height:18px; border-radius:3px; background:#e3eafc; margin-right:6px; vertical-align:middle; text-align:center; font-size:14px; }
|
|
|
- .fh-dashboard-sub { color:#888; font-size:13px; margin-bottom:8px; }
|
|
|
- `;
|
|
|
- document.head.appendChild(style);
|
|
|
- }
|
|
|
- // 30天活跃日历
|
|
|
- const today = new Date();
|
|
|
- let calendar = '<div class="fh-calendar">';
|
|
|
- for(let i=29;i>=0;i--){
|
|
|
- const d = new Date(today.getTime()-i*86400000);
|
|
|
- const ds = d.toISOString().slice(0,10);
|
|
|
- const used = data.allDates && data.allDates.includes(ds);
|
|
|
- calendar += `<span class="fh-cal-cell${used?' used':''}" title="${ds}">${d.getDate()}</span>`;
|
|
|
- }
|
|
|
- calendar += '</div>';
|
|
|
- // 主卡片区块
|
|
|
- let html = `
|
|
|
- <div class="fh-dashboard-header">
|
|
|
- <h2>FeHelper 使用统计仪表盘 <span style="font-size:16px;color:#bbb;">(近30天)</span></h2>
|
|
|
- </div>
|
|
|
- <div class="fh-dashboard-cards">
|
|
|
- <div class="fh-card main"><div class="fh-card-num">${data.totalCount||0}</div><div>总使用次数</div></div>
|
|
|
- <div class="fh-card main"><div class="fh-card-num">${data.activeDays||0}</div><div>活跃天数</div></div>
|
|
|
- <div class="fh-card"><div>${data.firstDate||'-'}<br>~<br>${data.lastDate||'-'}</div><div>统计区间</div></div>
|
|
|
- <div class="fh-card"><div class="fh-card-num">${data.maxStreak||0}</div><div>最长连续活跃天数</div></div>
|
|
|
- <div class="fh-card"><div class="fh-card-num">${data.monthCount||0}</div><div>本月使用次数</div></div>
|
|
|
- <div class="fh-card"><div class="fh-card-num">${data.weekCount||0}</div><div>本周使用次数</div></div>
|
|
|
- <div class="fh-card"><div class="fh-card-num">${data.avgPerDay||0}</div><div>平均每日使用</div></div>
|
|
|
- <div class="fh-card"><div>${data.maxDay.date||'-'}<br><b>${data.maxDay.count||0}</b></div><div>最活跃日</div></div>
|
|
|
- <div class="fh-card"><div class="fh-card-num">${data.daysSinceLast||0}</div><div>最近未使用天数</div></div>
|
|
|
- </div>
|
|
|
- <div class="fh-dashboard-section">
|
|
|
- <div class="fh-dashboard-sub">近30天活跃日历:</div>${calendar}
|
|
|
- </div>
|
|
|
- <div class="fh-dashboard-section" style="display:flex;gap:32px;flex-wrap:wrap;">
|
|
|
- <div style="flex:2;min-width:320px;">
|
|
|
- <div class="fh-dashboard-sub"><b>最近10天活跃趋势:</b></div>
|
|
|
- <div style="display:flex;align-items:end;height:80px;margin-top:8px;">
|
|
|
- ${
|
|
|
- (data.dailyTrend||[]).map(d=>{
|
|
|
- const max = Math.max(...(data.dailyTrend||[]).map(x=>x.count),1);
|
|
|
- return `<div title='${d.date}: ${d.count}' style='width:20px;height:${d.count/max*60}px;background:#4285f4;margin-right:4px;border-radius:2px;'></div>`;
|
|
|
- }).join('')
|
|
|
- }
|
|
|
- </div>
|
|
|
- <div style="font-size:12px;color:#888;margin-top:4px;">
|
|
|
- ${(data.dailyTrend||[]).map(d=>`<span style='display:inline-block;width:20px;text-align:center;'>${d.date.slice(5)}</span>`).join('')}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div style="flex:3;min-width:320px;">
|
|
|
- <div class="fh-dashboard-sub"><b>使用最多的工具:</b></div>
|
|
|
- <ul class="fh-tool-list">
|
|
|
- ${(data.mostUsed||[]).map(t=>{
|
|
|
- const percent = data.totalCount ? Math.round(t.count/data.totalCount*100) : 0;
|
|
|
- return `<li style='margin-bottom:12px;display:flex;align-items:center;'>
|
|
|
- <span class='fh-tool-icon'>${toolIcon(t.name)}</span>
|
|
|
- <span style='display:inline-block;width:100px;'>${toolName(t.name)}</span>
|
|
|
- <span style='display:inline-block;width:60px;color:#888;'>(x${t.count})</span>
|
|
|
- <span class='fh-tool-bar' style='width:80px;height:10px;margin:0 8px;'>
|
|
|
- <span class='fh-tool-bar-inner' style='width:${percent*0.8}px;'></span>
|
|
|
- </span>
|
|
|
- <span style='color:#888;'>${percent}%</span>
|
|
|
- </li>`;
|
|
|
- }).join('')}
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="fh-dashboard-section">
|
|
|
- <div class="fh-dashboard-sub"><b>最近10次使用的工具:</b></div>
|
|
|
- <ul style="margin:8px 0 0 0;padding:0;list-style:none;">
|
|
|
- ${(data.recentDetail||[]).map(t=>`<li style='display:inline-block;margin-right:24px;'>${toolName(t.tool)} <span style='color:#888;'>(${t.date})</span></li>`).join('')}
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- `;
|
|
|
- container.innerHTML = html;
|
|
|
- window.__vue__ = this;
|
|
|
- },
|
|
|
- },
|
|
|
-
|
|
|
- watch: {
|
|
|
- // 监听currentView变化
|
|
|
- currentView: {
|
|
|
- immediate: true,
|
|
|
- handler(newView) {
|
|
|
- this.updateActiveTools(newView);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 监听currentCategory变化
|
|
|
- currentCategory: {
|
|
|
- handler(newCategory) {
|
|
|
- // 保证在视图模式之外的分类切换也能正确显示
|
|
|
- if (this.currentView === 'all') {
|
|
|
- this.activeTools = { ...this.originalTools };
|
|
|
- }
|
|
|
- // 重置搜索条件
|
|
|
- if (this.searchKey) {
|
|
|
- this.searchKey = '';
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- showDashboard(val) {
|
|
|
- this.renderDashboard();
|
|
|
- },
|
|
|
- dashboardData(val) {
|
|
|
- this.renderDashboard();
|
|
|
- },
|
|
|
- },
|
|
|
-
|
|
|
- mounted() {
|
|
|
- this.$nextTick(() => {
|
|
|
- this.renderDashboard();
|
|
|
- });
|
|
|
- },
|
|
|
-});
|
|
|
-
|
|
|
-// 添加滚动事件监听
|
|
|
-window.addEventListener('scroll', () => {
|
|
|
- const header = document.querySelector('.market-header');
|
|
|
- const sidebar = document.querySelector('.market-sidebar');
|
|
|
-
|
|
|
- if (window.scrollY > 10) {
|
|
|
- header.classList.add('scrolled');
|
|
|
- sidebar && sidebar.classList.add('scrolled');
|
|
|
- } else {
|
|
|
- header.classList.remove('scrolled');
|
|
|
- sidebar && sidebar.classList.remove('scrolled');
|
|
|
- }
|
|
|
-});
|