🚧 add sidebar

This commit is contained in:
2024-12-03 00:49:16 +08:00
parent 594bd5b280
commit 2a1bb215ac
31 changed files with 440 additions and 99 deletions

17
components.d.ts vendored
View File

@@ -27,17 +27,16 @@ declare module 'vue' {
Alert: typeof import('./src/components/MyUI/Alert/Alert.vue')['default']
AList: typeof import('ant-design-vue/es')['List']
AListItem: typeof import('ant-design-vue/es')['ListItem']
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
AntDesignOutlined: typeof import('@ant-design/icons-vue')['AntDesignOutlined']
APagination: typeof import('ant-design-vue/es')['Pagination']
APopover: typeof import('ant-design-vue/es')['Popover']
AQrcode: typeof import('ant-design-vue/es')['QRCode']
ARadio: typeof import('ant-design-vue/es')['Radio']
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
ARate: typeof import('ant-design-vue/es')['Rate']
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
@@ -79,22 +78,18 @@ declare module 'vue' {
Ellipsis: typeof import('./src/components/MyUI/Ellipsis/Ellipsis.vue')['default']
Empty: typeof import('./src/components/MyUI/Empty/Empty.vue')['default']
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined']
Flex: typeof import('./src/components/MyUI/Flex/Flex.vue')['default']
FloatButton: typeof import('./src/components/MyUI/FloatButton/FloatButton.vue')['default']
ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default']
GaugeChart: typeof import('./src/components/MyUI/GaugeChart/GaugeChart.vue')['default']
GiteeOutlined: typeof import('@ant-design/icons-vue')['GiteeOutlined']
GithubOutlined: typeof import('@ant-design/icons-vue')['GithubOutlined']
GradientText: typeof import('./src/components/MyUI/GradientText/GradientText.vue')['default']
Image: typeof import('./src/components/MyUI/Image/Image.vue')['default']
ImageHoverEffect: typeof import('./src/components/ImageHoverEffect/imageHoverEffect.vue')['default']
ImageOverlaySlider: typeof import('./src/components/ImageOverlaySlider/imageOverlaySlider.vue')['default']
Input: typeof import('./src/components/MyUI/Input/Input.vue')['default']
InputSearch: typeof import('./src/components/MyUI/InputSearch/InputSearch.vue')['default']
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
List: typeof import('./src/components/MyUI/List/List.vue')['default']
LoadingBar: typeof import('./src/components/MyUI/LoadingBar/LoadingBar.vue')['default']
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
LoginPage: typeof import('./src/views/Login/LoginPage.vue')['default']
@@ -106,17 +101,20 @@ declare module 'vue' {
Notification: typeof import('./src/components/MyUI/Notification/Notification.vue')['default']
NumberAnimation: typeof import('./src/components/MyUI/NumberAnimation/NumberAnimation.vue')['default']
Pagination: typeof import('./src/components/MyUI/Pagination/Pagination.vue')['default']
PeopleAlbum: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbum.vue')['default']
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
PictureOutlined: typeof import('@ant-design/icons-vue')['PictureOutlined']
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
Popconfirm: typeof import('./src/components/MyUI/Popconfirm/Popconfirm.vue')['default']
Popover: typeof import('./src/components/MyUI/Popover/Popover.vue')['default']
Progress: typeof import('./src/components/MyUI/Progress/Progress.vue')['default']
QqOutlined: typeof import('@ant-design/icons-vue')['QqOutlined']
QRCode: typeof import('./src/components/MyUI/QRCode/QRCode.vue')['default']
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
QRLoginFooter: typeof import('./src/views/QRLogin/QRLoginFooter.vue')['default']
Radio: typeof import('./src/components/MyUI/Radio/Radio.vue')['default']
Rate: typeof import('./src/components/MyUI/Rate/Rate.vue')['default']
RecentUpload: typeof import('./src/views/Photograph/RecentUpload/RecentUpload.vue')['default']
RecyclingBin: typeof import('./src/views/RecyclingBin/RecyclingBin.vue')['default']
ReplyInput: typeof import('./src/components/CommentReply/src/ReplyInput/ReplyInput.vue')['default']
ReplyList: typeof import('./src/components/CommentReply/src/ReplyList/ReplyList.vue')['default']
ReplyReply: typeof import('./src/components/CommentReply/src/ReplyReplyInput/ReplyReply.vue')['default']
@@ -126,6 +124,7 @@ declare module 'vue' {
Row: typeof import('./src/components/MyUI/Grid/Row.vue')['default']
SafetyOutlined: typeof import('@ant-design/icons-vue')['SafetyOutlined']
Scrollbar: typeof import('./src/components/MyUI/Scrollbar/Scrollbar.vue')['default']
SearchOutlined: typeof import('@ant-design/icons-vue')['SearchOutlined']
Segmented: typeof import('./src/components/MyUI/Segmented/Segmented.vue')['default']
Select: typeof import('./src/components/MyUI/Select/Select.vue')['default']
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
@@ -144,6 +143,7 @@ declare module 'vue' {
Tag: typeof import('./src/components/MyUI/Tag/Tag.vue')['default']
Textarea: typeof import('./src/components/MyUI/Textarea/Textarea.vue')['default']
TextScroll: typeof import('./src/components/MyUI/TextScroll/TextScroll.vue')['default']
ThingAlbum: typeof import('./src/views/Album/ThingAlbum/ThingAlbum.vue')['default']
Timeline: typeof import('./src/components/MyUI/Timeline/Timeline.vue')['default']
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
TreeChart: typeof import('./src/components/MyUI/TreeChart/TreeChart.vue')['default']
@@ -154,6 +154,5 @@ declare module 'vue' {
Video: typeof import('./src/components/MyUI/Video/Video.vue')['default']
WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined']
Waterfall: typeof import('./src/components/MyUI/Waterfall/Waterfall.vue')['default']
WechatOutlined: typeof import('@ant-design/icons-vue')['WechatOutlined']
}
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/logo.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=yes"/>
<title><%- title %></title>
</head>
<body>

View File

@@ -10,7 +10,7 @@
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
},
"dependencies": {
"@alova/adapter-axios": "^2.0.10",
"@alova/adapter-axios": "^2.0.11",
"@ant-design/icons-vue": "^7.0.1",
"@tensorflow/tfjs": "^4.22.0",
"@types/animejs": "^3.1.12",
@@ -21,15 +21,16 @@
"@vuepic/vue-datepicker": "^10.0.0",
"@vueuse/core": "^12.0.0",
"@vueuse/integrations": "^12.0.0",
"alova": "^3.2.5",
"alova": "^3.2.6",
"animejs": "^3.2.2",
"ant-design-vue": "^4.2.6",
"autofit.js": "^3.2.2",
"axios": "^1.7.7",
"browser-image-compression": "^2.0.2",
"buffer": "^6.0.3",
"crypto-js": "^4.2.0",
"echarts": "^5.5.1",
"eslint": "9.15.0",
"eslint": "9.16.0",
"go-captcha-vue": "^2.0.4",
"jsencrypt": "^3.3.2",
"json-stringify-safe": "^5.0.1",
@@ -53,10 +54,10 @@
"ws": "^8.18.0"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
"@eslint/js": "^9.16.0",
"@vitejs/plugin-vue": "^5.2.1",
"eslint-plugin-vue": "^9.31.0",
"globals": "^15.12.0",
"eslint-plugin-vue": "^9.32.0",
"globals": "^15.13.0",
"sass": "^1.81.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.15.0",

View File

@@ -19,16 +19,4 @@ const app = useStore().theme;
const lang = useStore().lang;
</script>
<style scoped lang="scss">
.animation-enter {
opacity: 0; /* 初始透明 */
}
.animation-enter-active {
transition: opacity 0.2s; /* 渐入效果 */
}
.animation-leave-active {
transition: opacity 0.2s; /* 渐出效果 */
}
.animation-leave-to {
opacity: 0; /* 离开时透明 */
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<AFlex :vertical="true" class="reply-item-child">
<ASpin :spinning="comment.replyLoading[item.id]" size="default">
<ASpin :delay="500" :spinning="comment.replyLoading[item.id]" size="default">
<AFlex :vertical="true" v-if="comment.replyVisibility[item.id]?.data.comments">
<AFlex :vertical="false" style="margin-top: 5px"
v-for="(child, index) in comment.replyVisibility[item.id]?.data.comments"

View File

@@ -1,33 +1,59 @@
<template>
<header class="header-main">
<div class="header-container">
<AFlex :vertical="false" align="center" justify="flex-end" class="header-logo-container">
<AFlex :vertical="false" align="center" justify="flex-start" class="header-logo-container">
<AAvatar :size="50" shape="square" :src="logo" @click="router.push('/')"/>
<span class="header-logo-text" @click="router.push('/')">S.Album</span>
</AFlex>
<AFlex class="header-search-container" :vertical="false" align="center" justify="center">
<APopover :arrow="false" :overlayInnerStyle="{width: '28vw'}"
trigger="click">
<AInput size="large" class="header-search-input" placeholder="Search">
<template #suffix>
<AButton size="small" type="text" shape="circle" @click.prevent>
<template #icon>
<SearchOutlined style="font-size: 16px"/>
</template>
</AButton>
</template>
</AInput>
<template #content>
<AFlex :vertical="false" align="center" justify="space-between" class="header-search-content-header">
<span>搜索历史</span>
<AButton type="text" size="small" style="color: #707072">清空搜索历史</AButton>
</AFlex>
<div class="header-search-content-body">
<ATag :style="{
padding: '5px 15px',
fontSize: '15px',
marginTop: '20px',
}" :bordered="false" closable>搜索历史1
</ATag>
</div>
</template>
</APopover>
</AFlex>
<AFlex :vertical="false" align="center" justify="flex-end" class="header-menu-container">
<AFlex :vertical="false" align="center" justify="flex-start" class="header-menu-item">
<ABadge count="0" :numberStyle="{
marginTop: '5px',
}">
<ABadge count="5" :numberStyle="{marginTop: '10px', marginRight: '5px'}">
<AButton type="text" shape="circle" size="large" class="header-menu-item-btn"
:icon="h(BellOutlined)"/>
</ABadge>
</AFlex>
<AFlex :vertical="false" align="center" justify="flex-start" class="header-menu-item">
<ADropdown>
<template #overlay>
<AMenu @click="changeLang">
<AMenuItem key="zh">{{ t("landing.chinese") }}</AMenuItem>
<AMenuItem key="en">{{ t("landing.english") }}</AMenuItem>
</AMenu>
</template>
<AButton type="text" shape="circle" size="large" :icon="h(TranslationOutlined)">
</AButton>
</ADropdown>
</AFlex>
<!-- <AFlex :vertical="false" align="center" justify="flex-start" class="header-menu-item">-->
<!-- <ADropdown>-->
<!-- <template #overlay>-->
<!-- <AMenu @click="changeLang">-->
<!-- <AMenuItem key="zh">{{ t("landing.chinese") }}</AMenuItem>-->
<!-- <AMenuItem key="en">{{ t("landing.english") }}</AMenuItem>-->
<!-- </AMenu>-->
<!-- </template>-->
<!-- <AButton type="text" shape="circle" size="large" :icon="h(TranslationOutlined)">-->
<!-- </AButton>-->
<!-- </ADropdown>-->
<!-- </AFlex>-->
<AFlex :vertical="false" align="center" justify="flex-start" class="header-user-container">
<AAvatar :size="35" class="header-user-avatar" :src="user.user.avatar"/>
<AAvatar :size="40" class="header-user-avatar" :src="user.user.avatar"/>
</AFlex>
</AFlex>
</div>
@@ -36,23 +62,22 @@
<script lang="ts" setup>
import logo from "@/assets/svgs/logo-album.svg";
import useStore from "@/store";
import {BellOutlined, TranslationOutlined} from "@ant-design/icons-vue";
import {BellOutlined} from "@ant-design/icons-vue";
import {h} from "vue";
import {useI18n} from "vue-i18n";
const user = useStore().user;
const {t, locale} = useI18n();
const lang = useStore().lang;
// const lang = useStore().lang;
const router = useRouter();
/**
* 切换语言
* @param language
*/
async function changeLang(language: any) {
lang.lang = language.key;
locale.value = language.key;
}
// /**
// * 切换语言
// * @param language
// */
// async function changeLang(language: any) {
// lang.lang = language.key;
// locale.value = language.key;
// }
</script>
<style scoped lang="scss" src="./index.scss">

View File

@@ -1,19 +1,15 @@
.header-main {
height: 60px;
height: 70px;
width: 100%;
min-height: 70px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.38);
backdrop-filter: blur(20px);
transition: background-color 0.3s;
z-index: 3;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
//border-radius: 20px;
position: fixed;
top: 0;
left: 0;
//position: fixed;
.header-container {
@@ -21,40 +17,65 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 1200px;
max-width: 1200px;
width: 100%;
height: 100%;
padding: 0 2%;
.header-logo-text {
margin-left: 10px;
font-size: 25px;
font-family: "Comic Sans MS", cursive;
font-weight: bolder;
}
}
.header-logo-container {
min-width: 30%;
cursor: pointer;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
.header-logo-container {
min-width: 280px;
cursor: pointer;
}
.header-menu-container {
width: 30%;
.header-menu-item {
min-width: 50px;
.header-menu-item-btn {
font-size: 16px;
.header-logo-text {
margin-left: 2%;
font-size: 200%;
font-family: "Comic Sans MS", cursive;
font-weight: bolder;
}
}
.header-user-container {
min-width: 130px;
.header-search-container {
width: 30%;
.header-search-input {
border-radius: 20px;
}
.header-search-content-body {
margin-top: 10px;
.header-user-avatar {
cursor: pointer;
}
}
.header-menu-container {
width: 30%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.header-menu-item {
width: 85%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.header-user-container {
.header-user-avatar {
cursor: pointer;
}
}
}
}
}

View File

@@ -0,0 +1,80 @@
<template>
<div>
<AMenu
v-model:selectedKeys="state.selectedKeys"
class="sidebar"
:selectable="true"
:multiple="false"
mode="vertical"
v-model:openKeys="state.openKeys"
:inlineIndent="30"
:inlineCollapsed="false"
:items="items"
@click="handleClick"
>
</AMenu>
</div>
</template>
<script lang="ts" setup>
import {VueElement, h, reactive} from 'vue';
import {
PictureOutlined,
TagsOutlined,
AppstoreOutlined,
UploadOutlined,
RestOutlined,
TagOutlined,
SmileOutlined,
EnvironmentOutlined,
AppstoreAddOutlined
} from '@ant-design/icons-vue';
import {ItemType} from 'ant-design-vue';
import {useI18n} from 'vue-i18n';
const {t} = useI18n();
const router = useRouter();
const state = reactive({
openKeys: [router.currentRoute.value.path.split('/').slice(-1)],
selectedKeys: [router.currentRoute.value.path.split('/').slice(-1)],
});
function getItem(
label: VueElement | string,
key: string,
icon?: any,
children?: ItemType[],
type?: 'group',
): ItemType {
return {
key,
icon,
children,
label,
type,
} as ItemType;
}
const items: ItemType[] = reactive([
getItem(t('album.photo'), 'photo', () => h(AppstoreOutlined), [
getItem(t('album.allAlbums'), 'all', () => h(PictureOutlined)),
getItem(t('album.recentUploads'), 'recent', () => h(UploadOutlined)),
]),
getItem(t('album.albums'), 'album', () => h(TagsOutlined), [
getItem(t('album.albums'), 'albums', () => h(TagOutlined)),
getItem(t('album.peopleAlbums'), 'people', () => h(SmileOutlined)),
getItem(t('album.locationAlbums'), 'location', () => h(EnvironmentOutlined)),
getItem(t('album.thingsAlbums'), 'thing', () => h(AppstoreAddOutlined)),
]),
getItem(t('album.recyclingBin'), 'recycling', () => h(RestOutlined)),
getItem('网盘导入', '10'),
]);
function handleClick({keyPath}) {
const path = keyPath.join('/');
router.push(`/main/${path}`);
}
</script>
<style scoped lang="scss" src="./index.scss">
</style>

View File

@@ -0,0 +1,6 @@
.sidebar {
width: 15vw;
height: calc(100vh - 70px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

View File

@@ -135,5 +135,15 @@ export default {
reportSeletion: 'Please select the reason for reporting the comment',
view: 'look',
replies: 'replies',
},
album: {
photo: 'Photo',
allAlbums: 'All Albums',
recentUploads: 'Recent Uploads',
albums: 'Albums',
peopleAlbums: 'People',
locationAlbums: 'Location',
thingsAlbums: 'Things',
recyclingBin: 'Recycling Bin',
}
};

View File

@@ -137,6 +137,16 @@ export default {
reportSeletion: '请选择举报原因',
view: '查看',
replies: '条回复',
},
album: {
photo: '照片',
allAlbums: '所有相册',
recentUploads: '最近上传',
albums: '相册',
peopleAlbums: '人物',
locationAlbums: '地点',
thingsAlbums: '事物',
recyclingBin: '回收站',
}
};

View File

@@ -0,0 +1,43 @@
import Phoalbum from "@/views/Album/Phoalbum/Phoalbum.vue";
import PeopleAlbum from "@/views/Album/PeopleAlbum/PeopleAlbum.vue";
import LocationAlbum from "@/views/Album/LocationAlbum/LocationAlbum.vue";
import ThingAlbum from "@/views/Album/ThingAlbum/ThingAlbum.vue";
export default [
{
path: '/main/album/albums',
name: 'albums',
component: Phoalbum,
meta: {
requiresAuth: true,
title: '相册'
},
},
{
path: '/main/album/people',
name: 'people',
component: PeopleAlbum,
meta: {
requiresAuth: true,
title: '人物相册'
},
},
{
path: '/main/album/location',
name: 'location',
component: LocationAlbum,
meta: {
requiresAuth: true,
title: '地点相册'
},
},
{
path: '/main/album/thing',
name: 'thing',
component: ThingAlbum,
meta: {
requiresAuth: true,
title: '事物相册'
},
},
];

View File

@@ -1,11 +1,22 @@
import photo from "@/router/modules/photos.ts";
import albums from "@/router/modules/albums.ts";
import recycling_bin from "@/router/modules/recycling_bin.ts";
export default [
{
path: '/main',
name: 'main',
redirect: '/main/photos',
component: () => import('@/views/Main/MainPage.vue'),
meta: {
requiresAuth: true,
title: '主页'
}
},
children: [
...photo,
...albums,
...recycling_bin
]
}
];

View File

@@ -0,0 +1,23 @@
import AllPhoto from "@/views/Photograph/AllPhoto/AllPhoto.vue";
import RecentUpload from "@/views/Photograph/RecentUpload/RecentUpload.vue";
export default [
{
path: '/main/photo/all',
name: 'photos',
component: AllPhoto,
meta: {
requiresAuth: true,
title: '全部照片'
},
},
{
path: '/main/photo/recent',
name: 'recent',
component: RecentUpload,
meta: {
requiresAuth: true,
title: '最近上传'
},
},
];

View File

@@ -0,0 +1,13 @@
import RecyclingBin from "@/views/RecyclingBin/RecyclingBin.vue";
export default [
{
path: '/main/recycling',
name: 'recycling',
component: RecyclingBin,
meta: {
requiresAuth: true,
title: '回收站'
},
},
];

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

View File

@@ -1,21 +1,26 @@
<template>
<div class="main-page">
<Header/>
<div style="margin-top: 100px">
<CommentReply/>
<div class="main-header">
<Header/>
</div>
<div class="main-content">
<Sidebar/>
<div class="main-content-container">
<!-- <ACard class="main-container-card">-->
<router-view/>
<!-- </ACard>-->
</div>
</div>
</div>
</template>
<script setup lang="ts">
import useStore from "@/store";
import CommentReply from "@/components/CommentReply/index.vue";
import {h, onMounted, onUnmounted} from "vue";
import Header from "@/layout/default/Header/Header.vue";
import {notification} from "ant-design-vue";
import {SmileOutlined} from "@ant-design/icons-vue";
import Sidebar from "@/layout/default/Sidebar/Sidebar.vue";
const websocket = useStore().websocket;

View File

@@ -1,9 +1,35 @@
.main-page {
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--background-color);
background-color: #eaeef6;
color: var(--text-color);
width: 100%;
min-height: 100vh;
.main-header {
}
.main-content {
display: flex;
flex-direction: row;
.main-content-container {
display: flex;
flex-direction: column;
//justify-content: center;
//align-items: center;
width: calc(100% - 15vw);
height: calc(100vh - 90px);
padding: 10px;
.main-container-card {
width: 100%;
height: 100%;
overflow: scroll;
}
}
}
}

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
</script>
<template>
<div>
<p>AllPhoto</p>
</div>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss" src="./index.scss">
</style>

View File