@@ -1,11 +1,11 @@
< template >
< FullWidthLayout :show-padding = "false" class = "material-list-layout" >
< div class = "material-list-container" >
<!-- 左侧分组面板 - 仅混剪素材显示 -- >
<!-- 左侧分组面板 - 桌面端显示 , 移动端隐藏 -- >
< transition name = "sidebar-slide" >
< div
v-show = "activeCategory === 'MIX'"
class = "w-[220px] bg-card border-r border-border flex flex-col px-3 py-5 shrink-0"
class = "hidden md:flex w-[220px] bg-card border-r border-border flex-col px-3 py-5 shrink-0"
>
<!-- 分组列表 -- >
< div class = "flex flex-col h-full" >
@@ -72,49 +72,67 @@
<!-- 右侧内容区域 -- >
< div class = "material-content" >
<!-- 顶部工具栏 -- >
< div class = "flex items -ce nter justify-between px-6 py-4 bg-card border-b border-border gap-6 " >
<!-- 分类切换器 - 胶囊式设计 -- >
< div class = "flex bg-muted/50 rounded-full p-1 gap-1 " >
< button
class = "flex items-center gap-2 px-6 py-2 rounded-full cursor-pointer text-sm font-medium transition-all duration-200 "
: class = "activeCategory === 'MIX'
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground hover:bg-muted' "
@click ="handleCategoryChange('MIX')"
>
< Icon icon = "lucide:video" class = "text-base" / >
< span > 混剪素材 < / span >
< / button >
< button
class = "flex items-center gap-2 px-6 py-2 rounded-full cursor-pointer text-sm font-medium transition-all duration-200"
: class = "activeCategory === 'DIGITAL_HUMAN'
? 'bg-primary text-primary-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground hover:bg-muted'"
@click ="handleCategoryChange('DIGITAL_HUMAN')"
>
< Icon icon = "lucide:user" class = "text-base" / >
< span > 数字人素材 < / span >
< / button >
< / div >
<!-- 搜索和操作区 -- >
< div class = "flex items-center gap-3" >
<!-- 存储配额显示 -- >
< div class = "flex items-center gap-2 px-3 py-2 bg-muted rounded-lg border border-border" >
< span class = "text-xs text-muted-foreground" > 存储空间 < / span >
< spa n class = "text-xs font-medium" > { { userStore . usedStorage . toFixed ( 2 ) } } / { { userStore . totalStorage } } GB < / span >
< div class = "w-20 h-1 bg-border rounded-full overflow-hidden" >
< div
class = "h-full bg-gradient-to-r from-primary to-primary/70 rounded-full transition-all duration-300"
: style = "{ width: `${Math.min((userStore.usedStorage / userStore.totalStorage) * 100, 100)}%` }"
> < / div >
< div class = "toolbar -co ntainer " >
<!-- 第一行 : 分类切换 + 操作按钮 -- >
< div class = "flex items-center justify-between gap-3 " >
<!-- 移动端 : 分组按钮 + 分类切换器 -- >
< div class = "flex items-center gap-2">
< Button
v-if = "activeCategory === 'MIX'"
variant = "outline "
size = "sm"
class = "md:hidden shrink-0"
@click ="groupDrawerOpen = true"
>
< Icon icon = "lucide:folder" class = "size-4 mr-1" / >
{ { currentGroupName } }
< / Button >
<!-- 分类切换器 -- >
< div class = "flex bg-muted/50 rounded-full p-0.5 md:p-1 gap-0.5 md:gap-1" >
< button
class = "category-btn"
: class = "activeCategory === 'MIX' ? 'active' : ''"
@click ="handleCategoryChange('MIX')"
>
< Icon icon = "lucide:video" class = "size-4 md:size-[18px]" / >
< span class = "hidden sm:inline" > 混剪素材 < / span >
< / button >
< button
class = "category-btn"
: class = "activeCategory === 'DIGITAL_HUMAN' ? 'active' : ''"
@click ="handleCategoryChange('DIGITAL_HUMAN')"
>
< Ico n icon = "lucide:user" class = "size-4 md:size-[18px]" / >
< span class = "hidden sm:inline" > 数字人素材 < / span >
< / button >
< / div >
< / div >
< div class = "relative flex items-center" >
<!-- 操作按钮区 -- >
< div class = "flex items-center gap-2" >
<!-- 存储配额 - 桌面端显示 -- >
< div class = "hidden lg:flex items-center gap-2 px-3 py-1.5 bg-muted rounded-lg border border-border text-xs" >
< span class = "text-muted-foreground" > 存储 < / span >
< span class = "font-medium" > { { userStore . usedStorage . toFixed ( 1 ) } } / { { userStore . totalStorage } } GB < / span >
< / div >
<!-- 上传按钮 -- >
< Button
size = "sm"
: disabled = "activeCategory === 'MIX' && (!selectedGroupId || groupList.length === 0)"
@click ="handleOpenUploadModal"
>
< Icon icon = "lucide:upload" class = "size-4 md:mr-2" / >
< span class = "hidden md:inline" > 上传 < / span >
< / Button >
< / div >
< / div >
<!-- 第二行 : 搜索框 -- >
< div class = "flex items-center gap-2 mt-3" >
< div class = "relative flex-1" >
< Input
v-model = "searchKeyword"
placeholder = "搜索文件名..."
class = "w-72"
@ keypress -enter = " handleSearch "
>
< template # prefix >
@@ -125,13 +143,6 @@
< Icon icon = "lucide:x" class = "size-3" / >
< / Button >
< / div >
< Button
: disabled = "activeCategory === 'MIX' && (!selectedGroupId || groupList.length === 0)"
@click ="handleOpenUploadModal"
>
< Icon icon = "lucide:upload" class = "mr-2 size-4" / >
上传
< / Button >
< / div >
< / div >
@@ -334,6 +345,48 @@
:video-url=" previewUrl "
:title=" previewTitle "
/>
<!-- 移动端分组抽屉 -->
<Drawer :open=" groupDrawerOpen " @update:open=" groupDrawerOpen = $event ">
<DrawerContent class=" max - h - [ 85 vh ] ">
<DrawerHeader>
<DrawerTitle>选择分组</DrawerTitle>
</DrawerHeader>
<div class=" flex - 1 overflow - y - auto px - 4 pb - 4 ">
<!-- 新建分组按钮 -->
<Button
variant=" outline "
class=" w - full mb - 3 "
@click=" groupDrawerOpen = false ; handleOpenCreateGroupModal ( ) "
>
<Icon icon=" lucide : plus " class=" size - 4 mr - 2 " />
新建分组
</Button>
<!-- 分组列表 -->
<div class=" space - y - 1 ">
<div
v-for=" group in groupList "
:key=" group . id "
class=" flex items - center justify - between px - 4 py - 3 cursor - pointer rounded - xl transition - all "
:class=" selectedGroupId === group . id
? 'bg-primary/10 text-primary'
: 'hover:bg-muted active:bg-muted' "
@click=" handleSelectGroup ( group ) "
>
<div class=" flex items - center gap - 3 ">
<Icon
icon=" lucide : folder "
class=" size - 5 "
:class=" selectedGroupId === group . id ? 'text-primary' : 'text-muted-foreground' "
/>
<span class=" font - medium ">{{ group.name }}</span>
</div>
<span class=" text - sm text - muted - foreground ">{{ group.fileCount }}</span>
</div>
</div>
</div>
</DrawerContent>
</Drawer>
</div>
</FullWidthLayout>
</template>
@@ -356,6 +409,15 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
import {
AlertDialog,
AlertDialogAction,
@@ -370,8 +432,8 @@ import {
import MaterialUploadModal from '@/components/material/MaterialUploadModal.vue';
import MaterialService, { MaterialGroupService } from '@/api/material';
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue';
import { useUserStore } from '@/stores/user';
import VideoPreviewModal from '@/components/VideoPreviewModal.vue';
import { useUserStore } from '@/stores/user';
// 用户状态(获取存储配额)
const userStore = useUserStore()
@@ -388,6 +450,7 @@ const searchKeyword = ref('')
// 模态框状态
const uploadModalVisible = ref(false)
const createGroupModalVisible = ref(false)
const groupDrawerOpen = ref(false)
// 表单数据
const createGroupForm = reactive({
@@ -421,6 +484,12 @@ const pagination = reactive({
// 计算总页数
const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
// 当前分组名称
const currentGroupName = computed(() => {
const group = groupList.value.find(g => g.id === selectedGroupId.value)
return group?.name || '选择分组'
})
// 方法
const handleCategoryChange = async (category) => {
activeCategory.value = category
@@ -448,6 +517,7 @@ const handleSelectGroup = (group) => {
if (selectedGroupId.value === group.id) return
selectedGroupId.value = group.id
groupDrawerOpen.value = false // 关闭抽屉
loadFileList()
}
@@ -831,7 +901,6 @@ onMounted(async () => {
display : flex ;
height : 100 % ;
background : var ( -- background ) ;
gap : var ( -- space - 4 ) ;
}
// ========================================
@@ -845,6 +914,59 @@ onMounted(async () => {
transition : all var ( -- duration - slow ) cubic - bezier ( 0.4 , 0 , 0.2 , 1 ) ;
}
// ========================================
// 工具栏容器
// ========================================
. toolbar - container {
display : flex ;
flex - direction : column ;
padding : 12 px 16 px ;
background : var ( -- card ) ;
border - bottom : 1 px solid var ( -- border ) ;
@ media ( min - width : 768 px ) {
flex - direction : row ;
align - items : center ;
justify - content : space - between ;
gap : 24 px ;
padding : 16 px 24 px ;
}
}
// ========================================
// 分类按钮
// ========================================
. category - btn {
display : flex ;
align - items : center ;
gap : 6 px ;
padding : 6 px 12 px ;
border - radius : 9999 px ;
cursor : pointer ;
font - size : 13 px ;
font - weight : 500 ;
transition : all 0.2 s ;
color : var ( -- muted - foreground ) ;
border : none ;
background : transparent ;
@ media ( min - width : 768 px ) {
padding : 8 px 20 px ;
font - size : 14 px ;
}
& : hover {
color : var ( -- foreground ) ;
background : var ( -- muted ) ;
}
& . active {
background : var ( -- primary ) ;
color : var ( -- primary - foreground ) ;
box - shadow : 0 1 px 2 px rgba ( 0 , 0 , 0 , 0.05 ) ;
}
}
// ========================================
// 过渡动画
// ========================================