Browse Source

工具支持排序

zxlie 7 months ago
parent
commit
bd4e4cd704
5 changed files with 419 additions and 5 deletions
  1. 33 0
      README.md
  2. 179 2
      apps/options/index.css
  3. 29 0
      apps/options/index.html
  4. 152 1
      apps/options/index.js
  5. 26 2
      apps/popup/index.js

+ 33 - 0
README.md

@@ -134,3 +134,36 @@ FeHelper在开发者社区中广受好评,以下是一些使用推荐和案例
 
 ## 九、一些样例
 - [点击进入查看>>](/apps/static/screenshot/crx)
+
+# FeHelper功能开发
+
+## 最新更新:个性化工具排序功能
+
+### 功能简介
+新增了个性化工具排序功能,允许用户自定义已安装工具在弹窗中的显示顺序,提升使用体验。
+
+### 使用方法
+1. 点击扩展图标旁的"插件设置"按钮,或在弹窗中点击"更多"
+2. 在设置页面中找到"FH工具排序"部分
+3. 通过拖拽调整工具的显示顺序
+4. 点击"保存排序"按钮确认更改
+5. 新的排序将立即在弹窗中生效
+
+### 功能特性
+- **拖拽排序**: 直观的拖拽界面,轻松调整工具顺序
+- **实时预览**: 拖拽过程中提供视觉反馈
+- **自动保存**: 支持重置为默认顺序
+- **兼容性好**: 新安装的工具会自动添加到列表末尾
+- **持久存储**: 排序设置保存在本地,重启浏览器后依然有效
+
+### 技术实现
+- 使用HTML5拖拽API实现交互功能
+- 通过chrome.storage.local存储用户自定义排序
+- 修改popup显示逻辑,优先使用用户自定义排序
+- 提供友好的用户界面和操作反馈
+
+### 注意事项
+- 只有已安装的工具才会出现在排序列表中
+- 卸载工具后,该工具会自动从排序列表中移除
+- 重置排序将恢复到默认的安装时间顺序
+

+ 179 - 2
apps/options/index.css

@@ -264,8 +264,8 @@ body{
     display: inline-flex;
     align-items: center;
     justify-content: center;
-    width: 48px;
-    height: 48px;
+    width: 24px;
+    height: 24px;
     border-radius: 12px;
     margin-right: 12px;
     background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
@@ -1676,3 +1676,180 @@ body{
     color: #fff;
 }
 
+/* 工具排序样式 */
+.tool-sort-container {
+    background: #f8f9fa;
+    border-radius: 8px;
+    padding: 15px;
+    margin-top: 10px;
+}
+
+.sort-tips {
+    color: #6c757d;
+    font-size: 12px;
+    margin-bottom: 12px;
+    padding: 6px 10px;
+    background: rgba(0, 123, 255, 0.08);
+    border-left: 3px solid #007bff;
+    border-radius: 4px;
+    line-height: 1.4;
+}
+
+.sortable-list {
+    background: white;
+    border-radius: 6px;
+    border: 1px solid #e9ecef;
+    min-height: 180px;
+    max-height: 350px;
+    overflow-y: auto;
+    padding: 6px;
+}
+
+.sortable-item {
+    display: flex;
+    align-items: center;
+    padding: 2px 12px;
+    margin: 2px 0;
+    background: white;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    cursor: move;
+    transition: all 0.2s ease;
+    position: relative;
+}
+
+.sortable-item:hover {
+    background: #f8f9fa;
+    border-color: #007bff;
+    box-shadow: 0 1px 3px rgba(0, 123, 255, 0.15);
+    transform: translateY(-1px);
+}
+
+.sortable-item.dragging {
+    opacity: 0.6;
+    transform: scale(1.02);
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    background: #fff3cd;
+    border-color: #ffc107;
+}
+
+.sortable-item.drag-over {
+    border-top: 3px solid #007bff;
+    background: rgba(0, 123, 255, 0.05);
+}
+
+.drag-handle {
+    font-size: 14px;
+    color: #6c757d;
+    margin-right: 10px;
+    cursor: grab;
+    padding: 2px 4px;
+    border-radius: 3px;
+    transition: all 0.2s ease;
+    user-select: none;
+    width: 20px;
+    text-align: center;
+    flex-shrink: 0;
+}
+
+.drag-handle:hover {
+    background: #e9ecef;
+    color: #495057;
+}
+
+.drag-handle:active {
+    cursor: grabbing;
+}
+
+.sortable-item .tool-icon {
+    font-size: 16px;
+    margin-right: 10px;
+    text-align: center;
+    flex-shrink: 0;
+}
+
+.sortable-item .tool-name {
+    font-weight: 500;
+    color: #343a40;
+    margin-right: 12px;
+    min-width: 100px;
+    flex-shrink: 0;
+    font-size: 14px;
+}
+
+.sortable-item .tool-desc {
+    color: #6c757d;
+    font-size: 12px;
+    flex: 1;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    line-height: 1.3;
+}
+
+.sort-actions {
+    margin-top: 12px;
+    text-align: right;
+}
+
+.sort-actions .btn {
+    margin-left: 6px;
+}
+
+.btn-sm {
+    padding: 5px 10px;
+    font-size: 12px;
+}
+
+/* 暗黑模式下的排序样式 */
+.dark-mode .tool-sort-container {
+    background: #2a2a2a;
+}
+
+.dark-mode .sort-tips {
+    background: rgba(0, 123, 255, 0.2);
+    color: #d4d4d4;
+}
+
+.dark-mode .sortable-list {
+    background: #1e1e1e;
+    border-color: #404040;
+}
+
+.dark-mode .sortable-item {
+    background: #1e1e1e;
+    border-color: #404040;
+}
+
+.dark-mode .sortable-item:hover {
+    background: #2a2a2a;
+    border-color: #007bff;
+}
+
+.dark-mode .sortable-item.dragging {
+    background: #3a3a2a;
+    border-color: #ffc107;
+}
+
+.dark-mode .sortable-item.drag-over {
+    background: rgba(0, 123, 255, 0.1);
+}
+
+.dark-mode .drag-handle {
+    color: #888;
+}
+
+.dark-mode .drag-handle:hover {
+    background: #404040;
+    color: #d4d4d4;
+}
+
+.dark-mode .sortable-item .tool-name {
+    color: #d4d4d4;
+}
+
+.dark-mode .sortable-item .tool-desc {
+    color: #888;
+}
+
+

+ 29 - 0
apps/options/index.html

@@ -73,6 +73,34 @@
                         </div>
                     </div>
 
+                    <!-- FH工具排序 -->
+                    <div class="settings-section">
+                        <h4># FH工具排序 <span class="x-tips">(自定义已安装工具在弹窗中的显示顺序)</span></h4>
+                        <div class="tool-sort-container">
+                            <div class="sort-tips">拖拽下方工具条目来调整顺序,调整后的顺序将在弹窗中生效:</div>
+                            <div class="sortable-list" id="sortableList">
+                                <div v-for="(tool, index) in sortableTools" 
+                                     :key="tool.key" 
+                                     class="sortable-item"
+                                     :data-tool-key="tool.key"
+                                     draggable="true"
+                                     @dragstart="handleDragStart($event, index)"
+                                     @dragover="handleDragOver($event)"
+                                     @drop="handleDrop($event, index)"
+                                     @dragend="handleDragEnd">
+                                    <div class="drag-handle">⋮⋮</div>
+                                    <div class="tool-icon">{{tool.icon}}</div>
+                                    <div class="tool-name">{{tool.name}}</div>
+                                    <div class="tool-desc">{{tool.tips}}</div>
+                                </div>
+                            </div>
+                            <div class="sort-actions">
+                                <button class="btn btn-info btn-sm" @click="resetToolOrder">重置为默认顺序</button>
+                                <button class="btn btn-success btn-sm" @click="saveToolOrder">保存排序</button>
+                            </div>
+                        </div>
+                    </div>
+
                     <!-- FH基本配置 -->
                     <div class="settings-section">
                         <h4># FH基本配置 <span class="x-tips">(插件的一些基础配置,可以在这里统一操作)</span></h4>
@@ -304,3 +332,4 @@
 </body>
 </html> 
 
+

+ 152 - 1
apps/options/index.js

@@ -61,8 +61,13 @@ new Vue({
             data: null
         },
 
+        // 工具排序相关
+        sortableTools: [], // 可排序的工具列表
+        draggedIndex: -1, // 拖拽的工具索引
+
         recentCount: 0,
         versionChecked: false,
+        
         // 推荐卡片配置,后续可从服务端获取
         recommendationCards: [
             {
@@ -993,8 +998,10 @@ new Vue({
         },
         
         // 显示设置模态框
-        showSettings() {
+        async showSettings() {
             this.showSettingsModal = true;
+            // 加载可排序的工具列表
+            await this.loadSortableTools();
         },
 
         // 关闭设置模态框
@@ -1235,6 +1242,149 @@ new Vue({
                 console.error('获取远程推荐卡片配置失败:', error);
             }
         },
+
+        // 工具排序相关方法
+        async loadSortableTools() {
+            try {
+                const installedTools = 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;
+                
+                // 转换为可排序的数组格式
+                let toolsArray = Object.entries(installedTools).map(([key, tool]) => ({
+                    key,
+                    name: tool.name,
+                    tips: tool.tips,
+                    icon: tool.icon || (tool.menuConfig && tool.menuConfig[0] ? tool.menuConfig[0].icon : '🔧')
+                }));
+                
+                // 如果有保存的自定义排序,按照该顺序排列
+                if (savedOrder && Array.isArray(savedOrder)) {
+                    const orderedTools = [];
+                    const unorderedTools = [...toolsArray];
+                    
+                    // 按照保存的顺序添加工具
+                    savedOrder.forEach(toolKey => {
+                        const toolIndex = unorderedTools.findIndex(t => t.key === toolKey);
+                        if (toolIndex !== -1) {
+                            orderedTools.push(unorderedTools.splice(toolIndex, 1)[0]);
+                        }
+                    });
+                    
+                    // 添加新安装的工具(不在保存的顺序中的)
+                    orderedTools.push(...unorderedTools);
+                    toolsArray = orderedTools;
+                }
+                
+                this.sortableTools = toolsArray;
+            } catch (error) {
+                console.error('加载可排序工具失败:', error);
+            }
+        },
+
+        // 拖拽开始
+        handleDragStart(event, index) {
+            this.draggedIndex = index;
+            event.target.classList.add('dragging');
+            event.dataTransfer.setData('text/plain', index);
+        },
+
+        // 拖拽经过
+        handleDragOver(event) {
+            event.preventDefault();
+            // 移除所有 drag-over 类
+            document.querySelectorAll('.sortable-item').forEach(item => {
+                item.classList.remove('drag-over');
+            });
+            // 添加到当前元素
+            event.currentTarget.classList.add('drag-over');
+        },
+
+        // 放置
+        handleDrop(event, dropIndex) {
+            event.preventDefault();
+            
+            if (this.draggedIndex === -1 || this.draggedIndex === dropIndex) {
+                return;
+            }
+
+            // 重新排列数组
+            const draggedItem = this.sortableTools[this.draggedIndex];
+            const newTools = [...this.sortableTools];
+            
+            // 移除被拖拽的项目
+            newTools.splice(this.draggedIndex, 1);
+            
+            // 在新位置插入
+            if (dropIndex > this.draggedIndex) {
+                newTools.splice(dropIndex - 1, 0, draggedItem);
+            } else {
+                newTools.splice(dropIndex, 0, draggedItem);
+            }
+            
+            this.sortableTools = newTools;
+            
+            // 清理样式
+            this.cleanupDragStyles();
+        },
+
+        // 拖拽结束
+        handleDragEnd(event) {
+            this.cleanupDragStyles();
+            this.draggedIndex = -1;
+        },
+
+        // 清理拖拽样式
+        cleanupDragStyles() {
+            document.querySelectorAll('.sortable-item').forEach(item => {
+                item.classList.remove('dragging', 'drag-over');
+            });
+        },
+
+        // 重置工具顺序为默认
+        async resetToolOrder() {
+            try {
+                // 移除保存的自定义排序
+                await chrome.storage.local.remove('tool_custom_order');
+                
+                // 重新加载工具列表(会使用默认顺序)
+                await this.loadSortableTools();
+                
+                this.showInPageNotification({
+                    message: '工具顺序已重置为默认排序',
+                    type: 'success'
+                });
+            } catch (error) {
+                console.error('重置工具顺序失败:', error);
+                this.showInPageNotification({
+                    message: '重置失败,请重试',
+                    type: 'error'
+                });
+            }
+        },
+
+        // 保存工具排序
+        async saveToolOrder() {
+            try {
+                const toolOrder = this.sortableTools.map(tool => tool.key);
+                await chrome.storage.local.set({
+                    tool_custom_order: JSON.stringify(toolOrder)
+                });
+                
+                this.showInPageNotification({
+                    message: '工具排序已保存!弹窗中的工具将按此顺序显示',
+                    type: 'success'
+                });
+            } catch (error) {
+                console.error('保存工具排序失败:', error);
+                this.showInPageNotification({
+                    message: '保存失败,请重试',
+                    type: 'error'
+                });
+            }
+        }
     },
 
     watch: {
@@ -1282,3 +1432,4 @@ if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) {
 } 
 
 
+

+ 26 - 2
apps/popup/index.js

@@ -41,8 +41,31 @@ new Vue({
         // 获取当前ctx的version
         this.manifest = chrome.runtime.getManifest();
 
-        Awesome.getInstalledTools().then(tools => {
-            this.fhTools = tools;
+        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;
+            }
         });
 
         // 自动开关灯
@@ -143,3 +166,4 @@ new Vue({
     }
 });
 
+