generated from kevisual/vite-react-template
feat: 更新页面标题和图标,添加远端配置保存与加载功能
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const cnb: CNB = useContextKey('cnb', () => {
|
|||||||
cors
|
cors
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
//
|
|
||||||
|
|
||||||
// import '@kevisual/cnb-ai'
|
// import '@kevisual/cnb-ai'
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
// 首先按活动状态排序
|
// 首先按活动状态排序
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user