first commit

This commit is contained in:
2026-07-01 00:22:05 +08:00
commit e4efa4b54d
48 changed files with 7983 additions and 0 deletions

View File

@@ -0,0 +1,308 @@
<script setup lang="ts">
import { ref } from "vue";
const collapsed = ref(false);
const toggleCollapsed = () => {
collapsed.value = !collapsed.value;
};
// ---- file tree state ----
interface FileItem {
id: string;
name: string;
type: "file" | "folder";
children?: FileItem[];
expanded?: boolean;
}
const files = ref<FileItem[]>([
{
id: "1",
name: "文档",
type: "folder",
expanded: true,
children: [
{ id: "2", name: "README.md", type: "file" },
{ id: "3", name: "笔记.md", type: "file" },
],
},
{
id: "4",
name: "工作",
type: "folder",
expanded: false,
children: [{ id: "5", name: "项目计划.md", type: "file" }],
},
]);
const toggleFolder = (item: FileItem) => {
if (item.type === "folder") {
item.expanded = !item.expanded;
}
};
const newFile = () => {
const name = prompt("文件名称(含扩展名):");
if (name) {
files.value.push({ id: crypto.randomUUID(), name, type: "file" });
}
};
const newFolder = () => {
const name = prompt("文件夹名称:");
if (name) {
files.value.push({
id: crypto.randomUUID(),
name,
type: "folder",
expanded: true,
children: [],
});
}
};
// ---- tag state ----
const tags = ref(["技术", "随笔", "待办", "灵感"]);
const newTagInput = ref("");
const addingTag = ref(false);
const addTag = () => {
const t = newTagInput.value.trim();
if (t && !tags.value.includes(t)) {
tags.value.push(t);
}
newTagInput.value = "";
addingTag.value = false;
};
const removeTag = (tag: string) => {
tags.value = tags.value.filter((t) => t !== tag);
};
</script>
<template>
<aside
:class="[
'flex flex-col border-r border-gray-800 bg-gray-900/60 transition-all duration-300 ease-in-out overflow-hidden shrink-0',
collapsed ? 'w-12' : 'w-56',
]"
>
<!-- Header / toggle -->
<div
class="flex items-center justify-between px-3 py-3 border-b border-gray-800"
:class="collapsed ? 'justify-center' : ''"
>
<span v-if="!collapsed" class="text-xs font-semibold text-gray-400 uppercase tracking-wider">
目录
</span>
<button
class="p-1 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 transition-colors"
@click="toggleCollapsed"
>
<!-- hamburger / panel-close icon -->
<svg
v-if="!collapsed"
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>
</button>
</div>
<!-- Collapsed icon-only view -->
<div v-if="collapsed" class="flex flex-col items-center gap-1 py-3">
<!-- new file -->
<button
class="p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 transition-colors"
title="新建文件"
@click="newFile"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" />
</svg>
</button>
<!-- new folder -->
<button
class="p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 transition-colors"
title="新建文件夹"
@click="newFolder"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
</svg>
</button>
</div>
<!-- Expanded content -->
<div v-if="!collapsed" class="flex flex-col flex-1 overflow-hidden">
<!-- File tree -->
<div class="flex-1 overflow-y-auto px-2 py-2 space-y-0.5">
<div
v-for="item in files"
:key="item.id"
>
<!-- Folder -->
<div
v-if="item.type === 'folder'"
class="flex items-center gap-1 px-2 py-1 rounded-md text-sm text-gray-300 hover:bg-gray-800 cursor-pointer select-none"
@click="toggleFolder(item)"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-3.5 h-3.5 text-gray-500 transition-transform"
:class="item.expanded ? 'rotate-90' : ''"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-4 h-4 text-yellow-500/70"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
</svg>
<span class="truncate">{{ item.name }}</span>
</div>
<!-- Folder children -->
<div
v-if="item.type === 'folder' && item.expanded"
class="ml-4 space-y-0.5"
>
<div
v-for="child in item.children"
:key="child.id"
class="flex items-center gap-1.5 px-2 py-1 rounded-md text-sm text-gray-400 hover:bg-gray-800 hover:text-gray-200 cursor-pointer"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-3.5 h-3.5 text-indigo-400/60"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span class="truncate">{{ child.name }}</span>
</div>
</div>
<!-- Top-level file -->
<div
v-else-if="item.type === 'file'"
class="flex items-center gap-1.5 px-2 py-1 rounded-md text-sm text-gray-400 hover:bg-gray-800 hover:text-gray-200 cursor-pointer"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-3.5 h-3.5 text-indigo-400/60"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span class="truncate">{{ item.name }}</span>
</div>
</div>
</div>
<!-- Action buttons -->
<div class="flex gap-1 px-3 py-2 border-t border-gray-800/60">
<button
class="flex-1 flex items-center justify-center gap-1 px-2 py-1.5 text-xs text-gray-400 hover:text-white hover:bg-gray-800 rounded-md transition-colors"
@click="newFile"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" />
</svg>
新建文件
</button>
<button
class="flex-1 flex items-center justify-center gap-1 px-2 py-1.5 text-xs text-gray-400 hover:text-white hover:bg-gray-800 rounded-md transition-colors"
@click="newFolder"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" />
</svg>
新建文件夹
</button>
</div>
<!-- Tags section -->
<div class="border-t border-gray-800/60 px-3 py-3">
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-semibold text-gray-500 uppercase tracking-wider">标签</span>
<button
class="text-gray-500 hover:text-gray-300 transition-colors"
@click="addingTag = !addingTag"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" />
</svg>
</button>
</div>
<!-- Add tag input -->
<div v-if="addingTag" class="flex gap-1 mb-2">
<input
v-model="newTagInput"
class="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-700 rounded-md text-gray-200 outline-none focus:border-indigo-500"
placeholder="标签名称"
@keyup.enter="addTag"
/>
<button
class="px-2 py-1 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-500 transition-colors"
@click="addTag"
>
确定
</button>
</div>
<!-- Tag list -->
<div class="flex flex-wrap gap-1.5">
<span
v-for="tag in tags"
:key="tag"
class="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded-full bg-gray-800 text-gray-300 border border-gray-700/50 group"
>
{{ tag }}
<button
class="text-gray-600 hover:text-red-400 transition-colors"
@click="removeTag(tag)"
>
<svg xmlns="http://www.w3.org/2000/svg" class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</span>
</div>
</div>
</div>
</aside>
</template>