index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /**
  2. * FeHelper 代码美化工具
  3. */
  4. new Vue({
  5. el: '#pageContainer',
  6. data: {
  7. selectedType: 'Javascript',
  8. sourceContent: '',
  9. resultContent: '',
  10. showCopyBtn: false,
  11. examples: {
  12. js: `function foo(){var x=10;if(x>5){return x*2;}else{return x/2;}}`,
  13. css: `.header{position:fixed;top:0;left:0;width:100%;background:#fff;z-index:100;}.header .logo{float:left;margin:10px;}.header .nav{float:right;}`,
  14. html: `<div class="container"><div class="header"><h1>标题</h1><nav><ul><li><a href="#">首页</a></li><li><a href="#">关于</a></li></ul></nav></div><div class="content"><p>内容区域</p></div></div>`,
  15. xml: `<?xml version="1.0" encoding="UTF-8"?><root><person><name>张三</name><age>25</age><city>北京</city></person><person><name>李四</name><age>30</age><city>上海</city></person></root>`,
  16. sql: `SELECT u.name,o.order_id,p.product_name FROM users u LEFT JOIN orders o ON u.id=o.user_id LEFT JOIN products p ON o.product_id=p.id WHERE u.status='active' AND o.create_time>='2024-01-01' ORDER BY o.create_time DESC;`
  17. }
  18. },
  19. mounted: function () {
  20. // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
  21. if (location.protocol === 'chrome-extension:') {
  22. chrome.tabs.query({currentWindow: true,active: true, }, (tabs) => {
  23. let activeTab = tabs.filter(tab => tab.active)[0];
  24. chrome.runtime.sendMessage({
  25. type: 'fh-dynamic-any-thing',
  26. thing: 'request-page-content',
  27. tabId: activeTab.id
  28. }).then(resp => {
  29. if(!resp || !resp.content) return ;
  30. this.sourceContent = resp.content;
  31. this.format();
  32. });
  33. });
  34. }
  35. //输入框聚焦
  36. this.$refs.codeSource.focus();
  37. this.loadPatchHotfix();
  38. },
  39. methods: {
  40. loadPatchHotfix() {
  41. // 页面加载时自动获取并注入页面的补丁
  42. chrome.runtime.sendMessage({
  43. type: 'fh-dynamic-any-thing',
  44. thing: 'fh-get-tool-patch',
  45. toolName: 'code-beautify'
  46. }, patch => {
  47. if (patch) {
  48. if (patch.css) {
  49. const style = document.createElement('style');
  50. style.textContent = patch.css;
  51. document.head.appendChild(style);
  52. }
  53. if (patch.js) {
  54. try {
  55. if (window.evalCore && window.evalCore.getEvalInstance) {
  56. window.evalCore.getEvalInstance(window)(patch.js);
  57. }
  58. } catch (e) {
  59. console.error('code-beautify补丁JS执行失败', e);
  60. }
  61. }
  62. }
  63. });
  64. },
  65. format: function () {
  66. if (!this.sourceContent.trim()) {
  67. return this.toast('内容为空,不需要美化处理!', 'warning');
  68. }else{
  69. this.toast('格式化进行中...', 'info');
  70. }
  71. let beauty = (result) => {
  72. result = result.replace(/>/g, '&gt;').replace(/</g, '&lt;');
  73. result = '<pre class="language-' + this.selectedType.toLowerCase() + ' line-numbers"><code>' + result + '</code></pre>';
  74. this.resultContent = result;
  75. // 代码高亮
  76. this.$nextTick(() => {
  77. Prism.highlightAll();
  78. this.showCopyBtn = true;
  79. this.toast('格式化完成!', 'success');
  80. });
  81. };
  82. switch (this.selectedType) {
  83. case 'Javascript':
  84. try {
  85. let opts = {
  86. brace_style: "collapse",
  87. break_chained_methods: false,
  88. indent_char: " ",
  89. indent_scripts: "keep",
  90. indent_size: "4",
  91. keep_array_indentation: true,
  92. preserve_newlines: true,
  93. space_after_anon_function: true,
  94. space_before_conditional: true,
  95. unescape_strings: false,
  96. wrap_line_length: "120",
  97. "max_preserve_newlines": "5",
  98. "jslint_happy": false,
  99. "end_with_newline": false,
  100. "indent_inner_html": false,
  101. "comma_first": false,
  102. "e4x": false
  103. };
  104. beauty(js_beautify(this.sourceContent, opts));
  105. } catch (error) {
  106. this.toast('JavaScript格式化失败,请检查代码语法!', 'error');
  107. }
  108. break;
  109. case 'CSS':
  110. try {
  111. css_beautify(this.sourceContent, {}, result => beauty(result));
  112. } catch (error) {
  113. this.toast('CSS格式化失败,请检查代码语法!', 'error');
  114. }
  115. break;
  116. case 'HTML':
  117. try {
  118. beauty(html_beautify(this.sourceContent,{indent_size:4}));
  119. } catch (error) {
  120. this.toast('HTML格式化失败,请检查代码语法!', 'error');
  121. }
  122. break;
  123. case 'SQL':
  124. try {
  125. beauty(vkbeautify.sql(this.sourceContent, 4));
  126. } catch (error) {
  127. this.toast('SQL格式化失败,请检查代码语法!', 'error');
  128. }
  129. break;
  130. default:
  131. try {
  132. beauty(vkbeautify.xml(this.sourceContent));
  133. } catch (error) {
  134. this.toast('XML格式化失败,请检查代码语法!', 'error');
  135. }
  136. }
  137. },
  138. copy: function () {
  139. let _copyToClipboard = (text) => {
  140. let input = document.createElement('textarea');
  141. input.style.position = 'fixed';
  142. input.style.opacity = 0;
  143. input.value = text;
  144. document.body.appendChild(input);
  145. input.select();
  146. document.execCommand('Copy');
  147. document.body.removeChild(input);
  148. this.toast('复制成功,随处粘贴可用!', 'success')
  149. };
  150. let txt = this.$refs.jfContentBox.textContent;
  151. _copyToClipboard(txt);
  152. },
  153. /**
  154. * 自动消失的通知弹窗,仿Notification效果
  155. * @param content 通知内容
  156. * @param type 通知类型:success、error、warning、info(默认)
  157. */
  158. toast (content, type = 'info') {
  159. window.clearTimeout(window.feHelperAlertMsgTid);
  160. let elAlertMsg = document.querySelector("#fehelper_alertmsg");
  161. // 根据类型配置样式
  162. const typeConfig = {
  163. info: {
  164. background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
  165. borderColor: '#4ade80',
  166. icon: 'ℹ'
  167. },
  168. success: {
  169. background: 'linear-gradient(135deg, #4ade80 0%, #16a34a 100%)',
  170. borderColor: '#22c55e',
  171. icon: '✓'
  172. },
  173. error: {
  174. background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
  175. borderColor: '#f87171',
  176. icon: '✕'
  177. },
  178. warning: {
  179. background: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
  180. borderColor: '#fbbf24',
  181. icon: '⚠'
  182. }
  183. };
  184. const config = typeConfig[type] || typeConfig.info;
  185. if (!elAlertMsg) {
  186. let elWrapper = document.createElement('div');
  187. elWrapper.innerHTML = `
  188. <div id="fehelper_alertmsg" style="
  189. position: fixed;
  190. top: 20px;
  191. right: 20px;
  192. z-index: 10000000;
  193. min-width: 300px;
  194. max-width: 400px;
  195. opacity: 0;
  196. transform: translateX(100%);
  197. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  198. ">
  199. <div class="toast-inner" style="
  200. background: ${config.background};
  201. color: #fff;
  202. padding: 16px 20px;
  203. border-radius: 8px;
  204. box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2), 0 4px 10px rgba(0, 0, 0, 0.1);
  205. font-size: 14px;
  206. font-weight: 500;
  207. line-height: 1.4;
  208. position: relative;
  209. overflow: hidden;
  210. ">
  211. <div class="toast-border" style="
  212. position: absolute;
  213. top: 0;
  214. left: 0;
  215. width: 4px;
  216. height: 100%;
  217. background: ${config.borderColor};
  218. "></div>
  219. <div style="
  220. display: flex;
  221. align-items: center;
  222. gap: 12px;
  223. ">
  224. <div class="toast-icon" style="
  225. flex-shrink: 0;
  226. width: 20px;
  227. height: 20px;
  228. background: rgba(255, 255, 255, 0.2);
  229. border-radius: 50%;
  230. display: flex;
  231. align-items: center;
  232. justify-content: center;
  233. font-size: 12px;
  234. ">${config.icon}</div>
  235. <div class="toast-content" style="flex: 1;">${content}</div>
  236. </div>
  237. </div>
  238. </div>
  239. `;
  240. elAlertMsg = elWrapper.childNodes[1]; // 第一个是文本节点,第二个才是div
  241. document.body.appendChild(elAlertMsg);
  242. // 触发动画
  243. setTimeout(() => {
  244. elAlertMsg.style.opacity = '1';
  245. elAlertMsg.style.transform = 'translateX(0)';
  246. }, 10);
  247. } else {
  248. // 更新现有通知的内容和样式
  249. const toastInner = elAlertMsg.querySelector('.toast-inner');
  250. const toastBorder = elAlertMsg.querySelector('.toast-border');
  251. const toastIcon = elAlertMsg.querySelector('.toast-icon');
  252. const toastContent = elAlertMsg.querySelector('.toast-content');
  253. toastInner.style.background = config.background;
  254. toastBorder.style.background = config.borderColor;
  255. toastIcon.innerHTML = config.icon;
  256. toastContent.innerHTML = content;
  257. elAlertMsg.style.display = 'block';
  258. elAlertMsg.style.opacity = '1';
  259. elAlertMsg.style.transform = 'translateX(0)';
  260. }
  261. window.feHelperAlertMsgTid = window.setTimeout(function () {
  262. // 淡出动画
  263. elAlertMsg.style.opacity = '0';
  264. elAlertMsg.style.transform = 'translateX(100%)';
  265. // 动画完成后隐藏
  266. setTimeout(() => {
  267. elAlertMsg.style.display = 'none';
  268. }, 300);
  269. }, 3000);
  270. },
  271. loadExample(type,event) {
  272. if(event){
  273. event.preventDefault();
  274. }
  275. const typeMap = {
  276. 'js': 'Javascript',
  277. 'css': 'CSS',
  278. 'html': 'HTML',
  279. 'xml': 'XML',
  280. 'sql': 'SQL'
  281. };
  282. this.sourceContent = this.examples[type];
  283. this.selectedType = typeMap[type];
  284. this.toast(`已加载${typeMap[type]}示例代码`, 'info');
  285. this.$nextTick(() => {
  286. this.format();
  287. });
  288. },
  289. openOptionsPage: function(event) {
  290. event.preventDefault();
  291. event.stopPropagation();
  292. chrome.runtime.openOptionsPage();
  293. },
  294. openDonateModal: function(event ){
  295. event.preventDefault();
  296. event.stopPropagation();
  297. chrome.runtime.sendMessage({
  298. type: 'fh-dynamic-any-thing',
  299. thing: 'open-donate-modal',
  300. params: { toolName: 'code-beautify' }
  301. });
  302. }
  303. }
  304. });