Ver Fonte

feat: gm test

zhangjie há 1 ano atrás
pai
commit
77e9e5324e

+ 11 - 2
electron/main/index.ts

@@ -1,5 +1,6 @@
-import { app, shell, BrowserWindow } from 'electron';
-import { join } from 'path';
+import { app, shell, BrowserWindow, protocol, net } from 'electron';
+import { join, resolve } from 'path';
+import url from 'url';
 import { electronApp, optimizer, is } from '@electron-toolkit/utils';
 import icon from '../../resources/icon.png?asset';
 import useElectron from './useElectron';
@@ -56,6 +57,14 @@ app.whenReady().then(() => {
 
   createWindow();
 
+  // protocol
+  protocol.handle('local', (request) => {
+    const filePath = request.url.slice('local://'.length);
+    return net.fetch(
+      url.pathToFileURL(resolve(__dirname, filePath)).toString()
+    );
+  });
+
   app.on('activate', () => {
     // On macOS it's common to re-create a window in the app when the
     // dock icon is clicked and there are no other windows open.

+ 28 - 0
electron/preload/api.ts

@@ -0,0 +1,28 @@
+import gm from 'gm';
+import path from 'node:path';
+
+import { getTempPath } from './utils';
+
+const gmInst = gm.subClass({ imageMagick: '7+' });
+
+function cropImage(imgPath: string): Promise<string> {
+  return new Promise((resolve, reject) => {
+    const outpath = path.join(getTempPath(), '001.png');
+    gmInst(imgPath)
+      .crop(500, 200, 0, 0)
+      .write(outpath, (err) => {
+        if (!err) {
+          return resolve(outpath);
+        }
+        return reject(err);
+      });
+  });
+}
+
+const commonApi = {
+  cropImage,
+};
+
+export type CommonApi = typeof commonApi;
+
+export default commonApi;

+ 2 - 0
electron/preload/index.d.ts

@@ -1,9 +1,11 @@
 import type { ElectronApi } from './apiElectron';
 import type { NodeApi } from './apiNode';
+import type { CommonApi } from './api';
 
 declare global {
   interface Window {
     electron: ElectronApi;
     node: NodeApi;
+    api: CommonApi;
   }
 }

+ 4 - 0
electron/preload/index.ts

@@ -1,6 +1,7 @@
 import { contextBridge } from 'electron';
 import electronApi from './apiElectron';
 import nodeApi from './apiNode';
+import api from './api';
 
 // Use `contextBridge` APIs to expose Electron APIs to
 // renderer only if context isolation is enabled, otherwise
@@ -9,6 +10,7 @@ if (process.contextIsolated) {
   try {
     contextBridge.exposeInMainWorld('electron', electronApi);
     contextBridge.exposeInMainWorld('node', nodeApi);
+    contextBridge.exposeInMainWorld('api', api);
   } catch (error) {
     console.error(error);
   }
@@ -17,4 +19,6 @@ if (process.contextIsolated) {
   window.electron = electronApi;
   // @ts-ignore (define in dts)
   window.node = nodeApi;
+  // @ts-ignore (define in dts)
+  window.api = api;
 }

+ 28 - 0
electron/preload/utils.ts

@@ -0,0 +1,28 @@
+import fs from 'node:fs';
+import path from 'node:path';
+
+const TEMP_PATH_NAME = 'temp';
+
+export function getRootPath() {
+  return __dirname;
+}
+
+export function getTempPath() {
+  return path.join(getRootPath(), TEMP_PATH_NAME);
+}
+
+export function makeDirSync(pathContent: string) {
+  const mkPathList: string[] = [];
+  let curPath = pathContent;
+
+  while (!fs.existsSync(curPath)) {
+    mkPathList.unshift(curPath);
+    curPath = path.dirname(curPath);
+  }
+
+  mkPathList.forEach((mpath) => {
+    fs.mkdirSync(mpath);
+  });
+}
+
+makeDirSync(getTempPath());

+ 1 - 1
index.html

@@ -5,7 +5,7 @@
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <meta http-equiv="Content-Security-Policy"
-    content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" />
+    content="script-src 'self'; style-src 'self' 'unsafe-inline';" />
   <title>
     mark-tool
   </title>

+ 1 - 0
package.json

@@ -66,6 +66,7 @@
     "@electron-toolkit/tsconfig": "^1.0.1",
     "@rushstack/eslint-patch": "^1.7.1",
     "@types/crypto-js": "^4.2.1",
+    "@types/gm": "^1.25.4",
     "@types/lodash": "^4.14.186",
     "@types/mockjs": "^1.0.7",
     "@types/node": "^18.19.9",

+ 8 - 0
pnpm-lock.yaml

@@ -17,6 +17,7 @@ specifiers:
   '@electron-toolkit/utils': ^3.0.0
   '@rushstack/eslint-patch': ^1.7.1
   '@types/crypto-js': ^4.2.1
+  '@types/gm': ^1.25.4
   '@types/lodash': ^4.14.186
   '@types/mockjs': ^1.0.7
   '@types/node': ^18.19.9
@@ -103,6 +104,7 @@ devDependencies:
   '@electron-toolkit/tsconfig': 1.0.1_@types+node@18.19.33
   '@rushstack/eslint-patch': 1.10.2
   '@types/crypto-js': 4.2.2
+  '@types/gm': 1.25.4
   '@types/lodash': 4.17.1
   '@types/mockjs': 1.0.10
   '@types/node': 18.19.33
@@ -1486,6 +1488,12 @@ packages:
       '@types/node': 20.12.12
     dev: true
 
+  /@types/gm/1.25.4:
+    resolution: {integrity: sha512-123Spjn7f0eZZOiFlXCiloBRga+uczwAdZOlcgtva+I1h137ADcbC7I5B3SZToQBzYLMokYDlyPxRUozKSQ+Ow==}
+    dependencies:
+      '@types/node': 20.12.12
+    dev: true
+
   /@types/http-cache-semantics/4.0.4:
     resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
 

+ 19 - 1
src/views/login/test-page/index.vue

@@ -2,14 +2,23 @@
   <div>
     <h1>TestPage1</h1>
     <a-button type="primary" @click="toOpenFile">打开文件</a-button>
+    <br />
+    <img v-if="originImg" :src="originImg" alt="原图" />
+    <br />
+    <img v-if="cropImg" :src="cropImg" alt="裁切图" />
   </div>
 </template>
 
 <script setup lang="ts">
+  import { ref } from 'vue';
+
   defineOptions({
     name: 'TestPage',
   });
 
+  const originImg = ref('');
+  const cropImg = ref('');
+
   async function toOpenFile() {
     const res = await window.electron.dialogSelectFile({
       title: '选择文件',
@@ -17,7 +26,16 @@
     });
     console.log(res);
 
-    const info = window.node.readFileSync(res.filePaths[0]);
+    if (res.canceled) return;
+
+    originImg.value = `local://${res.filePaths[0]}`;
+
+    const info = await window.api.cropImage(originImg.value).catch((err) => {
+      console.log(err);
+    });
     console.log(info);
+
+    if (!info) return;
+    cropImg.value = `local://${info}`;
   }
 </script>