oxy-uni-ui 1.1.0 → 1.2.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.
- package/attributes.json +1 -1
- package/components/common/abstracts/variable.scss +59 -1
- package/components/common/path.ts +9 -0
- package/components/common/util.ts +42 -0
- package/components/composables/index.ts +1 -0
- package/components/composables/useGlobalLoading.ts +42 -0
- package/components/composables/useGlobalMessage.ts +48 -0
- package/components/composables/useGlobalToast.ts +84 -0
- package/components/composables/useVirtualScroll.ts +173 -0
- package/components/oxy-cell/oxy-cell.vue +15 -2
- package/components/oxy-cell/types.ts +4 -0
- package/components/oxy-checkbox/index.scss +1 -1
- package/components/oxy-checkbox/oxy-checkbox.vue +2 -2
- package/components/oxy-col-picker/oxy-col-picker.vue +3 -0
- package/components/oxy-col-picker/types.ts +5 -1
- package/components/oxy-corner/index.scss +121 -1
- package/components/oxy-corner/oxy-corner.vue +18 -5
- package/components/oxy-corner/types.ts +24 -3
- package/components/oxy-date-strip/index.scss +10 -0
- package/components/oxy-date-strip/oxy-date-strip.vue +198 -0
- package/components/oxy-date-strip/types.ts +98 -0
- package/components/oxy-date-strip/utils.ts +67 -0
- package/components/oxy-date-strip-item/index.scss +94 -0
- package/components/oxy-date-strip-item/oxy-date-strip-item.vue +102 -0
- package/components/oxy-date-strip-item/types.ts +53 -0
- package/components/oxy-datetime-picker/oxy-datetime-picker.vue +3 -1
- package/components/oxy-datetime-picker/types.ts +5 -1
- package/components/oxy-echarts/index.scss +17 -0
- package/components/oxy-echarts/index.ts +1 -0
- package/components/oxy-echarts/oxy-echarts.vue +32 -0
- package/components/oxy-echarts/types.ts +12 -0
- package/components/oxy-file-list/index.scss +26 -0
- package/components/oxy-file-list/oxy-file-list.vue +208 -34
- package/components/oxy-file-list/types.ts +58 -2
- package/components/oxy-global-loading/oxy-global-loading.vue +53 -0
- package/components/oxy-global-message/oxy-global-message.vue +64 -0
- package/components/oxy-global-toast/oxy-global-toast.vue +53 -0
- package/components/oxy-img-lazy/index.scss +17 -0
- package/components/oxy-img-lazy/oxy-img-lazy.vue +332 -0
- package/components/oxy-img-lazy/types.ts +69 -0
- package/components/oxy-link/index.scss +57 -0
- package/components/oxy-link/oxy-link.vue +130 -0
- package/components/oxy-link/types.ts +81 -0
- package/components/oxy-list/index.scss +8 -1
- package/components/oxy-list/oxy-list.vue +121 -40
- package/components/oxy-list/types.ts +3 -15
- package/components/oxy-picker/oxy-picker.vue +3 -0
- package/components/oxy-picker/types.ts +5 -1
- package/components/oxy-radio/index.scss +3 -3
- package/components/oxy-radio/oxy-radio.vue +1 -1
- package/components/oxy-rich-text/icon/emjio.svg +1 -0
- package/components/oxy-rich-text/icon/quote.svg +1 -0
- package/components/oxy-rich-text/icon/text.svg +1 -0
- package/components/oxy-rich-text/icon/title.svg +1 -0
- package/components/oxy-rich-text/index.scss +159 -0
- package/components/oxy-rich-text/mp-html/card/card.vue +122 -0
- package/components/oxy-rich-text/mp-html/card/index.js +7 -0
- package/components/oxy-rich-text/mp-html/editable/config.js +15 -0
- package/components/oxy-rich-text/mp-html/editable/index.js +553 -0
- package/components/oxy-rich-text/mp-html/emoji/index.js +203 -0
- package/components/oxy-rich-text/mp-html/highlight/config.js +5 -0
- package/components/oxy-rich-text/mp-html/highlight/index.js +96 -0
- package/components/oxy-rich-text/mp-html/highlight/prism.css +1 -0
- package/components/oxy-rich-text/mp-html/highlight/prism.min.js +7 -0
- package/components/oxy-rich-text/mp-html/img-cache/index.js +138 -0
- package/components/oxy-rich-text/mp-html/latex/index.js +80 -0
- package/components/oxy-rich-text/mp-html/latex/katex.css +1 -0
- package/components/oxy-rich-text/mp-html/latex/katex.min.js +1 -0
- package/components/oxy-rich-text/mp-html/markdown/index.js +50 -0
- package/components/oxy-rich-text/mp-html/markdown/marked.min.js +71 -0
- package/components/oxy-rich-text/mp-html/mp-html.d.ts +184 -0
- package/components/oxy-rich-text/mp-html/mp-html.vue +675 -0
- package/components/oxy-rich-text/mp-html/node/node.vue +1161 -0
- package/components/oxy-rich-text/mp-html/parser.js +1428 -0
- package/components/oxy-rich-text/mp-html/search/index.js +132 -0
- package/components/oxy-rich-text/mp-html/style/index.js +129 -0
- package/components/oxy-rich-text/mp-html/style/parser.js +175 -0
- package/components/oxy-rich-text/mp-html/template/index.js +67 -0
- package/components/oxy-rich-text/mp-html/txv-video/index.js +46 -0
- package/components/oxy-rich-text/oxy-rich-text.vue +642 -0
- package/components/oxy-rich-text/types.ts +71 -0
- package/components/oxy-select/index.scss +255 -0
- package/components/oxy-select/oxy-select.vue +421 -0
- package/components/oxy-select/types.ts +71 -0
- package/components/oxy-select-picker/oxy-select-picker.vue +3 -0
- package/components/oxy-select-picker/types.ts +5 -1
- package/components/oxy-stream-render/index.scss +6 -0
- package/components/oxy-stream-render/oxy-stream-render.vue +204 -0
- package/components/oxy-stream-render/types.ts +5 -0
- package/components/oxy-tree/index.scss +43 -5
- package/components/oxy-tree/oxy-tree.vue +233 -35
- package/components/oxy-tree/types.ts +54 -7
- package/components/oxy-tree/utils.ts +51 -0
- package/components/oxy-virtual-scroll/index.scss +1 -1
- package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +69 -110
- package/components/oxy-virtual-scroll/types.ts +95 -5
- package/components/oxy-waterfall/index.scss +18 -0
- package/components/oxy-waterfall/oxy-waterfall.vue +218 -0
- package/components/oxy-waterfall/types.ts +90 -0
- package/components/oxy-waterfall-item/index.scss +8 -0
- package/components/oxy-waterfall-item/oxy-waterfall-item.vue +89 -0
- package/components/oxy-waterfall-item/types.ts +16 -0
- package/global.d.ts +7 -0
- package/index.ts +3 -0
- package/locale/lang/en-US.ts +35 -9
- package/locale/lang/zh-CN.ts +31 -5
- package/oxy-uni-ui.zip +0 -0
- package/package.json +1 -1
- package/tags.json +1 -1
- package/uni-echarts/changelog.md +2 -0
- package/uni-echarts/components/index.js +1 -0
- package/uni-echarts/components/uni-echarts/events.js +95 -0
- package/uni-echarts/components/uni-echarts/types.d.ts +183 -0
- package/uni-echarts/components/uni-echarts/types.js +1 -0
- package/uni-echarts/components/uni-echarts/uni-echarts.vue +530 -0
- package/uni-echarts/components/uni-echarts/uni-echarts.vue.d.ts +19 -0
- package/uni-echarts/global.d.ts +7 -0
- package/uni-echarts/index.d.ts +440 -0
- package/uni-echarts/index.js +2 -0
- package/uni-echarts/package.json +105 -0
- package/uni-echarts/shared-core.d.ts +269 -0
- package/uni-echarts/shared-core.js +900 -0
- package/web-types.json +1 -1
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
width: 80px;
|
|
15
15
|
height: 80px;
|
|
16
16
|
overflow: hidden;
|
|
17
|
+
@include when(round) {
|
|
18
|
+
border-top-right-radius: $-corner-radius;
|
|
19
|
+
}
|
|
17
20
|
@include when(primary) {
|
|
18
21
|
.oxy-corner__text {
|
|
19
22
|
@include corner-type-style($-corner-color, $-corner-primary-bg);
|
|
@@ -23,6 +26,11 @@
|
|
|
23
26
|
@include corner-type-style($-corner-primary-color, $-corner-horizontal-primary-bg);
|
|
24
27
|
}
|
|
25
28
|
}
|
|
29
|
+
&.is-embedded {
|
|
30
|
+
.oxy-corner__text {
|
|
31
|
+
@include corner-type-style($-corner-primary-color, $-corner-embedded-primary-bg);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
35
|
@include when(info) {
|
|
28
36
|
.oxy-corner__text {
|
|
@@ -33,6 +41,11 @@
|
|
|
33
41
|
@include corner-type-style($-corner-info-color, $-corner-horizontal-info-bg);
|
|
34
42
|
}
|
|
35
43
|
}
|
|
44
|
+
&.is-embedded {
|
|
45
|
+
.oxy-corner__text {
|
|
46
|
+
@include corner-type-style($-corner-info-color, $-corner-embedded-info-bg);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
}
|
|
37
50
|
@include when(success) {
|
|
38
51
|
.oxy-corner__text {
|
|
@@ -43,6 +56,11 @@
|
|
|
43
56
|
@include corner-type-style($-corner-success-color, $-corner-horizontal-success-bg);
|
|
44
57
|
}
|
|
45
58
|
}
|
|
59
|
+
&.is-embedded {
|
|
60
|
+
.oxy-corner__text {
|
|
61
|
+
@include corner-type-style($-corner-success-color, $-corner-embedded-success-bg);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
46
64
|
}
|
|
47
65
|
@include when(warning) {
|
|
48
66
|
.oxy-corner__text {
|
|
@@ -53,6 +71,11 @@
|
|
|
53
71
|
@include corner-type-style($-corner-warning-color, $-corner-horizontal-warning-bg);
|
|
54
72
|
}
|
|
55
73
|
}
|
|
74
|
+
&.is-embedded {
|
|
75
|
+
.oxy-corner__text {
|
|
76
|
+
@include corner-type-style($-corner-warning-color, $-corner-embedded-warning-bg);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
56
79
|
}
|
|
57
80
|
@include when(danger) {
|
|
58
81
|
.oxy-corner__text {
|
|
@@ -63,12 +86,21 @@
|
|
|
63
86
|
@include corner-type-style($-corner-danger-color, $-corner-horizontal-danger-bg);
|
|
64
87
|
}
|
|
65
88
|
}
|
|
89
|
+
&.is-embedded {
|
|
90
|
+
.oxy-corner__text {
|
|
91
|
+
@include corner-type-style($-corner-danger-color, $-corner-embedded-danger-bg);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
66
94
|
}
|
|
67
95
|
&:not(.is-horizontal) {
|
|
68
96
|
&.is-top-left{
|
|
69
97
|
top: 0;
|
|
70
98
|
left: 0;
|
|
71
99
|
right: auto;
|
|
100
|
+
&.is-round {
|
|
101
|
+
border-top-right-radius: 0;
|
|
102
|
+
border-top-left-radius: $-corner-radius;
|
|
103
|
+
}
|
|
72
104
|
.oxy-corner__text {
|
|
73
105
|
transform: rotate(-45deg);
|
|
74
106
|
top: 10px;
|
|
@@ -79,6 +111,10 @@
|
|
|
79
111
|
bottom: 0;
|
|
80
112
|
left: 0;
|
|
81
113
|
top: auto;
|
|
114
|
+
&.is-round {
|
|
115
|
+
border-top-right-radius: 0;
|
|
116
|
+
border-bottom-left-radius: $-corner-radius;
|
|
117
|
+
}
|
|
82
118
|
.oxy-corner__text {
|
|
83
119
|
transform: rotate(-135deg);
|
|
84
120
|
bottom: 10px;
|
|
@@ -94,6 +130,10 @@
|
|
|
94
130
|
bottom: 0;
|
|
95
131
|
right: 0;
|
|
96
132
|
top: auto;
|
|
133
|
+
&.is-round {
|
|
134
|
+
border-top-right-radius: 0;
|
|
135
|
+
border-bottom-right-radius: $-corner-radius;
|
|
136
|
+
}
|
|
97
137
|
.oxy-corner__text {
|
|
98
138
|
transform: rotate(135deg);
|
|
99
139
|
bottom: 10px;
|
|
@@ -105,6 +145,62 @@
|
|
|
105
145
|
}
|
|
106
146
|
}
|
|
107
147
|
}
|
|
148
|
+
&.is-embedded {
|
|
149
|
+
&.is-top-left{
|
|
150
|
+
&::after {
|
|
151
|
+
left: auto;
|
|
152
|
+
right: -6px;
|
|
153
|
+
bottom: 0;
|
|
154
|
+
transform: skewX(-30deg);
|
|
155
|
+
}
|
|
156
|
+
.oxy-corner__text {
|
|
157
|
+
transform: none;
|
|
158
|
+
top: 0;
|
|
159
|
+
left: 0;
|
|
160
|
+
border-bottom-left-radius: 0;
|
|
161
|
+
border-bottom-right-radius: 32px;
|
|
162
|
+
padding-left: 0;
|
|
163
|
+
padding-right: 12px;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
&.is-bottom-left{
|
|
167
|
+
&::after {
|
|
168
|
+
left: auto;
|
|
169
|
+
right: -6px;
|
|
170
|
+
bottom: 0;
|
|
171
|
+
transform: skewX(30deg);
|
|
172
|
+
}
|
|
173
|
+
.oxy-corner__text {
|
|
174
|
+
transform: none;
|
|
175
|
+
bottom: 0;
|
|
176
|
+
left: 0;
|
|
177
|
+
border-bottom-left-radius: 0;
|
|
178
|
+
border-top-right-radius: 32px;
|
|
179
|
+
padding-left: 0;
|
|
180
|
+
padding-right: 12px;
|
|
181
|
+
> span{
|
|
182
|
+
transform: none;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
&.is-bottom-right{
|
|
187
|
+
&::after {
|
|
188
|
+
left: -6px;
|
|
189
|
+
bottom: 0;
|
|
190
|
+
transform: skewX(-30deg);
|
|
191
|
+
}
|
|
192
|
+
.oxy-corner__text {
|
|
193
|
+
transform: none;
|
|
194
|
+
bottom: 0;
|
|
195
|
+
right: 0;
|
|
196
|
+
border-bottom-left-radius: 0;
|
|
197
|
+
border-top-left-radius: 32px;
|
|
198
|
+
> span{
|
|
199
|
+
transform: none;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
108
204
|
.oxy-corner__text {
|
|
109
205
|
width: 106px;
|
|
110
206
|
height: 32px;
|
|
@@ -134,5 +230,29 @@
|
|
|
134
230
|
text-align: right;
|
|
135
231
|
}
|
|
136
232
|
}
|
|
233
|
+
&.is-embedded {
|
|
234
|
+
width: 106px;
|
|
235
|
+
height: 32px;
|
|
236
|
+
&::after {
|
|
237
|
+
content: '';
|
|
238
|
+
position: absolute;
|
|
239
|
+
left: -6px;
|
|
240
|
+
bottom: 0;
|
|
241
|
+
width: 16px;
|
|
242
|
+
height: 32px;
|
|
243
|
+
background-color: #ffffff;
|
|
244
|
+
transform: skewX(30deg);
|
|
245
|
+
}
|
|
246
|
+
.oxy-corner__text {
|
|
247
|
+
right: 0;
|
|
248
|
+
top: 0;
|
|
249
|
+
transform: none;
|
|
250
|
+
padding-left: 12px;
|
|
251
|
+
box-sizing: border-box;
|
|
252
|
+
font-size: $-corner-embedded-font-size;
|
|
253
|
+
font-weight: $-corner-embedded-font-weight;
|
|
254
|
+
border-bottom-left-radius: 32px;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
137
257
|
}
|
|
138
|
-
}
|
|
258
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<view class="oxy-corner">
|
|
2
|
+
<view :class="`oxy-corner ${customClass}`" :style="customStyle">
|
|
3
3
|
<slot />
|
|
4
4
|
<view v-if="text || $slots.text" :class="textClass">
|
|
5
|
-
<view class="oxy-corner__text">
|
|
5
|
+
<view class="oxy-corner__text" :style="textStyle">
|
|
6
6
|
<slot name="text">
|
|
7
7
|
<span>{{ text }}</span>
|
|
8
8
|
</slot>
|
|
@@ -22,6 +22,7 @@ export default {
|
|
|
22
22
|
}
|
|
23
23
|
</script>
|
|
24
24
|
<script lang="ts" setup>
|
|
25
|
+
import { objToStyle } from '../common/util'
|
|
25
26
|
import { computed, ref, watch } from 'vue'
|
|
26
27
|
import { cornerProps } from './types'
|
|
27
28
|
|
|
@@ -30,7 +31,7 @@ const props = defineProps(cornerProps)
|
|
|
30
31
|
const cornerClass = ref<string>('')
|
|
31
32
|
|
|
32
33
|
watch(
|
|
33
|
-
[() => () => props.position, () => props.mode],
|
|
34
|
+
[() => () => props.position, () => props.mode, () => props.round],
|
|
34
35
|
() => {
|
|
35
36
|
computeCornerClass()
|
|
36
37
|
},
|
|
@@ -49,15 +50,27 @@ watch(
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
const textClass = computed(() => {
|
|
52
|
-
return `oxy-corner__box ${
|
|
53
|
+
return `oxy-corner__box ${cornerClass.value}`
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const textStyle = computed(() => {
|
|
57
|
+
const textStyle: Record<string, any> = {}
|
|
58
|
+
if (props.color) {
|
|
59
|
+
textStyle['color'] = props.color
|
|
60
|
+
}
|
|
61
|
+
if (props.bgColor) {
|
|
62
|
+
textStyle['background'] = props.bgColor
|
|
63
|
+
}
|
|
64
|
+
return objToStyle(textStyle)
|
|
53
65
|
})
|
|
54
66
|
|
|
55
67
|
function computeCornerClass() {
|
|
56
|
-
const { type, position, mode } = props
|
|
68
|
+
const { type, position, mode, round } = props
|
|
57
69
|
let cornerClassList: string[] = []
|
|
58
70
|
type && cornerClassList.push(`is-${type}`)
|
|
59
71
|
position && cornerClassList.push(`is-${position}`)
|
|
60
72
|
mode && cornerClassList.push(`is-${mode}`)
|
|
73
|
+
round && cornerClassList.push('is-round')
|
|
61
74
|
cornerClass.value = cornerClassList.join(' ')
|
|
62
75
|
}
|
|
63
76
|
</script>
|
|
@@ -3,7 +3,7 @@ import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
|
|
|
3
3
|
|
|
4
4
|
export type CornerType = 'primary' | 'info' | 'success' | 'warning' | 'danger'
|
|
5
5
|
export type CornerPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
6
|
-
export type CornerMode = '' | 'horizontal'
|
|
6
|
+
export type CornerMode = '' | 'horizontal' | 'embedded'
|
|
7
7
|
|
|
8
8
|
export const cornerProps = {
|
|
9
9
|
...baseProps,
|
|
@@ -24,10 +24,17 @@ export const cornerProps = {
|
|
|
24
24
|
*/
|
|
25
25
|
position: makeStringProp<CornerPosition>('top-right'),
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 圆角类型
|
|
29
|
+
* 类型:boolean
|
|
30
|
+
* 默认值:false
|
|
31
|
+
*/
|
|
32
|
+
round: makeBooleanProp(false),
|
|
33
|
+
|
|
27
34
|
/**
|
|
28
35
|
* 角标模式
|
|
29
36
|
* 类型:string
|
|
30
|
-
* 可选值:'horizontal'
|
|
37
|
+
* 可选值:'horizontal' / 'embedded'
|
|
31
38
|
* 默认值:''
|
|
32
39
|
*/
|
|
33
40
|
mode: makeStringProp<CornerMode>(''),
|
|
@@ -37,7 +44,21 @@ export const cornerProps = {
|
|
|
37
44
|
* 类型:string
|
|
38
45
|
* 默认值:空字符串
|
|
39
46
|
*/
|
|
40
|
-
text: makeStringProp('')
|
|
47
|
+
text: makeStringProp(''),
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 文字颜色
|
|
51
|
+
* 类型:string
|
|
52
|
+
* 默认值:空字符串
|
|
53
|
+
*/
|
|
54
|
+
color: makeStringProp(''),
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 背景色和边框色
|
|
58
|
+
* 类型:string
|
|
59
|
+
* 默认值:空字符串
|
|
60
|
+
*/
|
|
61
|
+
bgColor: makeStringProp('')
|
|
41
62
|
}
|
|
42
63
|
|
|
43
64
|
export type CornerProps = ExtractPropTypes<typeof cornerProps>
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<scroll-view
|
|
3
|
+
:class="`oxy-date-strip oxy-date-strip__scroll ${customClass}`"
|
|
4
|
+
:scroll-x="true"
|
|
5
|
+
:scroll-left="scrollLeft"
|
|
6
|
+
:show-scrollbar="false"
|
|
7
|
+
direction="horizontal"
|
|
8
|
+
:style="rootStyle"
|
|
9
|
+
v-if="switchMode === 'none'"
|
|
10
|
+
>
|
|
11
|
+
<oxy-date-strip-item
|
|
12
|
+
:dates="displayWeeks[0].dates"
|
|
13
|
+
:color="color"
|
|
14
|
+
:activeBgColor="activeBgColor"
|
|
15
|
+
:activeColor="activeColor"
|
|
16
|
+
:bgColor="bgColor"
|
|
17
|
+
:radius="radius"
|
|
18
|
+
:switchMode="switchMode"
|
|
19
|
+
:shape="shape"
|
|
20
|
+
@click="onClick"
|
|
21
|
+
></oxy-date-strip-item>
|
|
22
|
+
</scroll-view>
|
|
23
|
+
<swiper
|
|
24
|
+
v-else
|
|
25
|
+
:class="`oxy-date-strip ${customClass}`"
|
|
26
|
+
:style="rootStyle"
|
|
27
|
+
:current="currentWeekIndex"
|
|
28
|
+
:circular="swiperCircular"
|
|
29
|
+
@change="swiperChange"
|
|
30
|
+
>
|
|
31
|
+
<swiper-item v-for="(week, g) in displayWeeks" :key="g">
|
|
32
|
+
<oxy-date-strip-item
|
|
33
|
+
:dates="week.dates"
|
|
34
|
+
:color="color"
|
|
35
|
+
:activeBgColor="activeBgColor"
|
|
36
|
+
:activeColor="activeColor"
|
|
37
|
+
:bgColor="bgColor"
|
|
38
|
+
:radius="radius"
|
|
39
|
+
:switchMode="switchMode"
|
|
40
|
+
:shape="shape"
|
|
41
|
+
@click="onClick"
|
|
42
|
+
></oxy-date-strip-item>
|
|
43
|
+
</swiper-item>
|
|
44
|
+
</swiper>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
export default {
|
|
49
|
+
name: 'oxy-date-strip',
|
|
50
|
+
options: {
|
|
51
|
+
addGlobalClass: true,
|
|
52
|
+
virtualHost: true,
|
|
53
|
+
styleIsolation: 'shared'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<script lang="ts" setup>
|
|
59
|
+
import OxyDateStripItem from '../oxy-date-strip-item/oxy-date-strip-item.vue'
|
|
60
|
+
import { ref, computed, watch, onMounted, nextTick, reactive } from 'vue'
|
|
61
|
+
import { objToStyle, unitConvert } from '../common/util'
|
|
62
|
+
import { getWeekRange, addDays, addWeeks, calcType, daysBetween } from './utils'
|
|
63
|
+
import { dateStripProps, type WeekDateCollection, type DateStriPDay } from './types'
|
|
64
|
+
|
|
65
|
+
const props = defineProps(dateStripProps)
|
|
66
|
+
const emit = defineEmits(['change', 'select', 'update:modelValue', 'input'])
|
|
67
|
+
|
|
68
|
+
// 当前选中的周索引
|
|
69
|
+
const currentWeekIndex = ref(0)
|
|
70
|
+
const scrollLeft = ref(0)
|
|
71
|
+
// 是否循环滚动
|
|
72
|
+
const swiperCircular = ref(true)
|
|
73
|
+
const selectedDate = ref<Date | null>(null)
|
|
74
|
+
// 计算一周的星期顺序
|
|
75
|
+
const firstDayOfWeek = computed((): number => Math.min(Math.max(props.firstDayOfWeek, 0), 6))
|
|
76
|
+
|
|
77
|
+
// 计算最小和最大日期
|
|
78
|
+
const today = new Date()
|
|
79
|
+
const minDate = computed((): Date => (props.minDate !== null ? new Date(props.minDate!) : today))
|
|
80
|
+
const maxDate = computed((): Date => (props.maxDate !== null ? new Date(props.maxDate!) : addDays(today, 31)))
|
|
81
|
+
|
|
82
|
+
const days = computed((): number => {
|
|
83
|
+
return daysBetween(maxDate.value, minDate.value)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const rootStyle = computed(() => {
|
|
87
|
+
const style: Record<string, any> = {}
|
|
88
|
+
if (props.height) {
|
|
89
|
+
style['height'] = props.height
|
|
90
|
+
}
|
|
91
|
+
if (props.bgColor) {
|
|
92
|
+
style['background'] = props.bgColor
|
|
93
|
+
}
|
|
94
|
+
return `${objToStyle(style)}${props.customStyle}`
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const cache = new Map<number, WeekDateCollection>()
|
|
98
|
+
const createWeek = (currentStartDate: Date, length: number): WeekDateCollection => {
|
|
99
|
+
const dates: DateStriPDay[] = []
|
|
100
|
+
const time = currentStartDate.getTime()
|
|
101
|
+
if (cache.has(time)) {
|
|
102
|
+
return cache.get(time)!
|
|
103
|
+
}
|
|
104
|
+
for (let i = 0; i < length; i++) {
|
|
105
|
+
const date = new Date(time)
|
|
106
|
+
date.setDate(currentStartDate.getDate() + i)
|
|
107
|
+
const week = date.getDay()
|
|
108
|
+
const year = date.getFullYear()
|
|
109
|
+
const month = date.getMonth() + 1
|
|
110
|
+
const day = date.getDate()
|
|
111
|
+
const dateObj = reactive<DateStriPDay>({
|
|
112
|
+
key: `${year}-${month}-${day}`,
|
|
113
|
+
date,
|
|
114
|
+
year,
|
|
115
|
+
month,
|
|
116
|
+
day,
|
|
117
|
+
text: `${day}`.padStart(2, '0'),
|
|
118
|
+
type: calcType(date, minDate.value, maxDate.value, selectedDate.value, props.disableDate),
|
|
119
|
+
prefix: props.weekdays[week]
|
|
120
|
+
})
|
|
121
|
+
dates.push(props.format !== null ? props.format!(dateObj) : dateObj)
|
|
122
|
+
}
|
|
123
|
+
const obj: WeekDateCollection = {
|
|
124
|
+
start: new Date(dates[0].year, dates[0].month - 1, dates[0].day).getTime(),
|
|
125
|
+
end: new Date(dates[length - 1].year, dates[length - 1].month - 1, dates[length - 1].day).getTime(),
|
|
126
|
+
dates
|
|
127
|
+
} as WeekDateCollection
|
|
128
|
+
return obj
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const currentDate = ref<Date>(today)
|
|
132
|
+
|
|
133
|
+
// 计算要显示的三周数据
|
|
134
|
+
const displayWeeks = computed((): WeekDateCollection[] => {
|
|
135
|
+
const weeks: WeekDateCollection[] = []
|
|
136
|
+
if (props.switchMode === 'week') {
|
|
137
|
+
const currentRange = getWeekRange(currentDate.value, firstDayOfWeek.value)
|
|
138
|
+
const offsetMap = new Map<number, number[]>([
|
|
139
|
+
[0, [0, 1, -1]],
|
|
140
|
+
[1, [-1, 0, 1]],
|
|
141
|
+
[2, [1, -1, 0]]
|
|
142
|
+
])
|
|
143
|
+
let indices = offsetMap.get(currentWeekIndex.value)!
|
|
144
|
+
indices.forEach((i) => {
|
|
145
|
+
const weekDate = addWeeks(currentRange.start, i)
|
|
146
|
+
weeks.push(createWeek(weekDate, 7))
|
|
147
|
+
})
|
|
148
|
+
} else {
|
|
149
|
+
weeks.push(createWeek(minDate.value, days.value))
|
|
150
|
+
}
|
|
151
|
+
return weeks
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const swiperChange = (event: any) => {
|
|
155
|
+
const delta = event.detail.current - currentWeekIndex.value
|
|
156
|
+
const newDate = addWeeks(currentDate.value, delta == 1 || delta == -2 ? 1 : -1)
|
|
157
|
+
currentDate.value = newDate
|
|
158
|
+
currentWeekIndex.value = event.detail.current
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const onClick = (day: DateStriPDay) => {
|
|
162
|
+
if (day.type == 'disabled') return
|
|
163
|
+
selectedDate.value = day.date
|
|
164
|
+
const v = day.date.getTime()
|
|
165
|
+
emit('change', v)
|
|
166
|
+
emit('select', v)
|
|
167
|
+
emit('update:modelValue', v)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const scrollToDate = (date: Date) => {
|
|
171
|
+
currentDate.value = date
|
|
172
|
+
if (props.switchMode !== 'none') return
|
|
173
|
+
scrollLeft.value = unitConvert(props.gridWidth || 50) * daysBetween(date, minDate.value)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
watch(
|
|
177
|
+
() => props.modelValue,
|
|
178
|
+
(value) => {
|
|
179
|
+
if (!value) return
|
|
180
|
+
selectedDate.value = new Date(value)
|
|
181
|
+
},
|
|
182
|
+
{ deep: true, immediate: true }
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
onMounted(() => {
|
|
186
|
+
nextTick(() => {
|
|
187
|
+
scrollToDate(currentDate.value)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
defineExpose({
|
|
192
|
+
scrollToDate
|
|
193
|
+
})
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<style lang="scss" scoped>
|
|
197
|
+
@import './index.scss';
|
|
198
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { baseProps, makeArrayProp, makeNumberProp, makeStringProp } from '../common/props'
|
|
4
|
+
|
|
5
|
+
export type DateType = 'selected' | 'disabled' | ''
|
|
6
|
+
export type DisableDate = (date: Date) => boolean
|
|
7
|
+
export type FormatDate = (day: DateStriPDay) => DateStriPDay
|
|
8
|
+
|
|
9
|
+
export type DateStripSwitchMode = 'week' | 'none'
|
|
10
|
+
export type DateStripShape = 'square' | 'circle' | 'none'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 定义一个表示周范围的类
|
|
14
|
+
*/
|
|
15
|
+
export type WeekRange = {
|
|
16
|
+
start: Date
|
|
17
|
+
end: Date
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type DateStriPDay = {
|
|
21
|
+
date: Date
|
|
22
|
+
key: string
|
|
23
|
+
day: number
|
|
24
|
+
year: number
|
|
25
|
+
month: number
|
|
26
|
+
text: string
|
|
27
|
+
type: DateType
|
|
28
|
+
prefix: string
|
|
29
|
+
suffix?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type WeekDateCollection = {
|
|
33
|
+
start: number
|
|
34
|
+
end: number
|
|
35
|
+
dates: DateStriPDay[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const dateStripProps = {
|
|
39
|
+
...baseProps,
|
|
40
|
+
// 当前选择的日期
|
|
41
|
+
modelValue: {
|
|
42
|
+
type: [Number, null] as PropType<number | null>,
|
|
43
|
+
default: null
|
|
44
|
+
},
|
|
45
|
+
// 日期行高
|
|
46
|
+
height: makeStringProp(''),
|
|
47
|
+
// 第一天从星期几开始,默认 1 = 周一
|
|
48
|
+
firstDayOfWeek: makeNumberProp(0),
|
|
49
|
+
// 日期格式,可选
|
|
50
|
+
format: {
|
|
51
|
+
type: Function as PropType<FormatDate>,
|
|
52
|
+
default: null
|
|
53
|
+
},
|
|
54
|
+
// 最大可选日期,不传则默认半年后
|
|
55
|
+
maxDate: {
|
|
56
|
+
type: [Number, null] as PropType<number | null>,
|
|
57
|
+
default: null
|
|
58
|
+
},
|
|
59
|
+
// 最小可选日期,不传则默认今天
|
|
60
|
+
minDate: {
|
|
61
|
+
type: [Number, null] as PropType<number | null>,
|
|
62
|
+
default: null
|
|
63
|
+
},
|
|
64
|
+
// 网格宽度
|
|
65
|
+
gridWidth: makeStringProp(''),
|
|
66
|
+
// 主题色,对底部按钮和选中日期生效
|
|
67
|
+
color: makeStringProp(''),
|
|
68
|
+
// 选中日期的背景色
|
|
69
|
+
activeBgColor: makeStringProp(''),
|
|
70
|
+
// 选中日期的文字颜色
|
|
71
|
+
activeColor: makeStringProp(''),
|
|
72
|
+
// 背景色
|
|
73
|
+
bgColor: makeStringProp(''),
|
|
74
|
+
// 圆角半径
|
|
75
|
+
radius: makeStringProp(''),
|
|
76
|
+
// 切换模式,'week' 或 'none'
|
|
77
|
+
switchMode: makeStringProp<DateStripSwitchMode>('week'),
|
|
78
|
+
// 形状,'square'、'circle' 或 'none'
|
|
79
|
+
shape: makeStringProp<DateStripShape>('square'),
|
|
80
|
+
// 星期几的名称数组
|
|
81
|
+
weekdays: {
|
|
82
|
+
...makeArrayProp<string>(),
|
|
83
|
+
default: () => ['日', '一', '二', '三', '四', '五', '六']
|
|
84
|
+
},
|
|
85
|
+
// 禁用日期函数
|
|
86
|
+
disableDate: {
|
|
87
|
+
type: Function as PropType<DisableDate>,
|
|
88
|
+
default: null
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type DateStripProps = ExtractPropTypes<typeof dateStripProps>
|
|
93
|
+
|
|
94
|
+
export type DateStripExpose = {
|
|
95
|
+
scrollToDate: (date: Date) => void
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type DateStripInstance = ComponentPublicInstance<DateStripProps, DateStripExpose>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { WeekRange, DateType, DisableDate } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取指定日期所在周的日期范围。
|
|
5
|
+
* @param {Date} date - 指定日期。
|
|
6
|
+
* @param {number} firstDayOfWeek - 一周的第一天,0 表示周日,1 表示周一,以此类推。
|
|
7
|
+
* @returns {WeekRange} 返回一个包含周起始和结束日期的对象。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function getWeekRange(date: Date, firstDayOfWeek: number): WeekRange {
|
|
11
|
+
const start = new Date(date.getTime())
|
|
12
|
+
const dayOffset = (date.getDay() - firstDayOfWeek + 7) % 7
|
|
13
|
+
start.setDate(start.getDate() - dayOffset)
|
|
14
|
+
|
|
15
|
+
const end = new Date(start.getTime())
|
|
16
|
+
end.setDate(end.getDate() + 6)
|
|
17
|
+
return { start, end } as WeekRange
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 向指定日期添加天数。
|
|
22
|
+
* @param {Date} date - 基础日期。
|
|
23
|
+
* @param {number} days - 要添加的天数,可以是正数或负数。
|
|
24
|
+
* @returns {Date} 返回一个新的日期对象,该对象是基础日期加上指定天数后的结果。
|
|
25
|
+
*/
|
|
26
|
+
export function addDays(date: Date, days: number): Date {
|
|
27
|
+
const result = new Date(date.getTime())
|
|
28
|
+
result.setDate(result.getDate() + days)
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addWeeks(date: Date, weeks: number): Date {
|
|
33
|
+
const result = new Date(date.getTime())
|
|
34
|
+
result.setDate(result.getDate() + weeks * 7)
|
|
35
|
+
return result
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 判断两个日期是否表示同一天(忽略时间部分)。
|
|
40
|
+
*
|
|
41
|
+
* @param date1 - 第一个日期。
|
|
42
|
+
* @param date2 - 第二个日期。
|
|
43
|
+
* @returns 如果两个日期是同一天,返回 true;否则返回 false。
|
|
44
|
+
*/
|
|
45
|
+
function isSameDay(date1: Date, date2: Date): boolean {
|
|
46
|
+
return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate()
|
|
47
|
+
}
|
|
48
|
+
export function calcType(date: Date, minDate: Date, maxDate: Date, selectedDate: Date | null, disableDate?: DisableDate): DateType {
|
|
49
|
+
// 检查日期是否早于 minDate 或晚于 maxDate
|
|
50
|
+
if (date.getTime() < minDate.getTime() || date.getTime() > maxDate.getTime()) {
|
|
51
|
+
return 'disabled'
|
|
52
|
+
}
|
|
53
|
+
if (disableDate && disableDate(date)) {
|
|
54
|
+
return 'disabled'
|
|
55
|
+
}
|
|
56
|
+
// 如果 selectedDate 不为 null,检查日期是否等于 selectedDate
|
|
57
|
+
if (selectedDate != null && isSameDay(date, selectedDate)) {
|
|
58
|
+
return 'selected'
|
|
59
|
+
}
|
|
60
|
+
return ''
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function daysBetween(date1: Date, date2: Date): number {
|
|
64
|
+
// 将两个日期转换为毫秒
|
|
65
|
+
const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime())
|
|
66
|
+
return Math.floor(diffInMilliseconds / (1000 * 3600 * 24))
|
|
67
|
+
}
|