feat: add account center

This commit is contained in:
Coldsmile_7
2026-06-05 23:30:30 +08:00
parent 928f742d5e
commit f7a3501262
5 changed files with 157 additions and 37 deletions

View File

@@ -1,11 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import {
loadGitHubPages,
loadGitHubSettings,
saveGitHubPage,
saveGitHubSettings,
} from "./api/githubSite";
import { loadCurrentUser, loginWithEmail, logoutCms } from "./api/auth";
import {
createGitHubRepository,
@@ -14,12 +8,19 @@ import {
logoutGitHub,
startGitHubLogin,
} from "./api/githubAuth";
import {
loadGitHubPages,
loadGitHubSettings,
saveGitHubPage,
saveGitHubSettings,
} from "./api/githubSite";
import {
loadLocalPages,
loadLocalSettings,
saveLocalPage,
saveLocalSettings,
} from "./api/localSite";
import AccountPanel from "./components/AccountPanel.vue";
import AppSidebar from "./components/AppSidebar.vue";
import ConfigPanel from "./components/ConfigPanel.vue";
import ConnectPanel from "./components/ConnectPanel.vue";
@@ -31,6 +32,7 @@ import { mockNavItems, mockPages, mockSidebarGroups, mockSiteSettings } from "./
import type { CmsUser, DataSource, DocPage, GitHubConnection, GitHubRepository, GitHubUser, PanelKey } from "./types";
const panels: Array<{ key: PanelKey; label: string; icon: string }> = [
{ key: "account", label: "用户中心", icon: "U" },
{ key: "connect", label: "仓库连接", icon: "G" },
{ key: "content", label: "页面管理", icon: "P" },
{ key: "structure", label: "站点结构", icon: "S" },
@@ -78,7 +80,6 @@ const repoLabel = computed(() => {
if (dataSource.value === "github" && hasGitHubConnection.value) {
return `${githubConnection.owner}/${githubConnection.repo}`;
}
return "test-vitepress";
});
const repoBranch = computed(() =>
@@ -110,17 +111,12 @@ function replacePages(nextPages: DocPage[]) {
}
async function loadPagesForSource(source: DataSource, connection = githubConnection) {
if (source === "github") {
return loadGitHubPages(connection);
}
return loadLocalPages();
return source === "github" ? loadGitHubPages(connection) : loadLocalPages();
}
async function loadSettingsForSource(source: DataSource, connection = githubConnection) {
if (source === "github") {
const result = await loadGitHubSettings(connection);
return result;
return loadGitHubSettings(connection);
}
return {
@@ -173,6 +169,22 @@ async function reloadSite() {
await Promise.all([loadPages(), loadSettings()]);
}
async function refreshRepositories() {
isLoadingRepos.value = true;
try {
repositories.value = await loadGitHubRepositories();
activityItems.value = [`已读取 ${repositories.value.length} 个 GitHub 仓库`, ...activityItems.value];
} catch (error) {
activityItems.value = [
error instanceof Error ? error.message : "读取 GitHub 仓库列表失败",
...activityItems.value,
];
} finally {
isLoadingRepos.value = false;
}
}
async function loadAuthState() {
try {
currentUser.value = await loadCurrentUser();
@@ -183,7 +195,7 @@ async function loadAuthState() {
}
} catch (error) {
activityItems.value = [
error instanceof Error ? error.message : "读取 GitHub 登录状态失败",
error instanceof Error ? error.message : "读取登录状态失败",
...activityItems.value,
];
}
@@ -211,22 +223,6 @@ async function handleLogout() {
repositories.value = [];
}
async function refreshRepositories() {
isLoadingRepos.value = true;
try {
repositories.value = await loadGitHubRepositories();
activityItems.value = [`已读取 ${repositories.value.length} 个 GitHub 仓库`, ...activityItems.value];
} catch (error) {
activityItems.value = [
error instanceof Error ? error.message : "读取 GitHub 仓库列表失败",
...activityItems.value,
];
} finally {
isLoadingRepos.value = false;
}
}
async function connectGithub(connection: GitHubConnection) {
const nextConnection = normalizeConnection(connection);
@@ -390,6 +386,7 @@ onMounted(async () => {
<div v-else id="app-shell">
<AppSidebar
:active-panel="activePanel"
:current-user="currentUser"
:data-source="dataSource"
:has-git-hub-connection="hasGitHubConnection"
:panels="panels"
@@ -406,16 +403,22 @@ onMounted(async () => {
<h2>{{ activePanelTitle }}</h2>
</div>
<div class="topbar-actions">
<button class="ghost-button" type="button" @click="handleLogout">
退出
</button>
<button class="ghost-button" type="button" @click="activePanel = 'account'">用户中心</button>
<button class="ghost-button" type="button" @click="reloadSite">重新读取</button>
<button class="primary-button" type="button" @click="activePanel = 'publish'">发布</button>
</div>
</header>
<AccountPanel
v-if="activePanel === 'account'"
:current-user="currentUser"
:github-user="githubUser"
@github-login="startGitHubLogin"
@github-logout="signOutGithub"
@logout="handleLogout"
/>
<ConnectPanel
v-if="activePanel === 'connect'"
v-else-if="activePanel === 'connect'"
:connection="githubConnection"
:data-source="dataSource"
:github-user="githubUser"

View File

@@ -0,0 +1,61 @@
<script setup lang="ts">
import type { CmsUser, GitHubUser } from "../types";
defineProps<{
currentUser: CmsUser;
githubUser: GitHubUser | null;
}>();
defineEmits<{
githubLogin: [];
githubLogout: [];
logout: [];
}>();
</script>
<template>
<section class="panel is-visible">
<div class="account-layout">
<section class="management-card">
<p class="eyebrow">account</p>
<h3>用户中心</h3>
<dl class="source-summary account-summary">
<div>
<dt>名称</dt>
<dd>{{ currentUser.name }}</dd>
</div>
<div>
<dt>邮箱</dt>
<dd>{{ currentUser.email || "-" }}</dd>
</div>
<div>
<dt>角色</dt>
<dd>{{ currentUser.role }}</dd>
</div>
</dl>
<button class="ghost-button account-logout" type="button" @click="$emit('logout')">
退出 CMS 登录
</button>
</section>
<section class="management-card">
<p class="eyebrow">github</p>
<h3>GitHub 绑定</h3>
<div v-if="githubUser" class="github-user">
<img v-if="githubUser.avatarUrl" :src="githubUser.avatarUrl" alt="" />
<div>
<strong>{{ githubUser.name || githubUser.login }}</strong>
<span>@{{ githubUser.login }}</span>
</div>
<button class="ghost-button" type="button" @click="$emit('githubLogout')">解绑</button>
</div>
<div v-else>
<p class="helper-text">绑定 GitHub 可以读取仓库列表选择站点仓库并提交内容变更</p>
<button class="primary-button" type="button" @click="$emit('githubLogin')">
绑定 GitHub
</button>
</div>
</section>
</div>
</section>
</template>

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import type { DataSource, PanelKey } from "../types";
import type { CmsUser, DataSource, PanelKey } from "../types";
defineProps<{
activePanel: PanelKey;
currentUser: CmsUser;
dataSource: DataSource;
hasGitHubConnection: boolean;
panels: Array<{ key: PanelKey; label: string; icon: string }>;
@@ -26,6 +27,14 @@ defineEmits<{
</div>
</div>
<button class="user-button" type="button" @click="$emit('changePanel', 'account')">
<span class="user-avatar">{{ currentUser.name.slice(0, 1).toUpperCase() }}</span>
<span>
<strong>{{ currentUser.name }}</strong>
<small>{{ currentUser.role }}</small>
</span>
</button>
<button class="connect-button" type="button" @click="$emit('changePanel', 'connect')">
<span>{{ hasGitHubConnection ? "GitHub" : dataSource === "github" ? "GitHub" : "Local" }}</span>
<strong>{{ hasGitHubConnection ? "已连接仓库" : "连接仓库" }}</strong>

View File

@@ -128,6 +128,7 @@ button:disabled {
}
.connect-button,
.user-button,
.primary-button,
.secondary-button,
.ghost-button,
@@ -149,6 +150,42 @@ button:disabled {
text-align: left;
}
.user-button {
display: grid;
grid-template-columns: 38px minmax(0, 1fr);
gap: 10px;
align-items: center;
padding: 10px;
border-color: var(--line);
background: var(--surface);
color: var(--ink);
text-align: left;
}
.user-button strong,
.user-button small {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-button small {
color: var(--muted);
font-size: 12px;
}
.user-avatar {
display: grid;
width: 38px;
height: 38px;
place-items: center;
border-radius: var(--radius);
background: var(--accent-soft);
color: var(--accent-strong);
font-weight: 800;
}
.connect-button span {
color: #bfc7d1;
}
@@ -505,6 +542,7 @@ label {
.two-column,
.publish-layout,
.account-layout,
.connect-layout {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -572,6 +610,14 @@ label {
border-top: 1px solid var(--line);
}
.account-summary {
margin-bottom: 16px;
}
.account-logout {
width: 100%;
}
.source-summary {
display: grid;
gap: 12px;
@@ -700,6 +746,7 @@ label {
.content-layout,
.two-column,
.publish-layout,
.account-layout,
.connect-layout,
.settings-grid,
.frontmatter-grid,

View File

@@ -1,4 +1,4 @@
export type PanelKey = "connect" | "content" | "structure" | "config" | "publish";
export type PanelKey = "account" | "connect" | "content" | "structure" | "config" | "publish";
export type EditorMode = "write" | "preview";
export type DataSource = "local" | "github";