From 923dfd249584079c3671e88ebc4893eec75a81c9 Mon Sep 17 00:00:00 2001 From: super <2903208875@qq.com> Date: Sun, 22 Mar 2026 12:45:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=89=80=E6=9C=89=E5=86=85?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__pycache__/ali_oss.cpython-312.pyc | Bin 0 -> 3937 bytes app/__pycache__/main.cpython-312.pyc | Bin 0 -> 18363 bytes .../__pycache__/brand.cpython-312.pyc | Bin 48086 -> 49754 bytes app/blueprints/brand.py | 28 +++++++++++++++++- app/main.py | 3 +- .../brand/components/BrandConvertTab.vue | 4 +-- .../pages/brand/components/BrandDedupeTab.vue | 4 +-- .../pages/brand/components/BrandSplitTab.vue | 4 +-- frontend-vue/src/shared/api/brand.ts | 4 +++ 9 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 app/__pycache__/ali_oss.cpython-312.pyc create mode 100644 app/__pycache__/main.cpython-312.pyc diff --git a/app/__pycache__/ali_oss.cpython-312.pyc b/app/__pycache__/ali_oss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27c3daf17b1b8be6a6995e1961b2869d77cae27f GIT binary patch literal 3937 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 literal 0 HcmV?d00001 diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38a623b779fb65bddf00551abd8ec16a81433775 GIT binary patch literal 18363 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 LKeB9HhWUR3K;#J= literal 0 HcmV?d00001 diff --git a/app/blueprints/__pycache__/brand.cpython-312.pyc b/app/blueprints/__pycache__/brand.cpython-312.pyc index 05adae356dc465925a9b554b48c85edbdd20ec3a..adf7c02b0aa2e7208b0942721c7234377b037c87 100644 GIT binary patch delta 5656 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 HWJUXLPfUMB diff --git a/app/blueprints/brand.py b/app/blueprints/brand.py index 1bf2a65..e611f60 100644 --- a/app/blueprints/brand.py +++ b/app/blueprints/brand.py @@ -57,6 +57,17 @@ def _push_task_event(task_id, event): def _expand_folder_xlsx(folder_path): + """返回文件夹下第一层 .xlsx 文件的绝对路径列表(保留旧逻辑,不再用于品牌工具文件夹上传)""" + if not folder_path or not os.path.isdir(folder_path): + return [] + paths = [] + for name in os.listdir(folder_path): + if name.endswith('.xlsx') or name.endswith('.XLSX'): + paths.append(os.path.normpath(os.path.join(folder_path, name))) + return paths + + +def _expand_folder_xlsx_recursive_items(folder_path): """返回文件夹下所有 .xlsx 文件的绝对路径和相对路径列表(递归)""" if not folder_path or not os.path.isdir(folder_path): return [] @@ -487,7 +498,22 @@ def api_brand_expand_folder(): folder = (data.get('folder') or '').strip() if not folder: return jsonify({'success': False, 'error': '请提供 folder 路径'}), 400 - items = _expand_folder_xlsx(folder) + paths = _expand_folder_xlsx(folder) + return jsonify({'success': True, 'paths': paths}) + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + + +@brand_bp.route('/api/brand/expand-folder-recursive', methods=['POST']) +@login_required +def api_brand_expand_folder_recursive(): + """递归展开文件夹,返回 .xlsx 文件绝对路径及相对路径列表""" + try: + data = request.get_json() or {} + folder = (data.get('folder') or '').strip() + if not folder: + return jsonify({'success': False, 'error': '请提供 folder 路径'}), 400 + items = _expand_folder_xlsx_recursive_items(folder) return jsonify({'success': True, 'items': items}) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 diff --git a/app/main.py b/app/main.py index 9287038..68e7e47 100644 --- a/app/main.py +++ b/app/main.py @@ -211,7 +211,7 @@ class WindowAPI: except Exception as e: return {'success': False, 'error': str(e)} - def upload_file_to_java(self, file_path): + def upload_file_to_java(self, file_path, relative_path=None): """按本地路径读取文件并上传到 Java 后端临时目录。""" if not file_path or not str(file_path).strip(): return {'success': False, 'error': '文件路径为空'} @@ -223,6 +223,7 @@ class WindowAPI: response = requests.post( f"{JAVA_API_BASE}/api/files/upload", files={'file': (os.path.basename(normalized_path), file_obj)}, + data={'relativePath': str(relative_path).strip()} if relative_path else None, timeout=120, ) response.raise_for_status() diff --git a/frontend-vue/src/pages/brand/components/BrandConvertTab.vue b/frontend-vue/src/pages/brand/components/BrandConvertTab.vue index 3fd8a4c..5172082 100644 --- a/frontend-vue/src/pages/brand/components/BrandConvertTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandConvertTab.vue @@ -173,7 +173,7 @@ import { computed, onMounted, ref } from 'vue' import { ElMessage } from 'element-plus' import BrandTopBar from './BrandTopBar.vue' -import { expandBrandFolder, getBrandTemplateXlsxUrl, getBrandTemplateZipUrl } from '@/shared/api/brand' +import { expandBrandFolderRecursive, getBrandTemplateXlsxUrl, getBrandTemplateZipUrl } from '@/shared/api/brand' import type { BrandExpandFolderItem } from '@/shared/api/brand' import { deleteConvertHistory, @@ -288,7 +288,7 @@ async function selectConvertFolder() { if (!folder) return convertArchiveName.value = folder.split(/[/\\]/).filter(Boolean).pop() || '' - const result = await expandBrandFolder(folder) + const result = await expandBrandFolderRecursive(folder) if (result.success && result.items?.length) { convertSelectedPaths.value = result.items.map((item) => item.relativePath) convertUploadedFiles.value = await uploadPathsToJava(result.items) diff --git a/frontend-vue/src/pages/brand/components/BrandDedupeTab.vue b/frontend-vue/src/pages/brand/components/BrandDedupeTab.vue index bbafb93..a6cc2cd 100644 --- a/frontend-vue/src/pages/brand/components/BrandDedupeTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandDedupeTab.vue @@ -147,7 +147,7 @@ import { computed, onMounted, ref } from 'vue' import { ElMessage } from 'element-plus' import BrandTopBar from './BrandTopBar.vue' -import { expandBrandFolder, type BrandExpandFolderItem } from '@/shared/api/brand' +import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand' import { deleteDedupeHistory, getDedupeHistory, getExcelInfo, runDedupe, type DedupeResultItem, type DedupeRunVo } from '@/shared/api/java-modules' import { getPywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview' @@ -247,7 +247,7 @@ async function selectCleanFolder() { if (!folder) return cleanArchiveName.value = folder.split(/[/\\]/).filter(Boolean).pop() || '' - const result = await expandBrandFolder(folder) + const result = await expandBrandFolderRecursive(folder) if (result.success && result.items?.length) { cleanSelectedPaths.value = result.items.map((item) => item.relativePath) cleanUploadedFiles.value = await uploadPathsToJava(result.items) diff --git a/frontend-vue/src/pages/brand/components/BrandSplitTab.vue b/frontend-vue/src/pages/brand/components/BrandSplitTab.vue index 903fa2b..641a63e 100644 --- a/frontend-vue/src/pages/brand/components/BrandSplitTab.vue +++ b/frontend-vue/src/pages/brand/components/BrandSplitTab.vue @@ -172,7 +172,7 @@ import { computed, onMounted, ref } from 'vue' import { ElMessage } from 'element-plus' import BrandTopBar from './BrandTopBar.vue' -import { expandBrandFolder, type BrandExpandFolderItem } from '@/shared/api/brand' +import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand' import { deleteSplitHistory, getExcelInfo, getSplitHistory, runSplit, type SplitResultItem, type SplitRunVo } from '@/shared/api/java-modules' import { getPywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview' @@ -262,7 +262,7 @@ async function selectSplitFolder() { if (!folder) return splitArchiveName.value = folder.split(/[/\\]/).filter(Boolean).pop() || '' - const result = await expandBrandFolder(folder) + const result = await expandBrandFolderRecursive(folder) if (result.success && result.items?.length) { splitSelectedPaths.value = result.items.map((item) => item.relativePath) splitUploadedFiles.value = await uploadPathsToJava(result.items) diff --git a/frontend-vue/src/shared/api/brand.ts b/frontend-vue/src/shared/api/brand.ts index 2db60f6..12a0288 100644 --- a/frontend-vue/src/shared/api/brand.ts +++ b/frontend-vue/src/shared/api/brand.ts @@ -66,6 +66,10 @@ export function expandBrandFolder(folder: string) { return requestPostJson(`${API_PREFIX}/api/brand/expand-folder`, { folder }) } +export function expandBrandFolderRecursive(folder: string) { + return requestPostJson(`${API_PREFIX}/api/brand/expand-folder-recursive`, { folder }) +} + export function runBrandNow(paths: string[], strategy: string) { return requestPostJson(`${API_PREFIX}/api/brand/run`, { paths, strategy }) }