@neatui/vue 2.17.2 → 2.17.3

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.
@@ -0,0 +1,422 @@
1
+ <template>
2
+ <div :ui-row="rows" class="n-sl-sub">
3
+ <template v-for="(col, idx) in state.form" :key="idx">
4
+ <div v-if="!col.clear" :id="col._fid" :ui-col="col.media" :ui-flex="`${vertical ? 'col xm' : 'row lt'}`">
5
+ <template v-if="col.label">
6
+ <h6 v-if="vertical" :style="`${col.rules?.some((validator: any) => validator.required) ? 'text-indent: -1em' : ''}`" class="o-ls">
7
+ <template v-if="col.label == '*'"></template>
8
+ <template v-else>
9
+ <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk pr" style="left: 0.1em">﹡</b>
10
+ <span>{{ col.label ? col.label + ':' : '' }}</span>
11
+ </template>
12
+ </h6>
13
+ <p v-else :class="`${clh({ ...(col.model[1] || {}) })}`" :style="`width:${state._lw + 'em'}; max-width:50%;`" class="ar nr-sm flex-fixed o-ls">
14
+ <template v-if="col.label == '*'"></template>
15
+ <template v-else>
16
+ <b v-if="col.rules?.some((validator: any) => validator.required)" class="co-risk">﹡</b>
17
+ <span>{{ col.label ? col.label + ':' : '' }}</span>
18
+ </template>
19
+ </p>
20
+ </template>
21
+ <Input v-if="col.model[0] === 'Input'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="w-full" v-bind="{ ...(col.model[1] || {}) }" />
22
+ <Select
23
+ v-else-if="col.model[0] === 'Select'"
24
+ v-model="col._data[col.field]"
25
+ :interacted="interacted"
26
+ :options="col.enums"
27
+ :rules="col?.rules"
28
+ class="w-full"
29
+ v-bind="{ ...(col.model[1] || {}) }"
30
+ ></Select>
31
+ <SelectList
32
+ v-else-if="col.model[0] === 'SelectList'"
33
+ v-model="col._data[col.field]"
34
+ :interacted="interacted"
35
+ :options="col.enums"
36
+ :rules="col?.rules"
37
+ class="w-full"
38
+ v-bind="{ ...(col.model[1] || {}) }"
39
+ />
40
+ <SelectTags
41
+ v-else-if="col.model[0] === 'SelectTags'"
42
+ v-model="col._data[col.field]"
43
+ :interacted="interacted"
44
+ :options="col.enums"
45
+ :rules="col?.rules"
46
+ class="w-full"
47
+ v-bind="{ ...(col.model[1] || {}) }"
48
+ />
49
+ <SelectPicker
50
+ v-else-if="col.model[0] === 'SelectPicker'"
51
+ v-model="col._data[col.field]"
52
+ :interacted="interacted"
53
+ :options="col.enums"
54
+ :rules="col?.rules"
55
+ class="w-full"
56
+ v-bind="{ ...(col.model[1] || {}) }"
57
+ />
58
+ <SearchSelect
59
+ v-else-if="col.model[0] === 'SearchSelect'"
60
+ v-model="col._data[col.field]"
61
+ :interacted="interacted"
62
+ :options="col.enums"
63
+ :rules="col.rules"
64
+ class="w-full"
65
+ v-bind="{ ...(col.model[1] || {}) }"
66
+ />
67
+ <MoreSelect
68
+ v-else-if="col.model[0] === 'MoreSelect'"
69
+ v-model="col._data[col.field]"
70
+ :interacted="interacted"
71
+ :options="col.enums"
72
+ :rules="col.rules"
73
+ class="w-full"
74
+ v-bind="{ ...(col.model[1] || {}) }"
75
+ />
76
+ <MoreSelectList
77
+ v-else-if="col.model[0] === 'MoreSelectList'"
78
+ v-model="col._data[col.field]"
79
+ :interacted="interacted"
80
+ :options="col.enums"
81
+ :rules="col.rules"
82
+ class="w-full"
83
+ v-bind="{ ...(col.model[1] || {}) }"
84
+ />
85
+ <MoreSelectTags
86
+ v-else-if="col.model[0] === 'MoreSelectTags'"
87
+ v-model="col._data[col.field]"
88
+ :interacted="interacted"
89
+ :options="col.enums"
90
+ :rules="col.rules"
91
+ class="w-full"
92
+ v-bind="{ ...(col.model[1] || {}) }"
93
+ />
94
+ <MoreSelectPicker
95
+ v-else-if="col.model[0] === 'MoreSelectPicker'"
96
+ v-model="col._data[col.field]"
97
+ :interacted="interacted"
98
+ :options="col.enums"
99
+ :rules="col.rules"
100
+ class="w-full"
101
+ v-bind="{ ...(col.model[1] || {}) }"
102
+ />
103
+ <SearchMoreSelect
104
+ v-else-if="col.model[0] === 'SearchMoreSelect'"
105
+ v-model="col._data[col.field]"
106
+ :interacted="interacted"
107
+ :options="col.enums"
108
+ :rules="col.rules"
109
+ class="w-full"
110
+ v-bind="{ ...(col.model[1] || {}) }"
111
+ />
112
+ <PageSelect
113
+ v-else-if="col.model[0] === 'PageSelect'"
114
+ v-model="col._data[col.field]"
115
+ :interacted="interacted"
116
+ :options="col.enums"
117
+ :rules="col.rules"
118
+ class="w-full"
119
+ v-bind="{ ...(col.model[1] || {}) }"
120
+ />
121
+ <SelectTree
122
+ v-else-if="col.model[0] === 'SelectTree'"
123
+ v-model="col._data[col.field]"
124
+ :interacted="interacted"
125
+ :options="col.enums"
126
+ :rules="col.rules"
127
+ class="w-full"
128
+ v-bind="{ ...(col.model[1] || {}) }"
129
+ />
130
+ <DatePicker
131
+ v-else-if="col.model[0] === 'DatePicker'"
132
+ v-model="col._data[col.field]"
133
+ :interacted="interacted"
134
+ :rules="col.rules"
135
+ class="w-full"
136
+ v-bind="{ ...(col.model[1] || {}) }"
137
+ />
138
+ <DateRangePicker
139
+ v-else-if="col.model[0] === 'DateRangePicker'"
140
+ v-model="col._data[col.field]"
141
+ :interacted="interacted"
142
+ :rules="col.rules"
143
+ class="w-full"
144
+ v-bind="{ ...(col.model[1] || {}) }"
145
+ />
146
+ <ImgUpload v-else-if="col.model[0] === 'ImgUpload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
147
+ <Upload v-else-if="col.model[0] === 'Upload'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
148
+ <Textarea
149
+ v-else-if="col.model[0] === 'Textarea'"
150
+ v-model="col._data[col.field]"
151
+ :interacted="interacted"
152
+ :rules="col.rules"
153
+ v-bind="{ ...(col.model[1] || {}) }"
154
+ ></Textarea>
155
+ <Switch v-else-if="col.model[0] === 'Switch'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
156
+ <Checkbox v-else-if="col.model[0] === 'Checkbox'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" v-bind="{ ...(col.model[1] || {}) }" />
157
+ <Tree v-else-if="col.model[0] === 'Tree'" v-model="col._data[col.field]" :interacted="interacted" :rules="col.rules" class="mt-sm" v-bind="{ ...(col.model[1] || {}) }" />
158
+ <Cascader
159
+ v-else-if="col.model[0] === 'Cascader'"
160
+ v-model="col._data[col.field]"
161
+ :interacted="interacted"
162
+ :options="col.enums"
163
+ :rules="col.rules"
164
+ class="w-full"
165
+ v-bind="{ ...(col.model[1] || {}) }"
166
+ />
167
+ <template v-else>
168
+ <slot v-if="col.slot" :name="col.slot" :col="col"></slot>
169
+ <slot v-else-if="col.model[0]" :name="col.model[0]" :col="col"></slot>
170
+ <slot v-else :name="col.field" :col="col"></slot>
171
+ </template>
172
+ </div>
173
+ </template>
174
+ <div v-if="$slots.submit" :ui-flex="`${vertical ? 'col xm' : 'row xt'}`">
175
+ <div v-if="vertical">
176
+ <slot name="submit"></slot>
177
+ </div>
178
+ <div v-else :style="`padding-left:${state._lw + 'em'};`">
179
+ <slot name="submit"></slot>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </template>
184
+ <script lang="ts" setup>
185
+ import { watch, reactive, onBeforeUnmount, nextTick } from 'vue';
186
+ import { useRoute, useRouter } from 'vue-router';
187
+ import { isString, isObject, isFunction, deepcopy, isArray } from '@fekit/utils';
188
+ import {
189
+ Input,
190
+ Switch,
191
+ Checkbox,
192
+ Textarea,
193
+ Select,
194
+ SelectList,
195
+ SelectTags,
196
+ SelectPicker,
197
+ SearchSelect,
198
+ PageSelect,
199
+ SelectTree,
200
+ MoreSelect,
201
+ MoreSelectList,
202
+ MoreSelectTags,
203
+ MoreSelectPicker,
204
+ DatePicker,
205
+ DateRangePicker,
206
+ SearchMoreSelect,
207
+ ImgUpload,
208
+ Upload,
209
+ Cascader,
210
+ } from '../../form';
211
+ import { Tree } from '../../display';
212
+
213
+ const route = useRoute();
214
+ const router = useRouter();
215
+
216
+ // 计算Label行高
217
+ const clh = ({ sz = '' }: any = {}) => {
218
+ const lhs: any = { xs: 'lh-sm', s: 'lh-mm', m: 'lh-ls', l: 'lh-ll', xl: 'lh-xl' };
219
+ return lhs[sz] || 'lh-ls';
220
+ };
221
+
222
+ // 计算Label宽度
223
+ function clw(label: any = '') {
224
+ let emWidth = 0;
225
+ if (isString(label)) {
226
+ for (let i = 0; i < label.length; i++) {
227
+ const char = label[i];
228
+ if (/^[\u4e00-\u9fa5]+$/.test(char)) {
229
+ emWidth += 1;
230
+ } else {
231
+ emWidth += 0.5;
232
+ }
233
+ }
234
+ }
235
+ return emWidth;
236
+ }
237
+
238
+ // 入参
239
+ const props: any = defineProps({
240
+ // 用户是否交互
241
+ interacted: {
242
+ type: Boolean,
243
+ default: false,
244
+ },
245
+ // JSON规则
246
+ form: {
247
+ type: Object,
248
+ default: () => ({}),
249
+ },
250
+ // 表单数据
251
+ data: {
252
+ type: Object as any,
253
+ default: () => ({}),
254
+ },
255
+ // 栅格系统
256
+ rows: {
257
+ type: String,
258
+ default: 'mob-24 pad-12 dpc-8',
259
+ },
260
+ // 如果表单是数组的话这里传入下标
261
+ idx: {
262
+ type: [String, Number],
263
+ default: '',
264
+ },
265
+ // 是否垂直排列
266
+ vertical: {
267
+ type: Boolean,
268
+ default: false,
269
+ },
270
+ });
271
+
272
+ // 数据
273
+ const state: any = reactive({
274
+ form: [],
275
+ _lw: 6,
276
+ });
277
+
278
+ // 监听结构
279
+ const unwatchs: any = [];
280
+ watch(
281
+ [() => props.form, () => props.data, () => props.idx],
282
+ ([form = []]) => {
283
+ const wkeys: any = {};
284
+ const onces: any = [];
285
+ state.form = deepcopy(form)?.map((item: any = {}, idx: number) => {
286
+ // 计算Label宽度
287
+ const _w = clw(item.label) + 2;
288
+ if (_w > state._lw) {
289
+ state._lw = _w;
290
+ }
291
+
292
+ if (isObject(item)) {
293
+ for (const key in item) {
294
+ let val = item[key] || {};
295
+ // 处理函数
296
+ if (isFunction(val)) {
297
+ val = val(deepcopy(props.data), props.form, route, router);
298
+ if (isObject(val)) {
299
+ if (!isFunction(val.setup)) {
300
+ val.setup = item[key];
301
+ }
302
+ if (!isArray(val.watch)) {
303
+ val.watch = deepcopy(form).map(({ field = '' }: any = {}) => field);
304
+ }
305
+ } else {
306
+ val = { value: val, setup: item[key], watch: deepcopy(form).map(({ field = '' }: any = {}) => field) };
307
+ }
308
+ }
309
+ // 对象处理
310
+ if (isObject(val)) {
311
+ const { value = null, setup = () => {}, watch: _watch = [] } = val || {};
312
+ // 监听字段为空
313
+ if (_watch.length) {
314
+ _watch?.forEach((k: any) => {
315
+ if (wkeys[k]) {
316
+ wkeys[k].push({ idx, key, value, setup });
317
+ } else {
318
+ wkeys[k] = [{ idx, key, value, setup }];
319
+ }
320
+ });
321
+ } else {
322
+ onces.push({ idx, key, value, setup });
323
+ }
324
+ item[key] = value;
325
+ } else {
326
+ item[key] = val;
327
+ }
328
+ }
329
+ }
330
+
331
+ // 模型处理
332
+ if (item.model) {
333
+ if (isString(item.model)) {
334
+ item.model = [item.model, { verify: false }];
335
+ }
336
+ } else {
337
+ item.model = ['', { verify: false }];
338
+ }
339
+ if (item.model[1] && !Object.prototype.hasOwnProperty.call(item.model[1], 'verify')) {
340
+ item.model[1].verify = false;
341
+ }
342
+
343
+ // 添加ID
344
+ item._fid = `form-${props.idx !== '' ? props.idx + '-' : ''}field-${item.field}`;
345
+
346
+ // 数据路径处理
347
+ item._data = props.data;
348
+ (item.paths?.split('.') || [])?.forEach((i: any) => {
349
+ if (item._data[i]) {
350
+ item._data = item._data[i];
351
+ }
352
+ });
353
+ return item;
354
+ });
355
+ const runsetup = (item: any = {}) => {
356
+ const { idx = null, key = null, value = null, setup = null }: any = item || {};
357
+ if (state.form[idx] && isFunction(setup)) {
358
+ const v = setup(deepcopy(props.data), state.form[idx], route, router);
359
+ nextTick(() => {
360
+ if (v instanceof Promise) {
361
+ state.form[idx][key] = value;
362
+ v.then((res: any) => {
363
+ state.form[idx][key] = res;
364
+ });
365
+ } else {
366
+ state.form[idx][key] = v;
367
+ }
368
+ });
369
+ }
370
+ };
371
+ // 执行一次
372
+ onces?.forEach((item: any = {}) => {
373
+ runsetup(item);
374
+ });
375
+ // 监听列表
376
+ for (const key in wkeys) {
377
+ unwatchs.push(
378
+ watch(
379
+ () => props.data[key],
380
+ () => {
381
+ wkeys[key]?.forEach((item: any = {}) => {
382
+ runsetup(item);
383
+ });
384
+ },
385
+ {
386
+ deep: true,
387
+ immediate: true,
388
+ },
389
+ ),
390
+ );
391
+ }
392
+ },
393
+ { deep: true, immediate: true },
394
+ );
395
+
396
+ // 处理
397
+ watch(
398
+ [() => state.form, () => props.data],
399
+ () => {
400
+ state.form.forEach((item: any = {}) => {
401
+ const { clear = false, field = '', paths = '' }: any = item;
402
+ if (clear) {
403
+ // 数据路径处理
404
+ let _data = props.data;
405
+ (paths?.split('.') || [])?.forEach((i: any) => {
406
+ if (_data[i]) {
407
+ _data = item._data[i];
408
+ }
409
+ });
410
+ _data[field] = isArray(_data[field]) ? [] : '';
411
+ }
412
+ });
413
+ },
414
+ { deep: true },
415
+ );
416
+
417
+ onBeforeUnmount(() => {
418
+ unwatchs?.forEach((unwatch: any) => {
419
+ unwatch();
420
+ });
421
+ });
422
+ </script>
@@ -1,224 +0,0 @@
1
- <template>
2
- <div :ui-form="`@a type:select sz:${sz}`" ui-tips="@a co:well" :ui-tips-view="state.show">
3
- <IFollowView>
4
- <Input v-bind="$props" :clearable="false" readonly v-model="state.value" @click="toggle">
5
- <template #suffix><i ui-form-select=""></i></template>
6
- </Input>
7
- <template #tips>
8
- <div ui-calendar="@a" class="w-ll">
9
- <div ui-calendar-head="">
10
- <div ui-flex="row lm" class="nx-ss-sub">
11
- <span class="fw-auto">{{ curr.y }}-{{ curr.m }}</span>
12
- </div>
13
- <div ui-flex="row rm">
14
- <button class="fs-xs mr-ss" ui-btn="@a main s">使用</button>
15
- </div>
16
- </div>
17
- <ul ui-calendar-week="">
18
- <li><span>日</span></li>
19
- <li><span>一</span></li>
20
- <li><span>二</span></li>
21
- <li><span>三</span></li>
22
- <li><span>四</span></li>
23
- <li><span>五</span></li>
24
- <li><span>六</span></li>
25
- </ul>
26
- <div>
27
- <ul ui-calendar-days="">
28
- <li
29
- v-for="(item, idx) in curr.days"
30
- :key="idx"
31
- :class="`${item.isToday ? 'today' : item.isLastMonth || item.isNextMonth ? 'o-sl' : item.isHoliday ? 'co-risk' : ''} ${
32
- state.selected && state.selected === item.date ? 'active' : ''
33
- }`"
34
- >
35
- <div v-if="item.day" @click="change(item)" class="pr">
36
- <span>{{ item.day }}</span>
37
- <sup style="font-size: 0.6em; margin-top: -0.3em; position: absolute; left: 100%; top: 0" v-if="item.holiday" class="lh-ss">休</sup>
38
- </div>
39
- </li>
40
- </ul>
41
- </div>
42
- <div ui-calendar-foot="">
43
- <div ui-flex="row lm">
44
- <div ui-calendar-prev-year="" @click="prev('y')"></div>
45
- <div ui-calendar-prev="" @click="prev('m')"></div>
46
- </div>
47
- <div ui-flex="row cm" class="nx-ss-sub">
48
- <ul class="fs-xs n-sm-sub" ui-flex="row lm">
49
- <li ui-btn="@a s none">年</li>
50
- <li ui-btn="@a s none">月</li>
51
- <li ui-btn="@a s none">日</li>
52
- <li ui-btn="@a s none">时</li>
53
- </ul>
54
- </div>
55
- <div ui-flex="row rm">
56
- <div ui-calendar-next="" @click="next('m')"></div>
57
- <div ui-calendar-next-year="" @click="next('y')"></div>
58
- </div>
59
- </div>
60
- </div>
61
- </template>
62
- </IFollowView>
63
- </div>
64
- </template>
65
- <script setup lang="ts">
66
- import { computed, reactive, toRefs } from 'vue';
67
- import { idate } from '@fekit/utils';
68
- import { Input } from '../form';
69
- import { IFollowView } from '../basic';
70
-
71
- // 创建日期
72
- const cDate = (date: any = null) => {
73
- const _date = new Date(date);
74
- return date && _date instanceof Date && !isNaN(_date.getTime()) ? _date : new Date();
75
- };
76
- // 个位补零
77
- const numfill = (num: string | number = '') => {
78
- return Number(num) < 10 ? `0${num}` : num;
79
- };
80
- // 外部入参
81
- const props = defineProps({
82
- theme: { type: String, default: '@a' },
83
- sz: { type: String, default: 'm' },
84
- co: { type: String, default: '' },
85
- block: { type: Boolean, default: true },
86
- // 提示文案
87
- tips: {
88
- type: String,
89
- default: ''
90
- },
91
- // 提示文案
92
- placeholder: {
93
- type: String,
94
- default: ''
95
- },
96
- // 格式
97
- format: {
98
- type: String,
99
- default: ''
100
- },
101
- // 值
102
- value: {
103
- type: [String, Number],
104
- default: ''
105
- },
106
- // 节假日
107
- holiday: {
108
- type: Object,
109
- default: () => ({})
110
- },
111
- // 校验规则
112
- rules: {
113
- type: Array,
114
- default: () => []
115
- },
116
- // 是否是示校验图标
117
- verify: {
118
- type: Boolean,
119
- default: false
120
- }
121
- });
122
-
123
- // 内部数据
124
- const state: any = reactive({
125
- view: 1,
126
- show: 0,
127
- pos: 'bl',
128
- selected: '',
129
- date: props.value,
130
- curr: computed(() => {
131
- const date = cDate(state.date);
132
- const calendar = [];
133
- // 年
134
- const y: any = date.getFullYear();
135
- // 月
136
- const m: any = date.getMonth() + 1;
137
- // 日
138
- const d: any = date.getDate();
139
- // 本月第一天星期几
140
- const w = new Date(`${y}-${m}-1`).getDay();
141
- // 当前月份一共几天
142
- const days = new Date(y, m, 0).getDate();
143
-
144
- // 当前
145
- const curr = new Date();
146
- const curr_y = curr.getFullYear();
147
- const curr_m = curr.getMonth() + 1;
148
- const curr_d = curr.getDate();
149
-
150
- // 上月
151
- const last = new Date(y, m - 1, 0);
152
- const last_y = last.getFullYear();
153
- const last_m = last.getMonth() + 1;
154
-
155
- // 下月
156
- const next = new Date(y, m + 1, 0);
157
- const next_y = next.getFullYear();
158
- const next_m = next.getMonth() + 1;
159
- let prevdays = last.getDate();
160
-
161
- for (let i = 0; i < w; i++) {
162
- calendar.unshift({
163
- date: `${last_y}-${numfill(last_m)}-${prevdays}`,
164
- day: prevdays,
165
- isLastMonth: 1
166
- });
167
- prevdays = prevdays - 1;
168
- }
169
- for (let i = 1; i <= days; i++) {
170
- const _i = numfill(i);
171
- calendar.push({
172
- date: `${y}-${numfill(m)}-${_i}`,
173
- day: _i,
174
- isToday: curr_y === y && curr_m === m && curr_d === i,
175
- isHoliday: props.holiday[`${y}-${numfill(m)}-${_i}`]?.name ? 1 : 0,
176
- holiday: props.holiday[`${y}-${numfill(m)}-${_i}`]?.name
177
- });
178
- }
179
- for (let i = 1; calendar.length < 42; i++) {
180
- const _i = numfill(i);
181
- calendar.push({
182
- date: `${next_y}-${numfill(next_m)}-${_i}`,
183
- day: _i,
184
- isNextMonth: 1
185
- });
186
- }
187
- return { days: calendar, y, m, d };
188
- }),
189
- value: computed(() => {
190
- return props.format ? idate(state.selected || props.value).format(props.format) : state.selected || props.value;
191
- })
192
- });
193
- const { curr = {} }: any = toRefs(state);
194
-
195
- const toggle = () => {
196
- state.show = state.show ? 0 : 1;
197
- };
198
- const upload = (a: any = 'm', b: any = 1) => {
199
- const date = cDate(state.date);
200
- if (a === 'm') {
201
- date.setMonth(date.getMonth() + b);
202
- } else {
203
- date.setFullYear(date.getFullYear() + b);
204
- }
205
- return date.toISOString().substring(0, 10);
206
- };
207
-
208
- // 日期前翻
209
- const prev = (type: any = 'm') => {
210
- state.date = upload(type, -1);
211
- };
212
- // 日期后翻
213
- const next = (type: any = 'm') => {
214
- state.date = upload(type, 1);
215
- };
216
-
217
- // 向外通信
218
- const emits = defineEmits(['change']);
219
- const change = (item: any) => {
220
- state.selected = item.date;
221
- state.date = item.date;
222
- emits('change', item.date);
223
- };
224
- </script>