form-create-wot 0.1.0 → 0.1.2
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/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<wd-
|
|
2
|
+
<wd-calendar
|
|
3
3
|
v-model="innerValue"
|
|
4
|
-
:type="pickerType"
|
|
5
4
|
:label="label"
|
|
6
5
|
:disabled="disabled"
|
|
7
6
|
:placeholder="placeholder"
|
|
@@ -10,22 +9,21 @@
|
|
|
10
9
|
</template>
|
|
11
10
|
|
|
12
11
|
<script setup lang="ts">
|
|
13
|
-
import { ref, watch
|
|
12
|
+
import { ref, watch } from 'vue'
|
|
14
13
|
|
|
15
14
|
const props = withDefaults(defineProps<{
|
|
16
15
|
modelValue?: any
|
|
17
|
-
type?: string
|
|
16
|
+
type?: string
|
|
18
17
|
disabled?: boolean
|
|
19
18
|
label?: string
|
|
20
19
|
placeholder?: string
|
|
21
|
-
|
|
22
|
-
picker?: string // antdv 的 picker: 'date' | 'week' | 'month' | 'year'
|
|
20
|
+
picker?: string
|
|
23
21
|
}>(), {
|
|
24
22
|
modelValue: undefined,
|
|
25
23
|
type: 'date',
|
|
26
24
|
disabled: false,
|
|
27
25
|
label: '',
|
|
28
|
-
placeholder: '
|
|
26
|
+
placeholder: '请选择日期',
|
|
29
27
|
})
|
|
30
28
|
|
|
31
29
|
const emit = defineEmits<{
|
|
@@ -39,14 +37,6 @@ watch(() => props.modelValue, (val) => {
|
|
|
39
37
|
innerValue.value = val
|
|
40
38
|
})
|
|
41
39
|
|
|
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
40
|
const onConfirm = ({ value }: any) => {
|
|
51
41
|
innerValue.value = value
|
|
52
42
|
emit('update:modelValue', value)
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
<script setup lang="ts">
|
|
41
41
|
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
|
|
42
42
|
import type { FormRule, FormOption, FormApi } from '../types'
|
|
43
|
-
import { parseRules, flattenFieldRules, normalizeRule } from '../core/parser'
|
|
43
|
+
import { parseRules, flattenFieldRules, normalizeRule, applyFieldPermissions } from '../core/parser'
|
|
44
44
|
import { validateValue } from '../core/validator'
|
|
45
45
|
import FcFormItem from './FcFormItem.vue'
|
|
46
46
|
|
|
@@ -51,10 +51,13 @@ const props = withDefaults(defineProps<{
|
|
|
51
51
|
option?: FormOption
|
|
52
52
|
/** 外部绑定的 api 对象 */
|
|
53
53
|
api?: FormApi | null
|
|
54
|
+
/** BPM 字段权限: { fieldName: '1'只读 | '2'可编辑 | '3'隐藏 } */
|
|
55
|
+
permissions?: Record<string, string>
|
|
54
56
|
}>(), {
|
|
55
57
|
rule: () => [],
|
|
56
58
|
option: () => ({}),
|
|
57
59
|
api: null,
|
|
60
|
+
permissions: () => ({}),
|
|
58
61
|
})
|
|
59
62
|
|
|
60
63
|
const emit = defineEmits<{
|
|
@@ -75,7 +78,12 @@ watch(() => props.rule, (newRules) => {
|
|
|
75
78
|
}, { immediate: true, deep: true })
|
|
76
79
|
|
|
77
80
|
function initForm(rules: FormRule[]) {
|
|
78
|
-
|
|
81
|
+
let parsed = parseRules(rules)
|
|
82
|
+
// 应用 BPM 字段权限
|
|
83
|
+
if (props.permissions && Object.keys(props.permissions).length > 0) {
|
|
84
|
+
parsed = applyFieldPermissions(parsed, props.permissions)
|
|
85
|
+
}
|
|
86
|
+
parsedRules.value = parsed
|
|
79
87
|
// 初始化 formData
|
|
80
88
|
const fields = flattenFieldRules(parsedRules.value)
|
|
81
89
|
for (const field of fields) {
|
|
@@ -395,14 +403,6 @@ onMounted(() => {
|
|
|
395
403
|
&__btn {
|
|
396
404
|
flex: 1;
|
|
397
405
|
max-width: 320rpx;
|
|
398
|
-
|
|
399
|
-
&--submit {
|
|
400
|
-
// 主色按钮
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
&--reset {
|
|
404
|
-
// 次要按钮
|
|
405
|
-
}
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
</style>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<view v-if="visible" class="fc-form-item" :class="
|
|
2
|
+
<view v-if="visible" class="fc-form-item" :class="itemClasses">
|
|
3
3
|
<!-- 标题/标签行 -->
|
|
4
4
|
<view class="fc-form-item__label" v-if="rule.title">
|
|
5
5
|
<text class="fc-form-item__required" v-if="isRequired">*</text>
|
|
6
|
-
<text>{{ rule.title }}</text>
|
|
6
|
+
<text class="fc-form-item__title">{{ rule.title }}</text>
|
|
7
7
|
<view class="fc-form-item__info" v-if="rule.info">
|
|
8
8
|
<text class="fc-form-item__info-text">{{ infoText }}</text>
|
|
9
9
|
</view>
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
v-model="fieldValue"
|
|
18
18
|
v-bind="componentProps"
|
|
19
19
|
:disabled="isDisabled"
|
|
20
|
+
:readonly="isReadonly"
|
|
21
|
+
:no-border="true"
|
|
20
22
|
@blur="onBlur"
|
|
21
23
|
@input="onInput"
|
|
22
24
|
/>
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
v-model="fieldValue"
|
|
28
30
|
v-bind="componentProps"
|
|
29
31
|
:disabled="isDisabled"
|
|
32
|
+
:readonly="isReadonly"
|
|
33
|
+
:no-border="true"
|
|
30
34
|
@blur="onBlur"
|
|
31
35
|
@input="onInput"
|
|
32
36
|
/>
|
|
@@ -108,7 +112,7 @@
|
|
|
108
112
|
|
|
109
113
|
<!-- 未知类型 fallback -->
|
|
110
114
|
<view v-else class="fc-form-item__unknown">
|
|
111
|
-
<text
|
|
115
|
+
<text class="fc-form-item__unknown-text">不支持的组件: {{ rule.type }}</text>
|
|
112
116
|
</view>
|
|
113
117
|
</view>
|
|
114
118
|
|
|
@@ -156,9 +160,14 @@ const isDisabled = computed(() => {
|
|
|
156
160
|
return props.globalDisabled || props.rule.props?.disabled || false
|
|
157
161
|
})
|
|
158
162
|
|
|
163
|
+
// 是否只读
|
|
164
|
+
const isReadonly = computed(() => {
|
|
165
|
+
return props.rule.props?.readonly || false
|
|
166
|
+
})
|
|
167
|
+
|
|
159
168
|
// 是否必填
|
|
160
169
|
const isRequired = computed(() => {
|
|
161
|
-
return props.rule.required || props.rule.validate?.some(v => v.required) || false
|
|
170
|
+
return props.rule.required || (props.rule as any).$required || props.rule.validate?.some(v => v.required) || false
|
|
162
171
|
})
|
|
163
172
|
|
|
164
173
|
// 提示信息
|
|
@@ -168,6 +177,14 @@ const infoText = computed(() => {
|
|
|
168
177
|
return props.rule.info.content || ''
|
|
169
178
|
})
|
|
170
179
|
|
|
180
|
+
// 样式 classes
|
|
181
|
+
const itemClasses = computed(() => ({
|
|
182
|
+
'fc-form-item--error': !!errorMsg.value,
|
|
183
|
+
'fc-form-item--required': isRequired.value,
|
|
184
|
+
'fc-form-item--disabled': isDisabled.value,
|
|
185
|
+
'fc-form-item--readonly': isReadonly.value,
|
|
186
|
+
}))
|
|
187
|
+
|
|
171
188
|
// 表单值
|
|
172
189
|
const fieldValue = ref<any>(props.modelValue)
|
|
173
190
|
|
|
@@ -221,28 +238,40 @@ defineExpose({
|
|
|
221
238
|
.fc-form-item {
|
|
222
239
|
padding: 24rpx 32rpx;
|
|
223
240
|
background: #fff;
|
|
241
|
+
border-bottom: 1rpx solid #f5f5f5;
|
|
242
|
+
transition: background-color 0.2s;
|
|
243
|
+
|
|
244
|
+
&:last-child {
|
|
245
|
+
border-bottom: none;
|
|
246
|
+
}
|
|
224
247
|
|
|
225
248
|
&__label {
|
|
226
249
|
display: flex;
|
|
227
250
|
align-items: center;
|
|
228
251
|
margin-bottom: 16rpx;
|
|
229
252
|
font-size: 28rpx;
|
|
230
|
-
color: #
|
|
253
|
+
color: #323233;
|
|
254
|
+
line-height: 1.4;
|
|
231
255
|
}
|
|
232
256
|
|
|
233
257
|
&__required {
|
|
234
|
-
color: #
|
|
258
|
+
color: #ee0a24;
|
|
235
259
|
margin-right: 4rpx;
|
|
236
260
|
font-size: 28rpx;
|
|
261
|
+
line-height: 1;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
&__title {
|
|
265
|
+
font-weight: 500;
|
|
237
266
|
}
|
|
238
267
|
|
|
239
268
|
&__info {
|
|
240
|
-
margin-left:
|
|
269
|
+
margin-left: 12rpx;
|
|
241
270
|
}
|
|
242
271
|
|
|
243
272
|
&__info-text {
|
|
244
273
|
font-size: 24rpx;
|
|
245
|
-
color: #
|
|
274
|
+
color: #969799;
|
|
246
275
|
}
|
|
247
276
|
|
|
248
277
|
&__body {
|
|
@@ -251,17 +280,39 @@ defineExpose({
|
|
|
251
280
|
|
|
252
281
|
&__error {
|
|
253
282
|
margin-top: 8rpx;
|
|
283
|
+
padding-left: 2rpx;
|
|
254
284
|
}
|
|
255
285
|
|
|
256
286
|
&__error-text {
|
|
257
287
|
font-size: 24rpx;
|
|
258
|
-
color: #
|
|
288
|
+
color: #ee0a24;
|
|
289
|
+
line-height: 1.4;
|
|
259
290
|
}
|
|
260
291
|
|
|
261
292
|
&__unknown {
|
|
262
|
-
padding:
|
|
263
|
-
background: #
|
|
293
|
+
padding: 20rpx;
|
|
294
|
+
background: #fffbe8;
|
|
264
295
|
border-radius: 8rpx;
|
|
296
|
+
border: 1rpx solid #fff1a8;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
&__unknown-text {
|
|
300
|
+
font-size: 24rpx;
|
|
301
|
+
color: #ed6a0c;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
&--disabled {
|
|
305
|
+
opacity: 0.7;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
&--readonly {
|
|
309
|
+
background: #fafafa;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
&--error {
|
|
313
|
+
.fc-form-item__label {
|
|
314
|
+
color: #ee0a24;
|
|
315
|
+
}
|
|
265
316
|
}
|
|
266
317
|
}
|
|
267
318
|
</style>
|
package/src/fc/core/parser.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON Rule 解析器
|
|
3
3
|
* 将 form-create 的 JSON Rule 解析并标准化为内部格式
|
|
4
|
+
* 兼容芋道 yudao BPM 后端的 formFields 格式
|
|
4
5
|
*/
|
|
5
6
|
import type { FormRule, OptionItem } from '../types'
|
|
6
7
|
|
|
@@ -24,6 +25,23 @@ export function normalizeRule(rule: FormRule): FormRule {
|
|
|
24
25
|
validate: rule.validate ? [...rule.validate] : []
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
// ====== 芋道 yudao 兼容处理 ======
|
|
29
|
+
|
|
30
|
+
// 1. $required → required(芋道 FcDesigner 用 $required)
|
|
31
|
+
if ((rule as any).$required && !normalized.required) {
|
|
32
|
+
normalized.required = true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. _fc_drag_tag 类型修正:
|
|
36
|
+
// type="input" + _fc_drag_tag="textarea" 或 props.type="textarea" → 实际是 textarea
|
|
37
|
+
const dragTag = (rule as any)._fc_drag_tag
|
|
38
|
+
if (normalized.type === 'input' && (dragTag === 'textarea' || normalized.props?.type === 'textarea')) {
|
|
39
|
+
normalized.type = 'textarea'
|
|
40
|
+
delete normalized.props?.type // 移除 props.type,避免传给 wd-textarea
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ====== 通用处理 ======
|
|
44
|
+
|
|
27
45
|
// 处理 required 快捷设置 → 自动插入 validate 规则
|
|
28
46
|
if (normalized.required && !normalized.validate?.some(v => v.required)) {
|
|
29
47
|
normalized.validate = [
|
|
@@ -93,9 +111,24 @@ export function parseRules(rules: FormRule[]): FormRule[] {
|
|
|
93
111
|
|
|
94
112
|
/**
|
|
95
113
|
* 从芋道后端返回的 JSON 字符串解析
|
|
114
|
+
* 支持两种格式:
|
|
115
|
+
* 1. 单个 JSON 字符串 (整个数组)
|
|
116
|
+
* 2. JSON 字符串数组 (每个元素是单条规则的 JSON 字符串) — 芋道 formFields 格式
|
|
96
117
|
*/
|
|
97
|
-
export function parseJsonString(json: string): FormRule[] {
|
|
118
|
+
export function parseJsonString(json: string | string[]): FormRule[] {
|
|
98
119
|
try {
|
|
120
|
+
// 格式2: 字符串数组(芋道 processDefinition.formFields)
|
|
121
|
+
if (Array.isArray(json)) {
|
|
122
|
+
const rules = json.map(item => {
|
|
123
|
+
if (typeof item === 'string') {
|
|
124
|
+
return JSON.parse(item) as FormRule
|
|
125
|
+
}
|
|
126
|
+
return item as FormRule
|
|
127
|
+
})
|
|
128
|
+
return parseRules(rules)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 格式1: 单个 JSON 字符串
|
|
99
132
|
const parsed = JSON.parse(json)
|
|
100
133
|
if (Array.isArray(parsed)) {
|
|
101
134
|
return parseRules(parsed)
|
|
@@ -107,6 +140,43 @@ export function parseJsonString(json: string): FormRule[] {
|
|
|
107
140
|
}
|
|
108
141
|
}
|
|
109
142
|
|
|
143
|
+
/**
|
|
144
|
+
* 解析芋道 formConf 配置
|
|
145
|
+
*/
|
|
146
|
+
export function parseFormConf(confJson: string): Record<string, any> {
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(confJson)
|
|
149
|
+
} catch {
|
|
150
|
+
return {}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 应用字段权限到规则
|
|
156
|
+
* 芋道 BPM 字段权限: "1" = 只读, "2" = 可编辑, "3" = 隐藏
|
|
157
|
+
*/
|
|
158
|
+
export function applyFieldPermissions(
|
|
159
|
+
rules: FormRule[],
|
|
160
|
+
permissions: Record<string, string>
|
|
161
|
+
): FormRule[] {
|
|
162
|
+
return rules.map(rule => {
|
|
163
|
+
if (!rule.field || !permissions[rule.field]) return rule
|
|
164
|
+
const perm = permissions[rule.field]
|
|
165
|
+
const updated = { ...rule, props: { ...rule.props } }
|
|
166
|
+
if (perm === '1') {
|
|
167
|
+
// 只读
|
|
168
|
+
updated.props!.disabled = true
|
|
169
|
+
updated.props!.readonly = true
|
|
170
|
+
} else if (perm === '3') {
|
|
171
|
+
// 隐藏
|
|
172
|
+
updated.hidden = true
|
|
173
|
+
updated.display = false
|
|
174
|
+
}
|
|
175
|
+
// perm === '2' 可编辑 (默认,不做处理)
|
|
176
|
+
return updated
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
110
180
|
/**
|
|
111
181
|
* 获取所有包含 field 的规则(扁平化)
|
|
112
182
|
*/
|
package/src/fc/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import FcCheckbox from './adapter/fc-checkbox.vue'
|
|
|
11
11
|
import FcDatePicker from './adapter/fc-date-picker.vue'
|
|
12
12
|
|
|
13
13
|
// Core
|
|
14
|
-
export { parseRules, parseJsonString, flattenFieldRules, normalizeRule } from './core/parser'
|
|
14
|
+
export { parseRules, parseJsonString, parseFormConf, flattenFieldRules, normalizeRule, applyFieldPermissions } from './core/parser'
|
|
15
15
|
export { validateValue, validateFields, registerValidator } from './core/validator'
|
|
16
16
|
export { getComponentConfig, registerComponent, convertProps } from './adapter/config'
|
|
17
17
|
|