xt-element-ui 1.2.6 → 1.2.8
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/README.md +1 -1
- package/docs/README.md +1 -1
- package/docs/components/base/xt-card-item.md +1 -1
- package/docs/components/base/xt-step-price.md +280 -0
- package/docs/components/base/xt-time.md +165 -0
- package/docs/components/extend/ex-date-picker.md +0 -0
- package/lib/index.common.js +810 -61
- package/lib/index.css +1 -1
- package/lib/index.umd.js +810 -61
- package/lib/index.umd.min.js +4 -4
- package/package.json +4 -4
- package/src/components/ex-chart/theme/dark.js +5 -4
- package/src/components/ex-chart/theme/white.js +1 -0
- package/src/components/ex-date-picker/index.js +2 -0
- package/src/components/{xt-date-picker → ex-date-picker}/index.vue +1 -1
- package/src/components/index.scss +6 -0
- package/src/components/xt-card-item/index.vue +3 -1
- package/src/components/xt-step-price/index.js +2 -0
- package/src/components/xt-step-price/index.vue +270 -0
- package/src/components/xt-step-price/style/index.scss +115 -0
- package/src/components/xt-step-price-item/index.js +2 -0
- package/src/components/xt-step-price-item/index.vue +183 -0
- package/src/components/xt-time/index.js +2 -0
- package/src/components/xt-time/index.vue +313 -0
- package/src/components/xt-time/style/index.scss +23 -0
- package/src/index.js +12 -3
- package/src/components/xt-date-picker/index.js +0 -2
- /package/src/components/{xt-date-picker → ex-date-picker}/SearchDate.vue +0 -0
- /package/src/components/{xt-date-picker → ex-date-picker}/quarter.vue +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class="xt-time"
|
|
4
|
+
:class="[
|
|
5
|
+
typeColor ? 'xt-time--' + typeColor : '',
|
|
6
|
+
'xt-time--' + size,
|
|
7
|
+
{ 'xt-time--bold': bold }
|
|
8
|
+
]"
|
|
9
|
+
:style="customStyle"
|
|
10
|
+
@click="handleClick"
|
|
11
|
+
>
|
|
12
|
+
<slot name="prefix">{{ prefix }}</slot>
|
|
13
|
+
|
|
14
|
+
<slot>
|
|
15
|
+
<template v-if="mode === 'text'">
|
|
16
|
+
<template v-if="displayText !== undefined">{{ displayText }}</template>
|
|
17
|
+
<template v-else-if="!hideEmpty">{{ emptyText }}</template>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<template v-else-if="mode === 'now'">
|
|
21
|
+
{{ nowText }}
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<template v-else-if="mode === 'countdown'">
|
|
25
|
+
<template v-if="isCountdownFinished">
|
|
26
|
+
<slot name="finished">{{ finishedText }}</slot>
|
|
27
|
+
</template>
|
|
28
|
+
<template v-else>
|
|
29
|
+
{{ countdownText }}
|
|
30
|
+
</template>
|
|
31
|
+
</template>
|
|
32
|
+
</slot>
|
|
33
|
+
|
|
34
|
+
<slot name="suffix">{{ suffix }}</slot>
|
|
35
|
+
</span>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script>
|
|
39
|
+
const DEFAULT_FORMAT = 'YYYY-MM-DD HH:mm:ss'
|
|
40
|
+
|
|
41
|
+
function pad(n) {
|
|
42
|
+
const s = String(n)
|
|
43
|
+
return s.length < 2 ? '0' + s : s
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 根据字符串/数字/Date 解析为 Date 对象
|
|
48
|
+
* 兼容:时间戳(ms/s)、Date、"2024-01-01"、"2024/01/01 12:00:00"、ISO 字符串
|
|
49
|
+
*/
|
|
50
|
+
function parseDate(value) {
|
|
51
|
+
if (value === null || value === undefined || value === '') return null
|
|
52
|
+
if (value instanceof Date) return isNaN(value.getTime()) ? null : value
|
|
53
|
+
|
|
54
|
+
// 数字时间戳
|
|
55
|
+
if (typeof value === 'number') {
|
|
56
|
+
// 10 位秒级时间戳补 000
|
|
57
|
+
const t = value < 1e12 ? value * 1000 : value
|
|
58
|
+
const d = new Date(t)
|
|
59
|
+
return isNaN(d.getTime()) ? null : d
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof value === 'string') {
|
|
63
|
+
// 纯数字字符串
|
|
64
|
+
if (/^\d+$/.test(value)) {
|
|
65
|
+
const n = Number(value)
|
|
66
|
+
const t = n < 1e12 ? n * 1000 : n
|
|
67
|
+
const d = new Date(t)
|
|
68
|
+
return isNaN(d.getTime()) ? null : d
|
|
69
|
+
}
|
|
70
|
+
// 将 "-" 统一为 "/",避免 Safari 下 "YYYY-MM-DD" 解析失败
|
|
71
|
+
const normalized = value.replace(/-/g, '/')
|
|
72
|
+
const d = new Date(normalized)
|
|
73
|
+
return isNaN(d.getTime()) ? null : d
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 简易 formatDateTime 实现(不依赖外部库)
|
|
81
|
+
* 支持占位符:YYYY MM DD HH mm ss SSS
|
|
82
|
+
*/
|
|
83
|
+
function formatDateTime(date, fmt) {
|
|
84
|
+
if (!date) return ''
|
|
85
|
+
const f = fmt || DEFAULT_FORMAT
|
|
86
|
+
const map = {
|
|
87
|
+
YYYY: date.getFullYear(),
|
|
88
|
+
MM: pad(date.getMonth() + 1),
|
|
89
|
+
DD: pad(date.getDate()),
|
|
90
|
+
HH: pad(date.getHours()),
|
|
91
|
+
mm: pad(date.getMinutes()),
|
|
92
|
+
ss: pad(date.getSeconds()),
|
|
93
|
+
SSS: String(date.getMilliseconds()).padStart(3, '0')
|
|
94
|
+
}
|
|
95
|
+
return f.replace(/YYYY|MM|DD|HH|mm|ss|SSS/g, (k) => map[k])
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default {
|
|
99
|
+
name: 'XtTime',
|
|
100
|
+
|
|
101
|
+
props: {
|
|
102
|
+
// 显示模式:now(当前时间实时刷新)/ countdown(倒计时)/ text(格式化日期文本)
|
|
103
|
+
type: {
|
|
104
|
+
type: String,
|
|
105
|
+
default: 'now',
|
|
106
|
+
validator: (val) => ['now', 'countdown', 'text'].includes(val)
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// ===== 通用 =====
|
|
110
|
+
size: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: 'base',
|
|
113
|
+
validator: (val) =>
|
|
114
|
+
['extra-large', 'large', 'medium', 'base', 'small', 'extra-small'].includes(val)
|
|
115
|
+
},
|
|
116
|
+
typeColor: {
|
|
117
|
+
type: String,
|
|
118
|
+
default: '',
|
|
119
|
+
validator: (val) => ['', 'primary', 'success', 'warning', 'danger'].includes(val)
|
|
120
|
+
},
|
|
121
|
+
bold: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false
|
|
124
|
+
},
|
|
125
|
+
format: {
|
|
126
|
+
type: String,
|
|
127
|
+
default: DEFAULT_FORMAT
|
|
128
|
+
},
|
|
129
|
+
prefix: {
|
|
130
|
+
type: String,
|
|
131
|
+
default: ''
|
|
132
|
+
},
|
|
133
|
+
suffix: {
|
|
134
|
+
type: String,
|
|
135
|
+
default: ''
|
|
136
|
+
},
|
|
137
|
+
emptyText: {
|
|
138
|
+
type: String,
|
|
139
|
+
default: '-'
|
|
140
|
+
},
|
|
141
|
+
hideEmpty: {
|
|
142
|
+
type: Boolean,
|
|
143
|
+
default: false
|
|
144
|
+
},
|
|
145
|
+
interval: {
|
|
146
|
+
type: Number,
|
|
147
|
+
default: 1000,
|
|
148
|
+
validator: (val) => val >= 100
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// ===== text 模式 =====
|
|
152
|
+
// 兼容 v-model 用法,也可直接传 value / :value
|
|
153
|
+
value: {
|
|
154
|
+
type: [String, Number, Date],
|
|
155
|
+
default: ''
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// ===== countdown 模式 =====
|
|
159
|
+
targetTime: {
|
|
160
|
+
type: [String, Number, Date],
|
|
161
|
+
default: ''
|
|
162
|
+
},
|
|
163
|
+
// 倒计时显示格式:
|
|
164
|
+
// DHMS -> 3天 12时 08分 30秒
|
|
165
|
+
// HMS -> 12时 08分 30秒(天数自动折算到小时)
|
|
166
|
+
// MS -> 08分 30秒(全部折算到分钟)
|
|
167
|
+
// SEC -> 3200秒
|
|
168
|
+
countdownFormat: {
|
|
169
|
+
type: String,
|
|
170
|
+
default: 'DHMS',
|
|
171
|
+
validator: (val) => ['DHMS', 'HMS', 'MS', 'SEC'].includes(val)
|
|
172
|
+
},
|
|
173
|
+
finishedText: {
|
|
174
|
+
type: String,
|
|
175
|
+
default: '已结束'
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
data() {
|
|
180
|
+
return {
|
|
181
|
+
tick: 0
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
computed: {
|
|
186
|
+
mode() {
|
|
187
|
+
return this.type
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
parsedValue() {
|
|
191
|
+
return parseDate(this.value)
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
parsedTarget() {
|
|
195
|
+
return parseDate(this.targetTime)
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
displayText() {
|
|
199
|
+
if (!this.parsedValue) return undefined
|
|
200
|
+
return formatDateTime(this.parsedValue, this.format)
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
nowText() {
|
|
204
|
+
// 通过 tick 触发刷新
|
|
205
|
+
// eslint-disable-next-line no-unused-vars
|
|
206
|
+
const _ = this.tick
|
|
207
|
+
return formatDateTime(new Date(), this.format)
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// 剩余毫秒数(countdown 模式)
|
|
211
|
+
remainingMs() {
|
|
212
|
+
if (this.mode !== 'countdown') return 0
|
|
213
|
+
// eslint-disable-next-line no-unused-vars
|
|
214
|
+
const _ = this.tick
|
|
215
|
+
if (!this.parsedTarget) return 0
|
|
216
|
+
const diff = this.parsedTarget.getTime() - Date.now()
|
|
217
|
+
return diff > 0 ? diff : 0
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
isCountdownFinished() {
|
|
221
|
+
return this.mode === 'countdown' && this.parsedTarget && this.remainingMs <= 0
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
countdownText() {
|
|
225
|
+
if (!this.parsedTarget) return this.emptyText
|
|
226
|
+
const total = Math.floor(this.remainingMs / 1000)
|
|
227
|
+
if (total <= 0) return this.finishedText
|
|
228
|
+
|
|
229
|
+
switch (this.countdownFormat) {
|
|
230
|
+
case 'SEC':
|
|
231
|
+
return `${total}秒`
|
|
232
|
+
case 'MS': {
|
|
233
|
+
const m = Math.floor(total / 60)
|
|
234
|
+
const s = total % 60
|
|
235
|
+
return `${pad(m)}分${pad(s)}秒`
|
|
236
|
+
}
|
|
237
|
+
case 'HMS': {
|
|
238
|
+
const h = Math.floor(total / 3600)
|
|
239
|
+
const m = Math.floor((total % 3600) / 60)
|
|
240
|
+
const s = total % 60
|
|
241
|
+
return `${pad(h)}时${pad(m)}分${pad(s)}秒`
|
|
242
|
+
}
|
|
243
|
+
case 'DHMS':
|
|
244
|
+
default: {
|
|
245
|
+
const d = Math.floor(total / 86400)
|
|
246
|
+
const h = Math.floor((total % 86400) / 3600)
|
|
247
|
+
const m = Math.floor((total % 3600) / 60)
|
|
248
|
+
const s = total % 60
|
|
249
|
+
return `${d}天${pad(h)}时${pad(m)}分${pad(s)}秒`
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
customStyle() {
|
|
255
|
+
return {}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
watch: {
|
|
260
|
+
type() {
|
|
261
|
+
this.restartTimer()
|
|
262
|
+
},
|
|
263
|
+
interval() {
|
|
264
|
+
this.restartTimer()
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
mounted() {
|
|
269
|
+
this.restartTimer()
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
beforeDestroy() {
|
|
273
|
+
this.clearTimer()
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
activated() {
|
|
277
|
+
this.restartTimer()
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
deactivated() {
|
|
281
|
+
this.clearTimer()
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
methods: {
|
|
285
|
+
restartTimer() {
|
|
286
|
+
this.clearTimer()
|
|
287
|
+
if (this.mode === 'now' || this.mode === 'countdown') {
|
|
288
|
+
// 立即刷新一次,避免首次渲染时 tick=0 导致的差异
|
|
289
|
+
this.tick++
|
|
290
|
+
this._timer = setInterval(() => {
|
|
291
|
+
this.tick++
|
|
292
|
+
// 倒计时结束触发 finish 事件
|
|
293
|
+
if (this.mode === 'countdown' && this.parsedTarget && this.remainingMs <= 0) {
|
|
294
|
+
this.clearTimer()
|
|
295
|
+
this.$emit('finish')
|
|
296
|
+
}
|
|
297
|
+
}, this.interval)
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
clearTimer() {
|
|
302
|
+
if (this._timer) {
|
|
303
|
+
clearInterval(this._timer)
|
|
304
|
+
this._timer = null
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
handleClick(e) {
|
|
309
|
+
this.$emit('click', e)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.xt-time {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
color: var(--xt-text-color, #303133);
|
|
4
|
+
font-variant-numeric: tabular-nums;
|
|
5
|
+
transition: color 0.2s ease;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* 字号(同步 XtText 的 CSS 变量方案,若未定义则使用 fallback) */
|
|
9
|
+
.xt-time--extra-large { font-size: var(--xt-font-size-extra-large, 20px); }
|
|
10
|
+
.xt-time--large { font-size: var(--xt-font-size-large, 18px); }
|
|
11
|
+
.xt-time--medium { font-size: var(--xt-font-size-medium, 16px); }
|
|
12
|
+
.xt-time--base { font-size: var(--xt-font-size-base, 14px); }
|
|
13
|
+
.xt-time--small { font-size: var(--xt-font-size-small, 13px); }
|
|
14
|
+
.xt-time--extra-small { font-size: var(--xt-font-size-extra-small, 12px); }
|
|
15
|
+
|
|
16
|
+
/* 加粗 */
|
|
17
|
+
.xt-time--bold { font-weight: 700; }
|
|
18
|
+
|
|
19
|
+
/* 颜色语义:与 Element UI/XT 主题色一致 */
|
|
20
|
+
.xt-time--primary { color: var(--xt-color-primary, #409eff); }
|
|
21
|
+
.xt-time--success { color: var(--xt-color-success, #67c23a); }
|
|
22
|
+
.xt-time--warning { color: var(--xt-color-warning, #e6a23c); }
|
|
23
|
+
.xt-time--danger { color: var(--xt-color-danger, #f56c6c); }
|
package/src/index.js
CHANGED
|
@@ -23,9 +23,12 @@ import XtCard from './components/xt-card'
|
|
|
23
23
|
import XtCardItem from './components/xt-card-item'
|
|
24
24
|
import XtConfigProvider from './components/xt-config-provider'
|
|
25
25
|
import XtText from './components/xt-text'
|
|
26
|
+
import XtTime from './components/xt-time'
|
|
27
|
+
import XtStepPrice from './components/xt-step-price'
|
|
28
|
+
import XtStepPriceItem from './components/xt-step-price-item'
|
|
26
29
|
import XtGridBox from './components/xt-grid-box'
|
|
27
30
|
import XtGridItem from './components/xt-grid-item'
|
|
28
|
-
import
|
|
31
|
+
import ExDatePicker from './components/ex-date-picker'
|
|
29
32
|
import ExButton from './components/ex-button'
|
|
30
33
|
import ExChart from './components/ex-chart' // ExChart 组件(基于 ECharts 封装)
|
|
31
34
|
import ExCard from './components/ex-card'
|
|
@@ -44,9 +47,12 @@ const components = [
|
|
|
44
47
|
XtCardItem,
|
|
45
48
|
XtConfigProvider,
|
|
46
49
|
XtText,
|
|
50
|
+
XtTime,
|
|
51
|
+
XtStepPrice,
|
|
52
|
+
XtStepPriceItem,
|
|
47
53
|
XtGridBox,
|
|
48
54
|
XtGridItem,
|
|
49
|
-
|
|
55
|
+
ExDatePicker,
|
|
50
56
|
ExButton,
|
|
51
57
|
ExChart,
|
|
52
58
|
ExCard,
|
|
@@ -131,9 +137,12 @@ export default {
|
|
|
131
137
|
XtCardItem,
|
|
132
138
|
XtConfigProvider,
|
|
133
139
|
XtText,
|
|
140
|
+
XtTime,
|
|
141
|
+
XtStepPrice,
|
|
142
|
+
XtStepPriceItem,
|
|
134
143
|
XtGridBox,
|
|
135
144
|
XtGridItem,
|
|
136
|
-
|
|
145
|
+
ExDatePicker,
|
|
137
146
|
ExButton,
|
|
138
147
|
ExCard,
|
|
139
148
|
ExIcon,
|
|
File without changes
|
|
File without changes
|