Code Block
通用代码块显示组件:基于 prism-react-renderer 的语法高亮,7 套内置配色主题(浅/深色变体)+ 自定义主题支持,支持折叠、滚动、自适应高度三种显示模式,行号 sticky 固定,自带完整样式。含 CodeBlockPanel 外层容器面板(仿 Tailwind CSS 官网风格),支持文件名标签与复制按钮。
快速预览
查看 Code Block 组件的基本效果
1import { useState } from "react";2
3export function Counter({ initial = 0 }) {4 const [count, setCount] = useState(initial);5
6 return (7 <div className="flex items-center gap-4">8 <button onClick={() => setCount(c => c - 1)}>-</button>9 <span className="text-xl font-bold">{count}</span>10 <button onClick={() => setCount(c => c + 1)}>+</button>11 </div>12 );13}基本用法
使用 Code Block 组件的基础示例
导入组件
1import { CodeBlock, CodeBlockPanel } from "@/components/qiuye-ui/code-block";使用组件
1<CodeBlockPanel filename="app.ts" code={code}>2 <CodeBlock language="typescript" isDark>3 {code}4 </CodeBlock>5</CodeBlockPanel>组件演示
查看组件的各种使用方式和效果
基础用法
最简单的代码块展示,自动语法高亮
1import React, { useState } from "react";2
3export function Counter({ initial = 0 }) {4 const [count, setCount] = useState(initial);5
6 return (7 <div className="flex items-center gap-4">8 <button onClick={() => setCount(c => c - 1)}>-</button>9 <span className="text-xl font-bold">{count}</span>10 <button onClick={() => setCount(c => c + 1)}>+</button>11 </div>12 );13}面板容器
仿 Tailwind CSS 官网风格的深色面板外壳,支持文件名标签与复制按钮
1import { useState, useEffect, useCallback, useMemo } from "react";2
3interface User {4 id: number;5 name: string;6 email: string;7 role: "admin" | "user" | "moderator";8 createdAt: Date;9}10
11type FilterFn<T> = (item: T) => boolean;12
13/**14 * 通用列表管理 Hook15 * 支持过滤、排序、分页16 */17export function useListManager<T extends { id: number }>(18 initialItems: T[],19 pageSize: number = 10,20) {21 const [items, setItems] = useState<T[]>(initialItems);22 const [filter, setFilter] = useState<FilterFn<T> | null>(null);23 const [page, setPage] = useState(0);24 const [sortKey, setSortKey] = useState<keyof T | null>(null);25 const [sortDesc, setSortDesc] = useState(false);不传 filename 时自动显示语言类型标签:
1import React, { useState } from "react";2
3export function Counter({ initial = 0 }) {4 const [count, setCount] = useState(initial);5
6 return (7 <div className="flex items-center gap-4">8 <button onClick={() => setCount(c => c - 1)}>-</button>9 <span className="text-xl font-bold">{count}</span>10 <button onClick={() => setCount(c => c + 1)}>+</button>11 </div>12 );13}搭配 displayMode="scroll" 使用:
1from dataclasses import dataclass, field2from typing import Optional, List3import asyncio4import aiohttp5
6
7@dataclass8class Config:9 """应用配置"""10 base_url: str = "https://api.example.com"11 timeout: int = 3012 max_retries: int = 313 headers: dict = field(default_factory=lambda: {14 "Content-Type": "application/json",15 "Accept": "application/json",16 })17
18
19class APIClient:20 """异步 API 客户端"""21
22 def __init__(self, config: Optional[Config] = None):23 self.config = config or Config()24 self._session: Optional[aiohttp.ClientSession] = None25
26 async def __aenter__(self):27 self._session = aiohttp.ClientSession(28 headers=self.config.headers,29 timeout=aiohttp.ClientTimeout(total=self.config.timeout),30 )31 return self32
33 async def __aexit__(self, *args):34 if self._session:35 await self._session.close()36
37 async def get(self, endpoint: str, **params) -> dict:38 """发送 GET 请求"""39 url = f"{self.config.base_url}/{endpoint}"40 for attempt in range(self.config.max_retries):41 try:42 async with self._session.get(url, params=params) as resp:43 resp.raise_for_status()44 return await resp.json()45 except aiohttp.ClientError as e:46 if attempt == self.config.max_retries - 1:47 raise48 await asyncio.sleep(2 ** attempt)49
50
51async def main():52 async with APIClient() as client:53 users = await client.get("users", page=1, limit=10)54 print(f"获取到 {len(users)} 个用户")55
56
57if __name__ == "__main__":58 asyncio.run(main())配色主题
7 套内置主题,自动适配浅色 / 深色模式
1import { useState, useEffect, useCallback, useMemo } from "react";2
3interface User {4 id: number;5 name: string;6 email: string;7 role: "admin" | "user" | "moderator";8 createdAt: Date;9}10
11type FilterFn<T> = (item: T) => boolean;12
13/**14 * 通用列表管理 Hook15 * 支持过滤、排序、分页16 */17export function useListManager<T extends { id: number }>(18 initialItems: T[],19 pageSize: number = 10,20) {21 const [items, setItems] = useState<T[]>(initialItems);22 const [filter, setFilter] = useState<FilterFn<T> | null>(null);23 const [page, setPage] = useState(0);24 const [sortKey, setSortKey] = useState<keyof T | null>(null);25 const [sortDesc, setSortDesc] = useState(false);26
27 // 过滤 + 排序28 const processed = useMemo(() => {29 let result = filter ? items.filter(filter) : [...items];30
31 if (sortKey) {32 result.sort((a, b) => {33 const va = a[sortKey];34 const vb = b[sortKey];35 if (va < vb) return sortDesc ? 1 : -1;36 if (va > vb) return sortDesc ? -1 : 1;37 return 0;38 });39 }40
41 return result;42 }, [items, filter, sortKey, sortDesc]);折叠模式
超出行数自动折叠,渐变遮罩 + 展开/收起按钮
滚动模式
设置最大高度,超出部分纵向滚动,带滚动阴影指示器
1from dataclasses import dataclass, field2from typing import Optional, List3import asyncio4import aiohttp5
6
7@dataclass8class Config:9 """应用配置"""10 base_url: str = "https://api.example.com"11 timeout: int = 3012 max_retries: int = 313 headers: dict = field(default_factory=lambda: {14 "Content-Type": "application/json",15 "Accept": "application/json",16 })17
18
19class APIClient:20 """异步 API 客户端"""21
22 def __init__(self, config: Optional[Config] = None):23 self.config = config or Config()24 self._session: Optional[aiohttp.ClientSession] = None25
26 async def __aenter__(self):27 self._session = aiohttp.ClientSession(28 headers=self.config.headers,29 timeout=aiohttp.ClientTimeout(total=self.config.timeout),30 )31 return self32
33 async def __aexit__(self, *args):34 if self._session:35 await self._session.close()36
37 async def get(self, endpoint: str, **params) -> dict:38 """发送 GET 请求"""39 url = f"{self.config.base_url}/{endpoint}"40 for attempt in range(self.config.max_retries):41 try:42 async with self._session.get(url, params=params) as resp:43 resp.raise_for_status()44 return await resp.json()45 except aiohttp.ClientError as e:46 if attempt == self.config.max_retries - 1:47 raise48 await asyncio.sleep(2 ** attempt)49
50
51async def main():52 async with APIClient() as client:53 users = await client.get("users", page=1, limit=10)54 print(f"获取到 {len(users)} 个用户")55
56
57if __name__ == "__main__":58 asyncio.run(main())自适应高度模式
高度自动适配父容器(如 Dialog),无需手动设置 maxHeight,适合弹窗 / Flex 布局场景
多语言支持
支持 TypeScript、Python、JSX/TSX 等多种编程语言
1from dataclasses import dataclass, field2from typing import Optional, List3import asyncio4import aiohttp5
6
7@dataclass8class Config:9 """应用配置"""10 base_url: str = "https://api.example.com"11 timeout: int = 3012 max_retries: int = 313 headers: dict = field(default_factory=lambda: {14 "Content-Type": "application/json",15 "Accept": "application/json",16 })无阴影模式
通过 noShadow 属性移除代码块容器的 box-shadow,适合嵌入卡片等已有阴影的容器中
默认(有阴影)
1const a = "有阴影";2console.log(a);noShadow(无阴影)
1const b = "无阴影";2console.log(b);行号显示控制
通过 showLineNumbers 控制行号显示策略:始终显示、始终隐藏、或按行数阈值自动切换
showLineNumbers={false} — 始终隐藏行号
showLineNumbers={15} — 代码不足 15 行,自动隐藏
showLineNumbers={15} — 代码超过 15 行,自动显示
1import { useState, useEffect, useCallback, useMemo } from "react";2
3interface User {4 id: number;5 name: string;6 email: string;7 role: "admin" | "user" | "moderator";8 createdAt: Date;9}10
11type FilterFn<T> = (item: T) => boolean;12
13/**14 * 通用列表管理 Hook15 * 支持过滤、排序、分页16 */17export function useListManager<T extends { id: number }>(18 initialItems: T[],19 pageSize: number = 10,20) {21 const [items, setItems] = useState<T[]>(initialItems);22 const [filter, setFilter] = useState<FilterFn<T> | null>(null);23 const [page, setPage] = useState(0);24 const [sortKey, setSortKey] = useState<keyof T | null>(null);25 const [sortDesc, setSortDesc] = useState(false);26
27 // 过滤 + 排序28 const processed = useMemo(() => {29 let result = filter ? items.filter(filter) : [...items];30
31 if (sortKey) {32 result.sort((a, b) => {33 const va = a[sortKey];34 const vb = b[sortKey];35 if (va < vb) return sortDesc ? 1 : -1;36 if (va > vb) return sortDesc ? -1 : 1;37 return 0;38 });39 }40
41 return result;42 }, [items, filter, sortKey, sortDesc]);面板 + 折叠模式组合
CodeBlockPanel 搭配 displayMode="collapse" 使用,面板自动重置内部样式