初始化项目文件

This commit is contained in:
2025-07-11 16:54:11 +08:00
parent 6bffd582a0
commit 39fedaac16
213 changed files with 16944 additions and 0 deletions

View File

@ -0,0 +1,231 @@
<script setup lang="ts">
import { ArrowRight, HomeFilled } from "@element-plus/icons-vue";
import { useFileStore } from "@/pages/files/store.ts";
import { useRequest } from "vue-request";
import { myRequest } from "@/requests/request.ts";
import { userinfo } from "@/store/store.ts";
import { ref, watch } from "vue";
import { ElMessage, UploadFile, UploadUserFile } from "element-plus";
import { formatError } from "@/utils/utils.ts";
import router from "@/router/router.ts";
const fileIcon = [
{ type: "dir", icon: "bi-folder" },
{ type: "zip", icon: "bi-file-zip" },
{ type: "image", icon: "bi-image" },
{ type: "audio", icon: "bi-music-note-beamed" },
{ type: "video", icon: "bi-camera-video" },
{ type: "doc", icon: "bi-file-earmark" },
];
const newFolder = ref({ visible: false, name: "" });
const fileList = ref<UploadUserFile[]>([]);
const imgPreview = ref({ visible: false, src: "" });
const { data, loading, run } = useRequest<{
data: { dirs: string[] | null; files: fileList[] | null };
}>(
() =>
myRequest(
`/api_file/file/upload${useFileStore.path.length > 0 ? "/" + useFileStore.path.join("/") : ""}`,
userinfo.token,
),
{ refreshDeps: [() => useFileStore.path] },
);
const { runAsync: addFolder } = useRequest(
() =>
myRequest(
`/api_file/file/upload/${useFileStore.path
.concat([newFolder.value.name])
.join("/")}`,
userinfo.token,
{},
"post",
),
{ manual: true },
);
const { runAsync: delFile } = useRequest(
(name: string) =>
myRequest(
`/api_file/file/upload/${useFileStore.path.concat([name]).join("/")}`,
userinfo.token,
{},
"delete",
),
{ manual: true },
);
const toPath = (index: number) => {
useFileStore.path = useFileStore.path.filter((_, i) => i <= index);
};
const funcAddFolder = () => {
addFolder()
.then(() => {
newFolder.value = { visible: false, name: "" };
run();
})
.catch((res) => ElMessage.error(formatError(res)));
};
const funcDelFile = (file: string) => {
delFile(file)
.then(() => {
run();
})
.catch((res) => ElMessage.error(formatError(res)));
};
const funcDownloadFile = (file: string) => {
const win = window.open("_black");
const url = `/api_file/file/download/${useFileStore.path.concat([file]).join("/")}`;
if (win) {
win.location.href = url;
}
};
const handleFileUpload = (file: UploadFile) => {
if (file.status === "success") {
fileList.value = fileList.value.filter((e) => e.name !== file.name);
run();
} else if (file.status === "fail") {
ElMessage.error(`${file.name}上传失败`);
}
};
watch(fileList, () => console.log(fileList));
</script>
<template>
<!-- 面包屑路由-->
<el-breadcrumb :separator-icon="ArrowRight" style="padding: 0.5rem">
<el-breadcrumb-item @click="() => (useFileStore.path = [])"
><el-icon size="20"><home-filled /></el-icon
></el-breadcrumb-item>
<el-breadcrumb-item v-for="(item, index) in useFileStore.path"
><el-tooltip :content="item" placement="top" effect="light"
><span class="path-item cursor-pointer" @click="() => toPath(index)">{{
item
}}</span></el-tooltip
></el-breadcrumb-item
>
</el-breadcrumb>
<!-- 分割线,新建文件夹,上传文件-->
<div class="divider">
<div style="background: #fff">
<el-button link @click="newFolder = { visible: true, name: '' }"
><el-icon><i class="bi-folder-plus" /></el-icon
><span> 新建文件夹</span></el-button
><el-upload
:action="`/api_file/file/upload-file/${useFileStore.path.length ? useFileStore.path.join('/') : 'root(根目录上传的文件)'}`"
multiple
:headers="{ Authorization: 'Bearer ' + userinfo.token }"
name="file"
:file-list="fileList"
:on-change="handleFileUpload"
><el-button link
><el-icon><i class="bi-upload" /></el-icon
><span> 上传文件</span></el-button
></el-upload
>
</div>
</div>
<!-- 文件列表-->
<el-table
v-loading="loading"
size="small"
v-if="data"
:data="
data?.data.dirs
? data.data.dirs
.map((e) => ({ name: e, type: 'dir' }))
.concat(data.data.files || [])
: data.data.files
"
:row-class-name="(d: any) => (d.row.type === 'dir' ? 'cursor-pointer' : '')"
@row-dblclick="
(r: fileList) =>
r.type === 'dir'
? (useFileStore.path = useFileStore.path.concat([r.name]))
: 1
"
>
<el-table-column label="名称" prop="name">
<template #default="scope">
<el-icon size="15"
><i
:class="`${fileIcon.filter((e) => e.type === scope.row.type)[0].icon}`"
/></el-icon>
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="大小" prop="size" align="center" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-icon
v-if="scope.row.type === 'image'"
@click="
() =>
(imgPreview = {
visible: true,
src: `/api_file/file/download/${useFileStore.path
.concat([scope.row.name])
.join('/')}`,
})
"
><i class="bi-eye"
/></el-icon>
<el-icon
v-if="scope.row.type === 'video'"
@click="
router.push(
`/file/video?file=${useFileStore.path.concat([scope.row.name]).join('/')}`,
)
"
><i class="bi-play-btn"
/></el-icon>
<el-icon
color="#108ee9"
@click="() => funcDownloadFile(scope.row.name)"
v-if="scope.row.type !== 'dir'"
><i class="bi-download"
/></el-icon>
<el-popconfirm
:title="`确认删除 ${scope.row.name} `"
confirm-button-text="确认"
cancel-button-text="取消"
@confirm="() => funcDelFile(scope.row.name)"
><template #reference
><el-icon color="red"><i class="bi-trash" /></el-icon></template
></el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 新建文件夹弹窗-->
<el-dialog title="新建文件夹" v-model="newFolder.visible">
<el-input placeholder="输入文件夹名称" v-model="newFolder.name" clearable />
<template #footer>
<el-button type="primary" @click="funcAddFolder">确认</el-button>
</template>
</el-dialog>
<el-image-viewer
:url-list="[imgPreview.src]"
v-if="imgPreview.visible"
@close="imgPreview.visible = false"
/>
</template>
<style scoped>
.path-item {
display: table-cell;
max-width: 10rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 20px;
}
.divider {
text-align: center;
padding: 0 35%;
background: linear-gradient(#fff calc(50% - 1px), #0003, #fff 50%);
}
i {
padding: 0 0.2rem;
font-size: 1rem;
}
</style>

View File

@ -0,0 +1,5 @@
import { reactive } from "vue";
export const useFileStore = reactive<{ path: string[] }>({
path: [],
});

View File

@ -0,0 +1,37 @@
<script setup lang="ts">
import router from "@/router/router.js";
import { useRoute } from "vue-router";
import YlHlsPlayer from "@/components/yl-hls-player.vue";
import { useRequest } from "vue-request";
import { myRequest } from "@/requests/request.ts";
const route = useRoute();
const { data: videoCheck, loading } = useRequest(() =>
myRequest(`/api_file/file/download-video-check/${route.query.file}`),
);
</script>
<template>
<el-button link @click="router.push(`/file`)" class="back"
><el-icon><i class="bi-arrow-left-circle" /></el-icon
><span> 返回</span></el-button
>
<div v-loading="loading" class="video">
<YlHlsPlayer
v-if="videoCheck.data.video && videoCheck.data.m3u8"
:src="`/api/file/download-video/${route.query.file}`"
/>
<video controls v-else style="height: 100%; width: 100%">
<source :src="`/api/file/download/${route.query.file}`" />
</video>
</div>
</template>
<style scoped>
.back {
margin: 5px 0;
}
.video {
width: 100%;
height: calc(100vh - 140px);
}
</style>