index.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /**
  2. * FeHelper Popup Menu
  3. */
  4. import Awesome from '../background/awesome.js'
  5. import MSG_TYPE from '../static/js/common.js';
  6. function triggerScreenshot() {
  7. chrome.tabs.query({active: true, currentWindow: true}, tabs => {
  8. if (!tabs || !tabs.length || !tabs[0].id) return;
  9. const tabId = tabs[0].id;
  10. // 先尝试直接发送消息给content script
  11. chrome.tabs.sendMessage(tabId, {
  12. type: 'fh-screenshot-start'
  13. }).then(response => {
  14. console.log('截图工具触发成功');
  15. window.close();
  16. }).catch(error => {
  17. console.log('无法直接触发截图工具,尝试使用noPage模式', error);
  18. // 如果发送消息失败,使用noPage模式
  19. chrome.runtime.sendMessage({
  20. type: 'fh-dynamic-any-thing',
  21. thing: 'trigger-screenshot',
  22. tabId: tabId
  23. });
  24. window.close();
  25. });
  26. });
  27. }
  28. new Vue({
  29. el: '#pageContainer',
  30. data: {
  31. manifest: {},
  32. fhTools: {},
  33. isLoading: true
  34. },
  35. created: function () {
  36. // 获取当前ctx的version
  37. this.manifest = chrome.runtime.getManifest();
  38. // 立即开始加载工具列表,不阻塞页面渲染
  39. this.loadTools();
  40. },
  41. mounted: function () {
  42. // 页面DOM渲染完成后,执行非关键操作
  43. this.$nextTick(() => {
  44. // 延迟执行非关键操作,避免阻塞UI渲染
  45. setTimeout(() => {
  46. // 自动开关灯
  47. if (typeof DarkModeMgr !== 'undefined') {
  48. DarkModeMgr.turnLightAuto();
  49. }
  50. // 记录工具使用(非关键操作)
  51. this.recordUsage();
  52. // 页面加载后自动采集(非关键操作)
  53. if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) {
  54. Awesome.collectAndSendClientInfo();
  55. }
  56. }, 50); // 延迟50ms执行,让UI先渲染
  57. });
  58. // 整个popup窗口支持上下键选择
  59. this.setupKeyboardNavigation();
  60. // 查找截图按钮并绑定事件
  61. this.setupScreenshotButton();
  62. },
  63. methods: {
  64. async loadTools() {
  65. try {
  66. const tools = await Awesome.getInstalledTools();
  67. // 获取用户自定义的工具排序
  68. const customOrder = await chrome.storage.local.get('tool_custom_order');
  69. const savedOrder = customOrder.tool_custom_order ? JSON.parse(customOrder.tool_custom_order) : null;
  70. // 如果有自定义排序,重新排列工具
  71. if (savedOrder && Array.isArray(savedOrder)) {
  72. const orderedTools = {};
  73. const unorderedTools = { ...tools };
  74. // 按照保存的顺序添加工具
  75. savedOrder.forEach(toolKey => {
  76. if (unorderedTools[toolKey]) {
  77. orderedTools[toolKey] = unorderedTools[toolKey];
  78. delete unorderedTools[toolKey];
  79. }
  80. });
  81. // 添加新安装的工具(不在保存的顺序中的)
  82. Object.assign(orderedTools, unorderedTools);
  83. this.fhTools = orderedTools;
  84. } else {
  85. this.fhTools = tools;
  86. }
  87. this.isLoading = false;
  88. } catch (error) {
  89. console.error('加载工具列表失败:', error);
  90. this.isLoading = false;
  91. // 即使加载失败,也不应该让popup完全无法使用
  92. this.fhTools = {};
  93. }
  94. },
  95. recordUsage() {
  96. try {
  97. // 埋点:自动触发popup统计
  98. chrome.runtime.sendMessage({
  99. type: 'fh-dynamic-any-thing',
  100. thing: 'statistics-tool-usage',
  101. params: {
  102. tool_name: 'popup'
  103. }
  104. });
  105. } catch (error) {
  106. // 忽略统计错误,不影响主功能
  107. console.warn('统计记录失败:', error);
  108. }
  109. },
  110. setupKeyboardNavigation() {
  111. document.body.addEventListener('keydown', e => {
  112. let keyCode = e.keyCode || e.which;
  113. if (![38, 40, 13].includes(keyCode)) {
  114. return false;
  115. }
  116. let ul = document.querySelector('#pageContainer ul');
  117. if (!ul) return false;
  118. let hovered = ul.querySelector('li.x-hovered');
  119. let next, prev;
  120. if (hovered) {
  121. hovered.classList.remove('x-hovered');
  122. next = hovered.nextElementSibling;
  123. prev = hovered.previousElementSibling;
  124. }
  125. if (!next) {
  126. next = ul.querySelector('li:first-child');
  127. }
  128. if (!prev) {
  129. prev = ul.querySelector('li:last-child');
  130. }
  131. switch (keyCode) {
  132. case 38: // 方向键:↑
  133. if (prev) prev.classList.add('x-hovered');
  134. break;
  135. case 40: // 方向键:↓
  136. if (next) next.classList.add('x-hovered');
  137. break;
  138. case 13: // 回车键:选择
  139. if (hovered) hovered.click();
  140. }
  141. }, false);
  142. },
  143. setupScreenshotButton() {
  144. // 查找截图按钮并绑定事件
  145. const screenshotButtons = Array.from(document.querySelectorAll('a[data-tool="screenshot"], button[data-tool="screenshot"]'));
  146. screenshotButtons.forEach(button => {
  147. // 移除原有的点击事件
  148. button.onclick = function(e) {
  149. e.preventDefault();
  150. e.stopPropagation();
  151. triggerScreenshot();
  152. return false;
  153. };
  154. });
  155. },
  156. runHelper: async function (toolName) {
  157. if (!toolName || !this.fhTools[toolName]) return;
  158. let request = {
  159. type: MSG_TYPE.OPEN_DYNAMIC_TOOL,
  160. page: toolName,
  161. noPage: !!this.fhTools[toolName].noPage
  162. };
  163. if(this.fhTools[toolName]._devTool) {
  164. request.page = 'dynamic';
  165. request.query = `tool=${toolName}`;
  166. }
  167. chrome.runtime.sendMessage(request);
  168. !!this.fhTools[toolName].noPage && setTimeout(window.close, 200);
  169. },
  170. openOptionsPage: () => {
  171. chrome.runtime.openOptionsPage();
  172. },
  173. openUrl: function (event) {
  174. event.preventDefault();
  175. // 获取后台页面,返回window对象
  176. chrome.tabs.create({url: event.currentTarget.href});
  177. return false;
  178. }
  179. }
  180. });