├── index.js ├── img ├── compare.png └── version3.png ├── package.json ├── src ├── normalTree.vue ├── tree.vue ├── complexTree.vue ├── index.vue └── utils.js ├── LICENSE └── README.md /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = require('./src/index.vue'); 3 | -------------------------------------------------------------------------------- /img/compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/5SSS/vue-json-compare/HEAD/img/compare.png -------------------------------------------------------------------------------- /img/version3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/5SSS/vue-json-compare/HEAD/img/version3.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-json-compare", 3 | "version": "3.0.0", 4 | "description": "A vue(2.x) components for compare JSON data", 5 | "main": "./index.js", 6 | "keywords": [ 7 | "vue", 8 | "json", 9 | "compare" 10 | ], 11 | "author": "alpaca", 12 | "license": "MIT", 13 | "dependencies": { 14 | "vue": "^2.6.11" 15 | } 16 | } -------------------------------------------------------------------------------- /src/normalTree.vue: -------------------------------------------------------------------------------- 1 | 12 | 36 | -------------------------------------------------------------------------------- /src/tree.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2018 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-json-compare 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/5SSS/vue-json-compare) 4 | 5 | A vue(2.x) components for compare Object or Array 6 | 7 | ## Links 8 | 9 | - [Github](https://github.com/5SSS/vue-json-compare) 10 | 11 | ## Install 12 | 13 | ```js 14 | npm install --save vue-json-compare 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```html 20 | 25 | ``` 26 | 27 | ```js 28 | import vueJsonCompare from 'vue-json-compare'; 29 | const oldData = { 30 | name: 'super', 31 | age: 18, 32 | task: [ 33 | { name: 'eat', time: '09:00' }, 34 | { name: 'work', time: '10:00', deleted: 'this prop has been deleted!' }, 35 | { name: 'sleep', time: '22:00' }, 36 | ], 37 | }; 38 | const newData = { 39 | name: 'coolapt', 40 | age: 20, 41 | task: [ 42 | { name: 'eat', time: '09:00' }, 43 | { name: 'work', time: '10:00' }, 44 | { name: 'sleep', time: '23:00' }, 45 | { name: 'running', time: '08:00' }, 46 | ], 47 | }; 48 | export default { 49 | components: { 50 | vueJsonCompare, 51 | }, 52 | data() { 53 | return { 54 | oldData: oldData, 55 | newData: newData, 56 | }; 57 | }, 58 | }; 59 | ``` 60 | 61 | ## Result 62 | 63 | ![示例](./img/version3.png) 64 | 65 | ## Props 66 | 67 | | Attribute | Level | Description | Type | Default | 68 | | --------- | ----- | ----------- | --------------------------------------------- | ------- | 69 | | oldData | basic | json data | object or object Array, {...}, [{...}, {...}] | - | 70 | | newData | basic | json data | object or object Array, {...}, [{...}, {...}] | - | 71 | 72 | ## Events 73 | 74 | not yet... 75 | 76 | ## PS 77 | 78 | 如果喜欢请给个星星,谢谢。 79 | If you like, please give me a star, thank you. 80 | 81 | 如果需要帮助: QQ:1573815240 邮箱: 1573815240@qq.com 82 | if you need help: QQ:1573815240 email: 1573815240@qq.com 83 | -------------------------------------------------------------------------------- /src/complexTree.vue: -------------------------------------------------------------------------------- 1 | 49 | 89 | -------------------------------------------------------------------------------- /src/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 52 | 53 | 218 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const isArray = (item) => { 2 | if (item === 'array') { 3 | return true; 4 | } 5 | return Object.prototype.toString.call(item) === '[object Array]'; 6 | }; 7 | 8 | const isObject = (item) => { 9 | return Object.prototype.toString.call(item) === '[object Object]'; 10 | }; 11 | 12 | const needFormat = (type) => { 13 | return type === 'array' || type === 'object'; 14 | }; 15 | 16 | const getIndent = (level) => { 17 | if (level === 1) { 18 | return { textIndent: '20px' }; 19 | } 20 | return { textIndent: `${level * 20}px` }; 21 | }; 22 | 23 | const getType = (item) => { 24 | let t = Object.prototype.toString.call(item); 25 | let match = /(?!\[).+(?=\])/g; 26 | t = t.match(match)[0].split(' ')[1]; 27 | return t.toLowerCase(); 28 | }; 29 | 30 | const isComplexType = (param) => { 31 | return isObject(param) || isArray(param); 32 | }; 33 | 34 | const isTheSametype = (a, b) => { 35 | return ( 36 | Object.prototype.toString.call(a) === Object.prototype.toString.call(b) 37 | ); 38 | }; 39 | 40 | const mergeData = (_old, _new) => { 41 | // finally result 42 | let result = []; 43 | // each line No. 44 | let start = 1; 45 | 46 | // convert array or object to Array [{}] 47 | const convertObject = (param, lineType) => { 48 | let list = []; 49 | if (isComplexType(param)) { 50 | let showIndex = getType(param) === 'object'; 51 | let keys = Object.keys(param); 52 | let length = keys.length; 53 | keys.forEach((key, index) => { 54 | let type = getType(param[key]); 55 | list.push({ 56 | name: key, 57 | line: start++, 58 | value: convertObject(param[key], lineType), 59 | type: type, 60 | showIndex: showIndex, 61 | needComma: length !== index + 1, 62 | lineType: lineType, 63 | lastLineType: lineType, 64 | lastLine: isComplexType(param[key]) ? start++ : null, 65 | }); 66 | }); 67 | return list; 68 | } else { 69 | switch (getType(param)) { 70 | case 'number': 71 | case 'boolean': 72 | case 'regexp': 73 | return param.toString(); 74 | case 'null': 75 | return 'null'; 76 | case 'undefined': 77 | return 'undefined'; 78 | case 'function': 79 | return ' ƒ() {...}'; 80 | default: 81 | return `"${param.toString()}"`; 82 | } 83 | } 84 | }; 85 | 86 | // return parsed data 87 | const parseValue = (key, value, showIndex, needComma, lineType) => { 88 | return { 89 | name: key, 90 | line: start++, 91 | value: convertObject(value, lineType), 92 | type: getType(value), 93 | showIndex: showIndex, 94 | needComma: needComma, 95 | lineType: lineType, 96 | lastLineType: lineType, 97 | lastLine: isComplexType(value) ? start++ : null, 98 | }; 99 | }; 100 | 101 | // merge two vars to target,target type Array[{}] 102 | const parseData = (a, b, target) => { 103 | let _ar = Object.keys(a); 104 | let _br = Object.keys(b); 105 | let showIndex = isObject(b); 106 | // deleted keys 107 | let _del = _ar.filter((ak) => !_br.some((bk) => bk === ak)); 108 | // not removed keys 109 | let _stl = _ar.filter((ak) => _br.some((bk) => bk === ak)); 110 | // new added keys 111 | let _add = _br.filter((bk) => !_ar.some((ak) => ak === bk)); 112 | // push deleted keys 113 | _del.forEach((key, index) => { 114 | let needComma = true; 115 | if (_stl.length === 0 && _add.length === 0 && index === _del.length - 1) { 116 | needComma = false; 117 | } 118 | target.push(parseValue(key, a[key], showIndex, needComma, 'del')); 119 | }); 120 | // The core function: compare 121 | _stl.forEach((key, index) => { 122 | let needComma = true; 123 | if (_add.length === 0 && index === _stl.length - 1) { 124 | needComma = false; 125 | } 126 | if (a[key] === b[key]) { 127 | target.push(parseValue(key, b[key], showIndex, needComma, 'none')); 128 | } else if (isTheSametype(a[key], b[key])) { 129 | if (isComplexType(b[key])) { 130 | let _target = parseValue( 131 | key, 132 | isArray(a[key]) ? [] : {}, 133 | showIndex, 134 | needComma, 135 | 'none' 136 | ); 137 | target.push(_target); 138 | // back one step 139 | start -= 1; 140 | // go inside 141 | parseData(a[key], b[key], _target.value); 142 | // rewrite lastline 143 | _target.lastLine = start++; 144 | } else { 145 | target.push(parseValue(key, a[key], showIndex, true, 'del')); 146 | target.push(parseValue(key, b[key], showIndex, needComma, 'add')); 147 | } 148 | } else { 149 | target.push(parseValue(key, a[key], showIndex, true, 'del')); 150 | target.push(parseValue(key, b[key], showIndex, needComma, 'add')); 151 | } 152 | }); 153 | // push new keys 154 | _add.forEach((key, index) => { 155 | target.push( 156 | parseValue(key, b[key], showIndex, _add.length !== index + 1, 'add') 157 | ); 158 | }); 159 | }; 160 | 161 | if (isTheSametype(_old, _new) && isComplexType(_new)) { 162 | parseData(_old, _new, result); 163 | } else { 164 | if (_old === _new) { 165 | result.push(parseValue(0, _new, false, false, 'none')); 166 | } else { 167 | result.push(parseValue(0, _old, false, true, 'del')); 168 | result.push(parseValue(1, _new, false, false, 'add')); 169 | } 170 | } 171 | return result; 172 | }; 173 | 174 | export { 175 | isArray, 176 | isObject, 177 | needFormat, 178 | getIndent, 179 | getType, 180 | isComplexType, 181 | isTheSametype, 182 | mergeData, 183 | }; 184 | --------------------------------------------------------------------------------