index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /**
  2. * FeHelper QR Code Tools
  3. */
  4. new Vue({
  5. el: '#pageContainer',
  6. data: {
  7. textContent: '',
  8. qrSize: 200,
  9. qrColor: '#000000',
  10. useIcon: 'no',
  11. previewSrc: '',
  12. resultContent: '',
  13. qrEncodeMode: true,
  14. showResult: false
  15. },
  16. mounted: function () {
  17. let mode = new URL(location.href).searchParams.get('mode');
  18. this.qrEncodeMode = mode !== 'decode';
  19. // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
  20. if (location.protocol === 'chrome-extension:') {
  21. chrome.tabs.query({currentWindow: true,active: true, }, (tabs) => {
  22. let activeTab = tabs.filter(tab => tab.active)[0];
  23. chrome.runtime.sendMessage({
  24. type: 'fh-dynamic-any-thing',
  25. thing: 'request-page-content',
  26. tabId: activeTab.id
  27. }).then(resp => {
  28. if(!resp) return ;
  29. let text = resp.content || (resp.tab ? (resp.tab.fromTab ? resp.tab.fromTab.url : '') : '');
  30. if (text) {
  31. if (!this.qrEncodeMode) {
  32. // 解码模式
  33. this._qrDecode(text);
  34. } else {
  35. this.textContent = text;
  36. this.convert();
  37. }
  38. }
  39. });
  40. });
  41. }
  42. this.$refs.codeSource && this.$refs.codeSource.focus();
  43. // 拖拽注册
  44. document.addEventListener('drop', (evt) => {
  45. evt.preventDefault();
  46. evt.stopPropagation();
  47. let files = evt.dataTransfer.files;
  48. if (files.length) {
  49. if (this.qrEncodeMode) {
  50. this._drawIcon(files[0]);
  51. } else {
  52. this._decodeLocal(files[0]);
  53. }
  54. }
  55. }, false);
  56. document.addEventListener('dragover', (e) => {
  57. e.preventDefault();
  58. e.stopPropagation();
  59. }, false);
  60. //监听paste事件
  61. document.addEventListener('paste', (event) => {
  62. if (this.qrEncodeMode) return;
  63. this.paste(event);
  64. }, false);
  65. // color picker绑定
  66. $("#opt_fc").colorpicker({
  67. fillcolor: true,
  68. success: (obj, color) => {
  69. this.qrColor = color;
  70. this.convert();
  71. }
  72. })
  73. this.loadPatchHotfix();
  74. },
  75. methods: {
  76. loadPatchHotfix() {
  77. // 页面加载时自动获取并注入页面的补丁
  78. chrome.runtime.sendMessage({
  79. type: 'fh-dynamic-any-thing',
  80. thing: 'fh-get-tool-patch',
  81. toolName: 'qr-code'
  82. }, patch => {
  83. if (patch) {
  84. if (patch.css) {
  85. const style = document.createElement('style');
  86. style.textContent = patch.css;
  87. document.head.appendChild(style);
  88. }
  89. if (patch.js) {
  90. try {
  91. if (window.evalCore && window.evalCore.getEvalInstance) {
  92. window.evalCore.getEvalInstance(window)(patch.js);
  93. }
  94. } catch (e) {
  95. console.error('qr-code补丁JS执行失败', e);
  96. }
  97. }
  98. }
  99. });
  100. },
  101. convert: function () {
  102. this.showResult = true;
  103. this.$nextTick(() => {
  104. if (this.textContent) {
  105. $('#preview').html('').qrcode(this._createOptions());
  106. } else {
  107. $('#preview').html('再在输入框里输入一些内容,就能生成二维码了哦~')
  108. }
  109. });
  110. },
  111. fileChanged: function (event) {
  112. if (event.target.files.length) {
  113. if (this.qrEncodeMode) {
  114. this._drawIcon(event.target.files[0]);
  115. } else {
  116. this._decodeLocal(event.target.files[0]);
  117. }
  118. event.target.value = '';
  119. }
  120. },
  121. _drawIcon: function (file) {
  122. if (/image\//.test(file.type)) {
  123. this.useIcon = 'custom';
  124. let reader = new FileReader();
  125. reader.onload = (evt) => {
  126. this.$refs.logoCustom.src = evt.target.result;
  127. this.convert();
  128. };
  129. reader.readAsDataURL(file);
  130. } else {
  131. alert('请选择图片文件!');
  132. }
  133. },
  134. _createOptions: function () {
  135. let defaultOptions = {
  136. render: 'canvas',
  137. minVersion: 1,
  138. maxVersion: 40,
  139. ecLevel: 'L',
  140. left: 0,
  141. top: 0,
  142. size: +this.qrSize || 200,
  143. fill: this.qrColor,
  144. background: '#fff',
  145. radius: 0,
  146. quiet: 0,
  147. text: this.textContent,
  148. mode: 0,
  149. mSize: 0.15,
  150. mPosX: 0.5,
  151. mPosY: 0.5,
  152. label: 'FH',
  153. fontname: 'sans',
  154. fontcolor: '#f00',
  155. image: false
  156. };
  157. // 判断是否需要设置icon
  158. switch (this.useIcon) {
  159. case 'default':
  160. defaultOptions.mode = 4;
  161. defaultOptions.image = this.$refs.logoDefault;
  162. break;
  163. case 'custom':
  164. defaultOptions.mode = 4;
  165. defaultOptions.image = this.$refs.logoCustom;
  166. break;
  167. }
  168. return defaultOptions;
  169. },
  170. trans: function () {
  171. this.qrEncodeMode = !this.qrEncodeMode;
  172. },
  173. select: function () {
  174. this.$refs.resultBox.select();
  175. },
  176. _decodeLocal: function (file) {
  177. let reader = new FileReader();
  178. reader.onload = (e) => {
  179. this._qrDecode(e.target.result);
  180. };
  181. reader.readAsDataURL(file);
  182. },
  183. paste: function (event) {
  184. let items = event.clipboardData.items || {};
  185. // 优先处理图片
  186. for (let index in items) {
  187. let item = items[index];
  188. if (/image\//.test(item.type)) {
  189. let file = item.getAsFile();
  190. return this._decodeLocal(file);
  191. }
  192. }
  193. // 然后处理url
  194. try {
  195. // 逐个遍历
  196. (async () => {
  197. for (let index in items) {
  198. let item = items[index];
  199. if (/text\/plain/.test(item.type)) {
  200. let url = await new Promise(resolve => {
  201. item.getAsString(url => resolve(url))
  202. });
  203. let flag = await new Promise(resolve => {
  204. this._qrDecode(url, flag => resolve(flag));
  205. });
  206. if (flag) break;
  207. }
  208. }
  209. })();
  210. } catch (ex) {
  211. // 只能处理一个了
  212. for (let index in items) {
  213. let item = items[index];
  214. if (/text\/plain/.test(item.type)) {
  215. return item.getAsString(url => {
  216. this._qrDecode(url);
  217. });
  218. }
  219. }
  220. }
  221. },
  222. /**
  223. * 二维码转码
  224. * @param imgSrc
  225. * @param callback
  226. */
  227. _qrDecode: function (imgSrc, callback) {
  228. let self = this;
  229. const codeReader = new ZXing.BrowserMultiFormatReader();
  230. let image = new Image();
  231. image.src = imgSrc;
  232. image.setAttribute('crossOrigin', 'Anonymous');
  233. image.onload = function () {
  234. codeReader.decodeFromImage(this).then((result) => {
  235. self._showDecodeResult(imgSrc, result.text);
  236. callback && callback(result.text);
  237. }).catch((err) => {
  238. self._showDecodeResult(imgSrc, err);
  239. callback && callback(err);
  240. });
  241. };
  242. image.onerror = function (e) {
  243. callback && callback(false);
  244. alert('抱歉,当前图片无法被解码,请保存图片再拖拽进来试试!')
  245. };
  246. },
  247. _showDecodeResult: function (src, txt) {
  248. this.previewSrc = src;
  249. this.$refs.panelBox.style.backgroundImage = 'none';
  250. this.resultContent = txt;
  251. },
  252. copyQR: function() {
  253. const canvas = this.$el.querySelector('#preview canvas');
  254. const copyButton = this.$el.querySelector('#copy_button');
  255. const originalText = '复制';
  256. if (!canvas || !copyButton) {
  257. alert('请先生成二维码!');
  258. return;
  259. }
  260. canvas.toBlob(blob => {
  261. if (navigator.clipboard && navigator.clipboard.write) {
  262. navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })])
  263. .then(() => {
  264. copyButton.textContent = '√ 已复制';
  265. copyButton.classList.add('btn-action-success');
  266. setTimeout(() => {
  267. copyButton.textContent = originalText;
  268. copyButton.classList.remove('btn-action-success');
  269. }, 2000);
  270. }).catch(err => {
  271. console.error('无法复制二维码: ', err);
  272. alert('复制失败,请检查浏览器权限或手动截图。');
  273. });
  274. } else {
  275. alert('当前浏览器不支持自动复制图片,请手动截图。');
  276. }
  277. });
  278. },
  279. downloadQR: function() {
  280. const canvas = this.$el.querySelector('#preview canvas');
  281. if (canvas) {
  282. const link = document.createElement('a');
  283. link.download = 'qrcode.png';
  284. link.href = canvas.toDataURL('image/png');
  285. link.click();
  286. } else {
  287. alert('请先生成二维码!');
  288. }
  289. },
  290. openOptionsPage: function(event ){
  291. event.preventDefault();
  292. event.stopPropagation();
  293. if (chrome && chrome.runtime && chrome.runtime.openOptionsPage) {
  294. chrome.runtime.openOptionsPage();
  295. } else {
  296. console.error('无法打开选项页。');
  297. }
  298. },
  299. openDonateModal: function(event ){
  300. event.preventDefault();
  301. event.stopPropagation();
  302. chrome.runtime.sendMessage({
  303. type: 'fh-dynamic-any-thing',
  304. thing: 'open-donate-modal',
  305. params: { toolName: 'qr-code' }
  306. });
  307. }
  308. }
  309. });