Merge remote-tracking branch 'origin/main' into eh4pzj-codex/add-whitelist-invitation-registration-mode

This commit is contained in:
tim
2025-07-14 22:05:27 +08:00
15 changed files with 334 additions and 12 deletions

View File

@@ -17,6 +17,15 @@
<i class="menu-item-icon fas fa-info-circle"></i>
<span class="menu-item-text">关于</span>
</router-link>
<router-link
v-if="shouldShowStats"
class="menu-item"
exact-active-class="selected"
to="/about/stats"
>
<i class="menu-item-icon fas fa-chart-line"></i>
<span class="menu-item-text">站点统计</span>
</router-link>
<router-link class="menu-item" exact-active-class="selected" to="/new-post">
<i class="menu-item-icon fas fa-edit"></i>
<span class="menu-item-text">发帖</span>
@@ -109,6 +118,9 @@ export default {
},
showUnreadCount() {
return this.unreadCount > 99 ? '99+' : this.unreadCount
},
shouldShowStats() {
return authState.role === 'ADMIN'
}
},
async mounted() {

View File

@@ -41,7 +41,7 @@ const fetchTypes = async () => {
try {
const token = getToken()
const res = await fetch(`${API_BASE_URL}/api/reaction-types`, {
headers: { Authorization: `Bearer ${token}` }
headers: { Authorization: token ? `Bearer ${token}` : '' }
})
if (res.ok) {
cachedTypes = await res.json()

View File

@@ -9,14 +9,14 @@ import { checkToken, clearToken } from './utils/auth'
import { initTheme } from './utils/theme'
// Configurable API domain and port
// export const API_DOMAIN = 'http://127.0.0.1'
// export const API_PORT = 8081
export const API_DOMAIN = 'http://127.0.0.1'
export const API_PORT = 8081
export const API_DOMAIN = 'http://47.82.99.208'
export const API_PORT = 8080
// export const API_DOMAIN = 'http://47.82.99.208'
// export const API_PORT = 8080
// export const API_BASE_URL = API_PORT ? `${API_DOMAIN}:${API_PORT}` : API_DOMAIN
export const API_BASE_URL = "";
export const API_BASE_URL = API_PORT ? `${API_DOMAIN}:${API_PORT}` : API_DOMAIN
// export const API_BASE_URL = "";
export const GOOGLE_CLIENT_ID = '777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com'
export const toast = useToast()

View File

@@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
import HomePageView from '../views/HomePageView.vue'
import MessagePageView from '../views/MessagePageView.vue'
import AboutPageView from '../views/AboutPageView.vue'
import SiteStatsPageView from '../views/SiteStatsPageView.vue'
import PostPageView from '../views/PostPageView.vue'
import LoginPageView from '../views/LoginPageView.vue'
import SignupPageView from '../views/SignupPageView.vue'
@@ -27,6 +28,11 @@ const routes = [
name: 'about',
component: AboutPageView
},
{
path: '/about/stats',
name: 'site-stats',
component: SiteStatsPageView
},
{
path: '/new-post',
name: 'new-post',

View File

@@ -60,11 +60,10 @@ export default {
<style scoped>
.about-page {
padding: 20px;
max-width: var(--page-max-width);
background-color: var(--background-color);
margin: 0 auto;
height: calc(100vh - var(--header-height) - 40px);
height: calc(100vh - var(--header-height));
overflow-y: auto;
}
@@ -87,5 +86,6 @@ export default {
.about-content {
line-height: 1.6;
padding: 20px;
}
</style>

View File

@@ -0,0 +1,53 @@
<template>
<div class="site-stats-page">
<VChart v-if="option" :option="option" :autoresize="true" style="height:400px" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { LineChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent, GridComponent, DataZoomComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import { API_BASE_URL } from '../main'
import { getToken } from '../utils/auth'
use([LineChart, TitleComponent, TooltipComponent, GridComponent, DataZoomComponent, CanvasRenderer])
const option = ref(null)
async function loadData() {
const token = getToken()
const res = await fetch(`${API_BASE_URL}/api/stats/dau-range?days=30`, {
headers: { Authorization: `Bearer ${token}` }
})
if (res.ok) {
const data = await res.json()
data.sort((a, b) => new Date(a.date) - new Date(b.date))
const dates = data.map(d => d.date)
const values = data.map(d => d.value)
option.value = {
title: { text: '站点 DAU' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: dates },
yAxis: { type: 'value' },
dataZoom: [{ type: 'slider', start: 80 }, { type: 'inside' }],
series: [{ type: 'line', areaStyle: {}, smooth: true, data: values }]
}
}
}
onMounted(loadData)
</script>
<style scoped>
.site-stats-page {
padding: 20px;
max-width: var(--page-max-width);
background-color: var(--background-color);
margin: 0 auto;
height: calc(100vh - var(--header-height) - 40px);
}
</style>