diff --git a/app/__pycache__/ali_oss.cpython-312.pyc b/app/__pycache__/ali_oss.cpython-312.pyc new file mode 100644 index 0000000..27c3daf Binary files /dev/null and b/app/__pycache__/ali_oss.cpython-312.pyc differ diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..38a623b Binary files /dev/null and b/app/__pycache__/main.cpython-312.pyc differ diff --git a/app/blueprints/__pycache__/brand.cpython-312.pyc b/app/blueprints/__pycache__/brand.cpython-312.pyc index 05adae3..adf7c02 100644 Binary files a/app/blueprints/__pycache__/brand.cpython-312.pyc and b/app/blueprints/__pycache__/brand.cpython-312.pyc differ 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 }) }