Forráskód Böngészése

popup页面打开效率提升

zxlie 3 hónapja
szülő
commit
5f3a719167
4 módosított fájl, 288 hozzáadás és 118 törlés
  1. 68 26
      apps/background/awesome.js
  2. 70 0
      apps/popup/index.css
  3. 18 2
      apps/popup/index.html
  4. 132 90
      apps/popup/index.js

+ 68 - 26
apps/background/awesome.js

@@ -161,38 +161,80 @@ let Awesome = (() => {
     };
 
     /**
-     * 检查看本地已安装过哪些工具
+     * 检查看本地已安装过哪些工具 - 性能优化版本
      * @returns {Promise}
      */
     let getInstalledTools = async () => {
-        let tools = await getAllTools();
-        if (!tools) return {};
-        let istTolls = {};
-        Object.keys(tools).forEach(tool => {
-            if (tools[tool] && tools[tool].installed) {
-                istTolls[tool] = tools[tool];
+        try {
+            // 一次性获取所有存储数据,避免多次访问
+            const allStorageData = await new Promise((resolve, reject) => {
+                chrome.storage.local.get(null, result => {
+                    resolve(result || {});
+                });
+            });
+
+            // 获取本地开发的插件
+            const DEV_TOOLS_MY_TOOLS = 'DEV-TOOLS:MY-TOOLS';
+            let localDevTools = {};
+            try {
+                localDevTools = JSON.parse(allStorageData[DEV_TOOLS_MY_TOOLS] || '{}');
+                Object.keys(localDevTools).forEach(tool => {
+                    toolMap[tool] = localDevTools[tool];
+                });
+            } catch (e) {
+                // 忽略解析错误
             }
-        });
 
-        // 先批量获取所有时间戳
-        const toolNames = Object.keys(istTolls);
-        const timeMap = {};
-        await Promise.all(toolNames.map(async tool => {
-            let time = parseInt(await StorageMgr.get(TOOL_NAME_TPL.replace('#TOOL-NAME#', tool))) || 0;
-            timeMap[tool] = time;
-        }));
-        
-        // 用同步sort排序
-        let sortedTools = toolNames.sort((a, b) => {
-            return timeMap[a] - timeMap[b];
-        });
+            let installedTools = {};
+            
+            // 遍历所有工具,从存储数据中检查安装状态
+            Object.keys(toolMap).forEach(toolName => {
+                const toolKey = TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName);
+                const menuKey = TOOL_MENU_TPL.replace('#TOOL-NAME#', toolName);
+                
+                // 检查工具是否已安装
+                let toolInstalled = !!allStorageData[toolKey];
+                // 系统预置的功能,是强制 installed 状态的
+                if (toolMap[toolName] && toolMap[toolName].systemInstalled) {
+                    toolInstalled = true;
+                }
+                
+                // 检查菜单状态
+                let menuInstalled = String(allStorageData[menuKey]) === '1';
+                
+                // 本地工具,还需要看是否处于开启状态
+                if (toolMap[toolName].hasOwnProperty('_devTool')) {
+                    toolInstalled = toolInstalled && toolMap[toolName]._enable;
+                    menuInstalled = menuInstalled && toolMap[toolName]._enable;
+                }
+                
+                // 只收集已安装的工具
+                if (toolInstalled) {
+                    installedTools[toolName] = {
+                        ...toolMap[toolName],
+                        installed: true,
+                        menu: menuInstalled,
+                        installTime: parseInt(allStorageData[toolKey]) || 0
+                    };
+                }
+            });
 
-        let sortedToolMap = {};
-        sortedTools.forEach(tool => {
-            sortedToolMap[tool] = istTolls[tool];
-        });
-        
-        return sortedToolMap;
+            // 按安装时间排序
+            const sortedToolNames = Object.keys(installedTools).sort((a, b) => {
+                return installedTools[a].installTime - installedTools[b].installTime;
+            });
+
+            let sortedToolMap = {};
+            sortedToolNames.forEach(toolName => {
+                sortedToolMap[toolName] = installedTools[toolName];
+            });
+            
+            return sortedToolMap;
+        } catch (error) {
+            console.error('getInstalledTools error:', error);
+            // 发生错误时返回空对象,避免popup完全无法加载
+            return {};
+        }
     };
 
     /**

+ 70 - 0
apps/popup/index.css

@@ -171,3 +171,73 @@ svg:not(:root) {
 {
     float: right;
 }
+
+/* 加载状态样式 */
+.fe-loading {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 20px;
+    min-height: 60px;
+}
+
+.loading-spinner {
+    width: 20px;
+    height: 20px;
+    border: 2px solid rgba(67, 97, 238, 0.1);
+    border-left: 2px solid #4361ee;
+    border-radius: 50%;
+    animation: spin 1s linear infinite;
+    margin-bottom: 8px;
+}
+
+.loading-text {
+    color: #666;
+    font-size: 12px;
+    text-align: center;
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+
+/* 无工具状态样式 */
+.fe-no-tools {
+    padding: 20px;
+    text-align: center;
+}
+
+.no-tools-message p {
+    color: #666;
+    font-size: 12px;
+    margin: 0 0 12px 0;
+}
+
+.install-tools-btn {
+    background: #4361ee;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    padding: 6px 12px;
+    font-size: 11px;
+    cursor: pointer;
+    transition: all 0.2s ease;
+}
+
+.install-tools-btn:hover {
+    background: #3651d4;
+    transform: translateY(-1px);
+}
+
+/* 暗色模式适配 */
+html[dark-mode="on"] .loading-text,
+html[dark-mode="on"] .no-tools-message p {
+    color: #ccc;
+}
+
+html[dark-mode="on"] .loading-spinner {
+    border-color: rgba(255, 255, 255, 0.1);
+    border-left-color: #4361ee;
+}

+ 18 - 2
apps/popup/index.html

@@ -10,13 +10,29 @@
     <body>
         <div id="pageContainer" class="fe-whole-page">
             <div class="fe-function-title">FeHelper<span>({{manifest.version}})</span></div>
-            <ul class="fe-function-list">
+            
+            <!-- 加载状态 -->
+            <div v-if="isLoading" class="fe-loading">
+                <div class="loading-spinner"></div>
+                <div class="loading-text">加载工具列表中...</div>
+            </div>
+            
+            <!-- 工具列表 -->
+            <ul v-else class="fe-function-list">
                 <li v-for="tool in Object.keys(fhTools)" :class="'-x-' + tool" @click="runHelper(tool)" v-if="fhTools[tool].installed">
                     <b>{{ fhTools[tool].icon || fhTools[tool].menuConfig[0].icon}}</b>{{fhTools[tool].name}}
                 </li>
             </ul>
+            
+            <!-- 如果没有工具 -->
+            <div v-if="!isLoading && Object.keys(fhTools).length === 0" class="fe-no-tools">
+                <div class="no-tools-message">
+                    <p>暂无已安装的工具</p>
+                    <button @click="openOptionsPage()" class="install-tools-btn">去安装工具</button>
+                </div>
+            </div>
+            
             <div class="fe-feedback">
-
                 <a href="https://github.com/zxlie/FeHelper/issues" @click="openUrl($event)" target="_blank" tabindex="-1" class="x-github" title="提交意见反馈">
                     <svg version="1.1" width="14" height="14" viewBox="0 0 426.667 426.667" style="enable-background:new 0 0 426.667 426.667;" xml:space="preserve">
                         <path d="M384,0H42.667C19.093,0,0.213,19.093,0.213,42.667L0,426.667l85.333-85.333H384c23.573,0,42.667-19.093,42.667-42.667v-256C426.667,19.093,407.573,0,384,0z M234.667,256H192v-42.667h42.667V256z M234.667,170.667H192V85.333h42.667V170.667z"/>

+ 132 - 90
apps/popup/index.js

@@ -34,112 +34,154 @@ new Vue({
     el: '#pageContainer',
     data: {
         manifest: {},
-        fhTools: {}
+        fhTools: {},
+        isLoading: true
     },
 
     created: function () {
         // 获取当前ctx的version
         this.manifest = chrome.runtime.getManifest();
+        
+        // 立即开始加载工具列表,不阻塞页面渲染
+        this.loadTools();
+    },
 
-        Awesome.getInstalledTools().then(async (tools) => {
-            // 获取用户自定义的工具排序
-            const customOrder = await chrome.storage.local.get('tool_custom_order');
-            const savedOrder = customOrder.tool_custom_order ? JSON.parse(customOrder.tool_custom_order) : null;
-            
-            // 如果有自定义排序,重新排列工具
-            if (savedOrder && Array.isArray(savedOrder)) {
-                const orderedTools = {};
-                const unorderedTools = { ...tools };
-                
-                // 按照保存的顺序添加工具
-                savedOrder.forEach(toolKey => {
-                    if (unorderedTools[toolKey]) {
-                        orderedTools[toolKey] = unorderedTools[toolKey];
-                        delete unorderedTools[toolKey];
-                    }
-                });
-                
-                // 添加新安装的工具(不在保存的顺序中的)
-                Object.assign(orderedTools, unorderedTools);
-                
-                this.fhTools = orderedTools;
-            } else {
-                this.fhTools = tools;
-            }
+    mounted: function () {
+        // 页面DOM渲染完成后,执行非关键操作
+        this.$nextTick(() => {
+            // 延迟执行非关键操作,避免阻塞UI渲染
+            setTimeout(() => {
+                // 自动开关灯
+                if (typeof DarkModeMgr !== 'undefined') {
+                    DarkModeMgr.turnLightAuto();
+                }
+
+                // 记录工具使用(非关键操作)
+                this.recordUsage();
+
+                // 页面加载后自动采集(非关键操作)
+                if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) {
+                    Awesome.collectAndSendClientInfo();
+                }
+            }, 50); // 延迟50ms执行,让UI先渲染
         });
 
-        // 自动开关灯
-        DarkModeMgr.turnLightAuto();
-
-        // 记录工具使用
-        // 埋点:自动触发json-format-auto
-        chrome.runtime.sendMessage({
-            type: 'fh-dynamic-any-thing',
-            thing: 'statistics-tool-usage',
-            params: {
-                tool_name: 'popup'
-            }
-        });
+        // 整个popup窗口支持上下键选择
+        this.setupKeyboardNavigation();
+        
+        // 查找截图按钮并绑定事件
+        this.setupScreenshotButton();
     },
 
-    mounted: function () {
-        // 整个popup窗口支持上线选择
-        document.body.addEventListener('keydown', e => {
-            let keyCode = e.keyCode || e.which;
-            if (![38, 40, 13].includes(keyCode)) {
-                return false;
-            }
-            let ul = document.querySelector('#pageContainer ul');
-            let hovered = ul.querySelector('li.x-hovered');
-            let next, prev;
-            if (hovered) {
-                hovered.classList.remove('x-hovered');
-                next = hovered.nextElementSibling;
-                prev = hovered.previousElementSibling;
-            }
-            if (!next) {
-                next = ul.querySelector('li:first-child');
-            }
-            if (!prev) {
-                prev = ul.querySelector('li:last-child');
+    methods: {
+        async loadTools() {
+            try {
+                const tools = await Awesome.getInstalledTools();
+                
+                // 获取用户自定义的工具排序
+                const customOrder = await chrome.storage.local.get('tool_custom_order');
+                const savedOrder = customOrder.tool_custom_order ? JSON.parse(customOrder.tool_custom_order) : null;
+                
+                // 如果有自定义排序,重新排列工具
+                if (savedOrder && Array.isArray(savedOrder)) {
+                    const orderedTools = {};
+                    const unorderedTools = { ...tools };
+                    
+                    // 按照保存的顺序添加工具
+                    savedOrder.forEach(toolKey => {
+                        if (unorderedTools[toolKey]) {
+                            orderedTools[toolKey] = unorderedTools[toolKey];
+                            delete unorderedTools[toolKey];
+                        }
+                    });
+                    
+                    // 添加新安装的工具(不在保存的顺序中的)
+                    Object.assign(orderedTools, unorderedTools);
+                    
+                    this.fhTools = orderedTools;
+                } else {
+                    this.fhTools = tools;
+                }
+                
+                this.isLoading = false;
+            } catch (error) {
+                console.error('加载工具列表失败:', error);
+                this.isLoading = false;
+                // 即使加载失败,也不应该让popup完全无法使用
+                this.fhTools = {};
             }
+        },
 
-            switch (keyCode) {
-                case 38: // 方向键:↑
-                    prev.classList.add('x-hovered');
-                    break;
-                case 40: // 方向键:↓
-                    next.classList.add('x-hovered');
-                    break;
-                case 13: // 回车键:选择
-                    hovered.click();
+        recordUsage() {
+            try {
+                // 埋点:自动触发popup统计
+                chrome.runtime.sendMessage({
+                    type: 'fh-dynamic-any-thing',
+                    thing: 'statistics-tool-usage',
+                    params: {
+                        tool_name: 'popup'
+                    }
+                });
+            } catch (error) {
+                // 忽略统计错误,不影响主功能
+                console.warn('统计记录失败:', error);
             }
+        },
 
-        }, false);
-
-        // 查找截图按钮并绑定事件
-        const screenshotButtons = Array.from(document.querySelectorAll('a[data-tool="screenshot"], button[data-tool="screenshot"]'));
-        
-        screenshotButtons.forEach(button => {
-            // 移除原有的点击事件
-            const oldClick = button.onclick;
-            button.onclick = function(e) {
-                e.preventDefault();
-                e.stopPropagation();
-                triggerScreenshot();
-                return false;
-            };
-        });
-
-        // 页面加载后自动采集
-        if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) {
-            Awesome.collectAndSendClientInfo();
-        }
-    },
+        setupKeyboardNavigation() {
+            document.body.addEventListener('keydown', e => {
+                let keyCode = e.keyCode || e.which;
+                if (![38, 40, 13].includes(keyCode)) {
+                    return false;
+                }
+                let ul = document.querySelector('#pageContainer ul');
+                if (!ul) return false;
+                
+                let hovered = ul.querySelector('li.x-hovered');
+                let next, prev;
+                if (hovered) {
+                    hovered.classList.remove('x-hovered');
+                    next = hovered.nextElementSibling;
+                    prev = hovered.previousElementSibling;
+                }
+                if (!next) {
+                    next = ul.querySelector('li:first-child');
+                }
+                if (!prev) {
+                    prev = ul.querySelector('li:last-child');
+                }
+
+                switch (keyCode) {
+                    case 38: // 方向键:↑
+                        if (prev) prev.classList.add('x-hovered');
+                        break;
+                    case 40: // 方向键:↓
+                        if (next) next.classList.add('x-hovered');
+                        break;
+                    case 13: // 回车键:选择
+                        if (hovered) hovered.click();
+                }
+            }, false);
+        },
 
-    methods: {
+        setupScreenshotButton() {
+            // 查找截图按钮并绑定事件
+            const screenshotButtons = Array.from(document.querySelectorAll('a[data-tool="screenshot"], button[data-tool="screenshot"]'));
+            
+            screenshotButtons.forEach(button => {
+                // 移除原有的点击事件
+                button.onclick = function(e) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    triggerScreenshot();
+                    return false;
+                };
+            });
+        },
 
         runHelper: async function (toolName) {
+            if (!toolName || !this.fhTools[toolName]) return;
+            
             let request = {
                 type: MSG_TYPE.OPEN_DYNAMIC_TOOL,
                 page: toolName,
@@ -150,7 +192,7 @@ new Vue({
                 request.query = `tool=${toolName}`;
             }
             chrome.runtime.sendMessage(request);
-            !!this.fhTools[toolName].noPage && setTimeout(window.close,200);
+            !!this.fhTools[toolName].noPage && setTimeout(window.close, 200);
         },
 
         openOptionsPage: () => {