277 lines
7.7 KiB
Vue
277 lines
7.7 KiB
Vue
|
|
<template>
|
||
|
|
<div class="page-user">
|
||
|
|
<d-skeleton :loading="loadingStatus.bgImageLoading" :rows="4">
|
||
|
|
<header class="page-user-header">
|
||
|
|
<img v-if="banner" class="page-user-banner" :src="banner || defaultBanner" />
|
||
|
|
<img v-else class="page-user-banner" src="@/assets/imgs/user-banner.png" />
|
||
|
|
<!--上传banner-->
|
||
|
|
<div class="page-user-header-actions" v-if="isSelf && false">
|
||
|
|
<d-upload class="page-user-header-button" accept=".png,.jpg,.gif,.jpeg" :before-upload="beforeUpload"
|
||
|
|
@file-select="onBannerUpload">
|
||
|
|
<Icon class="page-user-header-button-icon" name="gt-upload-cover" size="16px" color="#7E7E80" />
|
||
|
|
</d-upload>
|
||
|
|
</div>
|
||
|
|
<!--选择banner-->
|
||
|
|
<div class="page-user-header-actions" v-if="isSelf">
|
||
|
|
<div class="page-user-header-button">
|
||
|
|
<banner-choose @update="onChooseBanner" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
</d-skeleton>
|
||
|
|
<section class="page-user-wrapper">
|
||
|
|
<div class="page-user-info">
|
||
|
|
<user-info @update-banner="onUpdateBanner" @update-info="onUpdateInfo" />
|
||
|
|
<my-organs class="page-user-organizations" />
|
||
|
|
</div>
|
||
|
|
<div class="page-user-content">
|
||
|
|
<div class="page-user-intro mt-24 mb-24">
|
||
|
|
<div class="page-user-intro-md">
|
||
|
|
<md-intro />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="page-user-repos">
|
||
|
|
<panel :blank="false" class="overflow-hidden">
|
||
|
|
<template #header>
|
||
|
|
<div class="page-user-repos-header">
|
||
|
|
<Icon name="gt-folder-c" size="16px" />
|
||
|
|
<span class="page-user-repos-title">精选项目</span>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
<template #headerRight>
|
||
|
|
<repo-select-modal v-if="isSelf" @update="onUpdate" />
|
||
|
|
</template>
|
||
|
|
<DataPanel :empty="!repoList?.length" :loading="repoList?.length ? false : loading" skeleton>
|
||
|
|
<div class="page-user-repos-content" v-loading="loading">
|
||
|
|
<template v-for="(item, index) in repoList.slice(0, 6)" :key="item.id">
|
||
|
|
<repo-item class="page-user-repos-item" :iconHandleList="item.iconHandleList" :id="item.id" :imgSrc="item.imgSrc"
|
||
|
|
:title="item.title" :tag-list="item.tagList" :desc="item.desc" :isStar="item.isStar" :web_url="item.web_url" :topicNames="item.topicNames"
|
||
|
|
:class="{ 'is-last': index === repoList.length - 1 }" @handle-star="({isStar}) => item.isStar = isStar" />
|
||
|
|
</template>
|
||
|
|
</div>
|
||
|
|
</DataPanel>
|
||
|
|
</panel>
|
||
|
|
</div>
|
||
|
|
<div class="page-user-activities">
|
||
|
|
<activity-contributes />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script lang="ts">
|
||
|
|
export default {
|
||
|
|
name: 'User'
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<script lang="ts" setup>
|
||
|
|
import { ref, watch, reactive } from 'vue';
|
||
|
|
import UserInfo from '@/views/User/components/UserInfo.vue';
|
||
|
|
import MyOrgans from '@/views/User/components/my-organs.vue';
|
||
|
|
import MdIntro from '@/views/User/components/md-intro.vue';
|
||
|
|
import ActivityContributes from '@/views/User/components/activity-contributes.vue';
|
||
|
|
import Panel from '@/components/Panel/index.vue';
|
||
|
|
import RepoItem from '@/components/RepoItem/index.vue';
|
||
|
|
import RepoSelectModal from '@/views/User/components/repo-select-modal.vue';
|
||
|
|
import BannerChoose from '@/views/User/components/banner-choose.vue';
|
||
|
|
import { useUserInfo } from '@/views/User/hooks/useUserInfo';
|
||
|
|
import { useRepoList } from '@/views/User/hooks/useRepoList';
|
||
|
|
import { Message } from 'vue-devui/message';
|
||
|
|
import { uploadFile, updateUserProfile } from '@/api/user';
|
||
|
|
import { useRoute, useRouter } from 'vue-router';
|
||
|
|
import { otherAccountStore } from '@/stores/user';
|
||
|
|
import { usePageTitle } from '@/utils/hooks/useTitle';
|
||
|
|
|
||
|
|
const router = useRouter();
|
||
|
|
const route = useRoute();
|
||
|
|
// 精选项目
|
||
|
|
const { namespace, isSelf } = useUserInfo();
|
||
|
|
const { repoList, loading, init } = useRepoList({
|
||
|
|
profile: { params: { username: namespace }},
|
||
|
|
auto: true
|
||
|
|
});
|
||
|
|
|
||
|
|
const banner = ref('');
|
||
|
|
|
||
|
|
const defaultBanner = ref('https://cdn-img.gitcode.com/user-profile/skin-wave.jpg');
|
||
|
|
|
||
|
|
const loadingStatus = reactive({
|
||
|
|
// 加载状态
|
||
|
|
profileLoading: true,
|
||
|
|
bgImageLoading: true,
|
||
|
|
eventsLoading: true,
|
||
|
|
contributorLoading: true,
|
||
|
|
releasesLoading: true
|
||
|
|
});
|
||
|
|
const onUpdateBanner = (img: string) => {
|
||
|
|
banner.value = img;
|
||
|
|
loadingStatus.profileLoading = false;
|
||
|
|
loadingStatus.bgImageLoading = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const beforeUpload = (file: any) => {
|
||
|
|
return false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const infoData = ref<any>(null);
|
||
|
|
const onUpdateInfo = (data: any) => {
|
||
|
|
infoData.value = data;
|
||
|
|
};
|
||
|
|
const onBannerUpload = async(fileInfo: any) => {
|
||
|
|
if (fileInfo.length > 0 && fileInfo[0].size > 500 * 1024) {
|
||
|
|
return Message({
|
||
|
|
type: 'warning',
|
||
|
|
message: '图片大小不超过500kb!'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
const resImg: any = await uploadFile(fileInfo, false, fileInfo[0].type);
|
||
|
|
if (!resImg || resImg.includes('Error')) return false;
|
||
|
|
if (!infoData.value || !infoData.value.profile) return false;
|
||
|
|
banner.value = resImg + '?time' + new Date().getTime();
|
||
|
|
infoData.value.profile.bg_image = resImg;
|
||
|
|
updateUserProfile(infoData.value);
|
||
|
|
};
|
||
|
|
|
||
|
|
const onChooseBanner = (item: { img: string; title: string }) => {
|
||
|
|
const resImg = item.img;
|
||
|
|
banner.value = resImg + '?time' + new Date().getTime();
|
||
|
|
infoData.value.profile.bg_image = resImg;
|
||
|
|
updateUserProfile(infoData.value);
|
||
|
|
};
|
||
|
|
|
||
|
|
const onUpdate = () => {
|
||
|
|
init();
|
||
|
|
};
|
||
|
|
watch(
|
||
|
|
() => route,
|
||
|
|
(val) => {
|
||
|
|
if (val.params.namespace !== namespace) {
|
||
|
|
// 如果从其他用户切换过来导致路由不刷新强制刷新页面
|
||
|
|
router.go(0);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
deep: true
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
const user = otherAccountStore();
|
||
|
|
|
||
|
|
const userPathInfo = user.accountInfo;
|
||
|
|
watch(() => userPathInfo, () => {
|
||
|
|
usePageTitle(userPathInfo.name ? `${userPathInfo.name}(@${userPathInfo.username})` : '个人主页');
|
||
|
|
}, { deep: true, immediate: true });
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped lang="scss">
|
||
|
|
$g-page-user-border-color: #808080;
|
||
|
|
$g-page-user-border-color-second: #e6e6e8;
|
||
|
|
$g-page-user-border-radius: 4px;
|
||
|
|
$g-page-user-border-radius-large: 12px;
|
||
|
|
$g-page-user-bg-color: #d3d3d3;
|
||
|
|
$g-page-user-color: #000000;
|
||
|
|
$g-page-user-color-second: #2d2d2e;
|
||
|
|
$g-page-user-color-third: #7e7e80;
|
||
|
|
|
||
|
|
.page-user {
|
||
|
|
position: relative;
|
||
|
|
max-width: 1376px;
|
||
|
|
margin: 0 auto;
|
||
|
|
padding-top: 24px;
|
||
|
|
|
||
|
|
&-header {
|
||
|
|
position: relative;
|
||
|
|
|
||
|
|
&-button {
|
||
|
|
position: absolute;
|
||
|
|
bottom: 10px;
|
||
|
|
right: 10px;
|
||
|
|
line-height: 1;
|
||
|
|
|
||
|
|
&-icon {
|
||
|
|
color: $g-page-user-color;
|
||
|
|
cursor: pointer;
|
||
|
|
line-height: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
:deep(.button-content) {
|
||
|
|
line-height: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
&:hover {
|
||
|
|
opacity: 0.9;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
&-banner {
|
||
|
|
object-fit: cover;
|
||
|
|
width: 100%;
|
||
|
|
height: 110px;
|
||
|
|
display: block;
|
||
|
|
border-top-left-radius: $g-page-user-border-radius-large;
|
||
|
|
border-top-right-radius: $g-page-user-border-radius-large;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-wrapper {
|
||
|
|
display: flex;
|
||
|
|
position: relative;
|
||
|
|
z-index: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-info {
|
||
|
|
margin-top: -30px;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-content {
|
||
|
|
position: relative;
|
||
|
|
flex: 1;
|
||
|
|
// overflow: hidden;
|
||
|
|
// padding-top: 24px;
|
||
|
|
padding-left: 32px;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-repos {
|
||
|
|
&-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-title {
|
||
|
|
font-size: 14px;
|
||
|
|
font-weight: 500;
|
||
|
|
color: $g-page-user-color-second;
|
||
|
|
margin-left: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
&-content {
|
||
|
|
.is-last {
|
||
|
|
:deep(.g-repo-item) {
|
||
|
|
border-bottom: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
:deep(.g-repo-item) {
|
||
|
|
border-top: none;
|
||
|
|
border-left: none;
|
||
|
|
border-right: none;
|
||
|
|
border-radius: 0;
|
||
|
|
box-shadow: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
&-item {
|
||
|
|
:deep(.repo-title):hover {
|
||
|
|
color: $devui-link;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
&-activities {
|
||
|
|
margin-top: 24px;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|