瀏覽代碼

🎨 优化程序单独打包,返回以及其他优化

快乐的梦鱼 1 月之前
父節點
當前提交
43391097c2

+ 2 - 0
.gitignore

@@ -14,6 +14,8 @@ dist-ssr
 coverage
 *.local
 dist/
+dist-electron/
+release/
 
 /cypress/videos/
 /cypress/screenshots/

+ 1 - 0
.npmrc

@@ -0,0 +1 @@
+ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/

+ 17 - 0
electron-builder.yml

@@ -0,0 +1,17 @@
+appId: com.wenlv.huli-showroom
+productName: 湖里透明展示屏
+asar: true
+directories:
+  output: release
+files:
+  - dist/**
+  - electron/**
+win:
+  target: nsis
+  icon: electron/icons/icon.ico
+mac: false
+linux: false
+nsis:
+  oneClick: 
+electronDownload:
+  mirror: https://npmmirror.com/mirrors/electron/

二進制
electron/icons/icon.ico


+ 35 - 0
electron/main.js

@@ -0,0 +1,35 @@
+import { app, BrowserWindow, Menu, ipcMain } from 'electron';
+import { fileURLToPath } from 'node:url';
+import path, { dirname } from 'node:path';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+function createWindow() {
+  Menu.setApplicationMenu(null);
+
+  const win = new BrowserWindow({
+    width: 1200,
+    height: 800,
+    fullscreen: true,
+    webPreferences: {
+      preload: path.resolve(__dirname, './preload.js')
+    },
+  });
+  win.loadURL(process.env.VITE_DEV_SERVER_URL || path.resolve(__dirname, '../dist/index.html'));
+
+  if (process.env.NODE_ENV === 'development')
+    win.webContents.openDevTools();
+
+  ipcMain.on('close-window', () => {
+    win.close();
+  });
+  ipcMain.on('toggle-fullscreen', () => {
+    win.setFullScreen(!win.isFullScreen());
+  });
+  ipcMain.on('toggle-devtools', () => {
+    win.webContents.toggleDevTools();
+  });
+}
+
+app.whenReady().then(createWindow);

+ 7 - 0
electron/package.json

@@ -0,0 +1,7 @@
+{
+  "name": "test",
+  "version": "1.0.0",
+  "description": "",
+  "type": "module",
+  "main": "main.js"
+}

+ 7 - 0
electron/preload.js

@@ -0,0 +1,7 @@
+const { contextBridge, ipcRenderer } = require('electron');
+
+contextBridge.exposeInMainWorld('electronAPI', {
+  closeWindow: () => ipcRenderer.send('close-window'),
+  toggleFullscreen: () => ipcRenderer.send('toggle-fullscreen'),
+  toggleDevTools: () => ipcRenderer.send('toggle-devtools')
+});

+ 8 - 0
env.d.ts

@@ -1 +1,9 @@
 /// <reference types="vite/client" />
+
+interface Window {
+  electronAPI: {
+    closeWindow: () => void;
+    toggleFullscreen: () => void;
+    toggleDevTools: () => void;
+  }
+}

+ 0 - 216
pack/manifest.json

@@ -1,216 +0,0 @@
-{
-    "@platforms" : [ "android", "iPhone", "iPad" ],
-    "id" : "H5A0733B1", /*应用的标识*/
-    "name" : "湖里文旅展示大屏", /*应用名称,程序桌面图标名称*/
-    "version" : {
-        "name" : "1.0", /*应用版本名称*/
-        "code" : "100"
-    },
-    "description" : "", /*应用描述信息*/
-    "icons" : {
-        "72" : "icon.png"
-    },
-    "launch_path" : "index.html", /*应用的入口页面,默认为根目录下的index.html;支持网络地址,必须以http://或https://开头*/
-    "developer" : {
-        "name" : "", /*开发者名称*/
-        "email" : "", /*开发者邮箱地址*/
-        "url" : "" /*开发者个人主页地址*/
-    },
-    "permissions" : {
-        "Accelerometer" : {
-            "description" : "访问加速度感应器"
-        },
-        "Audio" : {
-            "description" : "访问麦克风"
-        },
-        "Cache" : {
-            "description" : "管理应用缓存"
-        },
-        "Console" : {
-            "description" : "跟踪调试输出日志"
-        },
-        "Device" : {
-            "description" : "访问设备信息"
-        },
-        "Downloader" : {
-            "description" : "文件下载管理"
-        },
-        "Events" : {
-            "description" : "应用扩展事件"
-        },
-        "File" : {
-            "description" : "访问本地文件系统"
-        },
-        "Gallery" : {
-            "description" : "访问系统相册"
-        },
-        "Invocation" : {
-            "description" : "使用Native.js能力"
-        },
-        "Orientation" : {
-            "description" : "访问方向感应器"
-        },
-        "Proximity" : {
-            "description" : "访问距离感应器"
-        },
-        "Storage" : {
-            "description" : "管理应用本地数据"
-        },
-        "Uploader" : {
-            "description" : "管理文件上传任务"
-        },
-        "Runtime" : {
-            "description" : "访问运行期环境"
-        },
-        "XMLHttpRequest" : {
-            "description" : "跨域网络访问"
-        },
-        "Zip" : {
-            "description" : "文件压缩与解压缩"
-        },
-        "Webview" : {
-            "description" : "窗口管理"
-        },
-        "NativeUI" : {
-            "description" : "原生UI控件"
-        },
-        "Navigator" : {
-            "description" : "浏览器信息"
-        },
-        "NativeObj" : {
-            "description" : "原生对象"
-        },
-        "VideoPlayer" : {}
-    },
-    "plus" : {
-        "splashscreen" : {
-            "autoclose" : true, /*是否自动关闭程序启动界面,true表示应用加载应用入口页面后自动关闭;false则需调plus.navigator.closeSplashscreen()关闭*/
-            "waiting" : true /*是否在程序启动界面显示等待雪花,true表示显示,false表示不显示。*/
-        },
-        "popGesture" : "close", /*设置应用默认侧滑返回关闭Webview窗口,"none"为无侧滑返回功能,"hide"为侧滑隐藏Webview窗口。参考http://ask.dcloud.net.cn/article/102*/
-        "runmode" : "normal", /*应用的首次启动运行模式,可取liberate或normal,liberate模式在第一次启动时将解压应用资源(Android平台File API才可正常访问_www目录)*/
-        "signature" : "Sk9JTiBVUyBtYWlsdG86aHIyMDEzQGRjbG91ZC5pbw==", /*可选,保留给应用签名,暂不使用*/
-        "distribute" : {
-            "apple" : {
-                "appid" : "", /*iOS应用标识,苹果开发网站申请的appid,如io.dcloud.HelloH5*/
-                "mobileprovision" : "", /*iOS应用打包配置文件*/
-                "password" : "", /*iOS应用打包个人证书导入密码*/
-                "p12" : "", /*iOS应用打包个人证书,打包配置文件关联的个人证书*/
-                "devices" : "universal", /*iOS应用支持的设备类型,可取值iphone/ipad/universal*/
-                "frameworks" : [] /*调用Native.js调用原生Objective-c API需要引用的FrameWork,如需调用GameCenter,则添加"GameKit.framework"*/
-            },
-            "google" : {
-                "packagename" : "", /*Android应用包名,如io.dcloud.HelloH5*/
-                "keystore" : "", /*Android应用打包使用的密钥库文件*/
-                "password" : "", /*Android应用打包使用密钥库中证书的密码*/
-                "aliasname" : "", /*Android应用打包使用密钥库中证书的别名*/
-                "permissions" : [
-                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
-                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
-                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
-                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
-                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>"
-                ],
-                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
-                "targetSdkVersion" : 25,
-                "minSdkVersion" : 23
-            },
-            /*使用Native.js调用原生安卓API需要使用到的系统权限*/
-            "orientation" : [ "portrait-primary" ], /*应用支持的方向,portrait-primary:竖屏正方向;portrait-secondary:竖屏反方向;landscape-primary:横屏正方向;landscape-secondary:横屏反方向*/
-            "icons" : {
-                "ios" : {
-                    "prerendered" : true, /*应用图标是否已经高亮处理,在iOS6及以下设备上有效*/
-                    "auto" : "", /*应用图标,分辨率:512x512,用于自动生成各种尺寸程序图标*/
-                    "iphone" : {
-                        "normal" : "", /*iPhone3/3GS程序图标,分辨率:57x57*/
-                        "retina" : "", /*iPhone4程序图标,分辨率:114x114*/
-                        "retina7" : "", /*iPhone4S/5/6程序图标,分辨率:120x120*/
-                        "retina8" : "", /*iPhone6 Plus程序图标,分辨率:180x180*/
-                        "spotlight-normal" : "", /*iPhone3/3GS Spotlight搜索程序图标,分辨率:29x29*/
-                        "spotlight-retina" : "", /*iPhone4 Spotlight搜索程序图标,分辨率:58x58*/
-                        "spotlight-retina7" : "", /*iPhone4S/5/6 Spotlight搜索程序图标,分辨率:80x80*/
-                        "settings-normal" : "", /*iPhone4设置页面程序图标,分辨率:29x29*/
-                        "settings-retina" : "", /*iPhone4S/5/6设置页面程序图标,分辨率:58x58*/
-                        "settings-retina8" : "", /*iPhone6Plus设置页面程序图标,分辨率:87x87*/
-                        "app@2x" : "unpackage/res/icons/120x120.png",
-                        "app@3x" : "unpackage/res/icons/180x180.png",
-                        "notification@2x" : "unpackage/res/icons/40x40.png",
-                        "notification@3x" : "unpackage/res/icons/60x60.png",
-                        "settings@2x" : "unpackage/res/icons/58x58.png",
-                        "settings@3x" : "unpackage/res/icons/87x87.png",
-                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
-                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
-                    },
-                    "ipad" : {
-                        "normal" : "", /*iPad普通屏幕程序图标,分辨率:72x72*/
-                        "retina" : "", /*iPad高分屏程序图标,分辨率:144x144*/
-                        "normal7" : "", /*iPad iOS7程序图标,分辨率:76x76*/
-                        "retina7" : "", /*iPad iOS7高分屏程序图标,分辨率:152x152*/
-                        "spotlight-normal" : "", /*iPad Spotlight搜索程序图标,分辨率:50x50*/
-                        "spotlight-retina" : "", /*iPad高分屏Spotlight搜索程序图标,分辨率:100x100*/
-                        "spotlight-normal7" : "", /*iPad iOS7 Spotlight搜索程序图标,分辨率:40x40*/
-                        "spotlight-retina7" : "", /*iPad iOS7高分屏Spotlight搜索程序图标,分辨率:80x80*/
-                        "settings-normal" : "", /*iPad设置页面程序图标,分辨率:29x29*/
-                        "settings-retina" : "", /*iPad高分屏设置页面程序图标,分辨率:58x58*/
-                        "app" : "unpackage/res/icons/76x76.png",
-                        "app@2x" : "unpackage/res/icons/152x152.png",
-                        "notification" : "unpackage/res/icons/20x20.png",
-                        "notification@2x" : "unpackage/res/icons/40x40.png",
-                        "proapp@2x" : "unpackage/res/icons/167x167.png",
-                        "settings" : "unpackage/res/icons/29x29.png",
-                        "settings@2x" : "unpackage/res/icons/58x58.png",
-                        "spotlight" : "unpackage/res/icons/40x40.png",
-                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
-                    },
-                    "appstore" : "unpackage/res/icons/1024x1024.png"
-                },
-                "android" : {
-                    "mdpi" : "", /*普通屏程序图标,分辨率:48x48*/
-                    "ldpi" : "", /*大屏程序图标,分辨率:48x48*/
-                    "hdpi" : "unpackage/res/icons/72x72.png", /*高分屏程序图标,分辨率:72x72*/
-                    "xhdpi" : "unpackage/res/icons/96x96.png", /*720P高分屏程序图标,分辨率:96x96*/
-                    "xxhdpi" : "unpackage/res/icons/144x144.png", /*1080P 高分屏程序图标,分辨率:144x144*/
-                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
-                }
-            },
-            "splashscreen" : {
-                "ios" : {
-                    "iphone" : {
-                        "default" : "", /*iPhone3启动图片选,分辨率:320x480*/
-                        "retina35" : "", /*3.5英寸设备(iPhone4)启动图片,分辨率:640x960*/
-                        "retina40" : "", /*4.0 英寸设备(iPhone5/iPhone5s)启动图片,分辨率:640x1136*/
-                        "retina47" : "", /*4.7 英寸设备(iPhone6)启动图片,分辨率:750x1334*/
-                        "retina55" : "", /*5.5 英寸设备(iPhone6 Plus)启动图片,分辨率:1242x2208*/
-                        "retina55l" : "" /*5.5 英寸设备(iPhone6 Plus)横屏启动图片,分辨率:2208x1242*/
-                    },
-                    "ipad" : {
-                        "portrait" : "", /*iPad竖屏启动图片,分辨率:768x1004*/
-                        "portrait-retina" : "", /*iPad高分屏竖屏图片,分辨率:1536x2008*/
-                        "landscape" : "", /*iPad横屏启动图片,分辨率:1024x748*/
-                        "landscape-retina" : "", /*iPad高分屏横屏启动图片,分辨率:2048x1496*/
-                        "portrait7" : "", /*iPad iOS7竖屏启动图片,分辨率:768x1024*/
-                        "portrait-retina7" : "", /*iPad iOS7高分屏竖屏图片,分辨率:1536x2048*/
-                        "landscape7" : "", /*iPad iOS7横屏启动图片,分辨率:1024x768*/
-                        "landscape-retina7" : "" /*iPad iOS7高分屏横屏启动图片,分辨率:2048x1536*/
-                    }
-                },
-                "android" : {
-                    "mdpi" : "", /*普通屏启动图片,分辨率:240x282*/
-                    "ldpi" : "", /*大屏启动图片,分辨率:320x442*/
-                    "hdpi" : "", /*高分屏启动图片,分辨率:480x762*/
-                    "xhdpi" : "", /*720P高分屏启动图片,分辨率:720x1242*/
-                    "xxhdpi" : "" /*1080P高分屏启动图片,分辨率:1080x1882*/
-                }
-            },
-            "ios" : {
-                "dSYMs" : false
-            }
-        }
-    },
-    "screenOrientation" : [ "landscape-primary" ],
-    "fullscreen" : true
-}

File diff suppressed because it is too large
+ 4736 - 222
package-lock.json


+ 14 - 1
package.json

@@ -3,11 +3,20 @@
   "version": "0.0.0",
   "private": true,
   "type": "module",
+  "main": "electron/main.js",
+  "author": {
+    "name": "博合文化",
+    "email": "wenlv@wenlvti.net"
+  },
+  "description": "湖里透明展示屏",
   "scripts": {
     "dev": "vite",
     "build": "run-p type-check \"build-only {@}\" --",
     "preview": "vite preview",
-    "build-only": "vite build && node .\\src\\scripts\\copyDist.cjs",
+    "build-only": "vite build && electron-builder",
+    "build-electron": "electron-builder",
+    "dev-electron": "cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://localhost:5173 electron ./electron/main.js",
+    "dev-dist-electron": "cross-env NODE_ENV=development electron ./electron/main.js",
     "type-check": "vue-tsc --build"
   },
   "dependencies": {
@@ -31,11 +40,15 @@
     "@vitejs/plugin-vue": "^6.0.0",
     "@vitejs/plugin-vue-jsx": "^5.0.0",
     "@vue/tsconfig": "^0.7.0",
+    "cross-env": "^10.0.0",
+    "electron": "^38.0.0",
+    "electron-builder": "^26.0.12",
     "npm-run-all2": "^8.0.4",
     "sass-embedded": "^1.89.2",
     "typescript": "~5.8.0",
     "unplugin-vue-components": "^28.8.0",
     "vite": "^7.0.0",
+    "vite-plugin-electron": "^0.29.0",
     "vite-plugin-vue-devtools": "^7.7.7",
     "vue-tsc": "^2.2.10"
   }

+ 7 - 0
src/App.vue

@@ -1,8 +1,15 @@
 <script setup lang="ts">
 import { RouterView } from 'vue-router'
 import { useTvFocusImprovement } from './composeable/TvFocusImprovement';
+import { useListenKey } from './composeable/ListenKey';
 
 useTvFocusImprovement(true);
+useListenKey('F12', () => {
+  window.electronAPI.toggleDevTools();
+});
+useListenKey('F11', () => {
+  window.electronAPI.toggleFullscreen();
+});
 </script>
 
 <template>

+ 4 - 0
src/assets/scss/main.scss

@@ -39,6 +39,10 @@ main {
     &:focus {
       outline: 8px solid var(--color-primary2);
     }
+
+    &.invisible {
+      opacity: 0.1;
+    }
   }
   .carousel__next, .carousel__prev, .carousel__pagination-button {
     outline: none;

+ 12 - 1
src/components/3d/Viewer.vue

@@ -1,7 +1,7 @@
 <template>
   <div :id="id" class="container">
     <SimpleLoading v-if="loading" />
-
+    <div v-else-if="error" class="error">{{ error }}</div>
 
   </div>
 </template>
@@ -86,10 +86,12 @@ function init() {
 }
 
 const loading = ref(false);
+const error = ref('');
 
 function loadPath(path: string) {
   console.log('load model', path);
   loading.value = true;
+  error.value = '';
   return new Promise<void>((resolve, reject) => {
     if (!scene) {
       reject('scene not init');
@@ -135,6 +137,8 @@ function loadPath(path: string) {
     }, undefined, reject);
   }).finally(() => {
     loading.value = false;
+  }).catch((e) => {
+    error.value = '' + e;
   });
 }
 
@@ -165,5 +169,12 @@ onMounted(() => {
     left: 50%;
     transform: translate(-50%, -50%);
   }
+  .error {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    color: #a0200f;
+  }
 }
 </style>

+ 11 - 4
src/components/parts/PageRoot.vue

@@ -22,6 +22,12 @@ navs.push({
 
 const menuShow = ref(false);
 
+function goIndex() {
+  if (route.path === '/menu')
+    router.push('/');
+  else
+    router.push('/menu');
+}
 function goBack() {
   router.back();
 }
@@ -43,7 +49,7 @@ defineProps({
       <div class="d-flex flex-row justify-start">
         <img v-if="!isRoot" tabindex="0" class="back" src="@/assets/images/IconMenu.png" @click="showMenu"></img>
         <img v-if="!isRoot" tabindex="0" class="back" src="@/assets/images/IconBack.png" @click="goBack"></img>
-        <div class="title d-flex flex-row align-center">
+        <div class="title d-flex flex-row align-center nav-title" @click="goIndex">
           <span>湖里文化遗产保护中心</span>
           <img src="@/assets/images/ImgPrintWithText.png"></img>
         </div>
@@ -97,8 +103,6 @@ defineProps({
   cursor: pointer;
 }
 .title {
-
-
   span {
     font-size: 24px;
     letter-spacing: 0.3em;
@@ -110,7 +114,10 @@ defineProps({
     height: 35px;
   }
 }
-
+.nav-title {
+  user-select: none;
+  cursor: pointer;
+}
 
 .mobile-menu-popover {
   position: fixed;

+ 17 - 0
src/composeable/ListenKey.ts

@@ -0,0 +1,17 @@
+import { onMounted, onUnmounted } from "vue";
+
+export function useListenKey(keyCode: string, callback: () => void) {
+
+  onMounted(() => {
+    window.addEventListener('keydown', listener);
+  });
+  onUnmounted(() => {
+    window.removeEventListener('keydown', listener);
+  });
+
+  const listener = (e: KeyboardEvent) => {
+    if (e.key === keyCode) {
+      callback();
+    }
+  };
+}

+ 0 - 52
src/scripts/copyDist.cjs

@@ -1,52 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-
-// 定义源目录和目标目录路径
-const srcDir = path.resolve(__dirname, '../..', 'dist');
-const destDir = path.resolve(__dirname, '../..', 'pack');
-
-/**
- * 递归拷贝目录内容
- * @param {string} src - 源目录路径
- * @param {string} dest - 目标目录路径
- */
-function copyDir(src, dest) {
-    // 检查源目录是否存在
-    if (!fs.existsSync(src)) {
-        console.error(`错误:源目录不存在 - ${src}`);
-        process.exit(1);
-    }
-
-    // 创建目标目录(如果不存在)
-    if (!fs.existsSync(dest)) {
-        fs.mkdirSync(dest, { recursive: true });
-        console.log(`创建目标目录:${dest}`);
-    }
-
-    // 读取源目录内容
-    const entries = fs.readdirSync(src, { withFileTypes: true });
-
-    for (const entry of entries) {
-        const srcPath = path.join(src, entry.name);
-        const destPath = path.join(dest, entry.name);
-
-        if (entry.isDirectory()) {
-            // 递归处理子目录
-            copyDir(srcPath, destPath);
-        } else {
-            // 拷贝文件
-            fs.copyFileSync(srcPath, destPath);
-            console.log(`已拷贝:${srcPath} -> ${destPath}`);
-        }
-    }
-}
-
-// 执行拷贝并处理错误
-try {
-    console.log('开始拷贝文件...');
-    copyDir(srcDir, destDir);
-    console.log('文件拷贝完成!');
-} catch (err) {
-    console.error('拷贝过程中发生错误:', err);
-    process.exit(1);
-}

+ 5 - 2
src/views/HomeView.vue

@@ -8,6 +8,9 @@ function goMenu() {
     name: 'Menu',
   });
 }
+function exitApp() {
+  window.electronAPI.closeWindow();
+}
 </script>
 
 <template>
@@ -25,12 +28,12 @@ function goMenu() {
         <div>触碰历史的温度,感受匠心的力量</div>
         <div>一场非遗匠艺手作之旅即将优雅启程</div>
       </div>
-      <div class="d-flex flex-row align-center justify-center mt-5 mb-5">
+      <div class="d-flex flex-column align-center justify-center mt-5 mb-5">
         <button>开始探索</button>
+        <button class="mt-3 invisible" @click.prevent.stop="exitApp">退出程序</button>
       </div>
       <div class="mb-5"></div>
       <div class="mb-5"></div>
-      <div class="mb-5"></div>
     </div>
   </main>
 </template>

+ 2 - 0
vite.config.ts

@@ -6,6 +6,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
 import vueDevTools from 'vite-plugin-vue-devtools'
 import Components from 'unplugin-vue-components/vite';
 import legacy from '@vitejs/plugin-legacy';
+import electron from 'vite-plugin-electron';
 import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
 
 // https://vite.dev/config/
@@ -26,6 +27,7 @@ export default defineConfig({
         }),
       ],
     }),
+    electron({ entry: 'electron/main.js' }),
   ],
   base: './',
   resolve: {