index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // 创建Vue实例并暴露到全局供事件处理使用
  2. window.vueApp = new Vue({
  3. el: '#pageContainer',
  4. data: {
  5. errorMessage: '',
  6. tipMessage: 'Tips:',
  7. errorHighlight: false,
  8. hasErrorClass: false,
  9. leftSideError: false,
  10. rightSideError: false,
  11. differenceCount: 0,
  12. isDifferent: false,
  13. jsonExamples: {
  14. userInfo: {
  15. left: {
  16. "id": 1001,
  17. "name": "张三",
  18. "age": 28,
  19. "email": "[email protected]",
  20. "address": {
  21. "city": "北京",
  22. "district": "朝阳区",
  23. "street": "建国路88号"
  24. },
  25. "tags": ["前端", "JavaScript", "Vue"],
  26. "isActive": true,
  27. "lastLogin": "2023-01-15T08:30:00Z"
  28. },
  29. right: {
  30. "id": 1001,
  31. "name": "张三",
  32. "age": 30,
  33. "email": "[email protected]",
  34. "address": {
  35. "city": "上海",
  36. "district": "浦东新区",
  37. "street": "建国路88号"
  38. },
  39. "tags": ["前端", "JavaScript", "React"],
  40. "isActive": true,
  41. "lastLogin": "2023-02-20T10:45:00Z"
  42. }
  43. },
  44. productData: {
  45. left: {
  46. "products": [
  47. {
  48. "id": "p001",
  49. "name": "智能手机",
  50. "price": 4999,
  51. "inventory": 100,
  52. "category": "电子产品",
  53. "specs": {
  54. "brand": "小米",
  55. "model": "Mi 11",
  56. "color": "黑色",
  57. "storage": "128GB"
  58. }
  59. },
  60. {
  61. "id": "p002",
  62. "name": "笔记本电脑",
  63. "price": 6999,
  64. "inventory": 50,
  65. "category": "电子产品",
  66. "specs": {
  67. "brand": "联想",
  68. "model": "ThinkPad",
  69. "color": "银色",
  70. "storage": "512GB"
  71. }
  72. }
  73. ]
  74. },
  75. right: {
  76. "products": [
  77. {
  78. "id": "p001",
  79. "name": "智能手机",
  80. "price": 5299,
  81. "inventory": 85,
  82. "category": "电子产品",
  83. "specs": {
  84. "brand": "小米",
  85. "model": "Mi 11 Pro",
  86. "color": "蓝色",
  87. "storage": "256GB"
  88. }
  89. },
  90. {
  91. "id": "p002",
  92. "name": "笔记本电脑",
  93. "price": 6999,
  94. "inventory": 50,
  95. "category": "电子产品",
  96. "specs": {
  97. "brand": "联想",
  98. "model": "ThinkPad",
  99. "color": "银色",
  100. "storage": "512GB"
  101. }
  102. }
  103. ]
  104. }
  105. },
  106. configOptions: {
  107. left: {
  108. "appConfig": {
  109. "theme": "light",
  110. "language": "zh-CN",
  111. "notifications": {
  112. "email": true,
  113. "push": true,
  114. "sms": false
  115. },
  116. "security": {
  117. "twoFactorAuth": true,
  118. "passwordExpiry": 90,
  119. "ipRestriction": false
  120. },
  121. "performance": {
  122. "cacheEnabled": true,
  123. "compressionLevel": "high",
  124. "preload": ["home", "dashboard"]
  125. }
  126. }
  127. },
  128. right: {
  129. "appConfig": {
  130. "theme": "dark",
  131. "language": "zh-CN",
  132. "notifications": {
  133. "email": true,
  134. "push": false,
  135. "sms": true
  136. },
  137. "security": {
  138. "twoFactorAuth": true,
  139. "passwordExpiry": 60,
  140. "ipRestriction": true
  141. },
  142. "performance": {
  143. "cacheEnabled": true,
  144. "compressionLevel": "medium",
  145. "preload": ["home", "profile", "dashboard"]
  146. }
  147. }
  148. }
  149. },
  150. apiResponse: {
  151. left: {
  152. "status": "success",
  153. "code": 200,
  154. "data": {
  155. "users": [
  156. {"id": 1, "name": "李明", "role": "admin"},
  157. {"id": 2, "name": "王芳", "role": "user"},
  158. {"id": 3, "name": "赵强", "role": "editor"}
  159. ],
  160. "pagination": {
  161. "total": 25,
  162. "page": 1,
  163. "limit": 10
  164. },
  165. "timestamp": 1642558132,
  166. "version": "1.0.0"
  167. }
  168. },
  169. right: {
  170. "status": "success",
  171. "code": 200,
  172. "data": {
  173. "users": [
  174. {"id": 1, "name": "李明", "role": "admin"},
  175. {"id": 2, "name": "王芳", "role": "user"},
  176. {"id": 3, "name": "赵强", "role": "moderator"}
  177. ],
  178. "pagination": {
  179. "total": 28,
  180. "page": 1,
  181. "limit": 10
  182. },
  183. "timestamp": 1652558132,
  184. "version": "1.2.0"
  185. }
  186. }
  187. }
  188. }
  189. },
  190. computed: {
  191. // 显示的消息,计算属性替代v-html
  192. displayMessage: function() {
  193. return this.tipMessage + this.errorMessage;
  194. }
  195. },
  196. methods: {
  197. fillExample: function(exampleType) {
  198. if (this.jsonExamples[exampleType]) {
  199. const example = this.jsonExamples[exampleType];
  200. jsonBox.left.setValue(JSON.stringify(example.left, null, 4));
  201. jsonBox.right.setValue(JSON.stringify(example.right, null, 4));
  202. // 触发比对
  203. setTimeout(() => {
  204. jsonBox.left.refresh();
  205. jsonBox.right.refresh();
  206. this.compareJson(); // 使用Vue实例的方法进行比对
  207. }, 100);
  208. }
  209. },
  210. // 添加比对JSON的方法
  211. compareJson: function() {
  212. // 使用全局变量中的实例
  213. let leftText = jsonBox.left.getValue();
  214. let rightText = jsonBox.right.getValue();
  215. let leftJson, rightJson;
  216. try {
  217. if (leftText) {
  218. leftJson = JSON.parse(leftText);
  219. }
  220. this.errorHandler('left', true);
  221. } catch (e) {
  222. console.log('left ==>', e);
  223. this.errorHandler('left', false);
  224. return;
  225. }
  226. try {
  227. if (rightText) {
  228. rightJson = JSON.parse(rightText);
  229. }
  230. this.errorHandler('right', true);
  231. } catch (e) {
  232. console.log('right ==>', e);
  233. this.errorHandler('right', false);
  234. return;
  235. }
  236. if (!leftJson || !rightJson) {
  237. if (!leftJson && !rightJson) {
  238. this.errorHandler('left-right', false);
  239. } else if (!leftJson) {
  240. this.errorHandler('left', false);
  241. } else {
  242. this.errorHandler('right', false);
  243. }
  244. return;
  245. }
  246. try {
  247. // 调用jsonpatch的compare方法进行比对
  248. let diffs = jsonpatch.compare(leftJson, rightJson);
  249. this.diffHandler(diffs);
  250. // 清除所有之前的标记
  251. this.clearMarkers();
  252. // 高亮差异
  253. diffs.forEach((diff) => {
  254. try {
  255. if (diff.op === 'remove') {
  256. this.highlightDiff(diff, 'remove');
  257. } else if (diff.op === 'add') {
  258. this.highlightDiff(diff, 'add');
  259. } else if (diff.op === 'replace') {
  260. this.highlightDiff(diff, 'replace');
  261. }
  262. } catch (e) {
  263. console.warn('error while trying to highlight diff', e);
  264. }
  265. });
  266. } catch (e) {
  267. console.error('比对过程出错:', e);
  268. }
  269. },
  270. // 清除所有标记
  271. clearMarkers: function() {
  272. jsonBox.left.getAllMarks().forEach(function(marker) {
  273. marker.clear();
  274. });
  275. jsonBox.right.getAllMarks().forEach(function(marker) {
  276. marker.clear();
  277. });
  278. },
  279. // 高亮差异
  280. highlightDiff: function(diff, op) {
  281. if (op === 'remove') {
  282. this.highlightRemoval(jsonBox.left, diff);
  283. } else if (op === 'add') {
  284. this.highlightAddition(jsonBox.right, diff);
  285. } else if (op === 'replace') {
  286. this.highlightChange(jsonBox.left, diff);
  287. this.highlightChange(jsonBox.right, diff);
  288. }
  289. },
  290. // 高亮删除
  291. highlightRemoval: function(editor, diff) {
  292. this._highlight(editor, diff, '#DD4444');
  293. },
  294. // 高亮添加
  295. highlightAddition: function(editor, diff) {
  296. this._highlight(editor, diff, '#4ba2ff');
  297. },
  298. // 高亮修改
  299. highlightChange: function(editor, diff) {
  300. this._highlight(editor, diff, '#E5E833');
  301. },
  302. // 高亮辅助方法
  303. _highlight: function(editor, diff, color) {
  304. try {
  305. let textValue = editor.getValue();
  306. // 使用全局jsonSourceMap对象
  307. let result = jsonSourceMap.parse(textValue);
  308. let pointers = result.pointers;
  309. let path = diff.path;
  310. if (!pointers[path]) {
  311. console.warn('找不到路径的指针:', path);
  312. return;
  313. }
  314. let start = {
  315. line: pointers[path].key ? pointers[path].key.line : pointers[path].value.line,
  316. ch: pointers[path].key ? pointers[path].key.column : pointers[path].value.column
  317. };
  318. let end = {
  319. line: pointers[path].valueEnd.line,
  320. ch: pointers[path].valueEnd.column
  321. };
  322. editor.markText(start, end, {
  323. css: 'background-color: ' + color
  324. });
  325. } catch (e) {
  326. console.error('高亮过程出错:', e);
  327. }
  328. },
  329. // 错误处理
  330. errorHandler: function(which, ok) {
  331. if (ok) {
  332. this.errorMessage = '两侧JSON比对完成!';
  333. this.errorHighlight = false;
  334. this.leftSideError = false;
  335. this.rightSideError = false;
  336. } else {
  337. let side = {'left': '左', 'right': '右', 'left-right': '两'}[which];
  338. if(!jsonBox.left.getValue().trim().length) {
  339. this.errorMessage = '请在左侧填入待比对的JSON内容!';
  340. this.leftSideError = true;
  341. this.rightSideError = false;
  342. }else if(!jsonBox.right.getValue().trim().length) {
  343. this.errorMessage = '请在右侧填入待比对的JSON内容!';
  344. this.leftSideError = false;
  345. this.rightSideError = true;
  346. }else{
  347. this.errorMessage = side + '侧JSON不合法!';
  348. if (which === 'left') {
  349. this.leftSideError = true;
  350. this.rightSideError = false;
  351. } else if (which === 'right') {
  352. this.leftSideError = false;
  353. this.rightSideError = true;
  354. } else {
  355. this.leftSideError = true;
  356. this.rightSideError = true;
  357. }
  358. }
  359. this.errorHighlight = true;
  360. }
  361. },
  362. // diff处理器
  363. diffHandler: function(diffs) {
  364. if (!this.errorHighlight) {
  365. this.differenceCount = diffs.length;
  366. this.isDifferent = diffs.length > 0;
  367. if (diffs.length) {
  368. this.errorMessage += '共有 ' + diffs.length + ' 处不一致!';
  369. } else {
  370. this.errorMessage += '且JSON内容一致!';
  371. }
  372. }
  373. },
  374. // 打开工具市场页面
  375. openOptionsPage: function(event){
  376. event.preventDefault();
  377. event.stopPropagation();
  378. chrome.runtime.openOptionsPage();
  379. },
  380. openDonateModal: function(event){
  381. event.preventDefault();
  382. event.stopPropagation();
  383. chrome.runtime.sendMessage({
  384. type: 'fh-dynamic-any-thing',
  385. thing: 'open-donate-modal',
  386. params: { toolName: 'json-diff' }
  387. });
  388. },
  389. loadPatchHotfix() {
  390. // 页面加载时自动获取并注入页面的补丁
  391. chrome.runtime.sendMessage({
  392. type: 'fh-dynamic-any-thing',
  393. thing: 'fh-get-tool-patch',
  394. toolName: 'json-diff'
  395. }, patch => {
  396. if (patch) {
  397. if (patch.css) {
  398. const style = document.createElement('style');
  399. style.textContent = patch.css;
  400. document.head.appendChild(style);
  401. }
  402. if (patch.js) {
  403. try {
  404. if (window.evalCore && window.evalCore.getEvalInstance) {
  405. window.evalCore.getEvalInstance(window)(patch.js);
  406. }
  407. } catch (e) {
  408. console.error('json-diff补丁JS执行失败', e);
  409. }
  410. }
  411. }
  412. });
  413. },
  414. },
  415. mounted: function () {
  416. // 初始化JSON编辑器
  417. let jsonBox = JsonDiff.init(this.$refs.srcLeft, this.$refs.srcRight,
  418. this.errorHandler.bind(this),
  419. this.diffHandler.bind(this)
  420. );
  421. // 添加比较方法
  422. jsonBox.compare = this.compareJson.bind(this);
  423. // 初始化文本变更监听
  424. jsonBox.left.on('change', () => {
  425. setTimeout(() => this.compareJson(), 300);
  426. });
  427. jsonBox.right.on('change', () => {
  428. setTimeout(() => this.compareJson(), 300);
  429. });
  430. // 暴露到全局,供示例数据使用
  431. window.jsonBox = jsonBox;
  432. this.loadPatchHotfix();
  433. }
  434. });