Skip to main content

样式定制

React Widget 提供了多层次的样式定制能力,从简单的类名覆盖到完全自定义渲染。

定制层次

层次方式适用场景
1CSS 类名覆盖调整颜色、间距、字体等
2style 属性单个组件的行内样式
3自定义图标替换内置图标
4children 渲染完全自定义组件结构

CSS 类名覆盖

命名规范

所有组件使用 BEM 命名规范,前缀为 apk-(AaaS Pilot Kit):

.apk-{组件名}                    /* 块 */
.apk-{组件名}__{元素名} /* 元素 */
.apk-{组件名}--{修饰符} /* 修饰符 */

示例

/* 修改控制面板背景 */
.apk-control-panel {
background: rgba(0, 0, 0, 0.8);
}

/* 修改输入框样式 */
.apk-control-panel__input {
border: 2px solid #007bff;
border-radius: 25px;
}

/* 修改移动端按钮大小 */
.apk-control-panel--mobile .apk-control-panel__mic-btn {
width: 60px;
height: 60px;
}

组件类名速查

PilotKit

类名说明
.apk-pilotkit根容器
.apk-pilotkit__content内容区域

ConversationList

类名说明
.apk-conversation-list根容器
.apk-conversation-list--mobile移动端样式
.apk-conversation-list--desktop桌面端样式
.apk-conversation-list__inner内层滚动容器
.apk-conversation-list__follow-button滚动跟随按钮

Conversation

类名说明
.apk-conversation根容器
.apk-conversation--client用户消息
.apk-conversation--aiAI 消息
.apk-conversation__content-text文字内容

ControlPanel

类名说明
.apk-control-panel根容器
.apk-control-panel--mobile移动端样式
.apk-control-panel__inner内层容器
.apk-control-panel__mode-btn模式切换按钮
.apk-control-panel__input输入框
.apk-control-panel__send-btn发送按钮
.apk-control-panel__mic-btn麦克风按钮
.apk-control-panel__hangup-btn挂断按钮

Input

类名说明
.apk-input根容器
.apk-input--mobile移动端样式
.apk-input__field输入框
.apk-input__send-icon发送图标
.apk-input--error错误状态
.apk-input--disabled禁用状态

更多类名

详见各组件文档的「样式定制」章节。

className 属性

每个组件都支持 className 属性,用于添加自定义类名:

<ControlPanel className="my-control-panel" />
.my-control-panel {
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

style 属性

使用 style 属性设置行内样式:

<ConversationList
style={{
maxHeight: 400,
borderRadius: 16,
backgroundColor: 'rgba(255, 255, 255, 0.9)'
}}
/>

自定义图标

多个组件支持自定义图标:

ControlPanel 图标

<ControlPanel
muteIcon={<MuteIcon />}
unmuteIcon={<UnmuteIcon />}
hangupIcon={<HangupIcon />}
asrIcon={<MicIcon />}
keyboardIcon={<KeyboardIcon />}
sendIcon={<SendIcon />}
/>

ConversationList 图标

<ConversationList
closeIcon={<CloseIcon />}
/>

RecommendedQuestions 图标

<RecommendedQuestions
arrowIcon={<ArrowIcon />}
/>

children 渲染

通过 children 函数可以完全自定义组件的渲染:

ConversationList

<ConversationList>
{({conversations, latestConversation}) => (
<div className="my-conversation-list">
<h3>对话历史</h3>
{conversations.map(conv => (
<div
key={conv.id}
className={`my-message my-message--${conv.type}`}
>
<span className="speaker">
{conv.type === 'client' ? '我' : 'AI'}
</span>
<p>{conv.text}</p>
</div>
))}
</div>
)}
</ConversationList>

Conversation

<Conversation conversation={conv}>
{({contents, fullText, type, isCompleted}) => (
<div className={`custom-bubble ${type}`}>
<p>{fullText}</p>
{!isCompleted && <span className="typing-indicator">...</span>}
</div>
)}
</Conversation>

Subtitle

<Subtitle>
{({speakerName, fullText, isExpanded, toggleExpand}) => (
<div className="custom-subtitle">
<strong>{speakerName}</strong>
<span>{fullText}</span>
<button onClick={toggleExpand}>
{isExpanded ? '收起' : '展开'}
</button>
</div>
)}
</Subtitle>

Input

<Input>
{({value, setValue, handleSend, disabled, loading}) => (
<div className="custom-input">
<textarea
value={value}
onChange={e => setValue(e.target.value)}
disabled={disabled}
placeholder="输入消息..."
/>
<button onClick={handleSend} disabled={loading || !value}>
{loading ? '发送中...' : '发送'}
</button>
</div>
)}
</Input>

StartupScreen

<StartupScreen show={showStartup}>
{({onStart, isActivating}) => (
<div className="custom-startup">
<img src="/logo.png" alt="Logo" />
<h1>欢迎使用数字员工</h1>
<button onClick={onStart} disabled={isActivating}>
{isActivating ? '激活中...' : '开始对话'}
</button>
</div>
)}
</StartupScreen>

CSS 变量(规划中)

提示

CSS 变量支持正在规划中,未来版本将提供更便捷的主题定制方式。

预期的变量示例:

:root {
--apk-primary-color: #007bff;
--apk-background-color: rgba(255, 255, 255, 0.9);
--apk-text-color: #333;
--apk-border-radius: 20px;
--apk-font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}

主题示例

深色主题

/* 深色主题 */
.dark-theme .apk-conversation-list {
background: rgba(30, 30, 30, 0.95);
}

.dark-theme .apk-conversation--ai {
background: rgba(50, 50, 50, 0.9);
color: #fff;
}

.dark-theme .apk-conversation--client {
background: #007bff;
color: #fff;
}

.dark-theme .apk-control-panel {
background: rgba(40, 40, 40, 0.9);
}

.dark-theme .apk-control-panel__input {
background: rgba(60, 60, 60, 0.9);
color: #fff;
border-color: rgba(100, 100, 100, 0.5);
}

圆角主题

/* 圆角主题 */
.rounded-theme .apk-conversation {
border-radius: 24px;
}

.rounded-theme .apk-control-panel__input {
border-radius: 30px;
}

.rounded-theme .apk-control-panel__mic-btn,
.rounded-theme .apk-control-panel__send-btn {
border-radius: 50%;
}

企业主题

/* 企业品牌主题 */
.corporate-theme .apk-conversation--ai {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
}

.corporate-theme .apk-control-panel__mic-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.corporate-theme .apk-subtitle {
border-left: 3px solid #667eea;
padding-left: 12px;
}

最佳实践

1. 使用 CSS 模块或 scoped 样式

避免全局样式污染:

// styles.module.css
.myControlPanel {
/* 样式 */
}

// Component.tsx
import styles from './styles.module.css';

<ControlPanel className={styles.myControlPanel} />

2. 保持原有类名

覆盖样式时保留原有类名,确保响应式等功能正常:

// ✅ 推荐
<ControlPanel className="my-custom-class" />

// ❌ 避免完全替换内部结构导致功能丢失

3. 优先使用 CSS 覆盖

简单的样式调整优先使用 CSS 覆盖,而非 children 渲染:

/* ✅ 推荐:简单的样式调整 */
.apk-conversation--ai {
background: #f0f0f0;
}
// ❌ 过度使用:简单样式不需要自定义渲染
<Conversation conversation={conv}>
{({fullText}) => (
<div style={{background: '#f0f0f0'}}>{fullText}</div>
)}
</Conversation>

4. 渐进式定制

从最简单的方式开始,按需升级:

  1. 先尝试 className + CSS
  2. 不够用再尝试 style 属性
  3. 需要结构变化时使用 children 渲染

相关