From 0a908a31f69f618c5a1b1d01b250d887089348c9 Mon Sep 17 00:00:00 2001 From: shenmo Date: Thu, 16 Jul 2026 14:03:40 +0800 Subject: [PATCH] chore: release v5.2.1 and improve submitter UI/UX 1. update version to 5.2.1 in changelog and package.json 2. add real-time upload progress display in submission window 3. optimize icon filename display and form reset logic 4. add success modal after successful submission 5. improve logging formatting in submitter backend --- debian/changelog | 2 +- electron/main/backend/submitter.ts | 7 +- package.json | 2 +- src/components/SubmitterWindow.vue | 223 +++++++++++++++++++++++++++-- 4 files changed, 221 insertions(+), 13 deletions(-) diff --git a/debian/changelog b/debian/changelog index b58e9000..775e4b7a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -spark-store (5.2.0) UNRELEASED; urgency=medium +spark-store (5.2.1) UNRELEASED; urgency=medium * Initial release. (Closes: #nnnn) diff --git a/electron/main/backend/submitter.ts b/electron/main/backend/submitter.ts index 7e2da43e..43625123 100644 --- a/electron/main/backend/submitter.ts +++ b/electron/main/backend/submitter.ts @@ -1302,6 +1302,7 @@ export function registerSubmitterHandlers( logger.info( "[Submitter] ============== STEP 4: SUBMIT APPLICATION ==============", ); + sendUploadProgress("submit", 0, "正在提交投稿信息..."); const debFileStat = fs.statSync(debFilePath); @@ -1311,7 +1312,10 @@ export function registerSubmitterHandlers( const pkgVersion = String(dataObj.version || "0.0.0"); const debPkgName = String(dataObj.pkgname || "unknown"); const formFileName = `${debPkgName}_${pkgVersion}_${debArch}.deb`; - logger.info({ formFileName, debArch }, "[Submitter] Constructed file_name"); + logger.info( + { formFileName, debArch }, + "[Submitter] Constructed file_name", + ); const categoryName = String(dataObj.category || ""); const categoryId = Number(dataObj.categoryId) || 0; @@ -1536,6 +1540,7 @@ export function registerSubmitterHandlers( { duration: requestDuration }, "[Submitter] Total duration (ms)", ); + sendUploadProgress("submit", 100, "投稿信息提交完成"); return { success: true, data: result }; } catch (err) { diff --git a/package.json b/package.json index 7c53af32..eda8f494 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spark-store", - "version": "5.2.0", + "version": "5.2.1", "main": "dist-electron/main/index.js", "description": "Client for Spark App Store", "author": "elysia-best ", diff --git a/src/components/SubmitterWindow.vue b/src/components/SubmitterWindow.vue index 2db193bd..5a30bc43 100644 --- a/src/components/SubmitterWindow.vue +++ b/src/components/SubmitterWindow.vue @@ -203,8 +203,8 @@

{{ iconPreview ? "点击浏览更换图标" : "点击浏览" }}

-

- {{ formData.iconPath.split("/").pop() }} +

+ {{ iconFileName }}

@@ -385,14 +385,106 @@
+
+ + + {{ isPackaging ? "正在打包..." : uploadStage || "正在提交..." }} + +
+
+
+
+
+

+ {{ uploadStageMessage || "准备中..." }} +

+
+
+ + + + {{ stage.label }} + {{ Math.floor(stage.progress) }}% +
+
+
+
+ +
- - 投稿提交成功!我们会尽快审核你的应用。 +
+
+ +
+
+

+ 投稿提交成功! +

+

+ 我们会尽快审核你的应用 +

+
+
+
+ + +
@@ -611,6 +703,7 @@ watch( const isSubmitting = ref(false); const submitSuccess = ref(false); const submitError = ref(""); +const showSubmitSuccessModal = ref(false); const isParsingDeb = ref(false); const debParseError = ref(""); const isSearchingHistory = ref(false); @@ -618,6 +711,14 @@ const showArchDialog = ref(false); const availableArchs = ref([]); const currentDebArch = ref(""); const iconPreview = ref(""); +const iconFileName = ref(""); + +const uploadProgress = ref(0); +const uploadStage = ref(""); +const uploadStageMessage = ref(""); +const uploadStages = ref<{ stage: string; label: string; progress: number }[]>( + [], +); const isLoadingCategories = ref(false); const categoriesLoadError = ref(""); @@ -705,7 +806,10 @@ const loadCategoriesList = async (): Promise => { data.data.length > 0 ) { categoriesList.value = data.data.map( - (item: { id: number; name: string; type?: string }, index: number) => ({ + ( + item: { id: number; name: string; type?: string }, + index: number, + ) => ({ id: typeof item.id === "number" ? item.id : index + 1, name: item.name || "", value: item.type || String(item.id), @@ -718,7 +822,10 @@ const loadCategoriesList = async (): Promise => { ); } else if (data.code === 0 && Array.isArray(data)) { categoriesList.value = data.map( - (item: { id: number; name: string; type?: string }, index: number) => ({ + ( + item: { id: number; name: string; type?: string }, + index: number, + ) => ({ id: typeof item.id === "number" ? item.id : index + 1, name: item.name || "", value: item.type || String(item.id), @@ -1166,6 +1273,7 @@ const selectArch = async (arch: HistoryArchInfo) => { if (arch.icon) { formData.iconPath = `${baseUrl}/icon.png`; iconPreview.value = formData.iconPath; + iconFileName.value = "icon.png"; console.log("[Submitter] Icon URL:", formData.iconPath); console.log("[Submitter] Icon preview set:", iconPreview.value); } @@ -1192,6 +1300,7 @@ const handleIconFileSelect = (event: Event) => { const target = event.target as HTMLInputElement; const file = target.files?.[0]; if (file) { + iconFileName.value = file.name; const reader = new FileReader(); reader.onload = (e) => { const dataUrl = e.target?.result as string; @@ -1211,6 +1320,7 @@ const handleIconDrop = (event: DragEvent) => { file.name.endsWith(".jpg") || file.name.endsWith(".jpeg")) ) { + iconFileName.value = file.name; const reader = new FileReader(); reader.onload = (e) => { const dataUrl = e.target?.result as string; @@ -1275,6 +1385,22 @@ const resetForm = () => { packageSuccess.value = false; packageError.value = ""; packageResult.value = null; + iconFileName.value = ""; + iconPreview.value = ""; + uploadProgress.value = 0; + uploadStage.value = ""; + uploadStageMessage.value = ""; + uploadStages.value = []; +}; + +const closeSubmitSuccessModal = () => { + showSubmitSuccessModal.value = false; + resetForm(); +}; + +const resetFormAndClose = () => { + showSubmitSuccessModal.value = false; + resetForm(); }; const submitForm = async () => { @@ -1284,6 +1410,45 @@ const submitForm = async () => { submitSuccess.value = false; submitError.value = ""; + uploadProgress.value = 0; + uploadStage.value = "准备提交..."; + uploadStageMessage.value = "正在准备..."; + uploadStages.value = [ + { stage: "icon", label: "上传图标", progress: 0 }, + { stage: "screenshots", label: "上传截图", progress: 0 }, + { stage: "deb", label: "上传安装包", progress: 0 }, + { stage: "submit", label: "提交信息", progress: 0 }, + ]; + + const progressListener = ( + _event: unknown, + data: { step: string; progress: number; message: string }, + ) => { + let stageIndex: number; + if (data.step.startsWith("screenshot")) { + stageIndex = uploadStages.value.findIndex( + (s) => s.stage === "screenshots", + ); + } else { + stageIndex = uploadStages.value.findIndex((s) => s.stage === data.step); + } + if (stageIndex !== -1) { + uploadStages.value[stageIndex].progress = Math.max( + uploadStages.value[stageIndex].progress, + data.progress, + ); + } + if (data.progress >= 100 && !data.step.startsWith("screenshot")) { + uploadProgress.value = data.progress; + } else { + uploadProgress.value = Math.max(uploadProgress.value, data.progress); + } + uploadStage.value = data.message; + uploadStageMessage.value = data.message; + }; + + window.ipcRenderer.on("submit-upload-progress", progressListener); + try { const submitData = { name: formData.name, @@ -1318,14 +1483,19 @@ const submitForm = async () => { const result = await window.ipcRenderer.invoke("submit-app", submitData); if (result?.success) { + uploadStages.value.forEach((s) => (s.progress = 100)); + uploadProgress.value = 100; + uploadStage.value = "提交完成"; + uploadStageMessage.value = "投稿提交成功!"; submitSuccess.value = true; - resetForm(); + showSubmitSuccessModal.value = true; } else { submitError.value = result?.message || "提交失败"; } } catch (error) { submitError.value = (error as Error)?.message || "提交失败"; } finally { + window.ipcRenderer.off("submit-upload-progress", progressListener); isSubmitting.value = false; } }; @@ -1343,6 +1513,35 @@ const packageApp = async (storeArch: string) => { packageError.value = ""; packageResult.value = null; + uploadProgress.value = 0; + uploadStage.value = "准备打包..."; + uploadStageMessage.value = "正在准备..."; + uploadStages.value = [ + { stage: "init", label: "创建临时目录", progress: 0 }, + { stage: "icon", label: "处理图标", progress: 0 }, + { stage: "screenshots", label: "处理截图", progress: 0 }, + { stage: "deb", label: "复制安装包", progress: 0 }, + { stage: "json", label: "生成配置文件", progress: 0 }, + { stage: "tar", label: "打包压缩", progress: 0 }, + ]; + + const progressListener = ( + _event: unknown, + data: { step: string; progress: number; message: string }, + ) => { + const stageIndex = uploadStages.value.findIndex( + (s) => s.stage === data.step, + ); + if (stageIndex !== -1) { + uploadStages.value[stageIndex].progress = data.progress; + } + uploadProgress.value = data.progress; + uploadStage.value = data.step; + uploadStageMessage.value = data.message; + }; + + window.ipcRenderer.on("package-progress", progressListener); + try { const packageData = { name: formData.name, @@ -1372,6 +1571,9 @@ const packageApp = async (storeArch: string) => { const result = await window.ipcRenderer.invoke("package-app", packageData); if (result?.success) { + uploadStages.value.forEach((s) => (s.progress = 100)); + uploadProgress.value = 100; + uploadStageMessage.value = "打包完成!"; packageSuccess.value = true; packageResult.value = result.data; } else { @@ -1380,6 +1582,7 @@ const packageApp = async (storeArch: string) => { } catch (error) { packageError.value = (error as Error)?.message || "打包失败"; } finally { + window.ipcRenderer.off("package-progress", progressListener); isPackaging.value = false; } };