diff --git a/.eslintrc.json b/.eslintrc.json index 4ae8571..a7cd4ad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,7 +2,8 @@ "env": { "browser": true, "es6": true, - "node": true + "node": true, + "no-console": "off" }, "extends": [ "eslint:recommended", diff --git a/.trae/documents/实现简约美观的登录页(Vue + Tailwind).md b/.trae/documents/实现简约美观的登录页(Vue + Tailwind).md deleted file mode 100644 index de24a0a..0000000 --- a/.trae/documents/实现简约美观的登录页(Vue + Tailwind).md +++ /dev/null @@ -1,85 +0,0 @@ -## 页面需求 - -* 输入:账号、密码;按钮:登录 - -* 交互:输入校验(必填、长度/格式)、回车提交、禁用按钮(加载中/校验失败)、错误提示、明文/密文切换 - -* 布局:简约卡片居中、品牌标题、副文、响应式(桌面)、浅色渐变背景 - -## 现状与集成点 - -* 已存在页面骨架:`src/views/login/index.vue`(中心卡片+标题,待补表单) - -* 路由:登录路由目前注释(`src/router/index.ts`),默认首页为 `/` 的 `BrowserLayout`(标签页界面) - -## 实现步骤 - -1. 表单与校验 - -* 在 `src/views/login/index.vue` 中添加账号/密码输入框与登录按钮 - -* 使用 `ref`/`reactive` 管理表单;规则: - - * 账号:必填、长度 4-32、允许邮箱/手机号/用户名(简单格式校验) - - * 密码:必填、长度 ≥ 6 - -* 按钮禁用条件:`!valid || loading` - -* 支持 `Enter` 键触发提交 - -* 密码框添加“显示/隐藏”切换 - -1. 反馈与跳转 - -* 登录过程显示 `loading`(按钮文案切换为“登录中…”) - -* 失败显示错误提示(红色文本);成功暂时跳转到 `/`(浏览器标签页界面) - -* 留出接口占位:`login(account, password)`(后续接入真实 API) - -1. 样式 - -* Tailwind 构建简约风格:卡片圆角阴影、输入框边框、聚焦效果、按钮主色(蓝色) - -* 响应式:移动端输入宽度 100%,桌面端 `max-w-md` 保持卡片紧凑 - -1. 路由接入 - -* 在 `src/router/index.ts` 增加登录路由:`/login` → `views/login/index.vue` - -* 暂不改默认首页;如需登录守卫,可后续将 `/login` 作为默认或在未认证时重定向到 `/login` - -## 代码改动概览(不执行,仅说明) - -* `src/views/login/index.vue` - - * 模板:两个输入框(账号、密码)、显示/隐藏密码按钮、登录按钮、错误提示区域 - - * 逻辑:`form`、`errors`、`validate()`、`onSubmit()`、`loading`、`togglePassword()`;成功后:`router.push('/')` - - * 样式:Tailwind 类名添加于容器/卡片/输入/按钮 - -* `src/router/index.ts` - - * 添加: - - ```ts - { path: '/login', name: 'Login', component: () => import('@/views/login/index.vue') } - ``` - -## 验证 - -* 输入为空时按钮禁用;填入合法后启用 - -* 按 `Enter` 能提交;失败提示文案显示;成功跳转到首页 - -* 移动端/桌面端居中显示良好 - -## 后续可选 - -* 接入真实认证 API(axios),保存 token(Pinia/LocalStorage),路由守卫控制访问 - -* 增加“记住我”、忘记密码、国际化 - -请确认以上方案,确认后我将直接补全 `index.vue` 表单与交互,并注册 `/login` 路由。 diff --git a/build/scripts/clean.js b/build/scripts/clean.js index cd0f940..badeb06 100644 --- a/build/scripts/clean.js +++ b/build/scripts/clean.js @@ -1,21 +1,23 @@ -const fs = require('fs') +const { execSync } = require('child_process') +const fs = require('fs-extra') const path = require('path') -function cleanDirectory(dirPath) { - const resolvedPath = path.resolve(dirPath) +function cleanOutRoot() { + const outDir = path.resolve(process.cwd(), 'out') - const parentDir = path.basename(path.dirname(resolvedPath)) - if (parentDir !== 'out') { - console.error(`Error: Directory "${resolvedPath}" is not inside a "out" folder`) + if (!fs.pathExistsSync(outDir)) { return } - const distPath = path.join(resolvedPath, 'dist') - if (fs.existsSync(distPath)) { - fs.rmSync(distPath, { recursive: true, force: true }) - console.log(`deleted: ${distPath}`) + fs.removeSync(outDir) + + if (fs.pathExistsSync(outDir)) { + execSync(`cmd /c rd /s /q "${outDir}"`, { stdio: 'ignore' }) + + process.exitCode = 1 } } -cleanDirectory(process.cwd()) +cleanOutRoot() + diff --git a/build/scripts/generateProdEntry.js b/build/scripts/generateProdEntry.js index e02a191..e88e3ae 100644 --- a/build/scripts/generateProdEntry.js +++ b/build/scripts/generateProdEntry.js @@ -1,21 +1,23 @@ const fs = require('fs') const path = require('path') -if (!fs.existsSync(path.join(process.cwd(), 'dist/main/build.jsc'))) { - throw new Error('字节码文件未找到,请先执行编译') +const entryPath = path.join(process.cwd(), '.vite/build/main.js') +const jscPath = path.join(process.cwd(), '.vite/build/main.jsc') +if (!fs.existsSync(entryPath)) { + throw new Error('主进程入口未找到,请先构建主进程') +} +if (!fs.existsSync(jscPath)) { + const bytenode = require('bytenode') + bytenode.compileFile({ filename: entryPath, output: jscPath }) } -const content = ` -const {app} = require('electron'); -require('bytenode'); -require('./index.jsc); -` +const content = [ + "require('bytenode')", + "require('./main.jsc')" +].join('\n') -const outputPath = path.join(process.cwd(), 'dist/main/build.js') +const outputPath = entryPath -// 确保目录存在 fs.mkdirSync(path.dirname(outputPath), { recursive: true }) - -// 写入文件 -fs.writeFileSync(outputPath, content.trim()) -console.log(`生产环境入口文件已生成: ${outputPath}`) \ No newline at end of file +fs.writeFileSync(outputPath, content) +console.log(`生产环境入口文件已生成: ${outputPath}`) diff --git a/forge.config.ts b/forge.config.ts index db7d809..ef7e82a 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -6,6 +6,8 @@ import { MakerRpm } from '@electron-forge/maker-rpm'; import { VitePlugin } from '@electron-forge/plugin-vite'; import { FusesPlugin } from '@electron-forge/plugin-fuses'; import { FuseV1Options, FuseVersion } from '@electron/fuses'; +import * as fs from 'fs-extra'; +import * as path from 'path'; const config: ForgeConfig = { packagerConfig: { @@ -54,6 +56,66 @@ const config: ForgeConfig = { [FuseV1Options.OnlyLoadAppFromAsar]: true, }), ], + hooks: { + async prePackage() { + const outDir = path.resolve(process.cwd(), 'out'); + fs.rmSync(outDir, { recursive: true, force: true }); + }, + + async preMake() { + const outDir = path.resolve(process.cwd(), 'out'); + fs.rmSync(outDir, { recursive: true, force: true }); + }, + + async postPackage(_forgeConfig, options) { + const electronVersion = require('electron/package.json').version; + const nodeVersion = process.version; + const versionData = { + node: nodeVersion, + electron: electronVersion, + platform: process.platform, + arch: process.arch, + buildTime: new Date().toISOString(), + }; + const outDir = path.resolve(process.cwd(), 'out'); + fs.ensureDirSync(outDir); + + const outputs = (options && (options as any).outputPaths) || []; + + if (Array.isArray(outputs) && outputs.length > 0) { + for (const p of outputs) { + try { + const versionFile = path.join(p, 'version'); + fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2)); + } catch {} + } + } + }, + + async postMake(_forgeConfig, outputs) { + const electronVersion = require('electron/package.json').version; + const nodeVersion = process.version; + const versionData = { + node: nodeVersion, + electron: electronVersion, + platform: process.platform, + arch: process.arch, + buildTime: new Date().toISOString(), + }; + + const outDir = path.resolve(process.cwd(), 'out'); + fs.ensureDirSync(outDir); + + if (Array.isArray(outputs) && outputs.length > 0) { + for (const p of outputs) { + try { + const versionFile = path.join(p as any, 'version'); + fs.writeFileSync(versionFile, JSON.stringify(versionData, null, 2)); + } catch {} + } + } + }, + }, }; export default config; diff --git a/src/plugins/bytenode/vite-plugin-electron-encrypt.ts b/src/plugins/bytenode/vite-plugin-electron-encrypt.ts index 1213e30..c3e4ab9 100644 --- a/src/plugins/bytenode/vite-plugin-electron-encrypt.ts +++ b/src/plugins/bytenode/vite-plugin-electron-encrypt.ts @@ -14,19 +14,6 @@ export default function electronBytecode(options?: ElectronBytecodeOptions): Plu apply: 'build', async closeBundle() { if (process.env.NODE_ENV !== 'production') return - const electronVersion = require('electron/package.json').version - const nodeVersion = process.version - const versionData = { - node: nodeVersion, - electron: electronVersion, - platform: process.platform, - arch: process.arch, - buildTime: new Date().toISOString() - } - const versionPath = path.resolve(process.cwd(), 'dist', 'version.json') - - fs.ensureDirSync(path.dirname(versionPath)) - fs.writeJsonSync(versionPath, versionData, { spaces: 2 }) const entryPath = path.resolve(process.cwd(), options?.entry || '.vite/build/main.js') @@ -34,14 +21,13 @@ export default function electronBytecode(options?: ElectronBytecodeOptions): Plu const outputPath = entryPath.replace(/\.js$/, '.jsc') const backupPath = `${entryPath}.bak` - fs.copyFileSync(entryPath, backupPath) - await bytenode.compileFile({ filename: entryPath, output: outputPath }) - - if (!options?.keepSource) { - fs.removeSync(entryPath) - } + const stub = [ + "require('bytenode')", + `require('./${path.basename(outputPath)}')` + ].join("\n") + fs.writeFileSync(entryPath, stub) } } } diff --git a/vite.main.config.ts b/vite.main.config.ts index 6cfac94..a15be3e 100644 --- a/vite.main.config.ts +++ b/vite.main.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from "vite"; import { resolve } from "path"; +import electronBytecode from "./src/plugins/bytenode/vite-plugin-electron-encrypt"; // https://vitejs.dev/config export default defineConfig({ + plugins: [electronBytecode({ entry: ".vite/build/main.js", keepSource: false })], resolve: { alias: { "@": resolve(__dirname, "./src/electron"),