🚧 add user center and account setting page

This commit is contained in:
2025-02-28 01:33:18 +08:00
parent 41fdc58c4e
commit 1c3ee31c0b
22 changed files with 288 additions and 49 deletions

4
components.d.ts vendored
View File

@@ -15,6 +15,10 @@ declare module 'vue' {
ACard: typeof import('ant-design-vue/es')['Card']
ACascader: typeof import('ant-design-vue/es')['Cascader']
AccountSetting: typeof import('./src/views/User/AccountSetting/AccountSetting.vue')['default']
AccountSettingHome: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/AccountSettingHome.vue')['default']
AccountSettingInfo: typeof import('./src/views/User/AccountSetting/components/AccountSettingInfo/AccountSettingInfo.vue')['default']
AccountSettingSidebar: typeof import('./src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue')['default']
AccountSettingStorage: typeof import('./src/views/User/AccountSetting/components/AccountSettingStorage/AccountSettingStorage.vue')['default']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADivider: typeof import('ant-design-vue/es')['Divider']

View File

@@ -132,11 +132,12 @@ export const albumListApi = (type: number, sort: boolean) => {
* @param provider
* @param bucket
*/
export const queryAlbumDetailListApi = (id: number, provider: string, bucket: string) => {
export const queryAlbumDetailListApi = (id: number, provider: string, bucket: string,type:number) => {
return service.Post('/api/auth/storage/album/detail/list', {
id: id,
provider: provider,
bucket: bucket,
type:type,
}, {
cacheFor: {
expire: 60 * 60 * 24 * 7,

View File

@@ -0,0 +1 @@
<svg t="1740663490419" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6329" width="200" height="200"><path d="M512 64l387.98 224v448L512 960 124.02 736V288z" fill="#C3D2FB" p-id="6330"></path><path d="M512 220.8l252.19 145.6v291.2L512 803.2 259.81 657.6V366.4z" fill="#386BF3" p-id="6331"></path><path d="M512 377.6l116.39 67.2v134.4L512 646.4l-116.39-67.2V444.8z" fill="#3D4265" p-id="6332"></path></svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@@ -1,7 +1,7 @@
<template>
<header class="header-main">
<div class="header-container">
<Logo/>
<Logo :logo-color="props.logoColor"/>
<Search/>
<Menu/>
</div>
@@ -11,6 +11,13 @@
import Logo from "@/layout/default/Header/Logo.vue";
import Search from "@/layout/default/Header/Search.vue";
import Menu from "@/layout/default/Header/Menu.vue";
const props = defineProps({
logoColor: {
type: String,
default: "black",
},
});
</script>
<style scoped lang="scss">
.header-main {

View File

@@ -75,7 +75,7 @@
<AAvatar :size="50" shape="square" :src="logo"/>
</ACard>
</APopover>
<span class="header-logo-text" @click="router.push('/')">S.Album</span>
<span class="header-logo-text" :style="{color:props.logoColor}" @click="router.push('/')">S.Album</span>
</AFlex>
</template>
<script setup lang="ts">
@@ -96,6 +96,14 @@ const router = useRouter();
const {t} = useI18n();
const cardShadow = ref('none');
const props = defineProps({
logoColor: {
type: String,
default: 'black'
}
});
/**
* 监听卡片聚焦事件
*/

View File

@@ -22,13 +22,13 @@
</div>
<!-- 社区按钮 -->
<div class="button-wrapper">
<AButton type="text" shape="circle" size="large" class="header-menu-item-btn">
<template #icon>
<AAvatar size="default" shape="circle" :src="community"/>
</template>
</AButton>
</div>
<!-- <div class="button-wrapper">-->
<!-- <AButton type="text" shape="circle" size="large" class="header-menu-item-btn">-->
<!-- <template #icon>-->
<!-- <AAvatar size="default" shape="circle" :src="community"/>-->
<!-- </template>-->
<!-- </AButton>-->
<!-- </div>-->
<!-- 上传按钮 -->
<div class="button-wrapper">
<AButton type="text" shape="circle" size="large" class="header-menu-item-btn"
@@ -54,19 +54,19 @@
</AButton>
<template #overlay>
<AMenu>
<AMenuItem key="reply">
<AMenuItem key="reply" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="atme"/>
</template>
<span style="font-weight: bold">回复我的</span>
</AMenuItem>
<AMenuItem key="like">
<AMenuItem key="like" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="like"/>
</template>
<span style="font-weight: bold">收到的赞</span>
</AMenuItem>
<AMenuItem key="message">
<AMenuItem key="message" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="systemMessage"/>
</template>
@@ -112,19 +112,19 @@
<ADivider/>
<div class="avatar-content-menu">
<AMenu>
<AMenuItem key="1" @click="router.push('/main/user/center/home')">
<AMenuItem key="1" @click="router.push('/main/user/center/home')" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="personalCenter"/>
</template>
<span class="avatar-content-menu-item">个人中心</span>
</AMenuItem>
<AMenuItem key="2" @click="router.push('/main/user/setting')">
<AMenuItem key="2" @click="router.push('/main/user/setting')" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="accountSetting"/>
</template>
<span class="avatar-content-menu-item">账号设置</span>
</AMenuItem>
<AMenuItem key="3">
<AMenuItem key="3" :style="menuCSSStyle">
<template #icon>
<AAvatar size="small" shape="circle" :src="logout"/>
</template>
@@ -142,7 +142,6 @@
</template>
<script setup lang="ts">
import community from "@/assets/svgs/community.svg";
import upload from "@/assets/svgs/upload.svg";
import notice from "@/assets/svgs/notice.svg";
import atme from "@/assets/svgs/atme.svg";
@@ -173,6 +172,11 @@ async function getUserConfigList() {
}
}
const menuCSSStyle: any = reactive({
display: 'flex',
alignItems: 'center',
});
onMounted(() => {
getUserConfigList();
@@ -210,6 +214,7 @@ onMounted(() => {
border-radius: 50%;
overflow: hidden;
transition: box-shadow 0.3s ease, transform 0.3s ease;
border: 1px solid rgb(255, 255, 255);
}
.button-wrapper:hover {
@@ -255,6 +260,7 @@ onMounted(() => {
border-radius: 50%;
overflow: hidden;
transition: box-shadow 0.3s ease, transform 0.3s ease;
border: 1px solid rgb(255, 255, 255);
}
.avatar-wrapper:hover {
@@ -277,10 +283,10 @@ onMounted(() => {
box-shadow: 0 0 10px rgba(112, 112, 114, 0.82);
}
.card-avatar:hover {
box-shadow: 0 0 10px rgba(77, 167, 255, 0.89);
transform: scale(1.1);
}
//.card-avatar:hover {
// box-shadow: 0 0 10px rgba(77, 167, 255, 0.89);
// transform: scale(1.1);
//}
.avatar-content-header-info {
width: 68%;

View File

@@ -1,7 +1,7 @@
export default [
{
path: '/main/photo/share',
name: 'share',
name: 'quick-share',
component: () => import('@/views/Share/ImageShare/ImageShare.vue'),
meta: {
requiresAuth: true,

View File

@@ -4,6 +4,10 @@ import UserCenterInfo from "@/views/User/PersonalCenter/components/UserCenterInf
import UserCenterAnalysis from "@/views/User/PersonalCenter/components/UserCenterAnalysis/UserCenterAnalysis.vue";
import UserCenterShare from "@/views/User/PersonalCenter/components/UserCenterShare/UserCenterShare.vue";
import UserCenterSetting from "@/views/User/PersonalCenter/components/UserCenterSetting/UserCenterSetting.vue";
import AccountSettingHome from "@/views/User/AccountSetting/components/AccountSettingHome/AccountSettingHome.vue";
import AccountSettingInfo from "@/views/User/AccountSetting/components/AccountSettingInfo/AccountSettingInfo.vue";
import AccountSettingStorage from "@/views/User/AccountSetting/components/AccountSettingStorage/AccountSettingStorage.vue";
export default [
{
@@ -18,7 +22,7 @@ export default [
children: [
{
path: '/main/user/center/home',
name: 'home',
name: 'userCenterHome',
component: UserCenterHome,
meta: {
requiresAuth: true,
@@ -27,7 +31,7 @@ export default [
},
{
path: '/main/user/center/dynamic',
name: 'dynamic',
name: 'UserCenterDynamic',
component: UserCenterDynamic,
meta: {
requiresAuth: true,
@@ -36,7 +40,7 @@ export default [
},
{
path: '/main/user/center/info',
name: 'info',
name: 'UserCenterInfo',
component: UserCenterInfo,
meta: {
requiresAuth: true,
@@ -45,7 +49,7 @@ export default [
},
{
path: '/main/user/center/analysis',
name: 'analysis',
name: 'UserCenterAnalysis',
component: UserCenterAnalysis,
meta: {
requiresAuth: true,
@@ -54,7 +58,7 @@ export default [
},
{
path: '/main/user/center/share',
name: 'share',
name: 'UserCenterShare',
component: UserCenterShare,
meta: {
requiresAuth: true,
@@ -63,7 +67,7 @@ export default [
},
{
path: '/main/user/center/setting',
name: 'setting',
name: 'UserCenterSetting',
component: UserCenterSetting,
meta: {
requiresAuth: true,
@@ -75,10 +79,40 @@ export default [
{
path: '/main/user/setting',
name: 'userSetting',
redirect: '/main/user/setting/home',
component: () => import('@/views/User/AccountSetting/AccountSetting.vue'),
meta: {
requiresAuth: true,
title: '账户设置'
}
},
children: [
{
path: '/main/user/setting/home',
name: 'AccountSettingHome',
component: AccountSettingHome,
meta: {
requiresAuth: true,
title: '主页'
},
},
{
path: '/main/user/setting/info',
name: 'AccountSettingInfo',
component: AccountSettingInfo,
meta: {
requiresAuth: true,
title: '个人信息'
},
},
{
path: '/main/user/setting/storage',
name: 'AccountSettingStorage',
component: AccountSettingStorage,
meta: {
requiresAuth: true,
title: '存储管理'
},
}
],
}
];

View File

@@ -7,6 +7,7 @@ import {useUpscaleStore} from "@/store/modules/upscaleStore.ts";
import {useMenuStore} from "@/store/modules/menuStore.ts";
import {useUploadStore} from "@/store/modules/uploadStore.ts";
import {useImageStore} from "@/store/modules/imageStore.ts";
import {useShareStore} from "@/store/modules/shareStore.ts";
export default function useStore() {
return {
@@ -19,5 +20,6 @@ export default function useStore() {
menu: useMenuStore(),
upload: useUploadStore(),
image: useImageStore(),
share: useShareStore(),
};
}

View File

@@ -4,9 +4,12 @@ export const useMenuStore = defineStore(
const currentMenu = ref<string>('photo/all');
const userCenterMenu = ref<string>('home');
const accountSettingMenu = ref<string>('home');
return {
currentMenu,
userCenterMenu,
accountSettingMenu
};
},
{
@@ -15,7 +18,7 @@ export const useMenuStore = defineStore(
persist: true,
storage: localStorage,
key: 'menu',
includePaths: ['currentMenu', 'userCenterMenu']
includePaths: ['currentMenu', 'userCenterMenu', 'accountSettingMenu']
}
}
);

View File

@@ -0,0 +1,30 @@
export const useShareStore = defineStore(
'share',
() => {
const sharePassword = reactive<Record<string, string>>({});
// 获取密码保持相同API
const getPassword = (key: string): string | undefined => {
return sharePassword[key];
};
// 添加密码保持相同API
const addPassword = (key: string, password: string) => {
sharePassword[key] = password;
};
return {
sharePassword,
getPassword,
addPassword
};
},
{
// 开启数据持久化
persistedState: {
persist: true,
storage: localStorage,
key: 'share',
includePaths: ['sharePassword']
}
}
);

View File

@@ -4,7 +4,7 @@
<div class="phoalbum-item"
v-for="(album, index) in imageStore.albumList"
:key="album.id"
@click.prevent="handleClick(album.id,album.name)"
@click.prevent="handleClick(album.id,album.name,album.type)"
@mouseover="isHovered = index"
@mouseleave="isHovered = null">
<PhotoStack :src="album.cover_image ?`data:image/png;base64,`+album.cover_image: ``"
@@ -80,10 +80,11 @@ const albumRenameValue = ref<string>("");
* 点击相册跳转到详情页
* @param id
* @param albumName
* @param type
*/
function handleClick(id: number, albumName: string) {
function handleClick(id: number, albumName: string, type: number) {
router.push({
path: route.path + `/${id}`, query: {name: albumName}
path: route.path + `/${id}`, query: {name: albumName, type: type}
});
}

View File

@@ -86,7 +86,7 @@ const upload = useStore().upload;
async function getImageList(id: number) {
imageStore.imageListLoading = true;
const res: any = await queryAlbumDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1]);
const res: any = await queryAlbumDetailListApi(id, upload.storageSelected?.[0], upload.storageSelected?.[1], parseInt(route.query.type as string));
if (res && res.code === 200) {
imageList.value = res.data.records;
}

View File

@@ -1,7 +1,7 @@
<template>
<div class="main-page">
<div class="main-header">
<Header/>
<Header :logo-color="`black`"/>
</div>
<div class="main-content">
<Sidebar/>

View File

@@ -364,8 +364,6 @@ async function customUploader() {
expire_date: expire_date.value,
access_limit: access_limit.value,
access_password: access_password.value,
provider: upload.storageSelected?.[0],
bucket: upload.storageSelected?.[1],
images: images,
};
watch(

View File

@@ -10,8 +10,10 @@
<div class="share-content-header">
<AButton type="link" size="large" class="share-content-header-button">图片列表</AButton>
</div>
<div class="share-content-verify" v-if="imageList && imageList.length <= 0">
<AInputPassword size="large" placeholder="请输入访问密码" style="width: 20%" @pressEnter="getShareImages"/>
<div class="share-content-verify"
v-if="imageList && imageList.length <= 0 && !shareStore.getPassword(route.params.id as string)">
<AInputPassword size="large" placeholder="请输入访问密码" style="width: 20%"
@pressEnter="(e)=>getShareImages(e.target.value)"/>
<p style="font-size: 12px;color: #999;">回车后可查看图片列表</p>
</div>
<ImageWaterfallList :image-list="imageList"/>
@@ -33,22 +35,34 @@ const imageList = ref<any[]>([]);
const route = useRoute();
const imageStore = useStore().image;
const shareStore = useStore().share;
/**
* 获取分享图片列表
* @param e
* @param password
*/
async function getShareImages(e) {
async function getShareImages(password: string) {
imageStore.imageListLoading = true;
const invite_code = route.params.id;
const code = Array.isArray(invite_code) ? invite_code[0] : invite_code;
const res: any = await queryShareImageApi(code, e.target.value);
const res: any = await queryShareImageApi(code, password);
if (res && res.code === 200) {
imageList.value = res.data.records;
shareStore.addPassword(code, password);
}
imageStore.imageListLoading = false;
}
onMounted(() => {
const invite_code = route.params.id;
const code = Array.isArray(invite_code) ? invite_code[0] : invite_code;
const password = shareStore.getPassword(code);
if (password) {
getShareImages(password);
}
});
</script>
<style scoped lang="scss">

View File

@@ -1,11 +1,38 @@
<script setup lang="ts">
</script>
<template>
<div class="account-setting">
<div class="personal-center-header">
<Header :logo-color="'black'"/>
</div>
<div class="account-setting-container">
<AccountSettingSidebar/>
<div class="account-setting-view">
<router-view/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import Header from "@/layout/default/Header/Header.vue";
import AccountSettingSidebar
from "@/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue";
</script>
<style scoped lang="scss">
.account-setting {
background-color: #eaeef6;
.account-setting-container {
display: flex;
flex-direction: row;
width: 100%;
height: calc(100vh - 70px);
.account-setting-view {
width: calc(100vw - 230px);
height: calc(100vh - 100px);
max-height: calc(100vh - 100px);
padding: 15px;
overflow: auto;
}
}
}
</style>

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
<template>
<div class="account-setting-sidebar">
<AMenu
:selectedKeys="[menuStore.accountSettingMenu]"
:selectable="true"
:multiple="false"
mode="vertical"
:inlineIndent="24"
:inlineCollapsed="false"
@select="handleClick"
style="margin-top: 10px"
>
<AMenuItem title="首页" key="home" :style="menuCSSStyle">
<template #icon>
<AAvatar shape="square" size="small" :src="home"/>
</template>
<span class="ant-menu-item-title">首页</span>
</AMenuItem>
<AMenuItem title="我的信息" key="info" :style="menuCSSStyle">
<template #icon>
<AAvatar shape="square" size="small" :src="peopleAlbum"/>
</template>
<span class="ant-menu-item-title">我的信息</span>
</AMenuItem>
<AMenuItem title="存储管理" key="storage" :style="menuCSSStyle">
<template #icon>
<AAvatar shape="square" size="small" :src="storage"/>
</template>
<span class="ant-menu-item-title">存储管理</span>
</AMenuItem>
</AMenu>
</div>
</template>
<script setup lang="ts">
import useStore from "@/store";
import home from "@/assets/svgs/home.svg";
import peopleAlbum from "@/assets/svgs/people-album.svg";
import storage from "@/assets/svgs/storage.svg";
const menuStore = useStore().menu;
const menuCSSStyle: any = reactive({
display: 'flex',
alignItems: 'center',
});
const router = useRouter();
function handleClick({key}) {
menuStore.accountSettingMenu = key;
router.push(`/main/user/setting/${key}`);
}
</script>
<style scoped lang="scss">
.account-setting-sidebar {
width: 220px;
height: calc(100vh - 70px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
overflow-y: auto;
overflow-x: hidden;
background-color: #fff;
.ant-menu-item-title {
font-size: 15px;
margin-left: 10px;
font-weight: bold;
}
}
</style>

View File

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

View File

@@ -1,7 +1,7 @@
<template>
<div class="personal-center">
<div class="personal-center-header">
<Header style="background: transparent;box-shadow: none;backdrop-filter: none;"/>
<Header :logo-color="'#fff'" style="background: transparent;box-shadow: none;backdrop-filter: none;"/>
<div class="personal-center-header-info">
<div class="personal-center-header-info-container">
<div class="personal-center-header-info-container-avatar">