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,6 +1,6 @@
1
1
  {
2
2
  "name": "form-create-wot",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "form-create JSON 动态表单渲染引擎,基于 wot-design-uni 组件库,适用于 uni-app 移动端(H5/小程序/App)",
5
5
  "main": "src/fc/index.ts",
6
6
  "module": "src/fc/index.ts",
@@ -1,7 +1,6 @@
1
1
  <template>
2
- <wd-datetime-picker
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, computed } from 'vue'
12
+ import { ref, watch } from 'vue'
14
13
 
15
14
  const props = withDefaults(defineProps<{
16
15
  modelValue?: any
17
- type?: string // 'date' | 'time' | 'year-month' | 'datetime'
16
+ type?: string
18
17
  disabled?: boolean
19
18
  label?: string
20
19
  placeholder?: string
21
- format?: string
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
- parsedRules.value = parseRules(rules)
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="{ 'fc-form-item--error': errorMsg }">
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>不支持的组件类型: {{ rule.type }}</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: #333;
253
+ color: #323233;
254
+ line-height: 1.4;
231
255
  }
232
256
 
233
257
  &__required {
234
- color: #f56c6c;
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: 8rpx;
269
+ margin-left: 12rpx;
241
270
  }
242
271
 
243
272
  &__info-text {
244
273
  font-size: 24rpx;
245
- color: #999;
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: #f56c6c;
288
+ color: #ee0a24;
289
+ line-height: 1.4;
259
290
  }
260
291
 
261
292
  &__unknown {
262
- padding: 16rpx;
263
- background: #fff3cd;
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>
@@ -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