This commit is contained in:
2025-10-09 10:02:37 +08:00
parent 4065da3766
commit db67a99288
7 changed files with 247 additions and 16 deletions

View File

@@ -0,0 +1,8 @@
const Path = require('path');
const FileSystem = require('fs-extra');
async function copyAssets() {
console.log('Static assets are now handled by Vite from src/renderer/public');
}
module.exports = copyAssets;

View File

@@ -111,12 +111,10 @@ function getSplashPath(): string {
return join(__dirname, '../../public/splash.html');
}
function getIconPath(): string {
if (process.env.NODE_ENV === 'development') {
return join(__dirname, '../../public/icon/icon.png');
}
const bundledIconPath = join(process.resourcesPath, 'app.asar.unpacked/public/icon/icon.png');
if (existsSync(bundledIconPath)) {
return bundledIconPath;
@@ -230,7 +228,7 @@ function startSpringBoot() {
}
}
startSpringBoot();
// startSpringBoot();
function stopSpringBoot() {
if (!springProcess) return;
@@ -268,7 +266,7 @@ function createWindow() {
Menu.setApplicationMenu(null);
mainWindow.setMenuBarVisibility(false);
// 打开开发者工具
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
@@ -315,7 +313,6 @@ app.whenReady().then(() => {
splashWindow.loadFile(splashPath);
}
//11111
// setTimeout(() => {
// openAppIfNotOpened();
// }, 2000);

View File

@@ -1,7 +1,6 @@
<template>
<div>
<div class="version-info" @click="autoCheck">v{{ version || '-' }}</div>
<el-dialog v-model="show" width="522px" :close-on-click-modal="false" align-center class="update-dialog"
:title="stage === 'downloading' ? `正在更新 ${appName}` : '软件更新'">
<div v-if="stage === 'check'" class="update-content">
@@ -14,7 +13,6 @@
<p class="desc">{{ appName }} {{ info.latestVersion }} 可供安装您现在的版本是 {{
version
}}要现在安装吗</p>
<div class="update-details form">
<h4>更新信息</h4>
<el-input
@@ -25,14 +23,13 @@
readonly
resize="none"/>
</div>
<div class="update-actions row">
<div class="update-buttons">
<div class="left-actions">
<el-button size="small" @click="show=false">跳过这个版本</el-button>
<el-button size="small" @click="skipVersion">跳过这个版本</el-button>
</div>
<div class="right-actions">
<el-button size="small" @click="show=false">稍后提醒</el-button>
<el-button size="small" @click="remindLater">稍后提醒</el-button>
<el-button size="small" type="primary" @click="start">下载更新</el-button>
</div>
</div>
@@ -64,7 +61,6 @@
</div>
</div>
</div>
<div v-else-if="stage === 'completed'" class="update-content">
<div class="update-header text-center">
<img src="/icon/icon.png" class="app-icon" alt="App Icon"/>
@@ -118,6 +114,9 @@ const info = ref({
hasUpdate: false
})
const SKIP_VERSION_KEY = 'skipped_version'
const REMIND_LATER_KEY = 'remind_later_time'
async function autoCheck() {
try {
version.value = await (window as any).electronAPI.getJarVersion()
@@ -129,6 +128,18 @@ async function autoCheck() {
return
}
// 检查是否跳过此版本
const skippedVersion = localStorage.getItem(SKIP_VERSION_KEY)
if (skippedVersion === result.latestVersion) {
return
}
// 检查是否在稍后提醒时间内
const remindLater = localStorage.getItem(REMIND_LATER_KEY)
if (remindLater && Date.now() < parseInt(remindLater)) {
return
}
info.value = {
currentVersion: result.currentVersion,
latestVersion: result.latestVersion,
@@ -145,6 +156,17 @@ async function autoCheck() {
}
}
function skipVersion() {
localStorage.setItem(SKIP_VERSION_KEY, info.value.latestVersion)
show.value = false
}
function remindLater() {
// 24小时后再提醒
localStorage.setItem(REMIND_LATER_KEY, (Date.now() + 24 * 60 * 60 * 1000).toString())
show.value = false
}
async function start() {
if (!info.value.downloadUrl) {
ElMessage.error('下载链接不可用')
@@ -222,6 +244,8 @@ onMounted(async () => {
onUnmounted(() => {
(window as any).electronAPI.removeDownloadProgressListener()
})
</script>
<style scoped>
@@ -396,10 +420,6 @@ onUnmounted(() => {
border-radius: 8px;
}
.download-header {
text-align: center;
margin-bottom: 20px;
}
.download-header h3 {
font-size: 14px;

View File

@@ -0,0 +1,6 @@
declare module 'element-plus' {
export const ElMessage: (options: { message: string; type?: 'success' | 'warning' | 'error' | 'info' }) => void
export const ElMessageBox: { confirm: (message: string, title?: string, options?: any) => Promise<void> }
}

View File

@@ -0,0 +1,78 @@
/**
* 通过后端代理获取图片并转换为Base64
* @param imageUrl 原始图片URL
* @param maxSize 最大尺寸默认80px
* @returns Promise<string | null> Base64字符串或null
*/
export async function convertImageToBase64ViaProxy(imageUrl: string, maxSize: number = 80): Promise<string | null> {
if (!imageUrl) return null
try {
const proxyUrl = `http://127.0.0.1:8081/api/proxy/image-url?url=${encodeURIComponent(imageUrl)}`
const response = await fetch(proxyUrl)
if (!response.ok) return null
const contentType = response.headers.get('Content-Type')
const arrayBuffer = await response.arrayBuffer()
if (!arrayBuffer || arrayBuffer.byteLength === 0) return null
if (arrayBuffer.byteLength < 1000) return null
const mimeType = contentType && contentType.startsWith('image/') ? contentType : 'image/jpeg'
const imageBlob = new Blob([arrayBuffer], { type: mimeType })
const objectUrl = URL.createObjectURL(imageBlob)
return new Promise((resolve) => {
const img = new Image()
img.onload = () => {
try {
const canvas = document.createElement('canvas')
const ratio = Math.min(maxSize / img.width, maxSize / img.height)
canvas.width = img.width * ratio
canvas.height = img.height * ratio
const ctx = canvas.getContext('2d')
if (!ctx) {
URL.revokeObjectURL(objectUrl)
resolve(null)
return
}
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
const base64 = canvas.toDataURL('image/jpeg', 0.8)
URL.revokeObjectURL(objectUrl)
resolve(base64)
} catch (error) {
URL.revokeObjectURL(objectUrl)
resolve(null)
}
}
img.onerror = () => {
URL.revokeObjectURL(objectUrl)
resolve(null)
}
img.src = objectUrl
})
} catch (error) {
return null
}
}
/**
* 批量处理图片转换
* @param imageUrls 图片URL数组
* @param maxSize 最大尺寸
* @returns Promise<(string | null)[]> Base64数组
*/
export async function batchConvertImages(imageUrls: string[], maxSize: number = 80): Promise<(string | null)[]> {
const promises = imageUrls.map(async (url) => {
if (!url) return null
return await convertImageToBase64ViaProxy(url, maxSize)
})
return await Promise.all(promises)
}

View File

@@ -0,0 +1,123 @@
// 应用设置管理工具
export type Platform = 'amazon' | 'rakuten' | 'zebra'
export interface PlatformExportSettings {
exportPath: string
}
export interface AppSettings {
// 全局设置
global: PlatformExportSettings
// 平台特定设置
platforms: {
amazon: PlatformExportSettings
rakuten: PlatformExportSettings
zebra: PlatformExportSettings
}
}
const SETTINGS_KEY = 'app-settings'
// 默认平台设置
const defaultPlatformSettings: PlatformExportSettings = {
exportPath: ''
}
// 默认设置
const defaultSettings: AppSettings = {
global: { ...defaultPlatformSettings },
platforms: {
amazon: { ...defaultPlatformSettings },
rakuten: { ...defaultPlatformSettings },
zebra: { ...defaultPlatformSettings }
}
}
// 获取设置
export function getSettings(): AppSettings {
const saved = localStorage.getItem(SETTINGS_KEY)
if (saved) {
const settings = JSON.parse(saved)
return {
global: { ...defaultSettings.global, ...settings.global },
platforms: {
amazon: { ...defaultSettings.platforms.amazon, ...settings.platforms?.amazon },
rakuten: { ...defaultSettings.platforms.rakuten, ...settings.platforms?.rakuten },
zebra: { ...defaultSettings.platforms.zebra, ...settings.platforms?.zebra }
}
}
}
return defaultSettings
}
// 保存设置
export function saveSettings(settings: Partial<AppSettings>): void {
const current = getSettings()
const updated = {
global: { ...current.global, ...settings.global },
platforms: {
amazon: { ...current.platforms.amazon, ...settings.platforms?.amazon },
rakuten: { ...current.platforms.rakuten, ...settings.platforms?.rakuten },
zebra: { ...current.platforms.zebra, ...settings.platforms?.zebra }
}
}
localStorage.setItem(SETTINGS_KEY, JSON.stringify(updated))
}
// 保存平台特定设置
export function savePlatformSettings(platform: Platform, settings: Partial<PlatformExportSettings>): void {
const current = getSettings()
const updated = {
...current,
platforms: {
...current.platforms,
[platform]: { ...current.platforms[platform], ...settings }
}
}
localStorage.setItem(SETTINGS_KEY, JSON.stringify(updated))
}
// 获取平台导出配置
export function getPlatformExportConfig(platform: Platform): PlatformExportSettings {
const settings = getSettings()
return settings.platforms[platform]
}
// 处理平台特定文件导出
export async function handlePlatformFileExport(
platform: Platform,
blob: Blob,
defaultFileName: string
): Promise<void> {
const config = getPlatformExportConfig(platform)
if (!config.exportPath) {
const result = await (window as any).electronAPI.showSaveDialog({
title: '保存文件',
defaultPath: defaultFileName,
filters: [
{ name: 'Excel 文件', extensions: ['xlsx', 'xls'] },
{ name: '所有文件', extensions: ['*'] }
]
})
if (!result.canceled && result.filePath) {
await writeFileToPath(blob, result.filePath)
}
} else {
const filePath = `${config.exportPath}/${defaultFileName}`
await writeFileToPath(blob, filePath)
}
}
// 写入文件到指定路径
async function writeFileToPath(blob: Blob, filePath: string): Promise<void> {
const arrayBuffer = await blob.arrayBuffer()
const buffer = new Uint8Array(arrayBuffer)
const result = await (window as any).electronAPI.writeFile(filePath, buffer)
if (!result.success) throw new Error(result.error)
}