feat:下拉框组件更换,用uniapp官方的

master
黄伟杰 1 month ago
parent 853472a7da
commit 6ff98266bc

@ -44,10 +44,12 @@
<view class="rate-trend-section"> <view class="rate-trend-section">
<view class="rate-trend-header"> <view class="rate-trend-header">
<text class="rate-trend-title">{{ t('deviceOverview.rateTrend') }}</text> <text class="rate-trend-title">{{ t('deviceOverview.rateTrend') }}</text>
<view class="filter-select" @click="showPeriodPicker = true"> <picker mode="selector" :range="periodRange" range-key="text" :value="periodIndex" @change="onPeriodChange">
<view class="filter-select">
<text class="filter-text">{{ currentPeriodLabel }}</text> <text class="filter-text">{{ currentPeriodLabel }}</text>
<text class="filter-arrow"></text> <text class="filter-arrow"></text>
</view> </view>
</picker>
</view> </view>
<view class="switch-bar"> <view class="switch-bar">
<view class="switch-item"> <view class="switch-item">
@ -80,10 +82,12 @@
<view class="rate-trend-section"> <view class="rate-trend-section">
<view class="rate-trend-header"> <view class="rate-trend-header">
<text class="rate-trend-title">{{ t('deviceOverview.deviceRateTrend') }}</text> <text class="rate-trend-title">{{ t('deviceOverview.deviceRateTrend') }}</text>
<view class="filter-select" @click="showDevicePicker = true"> <picker mode="selector" :range="deviceRange" range-key="text" :value="deviceIndex" @change="onDeviceChange">
<view class="filter-select">
<text class="filter-text">{{ selectedDeviceName || t('deviceOverview.selectDevice') }}</text> <text class="filter-text">{{ selectedDeviceName || t('deviceOverview.selectDevice') }}</text>
<text class="filter-arrow"></text> <text class="filter-arrow"></text>
</view> </view>
</picker>
</view> </view>
<view class="device-trend-chart-box" v-if="selectedDeviceId"> <view class="device-trend-chart-box" v-if="selectedDeviceId">
<qiun-data-charts type="bar" :chartData="deviceTrendChartData" :canvas2d="false" :opts="deviceTrendChartOpts" /> <qiun-data-charts type="bar" :chartData="deviceTrendChartData" :canvas2d="false" :opts="deviceTrendChartOpts" />
@ -93,10 +97,6 @@
</view> </view>
</view> </view>
<up-picker :show="showPeriodPicker" :columns="periodColumns" @confirm="onPeriodConfirm"
@cancel="showPeriodPicker = false" @close="showPeriodPicker = false" closeOnClickOverlay />
<up-picker :show="showDevicePicker" :columns="deviceColumns" @confirm="onDeviceConfirm"
@cancel="showDevicePicker = false" @close="showDevicePicker = false" closeOnClickOverlay />
</view> </view>
</template> </template>
@ -118,21 +118,23 @@ const deviceData = reactive({
faultRate: '-' faultRate: '-'
}) })
const showPeriodPicker = ref(false)
const currentPeriod = ref('LAST_7_DAYS') const currentPeriod = ref('LAST_7_DAYS')
const onlyScheduled = ref(true) const onlyScheduled = ref(true)
const skipHoliday = ref(false) const skipHoliday = ref(false)
const periodColumns = computed(() => [ const periodRange = computed(() => [
[
{ text: t('deviceOverview.periodLastWeek'), value: 'LAST_WEEK' }, { text: t('deviceOverview.periodLastWeek'), value: 'LAST_WEEK' },
{ text: t('deviceOverview.periodThisWeek'), value: 'THIS_WEEK' }, { text: t('deviceOverview.periodThisWeek'), value: 'THIS_WEEK' },
{ text: t('deviceOverview.periodLast7Days'), value: 'LAST_7_DAYS' }, { text: t('deviceOverview.periodLast7Days'), value: 'LAST_7_DAYS' },
{ text: t('deviceOverview.periodLastMonth'), value: 'LAST_MONTH' }, { text: t('deviceOverview.periodLastMonth'), value: 'LAST_MONTH' },
{ text: t('deviceOverview.periodThisMonth'), value: 'THIS_MONTH' } { text: t('deviceOverview.periodThisMonth'), value: 'THIS_MONTH' }
]
]) ])
const periodIndex = computed(() => {
const idx = periodRange.value.findIndex(item => item.value === currentPeriod.value)
return idx >= 0 ? idx : 0
})
const periodLabelMap = computed(() => ({ const periodLabelMap = computed(() => ({
LAST_WEEK: t('deviceOverview.periodLastWeek'), LAST_WEEK: t('deviceOverview.periodLastWeek'),
THIS_WEEK: t('deviceOverview.periodThisWeek'), THIS_WEEK: t('deviceOverview.periodThisWeek'),
@ -200,15 +202,19 @@ const rankingChartHeight = computed(() => {
return `${count * ITEM_HEIGHT_PX + 30}px` return `${count * ITEM_HEIGHT_PX + 30}px`
}) })
const showDevicePicker = ref(false)
const deviceList = ref([]) const deviceList = ref([])
const selectedDeviceId = ref(null) const selectedDeviceId = ref(null)
const selectedDeviceName = ref('') const selectedDeviceName = ref('')
const isInitialLoad = ref(true) const isInitialLoad = ref(true)
const deviceColumns = computed(() => [ const deviceRange = computed(() =>
deviceList.value.map((d) => ({ text: d.deviceName || d.name || '', value: d.id })) deviceList.value.map((d) => ({ text: d.deviceName || d.name || '', value: d.id }))
]) )
const deviceIndex = computed(() => {
const idx = deviceRange.value.findIndex(item => item.value === selectedDeviceId.value)
return idx >= 0 ? idx : 0
})
const deviceTrendChartOpts = { const deviceTrendChartOpts = {
color: ['#1a3a5c', '#18bc37'], color: ['#1a3a5c', '#18bc37'],
@ -347,18 +353,20 @@ async function loadDeviceRateTrend() {
] ]
} }
function onDeviceConfirm(e) { function onDeviceChange(e) {
const val = e.value[0] const idx = e.detail.value
selectedDeviceId.value = val.value const item = deviceRange.value[idx]
selectedDeviceName.value = val.text if (!item) return
showDevicePicker.value = false selectedDeviceId.value = item.value
selectedDeviceName.value = item.text
loadDeviceRateTrend() loadDeviceRateTrend()
} }
function onPeriodConfirm(e) { function onPeriodChange(e) {
const val = e.value[0]?.value || e.value[0] const idx = e.detail.value
const val = periodRange.value[idx]?.value
if (!val) return
currentPeriod.value = val currentPeriod.value = val
showPeriodPicker.value = false
loadRateTrend() loadRateTrend()
} }

@ -7,27 +7,27 @@
</text> </text>
</view> </view>
<view class="filter-bar"> <view class="filter-bar">
<view class="filter-select" @click="showFilterPicker = true"> <picker mode="selector" :range="filterRange" range-key="text" :value="filterIndex" @change="onFilterChange">
<view class="filter-select">
<text class="filter-text">{{ currentFilterLabel }}</text> <text class="filter-text">{{ currentFilterLabel }}</text>
<text class="filter-arrow"></text> <text class="filter-arrow"></text>
</view> </view>
<view class="filter-select" @click="showRangePicker = true"> </picker>
<picker mode="selector" :range="rangeRange" range-key="text" :value="rangeIndex" @change="onRangeChange">
<view class="filter-select">
<text class="filter-text">{{ currentRangeLabel }}</text> <text class="filter-text">{{ currentRangeLabel }}</text>
<text class="filter-arrow"></text> <text class="filter-arrow"></text>
</view> </view>
</picker>
</view> </view>
<view v-if="currentRange === 'custom'" class="filter-date-wrap"> <view v-if="currentRange === 'custom'" class="filter-date-wrap">
<view class="date-picker-item"> <view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" /> <uni-datetime-picker v-model="dateRange.start" type="datetime"
<up-datetime-picker ref="startPickerRef" hasInput v-model="dateRange.start" mode="datetime" :placeholder="t('dashboard.startDate')" @change="onStartDateChange" />
:placeholder="t('dashboard.startDate')" :formatter="datetimeFormatter" closeOnClickOverlay
@confirm="onStartDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view> </view>
<view class="date-picker-item"> <view class="date-picker-item">
<up-icon name="calendar" size="18" color="#999" /> <uni-datetime-picker v-model="dateRange.end" type="datetime"
<up-datetime-picker ref="endPickerRef" hasInput v-model="dateRange.end" mode="datetime" :placeholder="t('dashboard.endDate')" @change="onEndDateChange" />
:placeholder="t('dashboard.endDate')" :formatter="datetimeFormatter" closeOnClickOverlay
@confirm="onEndDateConfirm" @close="onPickerClose" @cancel="onPickerClose" />
</view> </view>
</view> </view>
<view v-if="currentFilter === 'product'" class="trend-content"> <view v-if="currentFilter === 'product'" class="trend-content">
@ -92,11 +92,6 @@
</view> </view>
</view> </view>
<up-picker :show="showFilterPicker" :columns="filterColumns" @confirm="onFilterConfirm"
@cancel="showFilterPicker = false" @close="showFilterPicker = false" closeOnClickOverlay />
<up-picker :show="showRangePicker" :columns="rangeColumns" @confirm="onRangeConfirm"
@cancel="showRangePicker = false" @close="showRangePicker = false" closeOnClickOverlay />
</view> </view>
</template> </template>
@ -126,35 +121,37 @@ const passRateChartOpts = {
extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } } extra: { line: { type: 'straight', width: 2, activeType: 'hollow' } }
} }
const showFilterPicker = ref(false)
const showRangePicker = ref(false)
const startPickerRef = ref(null)
const endPickerRef = ref(null)
const currentFilter = ref('task') const currentFilter = ref('task')
const currentRange = ref('year') const currentRange = ref('year')
const isInitialLoad = ref(true) const isInitialLoad = ref(true)
const filterColumns = computed(() => [ const filterRange = computed(() => [
[
{ text: t('dashboard.filterTask'), value: 'task' }, { text: t('dashboard.filterTask'), value: 'task' },
{ text: t('dashboard.filterProduct'), value: 'product' } { text: t('dashboard.filterProduct'), value: 'product' }
]
]) ])
const filterIndex = computed(() => {
const idx = filterRange.value.findIndex(item => item.value === currentFilter.value)
return idx >= 0 ? idx : 0
})
const currentFilterLabel = computed(() => { const currentFilterLabel = computed(() => {
return currentFilter.value === 'task' ? t('dashboard.filterTask') : t('dashboard.filterProduct') return currentFilter.value === 'task' ? t('dashboard.filterTask') : t('dashboard.filterProduct')
}) })
const rangeColumns = computed(() => [ const rangeRange = computed(() => [
[
{ text: t('dashboard.rangeYear'), value: 'year' }, { text: t('dashboard.rangeYear'), value: 'year' },
{ text: t('dashboard.rangeMonth'), value: 'month' }, { text: t('dashboard.rangeMonth'), value: 'month' },
{ text: t('dashboard.rangeWeek'), value: 'week' }, { text: t('dashboard.rangeWeek'), value: 'week' },
{ text: t('dashboard.rangeToday'), value: 'today' }, { text: t('dashboard.rangeToday'), value: 'today' },
{ text: t('dashboard.rangeCustom'), value: 'custom' } { text: t('dashboard.rangeCustom'), value: 'custom' }
]
]) ])
const rangeIndex = computed(() => {
const idx = rangeRange.value.findIndex(item => item.value === currentRange.value)
return idx >= 0 ? idx : 0
})
const rangeLabelMap = computed(() => ({ const rangeLabelMap = computed(() => ({
year: t('dashboard.rangeYear'), year: t('dashboard.rangeYear'),
month: t('dashboard.rangeMonth'), month: t('dashboard.rangeMonth'),
@ -292,21 +289,11 @@ function transformChartData(dayTrend, rangeType) {
} }
} }
function formatTimestamp(ts) { function onFilterChange(e) {
const d = new Date(ts) const idx = e.detail.value
const pad = (n) => String(n).padStart(2, '0') const val = filterRange.value[idx]?.value
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}` if (!val) return
}
function datetimeFormatter(type, value) {
const unitMap = { year: '年', month: '月', day: '日', hour: '时', minute: '分' }
return value + (unitMap[type] || '')
}
function onFilterConfirm(e) {
const val = e.value[0]?.value || e.value[0]
currentFilter.value = val currentFilter.value = val
showFilterPicker.value = false
if (val === 'product') { if (val === 'product') {
loadTrendData() loadTrendData()
} else if (val === 'task') { } else if (val === 'task') {
@ -314,10 +301,11 @@ function onFilterConfirm(e) {
} }
} }
function onRangeConfirm(e) { function onRangeChange(e) {
const val = e.value[0]?.value || e.value[0] const idx = e.detail.value
const val = rangeRange.value[idx]?.value
if (!val) return
currentRange.value = val currentRange.value = val
showRangePicker.value = false
if (val !== 'custom') { if (val !== 'custom') {
const range = getDateRange(val) const range = getDateRange(val)
dateRange.start = range.start dateRange.start = range.start
@ -354,31 +342,18 @@ function clearChartData() {
taskChartData.series = [{ name: t('dashboard.taskTrend'), data: [] }] taskChartData.series = [{ name: t('dashboard.taskTrend'), data: [] }]
} }
function onStartDateConfirm(e) { function onStartDateChange(val) {
dateRange.start = formatTimestamp(e.value) dateRange.start = val
if (dateRange.start && dateRange.end) { if (dateRange.start && dateRange.end) {
if (currentFilter.value === 'product') { loadData()
loadTrendData()
} else if (currentFilter.value === 'task') {
loadTaskTrendData()
}
} }
} }
function onEndDateConfirm(e) { function onEndDateChange(val) {
dateRange.end = formatTimestamp(e.value) dateRange.end = val
if (dateRange.start && dateRange.end) { if (dateRange.start && dateRange.end) {
if (currentFilter.value === 'product') { loadData()
loadTrendData()
} else if (currentFilter.value === 'task') {
loadTaskTrendData()
} }
}
}
function onPickerClose() {
if (startPickerRef.value) startPickerRef.value.showByClickInput = false
if (endPickerRef.value) endPickerRef.value.showByClickInput = false
} }
async function loadTrendData() { async function loadTrendData() {
@ -585,21 +560,20 @@ defineExpose({ loadData })
background: #ffffff; background: #ffffff;
} }
:deep(.u-datetime-picker) { :deep(.uni-date) {
flex: 1; flex: 1;
width: 100%;
.u-input { .uni-date-editor--x {
padding-left: 40rpx;
background: transparent;
border: none; border: none;
} background: transparent;
padding: 0;
} }
.up-icon { .uni-date__x-input {
position: absolute; background: transparent;
left: 12rpx; font-size: 24rpx;
z-index: 1; }
color: #4a90c2;
} }
} }

@ -89,7 +89,7 @@ onUnmounted(() => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(26, 58, 92, 0.3); box-shadow: 0 8rpx 24rpx rgba(26, 58, 92, 0.3);
z-index: 999; z-index: 10;
&:active { &:active {
transform: scale(0.95); transform: scale(0.95);

Loading…
Cancel
Save