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
c53ccfbd
Commit
c53ccfbd
authored
May 13, 2026
by
yuzhenWang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修改预约附件可预览文件
parent
8e482ad9
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 @
c53ccfbd
...
@@ -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