Skip to main content

样式定制

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

定制层次

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

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禁用状态

更多类名

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

class 属性

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

<template>
<ControlPanel class="my-control-panel" />
</template>

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

style 属性

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

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

自定义图标 (Slots)

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

ControlPanel 图标

<template>
<ControlPanel>
<template #muteIcon><MuteIcon /></template>
<template #unmuteIcon><UnmuteIcon /></template>
<template #hangupIcon><HangupIcon /></template>
<template #asrIcon><MicIcon /></template>
<template #keyboardIcon><KeyboardIcon /></template>
<template #sendIcon><SendIcon /></template>
</ControlPanel>
</template>

ConversationList 图标

<template>
<ConversationList>
<template #closeIcon><CloseIcon /></template>
</ConversationList>
</template>

RecommendedQuestions 图标

<template>
<RecommendedQuestions>
<template #arrowIcon><ArrowIcon /></template>
</RecommendedQuestions>
</template>

Scoped Slots 渲染

通过 scoped slots 可以完全自定义组件的渲染:

ConversationList

<template>
<ConversationList v-slot="{conversations, latestConversation}">
<div class="my-conversation-list">
<h3>对话历史</h3>
<div
v-for="conv in conversations"
:key="conv.id"
:class="['my-message', `my-message--${conv.type}`]"
>
<span class="speaker">
{{ conv.type === 'client' ? '我' : 'AI' }}
</span>
<p>{{ conv.text }}</p>
</div>
</div>
</ConversationList>
</template>

Conversation

<template>
<Conversation
:conversation="conv"
v-slot="{contents, fullText, type, isCompleted}"
>
<div :class="['custom-bubble', type]">
<p>{{ fullText }}</p>
<span v-if="!isCompleted" class="typing-indicator">...</span>
</div>
</Conversation>
</template>

Subtitle

<template>
<Subtitle v-slot="{speakerName, fullText, isExpanded, toggleExpand}">
<div class="custom-subtitle">
<strong>{{ speakerName }}:</strong>
<span>{{ fullText }}</span>
<button @click="toggleExpand">
{{ isExpanded ? '收起' : '展开' }}
</button>
</div>
</Subtitle>
</template>

Input

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

StartupScreen

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

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. 使用 scoped 样式

Vue SFC 的 scoped 样式避免全局污染:

<template>
<ControlPanel class="my-control-panel" />
</template>

<style scoped>
.my-control-panel {
/* 样式 */
}

/* 深度选择器用于覆盖子组件样式 */
:deep(.apk-control-panel__input) {
border-color: #007bff;
}
</style>

2. 保持原有类名

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

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

<!-- ❌ 避免完全替换内部结构导致功能丢失 -->
</template>

3. 优先使用 CSS 覆盖

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

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

4. 渐进式定制

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

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

相关