@neatui/vue 1.0.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.
Files changed (100) hide show
  1. package/README.md +139 -0
  2. package/package.json +26 -0
  3. package/src/components/basic/IDraggable.vue +84 -0
  4. package/src/components/basic/IDraggable@b.vue +80 -0
  5. package/src/components/basic/IFollowView.vue +172 -0
  6. package/src/components/basic/IFollowView@v2.vue +188 -0
  7. package/src/components/basic/IPickerView.vue +351 -0
  8. package/src/components/basic/IRouterView@v2.vue.backup +119 -0
  9. package/src/components/basic/IRouterView@v3.vue.backup +252 -0
  10. package/src/components/basic/IRouterView@v4.vue +362 -0
  11. package/src/components/basic/IScrollView.vue +127 -0
  12. package/src/components/basic/Icon.vue +54 -0
  13. package/src/components/basic/LayerView/Layer.vue +333 -0
  14. package/src/components/basic/LayerView/index.ts +5 -0
  15. package/src/components/basic/follow.ts +119 -0
  16. package/src/components/basic/index.ts +7 -0
  17. package/src/components/display/Avatar.vue +1 -0
  18. package/src/components/display/Badge.vue +1 -0
  19. package/src/components/display/Calendar.vue +245 -0
  20. package/src/components/display/CalendarReg.vue +245 -0
  21. package/src/components/display/Card.vue +1 -0
  22. package/src/components/display/Carousel.vue +1 -0
  23. package/src/components/display/Collapse.vue +1 -0
  24. package/src/components/display/Desriptions.vue +1 -0
  25. package/src/components/display/Empty.vue +1 -0
  26. package/src/components/display/Image.vue +112 -0
  27. package/src/components/display/List.vue +1 -0
  28. package/src/components/display/PhotoEditor.vue +181 -0
  29. package/src/components/display/PhotoViewer.vue +50 -0
  30. package/src/components/display/Popover.vue +1 -0
  31. package/src/components/display/QRCode.vue +1 -0
  32. package/src/components/display/Segmented.vue +1 -0
  33. package/src/components/display/Statistic.vue +1 -0
  34. package/src/components/display/Table.vue +1 -0
  35. package/src/components/display/Tabs.vue +1 -0
  36. package/src/components/display/Tag.vue +1 -0
  37. package/src/components/display/Timeline.vue +1 -0
  38. package/src/components/display/Tooltip.vue +1 -0
  39. package/src/components/display/Tour.vue +1 -0
  40. package/src/components/display/Tree.vue +431 -0
  41. package/src/components/display/TreeView.vue +222 -0
  42. package/src/components/display/index.ts +7 -0
  43. package/src/components/form/Cascader.vue +435 -0
  44. package/src/components/form/DatePicker.vue +124 -0
  45. package/src/components/form/DateRangePicker@v2.vue.backup +224 -0
  46. package/src/components/form/DateRangePicker@v3.vue +116 -0
  47. package/src/components/form/DateRangeView@v3.vue +386 -0
  48. package/src/components/form/DateView.vue +231 -0
  49. package/src/components/form/DateView@v2.vue +386 -0
  50. package/src/components/form/DateView@v3.vue +471 -0
  51. package/src/components/form/EditUpload.vue +4 -0
  52. package/src/components/form/ImgUpload.vue +174 -0
  53. package/src/components/form/Input.vue.backup +230 -0
  54. package/src/components/form/Input@v3.vue +267 -0
  55. package/src/components/form/InputNumber.vue +200 -0
  56. package/src/components/form/InputRange.vue +235 -0
  57. package/src/components/form/MoreSelect.vue.backup +144 -0
  58. package/src/components/form/MoreSelect@v3.vue +190 -0
  59. package/src/components/form/MoreSelectList.vue +120 -0
  60. package/src/components/form/MoreSelectPanel@v3.vue +190 -0
  61. package/src/components/form/MoreSelectPicker.vue +119 -0
  62. package/src/components/form/MoreSelectTags.vue +119 -0
  63. package/src/components/form/PageMoreSelect.vue +187 -0
  64. package/src/components/form/PageSelect.vue +189 -0
  65. package/src/components/form/SearchMoreSelect.vue +168 -0
  66. package/src/components/form/SearchSelect.vue.backup +194 -0
  67. package/src/components/form/SearchSelect@v3.vue +202 -0
  68. package/src/components/form/Select@v3.vue +201 -0
  69. package/src/components/form/SelectList.vue +58 -0
  70. package/src/components/form/SelectPicker.vue +97 -0
  71. package/src/components/form/SelectTags.vue +52 -0
  72. package/src/components/form/SelectTree/SelectTree@v1.vue +227 -0
  73. package/src/components/form/Switch.vue +135 -0
  74. package/src/components/form/TextArea.vue +171 -0
  75. package/src/components/form/TimePicker.vue +11 -0
  76. package/src/components/form/TimeView.vue +244 -0
  77. package/src/components/form/Upload.vue +349 -0
  78. package/src/components/form/index.ts +82 -0
  79. package/src/components/loader/FormLoader/FormLoader@v2.vue +422 -0
  80. package/src/components/loader/FormLoader/FormLoader@v3.vue.backup +318 -0
  81. package/src/components/loader/FormLoader/index.ts +2 -0
  82. package/src/components/loader/LimitLoader/LimitLoader.vue.backup +131 -0
  83. package/src/components/loader/LimitLoader/LimitLoader@v2.vue.backup +174 -0
  84. package/src/components/loader/LimitLoader/LimitLoader@v3.vue +183 -0
  85. package/src/components/loader/LimitLoader/index.ts +2 -0
  86. package/src/components/loader/TableLoader/TableColView.vue +115 -0
  87. package/src/components/loader/TableLoader/TableLoader.vue +356 -0
  88. package/src/components/loader/TableLoader/index.ts +2 -0
  89. package/src/components/loader/ViewLoader/ViewLoader@v1.vue +256 -0
  90. package/src/components/loader/ViewLoader/index.ts +2 -0
  91. package/src/components/loader/index.ts +5 -0
  92. package/src/components/tools/FormDraftsView.vue +308 -0
  93. package/src/components/tools/FormVerifyView.vue +206 -0
  94. package/src/components/tools/MoreTools.vue +74 -0
  95. package/src/components/tools/MoreTools@v2.vue +74 -0
  96. package/src/components/tools/Pagination@a.vue +222 -0
  97. package/src/components/tools/Pagination@b.vue +221 -0
  98. package/src/components/tools/index.ts +5 -0
  99. package/src/index.ts +5 -0
  100. package/tsconfig.json +24 -0
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # 基础 UI 组件
2
+
3
+ ```javascript
4
+ // 规则描述 Rule Description 简称 RD
5
+ const rd = {
6
+ // 筛选配置
7
+ limit: [
8
+ {
9
+ label: '',
10
+ field: '',
11
+ paths: '',
12
+ enums: [],
13
+ modle: '',
14
+ value: '',
15
+ rules: [],
16
+ logic: true | false,
17
+ _view: 1,
18
+ }
19
+ ],
20
+
21
+ // 表格配置
22
+ table: [
23
+ {
24
+ label: '', // 标签名
25
+ field: '', // 字段名
26
+ paths: '', // 源数据里的JSON结构层级 如{a:{aa:{aaaa:1},b:{bb:{bbb:2}}} 如果field是bbb则需要在paths里填写'b.bb'告诉驱动器这个字段在源数据中所处的位置
27
+ enums: [], // 枚举
28
+ modle: '', // 模型 链接Link|复制Copy|图片Image|标签Tag|... 有一个特殊的自定义代码'@',当驱动器不会自动处理这个字段需要手工在业务代码里处理
29
+ value: '', // 显示内容,一般不填写,只有需要计算显示值时才使用,比如给显示值设置特殊格式{ value: ()=> idate(val).format('YYYY-MM-DD') }
30
+ event: {}, // 事件
31
+ attrs: {}, // 属性
32
+ popup: {}, // 提示(无限套娃遍历)
33
+ logic: true, // 逻辑控制(开启关闭)
34
+ fixed: true|false|1|0|'l'|'r' // 是否固定
35
+ rules: [], // 校验规则
36
+ view: true|false|'always', // 是否显示(自动生成静态值交互可控)
37
+ chlid:[], // 子元素(无限套娃遍历)
38
+ clear: true|false // 是否清除字段,代替logic,logic另有他用
39
+ }
40
+ ],
41
+
42
+ // 表单配置
43
+ form: [
44
+ {
45
+ label: '', // 标签名
46
+ field: '', // 字段名
47
+ paths: '', // 源数据里的JSON结构层级 如{a:{aa:{aaaa:1},b:{bb:{bbb:2}}} 如果field是bbb则需要在paths里填写'b.bb'告诉驱动器这个字段在源数据中所处的位置
48
+ enums: [], // 枚举
49
+ modle: '', // 模型 简写'Select',详细写法 ['Select',{ attrName:'1' }]
50
+ value: '',
51
+ event: {}, // 事件
52
+ attrs: {}, // 属性
53
+ logic: true, // 逻辑控制(开启关闭)
54
+ rules: [
55
+ {
56
+ type: 'risk',
57
+ msg: '这是必填项',
58
+ required: true
59
+ },
60
+ {
61
+ type: 'risk',
62
+ msg: '请正确填写中文名',
63
+ validator: () => validateChineseName
64
+ }
65
+ ],
66
+ logic: true | false, // 是否显示
67
+ media: 'mob-24 pad-12 dpc-8', // 媒体查询
68
+
69
+ // 数组类型
70
+ array: true,
71
+ // 子块标题
72
+ title: (data: any, item: any) => {
73
+ return item.customerName + '的评价';
74
+ },
75
+ // 子块字段
76
+ child: []
77
+ }
78
+ ]
79
+ };
80
+ ```
81
+
82
+ ## 封装组件
83
+
84
+ ```
85
+ TableLoader ✓
86
+ LimitLoader ✓
87
+ FormView ✓
88
+ FormRowView ✓
89
+ FormColView ✓
90
+
91
+
92
+ Grid 0%
93
+ Button 0%
94
+ Input ✓
95
+ NumberInput 0%
96
+ Radio 0%
97
+ Checkbox 0%
98
+ Rate 0%
99
+ Slider 0%
100
+ Select ✓
101
+ MoreSelect ✓
102
+ SearchSelect ✓
103
+ SearchMoreSelect ✓
104
+ PageSelect ✓
105
+ TreeSelect ✓
106
+ CascaderSelect ✓
107
+ DatePicker 50% 急
108
+ TimePicker 50% 急
109
+ DateTimePicker 50% 急
110
+ Switch 50%
111
+ Textarea 50%
112
+ FileUpload 0%
113
+ ImageUpload ✓
114
+ Avatar 0%
115
+ Badge 0%
116
+ Calendar 0%
117
+ Card 0%
118
+ Carousel 0%
119
+ Collapse 0%
120
+ Empty 0%
121
+ Image ✓
122
+ InfiniteScroll 0%
123
+ Pagination ✓
124
+ Progress 0%
125
+ Result 0%
126
+ Tag 0%
127
+ Timeline 0%
128
+ Tree 0%
129
+ Tabs 0%
130
+ Divider 0%
131
+ Message 0%
132
+ ```
133
+
134
+ ## Version
135
+
136
+ ```
137
+ v1.0.0
138
+ 开源第一版本
139
+ ```
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@neatui/vue",
3
+ "version": "1.0.0",
4
+ "description": "basic component ui",
5
+ "main": "./src/index.ts",
6
+ "license": "MIT",
7
+ "author": "xiaojunbo",
8
+ "peerDependencies": {
9
+ "vue": "^3.2.0"
10
+ },
11
+ "devDependencies": {
12
+ "typescript": "^5.1.6"
13
+ },
14
+ "dependencies": {
15
+ "@fekit/floor": "^2.2.2",
16
+ "@fekit/follow": "^1.0.9",
17
+ "@fekit/itable": "^1.1.3",
18
+ "@fekit/pullload": "^1.3.4",
19
+ "@fekit/scale": "^2.0.1",
20
+ "@fekit/scrollto": "^3.0.2",
21
+ "@fekit/toast": "^2.2.3",
22
+ "@fekit/url": "^2.0.1",
23
+ "@fekit/utils": "^2.10.7",
24
+ "swiper": "^11.0.4"
25
+ }
26
+ }
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <ul>
3
+ <li
4
+ v-for="(item, idx) in list || []"
5
+ :key="idx"
6
+ class="pr drag-page-move n-sm"
7
+ draggable="true"
8
+ @dragstart.self="fMoveSta($event, list, idx)"
9
+ @dragend.self="fMoveEnd($event, list, idx)"
10
+ @dragenter.self="fMoveInt($event, list, idx)"
11
+ @dragover.prevent="fMoveIng($event)"
12
+ @dragleave="fMoveOut($event)"
13
+ >
14
+ <slot :item="item" :idx="idx">{{ item.label }}</slot>
15
+ </li>
16
+ </ul>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { reactive, PropType } from 'vue';
21
+ const props = defineProps({
22
+ list: {
23
+ type: Array as PropType<any[]>,
24
+ default: () => []
25
+ }
26
+ });
27
+ const state: any = reactive({});
28
+
29
+ // 按下
30
+ const fMoveSta = (e: any, arr: any, idx: any) => {
31
+ state.__page_moveing = 1;
32
+ state.__page_moveStaNode = { e, arr, idx };
33
+ };
34
+ // 移动
35
+ const fMoveIng = (e: any) => {
36
+ if (state.__page_moveing) {
37
+ const _h = e?.target?.getBoundingClientRect()?.height || 0;
38
+ const _v = _h / 2 > 0 ? _h / 2 : 0;
39
+ state.__page_moveEndNodeIsNext = e?.layerY > _v ? 1 : 0;
40
+ console.log(40, e, _v, e?.layerY > _v ? 'next' : 'prev');
41
+ e?.target?.setAttribute('data-ins', e?.layerY > _v ? 'next' : 'prev');
42
+ }
43
+ };
44
+ // 移入
45
+ const fMoveInt = (e: any, arr: any, idx: any, page: any = '') => {
46
+ if (state.__page_moveing) {
47
+ state.__page_moveEndNode = { e, arr, idx, page };
48
+ }
49
+ };
50
+ // 移出
51
+ const fMoveOut = (e: any) => {
52
+ if (state.__page_moveing) {
53
+ e?.target?.removeAttribute('data-ins');
54
+ }
55
+ };
56
+ // 松开
57
+ const fMoveEnd = (_e: any, arr: any, i2: any) => {
58
+ if (state.__page_moveing) {
59
+ if (state.__page_moveEndNode) {
60
+ const [item] = arr.splice(i2, 1);
61
+ let idx = state.__page_moveEndNode?.idx || 0;
62
+ if (state.__page_moveEndNodeIsNext && i2 >= idx) {
63
+ idx = idx + 1;
64
+ }
65
+ if (!state.__page_moveEndNodeIsNext && i2 < idx) {
66
+ idx = idx - 1;
67
+ }
68
+ state.__page_moveEndNode?.arr?.splice(idx, 0, item);
69
+ fMoveOut(state.__page_moveEndNode?.e);
70
+ }
71
+ state.__page_moveing = 0;
72
+ }
73
+ };
74
+ </script>
75
+ <style lang="scss">
76
+ .drag-page-move {
77
+ &[data-ins='prev'] {
78
+ box-shadow: inset 0 1px 0 0 #000;
79
+ }
80
+ &[data-ins='next'] {
81
+ box-shadow: inset 0 -1px 0 0 #000;
82
+ }
83
+ }
84
+ </style>
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <ul ref="aaa">
3
+ <li
4
+ v-for="(item, idx) in list || []"
5
+ :key="idx"
6
+ style="user-select: none"
7
+ class="drag-move-item b-solid b-line b-xs n-sm"
8
+ :data-move-ing="state.__moveing"
9
+ @mousedown="fMoveSta($event, list, idx)"
10
+ @mousemove="fMoveIng($event, list, idx)"
11
+ @mouseup="fMoveEnd($event, list, idx)"
12
+ @mouseleave="fMoveOut($event, list, idx)"
13
+ >
14
+ {{ item.label }}
15
+ </li>
16
+ </ul>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { reactive, ref, PropType } from 'vue';
21
+ const props = defineProps({
22
+ list: {
23
+ type: Array as PropType<any[]>,
24
+ default: () => []
25
+ }
26
+ });
27
+ const state: any = reactive({
28
+ __moveing: 0,
29
+ __x: 0,
30
+ __y: 0
31
+ });
32
+
33
+ const aaa: any = ref(null);
34
+
35
+ // class Drag {
36
+ // constructor(el: any) {}
37
+ // }
38
+
39
+ // 按下
40
+ const fMoveSta = (e: any, arr: any, idx: any) => {
41
+ console.log('sta', e, idx);
42
+ state.__moveing = 1;
43
+ state.__x = e.clientX;
44
+ state.__y = e.clientY;
45
+ state.__e = e.target;
46
+ // state.__e.style.pointerEvents = 'none';
47
+ };
48
+ // 移动
49
+ const fMoveIng = (e: any, arr: any, idx: any) => {
50
+ if (state.__moveing) {
51
+ // const deltaX = e.clientX - state.__x;
52
+ // const deltaY = e.clientY - state.__y;
53
+ // state.__e.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
54
+ if (e.target !== state.__e) {
55
+ const _h = e?.target?.getBoundingClientRect()?.height || 0;
56
+ const _v = _h / 2 > 0 ? _h / 2 : 0;
57
+ state.__moveEndNodeIsNext = e?.layerY > _v ? 1 : 0;
58
+ e?.target?.setAttribute('data-ins', e?.layerY > _v ? 'next' : 'prev');
59
+ }
60
+ }
61
+ };
62
+ const fMoveEnd = (e: any, arr: any, i2: any) => {
63
+ if (state.__moveing) {
64
+ e?.target?.removeAttribute('data-ins');
65
+ }
66
+ state.__moveing = 0;
67
+ };
68
+ const fMoveOut = (e: any, arr: any, idx: any) => {
69
+ e?.target?.removeAttribute('data-ins');
70
+ };
71
+ </script>
72
+ <style lang="scss">
73
+ .drag-move-item {
74
+ position: relative;
75
+ transition: all 250ms;
76
+ &[data-move-ing~='1'] {
77
+ transition: all 50ms;
78
+ }
79
+ }
80
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <div ref="dom" :class="btnClass" :style="btnStyle" @click="toggle" @mouseover="hovershow" @mouseout="hoverhide">
3
+ <slot>
4
+ <button ui-btn="@a s case :border"><i class="icon icon-dropdown"></i></button>
5
+ </slot>
6
+ </div>
7
+ <Teleport to="#ifollow" v-if="$slots.tips">
8
+ <div
9
+ ref="tip"
10
+ :class="tipClass"
11
+ :style="`position: fixed; pointer-events: none; ${tipStyle}`"
12
+ ui-tips="@a"
13
+ :ui-tips-view="state.show ? 1 : 0"
14
+ @mouseover="hovershow"
15
+ @mouseout="hoverhide"
16
+ >
17
+ <div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle">
18
+ <slot name="tips"></slot>
19
+ <div ui-tips-arrow=""></div>
20
+ </div>
21
+ </div>
22
+ </Teleport>
23
+ </template>
24
+ <script setup lang="ts">
25
+ import { reactive, ref, onMounted, onUnmounted, watch } from 'vue';
26
+ import Follow from '@fekit/follow';
27
+ // import Follow from './follow';
28
+
29
+ const emits = defineEmits(['onhide', 'onshow']);
30
+
31
+ const area: any = document.querySelector('#ifollow');
32
+ if (!area) {
33
+ const el = document.createElement('div');
34
+ el.id = 'ifollow';
35
+ document.body.appendChild(el);
36
+ }
37
+
38
+ const props = defineProps({
39
+ pos: { type: String, default: 'bl' },
40
+ event: { type: String, default: 'click' },
41
+ btnClass: { type: String, default: 'dib' },
42
+ btnStyle: { type: String, default: 'dib' },
43
+ tipClass: { type: String, default: '' },
44
+ tipStyle: { type: String, default: '' },
45
+ tipBoxClass: { type: String, default: '' },
46
+ tipBoxStyle: { type: String, default: '' }
47
+ });
48
+
49
+ const dom: any = ref(null);
50
+ const tip: any = ref(null);
51
+ const box: any = ref(null);
52
+ const state = reactive({
53
+ view: 0,
54
+ show: 0,
55
+ pos: props.pos,
56
+ hover: 0
57
+ });
58
+
59
+ const showTimer: any = ref(null);
60
+ const hideTimer: any = ref(null);
61
+ watch(
62
+ () => state.hover,
63
+ () => {
64
+ clearTimeout(showTimer.value);
65
+ clearTimeout(hideTimer.value);
66
+ if (state.hover) {
67
+ showTimer.value = setTimeout(() => {
68
+ state.show = 1;
69
+ }, 100);
70
+ } else {
71
+ hideTimer.value = setTimeout(() => {
72
+ state.show = 0;
73
+ }, 100);
74
+ }
75
+ }
76
+ );
77
+
78
+ // 开关
79
+ const toggle = () => {
80
+ if (props.event === 'click') {
81
+ state.show = state.show ? 0 : 1;
82
+ }
83
+ };
84
+
85
+ watch(
86
+ [() => state.show, () => state.hover],
87
+ () => {
88
+ if (state.show) {
89
+ emits('onshow');
90
+ } else {
91
+ emits('onhide');
92
+ }
93
+ },
94
+ { deep: true }
95
+ );
96
+
97
+ const hovershow = () => {
98
+ if (props.event === 'hover') {
99
+ state.hover = 1;
100
+ }
101
+ };
102
+
103
+ const hoverhide = () => {
104
+ if (props.event === 'hover') {
105
+ state.hover = 0;
106
+ }
107
+ };
108
+
109
+ const show = () => {
110
+ state.show = 1;
111
+ };
112
+
113
+ const hide = () => {
114
+ state.show = 0;
115
+ };
116
+
117
+ // 取消
118
+ const cancel = ({ target }: any = {}) => {
119
+ if (tip.value && !tip.value?.contains(target) && dom.value && !dom.value.contains(target)) {
120
+ state.show = 0;
121
+ }
122
+ };
123
+
124
+ onMounted(() => {
125
+ document.addEventListener('click', cancel, true);
126
+ });
127
+
128
+ const ex: any = ref(null);
129
+ watch([() => dom.value, () => tip.value], () => {
130
+ if (dom.value && tip.value) {
131
+ ex.value = new Follow(dom.value, tip.value, (status) => {
132
+ if (status === 'b') {
133
+ const _pos = props.pos.replace(/b/g, 't');
134
+ state.pos = _pos;
135
+ } else {
136
+ const _pos = props.pos.replace(/t/g, 'b');
137
+ state.pos = _pos;
138
+ }
139
+ });
140
+ } else {
141
+ ex.value = null;
142
+ }
143
+ });
144
+
145
+ onUnmounted(() => {
146
+ document.removeEventListener('click', cancel, true);
147
+ state.show = 0;
148
+ if (ex.value) {
149
+ ex.value.remove();
150
+ }
151
+ });
152
+
153
+ watch(
154
+ () => state.show,
155
+ (show: any) => {
156
+ if (ex.value) {
157
+ if (show) {
158
+ ex.value.listen();
159
+ } else {
160
+ ex.value.remove();
161
+ }
162
+ }
163
+ }
164
+ );
165
+
166
+ defineExpose({
167
+ toggle,
168
+ show,
169
+ hide,
170
+ cancel
171
+ });
172
+ </script>
@@ -0,0 +1,188 @@
1
+ <template>
2
+ <div ref="dom" :class="btnClass" :style="btnStyle" @click="toggle" @mouseover="hovershow" @mouseout="hoverhide">
3
+ <slot>
4
+ <button ui-btn="@a s case :border"><i class="icon icon-dropdown"></i></button>
5
+ </slot>
6
+ </div>
7
+ <Teleport to="#ifollow" v-if="$slots.tips">
8
+ <div
9
+ ref="tip"
10
+ :class="tipClass"
11
+ :style="`position: fixed; pointer-events: none; ${tipStyle}`"
12
+ ui-tips="@a"
13
+ :ui-tips-view="state.show ? 1 : 0"
14
+ @mouseover="hovershow"
15
+ @mouseout="hoverhide"
16
+ >
17
+ <div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle">
18
+ <slot name="tips"></slot>
19
+ <div ui-tips-arrow=""></div>
20
+ </div>
21
+ </div>
22
+ </Teleport>
23
+ </template>
24
+ <script setup lang="ts">
25
+ import { reactive, ref, onMounted, onUnmounted, watch } from 'vue';
26
+ import Follow from '@fekit/follow';
27
+
28
+ const emits = defineEmits(['update:modelValue', 'onhide', 'onshow']);
29
+
30
+ const area: any = document.querySelector('#ifollow');
31
+ if (!area) {
32
+ const el = document.createElement('div');
33
+ el.id = 'ifollow';
34
+ document.body.appendChild(el);
35
+ }
36
+
37
+ // 类型
38
+ interface Props {
39
+ modelValue?: string | number;
40
+ pos: string;
41
+ event: string;
42
+ btnClass: string;
43
+ btnStyle: string;
44
+ tipClass: string;
45
+ tipStyle: string;
46
+ tipBoxClass: string;
47
+ tipBoxStyle: string;
48
+ }
49
+
50
+ // 入参
51
+ const props: any = withDefaults(defineProps<Props>(), {
52
+ modelValue: 0,
53
+ pos: 'bl',
54
+ event: 'click',
55
+ btnClass: 'dib',
56
+ btnStyle: 'dib',
57
+ tipClass: '',
58
+ tipStyle: '',
59
+ tipBoxClass: '',
60
+ tipBoxStyle: ''
61
+ });
62
+
63
+ const dom: any = ref(null);
64
+ const tip: any = ref(null);
65
+ const box: any = ref(null);
66
+ const state = reactive({
67
+ view: 0,
68
+ show: 0,
69
+ pos: props.pos,
70
+ hover: 0
71
+ });
72
+
73
+ const showTimer: any = ref(null);
74
+ const hideTimer: any = ref(null);
75
+ watch(
76
+ () => state.hover,
77
+ () => {
78
+ clearTimeout(showTimer.value);
79
+ clearTimeout(hideTimer.value);
80
+ if (state.hover) {
81
+ showTimer.value = setTimeout(() => {
82
+ state.show = 1;
83
+ }, 100);
84
+ } else {
85
+ hideTimer.value = setTimeout(() => {
86
+ state.show = 0;
87
+ }, 100);
88
+ }
89
+ }
90
+ );
91
+
92
+ // 开关
93
+ const toggle = () => {
94
+ if (props.event === 'click') {
95
+ state.show = state.show ? 0 : 1;
96
+ }
97
+ };
98
+
99
+ watch(
100
+ [() => state.show, () => state.hover],
101
+ () => {
102
+ if (state.show) {
103
+ emits('onshow');
104
+ } else {
105
+ emits('onhide');
106
+ }
107
+ emits('update:modelValue', state.show);
108
+ },
109
+ { deep: true }
110
+ );
111
+
112
+ const hovershow = () => {
113
+ if (props.event === 'hover') {
114
+ state.hover = 1;
115
+ }
116
+ };
117
+
118
+ const hoverhide = () => {
119
+ if (props.event === 'hover') {
120
+ state.hover = 0;
121
+ }
122
+ };
123
+
124
+ const show = () => {
125
+ state.show = 1;
126
+ };
127
+
128
+ const hide = () => {
129
+ state.show = 0;
130
+ };
131
+
132
+ // 取消
133
+ const cancel = ({ target }: any = {}) => {
134
+ if (tip.value && !tip.value?.contains(target) && dom.value && !dom.value.contains(target)) {
135
+ state.show = 0;
136
+ }
137
+ };
138
+
139
+ onMounted(() => {
140
+ document.addEventListener('click', cancel, true);
141
+ });
142
+
143
+ const ex: any = ref(null);
144
+ watch([() => dom.value, () => tip.value], () => {
145
+ if (dom.value && tip.value) {
146
+ ex.value = new Follow(dom.value, tip.value, (status) => {
147
+ if (status === 'b') {
148
+ const _pos = props.pos.replace(/b/g, 't');
149
+ state.pos = _pos;
150
+ } else {
151
+ const _pos = props.pos.replace(/t/g, 'b');
152
+ state.pos = _pos;
153
+ }
154
+ });
155
+ } else {
156
+ ex.value = null;
157
+ }
158
+ });
159
+
160
+ onUnmounted(() => {
161
+ document.removeEventListener('click', cancel, true);
162
+ state.show = 0;
163
+
164
+ if (ex.value) {
165
+ ex.value.remove();
166
+ }
167
+ });
168
+
169
+ watch(
170
+ () => state.show,
171
+ (show: any) => {
172
+ if (ex.value) {
173
+ if (show) {
174
+ ex.value.listen();
175
+ } else {
176
+ ex.value.remove();
177
+ }
178
+ }
179
+ }
180
+ );
181
+
182
+ defineExpose({
183
+ toggle,
184
+ show,
185
+ hide,
186
+ cancel
187
+ });
188
+ </script>