Compare commits

...

8 Commits

3 changed files with 39 additions and 31 deletions
+4 -4
View File
@@ -31,13 +31,13 @@
oncontextmenu={(e) => { e.preventDefault(); e.stopPropagation(); oncontextmenu(e.clientX, e.clientY); }}
title={name}
>
<span class="flex h-14 w-14 items-center justify-center">
<span class="flex h-20 w-20 items-center justify-center">
{#if kind === "FOLDER"}
<svg width="54" height="54" viewBox="0 0 24 24" fill="#f5c94a" stroke="#d9a92b" stroke-width="0.6" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z" /></svg>
<svg width="72" height="72" viewBox="0 0 24 24" fill="#f5c94a" stroke="#d9a92b" stroke-width="0.6" stroke-linejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z" /></svg>
{:else if kind === "PROJECT"}
<svg width="50" height="50" viewBox="0 0 24 24" fill="#e8f0ea" stroke="#4a6741" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /></svg>
<svg width="66" height="66" viewBox="0 0 24 24" fill="#e8f0ea" stroke="#4a6741" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /></svg>
{:else}
<svg width="46" height="46" viewBox="0 0 24 24" fill="#ffffff" stroke="#9c9b96" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z" /><path d="M14 3v5h5" /><path d="M9 13h6M9 17h6" /></svg>
<svg width="60" height="60" viewBox="0 0 24 24" fill="#ffffff" stroke="#9c9b96" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z" /><path d="M14 3v5h5" /><path d="M9 13h6M9 17h6" /></svg>
{/if}
</span>
<span class="line-clamp-2 w-full break-all text-center text-[12.5px] leading-snug text-ink">{name}</span>
+2 -14
View File
@@ -11,7 +11,6 @@
import { onMount } from "svelte";
import { api } from "./api.js";
import { me, toastErr, toastOk } from "./stores.js";
import { logout } from "./session.js";
import { selectedFilePath, clearSelectedFile } from "./browser.js";
import { ROLE_LABEL } from "./labels.js";
import type { FileEntry, NodeChild, NodeDetail, Role } from "./types.js";
@@ -61,7 +60,6 @@
currentFolder === null ? ($me?.isWebsiteAdmin ?? false) : atLeast(currentFolder.role, "EDIT"),
);
const projectCanEdit = $derived(projectNode !== null && projectNode.role !== "VIEW");
const initial = $derived(($me?.displayName ?? $me?.userId ?? "U").slice(0, 1).toUpperCase());
/* ------------------------------------------------------------ 数据加载 */
@@ -387,16 +385,6 @@
</button>
{/if}
<button class="btn btn-sm" onclick={refresh} title="刷新"><Icon name="refresh" size={13} /></button>
<div class="ml-1 flex shrink-0 items-center gap-2 border-l border-line-soft pl-3">
<span class="flex h-6 w-6 items-center justify-center rounded-full bg-accent text-[11px] font-semibold text-white">{initial}</span>
<span class="max-w-[120px] truncate text-[12.5px] text-ink">{$me?.displayName ?? $me?.userId ?? ""}</span>
<button
class="rounded-lg border border-line-soft px-2 py-1 text-[11.5px] text-ink-3 transition hover:bg-hover hover:text-ink"
onclick={logout}
title="退出登录"
>退出</button>
</div>
</header>
<!-- 主体:网格 + 文件预览栏 -->
@@ -416,7 +404,7 @@
{currentFolder === null ? "空文件库" : "空文件夹"}{canCreateHere ? " · 右键或点上方按钮新建" : ""}
</div>
{:else}
<div class="grid grid-cols-[repeat(auto-fill,minmax(118px,1fr))] gap-x-2 gap-y-4">
<div class="grid grid-cols-[repeat(auto-fill,minmax(132px,1fr))] gap-x-2 gap-y-4">
{#each children as n (n.id)}
<GridCard
kind={n.kind}
@@ -438,7 +426,7 @@
{:else if files.length === 0}
<div class="quiet py-10 text-center">空仓库{projectCanEdit ? " · 右键或点上方按钮新建文件" : ""}</div>
{:else}
<div class="grid grid-cols-[repeat(auto-fill,minmax(118px,1fr))] gap-x-2 gap-y-4">
<div class="grid grid-cols-[repeat(auto-fill,minmax(132px,1fr))] gap-x-2 gap-y-4">
{#each files as f (f.path)}
<GridCard
kind="FILE"
+33 -13
View File
@@ -2,7 +2,7 @@
/** 老师端。未登录显示登录卡片;登录后是带左栏导航的文件库(ADR-0031)。 */
import { onMount } from "svelte";
import { me, authChecked } from "$lib/stores.js";
import { loadSession } from "$lib/session.js";
import { loadSession, logout } from "$lib/session.js";
import LoginView from "$lib/LoginView.svelte";
import GridLibraryView from "$lib/GridLibraryView.svelte";
import BinView from "$lib/BinView.svelte";
@@ -17,27 +17,47 @@
["library", "文件库", "layers"],
["bin", "回收站", "trash"],
];
const initial = $derived(($me?.displayName ?? $me?.userId ?? "U").slice(0, 1).toUpperCase());
</script>
<svelte:head><title>文件</title></svelte:head>
<svelte:head><title>教研数据</title></svelte:head>
{#if !$authChecked}
<div class="flex h-full items-center justify-center text-ink-3">加载中…</div>
{:else if $me}
<div class="flex h-full">
<!-- 左栏导航(ADR-0031) -->
<nav class="flex w-[168px] shrink-0 flex-col gap-0.5 border-r border-line-soft bg-sidebar px-2.5 py-3.5">
{#each tabs as [id, label, icon] (id)}
<nav class="flex w-[240px] shrink-0 flex-col border-r border-line-soft bg-sidebar px-3 py-4">
<!-- 标题 -->
<div class="mb-3 px-1 text-[15px] font-bold text-ink">教研数据库</div>
<div class="mb-2 border-t border-line-soft"></div>
<!-- 导航项 -->
<div class="flex flex-1 flex-col gap-0.5">
{#each tabs as [id, label, icon] (id)}
<button
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-left text-[13px] transition {view === id
? 'bg-selected font-semibold text-ink'
: 'text-ink-2 hover:bg-hover'}"
onclick={() => (view = id)}
>
<span class="text-ink-3"><Icon name={icon} size={15} /></span>
{label}
</button>
{/each}
</div>
<!-- 底部:用户身份 + 退出 -->
<div class="mt-auto flex items-center gap-2 border-t border-line-soft pt-3">
<span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-accent text-[11px] font-semibold text-white">{initial}</span>
<span class="min-w-0 flex-1 truncate text-[12.5px] text-ink">{$me?.displayName ?? $me?.userId ?? ""}</span>
<button
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-left text-[13px] transition {view === id
? 'bg-selected font-semibold text-ink'
: 'text-ink-2 hover:bg-hover'}"
onclick={() => (view = id)}
>
<span class="text-ink-3"><Icon name={icon} size={15} /></span>
{label}
</button>
{/each}
class="rounded-lg border border-line-soft px-2 py-1 text-[11.5px] text-ink-3 transition hover:bg-hover hover:text-ink"
onclick={logout}
title="退出登录"
>退出</button>
</div>
</nav>
{#if view === "library"}