@vyr/design 0.0.1

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.
Files changed (83) hide show
  1. package/package.json +16 -0
  2. package/src/components/Button.vue +64 -0
  3. package/src/components/Card.vue +203 -0
  4. package/src/components/Cascader.vue +171 -0
  5. package/src/components/Checked.vue +124 -0
  6. package/src/components/CheckedGroup.vue +51 -0
  7. package/src/components/Col.vue +52 -0
  8. package/src/components/ColorPicker.vue +331 -0
  9. package/src/components/Confirm.vue +86 -0
  10. package/src/components/Dialog.vue +220 -0
  11. package/src/components/Divider.vue +40 -0
  12. package/src/components/Draggable.vue +50 -0
  13. package/src/components/Dropdown.vue +175 -0
  14. package/src/components/DynamicDialog.vue +113 -0
  15. package/src/components/DynamicLayouter.vue +235 -0
  16. package/src/components/Form.vue +88 -0
  17. package/src/components/Input.vue +254 -0
  18. package/src/components/InputNumber.vue +96 -0
  19. package/src/components/Label.vue +116 -0
  20. package/src/components/Loading.vue +196 -0
  21. package/src/components/Mask.vue +47 -0
  22. package/src/components/Notify.vue +130 -0
  23. package/src/components/Option.vue +159 -0
  24. package/src/components/Options.vue +202 -0
  25. package/src/components/Popover.vue +271 -0
  26. package/src/components/Provider.vue +12 -0
  27. package/src/components/RightMenu.vue +127 -0
  28. package/src/components/Row.vue +50 -0
  29. package/src/components/Scroll.vue +99 -0
  30. package/src/components/Select.vue +223 -0
  31. package/src/components/Slot.vue +23 -0
  32. package/src/components/SubTree.vue +262 -0
  33. package/src/components/Tree.vue +129 -0
  34. package/src/components/common/DraggableController.ts +113 -0
  35. package/src/components/common/ResizeListener.ts +49 -0
  36. package/src/components/composables/useDefaultProps.ts +179 -0
  37. package/src/components/composables/useDraggable.ts +65 -0
  38. package/src/components/composables/useGetter.ts +15 -0
  39. package/src/components/composables/useMarginStyle.ts +45 -0
  40. package/src/components/composables/usePopover.ts +33 -0
  41. package/src/components/composables/useProvider.ts +186 -0
  42. package/src/components/composables/useScroll.ts +46 -0
  43. package/src/components/composables/useSearch.ts +97 -0
  44. package/src/components/composables/useTimer.ts +5 -0
  45. package/src/components/index.ts +1 -0
  46. package/src/components/singleton/confirm.ts +25 -0
  47. package/src/components/singleton/dialog.ts +25 -0
  48. package/src/components/singleton/index.ts +5 -0
  49. package/src/components/singleton/loading.ts +36 -0
  50. package/src/components/singleton/notify.ts +36 -0
  51. package/src/components/types/index.ts +82 -0
  52. package/src/components/utils/Cascader.ts +52 -0
  53. package/src/components/utils/Confirm.ts +41 -0
  54. package/src/components/utils/Dialog.ts +38 -0
  55. package/src/components/utils/DynamicDialog.ts +41 -0
  56. package/src/components/utils/DynamicLayouter.ts +5 -0
  57. package/src/components/utils/FloatLayer.ts +40 -0
  58. package/src/components/utils/InputNumber.ts +3 -0
  59. package/src/components/utils/Notify.ts +25 -0
  60. package/src/components/utils/Popover.ts +58 -0
  61. package/src/components/utils/RightMenu.ts +21 -0
  62. package/src/components/utils/Scroll.ts +4 -0
  63. package/src/components/utils/Slot.ts +73 -0
  64. package/src/components/utils/index.ts +12 -0
  65. package/src/font/demo.css +539 -0
  66. package/src/font/demo_index.html +1292 -0
  67. package/src/font/iconfont.css +207 -0
  68. package/src/font/iconfont.js +1 -0
  69. package/src/font/iconfont.json +345 -0
  70. package/src/font/iconfont.ttf +0 -0
  71. package/src/font/iconfont.woff +0 -0
  72. package/src/font/iconfont.woff2 +0 -0
  73. package/src/index.ts +68 -0
  74. package/src/locale/Language.ts +10 -0
  75. package/src/locale/LanguageProvider.ts +38 -0
  76. package/src/locale/index.ts +2 -0
  77. package/src/theme/global.less +91 -0
  78. package/src/theme/index.ts +155 -0
  79. package/src/tool/ArrayUtils.ts +38 -0
  80. package/src/tool/Color.ts +7 -0
  81. package/src/tool/Draggable.ts +36 -0
  82. package/src/tool/Listener.ts +59 -0
  83. package/src/tool/index.ts +4 -0
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <div class="vyr-drag" :style="`left:${state.x}px;top:${state.y}px;`" v-if="state.visible">
3
+ <vyr-option :label="state.label" :arrow="false"></vyr-option>
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { ref } from 'vue';
9
+ import { DraggableVue } from './types'
10
+ import VyrOption from './Option.vue'
11
+
12
+ const state = ref({
13
+ visible: false,
14
+ label: '',
15
+ x: 0,
16
+ y: 0,
17
+ })
18
+
19
+ const update = (x: 0, y: 0) => {
20
+ state.value.x = x
21
+ state.value.y = y
22
+ }
23
+ const show = (name: '') => {
24
+ document.body.classList.add('vyr-drag-mouse')
25
+ state.value.visible = true
26
+ state.value.label = name
27
+ }
28
+ const hide = () => {
29
+ document.body.classList.remove('vyr-drag-mouse')
30
+ state.value.visible = false
31
+ state.value.label = ''
32
+ }
33
+
34
+ defineExpose<DraggableVue>({ show, hide, update })
35
+ </script>
36
+
37
+ <style lang="less" scoped>
38
+ @import '../theme/global.less';
39
+
40
+ .vyr-drag {
41
+ .vyr-font-family;
42
+ width: 260px;
43
+ background-color: var(--vyr-blue-color);
44
+ z-index: 100000;
45
+ border: 1px solid var(--vyr-border-color);
46
+ position: absolute;
47
+ pointer-events: none;
48
+ cursor: move;
49
+ }
50
+ </style>
@@ -0,0 +1,175 @@
1
+ <template>
2
+ <div class="vyr-dropdown">
3
+ <vyr-popover ref="refPopover" :model-value="state.visible" :placement="placement"
4
+ :popover-class="`vyr-dropdown-popover ${popoverClass}`" :disabled="disabled" :popover-card="popoverCard"
5
+ :arrow="arrow" :offset="offset" @update:model-value="popoverVisibleChange" @trigger-change="changeTrigger">
6
+ <template #trigger>
7
+ <slot v-if="custom" name="default" :visible="state.visible"></slot>
8
+ <div v-else class="vyr-dropdown-trigger">
9
+ <div class="trigger-label">
10
+ <slot name="default" :visible="state.visible"></slot>
11
+ </div>
12
+ <div class="trigger-arrow" v-if="disabled === false">
13
+ <i class="vyrfont vyr-arrow-down-bold" :class="{ active: state.visible }"></i>
14
+ </div>
15
+ </div>
16
+ </template>
17
+ <slot name="popover" :width="slotWidth">
18
+ <vyr-options :data="data" :getter="curGetter" :checked="[modelValue]" :width="slotWidth"
19
+ :removable="removable" @click="change" @remove="oi => emit('remove', oi)">
20
+ <template #default="{ option, emit }">
21
+ <slot name="option" :option="option" :emit=emit :close=close></slot>
22
+ </template>
23
+ </vyr-options>
24
+ </slot>
25
+ </vyr-popover>
26
+ </div>
27
+ </template>
28
+
29
+ <script lang="ts" setup>
30
+ import { ref, useTemplateRef, } from 'vue'
31
+ import { Option, Options } from './types';
32
+ import { inputCommonDefaultProps } from './composables/useDefaultProps';
33
+ import { commonDefaultProps, selectCommonDefaultProps, popoverCommonDefaultProps, getterCommonDefaultProps } from './composables/useDefaultProps';
34
+ import { updateValidateItemProvider } from './composables/useProvider';
35
+ import { usePopover, observer } from './composables/usePopover';
36
+ import { useGetter } from './composables/useGetter';
37
+ import VyrPopover from './Popover.vue';
38
+ import VyrOptions from './Options.vue'
39
+
40
+ const props = defineProps({
41
+ ...commonDefaultProps,
42
+ ...selectCommonDefaultProps,
43
+ ...popoverCommonDefaultProps,
44
+ ...inputCommonDefaultProps,
45
+ ...getterCommonDefaultProps,
46
+ disabled: { default: false },
47
+ popoverCard: { default: false },
48
+ popoverWidth: { default: 'auto' },
49
+ custom: { default: false },
50
+ removable: { default: false },
51
+ data: { default(): Options { return [] } },
52
+ })
53
+
54
+ updateValidateItemProvider(ref({ validate() { } }))
55
+
56
+ const refPopover = useTemplateRef('refPopover')
57
+ const emit = defineEmits(['update:modelValue', 'change', 'remove', 'show', 'close'])
58
+ const { state, slotWidth, resize } = usePopover(props, refPopover)
59
+ const curGetter = useGetter(props)
60
+ const popoverState = {
61
+ bind: false,
62
+ cur: null as Element | null,
63
+ triggerResize: () => { },
64
+ }
65
+ const changeTrigger = (cur: Element) => {
66
+ if (popoverState.bind) {
67
+ unbindResizePopover()
68
+ popoverState.cur = cur
69
+ bindResizePopover()
70
+ } else {
71
+ popoverState.cur = cur
72
+ }
73
+ }
74
+ const bindResizePopover = () => {
75
+ popoverState.bind = true
76
+ if (popoverState.cur) popoverState.triggerResize = observer.listen(popoverState.cur, resize)
77
+ }
78
+ const unbindResizePopover = () => {
79
+ popoverState.bind = false
80
+ if (popoverState.cur) observer.unlisten(popoverState.cur)
81
+ popoverState.triggerResize = () => { }
82
+ }
83
+ const popoverVisibleChange = (visible: boolean) => {
84
+ state.value.visible = visible
85
+ if (visible) {
86
+ emit('show')
87
+ bindResizePopover()
88
+ } else {
89
+ unbindResizePopover()
90
+ emit('close')
91
+ }
92
+ }
93
+
94
+
95
+ const close = () => {
96
+ state.value.visible = false
97
+ }
98
+ const change = (item: Option, e: MouseEvent) => {
99
+ const value = curGetter.value(item)
100
+ if (props.modelValue !== value) {
101
+ emit('update:modelValue', value)
102
+ emit('change', value)
103
+ }
104
+ e.stopPropagation()
105
+ close()
106
+ }
107
+ </script>
108
+
109
+ <style lang="less" scoped>
110
+ @import '../theme/global.less';
111
+
112
+ @dropdown-arrow-size: 32px;
113
+
114
+ .vyr-dropdown {
115
+ .vyr-font-family;
116
+ width: 100%;
117
+ color: var(--vyr-font-color);
118
+ font-size: var(--vyr-font-size);
119
+
120
+ .vyr-dropdown-trigger {
121
+ cursor: pointer;
122
+ width: 100%;
123
+ padding: 0 4px;
124
+ box-sizing: border-box;
125
+ display: flex;
126
+ flex-wrap: nowrap;
127
+ flex-direction: row;
128
+
129
+ .trigger-label {
130
+ width: calc(~'100% - @{dropdown-arrow-size}');
131
+ }
132
+
133
+ .trigger-arrow {
134
+ text-align: right;
135
+ display: flex;
136
+ flex-wrap: nowrap;
137
+ flex-direction: row;
138
+ justify-content: flex-end;
139
+ align-items: center;
140
+ align-content: center;
141
+ width: @dropdown-arrow-size;
142
+
143
+ >.vyrfont {
144
+ font-size: 12px;
145
+ transition: all var(--vyr-animation-time);
146
+ }
147
+
148
+ >.vyrfont.active {
149
+ transform: rotate(180deg);
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ .vyr-dropdown-popover {
156
+
157
+ .vyr-options {
158
+ border-radius: 0;
159
+ }
160
+
161
+ .vyr-options:not(:first-child) {
162
+ border-left-color: transparent;
163
+ }
164
+
165
+ .vyr-options:first-child {
166
+ border-top-left-radius: var(--vyr-radius-size);
167
+ border-bottom-left-radius: var(--vyr-radius-size);
168
+ }
169
+
170
+ .vyr-options:last-child {
171
+ border-top-right-radius: var(--vyr-radius-size);
172
+ border-bottom-right-radius: var(--vyr-radius-size);
173
+ }
174
+ }
175
+ </style>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <vyr-dialog v-model="state.visible" :mask="false" :draggable="true" width="720px" maxHeight="70%" @close="close">
3
+ <template #title>
4
+ <template v-if="state.type === 'warning'">
5
+ <i class="vyrfont vyr-warning tips-type"></i>
6
+ </template>{{ state.title }}
7
+ </template>
8
+ <vyr-form>
9
+ <div class="dynamic-item" v-for="(option, i) in state.options" :key="i">
10
+ <div class="item-label">{{ option.label }}</div>
11
+ <template v-if="option.componentType === 'image'">
12
+ <img class="image" v-bind="option.componentProps">
13
+ </template>
14
+ <template v-if="option.componentType === 'input'">
15
+ <vyr-input v-model="privateState.data[option.name]" v-bind="option.componentProps"></vyr-input>
16
+ </template>
17
+ <template v-if="option.componentType === 'number'">
18
+ <vyr-input-number v-model="privateState.data[option.name]" v-bind="option.componentProps"
19
+ :data="currentData(option)"></vyr-input-number>
20
+ </template>
21
+ <template v-if="option.componentType === 'select'">
22
+ <vyr-select v-model="privateState.data[option.name]" v-bind="option.componentProps"
23
+ :data="currentData(option)" :searchable="true"></vyr-select>
24
+ </template>
25
+ <template v-if="option.componentType === 'cascader'">
26
+ <vyr-cascader v-model="privateState.data[option.name]" v-bind="option.componentProps"
27
+ :data="currentData(option)" :searchable="true"></vyr-cascader>
28
+ </template>
29
+ <template v-if="option.componentType === 'color'">
30
+ <vyr-color-picker v-model="privateState.data[option.name]"
31
+ v-bind="option.componentProps"></vyr-color-picker>
32
+ </template>
33
+ </div>
34
+ </vyr-form>
35
+ <template #footer>
36
+ <vyr-button @click="success">{{ language.get('dialog.button.submit') }}</vyr-button>
37
+ <vyr-button @click="cencel">{{ language.get('dialog.button.cancel') }}</vyr-button>
38
+ </template>
39
+ </vyr-dialog>
40
+ </template>
41
+
42
+ <script setup lang="ts">
43
+ import { ref } from 'vue';
44
+ import { DialogOptions, useDialogState } from './utils';
45
+ import { language } from '../locale'
46
+ import VyrDialog from './Dialog.vue';
47
+ import VyrButton from './Button.vue';
48
+ import VyrForm from './Form.vue';
49
+ import VyrInput from './Input.vue'
50
+ import VyrInputNumber from './InputNumber.vue'
51
+ import VyrSelect from './Select.vue'
52
+ import VyrCascader from './Cascader.vue'
53
+ import VyrColorPicker from './ColorPicker.vue'
54
+
55
+ defineOptions({
56
+ inheritAttrs: false
57
+ })
58
+
59
+ const state = useDialogState()
60
+ const privateState = ref({
61
+ data: state.value.value ?? {},
62
+ flag: false,
63
+ })
64
+
65
+ const currentData = (option: DialogOptions[number]) => {
66
+ if (option.componentData === undefined) return []
67
+ return Array.isArray(option.componentData) ? option.componentData : option.componentData(privateState.value.data)
68
+ }
69
+
70
+ const success = () => {
71
+ privateState.value.flag = true
72
+ state.value.visible = false
73
+ }
74
+ const cencel = () => {
75
+ privateState.value.flag = false
76
+ state.value.visible = false
77
+ }
78
+ const close = () => {
79
+ state.value.value = privateState.value.data ?? {}
80
+ state.value.close(privateState.value.flag)
81
+ }
82
+ </script>
83
+
84
+ <style lang="less" scoped>
85
+ .dynamic-item {
86
+ width: 100%;
87
+ margin-bottom: 16px;
88
+
89
+ .item-label {
90
+ color: #c9c9c9;
91
+ margin-bottom: 8px;
92
+ }
93
+
94
+ .image {
95
+ max-width: 100%;
96
+ }
97
+ }
98
+
99
+ .tips-type {
100
+ margin: 0 5px 0 0;
101
+ color: var(--vyr-warning-color);
102
+ }
103
+
104
+ .content {
105
+ margin: 10px 0 0;
106
+
107
+ .li {
108
+ display: inline-block;
109
+ min-height: 30px;
110
+ margin: 0 10px 0 0;
111
+ }
112
+ }
113
+ </style>
@@ -0,0 +1,235 @@
1
+ <template>
2
+ <div ref="refLayouter" class="vyr-dynamic-layouter" :class="`${mode} ${state.enable ? 'enable' : ''}`">
3
+ <div class="dynamic-wrapper" :style="mainStyle">
4
+ <slot></slot>
5
+ </div>
6
+ <div class="dynamic-sidebar" :style="sidebarStyle">
7
+ <div :class="`vyr-${mode}-bar ${state.enable ? 'enable' : ''}`" @mousedown.stop.prevent="start"></div>
8
+ <slot name="sidebar"></slot>
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script lang="ts" setup>
14
+ import { computed, ref, useTemplateRef, watch } from 'vue';
15
+ import { dynamicLayouterlisteners } from './utils';
16
+
17
+ const props = defineProps({
18
+ /**容器侧边栏的宽(高)度 */
19
+ modelValue: {
20
+ default: 0,
21
+ },
22
+ /**
23
+ * 容器的布局模式。默认为`horizontal`
24
+ *
25
+ * - vertical:垂直布局,将容器分为上下两部分
26
+ * - horizontal:水平布局,将容器分为左右两部分
27
+ */
28
+ mode: {
29
+ default: 'horizontal' as 'horizontal' | 'vertical'
30
+ },
31
+ /**
32
+ * 容器的侧边栏,会应用`size`的尺寸。默认为`true`
33
+ *
34
+ * - true:将容器的上部分(左部分)区域设为侧边栏
35
+ * - false:将容器的下部分(右部分)区域设为侧边栏
36
+ */
37
+ sidebar: {
38
+ default: true,
39
+ },
40
+ })
41
+
42
+ const emit = defineEmits(['update:modelValue'])
43
+ const refLayouter = useTemplateRef('refLayouter')
44
+
45
+ const state = ref({
46
+ needUpdate: false,
47
+ enable: false,
48
+ size: 0,
49
+ start: new MouseEvent('vyr'),
50
+ event: new MouseEvent('vyr'),
51
+ class: [] as string[],
52
+ })
53
+ watch(() => props.modelValue, () => state.value.size = props.modelValue, { immediate: true })
54
+
55
+ const mainStyle = computed(() => {
56
+
57
+ const attr = props.mode === 'horizontal' ? `height` : `width`
58
+
59
+ const style = props.sidebar ? `${attr}:${state.value.size}px;` : `${attr}:calc(100% - ${state.value.size}px);`
60
+
61
+ return style
62
+ })
63
+ const sidebarStyle = computed(() => {
64
+
65
+ const attr = props.mode === 'horizontal' ? `height` : `width`
66
+
67
+ const style = props.sidebar ? `${attr}:calc( 100% - ${state.value.size}px);` : `${attr}:${state.value.size}px;`
68
+
69
+ return style
70
+ })
71
+
72
+ const updateSize = () => {
73
+ state.value.needUpdate = false
74
+ state.value.size = Math.max(0, state.value.size + compute.size())
75
+ state.value.start = state.value.event
76
+ emit('update:modelValue', state.value.size)
77
+ }
78
+
79
+ const sizeVertical = () => {
80
+ const clientX = Math.max(compute.rect.left, Math.min(state.value.event.clientX, compute.rect.right))
81
+ const currentX = Math.max(compute.rect.left, Math.min(state.value.start.clientX, compute.rect.right))
82
+ const size = clientX - currentX
83
+
84
+ return props.sidebar ? size : -size
85
+ }
86
+ const sizeHorizontal = () => {
87
+ const clientY = Math.max(compute.rect.top, Math.min(state.value.event.clientY, compute.rect.bottom))
88
+ const currentY = Math.max(compute.rect.top, Math.min(state.value.start.clientY, compute.rect.bottom))
89
+ const size = clientY - currentY
90
+ return props.sidebar ? size : -size
91
+ }
92
+
93
+ const compute = {
94
+ rect: { left: 0, right: 0, top: 0, bottom: 0 },
95
+ size: sizeHorizontal,
96
+ }
97
+
98
+ const start = (event: MouseEvent) => {
99
+ if (!refLayouter.value) return
100
+ state.value.enable = true
101
+ state.value.start = event
102
+ compute.rect = refLayouter.value.getBoundingClientRect()
103
+ if (props.mode === 'vertical') {
104
+ compute.size = sizeVertical
105
+ } else {
106
+ compute.size = sizeHorizontal
107
+ }
108
+ unsetup()
109
+ setup()
110
+ }
111
+ const move = (event: MouseEvent) => {
112
+ event.preventDefault()
113
+ if (state.value.enable === false || state.value.needUpdate === true) return
114
+ state.value.needUpdate = true
115
+ state.value.event = event
116
+ window.dispatchEvent(new Event('resize'))
117
+ requestAnimationFrame(updateSize)
118
+ }
119
+ const end = () => {
120
+ unsetup()
121
+ state.value.enable = false
122
+ }
123
+
124
+ const setup = () => {
125
+ dynamicLayouterlisteners.length = 0
126
+ dynamicLayouterlisteners.push(move, end)
127
+ window.addEventListener('mousemove', move)
128
+ window.addEventListener('mouseup', end)
129
+ }
130
+ const unsetup = () => {
131
+ window.removeEventListener('mousemove', dynamicLayouterlisteners[0])
132
+ window.removeEventListener('mouseup', dynamicLayouterlisteners[1])
133
+ dynamicLayouterlisteners.length = 0
134
+ }
135
+ </script>
136
+
137
+ <style lang="less" scoped>
138
+ @import '../theme/global.less';
139
+
140
+ @sidebar-bar-size: 6px;
141
+
142
+ .vyr-dynamic-layouter {
143
+ .vyr-font-family;
144
+ width: 100%;
145
+ height: 100%;
146
+ display: flex;
147
+ flex-wrap: wrap;
148
+ flex-direction: row;
149
+
150
+ .dynamic-wrapper {
151
+ width: 100%;
152
+ height: 100%;
153
+ }
154
+
155
+ .dynamic-sidebar {
156
+ width: 100%;
157
+ height: 100%;
158
+ max-width: 100%;
159
+ max-height: 100%;
160
+ position: relative;
161
+ left: 0;
162
+ top: 0;
163
+ box-sizing: border-box;
164
+ border: 0 solid var(--vyr-border-color);
165
+
166
+ .vyr-vertical-bar {
167
+ width: @sidebar-bar-size;
168
+ height: 100%;
169
+ transform: translate(-100%, 0);
170
+ cursor: ew-resize;
171
+ }
172
+
173
+ .vyr-horizontal-bar {
174
+ width: 100%;
175
+ height: @sidebar-bar-size;
176
+ transform: translate(0, -100%);
177
+ cursor: ns-resize;
178
+ }
179
+
180
+ .vyr-vertical-bar,
181
+ .vyr-horizontal-bar {
182
+ background-color: var(--vyr-active-topic-color);
183
+ position: absolute;
184
+ left: 0;
185
+ top: 0;
186
+ opacity: 0;
187
+ transition: all var(--vyr-animation-time);
188
+ z-index: var(--vyr-z-index);
189
+ }
190
+ }
191
+ }
192
+
193
+ .vyr-dynamic-layouter.vertical {
194
+ display: flex;
195
+ flex-wrap: nowrap;
196
+ flex-direction: row;
197
+
198
+ .dynamic-sidebar {
199
+ border-left-width: 1px;
200
+ }
201
+ }
202
+
203
+ .vyr-dynamic-layouter.horizontal {
204
+
205
+ .dynamic-sidebar {
206
+ border-top-width: 1px;
207
+ }
208
+ }
209
+
210
+ .vyr-dynamic-layouter.enable {
211
+
212
+ .dynamic-wrapper,
213
+ .dynamic-sidebar {
214
+ pointer-events: none;
215
+ }
216
+
217
+ .dynamic-sidebar>.vyr-vertical-bar.enable {
218
+ opacity: 1;
219
+ pointer-events: all;
220
+ }
221
+
222
+ .dynamic-sidebar>.vyr-horizontal-bar.enable {
223
+ opacity: 1;
224
+ pointer-events: all;
225
+ }
226
+ }
227
+
228
+ .vyr-dynamic-layouter.vertical.enable {
229
+ cursor: ew-resize !important;
230
+ }
231
+
232
+ .vyr-dynamic-layouter.horizontal.enable {
233
+ cursor: ns-resize !important;
234
+ }
235
+ </style>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <div class="vyr-form">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { ref } from 'vue'
9
+ import Schema, { RuleItem, ValidateOption, Rules } from 'async-validator'
10
+ import { ValidateCollection } from './types'
11
+ import { updateValidateProvider } from './composables/useProvider'
12
+ import { ArrayUtils } from '../tool';
13
+
14
+ const props = defineProps({
15
+ data: { default() { return {} as { [k: string]: any } } },
16
+ rules: { default() { return [] as unknown as { [k: string]: RuleItem[] | RuleItem } } },
17
+ rulesOptions: { default() { return { tips: true, icon: true } } },
18
+ })
19
+
20
+ const addRule = (rule: string) => {
21
+ ArrayUtils.insert(validateProvider.value.rules, rule)
22
+ }
23
+ const removeRule = (rule: string) => {
24
+ ArrayUtils.remove(validateProvider.value.rules, rule)
25
+ }
26
+ const validate = async (rules: Rules, data = props.data, options?: ValidateOption) => {
27
+ let success = true
28
+ const schema = new Schema(rules)
29
+ await schema.validate(data, options).catch(({ fields }) => {
30
+ setErrorItems(fields)
31
+ success = false
32
+ })
33
+
34
+ return success
35
+ }
36
+ const validateItem = async (rule: string, value: any) => {
37
+ const rules = { [rule]: props.rules[rule] || {} }
38
+ const data = { [rule]: value }
39
+
40
+ const success = await validate(rules, data)
41
+ if (success === true) delete validateProvider.value.validateCollection[rule]
42
+
43
+ return success
44
+ }
45
+ const validateProvider = ref({
46
+ rules: [] as string[],
47
+ validateCollection: {} as ValidateCollection,
48
+ options: { ...{ tips: true, icon: true }, ...props.rulesOptions },
49
+ addRule,
50
+ removeRule,
51
+ validateItem
52
+ })
53
+ updateValidateProvider(validateProvider)
54
+
55
+ const setErrorItems = (fields: any) => {
56
+ for (const rule of Object.keys(fields)) {
57
+ validateProvider.value.validateCollection[rule] = fields[rule][0].message
58
+ }
59
+ }
60
+ const validator = async (options?: ValidateOption) => {
61
+ let ruleCount = 0
62
+ const ruleItems: Rules = {}
63
+ for (const rule of validateProvider.value.rules) {
64
+ const ruleItem = props.rules[rule] || {}
65
+ if (rule === undefined) continue
66
+ ruleItems[rule] = ruleItem
67
+ ruleCount++
68
+ }
69
+ if (ruleCount === 0) return true
70
+
71
+ const success = await validate(ruleItems, props.data, options)
72
+ if (success === true) validateProvider.value.validateCollection = {}
73
+
74
+ return success
75
+
76
+ }
77
+ const reset = () => validateProvider.value.validateCollection = {}
78
+ defineExpose({ validator, reset })
79
+ </script>
80
+
81
+ <style lang="less" scoped>
82
+ @import '../theme/global.less';
83
+
84
+ .vyr-form {
85
+ .vyr-font-family;
86
+ width: 100%;
87
+ }
88
+ </style>