@@ -123,21 +123,18 @@ import {
cancelBrandTask ,
createBrandTask ,
deleteBrandTask ,
expandBrandFolder ,
getBrandTaskDownloadUrl ,
getBrandTasks ,
runBrandNow ,
type BrandTaskItem ,
} from '@/shared/api/brand'
import { checkSelectedFiles , EXCEL _EXTENSIONS } from '@/shared/dispatch-guard.ts'
import { passGuard } from '@/shared/dispatch-guard-ui'
import type { UploadFileVo } from '@/shared/api/upload.ts'
import type { UploadedFileRef } from '@/shared/api/types/upload.ts'
import type { BrandExpandFolderItem } from '@/shared/api/types/modules/brand'
/** 扩展文件夹接口响应(向后端真实字段 paths 兼容,shared 类型仅声明 items 时以本接口为准) */
interface BrandExpandFolderPathsResponse {
success : boolean
paths ? : string [ ]
error ? : string
}
const selectedPaths = ref < string [ ] > ( [ ] )
const uploadedFiles = ref < UploadFileVo [ ] > ( [ ] )
const strategy = ref < 'Terms' | 'Simple' > ( 'Terms' )
const runMode = ref < 'immediate' | 'queue' > ( 'immediate' )
const submitting = ref ( false )
@@ -151,6 +148,10 @@ const hasUid = computed(() => {
const numeric = Number ( raw . trim ( ) )
return Number . isFinite ( numeric ) && numeric > 0
} )
// 展示用:已上传文件列表的展示名(优先相对路径,其次原文件名)
const selectedPaths = computed ( ( ) =>
uploadedFiles . value . map ( ( f ) => f . relativePath || f . originalFilename || f . fileKey ) ,
)
const displayPaths = computed ( ( ) => selectedPaths . value . slice ( 0 , 8 ) )
function baseName ( path : string ) {
@@ -275,10 +276,23 @@ function formatDateTime(value?: string) {
return ` ${ year } - ${ month } - ${ day } ${ hours } : ${ minutes } : ${ seconds } `
}
function normalizeSelected ( paths : string [ ] ) {
selectedPaths . value = paths
. filter ( ( path ) => / \ . xlsx$ / i . test ( path ) )
. filter ( ( path , index , array ) => array . indexOf ( path ) === index )
/** 上传本地 xlsx 到 Java 临时目录,成功后回填 fileUrl(服务器本地路径,Java 直接读取) */
async function uploadBrandPathsToJava ( paths : Array < string | BrandExpandFolderItem > ) {
const api = getPywebviewApi ( )
if ( ! api ? . upload _file _to _java ) {
throw new Error ( '当前环境未提供文件上传能力' )
}
const files : UploadFileVo [ ] = [ ]
for ( const item of paths ) {
const filePath = typeof item === 'string' ? item : item . absolutePath
const relativePath = typeof item === 'string' ? undefined : item . relativePath
const uploaded = await api . upload _file _to _java ( filePath , relativePath )
if ( ! uploaded ? . success || ! uploaded . data ) {
throw new Error ( uploaded ? . error || uploaded ? . message || ` 上传失败: ${ filePath } ` )
}
files . push ( uploaded . data )
}
return files
}
async function selectFiles ( ) {
@@ -287,13 +301,15 @@ async function selectFiles() {
ElMessage . warning ( '当前环境不支持文件选择,请在本机客户端中打开' )
return
}
const paths = await bridge . select _brand _xlsx _files ( )
if ( ! paths ? . length ) return
if ( ! ( await passGuard ( checkSelectedFiles ( paths , { allowedExtensions : EXCEL _EXTENSIONS } ) ) ) ) return
try {
const paths = await bridge . select _brand _xlsx _files ( )
if ( ! paths ? . length ) return
normalizeSelected ( paths )
ElMessage . success ( ` 已选择 ${ selectedPaths . value . length } 个 Excel 文件 ` )
const files = await uploadBrandPathsToJava ( paths )
uploadedFiles . value = files
ElMessage . success ( ` 已选择并上传 ${ files . length } 个 Excel 文件 ` )
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '文件选择 失败' )
ElMessage . error ( error instanceof Error ? error . message : '文件上传 失败' )
}
}
@@ -303,16 +319,23 @@ async function selectFolder() {
ElMessage . warning ( '当前环境不支持文件夹选择,请在本机客户端中打开' )
return
}
const folder = await bridge . select _brand _folder ( )
if ( ! folder ) return
// 新版桌面客户端桥在本机展开文件夹(服务器无法访问用户本机目录)
if ( ! bridge . expand _brand _folder ) {
ElMessage . warning ( '当前桌面客户端版本不支持文件夹展开,请使用"选择文件"(可多选)' )
return
}
try {
const folder = await bridge . select _brand _folder ( )
if ( ! folder ) return
const res = ( await expandBrandFolder ( folder ) ) as BrandExpandFolderPathsResponse
if ( ! res . success || ! res . paths ? . length ) {
const res = await bridge . expand _brand _folder ( folder )
if ( ! res . success || ! res . items ? . length ) {
ElMessage . warning ( res . error || '该文件夹下没有 xlsx 文件' )
return
}
normalizeSelected ( res . paths )
ElMessage . success ( ` 已选择文件夹内 ${ selectedPaths . value . length } 个 Excel 文件 ` )
if ( ! ( await passGuard ( checkSelectedFiles ( res . items . map ( ( i ) => i . absolutePath ) , { allowedExtensions : EXCEL _EXTENSIONS } ) ) ) ) return
const files = await uploadBrandPathsToJava ( res . items )
uploadedFiles . value = files
ElMessage . success ( ` 已选择并上传文件夹内 ${ files . length } 个 Excel 文件 ` )
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '文件夹选择失败' )
}
@@ -327,28 +350,35 @@ async function submitRun() {
ElMessage . warning ( '未获取到登录用户,请先在本机客户端登录后再试' )
return
}
if ( ! selectedPaths . value . length ) {
if ( ! uploadedFiles . value . length ) {
ElMessage . warning ( '请先选择待检 Excel 文件' )
return
}
submitting . value = true
try {
// 上传后以服务器可读的 localPath 作为 fileUrl( Java resolveSourceFile 直接读临时文件)
const files : UploadedFileRef [ ] = uploadedFiles . value . map ( ( f ) => ( {
fileKey : f . fileKey ,
originalFilename : f . originalFilename ,
relativePath : f . relativePath ,
fileUrl : f . localPath ,
} ) )
if ( runMode . value === 'immediate' ) {
const res = await runBrandNow ( selectedPaths . value , strategy . value )
const res = await runBrandNow ( files , strategy . value )
if ( res . success && res . task _id ) {
ElMessage . success ( ` 已创建任务 ${ res . task _id } ,客户端开始检测 ` )
} else {
throw new Error ( ( res as { error ? : string } ) . error || '运行失败' )
}
} else {
const res = await createBrandTask ( selectedPaths . value , strategy . value )
const res = await createBrandTask ( files , strategy . value )
if ( res . success && res . task _id ) {
ElMessage . success ( ` 任务 ${ res . task _id } 已添加到队列 ` )
} else {
throw new Error ( ( res as { error ? : string } ) . error || '添加失败' )
}
}
selectedPaths . value = [ ]
uploadedFiles . value = [ ]
await loadTasks ( )
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '提交失败' )