form-create-wot 0.1.0

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 ADDED
@@ -0,0 +1,182 @@
1
+ # form-create-wot
2
+
3
+ > form-create JSON 动态表单渲染引擎,基于 wot-design-uni,适用于 uni-app 移动端
4
+
5
+ 将管理端 [form-create](https://github.com/xaboy/form-create)(Ant Design Vue / Element Plus)设计的 JSON 表单配置,在 uni-app 移动端(H5 / 小程序 / App)使用 [wot-design-uni](https://wot-ui.cn) 组件渲染。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install form-create-wot
11
+ ```
12
+
13
+ > 前置依赖:项目中需已安装 `wot-design-uni` 和 `sass`
14
+
15
+ ## 配置
16
+
17
+ ### 1. 注册插件
18
+
19
+ ```typescript
20
+ // main.ts
21
+ import { createSSRApp } from 'vue'
22
+ import App from './App.vue'
23
+ import FormCreateWot from 'form-create-wot'
24
+
25
+ export function createApp() {
26
+ const app = createSSRApp(App)
27
+ app.use(FormCreateWot)
28
+ return { app }
29
+ }
30
+ ```
31
+
32
+ ### 2. 配置 easycom(pages.json)
33
+
34
+ ```json
35
+ {
36
+ "easycom": {
37
+ "autoscan": true,
38
+ "custom": {
39
+ "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
40
+ "^fc-form$": "form-create-wot/src/fc/components/FcForm.vue",
41
+ "^fc-form-item$": "form-create-wot/src/fc/components/FcFormItem.vue",
42
+ "^fc-select$": "form-create-wot/src/fc/adapter/fc-select.vue",
43
+ "^fc-radio$": "form-create-wot/src/fc/adapter/fc-radio.vue",
44
+ "^fc-checkbox$": "form-create-wot/src/fc/adapter/fc-checkbox.vue",
45
+ "^fc-date-picker$": "form-create-wot/src/fc/adapter/fc-date-picker.vue"
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## 使用
52
+
53
+ ### 基础用法
54
+
55
+ ```vue
56
+ <template>
57
+ <fc-form
58
+ :rule="rules"
59
+ :option="option"
60
+ v-model:api="fApi"
61
+ @submit="onSubmit"
62
+ />
63
+ </template>
64
+
65
+ <script setup lang="ts">
66
+ import { ref } from 'vue'
67
+ import type { FormApi, FormRule, FormOption } from 'form-create-wot'
68
+
69
+ const fApi = ref<FormApi | null>(null)
70
+
71
+ const rules = ref<FormRule[]>([
72
+ {
73
+ type: 'input',
74
+ field: 'name',
75
+ title: '姓名',
76
+ props: { placeholder: '请输入姓名' },
77
+ validate: [{ required: true, message: '请输入姓名' }]
78
+ },
79
+ {
80
+ type: 'select',
81
+ field: 'gender',
82
+ title: '性别',
83
+ options: [
84
+ { label: '男', value: 'male' },
85
+ { label: '女', value: 'female' }
86
+ ]
87
+ },
88
+ {
89
+ type: 'switch',
90
+ field: 'vip',
91
+ title: 'VIP',
92
+ value: false
93
+ }
94
+ ])
95
+
96
+ const option = ref<FormOption>({
97
+ submitBtn: { text: '提交', show: true },
98
+ resetBtn: { text: '重置', show: true }
99
+ })
100
+
101
+ const onSubmit = (data: Record<string, any>) => {
102
+ console.log('表单数据:', data)
103
+ }
104
+ </script>
105
+ ```
106
+
107
+ ### 从后端 API 加载 JSON
108
+
109
+ ```vue
110
+ <script setup lang="ts">
111
+ import { ref, onMounted } from 'vue'
112
+ import { parseJsonString } from 'form-create-wot'
113
+
114
+ const rules = ref([])
115
+
116
+ onMounted(async () => {
117
+ // 从芋道后端获取 form-create 的 JSON 配置
118
+ const [err, res] = await uni.request({ url: '/api/bpm/form/get?id=1' })
119
+ if (res?.data?.data?.conf) {
120
+ rules.value = parseJsonString(res.data.data.conf)
121
+ }
122
+ })
123
+ </script>
124
+ ```
125
+
126
+ ### fApi 操作
127
+
128
+ ```typescript
129
+ // 获取表单数据
130
+ const data = fApi.value.formData()
131
+
132
+ // 设置字段值
133
+ fApi.value.setValue('name', '张三')
134
+
135
+ // 批量设置
136
+ fApi.value.setValues({ name: '张三', gender: 'male' })
137
+
138
+ // 校验
139
+ await fApi.value.validate()
140
+
141
+ // 禁用/启用
142
+ fApi.value.disabled(true)
143
+ fApi.value.disabled(false, 'name')
144
+
145
+ // 显隐
146
+ fApi.value.hidden(true, 'gender')
147
+
148
+ // 重置
149
+ fApi.value.resetFields()
150
+ ```
151
+
152
+ ## 支持的组件类型
153
+
154
+ | type | 说明 | wot 组件 |
155
+ |---|---|---|
156
+ | `input` | 文本输入 | wd-input |
157
+ | `textarea` | 文本域 | wd-textarea |
158
+ | `InputNumber` | 数字输入 | wd-input-number |
159
+ | `select` | 下拉选择 | wd-select-picker |
160
+ | `radio` | 单选 | wd-radio-group |
161
+ | `checkbox` | 多选 | wd-checkbox-group |
162
+ | `switch` | 开关 | wd-switch |
163
+ | `datePicker` | 日期选择 | wd-datetime-picker |
164
+ | `timePicker` | 时间选择 | wd-datetime-picker |
165
+ | `rate` | 评分 | wd-rate |
166
+ | `slider` | 滑块 | wd-slider |
167
+
168
+ > 同时兼容 antdv 别名(如 `a-input`、`a-select` 等)
169
+
170
+ ## 平台兼容性
171
+
172
+ | 平台 | 状态 |
173
+ |---|---|
174
+ | H5 | ✅ |
175
+ | 微信小程序 | ✅ |
176
+ | 支付宝小程序 | ✅ |
177
+ | Android App | ✅ |
178
+ | iOS App | ✅ |
179
+
180
+ ## License
181
+
182
+ MIT
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "form-create-wot",
3
+ "version": "0.1.0",
4
+ "description": "form-create JSON 动态表单渲染引擎,基于 wot-design-uni 组件库,适用于 uni-app 移动端(H5/小程序/App)",
5
+ "main": "src/fc/index.ts",
6
+ "module": "src/fc/index.ts",
7
+ "types": "src/fc/types/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/fc/index.ts",
11
+ "require": "./src/fc/index.ts",
12
+ "types": "./src/fc/types/index.ts"
13
+ },
14
+ "./components/*": "./src/fc/components/*",
15
+ "./adapter/*": "./src/fc/adapter/*"
16
+ },
17
+ "files": [
18
+ "src/fc"
19
+ ],
20
+ "keywords": [
21
+ "form-create",
22
+ "wot-design-uni",
23
+ "uni-app",
24
+ "vue3",
25
+ "动态表单",
26
+ "表单生成器",
27
+ "json-form",
28
+ "mobile",
29
+ "低代码"
30
+ ],
31
+ "author": "",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": ""
36
+ },
37
+ "peerDependencies": {
38
+ "vue": ">=3.2.0",
39
+ "wot-design-uni": ">=1.0.0"
40
+ },
41
+ "scripts": {
42
+ "dev:custom": "uni -p",
43
+ "dev:h5": "uni",
44
+ "dev:h5:ssr": "uni --ssr",
45
+ "dev:mp-alipay": "uni -p mp-alipay",
46
+ "dev:mp-baidu": "uni -p mp-baidu",
47
+ "dev:mp-jd": "uni -p mp-jd",
48
+ "dev:mp-kuaishou": "uni -p mp-kuaishou",
49
+ "dev:mp-lark": "uni -p mp-lark",
50
+ "dev:mp-qq": "uni -p mp-qq",
51
+ "dev:mp-toutiao": "uni -p mp-toutiao",
52
+ "dev:mp-harmony": "uni -p mp-harmony",
53
+ "dev:mp-weixin": "uni -p mp-weixin",
54
+ "dev:mp-xhs": "uni -p mp-xhs",
55
+ "dev:quickapp-webview": "uni -p quickapp-webview",
56
+ "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
57
+ "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
58
+ "build:custom": "uni build -p",
59
+ "build:h5": "uni build",
60
+ "build:h5:ssr": "uni build --ssr",
61
+ "build:mp-alipay": "uni build -p mp-alipay",
62
+ "build:mp-baidu": "uni build -p mp-baidu",
63
+ "build:mp-jd": "uni build -p mp-jd",
64
+ "build:mp-kuaishou": "uni build -p mp-kuaishou",
65
+ "build:mp-lark": "uni build -p mp-lark",
66
+ "build:mp-qq": "uni build -p mp-qq",
67
+ "build:mp-toutiao": "uni build -p mp-toutiao",
68
+ "build:mp-harmony": "uni build -p mp-harmony",
69
+ "build:mp-weixin": "uni build -p mp-weixin",
70
+ "build:mp-xhs": "uni build -p mp-xhs",
71
+ "build:quickapp-webview": "uni build -p quickapp-webview",
72
+ "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
73
+ "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
74
+ "type-check": "vue-tsc --noEmit"
75
+ },
76
+ "dependencies": {
77
+ "@dcloudio/uni-app": "3.0.0-4080420251103001",
78
+ "@dcloudio/uni-app-harmony": "3.0.0-4080420251103001",
79
+ "@dcloudio/uni-app-plus": "3.0.0-4080420251103001",
80
+ "@dcloudio/uni-components": "3.0.0-4080420251103001",
81
+ "@dcloudio/uni-h5": "3.0.0-4080420251103001",
82
+ "@dcloudio/uni-mp-alipay": "3.0.0-4080420251103001",
83
+ "@dcloudio/uni-mp-baidu": "3.0.0-4080420251103001",
84
+ "@dcloudio/uni-mp-harmony": "3.0.0-4080420251103001",
85
+ "@dcloudio/uni-mp-jd": "3.0.0-4080420251103001",
86
+ "@dcloudio/uni-mp-kuaishou": "3.0.0-4080420251103001",
87
+ "@dcloudio/uni-mp-lark": "3.0.0-4080420251103001",
88
+ "@dcloudio/uni-mp-qq": "3.0.0-4080420251103001",
89
+ "@dcloudio/uni-mp-toutiao": "3.0.0-4080420251103001",
90
+ "@dcloudio/uni-mp-weixin": "3.0.0-4080420251103001",
91
+ "@dcloudio/uni-mp-xhs": "3.0.0-4080420251103001",
92
+ "@dcloudio/uni-quickapp-webview": "3.0.0-4080420251103001",
93
+ "sass": "^1.98.0",
94
+ "vue": "^3.4.21",
95
+ "vue-i18n": "^9.1.9",
96
+ "wot-design-uni": "^1.14.0"
97
+ },
98
+ "devDependencies": {
99
+ "@dcloudio/types": "^3.4.8",
100
+ "@dcloudio/uni-automator": "3.0.0-4080420251103001",
101
+ "@dcloudio/uni-cli-shared": "3.0.0-4080420251103001",
102
+ "@dcloudio/uni-stacktracey": "3.0.0-4080420251103001",
103
+ "@dcloudio/vite-plugin-uni": "3.0.0-4080420251103001",
104
+ "@vue/runtime-core": "^3.4.21",
105
+ "@vue/tsconfig": "^0.1.3",
106
+ "typescript": "^4.9.4",
107
+ "vite": "5.2.8",
108
+ "vue-tsc": "^1.0.24"
109
+ }
110
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * 组件映射配置
3
+ * Ant Design Vue 组件类型 → wot-design-uni 组件映射
4
+ */
5
+
6
+ export interface ComponentConfig {
7
+ /** 对应的 wot-design-uni 组件名 */
8
+ component: string
9
+ /** antdv props → wot props 转换 */
10
+ propsMap?: Record<string, string>
11
+ /** 默认 props */
12
+ defaultProps?: Record<string, any>
13
+ /** 值的属性名(默认 modelValue) */
14
+ modelProp?: string
15
+ /** 值更新的事件名(默认 update:modelValue) */
16
+ modelEvent?: string
17
+ }
18
+
19
+ /**
20
+ * 组件映射表
21
+ * key: form-create / antdv 的 type
22
+ * value: wot-design-uni 的配置
23
+ */
24
+ export const componentMap: Record<string, ComponentConfig> = {
25
+ // 文本输入
26
+ input: {
27
+ component: 'wd-input',
28
+ propsMap: {
29
+ allowClear: 'clearable',
30
+ addonBefore: 'prefixIcon',
31
+ addonAfter: 'suffixIcon',
32
+ },
33
+ defaultProps: {
34
+ clearable: true,
35
+ },
36
+ },
37
+ 'a-input': {
38
+ component: 'wd-input',
39
+ propsMap: {
40
+ allowClear: 'clearable',
41
+ },
42
+ defaultProps: {
43
+ clearable: true,
44
+ },
45
+ },
46
+
47
+ // 文本域
48
+ textarea: {
49
+ component: 'wd-textarea',
50
+ propsMap: {
51
+ autoSize: 'autoHeight',
52
+ showCount: 'showWordLimit',
53
+ },
54
+ defaultProps: {
55
+ clearable: true,
56
+ autoHeight: true,
57
+ },
58
+ },
59
+ 'a-textarea': {
60
+ component: 'wd-textarea',
61
+ propsMap: {
62
+ autoSize: 'autoHeight',
63
+ showCount: 'showWordLimit',
64
+ },
65
+ defaultProps: {
66
+ clearable: true,
67
+ autoHeight: true,
68
+ },
69
+ },
70
+
71
+ // 数字输入
72
+ InputNumber: {
73
+ component: 'wd-input-number',
74
+ propsMap: {},
75
+ defaultProps: {},
76
+ },
77
+ 'a-input-number': {
78
+ component: 'wd-input-number',
79
+ propsMap: {},
80
+ defaultProps: {},
81
+ },
82
+ inputNumber: {
83
+ component: 'wd-input-number',
84
+ propsMap: {},
85
+ defaultProps: {},
86
+ },
87
+
88
+ // 下拉选择
89
+ select: {
90
+ component: 'fc-select',
91
+ propsMap: {
92
+ mode: 'mode',
93
+ allowClear: 'clearable',
94
+ showSearch: 'filterable',
95
+ },
96
+ defaultProps: {},
97
+ },
98
+ 'a-select': {
99
+ component: 'fc-select',
100
+ propsMap: {
101
+ mode: 'mode',
102
+ allowClear: 'clearable',
103
+ },
104
+ defaultProps: {},
105
+ },
106
+
107
+ // 单选
108
+ radio: {
109
+ component: 'fc-radio',
110
+ propsMap: {},
111
+ defaultProps: {},
112
+ },
113
+ 'a-radio-group': {
114
+ component: 'fc-radio',
115
+ propsMap: {},
116
+ defaultProps: {},
117
+ },
118
+ radioGroup: {
119
+ component: 'fc-radio',
120
+ propsMap: {},
121
+ defaultProps: {},
122
+ },
123
+
124
+ // 多选
125
+ checkbox: {
126
+ component: 'fc-checkbox',
127
+ propsMap: {},
128
+ defaultProps: {},
129
+ },
130
+ 'a-checkbox-group': {
131
+ component: 'fc-checkbox',
132
+ propsMap: {},
133
+ defaultProps: {},
134
+ },
135
+ checkboxGroup: {
136
+ component: 'fc-checkbox',
137
+ propsMap: {},
138
+ defaultProps: {},
139
+ },
140
+
141
+ // 开关
142
+ switch: {
143
+ component: 'wd-switch',
144
+ propsMap: {
145
+ checked: 'modelValue',
146
+ checkedChildren: 'activeText',
147
+ unCheckedChildren: 'inactiveText',
148
+ },
149
+ defaultProps: {},
150
+ },
151
+ 'a-switch': {
152
+ component: 'wd-switch',
153
+ propsMap: {
154
+ checked: 'modelValue',
155
+ checkedChildren: 'activeText',
156
+ unCheckedChildren: 'inactiveText',
157
+ },
158
+ defaultProps: {},
159
+ },
160
+
161
+ // 日期选择
162
+ datePicker: {
163
+ component: 'fc-date-picker',
164
+ propsMap: {},
165
+ defaultProps: {},
166
+ },
167
+ 'a-date-picker': {
168
+ component: 'fc-date-picker',
169
+ propsMap: {},
170
+ defaultProps: {},
171
+ },
172
+
173
+ // 时间选择
174
+ timePicker: {
175
+ component: 'fc-date-picker',
176
+ propsMap: {},
177
+ defaultProps: { type: 'time' },
178
+ },
179
+ 'a-time-picker': {
180
+ component: 'fc-date-picker',
181
+ propsMap: {},
182
+ defaultProps: { type: 'time' },
183
+ },
184
+
185
+ // 评分
186
+ rate: {
187
+ component: 'wd-rate',
188
+ propsMap: {
189
+ count: 'num',
190
+ allowHalf: 'allowHalf',
191
+ },
192
+ defaultProps: {},
193
+ },
194
+ 'a-rate': {
195
+ component: 'wd-rate',
196
+ propsMap: {
197
+ count: 'num',
198
+ allowHalf: 'allowHalf',
199
+ },
200
+ defaultProps: {},
201
+ },
202
+
203
+ // 滑块
204
+ slider: {
205
+ component: 'wd-slider',
206
+ propsMap: {},
207
+ defaultProps: {},
208
+ },
209
+ 'a-slider': {
210
+ component: 'wd-slider',
211
+ propsMap: {},
212
+ defaultProps: {},
213
+ },
214
+ }
215
+
216
+ /**
217
+ * 获取组件配置,如找不到返回 null
218
+ */
219
+ export function getComponentConfig(type: string): ComponentConfig | null {
220
+ return componentMap[type] || null
221
+ }
222
+
223
+ /**
224
+ * 注册自定义组件映射
225
+ */
226
+ export function registerComponent(type: string, config: ComponentConfig) {
227
+ componentMap[type] = config
228
+ }
229
+
230
+ /**
231
+ * 转换 props:将 antdv 的 props 名称映射为 wot 的
232
+ */
233
+ export function convertProps(
234
+ originalProps: Record<string, any>,
235
+ config: ComponentConfig
236
+ ): Record<string, any> {
237
+ const result: Record<string, any> = { ...config.defaultProps }
238
+
239
+ for (const [key, val] of Object.entries(originalProps)) {
240
+ const mappedKey = config.propsMap?.[key] || key
241
+ result[mappedKey] = val
242
+ }
243
+
244
+ return result
245
+ }
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <wd-checkbox-group v-model="innerValue" :disabled="disabled" @change="onChange">
3
+ <wd-checkbox
4
+ v-for="opt in options"
5
+ :key="opt.value"
6
+ :value="opt.value"
7
+ :disabled="opt.disabled"
8
+ >
9
+ {{ opt.label }}
10
+ </wd-checkbox>
11
+ </wd-checkbox-group>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { ref, watch } from 'vue'
16
+ import type { OptionItem } from '../types'
17
+
18
+ const props = withDefaults(defineProps<{
19
+ modelValue?: any[]
20
+ options?: OptionItem[]
21
+ disabled?: boolean
22
+ }>(), {
23
+ modelValue: () => [],
24
+ options: () => [],
25
+ disabled: false,
26
+ })
27
+
28
+ const emit = defineEmits<{
29
+ (e: 'update:modelValue', value: any[]): void
30
+ (e: 'change', value: any[]): void
31
+ }>()
32
+
33
+ const innerValue = ref<any[]>(props.modelValue || [])
34
+
35
+ watch(() => props.modelValue, (val) => {
36
+ innerValue.value = val || []
37
+ })
38
+
39
+ const onChange = ({ value }: any) => {
40
+ innerValue.value = value
41
+ emit('update:modelValue', value)
42
+ emit('change', value)
43
+ }
44
+ </script>
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <wd-datetime-picker
3
+ v-model="innerValue"
4
+ :type="pickerType"
5
+ :label="label"
6
+ :disabled="disabled"
7
+ :placeholder="placeholder"
8
+ @confirm="onConfirm"
9
+ />
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { ref, watch, computed } from 'vue'
14
+
15
+ const props = withDefaults(defineProps<{
16
+ modelValue?: any
17
+ type?: string // 'date' | 'time' | 'year-month' | 'datetime'
18
+ disabled?: boolean
19
+ label?: string
20
+ placeholder?: string
21
+ format?: string
22
+ picker?: string // antdv 的 picker: 'date' | 'week' | 'month' | 'year'
23
+ }>(), {
24
+ modelValue: undefined,
25
+ type: 'date',
26
+ disabled: false,
27
+ label: '',
28
+ placeholder: '请选择',
29
+ })
30
+
31
+ const emit = defineEmits<{
32
+ (e: 'update:modelValue', value: any): void
33
+ (e: 'change', value: any): void
34
+ }>()
35
+
36
+ const innerValue = ref<any>(props.modelValue)
37
+
38
+ watch(() => props.modelValue, (val) => {
39
+ innerValue.value = val
40
+ })
41
+
42
+ /** 将 antdv picker 类型映射为 wd-datetime-picker type */
43
+ const pickerType = computed(() => {
44
+ if (props.type === 'time') return 'time'
45
+ if (props.picker === 'month' || props.type === 'month') return 'year-month'
46
+ if (props.type === 'datetime') return 'datetime'
47
+ return 'date'
48
+ })
49
+
50
+ const onConfirm = ({ value }: any) => {
51
+ innerValue.value = value
52
+ emit('update:modelValue', value)
53
+ emit('change', value)
54
+ }
55
+ </script>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <wd-radio-group v-model="innerValue" :disabled="disabled" @change="onChange">
3
+ <wd-radio
4
+ v-for="opt in options"
5
+ :key="opt.value"
6
+ :value="opt.value"
7
+ :disabled="opt.disabled"
8
+ >
9
+ {{ opt.label }}
10
+ </wd-radio>
11
+ </wd-radio-group>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { ref, watch } from 'vue'
16
+ import type { OptionItem } from '../types'
17
+
18
+ const props = withDefaults(defineProps<{
19
+ modelValue?: any
20
+ options?: OptionItem[]
21
+ disabled?: boolean
22
+ }>(), {
23
+ modelValue: undefined,
24
+ options: () => [],
25
+ disabled: false,
26
+ })
27
+
28
+ const emit = defineEmits<{
29
+ (e: 'update:modelValue', value: any): void
30
+ (e: 'change', value: any): void
31
+ }>()
32
+
33
+ const innerValue = ref<any>(props.modelValue)
34
+
35
+ watch(() => props.modelValue, (val) => {
36
+ innerValue.value = val
37
+ })
38
+
39
+ const onChange = ({ value }: any) => {
40
+ innerValue.value = value
41
+ emit('update:modelValue', value)
42
+ emit('change', value)
43
+ }
44
+ </script>