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.
@@ -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 XtDatePicker from './components/xt-date-picker'
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
- XtDatePicker,
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
- XtDatePicker,
145
+ ExDatePicker,
137
146
  ExButton,
138
147
  ExCard,
139
148
  ExIcon,
@@ -1,2 +0,0 @@
1
- import XtDatePicker from './index.vue'
2
- export default XtDatePicker