📝 [docs]: 新增在线展示
This commit is contained in:
@ -1,30 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import {ref, onMounted, onUnmounted} from "vue";
|
||||
import getText from "./scripts/i18n";
|
||||
import {statsApi, GithubStats} from "./scripts/statsApi";
|
||||
import {getTextRef, updateRef} from "./scripts/i18n";
|
||||
|
||||
|
||||
const {Layout} = DefaultTheme;
|
||||
|
||||
const onlineText = getText('online');
|
||||
const totalText = getText('total');
|
||||
let githubStats: GithubStats | null = null;
|
||||
|
||||
const onlineFetchUrl = "https://api.liteyuki.icu/online";
|
||||
const totalFetchUrl = "https://api.liteyuki.icu/count";
|
||||
const dataSections = {
|
||||
total: {
|
||||
name: 'total',
|
||||
color: '#00a6ff',
|
||||
value: ref(0),
|
||||
},
|
||||
online: {
|
||||
name: 'online',
|
||||
color: '#00ff00',
|
||||
value: ref(0),
|
||||
},
|
||||
stars: {
|
||||
name: 'stars',
|
||||
color: '#ffcc00',
|
||||
value: ref(0),
|
||||
},
|
||||
forks: {
|
||||
name: 'forks',
|
||||
color: '#ff6600',
|
||||
value: ref(0),
|
||||
},
|
||||
issues: {
|
||||
name: 'issues',
|
||||
color: '#ff0000',
|
||||
value: ref(0),
|
||||
},
|
||||
prs: {
|
||||
name: 'prs',
|
||||
color: '#ff00ff',
|
||||
value: ref(0),
|
||||
},
|
||||
}
|
||||
|
||||
const online = ref(0);
|
||||
const total = ref(0);
|
||||
|
||||
function updateData() {
|
||||
|
||||
fetch(onlineFetchUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => online.value = data.online)
|
||||
.catch(error => console.error('Error fetching online data:', error));
|
||||
|
||||
fetch(totalFetchUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => total.value = data.register)
|
||||
.catch(error => console.error('Error fetching total data:', error));
|
||||
async function updateData() {
|
||||
// dataSections.online.value.value = await statsApi.getOnline();
|
||||
// dataSections.total.value.value = await statsApi.getTotal();
|
||||
[
|
||||
dataSections.online.value.value,
|
||||
dataSections.total.value.value,
|
||||
githubStats,
|
||||
] = await Promise.all([
|
||||
statsApi.getOnline(),
|
||||
statsApi.getTotal(),
|
||||
statsApi.getGithubStats(),
|
||||
]);
|
||||
dataSections.stars.value.value = githubStats?.stars || 0;
|
||||
dataSections.forks.value.value = githubStats?.forks || 0;
|
||||
dataSections.issues.value.value = githubStats?.issues || 0;
|
||||
dataSections.prs.value.value = githubStats?.prs || 0;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -38,37 +71,45 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="stats-bar">
|
||||
<div class="stats-info">
|
||||
<div id="total" class="section">
|
||||
<div class="line">
|
||||
<span class=dot style="background-color: #00a6ff"></span>
|
||||
<span class="text">{{ totalText }}</span>
|
||||
<Layout>
|
||||
<template #home-features-before>
|
||||
<div class="stats-bar-content">
|
||||
<div class="stats-bar">
|
||||
<div class="stats-info">
|
||||
<div v-for="section in Object.values(dataSections)" :key="section.name" class="section">
|
||||
<div class="section-tab">
|
||||
<span class="dot" :style="{backgroundColor: section.color}"></span>
|
||||
<span class="text">{{ getTextRef(section.name) }}</span>
|
||||
</div>
|
||||
<div class="number">{{ section.value.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="starmap">
|
||||
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="number">{{ total }}</div>
|
||||
</div>
|
||||
<div id="online" class="section">
|
||||
<div class="line">
|
||||
<span class=dot style="background-color: #00ff00"></span>
|
||||
<span class="text">{{ onlineText }}</span>
|
||||
</div>
|
||||
<div class="number">{{ online }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="starmap">
|
||||
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.stats-bar-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
width: 80%;
|
||||
max-width: 1150px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
margin: 30px 10px 10px 10px;
|
||||
margin: 30px;
|
||||
border-radius: var(--border-radius-2);
|
||||
background-color: var(--vp-c-gray-1);
|
||||
flex-direction: column; /* 默认纵向布局 */
|
||||
@ -77,9 +118,10 @@ onMounted(() => {
|
||||
.stats-info {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
margin-bottom: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section {
|
||||
@ -87,16 +129,14 @@ onMounted(() => {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section:not(:last-child) {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin-bottom: 20px;
|
||||
.section-tab {
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center; /* 确保垂直居中 */
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
@ -105,12 +145,15 @@ onMounted(() => {
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.starmap {
|
||||
|
@ -1,174 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useData} from "vitepress";
|
||||
import {ref} from "vue";
|
||||
|
||||
const i18nData = {
|
||||
"zh": {
|
||||
online: '当前在线',
|
||||
offline: '离线',
|
||||
total: '全球实例',
|
||||
fetching: '获取中',
|
||||
},
|
||||
"en": {
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
total: 'Total',
|
||||
fetching: 'Fetching',
|
||||
}
|
||||
}
|
||||
|
||||
function getText(key: string): string {
|
||||
// 转换语言
|
||||
// zh-Hans -> zh
|
||||
// en-US -> en
|
||||
if (useData().site.value.lang.includes('-')) {
|
||||
return i18nData[useData().site.value.lang.split('-')[0]][key];
|
||||
} else {
|
||||
return i18nData[useData().site.value.lang][key];
|
||||
}
|
||||
}
|
||||
|
||||
const onlineText = getText('online');
|
||||
const totalText = getText('total');
|
||||
|
||||
const onlineFetchUrl = "https://api.liteyuki.icu/online";
|
||||
const totalFetchUrl = "https://api.liteyuki.icu/count";
|
||||
|
||||
const online = ref(0);
|
||||
const total = ref(0);
|
||||
|
||||
function updateData() {
|
||||
|
||||
fetch(onlineFetchUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => online.value = data.online)
|
||||
.catch(error => console.error('Error fetching online data:', error));
|
||||
|
||||
fetch(totalFetchUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => total.value = data.register)
|
||||
.catch(error => console.error('Error fetching total data:', error));
|
||||
}
|
||||
|
||||
updateData();
|
||||
setInterval(updateData, 10000);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="stats-bar">
|
||||
<div class="stats-info">
|
||||
<div id="total" class="section">
|
||||
<div class="line">
|
||||
<span class=dot style="background-color: #00a6ff"></span>
|
||||
<span class="text">{{ totalText }}</span>
|
||||
</div>
|
||||
<div class="number">{{ total }}</div>
|
||||
</div>
|
||||
<div id="online" class="section">
|
||||
<div class="line">
|
||||
<span class=dot style="background-color: #00ff00"></span>
|
||||
<span class="text">{{ onlineText }}</span>
|
||||
</div>
|
||||
<div class="number">{{ online }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="starmap">
|
||||
<iframe src="https://starmap.liteyuki.icu/" width="100%" height="300px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.stats-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
margin: 30px 10px 10px 10px;
|
||||
border-radius: var(--border-radius-2);
|
||||
background-color: var(--vp-c-gray-1);
|
||||
flex-direction: column; /* 默认纵向布局 */
|
||||
}
|
||||
|
||||
.stats-info {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section:not(:last-child) {
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.starmap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius-2);
|
||||
}
|
||||
|
||||
.starmap iframe {
|
||||
position: absolute;
|
||||
top: -150px; /* 根据需要调整裁剪位置 */
|
||||
left: -40px; /* 根据需要调整裁剪位置 */;
|
||||
width: calc(100% + 80px); /* 根据需要调整裁剪宽度 */
|
||||
height: calc(100% + 300px); /* 根据需要调整裁剪高度 */
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
/* PC模式下的样式 */
|
||||
.stats-bar {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.stats-info {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.starmap {
|
||||
width: 60%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.starmap iframe {
|
||||
position: absolute;
|
||||
top: -130px; /* 根据需要调整裁剪位置 */
|
||||
left: -60px; /* 根据需要调整裁剪位置 */;
|
||||
width: calc(100% + 120px); /* 根据需要调整裁剪宽度 */
|
||||
height: calc(100% + 280px); /* 根据需要调整裁剪高度 */
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
0
docs/components/Tabs.vue
Normal file
0
docs/components/Tabs.vue
Normal file
@ -1,27 +1,52 @@
|
||||
import {ref} from "vue";
|
||||
import {useData} from "vitepress";
|
||||
|
||||
const i18nData = {
|
||||
"zh": {
|
||||
online: '当前在线',
|
||||
offline: '离线',
|
||||
total: '全球实例',
|
||||
fetching: '获取中',
|
||||
},
|
||||
"en": {
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
total: 'Total',
|
||||
fetching: 'Fetching',
|
||||
}
|
||||
en: {
|
||||
online: 'Online',
|
||||
offline: 'Offline',
|
||||
total: 'Total',
|
||||
fetching: 'Fetching',
|
||||
stars: 'Stars',
|
||||
forks: 'Forks',
|
||||
issues: 'Issues',
|
||||
prs: 'Pull Requests',
|
||||
},
|
||||
zh: {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
total: '实例',
|
||||
fetching: '获取中',
|
||||
stars: '星星',
|
||||
forks: '叉子',
|
||||
issues: '议题',
|
||||
prs: '合并',
|
||||
}
|
||||
}
|
||||
|
||||
export default function getText(key: string): string {
|
||||
// 转换语言
|
||||
// zh-Hans -> zh
|
||||
// en-US -> en
|
||||
if (useData().site.value.lang.includes('-')) {
|
||||
return i18nData[useData().site.value.lang.split('-')[0]][key];
|
||||
} else {
|
||||
return i18nData[useData().site.value.lang][key];
|
||||
}
|
||||
let refData = {}
|
||||
|
||||
function getText(lang: string, key: string): string {
|
||||
lang = formatLang(lang);
|
||||
return i18nData[key][key];
|
||||
}
|
||||
|
||||
function formatLang(lang: string): string {
|
||||
if (lang.includes('-')) {
|
||||
return lang.split('-')[0];
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
export function updateRef() {
|
||||
const lang = useData().site.value.lang;
|
||||
for (let key in refData) {
|
||||
refData[key].value = getText(lang, key);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTextRef(key: string): any {
|
||||
const lang = formatLang(useData().site.value.lang);
|
||||
refData[key] = ref(i18nData[lang][key]);
|
||||
return refData[key]
|
||||
}
|
70
docs/components/scripts/statsApi.ts
Normal file
70
docs/components/scripts/statsApi.ts
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
// URL
|
||||
const OWNER = "LiteyukiStudio"
|
||||
const REPO = "LiteyukiBot"
|
||||
const githubAPIUrl = "https://api.github.com"
|
||||
const onlineFetchUrl = "https://api.liteyuki.icu/online";
|
||||
const totalFetchUrl = "https://api.liteyuki.icu/count";
|
||||
|
||||
|
||||
type GithubStats = {
|
||||
stars: number;
|
||||
forks: number;
|
||||
watchers: number;
|
||||
issues?: number;
|
||||
prs?: number;
|
||||
}
|
||||
|
||||
// 异步接口
|
||||
interface StatsApi {
|
||||
getTotal: () => Promise<number>;
|
||||
getOnline: () => Promise<number>;
|
||||
getGithubStats: () => Promise<GithubStats>;
|
||||
}
|
||||
|
||||
|
||||
export type { GithubStats };
|
||||
|
||||
|
||||
// 实现接口
|
||||
export const statsApi: StatsApi = {
|
||||
getTotal: async () => {
|
||||
try {
|
||||
const res = await fetch(totalFetchUrl);
|
||||
const data = await res.json();
|
||||
return data.register;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getOnline: async () => {
|
||||
try {
|
||||
const res = await fetch(onlineFetchUrl);
|
||||
const data = await res.json();
|
||||
return data.online;
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
getGithubStats: async () => {
|
||||
try {
|
||||
const res = await fetch(`${githubAPIUrl}/repos/${OWNER}/${REPO}`);
|
||||
const data = await res.json();
|
||||
return {
|
||||
stars: data.stargazers_count,
|
||||
forks: data.forks_count,
|
||||
watchers: data.watchers_count,
|
||||
issues: data.open_issues_count,
|
||||
prs: data.open_issues_count,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
stars: -1,
|
||||
forks: -1,
|
||||
watchers: -1,
|
||||
issues: -1,
|
||||
prs: -1,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user