diff --git a/README.md b/README.md
index 7a81ddf..174ce55 100644
--- a/README.md
+++ b/README.md
@@ -4,27 +4,63 @@
## 功能特点
-- **时间和日期显示**:可自定义格式的时间和日期显示
-- **文本显示**:展示固定文字或 API 返回内容,支持自定义样式
-- **图片显示**:展示本地或远程图片,支持自定义设置
+- **多种小组件**:内置时钟、日期、文本、图片等多种可配置小组件
+- **动态首页**:自动展示所有已注册小组件,便于预览和选择
- **分屏界面**:左侧为配置面板,右侧为实时预览
- **透明背景**:所有小组件均具有适合 OBS 悬浮的透明背景
- **URL 生成**:自动生成包含编码配置的可分享 URL
- **纯预览模式**:打开生成的 URL 仅显示小组件内容,无配置界面,背景透明
+- **可扩展性**:集中化的小组件注册机制,便于添加新的小组件类型
## 小组件类型
-1. **时钟小组件**:显示当前时间,可自定义格式、样式和特效
+1. **时钟小组件**:显示当前时间,可自定义格式、样式和特效,支持显示日期
2. **日期小组件**:显示当前日期,可自定义格式、样式和特效
3. **文本小组件**:显示文本,支持渐变、阴影、字体等自定义样式
4. **图片小组件**:显示图片,可自定义大小、特效和位置
## 使用方法
-1. 从下拉菜单中选择小组件类型
-2. 使用左侧控制面板配置小组件
-3. 在右侧实时查看预览效果
-4. 复制生成的 URL,在 OBS Studio 中作为浏览器源使用
+1. 在首页浏览并选择需要的小组件类型
+2. 在配置页面中,从下拉菜单中选择小组件类型
+3. 使用左侧控制面板配置小组件属性和样式
+4. 在右侧实时查看预览效果
+5. 复制生成的 URL,在 OBS Studio 中作为浏览器源使用
+
+## 小组件配置详解
+
+### 时钟小组件
+
+- **时间格式**:支持 24 小时制 (HH:mm:ss) 和 12 小时制 (hh:mm:ss A)
+- **显示秒**:切换是否显示秒数
+- **显示日期**:切换是否在时钟下方显示日期
+- **日期格式**:多种日期格式可选,如 YYYY-MM-DD、MM/DD/YYYY 等
+- **样式设置**:
+ - 字体大小、字体类型、字重
+ - 文本颜色或渐变色
+ - 文字阴影及模糊度
+
+### 日期小组件
+
+- **日期格式**:多种格式可选,支持年月日不同顺序排列
+- **显示星期**:切换是否显示星期几
+- **样式设置**:与时钟小组件类似,支持字体、颜色和阴影自定义
+
+### 文本小组件
+
+- **文本内容**:自定义显示的文字内容
+- **样式设置**:
+ - 字体大小、字体类型、字重
+ - 文本颜色或渐变色
+ - 文字阴影及模糊度
+
+### 图片小组件
+
+- **图片 URL**:输入远程图片的 URL
+- **尺寸设置**:自定义宽度和高度
+- **透明度**:调整图片透明度
+- **圆角**:调整图片圆角半径
+- **阴影效果**:添加阴影及调整阴影颜色和模糊度
## 开发
@@ -42,6 +78,196 @@ npm run build
npm run preview
```
+## 项目结构
+
+```
+src/
+├── assets/ # 静态资源
+├── components/ # 组件
+│ └── config/ # 小组件配置面板组件
+│ ├── ClockConfig.vue
+│ ├── DateConfig.vue
+│ ├── TextConfig.vue
+│ └── ImageConfig.vue
+├── router/ # 路由配置
+├── utils/ # 工具函数
+│ └── configUtils.ts # 配置编码/解码工具
+├── views/ # 页面视图
+│ ├── HomeView.vue # 主页
+│ ├── ConfigView.vue # 配置页面
+│ └── PreviewView.vue# 预览页面
+├── widgets/ # 小组件实现
+│ ├── registry.ts # 小组件注册中心
+│ ├── ClockWidget.vue
+│ ├── DateWidget.vue
+│ ├── TextWidget.vue
+│ └── ImageWidget.vue
+├── App.vue # 应用入口组件
+└── main.ts # 应用入口文件
+```
+
+## 实现原理
+
+本项目基于 Vue 3 组合式 API 和 TypeScript 构建,核心实现包括:
+
+1. **小组件系统**:每个小组件都是独立的 Vue 组件,通过 props 接收配置
+2. **配置系统**:每种小组件对应一个配置组件,使用 v-model 进行双向绑定
+3. **集中注册机制**:通过 `registry.ts` 统一管理所有小组件,便于扩展和维护
+4. **动态首页展示**:自动从注册表中读取小组件信息展示在首页,通过映射添加图标和描述
+5. **URL 参数传递**:使用 Base64 编码将配置数据序列化到 URL 中
+6. **预览模式**:通过 URL 参数判断是否处于纯预览模式,隐藏配置界面
+7. **响应式更新**:使用 Vue 的响应式系统实现配置变更时的实时预览
+
+## 最近更新
+
+- **界面全面中文化**:所有小组件界面、配置面板、提示文本已全部中文化
+- **时钟小组件增强**:时钟小组件支持显示日期功能,样式与时间保持一致
+- **动态首页改进**:首页自动读取注册的小组件列表,支持内容滚动
+- **小组件注册中心**:新增 `registry.ts` 作为小组件注册中心,统一管理
+- **响应式布局优化**:改进移动端和小屏幕适配体验
+
+## 如何添加新小组件
+
+要添加新的小组件,需要以下步骤:
+
+1. **创建小组件组件**:在 `src/widgets/` 目录下创建新的 Vue 组件,如 `NewWidget.vue`
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+1. **创建配置组件**:在 `src/components/config/` 目录下创建对应的配置组件,如 `NewConfig.vue`
+
+```vue
+
+
+
新小组件设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+1. **注册新小组件**:在 `src/widgets/registry.ts` 中添加新小组件
+
+```typescript
+// 导入新组件
+import NewWidget from './NewWidget.vue';
+import NewConfig from '../components/config/NewConfig.vue';
+
+// 小组件注册表
+export const widgets = [
+ // 现有小组件...
+ { label: '新小组件', value: 'new', component: NewWidget, configComponent: NewConfig },
+];
+
+// 在 getDefaultConfig 函数中添加默认配置
+case 'new':
+ return {
+ property1: 'default',
+ property2: 0,
+ // ...
+ };
+```
+
+添加完成后,新小组件会自动出现在首页的可用小组件列表中,并可在配置页面中使用。
+
## 故障排除
如果遇到与未定义属性相关的 TypeScript 错误,请确保:
diff --git a/src/views/ConfigView.vue b/src/views/ConfigView.vue
index 496bead..2399876 100644
--- a/src/views/ConfigView.vue
+++ b/src/views/ConfigView.vue
@@ -41,24 +41,8 @@ import { CopyDocument } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { encodeConfig, decodeConfig } from '../utils/configUtils';
-// 小组件组件及其配置
-import ClockWidget from '../widgets/ClockWidget.vue';
-import DateWidget from '../widgets/DateWidget.vue';
-import TextWidget from '../widgets/TextWidget.vue';
-import ImageWidget from '../widgets/ImageWidget.vue';
-
-// 配置组件
-import ClockConfig from '../components/config/ClockConfig.vue';
-import DateConfig from '../components/config/DateConfig.vue';
-import TextConfig from '../components/config/TextConfig.vue';
-import ImageConfig from '../components/config/ImageConfig.vue';
-
-const widgets = [
- { label: '时钟小组件', value: 'clock', component: ClockWidget, configComponent: ClockConfig },
- { label: '日期小组件', value: 'date', component: DateWidget, configComponent: DateConfig },
- { label: '文本小组件', value: 'text', component: TextWidget, configComponent: TextConfig },
- { label: '图片小组件', value: 'image', component: ImageWidget, configComponent: ImageConfig },
-];
+// 导入小组件注册表
+import { widgets, getDefaultConfig } from '../widgets/registry';
const selectedWidget = ref('clock');
const currentWidgetConfig = ref({});
@@ -76,66 +60,6 @@ const currentConfigComponent = computed(() => {
return widget?.configComponent;
});
-// 为每种小组件类型设置默认配置
-const getDefaultConfig = (widgetType: string) => {
- switch (widgetType) {
- case 'clock':
- return {
- format: 'HH:mm:ss',
- color: '#ffffff',
- fontSize: 48,
- fontWeight: 'normal',
- fontFamily: 'Arial',
- textShadow: false,
- shadowColor: 'rgba(0,0,0,0.5)',
- shadowBlur: 4,
- useGradient: false,
- gradientColors: ['#ff0000', '#0000ff'],
- showSeconds: true
- };
- case 'date':
- return {
- format: 'YYYY-MM-DD',
- color: '#ffffff',
- fontSize: 32,
- fontWeight: 'normal',
- fontFamily: 'Arial',
- textShadow: false,
- shadowColor: 'rgba(0,0,0,0.5)',
- shadowBlur: 4,
- useGradient: false,
- gradientColors: ['#ff0000', '#0000ff'],
- showWeekday: true
- };
- case 'text':
- return {
- text: 'Sample Text',
- color: '#ffffff',
- fontSize: 32,
- fontWeight: 'normal',
- fontFamily: 'Arial',
- textShadow: false,
- shadowColor: 'rgba(0,0,0,0.5)',
- shadowBlur: 4,
- useGradient: false,
- gradientColors: ['#ff0000', '#0000ff'],
- };
- case 'image':
- return {
- imageUrl: '',
- width: 200,
- height: 200,
- opacity: 1,
- borderRadius: 0,
- shadow: false,
- shadowColor: 'rgba(0,0,0,0.5)',
- shadowBlur: 10
- };
- default:
- return {};
- }
-};
-
// Handle widget type change
const handleWidgetChange = () => {
currentWidgetConfig.value = getDefaultConfig(selectedWidget.value);
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index f1cb823..9aea0a3 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -32,35 +32,11 @@
可用小组件
-
@@ -76,16 +52,58 @@
@@ -94,11 +112,15 @@ const goToDoc = () => {
min-height: 100vh;
background-color: #f5f7fa;
padding: 20px;
+ /* 添加溢出滚动,确保内容可以完整显示 */
+ overflow-y: auto;
}
.container {
max-width: 1200px;
margin: 0 auto;
+ /* 添加底部间距,确保页脚有足够空间 */
+ padding-bottom: 30px;
}
.header {
@@ -175,8 +197,10 @@ const goToDoc = () => {
.widget-list {
display: grid;
- grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
+ /* 确保底部有足够的边距 */
+ margin-bottom: 30px;
}
.widget-item {
@@ -185,6 +209,13 @@ const goToDoc = () => {
border-radius: 10px;
padding: 20px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
+ /* 添加过渡效果 */
+ transition: transform 0.3s, box-shadow 0.3s;
+}
+
+.widget-item:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.12);
}
.widget-icon {
diff --git a/src/widgets/registry.ts b/src/widgets/registry.ts
new file mode 100644
index 0000000..4fe467b
--- /dev/null
+++ b/src/widgets/registry.ts
@@ -0,0 +1,81 @@
+// 小组件组件及其配置
+import ClockWidget from './ClockWidget.vue';
+import DateWidget from './DateWidget.vue';
+import TextWidget from './TextWidget.vue';
+import ImageWidget from './ImageWidget.vue';
+
+// 配置组件
+import ClockConfig from '../components/config/ClockConfig.vue';
+import DateConfig from '../components/config/DateConfig.vue';
+import TextConfig from '../components/config/TextConfig.vue';
+import ImageConfig from '../components/config/ImageConfig.vue';
+
+// 小组件注册表
+export const widgets = [
+ { label: '时钟小组件', value: 'clock', component: ClockWidget, configComponent: ClockConfig },
+ { label: '日期小组件', value: 'date', component: DateWidget, configComponent: DateConfig },
+ { label: '文本小组件', value: 'text', component: TextWidget, configComponent: TextConfig },
+ { label: '图片小组件', value: 'image', component: ImageWidget, configComponent: ImageConfig },
+];
+
+// 获取小组件默认配置
+export const getDefaultConfig = (widgetType: string) => {
+ switch (widgetType) {
+ case 'clock':
+ return {
+ format: 'HH:mm:ss',
+ color: '#ffffff',
+ fontSize: 48,
+ fontWeight: 'normal',
+ fontFamily: 'Arial',
+ textShadow: false,
+ shadowColor: 'rgba(0,0,0,0.5)',
+ shadowBlur: 4,
+ useGradient: false,
+ gradientColors: ['#ff0000', '#0000ff'],
+ showSeconds: true,
+ showDate: false,
+ dateFormat: 'YYYY-MM-DD'
+ };
+ case 'date':
+ return {
+ format: 'YYYY-MM-DD',
+ color: '#ffffff',
+ fontSize: 32,
+ fontWeight: 'normal',
+ fontFamily: 'Arial',
+ textShadow: false,
+ shadowColor: 'rgba(0,0,0,0.5)',
+ shadowBlur: 4,
+ useGradient: false,
+ gradientColors: ['#ff0000', '#0000ff'],
+ showWeekday: true
+ };
+ case 'text':
+ return {
+ text: 'Sample Text',
+ color: '#ffffff',
+ fontSize: 32,
+ fontWeight: 'normal',
+ fontFamily: 'Arial',
+ textShadow: false,
+ shadowColor: 'rgba(0,0,0,0.5)',
+ shadowBlur: 4,
+ useGradient: false,
+ gradientColors: ['#ff0000', '#0000ff'],
+ };
+ case 'image':
+ return {
+ imageUrl: '',
+ width: 200,
+ height: 200,
+ opacity: 1,
+ borderRadius: 0,
+ shadow: false,
+ shadowColor: 'rgba(0,0,0,0.5)',
+ shadowBlur: 10
+ };
+ default:
+ return {};
+ }
+};