index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. computed: {
  36. // 计算已安装的工具数量
  37. installedToolsCount() {
  38. return Object.values(this.fhTools).filter(tool => tool.installed).length;
  39. }
  40. },
  41. created: function () {
  42. // 获取当前ctx的version
  43. this.manifest = chrome.runtime.getManifest();
  44. // 立即开始加载工具列表,不阻塞页面渲染
  45. this.loadTools();
  46. // 页面加载时自动获取并注入popup页面的补丁
  47. this.loadPatchHotfix();
  48. },
  49. mounted: function () {
  50. // 页面DOM渲染完成后,执行非关键操作
  51. this.$nextTick(() => {
  52. // 延迟执行非关键操作,避免阻塞UI渲染
  53. setTimeout(() => {
  54. // 自动开关灯
  55. if (typeof DarkModeMgr !== 'undefined') {
  56. DarkModeMgr.turnLightAuto();
  57. }
  58. // 记录工具使用(非关键操作)
  59. this.recordUsage();
  60. // 页面加载后自动采集(非关键操作)
  61. if (window.chrome && chrome.runtime && chrome.runtime.sendMessage) {
  62. Awesome.collectAndSendClientInfo();
  63. }
  64. }, 50); // 延迟50ms执行,让UI先渲染
  65. });
  66. // 整个popup窗口支持上下键选择
  67. this.setupKeyboardNavigation();
  68. // 查找截图按钮并绑定事件
  69. this.setupScreenshotButton();
  70. },
  71. methods: {
  72. loadPatchHotfix() {
  73. // 页面加载时自动获取并注入options页面的补丁
  74. chrome.runtime.sendMessage({
  75. type: 'fh-dynamic-any-thing',
  76. thing: 'fh-get-tool-patch',
  77. toolName: 'popup'
  78. }, patch => {
  79. if (patch) {
  80. if (patch.css) {
  81. const style = document.createElement('style');
  82. style.textContent = patch.css;
  83. document.head.appendChild(style);
  84. }
  85. if (patch.js) {
  86. try {
  87. if (window.evalCore && window.evalCore.getEvalInstance) {
  88. window.evalCore.getEvalInstance(window)(patch.js);
  89. }
  90. } catch (e) {
  91. console.error('popup补丁JS执行失败', e);
  92. }
  93. }
  94. }
  95. });
  96. },
  97. getLayoutClasses() {
  98. const installedCount = this.installedToolsCount;
  99. const classes = [];
  100. if (installedCount <= 1) {
  101. classes.push('very-few-tools');
  102. } else if (installedCount <= 3) {
  103. classes.push('few-tools');
  104. }
  105. return classes;
  106. },
  107. async loadTools() {
  108. try {
  109. const tools = await Awesome.getInstalledTools();
  110. // 获取用户自定义的工具排序
  111. const customOrder = await chrome.storage.local.get('tool_custom_order');
  112. const savedOrder = customOrder.tool_custom_order ? JSON.parse(customOrder.tool_custom_order) : null;
  113. // 如果有自定义排序,重新排列工具
  114. if (savedOrder && Array.isArray(savedOrder)) {
  115. const orderedTools = {};
  116. const unorderedTools = { ...tools };
  117. // 按照保存的顺序添加工具
  118. savedOrder.forEach(toolKey => {
  119. if (unorderedTools[toolKey]) {
  120. orderedTools[toolKey] = unorderedTools[toolKey];
  121. delete unorderedTools[toolKey];
  122. }
  123. });
  124. // 添加新安装的工具(不在保存的顺序中的)
  125. Object.assign(orderedTools, unorderedTools);
  126. this.fhTools = orderedTools;
  127. } else {
  128. this.fhTools = tools;
  129. }
  130. this.isLoading = false;
  131. // 根据工具数量添加相应的CSS类来优化显示
  132. this.$nextTick(() => {
  133. this.updateLayoutClasses();
  134. });
  135. } catch (error) {
  136. console.error('加载工具列表失败:', error);
  137. this.isLoading = false;
  138. // 即使加载失败,也不应该让popup完全无法使用
  139. this.fhTools = {};
  140. // 加载失败时也需要更新布局类
  141. this.$nextTick(() => {
  142. this.updateLayoutClasses();
  143. });
  144. }
  145. },
  146. recordUsage() {
  147. try {
  148. // 埋点:自动触发popup统计
  149. chrome.runtime.sendMessage({
  150. type: 'fh-dynamic-any-thing',
  151. thing: 'statistics-tool-usage',
  152. params: {
  153. tool_name: 'popup'
  154. }
  155. });
  156. } catch (error) {
  157. // 忽略统计错误,不影响主功能
  158. console.warn('统计记录失败:', error);
  159. }
  160. },
  161. setupKeyboardNavigation() {
  162. document.body.addEventListener('keydown', e => {
  163. let keyCode = e.keyCode || e.which;
  164. if (![38, 40, 13].includes(keyCode)) {
  165. return false;
  166. }
  167. let ul = document.querySelector('#pageContainer ul');
  168. if (!ul) return false;
  169. let hovered = ul.querySelector('li.x-hovered');
  170. let next, prev;
  171. if (hovered) {
  172. hovered.classList.remove('x-hovered');
  173. next = hovered.nextElementSibling;
  174. prev = hovered.previousElementSibling;
  175. }
  176. if (!next) {
  177. next = ul.querySelector('li:first-child');
  178. }
  179. if (!prev) {
  180. prev = ul.querySelector('li:last-child');
  181. }
  182. switch (keyCode) {
  183. case 38: // 方向键:↑
  184. if (prev) prev.classList.add('x-hovered');
  185. break;
  186. case 40: // 方向键:↓
  187. if (next) next.classList.add('x-hovered');
  188. break;
  189. case 13: // 回车键:选择
  190. if (hovered) hovered.click();
  191. }
  192. }, false);
  193. },
  194. setupScreenshotButton() {
  195. // 查找截图按钮并绑定事件
  196. const screenshotButtons = Array.from(document.querySelectorAll('a[data-tool="screenshot"], button[data-tool="screenshot"]'));
  197. screenshotButtons.forEach(button => {
  198. // 移除原有的点击事件
  199. button.onclick = function(e) {
  200. e.preventDefault();
  201. e.stopPropagation();
  202. triggerScreenshot();
  203. return false;
  204. };
  205. });
  206. },
  207. runHelper: async function (toolName) {
  208. if (!toolName || !this.fhTools[toolName]) return;
  209. let request = {
  210. type: MSG_TYPE.OPEN_DYNAMIC_TOOL,
  211. page: toolName,
  212. noPage: !!this.fhTools[toolName].noPage
  213. };
  214. if(this.fhTools[toolName]._devTool) {
  215. request.page = 'dynamic';
  216. request.query = `tool=${toolName}`;
  217. }
  218. chrome.runtime.sendMessage(request);
  219. !!this.fhTools[toolName].noPage && setTimeout(window.close, 200);
  220. },
  221. openOptionsPage: () => {
  222. chrome.runtime.openOptionsPage();
  223. },
  224. openUrl: function (event) {
  225. event.preventDefault();
  226. // 获取后台页面,返回window对象
  227. chrome.tabs.create({url: event.currentTarget.href});
  228. return false;
  229. },
  230. updateLayoutClasses() {
  231. const container = document.getElementById('pageContainer');
  232. if (!container) return;
  233. const installedCount = this.installedToolsCount;
  234. // 移除所有布局相关的类
  235. container.classList.remove('few-tools', 'very-few-tools');
  236. // 根据工具数量添加相应的类
  237. if (installedCount <= 1) {
  238. container.classList.add('very-few-tools');
  239. console.log('Popup布局:应用very-few-tools类 (工具数量:', installedCount, ')');
  240. } else if (installedCount <= 3) {
  241. container.classList.add('few-tools');
  242. console.log('Popup布局:应用few-tools类 (工具数量:', installedCount, ')');
  243. } else {
  244. console.log('Popup布局:使用默认布局 (工具数量:', installedCount, ')');
  245. }
  246. }
  247. }
  248. });