Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-csf-front
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
yuzhenWang
yd-csf-front
Commits
b32db88e
Commit
b32db88e
authored
May 13, 2026
by
yuzhenWang
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'wyz' into 'test'
修改预约附件可预览文件 See merge request
!142
parents
a0e22287
c53ccfbd
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
168 additions
and
75 deletions
+168
-75
src/views/sign/appointment/components/fileUpload.vue
+168
-75
No files found.
src/views/sign/appointment/components/fileUpload.vue
View file @
b32db88e
...
@@ -2,21 +2,14 @@
...
@@ -2,21 +2,14 @@
<div
class=
"uploadContainer"
>
<div
class=
"uploadContainer"
>
<CardOne
title=
"材料信息"
>
<CardOne
title=
"材料信息"
>
<template
#
headerRight
>
<template
#
headerRight
>
<div
style=
"margin-top: 20px;"
>
<div
style=
"margin-top: 20px"
>
<el-button
<el-button
type=
"primary"
:loading=
"downloading"
@
click=
"handleBatchDownloadSelected"
>
type=
"primary"
:loading=
"downloading"
@
click=
"handleBatchDownloadSelected"
>
{{
downloading
?
'正在打包中...'
:
'下载材料包'
}}
{{
downloading
?
'正在打包中...'
:
'下载材料包'
}}
</el-button>
</el-button>
<div
v-if=
"downloading"
style=
"margin-top: 10px;"
>
<div
v-if=
"downloading"
style=
"margin-top: 10px"
>
<el-progress
<el-progress
:percentage=
"progressPercentage"
:format=
"progressFormat"
/>
:percentage=
"progressPercentage"
<div
style=
"font-size: 12px; color: #666; margin-top: 5px"
>
:format=
"progressFormat"
/>
<div
style=
"font-size: 12px; color: #666; margin-top: 5px;"
>
已处理文件:
{{
currentCount
}}
/
{{
totalCount
}}
已处理文件:
{{
currentCount
}}
/
{{
totalCount
}}
</div>
</div>
</div>
</div>
...
@@ -120,7 +113,7 @@
...
@@ -120,7 +113,7 @@
</div>
</div>
</el-upload>
</el-upload>
</div>
</div>
<div
class=
"tip"
>
(支持
Word,Excel,
PDF,图片格式)
</div>
<div
class=
"tip"
>
(支持PDF,图片格式)
</div>
</div>
</div>
<div
class=
"dialogRight"
>
<div
class=
"dialogRight"
>
<div
<div
...
@@ -136,14 +129,55 @@
...
@@ -136,14 +129,55 @@
class=
"uploaded-file-item"
class=
"uploaded-file-item"
>
>
<div
class=
"fileName"
>
{{ file.originalName }}
</div>
<div
class=
"fileName"
>
{{ file.originalName }}
</div>
<el-icon
color=
"red"
size=
"18"
@
click=
"removeUploadedFile(file, index)"
<div>
<el-button
type=
"primary"
size=
"small"
link
@
click=
"previewFile(file)"
>
查看
</el-button>
<el-button
type=
"danger"
size=
"small"
link
@
click=
"removeUploadedFile(file, index)"
>
删除
</el-button>
</div>
<!-- <el-icon color="red" size="18" @click="removeUploadedFile(file, index)"
><Delete
><Delete
/></el-icon>
/></el-icon>
-->
</div>
</div>
</el-scrollbar>
</el-scrollbar>
</div>
</div>
</div>
</div>
</CommonDialog>
</CommonDialog>
<!-- 文件预览弹窗(页面内查看,不打开新窗口) -->
<el-dialog
v-model=
"previewDialogVisible"
:title=
"previewFileName"
width=
"70%"
:close-on-click-modal=
"false"
destroy-on-close
@
close=
"previewUrl = ''"
>
<div
class=
"preview-container"
>
<!-- 图片预览 -->
<div
v-if=
"previewFileType === 'image'"
class=
"preview-image-wrapper"
>
<img
:src=
"previewUrl"
class=
"preview-image"
alt=
"预览图片"
/>
</div>
<!-- PDF 预览(使用 iframe 内嵌) -->
<iframe
v-else-if=
"previewFileType === 'pdf'"
:src=
"previewUrl"
class=
"preview-pdf"
frameborder=
"0"
></iframe>
<!-- 不支持预览的文件类型 -->
<div
v-else-if=
"previewFileType === 'unsupported'"
class=
"preview-unsupported"
>
<el-icon
:size=
"48"
color=
"#909399"
><Document
/></el-icon>
<p>
暂不支持预览此类型文件
</p>
<el-button
type=
"primary"
@
click=
"previewDialogVisible = false"
>
关闭
</el-button>
</div>
</div>
</el-dialog>
<el-dialog
v-model=
"imageViewerVisible"
title=
"图片预览"
width=
"60%"
>
<el-dialog
v-model=
"imageViewerVisible"
title=
"图片预览"
width=
"60%"
>
<div
style=
"text-align: center"
>
<div
style=
"text-align: center"
>
<el-image
:src=
"imageUrl"
fit=
"contain"
/>
<el-image
:src=
"imageUrl"
fit=
"contain"
/>
...
@@ -153,9 +187,9 @@
...
@@ -153,9 +187,9 @@
</template>
</template>
<
script
setup
name=
"FileUpload"
>
<
script
setup
name=
"FileUpload"
>
import
{
ref
}
from
'vue'
;
import
{
ref
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
;
import
{
ElMessage
}
from
'element-plus'
import
{
downloadFilesAsZip
}
from
'@/utils/zipDownload'
;
// 引入刚才封装的工具
import
{
downloadFilesAsZip
}
from
'@/utils/zipDownload'
// 引入刚才封装的工具
import
CommonDialog
from
'@/components/commonDialog'
import
CommonDialog
from
'@/components/commonDialog'
import
CardOne
from
'@/components/formCard/cardOne'
import
CardOne
from
'@/components/formCard/cardOne'
import
{
getToken
}
from
'@/utils/auth'
import
{
getToken
}
from
'@/utils/auth'
...
@@ -188,6 +222,40 @@ const limit = ref(10)
...
@@ -188,6 +222,40 @@ const limit = ref(10)
const
fileSize
=
ref
(
10
)
const
fileSize
=
ref
(
10
)
const
headers
=
ref
({
Authorization
:
'Bearer '
+
getToken
()
})
const
headers
=
ref
({
Authorization
:
'Bearer '
+
getToken
()
})
const
uploadImgUrl
=
ref
(
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
)
// 上传的服务器地址
const
uploadImgUrl
=
ref
(
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
)
// 上传的服务器地址
// ==================== 文件预览弹窗 ====================
const
previewDialogVisible
=
ref
(
false
)
const
previewUrl
=
ref
(
''
)
const
previewFileName
=
ref
(
''
)
const
previewFileType
=
ref
(
''
)
// 'image', 'pdf', 'unsupported'
// 预览文件(页面内弹窗,不打开新窗口)
function
previewFile
(
file
)
{
const
url
=
file
.
url
||
file
.
fileUrl
if
(
!
url
)
{
ElMessage
.
warning
(
'文件地址不存在'
)
return
}
const
ext
=
(
file
.
originalName
||
''
).
split
(
'.'
).
pop
().
toLowerCase
()
previewUrl
.
value
=
url
previewFileName
.
value
=
file
.
originalName
||
'文件'
if
([
'jpg'
,
'jpeg'
,
'png'
,
'webp'
,
'gif'
,
'bmp'
,
'svg'
].
includes
(
ext
))
{
previewFileType
.
value
=
'image'
previewDialogVisible
.
value
=
true
}
else
if
(
ext
===
'pdf'
)
{
previewFileType
.
value
=
'pdf'
previewDialogVisible
.
value
=
true
}
else
{
// 不支持预览的文件类型,弹窗显示提示
previewFileType
.
value
=
'unsupported'
previewDialogVisible
.
value
=
true
}
console
.
log
(
'===================================='
)
console
.
log
(
'previewUrl.value'
,
previewUrl
.
value
)
console
.
log
(
'previewFileName.value'
,
previewFileName
.
value
)
console
.
log
(
'previewFileType.value'
,
previewFileType
.
value
)
console
.
log
(
'===================================='
)
}
// 图片查看相关状态
// 图片查看相关状态
const
imageViewerVisible
=
ref
(
false
)
const
imageViewerVisible
=
ref
(
false
)
const
imageUrl
=
ref
(
''
)
const
imageUrl
=
ref
(
''
)
...
@@ -201,35 +269,39 @@ const data = reactive({
...
@@ -201,35 +269,39 @@ const data = reactive({
// 下载材料包
// 下载材料包
// 状态管理
// 状态管理
const
downloading
=
ref
(
false
);
const
downloading
=
ref
(
false
)
const
currentCount
=
ref
(
0
);
const
currentCount
=
ref
(
0
)
const
totalCount
=
ref
(
0
);
const
totalCount
=
ref
(
0
)
const
viewFile
=
file
=>
{
console
.
log
(
'===================================='
)
console
.
log
(
'file'
,
file
)
console
.
log
(
'===================================='
)
}
const
progressPercentage
=
computed
(()
=>
{
const
progressPercentage
=
computed
(()
=>
{
if
(
totalCount
.
value
===
0
)
return
0
;
if
(
totalCount
.
value
===
0
)
return
0
return
Math
.
floor
((
currentCount
.
value
/
totalCount
.
value
)
*
100
)
;
return
Math
.
floor
((
currentCount
.
value
/
totalCount
.
value
)
*
100
)
})
;
})
const
progressFormat
=
(
percentage
)
=>
`
${
currentCount
.
value
}
/
${
totalCount
.
value
}
`
;
const
progressFormat
=
percentage
=>
`
${
currentCount
.
value
}
/
${
totalCount
.
value
}
`
// 2. 核心处理方法
// 2. 核心处理方法
const
handleBatchDownloadSelected
=
async
()
=>
{
const
handleBatchDownloadSelected
=
async
()
=>
{
let
apiMaterialDtoList
=
[]
let
apiMaterialDtoList
=
[]
if
(
!
props
.
idsObj
.
appointmentBizId
)
{
if
(
!
props
.
idsObj
.
appointmentBizId
)
{
apiMaterialDtoList
=
fileTableList
.
value
.
map
(
item
=>
{
apiMaterialDtoList
=
fileTableList
.
value
.
map
(
item
=>
{
return
{
return
{
dataPerson
:
item
.
dataPerson
,
//资料人(字典)
dataPerson
:
item
.
dataPerson
,
//资料人(字典)
dataPersonName
:
item
.
dataPersonName
,
//资料人(字典)
dataPersonName
:
item
.
dataPersonName
,
//资料人(字典)
dataType
:
item
.
dataType
,
//资料类型(字典)
dataType
:
item
.
dataType
,
//资料类型(字典)
dataTypeName
:
item
.
dataTypeName
,
//资料类型(字典)
dataTypeName
:
item
.
dataTypeName
,
//资料类型(字典)
fileUrlList
:
item
.
fileBizIdList
.
map
(
item2
=>
({
fileUrlList
:
item
.
fileBizIdList
.
map
(
item2
=>
({
fileUrl
:
item2
.
url
,
fileUrl
:
item2
.
url
,
fileName
:
item2
.
originalName
fileName
:
item2
.
originalName
}))
}))
}
}
})
})
}
else
{
}
else
{
apiMaterialDtoList
=
fileTableList
.
value
.
map
(
item
=>
{
apiMaterialDtoList
=
fileTableList
.
value
.
map
(
item
=>
{
return
{
return
{
dataPerson
:
item
.
dataPerson
,
//资料人(字典)
dataPerson
:
item
.
dataPerson
,
//资料人(字典)
dataPersonName
:
item
.
dataPersonName
,
//资料人(字典)
dataPersonName
:
item
.
dataPersonName
,
//资料人(字典)
...
@@ -238,85 +310,80 @@ if (!props.idsObj.appointmentBizId) {
...
@@ -238,85 +310,80 @@ if (!props.idsObj.appointmentBizId) {
fileUrlList
:
item
.
fileUrlList
fileUrlList
:
item
.
fileUrlList
}
}
})
})
}
}
if
(
!
apiMaterialDtoList
||
apiMaterialDtoList
.
length
===
0
)
{
if
(
!
apiMaterialDtoList
||
apiMaterialDtoList
.
length
===
0
)
{
ElMessage
.
warning
(
'没有要下载的材料'
)
;
ElMessage
.
warning
(
'没有要下载的材料'
)
return
;
return
}
}
// --- 步骤 1: 数据清洗与扁平化 ---
// --- 步骤 1: 数据清洗与扁平化 ---
const
flatFileList
=
[]
;
const
flatFileList
=
[]
let
hasFiles
=
false
;
let
hasFiles
=
false
apiMaterialDtoList
.
forEach
((
item
,
index
)
=>
{
apiMaterialDtoList
.
forEach
((
item
,
index
)
=>
{
// 安全检查:确保 fileUrlList 存在且是数组
// 安全检查:确保 fileUrlList 存在且是数组
const
urls
=
item
.
fileUrlList
;
const
urls
=
item
.
fileUrlList
if
(
!
urls
||
!
Array
.
isArray
(
urls
)
||
urls
.
length
===
0
)
{
if
(
!
urls
||
!
Array
.
isArray
(
urls
)
||
urls
.
length
===
0
)
{
return
;
// 跳过没有文件的行
return
// 跳过没有文件的行
}
}
hasFiles
=
true
;
hasFiles
=
true
// 生成安全的业务前缀
// 生成安全的业务前缀
// 规则:[人员类型]_[资料类型]_[业务ID]
// 规则:[人员类型]_[资料类型]_[业务ID]
// 例如:POLICYHOLDER_FRONT_2216
// 例如:POLICYHOLDER_FRONT_2216
const
safePerson
=
(
item
.
dataPersonName
||
'UNKNOWN'
).
replace
(
/
[\/\\
:*?"<>|
]
/g
,
'_'
)
;
const
safePerson
=
(
item
.
dataPersonName
||
'UNKNOWN'
).
replace
(
/
[\/\\
:*?"<>|
]
/g
,
'_'
)
const
safeType
=
(
item
.
dataTypeName
||
'FILE'
).
replace
(
/
[\/\\
:*?"<>|
]
/g
,
'_'
)
;
const
safeType
=
(
item
.
dataTypeName
||
'FILE'
).
replace
(
/
[\/\\
:*?"<>|
]
/g
,
'_'
)
const
filePrefix
=
`
${
safePerson
}
_
${
safeType
}
`
;
const
filePrefix
=
`
${
safePerson
}
_
${
safeType
}
`
urls
.
forEach
((
fileItem
,
fIndex
)
=>
{
urls
.
forEach
((
fileItem
,
fIndex
)
=>
{
// 兼容 fileUrlList 可能是字符串数组 或 对象数组
// 兼容 fileUrlList 可能是字符串数组 或 对象数组
let
fileUrl
=
fileItem
.
fileUrl
;
let
fileUrl
=
fileItem
.
fileUrl
let
originalFileName
=
fileItem
.
fileName
;
let
originalFileName
=
fileItem
.
fileName
// --- 关键:构建最终文件名 ---
// --- 关键:构建最终文件名 ---
const
finalFileName
=
`
${
filePrefix
}
_
${
originalFileName
}
`
;
const
finalFileName
=
`
${
filePrefix
}
_
${
originalFileName
}
`
if
(
fileUrl
)
{
if
(
fileUrl
)
{
flatFileList
.
push
({
flatFileList
.
push
({
url
:
fileUrl
,
url
:
fileUrl
,
name
:
finalFileName
,
name
:
finalFileName
,
// 可选:保留元数据用于调试
// 可选:保留元数据用于调试
_meta
:
{
_meta
:
{
type
:
item
.
dataType
,
type
:
item
.
dataType
,
note
:
item
.
precautions
note
:
item
.
precautions
}
}
})
;
})
}
}
})
;
})
})
;
})
if
(
!
hasFiles
)
{
if
(
!
hasFiles
)
{
ElMessage
.
warning
(
'选中的项中没有包含任何附件'
)
;
ElMessage
.
warning
(
'选中的项中没有包含任何附件'
)
return
;
return
}
}
// --- 步骤 2: 执行下载 ---
// --- 步骤 2: 执行下载 ---
totalCount
.
value
=
flatFileList
.
length
;
totalCount
.
value
=
flatFileList
.
length
currentCount
.
value
=
0
;
currentCount
.
value
=
0
downloading
.
value
=
true
;
downloading
.
value
=
true
try
{
try
{
const
zipName
=
`预约附件包_
${
new
Date
().
toISOString
().
slice
(
0
,
10
)}
.zip`
;
const
zipName
=
`预约附件包_
${
new
Date
().
toISOString
().
slice
(
0
,
10
)}
.zip`
await
downloadFilesAsZip
(
flatFileList
,
zipName
,
(
current
,
total
)
=>
{
await
downloadFilesAsZip
(
flatFileList
,
zipName
,
(
current
,
total
)
=>
{
currentCount
.
value
=
current
;
currentCount
.
value
=
current
})
;
})
ElMessage
.
success
(
`成功打包
${
flatFileList
.
length
}
个文件`
)
;
ElMessage
.
success
(
`成功打包
${
flatFileList
.
length
}
个文件`
)
}
catch
(
error
)
{
}
catch
(
error
)
{
ElMessage
.
error
(
'下载过程中出现异常,请查看控制台'
)
;
ElMessage
.
error
(
'下载过程中出现异常,请查看控制台'
)
console
.
error
(
error
)
;
console
.
error
(
error
)
}
finally
{
}
finally
{
downloading
.
value
=
false
;
downloading
.
value
=
false
}
}
};
}
const
{
queryParams
,
form
}
=
toRefs
(
data
)
const
{
queryParams
,
form
}
=
toRefs
(
data
)
// 新增:用于存储已上传成功的文件列表
// 新增:用于存储已上传成功的文件列表
...
@@ -444,7 +511,7 @@ const handleView = row => {
...
@@ -444,7 +511,7 @@ const handleView = row => {
const
downloadFile
=
()
=>
{
const
downloadFile
=
()
=>
{
let
apiMaterialDtoList
=
[]
let
apiMaterialDtoList
=
[]
let
params
=
{
let
params
=
{
projectBizId
:
userStore
.
projectInfo
.
projectBizId
||
''
,
projectBizId
:
userStore
.
projectInfo
.
projectBizId
||
''
,
objectName
:
'预约附件材料包'
,
//对象名(包名)
objectName
:
'预约附件材料包'
,
//对象名(包名)
objectBizId
:
''
//对象业务ID
objectBizId
:
''
//对象业务ID
}
}
...
@@ -522,7 +589,7 @@ function handleExceed() {
...
@@ -522,7 +589,7 @@ function handleExceed() {
// 文件上传成功回调
// 文件上传成功回调
const
uploadSuccess
=
(
res
,
file
,
fileList
)
=>
{
const
uploadSuccess
=
(
res
,
file
,
fileList
)
=>
{
console
.
log
(
'上传成功'
,
res
,
file
)
console
.
log
(
'上传成功'
,
res
,
file
)
proxy
.
$modal
.
closeLoading
()
;
proxy
.
$modal
.
closeLoading
()
if
(
res
.
code
===
200
)
{
if
(
res
.
code
===
200
)
{
// 构造前端使用的文件对象(保留原始 file 信息 + 后端返回的 url 等)
// 构造前端使用的文件对象(保留原始 file 信息 + 后端返回的 url 等)
const
uploadedFile
=
{
const
uploadedFile
=
{
...
@@ -614,6 +681,32 @@ defineExpose({
...
@@ -614,6 +681,32 @@ defineExpose({
})
})
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.preview-container
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
min-height
:
400px
;
}
.preview-image-wrapper
{
text-align
:
center
;
}
.preview-image
{
max-width
:
100%
;
max-height
:
70vh
;
object-fit
:
contain
;
}
.preview-pdf
{
width
:
100%
;
height
:
70vh
;
}
.preview-unsupported
{
text-align
:
center
;
padding
:
40px
;
}
.preview-unsupported
p
{
margin
:
16px
0
;
color
:
#909399
;
}
.uploadContainer
{
.uploadContainer
{
padding-left
:
10px
;
padding-left
:
10px
;
padding-top
:
10px
;
padding-top
:
10px
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment