243 lines
7.7 KiB
Vue
243 lines
7.7 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="repo-info">
|
|||
|
|
<d-row :gutter="64">
|
|||
|
|
<d-col flex="1" class="min-w-0">
|
|||
|
|
<div class="flex items-end flex-wrap gap-x-4 gap-y-1">
|
|||
|
|
<div class="text-[20px]">
|
|||
|
|
<span v-for="(path, index) in breadcrumb" :key="index">
|
|||
|
|
<GLink :href="path.href" >
|
|||
|
|
<span class="font-color-t1 font-semibold" :class="!index ? 'max-w-[200px] whitespace-nowrap table-cell overflow-ellipsis overflow-hidden' : 'break-all'">{{ path.label }}</span>
|
|||
|
|
</GLink>
|
|||
|
|
<span v-if="index !== breadcrumb.length - 1" class="breadcrumb-splitter font-color-t1"> / </span>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<template v-for="(item, index) in topicData.officialTopicList" :key="index">
|
|||
|
|
<div class="official-topic mb-0.5">
|
|||
|
|
<GIcon name="gt-plane-officialCertification" />
|
|||
|
|
<span class="ml-1">{{ item.name }}</span>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
<div v-if="repoInfo.visibility === 'private'" class="repo-visible font-color-t2 inline-flex items-center justify-center text-xs">
|
|||
|
|
私有
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-4 flex items-center flex-wrap gap-x-4 gap-y-1" v-if="repoInfo?.forked_from_project">
|
|||
|
|
<span class="font-color-t2 cursor-pointer" :class="{'cursor-pointer': repoInfo.value?.forked_from_project?.web_url}" @click="toForkRepo">
|
|||
|
|
fork 源:{{ repoInfo?.forked_from_project?.name_with_namespace }}
|
|||
|
|
</span>
|
|||
|
|
<div v-if="isAdminOperate" class="fork-btn" @click="handleSync()">
|
|||
|
|
<GIcon name="gt-plane-synchronous" class="mr-1" />
|
|||
|
|
同步源项目
|
|||
|
|
<GIcon size="14" name="gt-line-right" class="ml-1" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<template v-if="isPhone">
|
|||
|
|
<RepoData class="mt-4" :widthType="widthType" @update-star="handleUpdateStarNumber" />
|
|||
|
|
<RepoTopic :tagList="tagList" />
|
|||
|
|
<RepoAction class="mt-3" />
|
|||
|
|
<!-- 只在项目首页展示 -->
|
|||
|
|
<DownloadChart v-if="route?.name === 'repoDashboard'" :repoId="repoId" class="mt-8" />
|
|||
|
|
</template>
|
|||
|
|
<template v-else>
|
|||
|
|
<RepoTopic :tagList="tagList" />
|
|||
|
|
</template>
|
|||
|
|
</d-col>
|
|||
|
|
<d-col v-if="!isPhone">
|
|||
|
|
<RepoData :widthType="widthType" @update-star="handleUpdateStarNumber" />
|
|||
|
|
</d-col>
|
|||
|
|
</d-row>
|
|||
|
|
|
|||
|
|
<d-row align="middle" class="mt-32">
|
|||
|
|
<d-col flex="1">
|
|||
|
|
<NewNavTabs />
|
|||
|
|
</d-col>
|
|||
|
|
<d-col v-if="!isPhone">
|
|||
|
|
<RepoAction />
|
|||
|
|
</d-col>
|
|||
|
|
</d-row>
|
|||
|
|
<SyncFork ref="syncFork" :repoId="repoId" :repoInfo="repoInfo" />
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { ref, onUnmounted, watch, computed } from 'vue';
|
|||
|
|
import RepoData from './RepoData.vue';
|
|||
|
|
import RepoAction from './RepoAction.vue';
|
|||
|
|
import DownloadChart from '@/components/Repo/DownloadChart.vue';
|
|||
|
|
import NewNavTabs from '@/components/NavTabs/NewNavTabs.vue';
|
|||
|
|
import RepoTopic from './RepoTopic.vue';
|
|||
|
|
import SyncFork from './SyncFork.vue';
|
|||
|
|
import { addEventListener, offEvent } from '@/utils/eventBus';
|
|||
|
|
import { reqCatch } from '@/utils/catch';
|
|||
|
|
import { storeToRefs } from 'pinia';
|
|||
|
|
import { useRouter, useRoute } from 'vue-router';
|
|||
|
|
import * as types from '@/api/org/types';
|
|||
|
|
import { getCommunityInfo } from '@/api/org/index';
|
|||
|
|
import { getOrgInfo } from '@/views/Org/hooks/orgInfo';
|
|||
|
|
import { orgInfoStore } from '@/stores/Org/index';
|
|||
|
|
import { useRepoHeaderInit } from '@/utils/hooks/useRepoHeaderInit';
|
|||
|
|
import { useRepoId } from '@/utils/hooks/useRepoId';
|
|||
|
|
import { useAccountStore } from '@/stores/user';
|
|||
|
|
import { getSettingsValues } from '@/api/repo';
|
|||
|
|
import { repoInfoStore } from '@/stores/Repo';
|
|||
|
|
import { useOrgId } from '@/utils/hooks/useOrgId';
|
|||
|
|
import setting from '@/setting';
|
|||
|
|
const props = withDefaults(
|
|||
|
|
defineProps<{
|
|||
|
|
widthType: string;
|
|||
|
|
}>(),
|
|||
|
|
{
|
|||
|
|
widthType: 'md'
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
const { isLogin } = useAccountStore();
|
|||
|
|
const { repoId } = useRepoId();
|
|||
|
|
const { orgId } = useOrgId('/');
|
|||
|
|
const route = useRoute();
|
|||
|
|
const { access_level, repoInfo, isAdminOperate } = storeToRefs(repoInfoStore()); // pinia直接解构会失去响应式
|
|||
|
|
const { setRepoInfo } = repoInfoStore();
|
|||
|
|
// 获取组织namespace
|
|||
|
|
const { namespace } = getOrgInfo();
|
|||
|
|
|
|||
|
|
const { initRepoDashboard, validLinks, topicData } = useRepoHeaderInit(isLogin);
|
|||
|
|
initRepoDashboard();
|
|||
|
|
|
|||
|
|
const tagList = ref(validLinks.value);
|
|||
|
|
const isPhone = computed(() => ['lg', 'sm'].includes(props.widthType));
|
|||
|
|
|
|||
|
|
const visiMap = new Map([
|
|||
|
|
['public', '公开'],
|
|||
|
|
['private', '私有'],
|
|||
|
|
['internal', '内部']
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const toForkRepo = () => {
|
|||
|
|
if (repoInfo.value?.forked_from_project?.web_url) {
|
|||
|
|
location.href = repoInfo.value?.forked_from_project?.web_url;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const router = useRouter();
|
|||
|
|
const breadcrumb = computed(() => {
|
|||
|
|
const pathStrList = repoInfo.value.name_with_namespace?.split('/');
|
|||
|
|
const pathList = pathStrList.map((item) => ({
|
|||
|
|
label: item
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
// 第一个路由带链接
|
|||
|
|
if (pathList.length === 2) {
|
|||
|
|
pathList[0].href = router.resolve({
|
|||
|
|
name: 'homepage',
|
|||
|
|
params: { namespace: orgId.value }
|
|||
|
|
}).path;
|
|||
|
|
pathList[1].href = router.resolve({
|
|||
|
|
name: 'repoDashboard'
|
|||
|
|
}).path;
|
|||
|
|
}
|
|||
|
|
return pathList;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 获取仓库
|
|||
|
|
const orgStore = orgInfoStore();
|
|||
|
|
// 社区基本信息
|
|||
|
|
const getCommunityInfoFun = async() => {
|
|||
|
|
const params: types.commonGroupReqType = {
|
|||
|
|
orgId: namespace.value
|
|||
|
|
};
|
|||
|
|
const { data, error } = await reqCatch(getCommunityInfo, params);
|
|||
|
|
if (!error && data) {
|
|||
|
|
orgStore.setCommunityUrl({
|
|||
|
|
communityUrl: `https://${data.data.custom_domain}`,
|
|||
|
|
communityactivityUrl: `https://${data.data.custom_domain}/activelist`
|
|||
|
|
});
|
|||
|
|
orgStore.setCommunityInfo(data.data);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
async function getModuleState() {
|
|||
|
|
const res = await getSettingsValues({
|
|||
|
|
repoId: repoId.value,
|
|||
|
|
group_id: namespace.value
|
|||
|
|
});
|
|||
|
|
if (res.data) {
|
|||
|
|
const { modules } = res.data.data;
|
|||
|
|
modules.forEach(({ key, value }) => {
|
|||
|
|
if (key === 'COMMUNITY' && value === '1') {
|
|||
|
|
getCommunityInfoFun();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const handleUpdateStarNumber = (params:Record<string, any>) => {
|
|||
|
|
setRepoInfo(params);
|
|||
|
|
};
|
|||
|
|
const syncFork = ref(null);
|
|||
|
|
const handleSync = () => {
|
|||
|
|
syncFork.value.handleOpen();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// @TODO 此处只用于repo切换时,读取项目模块配置信息。是否直接从repo详情获取?
|
|||
|
|
watch(repoId, () => getModuleState(), { flush: 'post', immediate: true });
|
|||
|
|
|
|||
|
|
addEventListener('updateNaviBar', getModuleState);
|
|||
|
|
onUnmounted(() => offEvent('updateNaviBar', getModuleState));
|
|||
|
|
|
|||
|
|
watch(() => topicData.userTopicList, val => {
|
|||
|
|
if (val && val.length) {
|
|||
|
|
tagList.value = validLinks.value.concat(topicData.userTopicList);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
watch(() => validLinks.value, val => {
|
|||
|
|
if (val && val.length) {
|
|||
|
|
tagList.value = validLinks.value.concat(topicData.userTopicList);
|
|||
|
|
}
|
|||
|
|
}, { deep: true });
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.repo-info {
|
|||
|
|
.repo-visible {
|
|||
|
|
border-radius: 14px;
|
|||
|
|
border: 1px solid var(--devui-btn-common-border-color);
|
|||
|
|
padding: 7px;
|
|||
|
|
line-height: 16px;
|
|||
|
|
height: 24px;
|
|||
|
|
}
|
|||
|
|
.official-topic {
|
|||
|
|
background: rgba(#2951E0, 0.12);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
padding: 0 8px;
|
|||
|
|
height: 22px;
|
|||
|
|
color: #2951E0;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
.status-btn-tag {
|
|||
|
|
height: 24px;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
border: 1px solid #e3e3ee;
|
|||
|
|
padding: 0 8px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
&:hover {
|
|||
|
|
background: #f1f1f8;
|
|||
|
|
color: #000 !important;
|
|||
|
|
}
|
|||
|
|
&:not(:last-of-type) {
|
|||
|
|
margin-right: 4px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
.fork-btn {
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #E3E3EE;
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
&:hover {
|
|||
|
|
background: linear-gradient(0deg, rgba(188, 188, 208, 0.1) 0%, rgba(188, 188, 208, 0.1) 100%), var(--White, #fff);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|