improve(ui):日期时间选择器优化
This commit is contained in:
1 parent
fb8f96217d
commit
a16436fecc
2 files changed
+59
-25
No files matched your search
@@ -393,7 +393,7 @@ cmd(路由注册)
|
||||
|
||||
- Vue 3 Composition API + `<script setup>` 语法
|
||||
- **管理后台**:使用 **Arco Design Vue** 组件库(`@arco-design/web-vue`),快速构建数据密集型页面(表格、表单、图表等),可参考 [Soybean Admin](https://github.com/soybeanjs/soybean-admin) 的架构设计(路由、权限、布局)
|
||||
- **租户控制台**:TailwindCSS 原子类 + 自建组件,面向客户,注重品牌调性和视觉差异化
|
||||
- **租户控制台**:TailwindCSS 原子类 + **Naive UI 组件库**(`naive-ui`,全量引入 + `n-config-provider` 主题对齐主色 `#14b8a6`)。表格用 `NDataTable`、表单用 `NForm`/`NInput`/`NInputNumber`/`NSelect`/`NCheckbox`/`NModal`;保留自研组件:`SlideCaptcha`(滑块验证码)、`Turnstile`(Cloudflare)、`Icon`(图标)、`MarkdownRenderer`。公共渲染工具在 `src/utils/renderUtils.ts`(`renderBadge`/`formatMoney`/`formatDate`/`formatTokens`/`formatMs`)。面向客户,注重品牌调性和视觉差异化
|
||||
- TailwindCSS 作为基础样式层,两个控制台共用(注意与 Arco 组件内置样式共存,避免全局 reset 冲突)
|
||||
- 状态管理用 Pinia
|
||||
- HTTP 请求封装 Axios,JWT 自动刷新,统一错误处理
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
// 日期时间范围选择器:开始/结束各一个 datetime-local 输入(支持到分钟精度)。
|
||||
// 日期时间范围选择器:基于 Naive NDatePicker(datetimerange)+ 快捷选择按钮(今日/昨天/近3天/近一周)。
|
||||
// 对外暴露 v-model:start / v-model:end,值为后端格式字符串 YYYY-MM-DD HH:mm:ss(结束留空 = 到现在)。
|
||||
const props = defineProps<{
|
||||
start?: string
|
||||
@@ -12,33 +12,67 @@ const emit = defineEmits<{
|
||||
'update:end': [value: string]
|
||||
}>()
|
||||
|
||||
// datetime-local 值(YYYY-MM-DDTHH:mm)与后端值(YYYY-MM-DD HH:mm:ss)互转
|
||||
function toInputValue(v: string | undefined): string {
|
||||
if (!v) return ''
|
||||
return v.replace(' ', 'T').slice(0, 16)
|
||||
// timestamp(ms) ↔ 后端字符串 YYYY-MM-DD HH:mm:ss
|
||||
function tsToStr(ts: number): string {
|
||||
const d = new Date(ts)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
function toBackendValue(v: string): string {
|
||||
if (!v) return ''
|
||||
const s = v.replace('T', ' ')
|
||||
return s.length === 16 ? s + ':00' : s
|
||||
function strToTs(v: string | undefined): number | null {
|
||||
if (!v) return null
|
||||
const t = new Date(v.replace(' ', 'T')).getTime()
|
||||
return Number.isNaN(t) ? null : t
|
||||
}
|
||||
|
||||
const startInput = computed({
|
||||
get: () => toInputValue(props.start),
|
||||
set: (v: string) => emit('update:start', toBackendValue(v)),
|
||||
})
|
||||
const endInput = computed({
|
||||
get: () => toInputValue(props.end),
|
||||
set: (v: string) => emit('update:end', toBackendValue(v)),
|
||||
// NDatePicker 显示值:仅当 start/end 均有效时返回 [s, e]
|
||||
const rangeValue = computed<[number, number] | null>(() => {
|
||||
const s = strToTs(props.start)
|
||||
const e = strToTs(props.end)
|
||||
return s != null && e != null ? [s, e] : null
|
||||
})
|
||||
|
||||
function onRangeChange(v: [number, number] | null) {
|
||||
if (v) {
|
||||
emit('update:start', tsToStr(v[0]))
|
||||
emit('update:end', tsToStr(v[1]))
|
||||
} else {
|
||||
emit('update:start', '')
|
||||
emit('update:end', '')
|
||||
}
|
||||
}
|
||||
|
||||
// 以本地时区"天"为粒度的快捷范围
|
||||
function dayStart(offsetDays: number): number {
|
||||
const d = new Date()
|
||||
d.setDate(d.getDate() + offsetDays)
|
||||
d.setHours(0, 0, 0, 0)
|
||||
return d.getTime()
|
||||
}
|
||||
function dayEnd(offsetDays: number): number {
|
||||
const d = new Date()
|
||||
d.setDate(d.getDate() + offsetDays)
|
||||
d.setHours(23, 59, 59, 999)
|
||||
return d.getTime()
|
||||
}
|
||||
|
||||
// NDatePicker 面板内的快捷选项(点击后自动设置 start/end)
|
||||
// 键为面板内显示的标签,值为 [startTs, endTs] 或返回该元组的函数
|
||||
const shortcuts = {
|
||||
今日: () => [dayStart(0), dayEnd(0)] as [number, number],
|
||||
昨天: () => [dayStart(-1), dayEnd(-1)] as [number, number],
|
||||
近3天: () => [dayStart(-2), dayEnd(0)] as [number, number],
|
||||
近一周: () => [dayStart(-6), dayEnd(0)] as [number, number],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-sm text-gray-500 whitespace-nowrap">开始时间</label>
|
||||
<input v-model="startInput" type="datetime-local" class="input" style="width:190px" />
|
||||
<span class="text-gray-400 select-none">~</span>
|
||||
<label class="text-sm text-gray-500 whitespace-nowrap">结束时间</label>
|
||||
<input v-model="endInput" type="datetime-local" class="input" style="width:190px" />
|
||||
</div>
|
||||
</template>
|
||||
<n-date-picker
|
||||
type="datetimerange"
|
||||
:value="rangeValue"
|
||||
:shortcuts="shortcuts"
|
||||
:clearable="true"
|
||||
:actions="['clear', 'confirm']"
|
||||
style="width: 400px"
|
||||
@update:value="onRangeChange"
|
||||
/>
|
||||
</template>
|
||||
Reference in new issue
Block a user