first commit
This commit is contained in:
7
frontend/src/App.vue
Normal file
7
frontend/src/App.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import FilerLayout from '@/layout/FilerLayout.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FilerLayout></FilerLayout>
|
||||
</template>
|
||||
3
frontend/src/assets/app.css
Normal file
3
frontend/src/assets/app.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
50
frontend/src/components/Navbar.vue
Normal file
50
frontend/src/components/Navbar.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="navbar bg-base-100">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h7"
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52"
|
||||
>
|
||||
<li>
|
||||
<RouterLink :to="{ name: 'setting' }">设置</RouterLink>
|
||||
</li>
|
||||
<li>
|
||||
<RouterLink :to="{ name: 'upyun' }">又拍云存储</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-center">
|
||||
<a class="btn btn-ghost normal-case text-xl">Filer</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<BackspaceIcon class="btn btn-ghost btn-circle h-8 w-8 mr-2" @click="quit" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { BackspaceIcon } from '@heroicons/vue/24/solid'
|
||||
import { Quit } from '@/../wailsjs/go/controller/Storage'
|
||||
|
||||
const quit = () => {
|
||||
Quit()
|
||||
}
|
||||
</script>
|
||||
12
frontend/src/layout/FilerLayout.vue
Normal file
12
frontend/src/layout/FilerLayout.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="flex flex-col min-h-screen bg-base-200">
|
||||
<Navbar></Navbar>
|
||||
<div class="flex justify-center">
|
||||
<RouterView></RouterView>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Navbar from '@/components/Navbar.vue'
|
||||
</script>
|
||||
18
frontend/src/main.ts
Normal file
18
frontend/src/main.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import './assets/app.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import { VueClipboard } from '@soerenmartius/vue3-clipboard'
|
||||
import Notifications from '@kyvg/vue3-notification'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(VueClipboard)
|
||||
app.use(Notifications)
|
||||
|
||||
app.mount('#app')
|
||||
20
frontend/src/router/index.ts
Normal file
20
frontend/src/router/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import SettingView from '../views/SettingView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'setting',
|
||||
component: SettingView
|
||||
},
|
||||
{
|
||||
path: '/upyun',
|
||||
name: 'upyun',
|
||||
component: () => import('../views/UpyunView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
26
frontend/src/style.css
Normal file
26
frontend/src/style.css
Normal file
@@ -0,0 +1,26 @@
|
||||
html {
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
text-align: center;
|
||||
}
|
||||
75
frontend/src/views/SettingView.vue
Normal file
75
frontend/src/views/SettingView.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<notifications />
|
||||
<div class="card w-96 top-5 bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">又拍云对象存储!</h2>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label">
|
||||
<span class="label-text">服务名</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered input-primary w-full max-w-xs"
|
||||
v-model="allConfig.config.upyun.bucket"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label">
|
||||
<span class="label-text">操作员名称</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered input-primary w-full max-w-xs"
|
||||
v-model="allConfig.config.upyun.operator"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label">
|
||||
<span class="label-text">密码</span>
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
class="input input-bordered input-primary w-full max-w-xs"
|
||||
v-model="allConfig.config.upyun.password"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full max-w-xs">
|
||||
<label class="label">
|
||||
<span class="label-text">域名</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered input-primary w-full max-w-xs"
|
||||
v-model="allConfig.config.upyun.domain"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<button class="btn btn-primary" @click="upyun_update">更新配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { notify } from '@kyvg/vue3-notification'
|
||||
import { GetConfig, SetUpyunConfig } from '@/../wailsjs/go/controller/Storage'
|
||||
import { controller } from '@/../wailsjs/go/models'
|
||||
|
||||
let current_config: controller.ServerConfig = new controller.ServerConfig()
|
||||
current_config.upyun = new controller.UpyunConfig()
|
||||
let allConfig = reactive({ config: current_config })
|
||||
|
||||
const upyun_update = async () => {
|
||||
console.log('current config: ', allConfig.config.upyun)
|
||||
await SetUpyunConfig(allConfig.config.upyun)
|
||||
notify({
|
||||
title: '更新成功',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
allConfig.config = await GetConfig()
|
||||
})
|
||||
</script>
|
||||
182
frontend/src/views/UpyunView.vue
Normal file
182
frontend/src/views/UpyunView.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<notifications />
|
||||
<div class="card w-full bg-base-100 shadow-xl mt-8 mx-8">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
<div class="join">
|
||||
<input class="input input-bordered join-item" v-model="current_prefix" />
|
||||
<button class="btn join-item btn-active rounded-r-full" @click="go_to">Go</button>
|
||||
</div>
|
||||
<button class="btn ml-2 btn-outline btn-accent" @click="go_back">返回上级</button>
|
||||
<button class="btn ml-2 btn-outline btn-success" @click="mkdir">创建目录</button>
|
||||
<button
|
||||
class="btn ml-2 join-item btn-active btn-secondary rounded-r-full"
|
||||
@click="on_file_upload"
|
||||
>
|
||||
上传文件
|
||||
</button>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr v-for="(file, index) in current_files.list" :key="index">
|
||||
<td>
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="avatar">
|
||||
<div class="mask mask-squircle w-8 h-8" v-if="file.is_dir">
|
||||
<FolderPlusIcon class="h-8 w-8 text-blue-500" />
|
||||
</div>
|
||||
<div class="mask mask-squircle w-8 h-8" v-else>
|
||||
<DocumentTextIcon class="h-8 w-8 text-orange-800" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="file.is_dir">
|
||||
<a class="font-bold text-blue-300" @click="click_enter_to(file.filename!)">{{
|
||||
file.filename
|
||||
}}</a>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="font-bold">{{ file.filename }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ file.created_at }}
|
||||
</td>
|
||||
<th>
|
||||
<button
|
||||
class="btn btn-ghost btn-outline btn-xs"
|
||||
v-if="!file.is_dir"
|
||||
@click="toClipboard(copy_link(file.filename!))"
|
||||
>
|
||||
link
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-error btn-outline btn-xs ml-1"
|
||||
@click="to_delete(file.filename!, file.is_dir!)"
|
||||
>
|
||||
delete
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-actions justify-end">
|
||||
<div>
|
||||
当前
|
||||
<div class="badge badge-accent">{{ current_files.list.length }}</div>
|
||||
个文件
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FolderPlusIcon, DocumentTextIcon } from '@heroicons/vue/24/solid'
|
||||
import { toClipboard } from '@soerenmartius/vue3-clipboard'
|
||||
import { notify } from '@kyvg/vue3-notification'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
|
||||
import {
|
||||
GetConfig,
|
||||
UperList,
|
||||
UperUpload,
|
||||
UperDelete,
|
||||
UperCreateDirectory
|
||||
} from '@/../wailsjs/go/controller/Storage'
|
||||
import { controller } from '@/../wailsjs/go/models'
|
||||
|
||||
let current_config: controller.ServerConfig = new controller.ServerConfig()
|
||||
current_config.upyun = new controller.UpyunConfig()
|
||||
let allConfig = reactive({ config: current_config })
|
||||
|
||||
const current_prefix = ref('/')
|
||||
let file_list_object: Array<controller.FileInfo> = []
|
||||
let current_files = reactive({ list: file_list_object })
|
||||
|
||||
const on_file_upload = async () => {
|
||||
await UperUpload(current_prefix.value)
|
||||
|
||||
notify({
|
||||
title: '文件上传成功',
|
||||
duration: 5000
|
||||
})
|
||||
|
||||
await list(current_prefix.value)
|
||||
}
|
||||
|
||||
const list = async (prefix: string) => {
|
||||
current_files.list = await UperList(prefix)
|
||||
console.log('get list: ', current_files.list)
|
||||
}
|
||||
|
||||
const mkdir = async () => {
|
||||
if (current_prefix.value === '/') return
|
||||
await UperCreateDirectory(current_prefix.value)
|
||||
await list(current_prefix.value)
|
||||
|
||||
notify({
|
||||
title: '创建目录成功',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
|
||||
const go_to = async () => {
|
||||
await list(current_prefix.value)
|
||||
}
|
||||
|
||||
const to_delete = async (filename: string, is_dir: boolean) => {
|
||||
let delete_filename = ''
|
||||
if (current_prefix.value == '/') {
|
||||
delete_filename = current_prefix.value + filename
|
||||
} else {
|
||||
delete_filename = current_prefix.value + '/' + filename
|
||||
}
|
||||
await UperDelete(delete_filename, is_dir)
|
||||
await list(current_prefix.value)
|
||||
|
||||
notify({
|
||||
title: '删除成功',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
|
||||
const click_enter_to = async (dir: string) => {
|
||||
if (current_prefix.value == '/') {
|
||||
current_prefix.value += dir
|
||||
} else {
|
||||
current_prefix.value += '/' + dir
|
||||
}
|
||||
await list(current_prefix.value)
|
||||
}
|
||||
|
||||
const go_back = async () => {
|
||||
if (current_prefix.value === '/') return
|
||||
const lastIndex = current_prefix.value.lastIndexOf('/')
|
||||
current_prefix.value = current_prefix.value.substring(0, lastIndex)
|
||||
if (current_prefix.value === '') current_prefix.value = '/'
|
||||
await list(current_prefix.value)
|
||||
}
|
||||
|
||||
const copy_link = (filename: string) => {
|
||||
let real_filename = ''
|
||||
if (current_prefix.value == '/') {
|
||||
real_filename = current_prefix.value + filename
|
||||
} else {
|
||||
real_filename = current_prefix.value + '/' + filename
|
||||
}
|
||||
notify({
|
||||
title: '已拷贝链接',
|
||||
duration: 5000
|
||||
})
|
||||
return allConfig.config.upyun.domain + real_filename
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
allConfig.config = await GetConfig()
|
||||
await list(current_prefix.value)
|
||||
})
|
||||
</script>
|
||||
7
frontend/src/vite-env.d.ts
vendored
Normal file
7
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type {DefineComponent} from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
Reference in New Issue
Block a user