v-uni-app-ui 1.0.0 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -0
- package/components/config/css/basic.scss +19 -0
- package/components/config/interface/basic-type.js +16 -0
- package/components/config/interface/components-interface.ts +0 -0
- package/components/config/interface/monitor/components/input-monitor.js +0 -0
- package/components/config/interface/monitor/property-monitor.ts +136 -0
- package/components/config/interface/props/basic-props.ts +88 -0
- package/components/config/interface/props/components/button-props.ts +85 -0
- package/components/config/interface/props/components/input-props.ts +69 -0
- package/components/config/interface/props/props-tools.ts +64 -0
- package/components/config/style/basic.js +346 -0
- package/components/config/style/component-registry.js +142 -0
- package/components/config/style/components/button-style.js +160 -0
- package/components/config/style/components/input-style.js +98 -0
- package/components/config/style/components-style.js +622 -0
- package/components/config/style/property-mapper.js +377 -0
- package/components/config/style/pseudo-processor.js +213 -0
- package/components/config.js +123 -0
- package/components/icon/iconfont.css +87 -0
- package/components/icon/iconfont.js +1 -0
- package/components/icon/iconfont.json +135 -0
- package/components/icon/iconfont.ttf +0 -0
- package/components/icon/iconfont.woff +0 -0
- package/components/icon/iconfont.woff2 +0 -0
- package/components/layout/v-card/v-card.vue +108 -0
- package/components/layout/v-grid/v-grid.vue +162 -0
- package/components/layout/v-icon-grid/v-icon-grid.vue +195 -0
- package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +172 -0
- package/components/layout/v-list/v-list.vue +43 -0
- package/components/layout/v-row/v-row.vue +142 -0
- package/components/layout/v-waterfall/v-waterfall.vue +79 -0
- package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +96 -0
- package/components/model/compound/v-console/v-console.js +20 -0
- package/components/model/compound/v-console/v-console.vue +299 -0
- package/components/model/compound/v-date-time/v-date-time.vue +261 -0
- package/components/model/compound/v-dialog/v-dialog.vue +178 -0
- package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +83 -0
- package/components/model/compound/v-form/v-form.vue +226 -0
- package/components/model/compound/v-form-item/v-form-item.vue +255 -0
- package/components/model/compound/v-image/v-image.vue +357 -0
- package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +101 -0
- package/components/model/compound/v-page/v-page.vue +11 -0
- package/components/model/compound/v-pages/v-pages.vue +141 -0
- package/components/model/compound/v-picker-list/v-picker-list.vue +109 -0
- package/components/model/compound/v-popup/v-popup.vue +151 -0
- package/components/model/compound/v-radio-group/v-radio-group.vue +86 -0
- package/components/model/compound/v-select-picker/v-select-picker.vue +202 -0
- package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +221 -0
- package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +203 -0
- package/components/model/compound/v-switch/v-switch.vue +136 -0
- package/components/model/compound/v-tabs-page/v-tabs-page.vue +138 -0
- package/components/model/native/v-badge/v-badge.vue +143 -0
- package/components/model/native/v-button/v-button.vue +81 -0
- package/components/model/native/v-carousel/v-carousel.vue +138 -0
- package/components/model/native/v-checkbox/v-checkbox.vue +215 -0
- package/components/model/native/v-collapse/v-collapse.vue +190 -0
- package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +92 -0
- package/components/model/native/v-input/v-input.vue +163 -0
- package/components/model/native/v-input-code/v-input-code.vue +146 -0
- package/components/model/native/v-loading/v-loading.vue +206 -0
- package/components/model/native/v-menu/v-menu.vue +222 -0
- package/components/model/native/v-menu-slide/v-menu-slide.vue +364 -0
- package/components/model/native/v-min-loading/v-min-loading.vue +80 -0
- package/components/model/native/v-null/v-null.vue +97 -0
- package/components/model/native/v-overlay/v-overlay.vue +96 -0
- package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +157 -0
- package/components/model/native/v-radio/v-radio.vue +138 -0
- package/components/model/native/v-scroll-list/v-scroll-list.vue +169 -0
- package/components/model/native/v-steps/v-steps.vue +253 -0
- package/components/model/native/v-table/v-table.vue +203 -0
- package/components/model/native/v-tabs/v-tabs.vue +235 -0
- package/components/model/native/v-tag/v-tag.vue +206 -0
- package/components/model/native/v-text/v-text.vue +187 -0
- package/components/model/native/v-textarea/v-textarea.vue +178 -0
- package/components/model/native/v-title/v-title.vue +91 -0
- package/components/model/native/v-toast/info.png +0 -0
- package/components/model/native/v-toast/success.png +0 -0
- package/components/model/native/v-toast/v-toast.vue +198 -0
- package/components/model/native/v-toast/warn.png +0 -0
- package/components/model/native/v-upload-file-button/v-upload-file-button.vue +296 -0
- package/components/model/native/v-video/v-video.vue +175 -0
- package/components/model/native/v-window/v-window.vue +158 -0
- package/components/utils/event-modifiers.ts +139 -0
- package/components/utils/validator.ts +451 -0
- package/index.js +372 -0
- package/package.json +25 -93
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# v-uni-app ui Bate版(1.0.3)
|
|
2
|
+
|
|
3
|
+
#### 一、框架定位
|
|
4
|
+
1.新一代轻量级跨端解决方案。
|
|
5
|
+
2.基于 Uni-App 的自研 UI 框架 vuniapp 设计方案。
|
|
6
|
+
3.专为 Uni-App 生态打造的模块化 UI 框架,面向高效开发与极致性能而生。
|
|
7
|
+
|
|
8
|
+
#### 二、核心目标
|
|
9
|
+
| 特性 | 技术实现 |
|
|
10
|
+
|----------|------------------------------------|
|
|
11
|
+
| 零学习成本 | 完全兼容 Uni-App 原生语法,支持 Vue3 版本 |
|
|
12
|
+
| 原子化架构 | 组件支持单文件独立运行,最低仅需 3KB 即可启用核心功能 |
|
|
13
|
+
| 工程化友好 | 独创 文件级按需加载 机制,无需配置构建工具 |
|
|
14
|
+
| 智能样式 | 基于 CSS 变量的主题系统,支持运行时动态换肤 |
|
|
15
|
+
| 增强型组件 | 内置数据驱动表单生成器、可视化布局编排器等进阶工具 |
|
|
16
|
+
|
|
17
|
+
#### 二、框架结构
|
|
18
|
+
v-uni-app-ui
|
|
19
|
+
│ config.js (预计在上线正式1.0.0废弃)
|
|
20
|
+
│
|
|
21
|
+
├─config
|
|
22
|
+
│ ├─css
|
|
23
|
+
│ │ basic.scss 基础样式配置
|
|
24
|
+
│ │
|
|
25
|
+
│ ├─interface
|
|
26
|
+
│ │ │ basic-type.js 基础枚举
|
|
27
|
+
│ │ │ components-interface.ts 组件接口
|
|
28
|
+
│ │ │
|
|
29
|
+
│ │ ├─monitor
|
|
30
|
+
│ │ │ │ property-monitor.ts 映射监控
|
|
31
|
+
│ │ │ │
|
|
32
|
+
│ │ │ └─components 组件监控
|
|
33
|
+
│ │ │ input-monitor.js
|
|
34
|
+
│ │ │
|
|
35
|
+
│ │ └─props props统一封装
|
|
36
|
+
│ │ │ basic-props.ts 基础Props
|
|
37
|
+
│ │ │ props-tools.ts Props类型验证
|
|
38
|
+
│ │ │
|
|
39
|
+
│ │ └─components 组件Props
|
|
40
|
+
│ │ button-props.ts
|
|
41
|
+
│ │ input-props.ts
|
|
42
|
+
│ │
|
|
43
|
+
│ ├─style style组件样式
|
|
44
|
+
│ │ │ basic.js 基础样式
|
|
45
|
+
│ │ │ component-registry.js 组件样式注册
|
|
46
|
+
│ │ │ components-style.js 组件style配置
|
|
47
|
+
│ │ │ property-mapper.js 样式映射配置
|
|
48
|
+
│ │ │ pseudo-processor.js 伪元素映射
|
|
49
|
+
│ │ │
|
|
50
|
+
│ │ └─components style组件封装
|
|
51
|
+
│ │ button-style.js
|
|
52
|
+
│ │ input-style.js
|
|
53
|
+
│ │
|
|
54
|
+
│ └─type (预计在上线正式1.0.0废弃)
|
|
55
|
+
├─icon 图标库
|
|
56
|
+
│ iconfont.css
|
|
57
|
+
│ iconfont.js
|
|
58
|
+
│ iconfont.json
|
|
59
|
+
│ iconfont.ttf
|
|
60
|
+
│ iconfont.woff
|
|
61
|
+
│ iconfont.woff2
|
|
62
|
+
|
|
|
63
|
+
├─layout 布局组件
|
|
64
|
+
│
|
|
65
|
+
├─model 模型组件
|
|
66
|
+
│ ├─compound 复合组件
|
|
67
|
+
│ │
|
|
68
|
+
│ └─native 原生组件
|
|
69
|
+
│
|
|
70
|
+
└─utils 工具包
|
|
71
|
+
validator.ts
|
|
72
|
+
|
|
73
|
+
##### 1. Model 组件层(核心交互单元)
|
|
74
|
+
|
|
75
|
+
| 模块 | 功能特性 | 技术实现 |
|
|
76
|
+
|------|--------------------|-----------------|
|
|
77
|
+
| 原子组件 | Button/Input 等基础元素 | 基于 uni-app 封装 |
|
|
78
|
+
| 复合组件 | 表单生成器/选择器/级联选择器 | 基于 原子组件 封装、可以随意删减复合组件 |
|
|
79
|
+
|
|
80
|
+
##### 2.Layout 布局层(视觉编排系统)
|
|
81
|
+
|
|
82
|
+
| 模块 | 功能特性 | 技术实现 |
|
|
83
|
+
|-------|---------|---------------------------|
|
|
84
|
+
| 基础布局 | 多种流式布局 | CSS Flex / Grid + 自定义属性变量 |
|
|
85
|
+
| 响应式系统 | 多端自适应方案 | - |
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
#### 四、版本说明
|
|
89
|
+
|
|
90
|
+
我们正在废弃Bate版原有的架构,将最使用更加高度自由化、灵活化、适配化的组件。
|
|
91
|
+
我们已经修改如下:
|
|
92
|
+
V-Btton
|
|
93
|
+
V-Input
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
#### 五、支持环境
|
|
98
|
+
|
|
99
|
+
| 环境名称 | 是否支持 |
|
|
100
|
+
|-----------|------|
|
|
101
|
+
| APP(Vue3) | √ |
|
|
102
|
+
| H5 | √ |
|
|
103
|
+
| 微信小程序(H5) | √ |
|
|
104
|
+
| 支付宝(H5) | √ |
|
|
105
|
+
| 360小程序 | × |
|
|
106
|
+
| 等等 | × |
|
|
107
|
+
|
|
108
|
+
#### 五、支持设备
|
|
109
|
+
| 设备名称 | 是否支持 |
|
|
110
|
+
|------|------|
|
|
111
|
+
| 安卓 | √ |
|
|
112
|
+
| IOS | √ |
|
|
113
|
+
| 鸿蒙 | √ |
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### 六、企业级应用案例
|
|
118
|
+
1. 湖南省政务平台《湘易办》
|
|
119
|
+
- 应用场景:娄底市政务办事大厅系统
|
|
120
|
+
- 技术亮点:实现 100+ 表单页面的可视化配置、跨端组件复用率达到 80%、核心业务模块开发效率提升 50%↑
|
|
121
|
+
- 用户反馈:"通过 v-uni-app 的布局系统,我们仅用 5 天就完成了原本需要 2 周工期的政务门户改版"
|
|
122
|
+
|
|
123
|
+
### 七、开发者生态
|
|
124
|
+
##### 贡献者计划
|
|
125
|
+
|
|
126
|
+
| 角色 | 成员 | 主要贡献 |
|
|
127
|
+
|-------|--------|----------------|
|
|
128
|
+
| 核心开发者 | 方笔山、短短 | 框架架构设计、核心模块开发 |
|
|
129
|
+
| 质量保障 | 抱歉、风 | 多端兼容性测试、性能优化 |
|
|
130
|
+
| 社区运营 | 橙子 | 文档体系建设、开发者社区维护 |
|
|
131
|
+
|
|
132
|
+
##### 高校合作
|
|
133
|
+
- 湖南工业大学
|
|
134
|
+
- 湖南师范大学
|
|
135
|
+
|
|
136
|
+
#### 联系我们
|
|
137
|
+
|
|
138
|
+
| 渠道 | 联系方式 | 响应时间 |
|
|
139
|
+
|------|----------------------------------|--------|
|
|
140
|
+
| 紧急问题 | QQ: 2214685773 (12h 工单) | 12 小时内 |
|
|
141
|
+
| 技术咨询 | kwc1234@qq.com | 12 小时内 |
|
|
142
|
+
| 文档中心 | https://docs.qq.com/doc/DWEVjQnZVbXBsdnBu | - |
|
|
143
|
+
| GitEE 仓库 | https://gitee.com/kwc410/vuniapp | - |
|
|
144
|
+
|
|
145
|
+
#### 招募
|
|
146
|
+
|
|
147
|
+
招募前端开发人员由于本项目为开源项目前期不会提供任何资金,将会在后续根据下载量分红 1000(下载量)/10(RMB)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.loading-icon {
|
|
2
|
+
margin: 0 8rpx 0 0;
|
|
3
|
+
.spinner {
|
|
4
|
+
width: 20rpx;
|
|
5
|
+
height: 20rpx;
|
|
6
|
+
border: 1rpx solid;
|
|
7
|
+
border-radius: 50%;
|
|
8
|
+
border-top-color: white;
|
|
9
|
+
animation: spin 1s ease-in-out infinite;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@keyframes spin {
|
|
16
|
+
to {
|
|
17
|
+
transform: rotate(360deg);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// 基础类型定义
|
|
2
|
+
export type BasicType = 'default' | 'delete' | 'success' | 'info' | 'warning'
|
|
3
|
+
export type BasicSize = 'small' | 'medium' | 'large'
|
|
4
|
+
export type BasicDetailedSize = 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large'
|
|
5
|
+
export type BasicShape = 'semicircle' | 'square' | 'round'
|
|
6
|
+
export type BasicHorizontallyPosition = 'left' | 'right' | 'center'
|
|
7
|
+
export type BasicVerticalPosition = 'top' | 'bottom' | 'center'
|
|
8
|
+
export type BasicPosition = 'left' | 'right' | 'top' | 'bottom' | 'center'
|
|
9
|
+
|
|
10
|
+
// 交互类型
|
|
11
|
+
export type InteractionMode = 'hover' | 'active' | 'focus' | 'disabled'
|
|
12
|
+
|
|
13
|
+
//组件类型定义
|
|
14
|
+
export type ButtonShape = 'default' | 'none' | 'circle'
|
|
15
|
+
export type InputType = 'text' | 'id' | 'safe-password' | 'password' | 'number' | 'digit' | 'numeric' | 'decimal' |
|
|
16
|
+
'email' | 'phone' | 'url' | 'textarea' | 'name' | 'search' | 'none'
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { watch, onUnmounted, WatchStopHandle, WatchSource, Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
type EmitFn = (event: string, ...args: any[]) => void;
|
|
4
|
+
|
|
5
|
+
interface MonitorOptions<T = any> {
|
|
6
|
+
deep?: boolean;
|
|
7
|
+
immediate?: boolean;
|
|
8
|
+
onChange?: (newValue: T, oldValue: T) => void;
|
|
9
|
+
emitEvent?: string;
|
|
10
|
+
lazy?: boolean;
|
|
11
|
+
enabled?: Ref<boolean>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface MonitorInstance extends WatchStopHandle {
|
|
15
|
+
start: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface BaseConfig<T = any> extends MonitorOptions<T> {
|
|
19
|
+
propertyName: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface PropsConfig<T = any> extends BaseConfig<T> {
|
|
23
|
+
source?: never; // 明确禁止source
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface SourceConfig<T = any> extends BaseConfig<T> {
|
|
27
|
+
source: WatchSource<T>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ConfigItem<T = any> = PropsConfig<T> | SourceConfig<T>;
|
|
31
|
+
|
|
32
|
+
function isSourceConfig<T = any>(config: ConfigItem<T>): config is SourceConfig<T> {
|
|
33
|
+
return 'source' in config && config.source !== undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function usePropertyMonitor<T = any>(props: Record<string, any>, propertyName: string, emit: EmitFn, options?: MonitorOptions<T>): MonitorInstance;
|
|
37
|
+
|
|
38
|
+
export function usePropertyMonitor<T = any>(config: SourceConfig<T>, emit: EmitFn): MonitorInstance;
|
|
39
|
+
|
|
40
|
+
export function usePropertyMonitor<T = any>(configs: ConfigItem<T>[], emit: EmitFn): MonitorInstance;
|
|
41
|
+
|
|
42
|
+
export function usePropertyMonitor<T = any>(
|
|
43
|
+
propsOrConfigs: any,
|
|
44
|
+
propertyNameOrEmit: string | EmitFn,
|
|
45
|
+
emitOrOptions?: EmitFn | MonitorOptions<T>,
|
|
46
|
+
options?: MonitorOptions<T>
|
|
47
|
+
): MonitorInstance {
|
|
48
|
+
// 参数解析
|
|
49
|
+
const isBatch = Array.isArray(propsOrConfigs);
|
|
50
|
+
const emitFn = typeof propertyNameOrEmit === 'function' ? propertyNameOrEmit : (emitOrOptions as EmitFn);
|
|
51
|
+
|
|
52
|
+
const configs: ConfigItem<T>[] = [];
|
|
53
|
+
|
|
54
|
+
if (isBatch) {
|
|
55
|
+
configs.push(...propsOrConfigs);
|
|
56
|
+
} else if (typeof propsOrConfigs === 'object' && !('source' in propsOrConfigs)) {
|
|
57
|
+
// Props模式:提取 propertyName 和 options
|
|
58
|
+
const propertyName = propertyNameOrEmit as string;
|
|
59
|
+
const monitorOptions = typeof emitOrOptions === 'function' ? options : (emitOrOptions as MonitorOptions<T>);
|
|
60
|
+
|
|
61
|
+
configs.push({
|
|
62
|
+
propertyName,
|
|
63
|
+
...(monitorOptions || {})
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
// 单配置模式
|
|
67
|
+
configs.push(propsOrConfigs as ConfigItem<T>);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let stopHandles: WatchStopHandle[] = [];
|
|
71
|
+
let started = false;
|
|
72
|
+
|
|
73
|
+
const createWatchers = () => {
|
|
74
|
+
if (stopHandles.length) {
|
|
75
|
+
stopHandles.forEach((stop) => stop());
|
|
76
|
+
stopHandles = [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
configs.forEach((config) => {
|
|
80
|
+
const { propertyName, onChange, emitEvent, enabled, ...watchOpts } = config;
|
|
81
|
+
|
|
82
|
+
const shouldWatch = enabled ? enabled.value : true;
|
|
83
|
+
if (!shouldWatch) return;
|
|
84
|
+
|
|
85
|
+
const watchSource = isSourceConfig(config) ? config.source : () => (propsOrConfigs as Record<string, any>)[propertyName!];
|
|
86
|
+
|
|
87
|
+
const stop = watch(
|
|
88
|
+
watchSource,
|
|
89
|
+
(newValue, oldValue) => {
|
|
90
|
+
onChange?.(newValue, oldValue);
|
|
91
|
+
emitFn(emitEvent ?? `update:${propertyName}`, newValue);
|
|
92
|
+
},
|
|
93
|
+
watchOpts
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
stopHandles.push(stop);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// 监听enabled变化
|
|
101
|
+
configs.forEach((config) => {
|
|
102
|
+
if (config.enabled) {
|
|
103
|
+
watch(
|
|
104
|
+
config.enabled,
|
|
105
|
+
() => {
|
|
106
|
+
if (started) {
|
|
107
|
+
createWatchers();
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{ immediate: false }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const start = () => {
|
|
116
|
+
if (!started) {
|
|
117
|
+
started = true;
|
|
118
|
+
createWatchers();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const stop = () => {
|
|
123
|
+
stopHandles.forEach((handle) => handle());
|
|
124
|
+
stopHandles = [];
|
|
125
|
+
started = false;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const isLazy = configs.some((c) => c.lazy);
|
|
129
|
+
if (!isLazy) {
|
|
130
|
+
start();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
onUnmounted(stop);
|
|
134
|
+
|
|
135
|
+
return Object.assign(stop, { start });
|
|
136
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// 简化 BasicPropsDefinition 类型
|
|
2
|
+
type VuePropDefinition = {
|
|
3
|
+
type: any;
|
|
4
|
+
default?: any;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
validator?: (value: any) => boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 通用组件基础 props 定义
|
|
11
|
+
* 所有组件都会自动继承这些 props
|
|
12
|
+
*/
|
|
13
|
+
export const BasicProps = {
|
|
14
|
+
id: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: ''
|
|
17
|
+
},
|
|
18
|
+
className: {
|
|
19
|
+
type: String,
|
|
20
|
+
default: ''
|
|
21
|
+
},
|
|
22
|
+
boxStyle: {
|
|
23
|
+
type: Object,
|
|
24
|
+
default: () => ({})
|
|
25
|
+
},
|
|
26
|
+
style: {
|
|
27
|
+
type: Object,
|
|
28
|
+
default: () => ({})
|
|
29
|
+
},
|
|
30
|
+
accessibility:{
|
|
31
|
+
type:Object,
|
|
32
|
+
default:()=>({})
|
|
33
|
+
}
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
// 获取基础属性的键名类型
|
|
37
|
+
type BasicPropsKeys = keyof typeof BasicProps;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 创建扩展基础 props 的验证器
|
|
41
|
+
*/
|
|
42
|
+
export function extendWithBasicProps<T extends Record<string, VuePropDefinition>>(
|
|
43
|
+
componentProps: T,
|
|
44
|
+
options: {
|
|
45
|
+
exclude?: BasicPropsKeys[];
|
|
46
|
+
override?: Partial<Record<BasicPropsKeys, any>>;
|
|
47
|
+
} = {}
|
|
48
|
+
): Record<string, VuePropDefinition> {
|
|
49
|
+
const { exclude = [], override = {} } = options;
|
|
50
|
+
const mergedProps: Record<string, VuePropDefinition> = {};
|
|
51
|
+
|
|
52
|
+
// 添加基础 props
|
|
53
|
+
Object.entries(BasicProps).forEach(([key, value]) => {
|
|
54
|
+
if (!exclude.includes(key as BasicPropsKeys)) {
|
|
55
|
+
const propKey = key as BasicPropsKeys;
|
|
56
|
+
if (override[propKey] !== undefined) {
|
|
57
|
+
// 修复:正确处理覆盖值
|
|
58
|
+
mergedProps[key] = {
|
|
59
|
+
...value,
|
|
60
|
+
default: typeof override[propKey] === 'object' && override[propKey] !== null && 'default' in override[propKey] ? override[propKey].default : override[propKey]
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
mergedProps[key] = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 添加组件特定 props
|
|
69
|
+
Object.entries(componentProps).forEach(([key, value]) => {
|
|
70
|
+
mergedProps[key] = value;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return mergedProps;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 从 props 配置中提取类型
|
|
78
|
+
*/
|
|
79
|
+
export type ExtractPropTypes<T> = {
|
|
80
|
+
[K in keyof T]: T[K] extends { type: any } ? (T[K] extends { default?: infer D } ? (D extends () => infer R ? R : D) : any) : any;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 创建 Vue 兼容的 props 定义
|
|
85
|
+
*/
|
|
86
|
+
export function createVueProps<T extends Record<string, VuePropDefinition>>(props: T): T {
|
|
87
|
+
return props;
|
|
88
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { PropType } from 'vue';
|
|
2
|
+
import { extendWithBasicProps, ExtractPropTypes } from '@/components/config/interface/props/basic-props';
|
|
3
|
+
import { BasicType, ButtonShape, BasicSize } from '@/components/config/interface/basic-type.js';
|
|
4
|
+
import { Validators } from '@/components/utils/validator';
|
|
5
|
+
import { PropTools } from '@/components/config/interface/props/props-tools';
|
|
6
|
+
/**
|
|
7
|
+
* 按钮组件 props 定义
|
|
8
|
+
*/
|
|
9
|
+
const buttonProps = extendWithBasicProps(
|
|
10
|
+
{
|
|
11
|
+
//使用PropTools简写
|
|
12
|
+
shape: PropTools.enum<ButtonShape>(['default', 'none', 'circle'], 'default', 'buttonShape'),
|
|
13
|
+
type: {
|
|
14
|
+
type: String as PropType<BasicType>,
|
|
15
|
+
default: 'default',
|
|
16
|
+
validator: Validators.enum(['default', 'success', 'warning', 'info', 'delete'], 'buttonType')
|
|
17
|
+
},
|
|
18
|
+
size: PropTools.enum<BasicSize>(['small', 'medium', 'large'], 'medium', 'buttonSize'),
|
|
19
|
+
disabled: PropTools.boolean(false),
|
|
20
|
+
loading: PropTools.boolean(false),
|
|
21
|
+
plain: PropTools.boolean(false),
|
|
22
|
+
text: PropTools.boolean(false),
|
|
23
|
+
stabilizationTime: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 0,
|
|
26
|
+
validator: Validators.numberRange(0, 1_000_000, 'Stabilization time must be between 0 and 1,000,000 ms.')
|
|
27
|
+
},
|
|
28
|
+
debounceTime: {
|
|
29
|
+
type: Number,
|
|
30
|
+
default: 0,
|
|
31
|
+
validator: Validators.numberRange(0, 1_000_000, 'The anti-shake waiting time must be between 0 and 1,000,000 milliseconds.')
|
|
32
|
+
},
|
|
33
|
+
intervalUpdateTime: {
|
|
34
|
+
type: Number,
|
|
35
|
+
default: 1000,
|
|
36
|
+
validator: Validators.numberRange(100, 100_000, 'Interval must be between 100 and 100,000 ms.')
|
|
37
|
+
},
|
|
38
|
+
degressionTime: {
|
|
39
|
+
type: Number,
|
|
40
|
+
default: 1000,
|
|
41
|
+
validator: Validators.numberRange(100, 10_000, 'Degression time must be between 100 and 10,000 ms.')
|
|
42
|
+
},
|
|
43
|
+
resetOnClick: PropTools.boolean(false),
|
|
44
|
+
hoverStartTime: {
|
|
45
|
+
type: Number,
|
|
46
|
+
default: 20,
|
|
47
|
+
validator: Validators.numberRange(10, 10_000, 'hover start time must be between 10 and 10,000 ms.')
|
|
48
|
+
},
|
|
49
|
+
hoverStayTime: {
|
|
50
|
+
type: Number,
|
|
51
|
+
default: 70,
|
|
52
|
+
validator: Validators.numberRange(10, 10_000, 'hover stay time must be between 10 and 10,000 ms.')
|
|
53
|
+
},
|
|
54
|
+
//组合校验
|
|
55
|
+
// customProp: {
|
|
56
|
+
// type: String,
|
|
57
|
+
// default: '',
|
|
58
|
+
// validator: Validators.combine(
|
|
59
|
+
// Validators.required('This field is required.'),
|
|
60
|
+
// Validators.length(3, 20, 'Length must be 3-20 characters.'),
|
|
61
|
+
// Validators.pattern(/^[a-zA-Z0-9_]+$/, 'Only letters, numbers and underscore are allowed.')
|
|
62
|
+
// )
|
|
63
|
+
// }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
override: {
|
|
67
|
+
id: {
|
|
68
|
+
default: 'VButton'
|
|
69
|
+
},
|
|
70
|
+
className: {
|
|
71
|
+
default: 'v-button'
|
|
72
|
+
},
|
|
73
|
+
boxStyle: {
|
|
74
|
+
default: {
|
|
75
|
+
width: 'auto',
|
|
76
|
+
height: '50rpx',
|
|
77
|
+
padding: '14rpx 2rpx'
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
accessibility: {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
export default buttonProps;
|
|
85
|
+
export type ButtonProps = ExtractPropTypes<typeof buttonProps>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { extendWithBasicProps, ExtractPropTypes } from '@/components/config/interface/props/basic-props';
|
|
2
|
+
import { InputType, BasicSize, BasicHorizontallyPosition } from '@/components/config/interface/basic-type.js';
|
|
3
|
+
import { Validators } from '@/components/utils/validator';
|
|
4
|
+
import { PropTools } from '@/components/config/interface/props/props-tools';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 文本框组件 props 定义
|
|
8
|
+
*/
|
|
9
|
+
const inputProps = extendWithBasicProps(
|
|
10
|
+
{
|
|
11
|
+
value: PropTools.string(),
|
|
12
|
+
size: PropTools.enum<BasicSize>(['small', 'medium', 'large'], 'medium', 'inputSize'),
|
|
13
|
+
placeholder: PropTools.array(),
|
|
14
|
+
placeholderTimeNumber: {
|
|
15
|
+
type: Number,
|
|
16
|
+
default: 0,
|
|
17
|
+
validator: Validators.numberRange(0, 1_000_000, 'The prompt switching time must be between 0 and 1,000,000 milliseconds.')
|
|
18
|
+
},
|
|
19
|
+
maxlength: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: -1,
|
|
22
|
+
validator: Validators.numberRange(-1, 1_0000_0000, 'The prompt switching time must be between 0 and 1,000,000 milliseconds.')
|
|
23
|
+
},
|
|
24
|
+
disabled: PropTools.boolean(false),
|
|
25
|
+
type: PropTools.enum<InputType>(
|
|
26
|
+
['text', 'id', 'safe-password', 'password', 'number', 'digit', 'numeric', 'decimal', 'email', 'phone', 'url', 'textarea', 'name', 'search', 'none'],
|
|
27
|
+
'text',
|
|
28
|
+
'inputType'
|
|
29
|
+
),
|
|
30
|
+
isWordCounter: PropTools.boolean(false),
|
|
31
|
+
textPosition: PropTools.enum<BasicHorizontallyPosition>(['left', 'right', 'center'], 'left', 'inputTextPosition'),
|
|
32
|
+
autoFocus: PropTools.boolean(false),
|
|
33
|
+
validators: PropTools.string(),
|
|
34
|
+
placeholderStyle: PropTools.object(),
|
|
35
|
+
cursor: PropTools.number(),
|
|
36
|
+
selectionStart: {
|
|
37
|
+
type: Number,
|
|
38
|
+
default: -1,
|
|
39
|
+
validator: Validators.numberRange(-1, 10_000, 'selection start must be between -1 and 10,000 milliseconds.')
|
|
40
|
+
},
|
|
41
|
+
selectionEnd: {
|
|
42
|
+
type: Number,
|
|
43
|
+
default: -1,
|
|
44
|
+
validator: Validators.numberRange(-1, 10_000, 'selection end must be between -1 and 10,000 milliseconds.')
|
|
45
|
+
},
|
|
46
|
+
isIcon: PropTools.boolean(false),
|
|
47
|
+
hiddenSymbols: PropTools.string('·'),
|
|
48
|
+
randomNumber: PropTools.boolean(false),
|
|
49
|
+
cursorColor: PropTools.string(),
|
|
50
|
+
focus: PropTools.boolean(false),
|
|
51
|
+
autoBlur: PropTools.boolean(false),
|
|
52
|
+
cursorSpacing:PropTools.number(0),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
override: {
|
|
56
|
+
id: {
|
|
57
|
+
default: 'VInput'
|
|
58
|
+
},
|
|
59
|
+
className: {
|
|
60
|
+
default: 'v-input'
|
|
61
|
+
},
|
|
62
|
+
boxStyle: {
|
|
63
|
+
default: {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
export default inputProps;
|
|
69
|
+
export type inputProps = ExtractPropTypes<typeof inputProps>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Validators } from '@/components/utils/validator.ts';
|
|
2
|
+
|
|
3
|
+
// 简化类型定义
|
|
4
|
+
type SimplePropDefinition = {
|
|
5
|
+
type: any;
|
|
6
|
+
default?: any;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
validator?: (value: any) => boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// 简化的属性创建函数
|
|
12
|
+
export function createProp<T>(type: any, defaultValue?: T, validator?: (value: any) => boolean): SimplePropDefinition {
|
|
13
|
+
const prop: SimplePropDefinition = { type };
|
|
14
|
+
|
|
15
|
+
if (defaultValue !== undefined) {
|
|
16
|
+
prop.default = defaultValue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (validator) {
|
|
20
|
+
prop.validator = validator;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return prop;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 快捷方法
|
|
27
|
+
export const PropTools = {
|
|
28
|
+
// 基础类型
|
|
29
|
+
string: (defaultValue: string = '', validator?: (value: string) => boolean) => createProp(String, defaultValue, validator),
|
|
30
|
+
|
|
31
|
+
number: (defaultValue: number = 0, validator?: (value: number) => boolean) => createProp(Number, defaultValue, validator),
|
|
32
|
+
|
|
33
|
+
boolean: (defaultValue: boolean = false) => createProp(Boolean, defaultValue),
|
|
34
|
+
|
|
35
|
+
array: <T>(defaultValue: T[] = []) => createProp(Array, () => defaultValue),
|
|
36
|
+
|
|
37
|
+
object: <T extends Record<string, any>>(defaultValue: T = {} as T) => createProp(Object, () => defaultValue),
|
|
38
|
+
|
|
39
|
+
function: <T extends Function>(defaultValue?: T) => createProp(Function, defaultValue),
|
|
40
|
+
|
|
41
|
+
// 枚举类型
|
|
42
|
+
enum: <T extends string>(allowedValues: readonly T[], defaultValue: T, displayName?: string) =>
|
|
43
|
+
createProp(String, defaultValue, Validators.enum(allowedValues, `${displayName || '属性'}必须是以下值之一: ${allowedValues.join(', ')}`)),
|
|
44
|
+
|
|
45
|
+
// 任意类型
|
|
46
|
+
any: (defaultValue: any = null) => createProp(null, defaultValue),
|
|
47
|
+
|
|
48
|
+
// 常用属性
|
|
49
|
+
id: () => createProp(String, ''),
|
|
50
|
+
className: () => createProp(String, ''),
|
|
51
|
+
style: () => createProp(Object, () => ({})),
|
|
52
|
+
|
|
53
|
+
// 带验证的属性
|
|
54
|
+
stringLength: (defaultValue: string = '', min?: number, max?: number) => createProp(String, defaultValue, Validators.length(min, max)),
|
|
55
|
+
|
|
56
|
+
numberRange: (defaultValue: number, min: number, max: number) => createProp(Number, defaultValue, Validators.numberRange(min, max)),
|
|
57
|
+
|
|
58
|
+
// 格式验证
|
|
59
|
+
email: (defaultValue: string = '') => createProp(String, defaultValue, Validators.email()),
|
|
60
|
+
|
|
61
|
+
phoneNumber: (defaultValue: string = '') => createProp(String, defaultValue, Validators.phoneNumber()),
|
|
62
|
+
|
|
63
|
+
url: (defaultValue: string = '') => createProp(String, defaultValue, Validators.url())
|
|
64
|
+
};
|