feat: 更新页面标题和图标,添加远端配置保存与加载功能

This commit is contained in:
2026-02-27 01:32:39 +08:00
parent 3227c795a3
commit d85fc1154f
6 changed files with 69 additions and 43 deletions

View File

@@ -3,9 +3,9 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/jpg" href="https://kevisual.xiongxiao.me/root/center/panda.jpg" /> <link rel="icon" type="image/jpg" href="https://kevisual.cn/root/logo/assets/logos/cnb-gateway-logo-980x980.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CNB Live中心</title> <title>CNB Center</title>
<style> <style>
html, html,
body { body {

View File

@@ -22,7 +22,6 @@ export const cnb: CNB = useContextKey('cnb', () => {
cors cors
}) })
}) })
//
// import '@kevisual/cnb-ai' // import '@kevisual/cnb-ai'

View File

@@ -10,10 +10,9 @@ import { configSchema } from './store/schema';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useLayoutStore } from '../auth/store'; import { useLayoutStore } from '../auth/store';
import { useShallow } from 'zustand/shallow'; import { useShallow } from 'zustand/shallow';
import { queryLogin } from '@/modules/query';
export const ConfigPage = () => { export const ConfigPage = () => {
const { config, setConfig, resetConfig } = useConfigStore(); const { config, setConfig, resetConfig, saveToRemote, loadFromRemote } = useConfigStore();
const layoutStore = useLayoutStore(useShallow(state => ({ me: state.me }))) const layoutStore = useLayoutStore(useShallow(state => ({ me: state.me })))
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -31,42 +30,6 @@ export const ConfigPage = () => {
const handleChange = (field: keyof typeof config, value: string | boolean) => { const handleChange = (field: keyof typeof config, value: string | boolean) => {
setConfig({ [field]: value }); setConfig({ [field]: value });
}; };
const saveToRemote = async () => {
const _config = config;
const res = await queryLogin.post({
path: 'config',
key: 'update',
data: {
key: 'cnb_center_config.json',
data: _config,
}
});
if (res.code === 200) {
toast.success('保存到远端成功')
} else {
toast.error('保存到远端失败')
}
}
const loadFromRemote = async () => {
const res = await queryLogin.post({
path: 'config',
key: 'get',
data: {
key: 'cnb_center_config.json',
}
})
if (res.code === 404) {
toast.error('远端配置不存在')
return;
}
if (res.code === 200) {
const config = res.data?.data as typeof config;
setConfig(config);
toast.success('获取远端配置成功')
}
}
return ( return (
<TooltipProvider> <TooltipProvider>

View File

@@ -1,11 +1,16 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import type { Config, defaultConfig } from './schema'; import type { Config, defaultConfig } from './schema';
import { queryLogin } from '@/modules/query';
import { toast } from 'sonner';
type ConfigState = { type ConfigState = {
config: Config; config: Config;
setConfig: (config: Partial<Config>) => void; setConfig: (config: Partial<Config>) => void;
resetConfig: () => void; resetConfig: () => void;
saveToRemote: () => Promise<void>;
loadFromRemote: () => Promise<void>;
checkConfig: (opts?: { isUser?: boolean, reload?: boolean }) => Promise<boolean>;
}; };
const STORAGE_KEY = 'cnb-config'; const STORAGE_KEY = 'cnb-config';
@@ -33,7 +38,7 @@ const loadInitialConfig = (): Config => {
export const useConfigStore = create<ConfigState>()( export const useConfigStore = create<ConfigState>()(
persist( persist(
(set) => ({ (set, get) => ({
config: loadInitialConfig(), config: loadInitialConfig(),
setConfig: (newConfig) => setConfig: (newConfig) =>
set((state) => ({ set((state) => ({
@@ -43,6 +48,52 @@ export const useConfigStore = create<ConfigState>()(
set({ set({
config: DEFAULT_CONFIG, config: DEFAULT_CONFIG,
}), }),
saveToRemote: async () => {
const _config = get().config;
const res = await queryLogin.post({
path: 'config',
key: 'update',
data: {
key: 'cnb_center_config.json',
data: _config,
}
});
if (res.code === 200) {
toast.success('保存到远端成功')
} else {
toast.error('保存到远端失败')
}
},
loadFromRemote: async () => {
const setConfig = (config: Config) => set({ config });
const res = await queryLogin.post({
path: 'config',
key: 'get',
data: {
key: 'cnb_center_config.json',
}
})
if (res.code === 404) {
toast.error('远端配置不存在')
return;
}
if (res.code === 200) {
const config = res.data?.data as typeof config;
setConfig(config);
toast.success('获取远端配置成功')
}
},
checkConfig: async (opts?: { isUser?: boolean, reload?: boolean }) => {
const { CNB_API_KEY } = get().config;
if (!CNB_API_KEY && opts?.isUser) {
await get().loadFromRemote();
if (opts?.reload) {
location.reload();
}
return true
}
return false
}
}), }),
{ {
name: STORAGE_KEY, name: STORAGE_KEY,

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react' import { use, useEffect, useMemo, useState } from 'react'
import { useRepoStore } from './store/index' import { useRepoStore } from './store/index'
import { useShallow } from 'zustand/shallow' import { useShallow } from 'zustand/shallow'
import { RepoCard } from './components/RepoCard' import { RepoCard } from './components/RepoCard'
@@ -11,6 +11,8 @@ import { Input } from '@/components/ui/input'
import { ExternalLinkIcon, Plus, RefreshCw, Search, Settings } from 'lucide-react' import { ExternalLinkIcon, Plus, RefreshCw, Search, Settings } from 'lucide-react'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { useNavigate } from '@tanstack/react-router' import { useNavigate } from '@tanstack/react-router'
import { useLayoutStore } from '../auth/store'
import { useConfigStore } from '../config/store'
export const App = () => { export const App = () => {
const { list, refresh, loading, workspaceList, setShowCreateDialog } = useRepoStore(useShallow((state) => ({ const { list, refresh, loading, workspaceList, setShowCreateDialog } = useRepoStore(useShallow((state) => ({
@@ -22,9 +24,17 @@ export const App = () => {
}))) })))
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const navigate = useNavigate(); const navigate = useNavigate();
const me = useLayoutStore(state => state.me)
const configStore = useConfigStore(useShallow(state => ({ checkConfig: state.checkConfig })))
useEffect(() => { useEffect(() => {
refresh({ showTips: false }) refresh({ showTips: false })
}, []) }, [])
useEffect(() => {
if (me && me.id) {
configStore.checkConfig({ isUser: true, reload: true })
}
}, [me])
const appList = useMemo(() => { const appList = useMemo(() => {
// 首先按活动状态排序 // 首先按活动状态排序

View File

@@ -5,6 +5,7 @@ import { cnb } from '@/agents/app'
import { WorkspaceInfo } from '@kevisual/cnb' import { WorkspaceInfo } from '@kevisual/cnb'
import { createBuildConfig, createCommitBlankConfig, createDevConfig } from './build'; import { createBuildConfig, createCommitBlankConfig, createDevConfig } from './build';
import { useLayoutStore } from '@/pages/auth/store'; import { useLayoutStore } from '@/pages/auth/store';
import { useConfigStore } from '@/pages/config/store';
interface DisplayModule { interface DisplayModule {
activity: boolean; activity: boolean;
contributors: boolean; contributors: boolean;
@@ -312,6 +313,8 @@ export const useRepoStore = create<State>((set, get) => {
}, },
refresh: async (opts?: { message?: string, showTips?: boolean, search?: string }) => { refresh: async (opts?: { message?: string, showTips?: boolean, search?: string }) => {
const getList = get().getList({ search: opts?.search }, true); const getList = get().getList({ search: opts?.search }, true);
const getWorkspaceList = get().getWorkspaceList(); const getWorkspaceList = get().getWorkspaceList();
await Promise.all([getList, getWorkspaceList]); await Promise.all([getList, getWorkspaceList]);
if (opts?.showTips !== false) { if (opts?.showTips !== false) {