2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 06:47:20 +08:00

feat(文件): 图片支持预览

This commit is contained in:
耗子
2024-11-28 20:32:16 +08:00
parent 746d2297fe
commit 1dc0042507
7 changed files with 111 additions and 14 deletions

View File

@@ -3,6 +3,7 @@
package service
import (
"encoding/base64"
"fmt"
stdio "io"
"net/http"
@@ -15,6 +16,7 @@ import (
"time"
"github.com/go-rat/chix"
"github.com/go-rat/utils/file"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/app"
@@ -81,13 +83,21 @@ func (s *FileService) Content(w http.ResponseWriter, r *http.Request) {
return
}
content, err := io.Read(req.Path)
content, err := io.ReadBytes(req.Path)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
mime, err := file.MimeType(req.Path)
if err != nil {
Error(w, http.StatusInternalServerError, "%v", err)
return
}
Success(w, content)
Success(w, chix.M{
"mime": mime,
"content": base64.StdEncoding.EncodeToString(content),
})
}
func (s *FileService) Save(w http.ResponseWriter, r *http.Request) {

View File

@@ -91,6 +91,11 @@ func Read(path string) (string, error) {
return string(data), err
}
// ReadBytes 读取文件
func ReadBytes(path string) ([]byte, error) {
return os.ReadFile(path)
}
// FileInfo 获取文件大小
func FileInfo(path string) (os.FileInfo, error) {
return os.Stat(path)

View File

@@ -21,7 +21,7 @@ const get = async () => {
await file
.content(props.path)
.then((res) => {
data.value = res.data
data.value = atob(res.data.content)
window.$message.success('获取成功')
})
.catch(() => {

View File

@@ -293,6 +293,25 @@ const isCompress = (name: string) => {
return ['zip', 'bz2', 'tar', 'gz', 'tgz', 'xz', '7z'].includes(ext)
}
const isImage = (name: string) => {
const ext = getExt(name)
return [
'png',
'jpg',
'jpeg',
'gif',
'bmp',
'ico',
'svg',
'webp',
'avif',
'tiff',
'heif',
'heic',
'jxl'
].includes(ext)
}
const formatPercent = (num: any) => {
num = Number(num)
return Number(num.toFixed(2))
@@ -326,6 +345,7 @@ export {
getFilename,
getIconByExt,
isCompress,
isImage,
languageByPath,
lastDirectory
}

View File

@@ -6,8 +6,17 @@ import type { RowData } from 'naive-ui/es/data-table/src/interface'
import file from '@/api/panel/file'
import TheIcon from '@/components/custom/TheIcon.vue'
import { checkName, checkPath, getExt, getFilename, getIconByExt, isCompress } from '@/utils/file'
import {
checkName,
checkPath,
getExt,
getFilename,
getIconByExt,
isCompress,
isImage
} from '@/utils/file'
import EditModal from '@/views/file/EditModal.vue'
import PreviewModal from '@/views/file/PreviewModal.vue'
import type { Marked } from '@/views/file/types'
const loading = ref(false)
@@ -19,7 +28,8 @@ const markedType = defineModel<string>('markedType', { type: String, required: t
const compress = defineModel<boolean>('compress', { type: Boolean, required: true })
const permission = defineModel<boolean>('permission', { type: Boolean, required: true })
const editorModal = ref(false)
const editorFile = ref('')
const previewModal = ref(false)
const currentFile = ref('')
const showDropdown = ref(false)
const selectedRow = ref<any>()
@@ -41,8 +51,8 @@ const options = computed<DropdownOption[]>(() => {
if (selectedRow.value == null) return []
const options = [
{
label: selectedRow.value.dir ? '打开' : '编辑',
key: selectedRow.value.dir ? 'open' : 'edit'
label: selectedRow.value.dir ? '打开' : isImage(selectedRow.value.name) ? '预览' : '编辑',
key: selectedRow.value.dir ? 'open' : isImage(selectedRow.value.name) ? 'preview' : 'edit'
},
{ label: '复制', key: 'copy' },
{ label: '移动', key: 'move' },
@@ -97,7 +107,7 @@ const columns: DataTableColumns<RowData> = [
if (row.dir) {
path.value = row.full
} else {
editorFile.value = row.full
currentFile.value = row.full
editorModal.value = true
}
}
@@ -179,8 +189,12 @@ const columns: DataTableColumns<RowData> = [
tertiary: true,
onClick: () => {
if (!row.dir && !row.symlink) {
editorFile.value = row.full
editorModal.value = true
currentFile.value = row.full
if (isImage(row.name)) {
previewModal.value = true
} else {
editorModal.value = true
}
} else {
path.value = row.full
}
@@ -189,7 +203,7 @@ const columns: DataTableColumns<RowData> = [
{
default: () => {
if (!row.dir && !row.symlink) {
return '编辑'
return isImage(row.name) ? '预览' : '编辑'
} else {
return '打开'
}
@@ -529,9 +543,13 @@ const handleSelect = (key: string) => {
path.value = selectedRow.value.full
break
case 'edit':
editorFile.value = selectedRow.value.full
currentFile.value = selectedRow.value.full
editorModal.value = true
break
case 'preview':
currentFile.value = selectedRow.value.full
previewModal.value = true
break
case 'copy':
markedType.value = 'copy'
marked.value = [
@@ -662,7 +680,8 @@ onUnmounted(() => {
:on-clickoutside="onCloseDropdown"
@select="handleSelect"
/>
<edit-modal v-model:show="editorModal" v-model:file="editorFile" />
<edit-modal v-model:show="editorModal" v-model:file="currentFile" />
<preview-modal v-model:show="previewModal" v-model:path="currentFile" />
<n-modal
v-model:show="renameModal"
preset="card"

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import file from '@/api/panel/file'
const show = defineModel<boolean>('show', { type: Boolean, required: true })
const path = defineModel<string>('path', { type: String, required: true })
const mime = ref('')
const content = ref('')
const img = computed(() => {
return `data:${mime.value};base64,${content.value}`
})
watch(
() => path.value,
() => {
content.value = ''
file.content(path.value).then((res) => {
mime.value = res.data.mime
content.value = res.data.content
})
}
)
</script>
<template>
<n-modal
v-model:show="show"
preset="card"
:title="'预览 - ' + path"
style="width: 60vw"
size="huge"
:bordered="false"
:segmented="false"
>
<n-image width="100%" :src="img" preview-disabled :show-toolbar="false">
<template #placeholder>
<n-spin />
</template>
</n-image>
</n-modal>
</template>
<style scoped lang="scss"></style>

View File

@@ -209,7 +209,7 @@ const handleEdit = async (row: any) => {
editTask.value.id = row.id
editTask.value.name = row.name
editTask.value.time = row.time
editTask.value.script = res.data
editTask.value.script = atob(res.data.content)
editModal.value = true
})
})