初始化项目文件
This commit is contained in:
231
web_vue/src/pages/files/index.vue
Normal file
231
web_vue/src/pages/files/index.vue
Normal 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>
|
5
web_vue/src/pages/files/store.ts
Normal file
5
web_vue/src/pages/files/store.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { reactive } from "vue";
|
||||
|
||||
export const useFileStore = reactive<{ path: string[] }>({
|
||||
path: [],
|
||||
});
|
37
web_vue/src/pages/files/yl-video.vue
Normal file
37
web_vue/src/pages/files/yl-video.vue
Normal 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>
|
Reference in New Issue
Block a user