Pārlūkot izejas kodu

update json-format & json-diff css style

zxlie 6 mēneši atpakaļ
vecāks
revīzija
c316498423

+ 3 - 3
.gitignore

@@ -5,6 +5,6 @@
 node_modules/
 package-lock.json
 Users/
-output/apps/
-output-firefox/apps/
-output-edge/apps/
+output/
+output-firefox/
+output-edge/

+ 18 - 0
apps/json-diff/index-event-handler.js

@@ -0,0 +1,18 @@
+/**
+ * 事件处理器
+ * 用于处理所有示例数据的点击事件
+ */
+(function() {
+    window.addEventListener('DOMContentLoaded', function() {
+        // 获取所有示例按钮并绑定事件
+        document.querySelectorAll('.example-button').forEach(function(button) {
+            button.addEventListener('click', function(e) {
+                e.preventDefault();
+                const exampleType = this.getAttribute('data-example');
+                if (window.vueApp && typeof window.vueApp.fillExample === 'function') {
+                    window.vueApp.fillExample(exampleType);
+                }
+            });
+        });
+    });
+})(); 

+ 84 - 3
apps/json-diff/index.css

@@ -6,7 +6,7 @@
 }
 .wp-json .mod-json {
     position: absolute;
-    top: 60px;
+    top: 40px;
     bottom: 0;
     right:0;
     left:0;
@@ -27,12 +27,45 @@ body[browser-extension] .wp-json .mod-json {
     min-height: 550px;
 }
 
+.json-examples {
+    margin: 0 15px 10px 15px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #eee;
+    display: flex;
+    align-items: center;
+}
+
+.json-examples span {
+    font-size: 14px;
+    color: #666;
+    margin-right: 10px;
+}
+
+.json-examples a {
+    display: inline-block;
+    margin-right: 15px;
+    color: #337ab7;
+    text-decoration: none;
+    font-size: 14px;
+}
+
+.json-examples a:hover {
+    color: #23527c;
+    text-decoration: underline;
+}
+
+.result-display {
+    margin-left: auto !important;
+    font-size: 14px !important;
+    white-space: nowrap;
+}
+
 .box-wrapper-left {
-    height: 100%;
+    height: 90%;
     padding:0 5px 0 0;
 }
 .box-wrapper-right {
-    height: 100%;
+    height: 90%;
     padding:0 0 0 5px;
 }
 #jsonSourceLeft, #jsonSourceRight, .CodeMirror {
@@ -51,3 +84,51 @@ body[browser-extension] .wp-json .mod-json {
 .x-error .x-hlt1 {
     color:#f00;
 }
+
+/* 探索更多工具样式 */
+.panel-title>a.x-other-tools {
+    margin: 1px 0 0;
+    font-size: 13px;
+    cursor: pointer;
+    text-decoration: none;
+    -webkit-user-select: none;
+    user-select: none;
+    color: #333;
+    float: right;
+    background-color: #f5f8ff;
+    padding: 5px 10px;
+    border-radius: 15px;
+    border: 1px solid #d0d9ff;
+    transition: all 0.3s ease;
+    display: flex;
+    align-items: center;
+    position: relative;
+    top: -6px;
+}
+
+.panel-title>a.x-other-tools .icon-plus-circle {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    background: url(../static/img/plus-circle.svg) no-repeat center center;
+    background-size: contain;
+    margin-right: 5px;
+}
+
+.panel-title>a.x-other-tools .tool-market-badge {
+    display: inline-block;
+    background-color: #4d89fe;
+    color: white;
+    padding: 2px 6px;
+    border-radius: 10px;
+    margin-left: 5px;
+    font-size: 12px;
+    font-weight: bold;
+}
+
+.panel-title>a.x-other-tools:hover {
+    color: #333;
+    background-color: #e6edff;
+    box-shadow: 0 2px 5px rgba(0,0,0,0.15);
+    transform: translateY(-1px);
+}

+ 17 - 2
apps/json-diff/index.html

@@ -15,13 +15,26 @@
                     <h3 class="panel-title">
                         <a href="https://www.baidufe.com/fehelper/index/index.html" target="_blank" class="x-a-high">
                             <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper</a>:JSON比对工具
-
-                        <span class="x-error" v-bind:class="{'x-hlt' : errorHighlight}" v-html="errorMessage"></span>
+                            
+                        <a class="x-other-tools" @click="openOptionsPage"><i class="icon-plus-circle"></i> 探索更多实用工具 <span class="tool-market-badge">工具市场</span></a>
                     </h3>
                 </div>
             </div>
 
             <div class="panel-body mod-json">
+                <div class="json-examples">
+                    <span>示例数据:</span>
+                    <a href="#" class="example-button" data-example="userInfo">用户信息</a>
+                    <a href="#" class="example-button" data-example="productData">商品数据</a>
+                    <a href="#" class="example-button" data-example="configOptions">配置选项</a>
+                    <a href="#" class="example-button" data-example="apiResponse">API响应</a>
+                    <span class="x-error result-display" v-bind:class="{'x-hlt': errorHighlight}" v-if="!!errorMessage">
+                        <span class="x-hlt">Tips:</span>
+                        <span v-if="leftSideError" class="x-hlt1">左侧</span>
+                        <span v-if="rightSideError" class="x-hlt1">右侧</span>
+                        <span>{{ errorMessage }}</span>
+                    </span>
+                </div>
                 <div class="col-md-6 box-wrapper-left">
                     <textarea class="form-control mod-textarea" id="jsonSourceLeft" ref="srcLeft" placeholder="在这里粘贴JSON代码"></textarea>
                 </div>
@@ -42,6 +55,8 @@
         <script src="../static/vendor/json-diff/json-patch-duplex.min.js"></script>
         <script src="../static/vendor/json-diff/json-source-map.js"></script>
         <script src="../static/vendor/json-diff/json-diff.js"></script>
+        <script src="./js-adapter.js"></script>
         <script src="./index.js"></script>
+        <script src="./index-event-handler.js"></script>
     </body>
 </html>

+ 387 - 18
apps/json-diff/index.js

@@ -1,42 +1,411 @@
-new Vue({
+// 创建Vue实例并暴露到全局供事件处理使用
+window.vueApp = new Vue({
     el: '#pageContainer',
     data: {
         errorMessage: '',
-        errorHighlight: false
+        tipMessage: 'Tips:',
+        errorHighlight: false,
+        hasErrorClass: false,
+        leftSideError: false,
+        rightSideError: false,
+        differenceCount: 0,
+        isDifferent: false,
+        jsonExamples: {
+            userInfo: {
+                left: {
+                    "id": 1001,
+                    "name": "张三",
+                    "age": 28,
+                    "email": "[email protected]",
+                    "address": {
+                        "city": "北京",
+                        "district": "朝阳区",
+                        "street": "建国路88号"
+                    },
+                    "tags": ["前端", "JavaScript", "Vue"],
+                    "isActive": true,
+                    "lastLogin": "2023-01-15T08:30:00Z"
+                },
+                right: {
+                    "id": 1001,
+                    "name": "张三",
+                    "age": 30,
+                    "email": "[email protected]",
+                    "address": {
+                        "city": "上海",
+                        "district": "浦东新区",
+                        "street": "建国路88号"
+                    },
+                    "tags": ["前端", "JavaScript", "React"],
+                    "isActive": true,
+                    "lastLogin": "2023-02-20T10:45:00Z"
+                }
+            },
+            productData: {
+                left: {
+                    "products": [
+                        {
+                            "id": "p001",
+                            "name": "智能手机",
+                            "price": 4999,
+                            "inventory": 100,
+                            "category": "电子产品",
+                            "specs": {
+                                "brand": "小米",
+                                "model": "Mi 11",
+                                "color": "黑色",
+                                "storage": "128GB"
+                            }
+                        },
+                        {
+                            "id": "p002",
+                            "name": "笔记本电脑",
+                            "price": 6999,
+                            "inventory": 50,
+                            "category": "电子产品",
+                            "specs": {
+                                "brand": "联想",
+                                "model": "ThinkPad",
+                                "color": "银色",
+                                "storage": "512GB"
+                            }
+                        }
+                    ]
+                },
+                right: {
+                    "products": [
+                        {
+                            "id": "p001",
+                            "name": "智能手机",
+                            "price": 5299,
+                            "inventory": 85,
+                            "category": "电子产品",
+                            "specs": {
+                                "brand": "小米",
+                                "model": "Mi 11 Pro",
+                                "color": "蓝色",
+                                "storage": "256GB"
+                            }
+                        },
+                        {
+                            "id": "p002",
+                            "name": "笔记本电脑",
+                            "price": 6999,
+                            "inventory": 50,
+                            "category": "电子产品",
+                            "specs": {
+                                "brand": "联想",
+                                "model": "ThinkPad",
+                                "color": "银色",
+                                "storage": "512GB"
+                            }
+                        }
+                    ]
+                }
+            },
+            configOptions: {
+                left: {
+                    "appConfig": {
+                        "theme": "light",
+                        "language": "zh-CN",
+                        "notifications": {
+                            "email": true,
+                            "push": true,
+                            "sms": false
+                        },
+                        "security": {
+                            "twoFactorAuth": true,
+                            "passwordExpiry": 90,
+                            "ipRestriction": false
+                        },
+                        "performance": {
+                            "cacheEnabled": true,
+                            "compressionLevel": "high",
+                            "preload": ["home", "dashboard"]
+                        }
+                    }
+                },
+                right: {
+                    "appConfig": {
+                        "theme": "dark",
+                        "language": "zh-CN",
+                        "notifications": {
+                            "email": true,
+                            "push": false,
+                            "sms": true
+                        },
+                        "security": {
+                            "twoFactorAuth": true,
+                            "passwordExpiry": 60,
+                            "ipRestriction": true
+                        },
+                        "performance": {
+                            "cacheEnabled": true,
+                            "compressionLevel": "medium",
+                            "preload": ["home", "profile", "dashboard"]
+                        }
+                    }
+                }
+            },
+            apiResponse: {
+                left: {
+                    "status": "success",
+                    "code": 200,
+                    "data": {
+                        "users": [
+                            {"id": 1, "name": "李明", "role": "admin"},
+                            {"id": 2, "name": "王芳", "role": "user"},
+                            {"id": 3, "name": "赵强", "role": "editor"}
+                        ],
+                        "pagination": {
+                            "total": 25,
+                            "page": 1,
+                            "limit": 10
+                        },
+                        "timestamp": 1642558132,
+                        "version": "1.0.0"
+                    }
+                },
+                right: {
+                    "status": "success",
+                    "code": 200,
+                    "data": {
+                        "users": [
+                            {"id": 1, "name": "李明", "role": "admin"},
+                            {"id": 2, "name": "王芳", "role": "user"},
+                            {"id": 3, "name": "赵强", "role": "moderator"}
+                        ],
+                        "pagination": {
+                            "total": 28,
+                            "page": 1,
+                            "limit": 10
+                        },
+                        "timestamp": 1652558132,
+                        "version": "1.2.0"
+                    }
+                }
+            }
+        }
     },
-    mounted: function () {
-        // 错误处理器
-        let errorHandler = (which, ok) => {
-            let message = '';
+    computed: {
+        // 显示的消息,计算属性替代v-html
+        displayMessage: function() {
+            return this.tipMessage + this.errorMessage;
+        }
+    },
+    methods: {
+        fillExample: function(exampleType) {
+            if (this.jsonExamples[exampleType]) {
+                const example = this.jsonExamples[exampleType];
+                jsonBox.left.setValue(JSON.stringify(example.left, null, 4));
+                jsonBox.right.setValue(JSON.stringify(example.right, null, 4));
+                
+                // 触发比对
+                setTimeout(() => {
+                    jsonBox.left.refresh();
+                    jsonBox.right.refresh();
+                    this.compareJson(); // 使用Vue实例的方法进行比对
+                }, 100);
+            }
+        },
+        // 添加比对JSON的方法
+        compareJson: function() {
+            // 使用全局变量中的实例
+            let leftText = jsonBox.left.getValue();
+            let rightText = jsonBox.right.getValue();
+            let leftJson, rightJson;
+            
+            try {
+                if (leftText) {
+                    leftJson = JSON.parse(leftText);
+                }
+                this.errorHandler('left', true);
+            } catch (e) {
+                console.log('left ==>', e);
+                this.errorHandler('left', false);
+                return;
+            }
+            
+            try {
+                if (rightText) {
+                    rightJson = JSON.parse(rightText);
+                }
+                this.errorHandler('right', true);
+            } catch (e) {
+                console.log('right ==>', e);
+                this.errorHandler('right', false);
+                return;
+            }
+            
+            if (!leftJson || !rightJson) {
+                if (!leftJson && !rightJson) {
+                    this.errorHandler('left-right', false);
+                } else if (!leftJson) {
+                    this.errorHandler('left', false);
+                } else {
+                    this.errorHandler('right', false);
+                }
+                return;
+            }
+            
+            try {
+                // 调用jsonpatch的compare方法进行比对
+                let diffs = jsonpatch.compare(leftJson, rightJson);
+                this.diffHandler(diffs);
+                
+                // 清除所有之前的标记
+                this.clearMarkers();
+                
+                // 高亮差异
+                diffs.forEach((diff) => {
+                    try {
+                        if (diff.op === 'remove') {
+                            this.highlightDiff(diff, 'remove');
+                        } else if (diff.op === 'add') {
+                            this.highlightDiff(diff, 'add');
+                        } else if (diff.op === 'replace') {
+                            this.highlightDiff(diff, 'replace');
+                        }
+                    } catch (e) {
+                        console.warn('error while trying to highlight diff', e);
+                    }
+                });
+            } catch (e) {
+                console.error('比对过程出错:', e);
+            }
+        },
+        // 清除所有标记
+        clearMarkers: function() {
+            jsonBox.left.getAllMarks().forEach(function(marker) {
+                marker.clear();
+            });
+            jsonBox.right.getAllMarks().forEach(function(marker) {
+                marker.clear();
+            });
+        },
+        // 高亮差异
+        highlightDiff: function(diff, op) {
+            if (op === 'remove') {
+                this.highlightRemoval(jsonBox.left, diff);
+            } else if (op === 'add') {
+                this.highlightAddition(jsonBox.right, diff);
+            } else if (op === 'replace') {
+                this.highlightChange(jsonBox.left, diff);
+                this.highlightChange(jsonBox.right, diff);
+            }
+        },
+        // 高亮删除
+        highlightRemoval: function(editor, diff) {
+            this._highlight(editor, diff, '#DD4444');
+        },
+        // 高亮添加
+        highlightAddition: function(editor, diff) {
+            this._highlight(editor, diff, '#4ba2ff');
+        },
+        // 高亮修改
+        highlightChange: function(editor, diff) {
+            this._highlight(editor, diff, '#E5E833');
+        },
+        // 高亮辅助方法
+        _highlight: function(editor, diff, color) {
+            try {
+                let textValue = editor.getValue();
+                // 使用全局jsonSourceMap对象
+                let result = jsonSourceMap.parse(textValue);
+                let pointers = result.pointers;
+                let path = diff.path;
+                
+                if (!pointers[path]) {
+                    console.warn('找不到路径的指针:', path);
+                    return;
+                }
+                
+                let start = {
+                    line: pointers[path].key ? pointers[path].key.line : pointers[path].value.line,
+                    ch: pointers[path].key ? pointers[path].key.column : pointers[path].value.column
+                };
+                let end = {
+                    line: pointers[path].valueEnd.line,
+                    ch: pointers[path].valueEnd.column
+                };
+                
+                editor.markText(start, end, {
+                    css: 'background-color: ' + color
+                });
+            } catch (e) {
+                console.error('高亮过程出错:', e);
+            }
+        },
+        // 错误处理
+        errorHandler: function(which, ok) {
             if (ok) {
-                message = '两侧JSON比对完成!';
+                this.errorMessage = '两侧JSON比对完成!';
                 this.errorHighlight = false;
+                this.leftSideError = false;
+                this.rightSideError = false;
             } else {
                 let side = {'left': '左', 'right': '右', 'left-right': '两'}[which];
                 if(!jsonBox.left.getValue().trim().length) {
-                    message = '请在<span class="x-hlt1">左侧</span>填入待比对的JSON内容!'
+                    this.errorMessage = '请在左侧填入待比对的JSON内容!';
+                    this.leftSideError = true;
+                    this.rightSideError = false;
                 }else if(!jsonBox.right.getValue().trim().length) {
-                    message = '请在<span class="x-hlt1">右侧</span>填入待比对的JSON内容!'
+                    this.errorMessage = '请在右侧填入待比对的JSON内容!';
+                    this.leftSideError = false;
+                    this.rightSideError = true;
                 }else{
-                    message = '<span class="x-hlt1">' + side + '侧</span>JSON不合法!';
+                    this.errorMessage = side + '侧JSON不合法!';
+                    if (which === 'left') {
+                        this.leftSideError = true;
+                        this.rightSideError = false;
+                    } else if (which === 'right') {
+                        this.leftSideError = false;
+                        this.rightSideError = true;
+                    } else {
+                        this.leftSideError = true;
+                        this.rightSideError = true;
+                    }
                 }
                 this.errorHighlight = true;
             }
-            this.errorMessage = '<span class="x-hlt">Tips:</span>' + message;
-        };
-
+        },
         // diff处理器
-        let diffHandler = (diffs) => {
+        diffHandler: function(diffs) {
             if (!this.errorHighlight) {
+                this.differenceCount = diffs.length;
+                this.isDifferent = diffs.length > 0;
                 if (diffs.length) {
-                    this.errorMessage += '共有 <span class="x-hlt">' + diffs.length + '</span> 处不一致!';
+                    this.errorMessage += '共有 ' + diffs.length + ' 处不一致!';
                 } else {
                     this.errorMessage += '且JSON内容一致!';
                 }
             }
-        };
+        },
 
-        // 代码比对
-        let jsonBox = JsonDiff.init(this.$refs.srcLeft, this.$refs.srcRight, errorHandler, diffHandler);
+        // 打开工具市场页面
+        openOptionsPage: function() {
+            chrome.runtime.openOptionsPage();
+        }
+    },
+    mounted: function () {
+        // 初始化JSON编辑器
+        let jsonBox = JsonDiff.init(this.$refs.srcLeft, this.$refs.srcRight, 
+            this.errorHandler.bind(this), 
+            this.diffHandler.bind(this)
+        );
+        
+        // 添加比较方法
+        jsonBox.compare = this.compareJson.bind(this);
+        
+        // 初始化文本变更监听
+        jsonBox.left.on('change', () => {
+            setTimeout(() => this.compareJson(), 300);
+        });
+        jsonBox.right.on('change', () => {
+            setTimeout(() => this.compareJson(), 300);
+        });
+        
+        // 暴露到全局,供示例数据使用
+        window.jsonBox = jsonBox;
     }
 });

+ 10 - 0
apps/json-diff/js-adapter.js

@@ -0,0 +1,10 @@
+/**
+ * JSON工具适配器
+ * 用于桥接json-source-map库和主应用
+ */
+(function() {
+    // 确保json-source-map.js中的parse函数在全局可用
+    window.jsonSourceMap = {
+        parse: parse
+    };
+})(); 

+ 37 - 6
apps/json-format/index.css

@@ -145,19 +145,50 @@ html.fh-jf .x-toolbar.x-inpage {
     margin:10px 20px;
 }
 .panel-title>a.x-other-tools {
-    margin:10px 0 0;
-    font-size: 12px;
+    margin: 1px 0 0;
+    font-size: 13px;
     cursor: pointer;
-    text-decoration: underline;
+    text-decoration: none;
     -webkit-user-select: none;
     user-select: none;
-    color: #f00;
-    border-bottom: 1px solid #f00;
+    color: #333;
     float: right;
+    background-color: #f5f8ff;
+    padding: 5px 10px;
+    border-radius: 15px;
+    border: 1px solid #d0d9ff;
+    transition: all 0.3s ease;
+    display: flex;
+    align-items: center;
 }
+
+.panel-title>a.x-other-tools .icon-plus-circle {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    background: url(../static/img/plus-circle.svg) no-repeat center center;
+    background-size: contain;
+    margin-right: 5px;
+}
+
+.panel-title>a.x-other-tools .tool-market-badge {
+    display: inline-block;
+    background-color: #4d89fe;
+    color: white;
+    padding: 2px 6px;
+    border-radius: 10px;
+    margin-left: 5px;
+    font-size: 12px;
+    font-weight: bold;
+}
+
 .panel-title>a.x-other-tools:hover {
-    color:#00f;
+    color: #333;
+    background-color: #e6edff;
+    box-shadow: 0 2px 5px rgba(0,0,0,0.15);
+    transform: translateY(-1px);
 }
+
 .panel-heading {
     padding:5px 15px;
 }

+ 1 - 1
apps/json-format/index.html

@@ -21,7 +21,7 @@
                             <button id="btnLeftRight" ref="btnLeftRight" class="selected" @click="changeLayout('left-right')">左右布局</button><button id="btnUpDown" ref="btnUpDown" @click="changeLayout('up-down')">上下布局</button>
                         </span>
 
-                        <a class="x-other-tools" @click="openOptionsPage()">去开启FeHelper的更多功能&gt;&gt;</a>
+                        <a class="x-other-tools" @click="openOptionsPage()"><i class="icon-plus-circle"></i> 探索更多实用工具 <span class="tool-market-badge">工具市场</span></a>
                     </h3>
                 </div>
             </div>

+ 4 - 2
apps/options/market.js

@@ -100,7 +100,8 @@ new Vue({
 
             // 分类过滤,在所有视图下生效
             if (this.currentCategory) {
-                const categoryTools = TOOL_CATEGORIES.find(c => c.key === this.currentCategory)?.tools || [];
+                const category = TOOL_CATEGORIES.find(c => c.key === this.currentCategory);
+                const categoryTools = category ? category.tools : [];
                 result = result.filter(tool => categoryTools.includes(tool.key));
             }
 
@@ -627,7 +628,8 @@ new Vue({
         },
 
         getCategoryCount(categoryKey) {
-            const categoryTools = TOOL_CATEGORIES.find(c => c.key === categoryKey)?.tools || [];
+            const category = TOOL_CATEGORIES.find(c => c.key === categoryKey);
+            const categoryTools = category ? category.tools : [];
             return categoryTools.length;
         },
 

+ 6 - 1
apps/static/css/bootstrap.min.css

@@ -262,7 +262,7 @@ a.x-a-high, a.x-a-high:visited {
     padding-left: 90px;
 }
 #pageContainer>.panel-default {
-    margin: 20px;
+    margin: 0px;
 }
 #pageContainer>.panel-body {
     margin: 0 20px;
@@ -287,4 +287,9 @@ a.x-a-high, a.x-a-high:visited {
 
 .mod-footer .footer-box .x-beian {
     color: #666
+}
+
+html.fh-jf input[type=radio], input[type=checkbox] {
+    position: relative;
+    top: 2px;
 }

+ 5 - 0
apps/static/img/plus-circle.svg

@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4d89fe" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+  <circle cx="12" cy="12" r="10"></circle>
+  <line x1="12" y1="8" x2="12" y2="16"></line>
+  <line x1="8" y1="12" x2="16" y2="12"></line>
+</svg>