1
This commit is contained in:
8
electron-vue-template/scripts/copy-assets.js
Normal file
8
electron-vue-template/scripts/copy-assets.js
Normal 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;
|
||||||
@@ -111,12 +111,10 @@ function getSplashPath(): string {
|
|||||||
|
|
||||||
return join(__dirname, '../../public/splash.html');
|
return join(__dirname, '../../public/splash.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIconPath(): string {
|
function getIconPath(): string {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
return join(__dirname, '../../public/icon/icon.png');
|
return join(__dirname, '../../public/icon/icon.png');
|
||||||
}
|
}
|
||||||
|
|
||||||
const bundledIconPath = join(process.resourcesPath, 'app.asar.unpacked/public/icon/icon.png');
|
const bundledIconPath = join(process.resourcesPath, 'app.asar.unpacked/public/icon/icon.png');
|
||||||
if (existsSync(bundledIconPath)) {
|
if (existsSync(bundledIconPath)) {
|
||||||
return bundledIconPath;
|
return bundledIconPath;
|
||||||
@@ -230,7 +228,7 @@ function startSpringBoot() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startSpringBoot();
|
// startSpringBoot();
|
||||||
|
|
||||||
function stopSpringBoot() {
|
function stopSpringBoot() {
|
||||||
if (!springProcess) return;
|
if (!springProcess) return;
|
||||||
@@ -315,7 +313,6 @@ app.whenReady().then(() => {
|
|||||||
splashWindow.loadFile(splashPath);
|
splashWindow.loadFile(splashPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
//11111
|
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// openAppIfNotOpened();
|
// openAppIfNotOpened();
|
||||||
// }, 2000);
|
// }, 2000);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="version-info" @click="autoCheck">v{{ version || '-' }}</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"
|
<el-dialog v-model="show" width="522px" :close-on-click-modal="false" align-center class="update-dialog"
|
||||||
:title="stage === 'downloading' ? `正在更新 ${appName}` : '软件更新'">
|
:title="stage === 'downloading' ? `正在更新 ${appName}` : '软件更新'">
|
||||||
<div v-if="stage === 'check'" class="update-content">
|
<div v-if="stage === 'check'" class="update-content">
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
<p class="desc">{{ appName }} {{ info.latestVersion }} 可供安装,您现在的版本是 {{
|
<p class="desc">{{ appName }} {{ info.latestVersion }} 可供安装,您现在的版本是 {{
|
||||||
version
|
version
|
||||||
}},要现在安装吗?</p>
|
}},要现在安装吗?</p>
|
||||||
|
|
||||||
<div class="update-details form">
|
<div class="update-details form">
|
||||||
<h4>更新信息</h4>
|
<h4>更新信息</h4>
|
||||||
<el-input
|
<el-input
|
||||||
@@ -25,14 +23,13 @@
|
|||||||
readonly
|
readonly
|
||||||
resize="none"/>
|
resize="none"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="update-actions row">
|
<div class="update-actions row">
|
||||||
<div class="update-buttons">
|
<div class="update-buttons">
|
||||||
<div class="left-actions">
|
<div class="left-actions">
|
||||||
<el-button size="small" @click="show=false">跳过这个版本</el-button>
|
<el-button size="small" @click="skipVersion">跳过这个版本</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-actions">
|
<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>
|
<el-button size="small" type="primary" @click="start">下载更新</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -64,7 +61,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="stage === 'completed'" class="update-content">
|
<div v-else-if="stage === 'completed'" class="update-content">
|
||||||
<div class="update-header text-center">
|
<div class="update-header text-center">
|
||||||
<img src="/icon/icon.png" class="app-icon" alt="App Icon"/>
|
<img src="/icon/icon.png" class="app-icon" alt="App Icon"/>
|
||||||
@@ -118,6 +114,9 @@ const info = ref({
|
|||||||
hasUpdate: false
|
hasUpdate: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SKIP_VERSION_KEY = 'skipped_version'
|
||||||
|
const REMIND_LATER_KEY = 'remind_later_time'
|
||||||
|
|
||||||
async function autoCheck() {
|
async function autoCheck() {
|
||||||
try {
|
try {
|
||||||
version.value = await (window as any).electronAPI.getJarVersion()
|
version.value = await (window as any).electronAPI.getJarVersion()
|
||||||
@@ -129,6 +128,18 @@ async function autoCheck() {
|
|||||||
return
|
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 = {
|
info.value = {
|
||||||
currentVersion: result.currentVersion,
|
currentVersion: result.currentVersion,
|
||||||
latestVersion: result.latestVersion,
|
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() {
|
async function start() {
|
||||||
if (!info.value.downloadUrl) {
|
if (!info.value.downloadUrl) {
|
||||||
ElMessage.error('下载链接不可用')
|
ElMessage.error('下载链接不可用')
|
||||||
@@ -222,6 +244,8 @@ onMounted(async () => {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
(window as any).electronAPI.removeDownloadProgressListener()
|
(window as any).electronAPI.removeDownloadProgressListener()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -396,10 +420,6 @@ onUnmounted(() => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.download-header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.download-header h3 {
|
.download-header h3 {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
6
electron-vue-template/src/renderer/typings/element-plus-shim.d.ts
vendored
Normal file
6
electron-vue-template/src/renderer/typings/element-plus-shim.d.ts
vendored
Normal 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> }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
78
electron-vue-template/src/renderer/utils/imageProxy.ts
Normal file
78
electron-vue-template/src/renderer/utils/imageProxy.ts
Normal 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)
|
||||||
|
}
|
||||||
123
electron-vue-template/src/renderer/utils/settings.ts
Normal file
123
electron-vue-template/src/renderer/utils/settings.ts
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>4.1.2</version>
|
<version>4.1.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 已移除 JavaFX/FxWeaver 相关依赖,保留为纯 Spring Boot -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.qiniu</groupId>
|
<groupId>com.qiniu</groupId>
|
||||||
<artifactId>qiniu-java-sdk</artifactId>
|
<artifactId>qiniu-java-sdk</artifactId>
|
||||||
|
|||||||
Reference in New Issue
Block a user