generated from kevisual/vite-react-template
feat: 添加远端配置的保存和加载功能,优化仓库卡片显示主题和可见性
This commit is contained in:
@@ -99,5 +99,5 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
|||||||
setLoginPageConfig: (config) => set((state) => ({
|
setLoginPageConfig: (config) => set((state) => ({
|
||||||
loginPageConfig: { ...state.loginPageConfig, ...config },
|
loginPageConfig: { ...state.loginPageConfig, ...config },
|
||||||
})),
|
})),
|
||||||
links: [{ title: '首页', href: '/', key: 'home' }],
|
links: [{ title: '', href: '/', key: 'home' }],
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
|
|||||||
import { Info } from 'lucide-react';
|
import { Info } from 'lucide-react';
|
||||||
import { configSchema } from './store/schema';
|
import { configSchema } from './store/schema';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { useLayoutStore } from '../auth/store';
|
||||||
|
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 } = useConfigStore();
|
||||||
|
const layoutStore = useLayoutStore(useShallow(state => ({ me: state.me })))
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const result = configSchema.safeParse(config);
|
const result = configSchema.safeParse(config);
|
||||||
@@ -28,6 +31,42 @@ 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>
|
||||||
@@ -175,6 +214,15 @@ export const ConfigPage = () => {
|
|||||||
<Button type="button" variant="outline" onClick={resetConfig}>
|
<Button type="button" variant="outline" onClick={resetConfig}>
|
||||||
重置为默认值
|
重置为默认值
|
||||||
</Button>
|
</Button>
|
||||||
|
{layoutStore.me && <>
|
||||||
|
<Button type="button" variant="outline" onClick={loadFromRemote}>
|
||||||
|
获取远端配置
|
||||||
|
</Button>
|
||||||
|
<Button type="button" variant="outline" onClick={saveToRemote}>
|
||||||
|
保存到远端
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -246,15 +246,19 @@ export function RepoCard({ repo, onStartWorkspace, onEdit, onIssue, onSettings,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{repo.topics && (
|
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{repo.topics.split(',').map((topic: string, idx: number) => (
|
{repo.topics && (<>
|
||||||
|
{
|
||||||
|
repo.topics.split(',').map((topic: string, idx: number) => (
|
||||||
<Badge key={idx} variant="outline" className="text-xs border-neutral-300 text-neutral-700 hover:bg-neutral-100 transition-colors">
|
<Badge key={idx} variant="outline" className="text-xs border-neutral-300 text-neutral-700 hover:bg-neutral-100 transition-colors">
|
||||||
{topic.trim()}
|
{topic.trim()}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))
|
||||||
</div>
|
}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
<Badge variant="outline" className="text-xs border-neutral-300 text-neutral-700 hover:bg-neutral-100 transition-colors">{repo.visibility_level}</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
{repo.site && (
|
{repo.site && (
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import { WorkspaceDetailDialog } from './modules/WorkspaceDetailDialog'
|
|||||||
import { SyncRepoDialog } from './modules/SyncRepoDialog'
|
import { SyncRepoDialog } from './modules/SyncRepoDialog'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Plus, RefreshCw, Search } from 'lucide-react'
|
import { Plus, RefreshCw, Search, Settings } from 'lucide-react'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
|
import { useNavigate } from '@tanstack/react-router'
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
const { list, refresh, loading, editRepo, setEditRepo, workspaceList, showEditDialog, setShowEditDialog, showCreateDialog, setShowCreateDialog, startWorkspace, deleteItem, setSelectedSyncRepo, setSyncDialogOpen } = useRepoStore()
|
const { list, refresh, loading, editRepo, setEditRepo, workspaceList, showEditDialog, setShowEditDialog, showCreateDialog, setShowCreateDialog, startWorkspace, deleteItem, setSelectedSyncRepo, setSyncDialogOpen } = useRepoStore()
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
const navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh({ showTips: false })
|
refresh({ showTips: false })
|
||||||
}, [])
|
}, [])
|
||||||
@@ -73,7 +74,10 @@ export const App = () => {
|
|||||||
<div className="container mx-auto p-6 max-w-7xl flex-1">
|
<div className="container mx-auto p-6 max-w-7xl flex-1">
|
||||||
<div className="mb-8 flex items-center justify-between">
|
<div className="mb-8 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-4xl font-bold text-neutral-900 mb-2">仓库列表</h1>
|
<h1 className="text-4xl font-bold text-neutral-900 mb-2 flex gap-1 items-center">
|
||||||
|
仓库列表
|
||||||
|
<Settings className="inline-block h-5 w-5 ml-2 text-neutral-400 hover:text-neutral-600 cursor-pointer" onClick={() => navigate({ to: '/config' })} />
|
||||||
|
</h1>
|
||||||
<p className="text-neutral-600">共 {list.length} 个仓库</p>
|
<p className="text-neutral-600">共 {list.length} 个仓库</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface Data {
|
|||||||
name: string;
|
name: string;
|
||||||
freeze: boolean;
|
freeze: boolean;
|
||||||
status: number;
|
status: number;
|
||||||
|
// Public, Private
|
||||||
visibility_level: string;
|
visibility_level: string;
|
||||||
flags: string;
|
flags: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user