项目重构,增强扩展性
This commit is contained in:
parent
c1dfe48a06
commit
925c8ad6bc
94
README.md
94
README.md
@ -83,12 +83,7 @@ npm run preview
|
||||
```
|
||||
src/
|
||||
├── assets/ # 静态资源
|
||||
├── components/ # 组件
|
||||
│ └── config/ # 小组件配置面板组件
|
||||
│ ├── ClockConfig.vue
|
||||
│ ├── DateConfig.vue
|
||||
│ ├── TextConfig.vue
|
||||
│ └── ImageConfig.vue
|
||||
├── components/ # 通用组件
|
||||
├── router/ # 路由配置
|
||||
├── utils/ # 工具函数
|
||||
│ └── configUtils.ts # 配置编码/解码工具
|
||||
@ -96,8 +91,91 @@ src/
|
||||
│ ├── HomeView.vue # 主页
|
||||
│ ├── ConfigView.vue # 配置页面
|
||||
│ └── PreviewView.vue# 预览页面
|
||||
├── widgets/ # 小组件实现
|
||||
│ ├── registry.ts # 小组件注册中心
|
||||
├── widgets/ # 小组件模块
|
||||
│ ├── clock/ # 时钟小组件模块
|
||||
│ │ ├── index.ts # 模块导出
|
||||
│ │ ├── types.ts # 类型定义
|
||||
│ │ ├── Widget.vue # 小组件组件
|
||||
│ │ └── Config.vue # 配置组件
|
||||
│ ├── date/ # 日期小组件模块
|
||||
│ ├── text/ # 文本小组件模块
|
||||
│ ├── image/ # 图片小组件模块
|
||||
│ └── registry.ts # 小组件注册中心
|
||||
```
|
||||
|
||||
## 小组件开发指南
|
||||
|
||||
### 创建新小组件
|
||||
|
||||
1. **创建小组件文件夹**:在 `src/widgets/` 下创建新的文件夹,如 `newwidget/`
|
||||
|
||||
2. **创建必要文件**:
|
||||
- `types.ts` - 定义配置接口和默认配置
|
||||
- `Widget.vue` - 小组件显示组件
|
||||
- `Config.vue` - 小组件配置组件
|
||||
- `index.ts` - 模块导出文件
|
||||
|
||||
3. **实现类型定义**(types.ts):
|
||||
```typescript
|
||||
// 小组件配置接口
|
||||
export interface NewWidgetConfig {
|
||||
// 配置属性定义
|
||||
}
|
||||
|
||||
// 默认配置
|
||||
export const defaultConfig: NewWidgetConfig = {
|
||||
// 默认值
|
||||
};
|
||||
```
|
||||
|
||||
4. **实现小组件组件**(Widget.vue):
|
||||
```vue
|
||||
<template>
|
||||
<!-- 小组件UI -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NewWidgetConfig } from './types';
|
||||
// 组件逻辑
|
||||
</script>
|
||||
```
|
||||
|
||||
5. **实现配置组件**(Config.vue):
|
||||
```vue
|
||||
<template>
|
||||
<!-- 配置UI -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NewWidgetConfig } from './types';
|
||||
// 配置逻辑
|
||||
</script>
|
||||
```
|
||||
|
||||
6. **导出模块**(index.ts):
|
||||
```typescript
|
||||
import Widget from './Widget.vue';
|
||||
import Config from './Config.vue';
|
||||
import { defaultConfig } from './types';
|
||||
|
||||
export default {
|
||||
label: '新小组件',
|
||||
value: 'newwidget',
|
||||
component: Widget,
|
||||
configComponent: Config,
|
||||
getDefaultConfig: () => defaultConfig
|
||||
};
|
||||
```
|
||||
|
||||
7. **注册小组件**:在 `registry.ts` 中导入并添加到 `widgets` 数组
|
||||
|
||||
### 架构优势
|
||||
|
||||
- **模块化设计**:每个小组件独立成模块,包含所有相关代码
|
||||
- **类型安全**:TypeScript 提供完整的类型检查和智能提示
|
||||
- **可扩展性**:添加新小组件无需修改核心逻辑,只需创建新模块并注册
|
||||
- **代码组织**:相关代码集中在同一文件夹,便于维护和理解
|
||||
- **零侵入**:新增小组件不会影响现有小组件和核心功能
|
||||
│ ├── ClockWidget.vue
|
||||
│ ├── DateWidget.vue
|
||||
│ ├── TextWidget.vue
|
||||
|
@ -11,25 +11,18 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { decodeConfig } from '../utils/configUtils';
|
||||
import { widgets } from '../widgets/registry';
|
||||
|
||||
// 导入小组件组件
|
||||
import ClockWidget from '../widgets/ClockWidget.vue';
|
||||
import DateWidget from '../widgets/DateWidget.vue';
|
||||
import TextWidget from '../widgets/TextWidget.vue';
|
||||
import ImageWidget from '../widgets/ImageWidget.vue';
|
||||
|
||||
// 小组件注册表
|
||||
const widgetRegistry = {
|
||||
'clock': ClockWidget,
|
||||
'date': DateWidget,
|
||||
'text': TextWidget,
|
||||
'image': ImageWidget
|
||||
};
|
||||
// 创建小组件注册表
|
||||
const widgetRegistry: Record<string, any> = {};
|
||||
widgets.forEach(widget => {
|
||||
widgetRegistry[widget.value] = widget.component;
|
||||
});
|
||||
|
||||
const widgetType = ref('');
|
||||
const widgetConfig = ref({});
|
||||
const widgetComponent = computed(() => {
|
||||
return widgetRegistry[widgetType.value as keyof typeof widgetRegistry];
|
||||
return widgetRegistry[widgetType.value];
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -99,6 +99,8 @@
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-divider>预设样式</el-divider>
|
||||
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="applyPreset('modern')">现代风格</el-button>
|
||||
@ -113,23 +115,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
|
||||
// 为配置定义 props 接口
|
||||
interface ClockConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showSeconds: boolean;
|
||||
showDate: boolean;
|
||||
dateFormat: string;
|
||||
}
|
||||
import type { ClockConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
@ -8,23 +8,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// Define props interface
|
||||
interface ClockConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showSeconds: boolean;
|
||||
showDate: boolean;
|
||||
dateFormat: string;
|
||||
}
|
||||
import type { ClockConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
11
src/widgets/clock/index.ts
Normal file
11
src/widgets/clock/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Widget from './Widget.vue';
|
||||
import Config from './Config.vue';
|
||||
import { defaultConfig } from './types';
|
||||
|
||||
export default {
|
||||
label: '时钟小组件',
|
||||
value: 'clock',
|
||||
component: Widget,
|
||||
configComponent: Config,
|
||||
getDefaultConfig: () => defaultConfig
|
||||
};
|
33
src/widgets/clock/types.ts
Normal file
33
src/widgets/clock/types.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// 时钟小组件配置接口
|
||||
export interface ClockConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showSeconds: boolean;
|
||||
showDate: boolean;
|
||||
dateFormat: string;
|
||||
}
|
||||
|
||||
// 时钟小组件默认配置
|
||||
export const defaultConfig: ClockConfig = {
|
||||
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'
|
||||
};
|
@ -95,21 +95,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
|
||||
// Define props interface for config
|
||||
interface DateConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showWeekday: boolean;
|
||||
}
|
||||
import type { DateConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
@ -7,21 +7,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
// Define props interface
|
||||
interface DateConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showWeekday: boolean;
|
||||
}
|
||||
import type { DateConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
11
src/widgets/date/index.ts
Normal file
11
src/widgets/date/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Widget from './Widget.vue';
|
||||
import Config from './Config.vue';
|
||||
import { defaultConfig } from './types';
|
||||
|
||||
export default {
|
||||
label: '日期小组件',
|
||||
value: 'date',
|
||||
component: Widget,
|
||||
configComponent: Config,
|
||||
getDefaultConfig: () => defaultConfig
|
||||
};
|
29
src/widgets/date/types.ts
Normal file
29
src/widgets/date/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// 日期小组件配置接口
|
||||
export interface DateConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showWeekday: boolean;
|
||||
}
|
||||
|
||||
// 日期小组件默认配置
|
||||
export const defaultConfig: DateConfig = {
|
||||
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
|
||||
};
|
@ -59,18 +59,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
|
||||
// Define props interface for config
|
||||
interface ImageConfig {
|
||||
imageUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
opacity: number;
|
||||
borderRadius: number;
|
||||
shadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
}
|
||||
import type { ImageConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
@ -14,18 +14,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
// Define props interface
|
||||
interface ImageConfig {
|
||||
imageUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
opacity: number;
|
||||
borderRadius: number;
|
||||
shadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
}
|
||||
import type { ImageConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
11
src/widgets/image/index.ts
Normal file
11
src/widgets/image/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Widget from './Widget.vue';
|
||||
import Config from './Config.vue';
|
||||
import { defaultConfig } from './types';
|
||||
|
||||
export default {
|
||||
label: '图片小组件',
|
||||
value: 'image',
|
||||
component: Widget,
|
||||
configComponent: Config,
|
||||
getDefaultConfig: () => defaultConfig
|
||||
};
|
23
src/widgets/image/types.ts
Normal file
23
src/widgets/image/types.ts
Normal file
@ -0,0 +1,23 @@
|
||||
// 图片小组件配置接口
|
||||
export interface ImageConfig {
|
||||
imageUrl: string;
|
||||
width: number;
|
||||
height: number;
|
||||
opacity: number;
|
||||
borderRadius: number;
|
||||
shadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
}
|
||||
|
||||
// 图片小组件默认配置
|
||||
export const defaultConfig: ImageConfig = {
|
||||
imageUrl: '',
|
||||
width: 200,
|
||||
height: 200,
|
||||
opacity: 1,
|
||||
borderRadius: 0,
|
||||
shadow: false,
|
||||
shadowColor: 'rgba(0,0,0,0.5)',
|
||||
shadowBlur: 10
|
||||
};
|
@ -1,81 +1,23 @@
|
||||
// 小组件组件及其配置
|
||||
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';
|
||||
// 导入各个小组件模块
|
||||
import ClockWidget from './clock';
|
||||
import DateWidget from './date';
|
||||
import TextWidget from './text';
|
||||
import ImageWidget from './image';
|
||||
|
||||
// 小组件注册表
|
||||
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 },
|
||||
ClockWidget,
|
||||
DateWidget,
|
||||
TextWidget,
|
||||
ImageWidget,
|
||||
];
|
||||
|
||||
// 获取小组件默认配置
|
||||
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 {};
|
||||
for (const widget of widgets) {
|
||||
if (widget.value === widgetType) {
|
||||
return widget.getDefaultConfig();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
@ -85,20 +85,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
|
||||
// Define props interface for config
|
||||
interface TextConfig {
|
||||
text: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
}
|
||||
import type { TextConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
@ -6,20 +6,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
// Define props interface
|
||||
interface TextConfig {
|
||||
text: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
}
|
||||
import type { TextConfig } from './types';
|
||||
|
||||
// Define props with default values
|
||||
const props = withDefaults(defineProps<{
|
11
src/widgets/text/index.ts
Normal file
11
src/widgets/text/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Widget from './Widget.vue';
|
||||
import Config from './Config.vue';
|
||||
import { defaultConfig } from './types';
|
||||
|
||||
export default {
|
||||
label: '文本小组件',
|
||||
value: 'text',
|
||||
component: Widget,
|
||||
configComponent: Config,
|
||||
getDefaultConfig: () => defaultConfig
|
||||
};
|
27
src/widgets/text/types.ts
Normal file
27
src/widgets/text/types.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// 文本小组件配置接口
|
||||
export interface TextConfig {
|
||||
text: string;
|
||||
color: string;
|
||||
fontSize: number;
|
||||
fontWeight: string;
|
||||
fontFamily: string;
|
||||
textShadow: boolean;
|
||||
shadowColor: string;
|
||||
shadowBlur: number;
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
}
|
||||
|
||||
// 文本小组件默认配置
|
||||
export const defaultConfig: TextConfig = {
|
||||
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']
|
||||
};
|
20
src/widgets/types.ts
Normal file
20
src/widgets/types.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import type { Component } from 'vue';
|
||||
|
||||
// 基础小组件配置接口
|
||||
export interface BaseWidgetConfig {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 小组件注册信息接口
|
||||
export interface WidgetRegistration {
|
||||
label: string;
|
||||
value: string;
|
||||
component: Component;
|
||||
configComponent: Component;
|
||||
getDefaultConfig: () => BaseWidgetConfig;
|
||||
}
|
||||
|
||||
// 小组件模块导出接口
|
||||
export interface WidgetModule {
|
||||
registration: WidgetRegistration;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user