@@ -5,6 +5,7 @@ import '@uppy/dashboard/dist/style.min.css'
55import DeleteDialog from ' @/components/common/DeleteDialog.vue'
66import TicketPanel from ' @/components/ticket/TicketPanel.vue'
77import FileAddDialog from ' @/components/ticket/file/FileAddDialog.vue'
8+ import ImageModal from ' @/components/ticket/file/ImageModal.vue'
89import { Button } from ' @/components/ui/button'
910import { useToast } from ' @/components/ui/toast/use-toast'
1011
@@ -30,6 +31,36 @@ const props = defineProps<{
3031 files: Array <ModelFile > | undefined
3132}>()
3233
34+ const isImageModalOpen = ref (false )
35+ const selectedImageUrl = ref (' ' )
36+ const selectedImageName = ref (' ' )
37+
38+ const isImage = (fileName : string ) => {
39+ const imageExtensions = [' png' , ' jpg' , ' jpeg' , ' gif' , ' bmp' , ' webp' , ' svg' ]
40+ const extension = fileName .split (' .' ).pop ()?.toLowerCase ()
41+ return extension ? imageExtensions .includes (extension ) : false
42+ }
43+
44+ const openImageModal = async (file : ModelFile ) => {
45+ if (! isImage (file .name )) return
46+ try {
47+ const response = await fetch (` /api/files/${file .id }/download ` , {
48+ headers: { Authorization: ` Bearer ${authStore .token } ` }
49+ })
50+ const blob = await response .blob ()
51+ selectedImageUrl .value = window .URL .createObjectURL (blob )
52+ selectedImageName .value = file .name
53+ isImageModalOpen .value = true
54+ } catch (err ) {
55+ console .error (' Error fetching image:' , err )
56+ toast ({
57+ title: ' Error' ,
58+ description: ' Could not load image for preview.' ,
59+ variant: ' destructive'
60+ })
61+ }
62+ }
63+
3364const downloadFile = (file : any ) => {
3465 fetch (` /api/files/${file .id }/download ` , {
3566 headers: { Authorization: ` Bearer ${authStore .token } ` }
@@ -44,6 +75,8 @@ const downloadFile = (file: any) => {
4475 link .download = file .name
4576 document .body .appendChild (link )
4677 link .click ()
78+ document .body .removeChild (link )
79+ window .URL .revokeObjectURL (_url )
4780 })
4881 .catch ((err ) => {
4982 console .log (err )
@@ -82,11 +115,24 @@ watch(
82115 },
83116 { immediate: true }
84117)
118+
119+ watch (isImageModalOpen , (isOpen ) => {
120+ if (! isOpen && selectedImageUrl .value ) {
121+ window .URL .revokeObjectURL (selectedImageUrl .value )
122+ selectedImageUrl .value = ' '
123+ selectedImageName .value = ' '
124+ }
125+ })
85126 </script >
86127
87128<template >
88129 <TicketPanel title =" Files" @add =" dialogOpen = true" :hideAdd =" isDemo" >
89130 <FileAddDialog v-if =" !isDemo" v-model =" dialogOpen" :ticket =" ticket" />
131+ <ImageModal
132+ v-model =" isImageModalOpen"
133+ :image-url =" selectedImageUrl"
134+ :file-name =" selectedImageName"
135+ />
90136 <div
91137 v-if =" !files || files.length === 0"
92138 class =" flex h-10 items-center p-4 text-muted-foreground"
@@ -99,7 +145,11 @@ watch(
99145 :title =" file.name"
100146 class =" flex w-full items-center border-t py-1 pl-2 pr-1 first:rounded-t first:border-none last:rounded-b"
101147 >
102- <div class =" flex flex-1 items-center overflow-hidden pr-2" >
148+ <div
149+ class =" flex flex-1 items-center overflow-hidden pr-2"
150+ :class =" isImage(file.name) ? 'cursor-pointer' : ''"
151+ @click =" openImageModal(file)"
152+ >
103153 {{ file.name }}
104154
105155 <div class =" ml-1 flex-1 text-nowrap text-sm text-muted-foreground" >
0 commit comments