|
|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
<NavBar :title="t('deviceOverview.title')" />
|
|
|
|
|
|
|
|
|
|
<view class="summary-wrap">
|
|
|
|
|
<view class="section-heading">设备状态</view>
|
|
|
|
|
<view class="summary-grid">
|
|
|
|
|
<view
|
|
|
|
|
v-for="item in statisticCards"
|
|
|
|
|
@ -20,14 +21,74 @@
|
|
|
|
|
<text class="stat-label">{{ item.label }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="refresh-row" @click="fetchOverview">
|
|
|
|
|
<uni-icons type="refresh" size="18" color="#64748b"></uni-icons>
|
|
|
|
|
<text class="refresh-text">刷新数据</text>
|
|
|
|
|
<uni-popup ref="filterPopupRef" class="overview-filter-popup" type="right" background-color="transparent" :animation="false">
|
|
|
|
|
<view class="filter-drawer">
|
|
|
|
|
<view class="drawer-header">
|
|
|
|
|
<text class="drawer-title">更多筛选</text>
|
|
|
|
|
</view>
|
|
|
|
|
<scroll-view scroll-y class="drawer-body">
|
|
|
|
|
<view class="drawer-section drawer-fields">
|
|
|
|
|
<view class="drawer-field">
|
|
|
|
|
<text class="drawer-label">设备分组</text>
|
|
|
|
|
<picker class="drawer-picker-host" :range="groupPickerLabels" :value="groupPickerIndex" @change="onGroupChange">
|
|
|
|
|
<view class="drawer-picker">
|
|
|
|
|
<text :class="['drawer-picker-text', runFilter.groupId === '' ? 'placeholder' : '']">{{ selectedGroupLabel }}</text>
|
|
|
|
|
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</picker>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="drawer-field">
|
|
|
|
|
<text class="drawer-label">设备名称</text>
|
|
|
|
|
<picker class="drawer-picker-host" :range="devicePickerLabels" :value="devicePickerIndex" @change="onDeviceFilterChange">
|
|
|
|
|
<view class="drawer-picker">
|
|
|
|
|
<text :class="['drawer-picker-text', runFilter.deviceId === '' ? 'placeholder' : '']">{{ selectedDeviceLabel }}</text>
|
|
|
|
|
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</picker>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="drawer-field drawer-field-wide">
|
|
|
|
|
<text class="drawer-label">时间范围</text>
|
|
|
|
|
<view class="drawer-date">
|
|
|
|
|
<uni-datetime-picker
|
|
|
|
|
v-model="runFilter.timeRange"
|
|
|
|
|
type="datetimerange"
|
|
|
|
|
:clear-icon="true"
|
|
|
|
|
start-placeholder="开始时间"
|
|
|
|
|
end-placeholder="结束时间"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="drawer-field drawer-field-column">
|
|
|
|
|
<text class="drawer-label">快捷日期</text>
|
|
|
|
|
<view class="quick-range-row">
|
|
|
|
|
<view
|
|
|
|
|
v-for="item in quickRanges"
|
|
|
|
|
:key="item.value"
|
|
|
|
|
:class="['quick-range-btn', runFilter.quickRange === item.value ? 'active' : '']"
|
|
|
|
|
@click="handleQuickRangeChange(item.value)"
|
|
|
|
|
>
|
|
|
|
|
{{ item.label }}
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
<view class="drawer-actions">
|
|
|
|
|
<view class="drawer-action reset" @click="resetRunFilter">重置</view>
|
|
|
|
|
<view class="drawer-action confirm" @click="confirmFilterDrawer">确定</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</uni-popup>
|
|
|
|
|
|
|
|
|
|
<view class="operation-wrap">
|
|
|
|
|
<view class="section-heading-row">
|
|
|
|
|
<view class="section-heading operation-heading">设备运行</view>
|
|
|
|
|
<view class="operation-filter-btn" @click="openFilterDrawer">
|
|
|
|
|
<uni-icons type="settings" size="22" color="#7b8491"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="metric-grid">
|
|
|
|
|
<view v-for="item in operationMetrics" :key="item.key" class="metric-card">
|
|
|
|
|
<view class="metric-icon" :style="{ background: item.bgColor }">
|
|
|
|
|
@ -83,13 +144,22 @@ import { computed, reactive, ref } from 'vue'
|
|
|
|
|
import { onLoad, onPullDownRefresh, onShow } from '@dcloudio/uni-app'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
import NavBar from '@/components/common/NavBar.vue'
|
|
|
|
|
import { getDeviceRunOverview, getIotDevicePage } from '@/api/iot/device'
|
|
|
|
|
import { getDeviceRunOverview, getIotDevicePage, getRunOverviewOrganizationList } from '@/api/iot/device'
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const loadFailed = ref(false)
|
|
|
|
|
const initialized = ref(false)
|
|
|
|
|
const filterPopupRef = ref(null)
|
|
|
|
|
const organizationList = ref([])
|
|
|
|
|
const allDeviceRows = ref([])
|
|
|
|
|
const runFilter = reactive({
|
|
|
|
|
groupId: '',
|
|
|
|
|
deviceId: '',
|
|
|
|
|
quickRange: 'today',
|
|
|
|
|
timeRange: createDateRangeByQuickKey('today')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const statistics = reactive({
|
|
|
|
|
total: 0,
|
|
|
|
|
@ -120,6 +190,47 @@ const statusConfig = {
|
|
|
|
|
offline: { label: '离线', color: '#cfd4df' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const quickRanges = computed(() => [
|
|
|
|
|
{ label: '今天', value: 'today' },
|
|
|
|
|
{ label: '昨天', value: 'yesterday' },
|
|
|
|
|
{ label: '近7天', value: 'last7Days' },
|
|
|
|
|
{ label: '近30天', value: 'last30Days' }
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const groupPickerOptions = computed(() => [
|
|
|
|
|
{ id: '', name: '设备分组' },
|
|
|
|
|
...organizationList.value
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const groupPickerLabels = computed(() => groupPickerOptions.value.map((item) => item.name || '-'))
|
|
|
|
|
|
|
|
|
|
const groupPickerIndex = computed(() => {
|
|
|
|
|
const index = groupPickerOptions.value.findIndex((item) => String(item.id) === String(runFilter.groupId))
|
|
|
|
|
return index >= 0 ? index : 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const selectedGroupLabel = computed(() => {
|
|
|
|
|
const found = groupPickerOptions.value.find((item) => String(item.id) === String(runFilter.groupId))
|
|
|
|
|
return found?.name || '设备分组'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const devicePickerOptions = computed(() => [
|
|
|
|
|
{ value: '', label: '设备名称' },
|
|
|
|
|
...collectDeviceOptionsByGroup(runFilter.groupId)
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const devicePickerLabels = computed(() => devicePickerOptions.value.map((item) => item.label || '-'))
|
|
|
|
|
|
|
|
|
|
const devicePickerIndex = computed(() => {
|
|
|
|
|
const index = devicePickerOptions.value.findIndex((item) => String(item.value) === String(runFilter.deviceId))
|
|
|
|
|
return index >= 0 ? index : 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const selectedDeviceLabel = computed(() => {
|
|
|
|
|
const found = devicePickerOptions.value.find((item) => String(item.value) === String(runFilter.deviceId))
|
|
|
|
|
return found?.label || '设备名称'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const statisticCards = computed(() => [
|
|
|
|
|
{
|
|
|
|
|
key: 'total',
|
|
|
|
|
@ -205,7 +316,7 @@ const donutStyle = computed(() => {
|
|
|
|
|
return { background: 'conic-gradient(' + segments.join(', ') + ')' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onLoad(fetchOverview)
|
|
|
|
|
onLoad(initPage)
|
|
|
|
|
|
|
|
|
|
onShow(() => {
|
|
|
|
|
if (initialized.value) fetchOverview(false)
|
|
|
|
|
@ -216,6 +327,11 @@ onPullDownRefresh(async () => {
|
|
|
|
|
uni.stopPullDownRefresh()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async function initPage() {
|
|
|
|
|
await loadOrganizations()
|
|
|
|
|
await fetchOverview()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchOverview(showLoading = true) {
|
|
|
|
|
if (loading.value) return
|
|
|
|
|
if (showLoading) loading.value = true
|
|
|
|
|
@ -231,11 +347,14 @@ async function fetchOverview(showLoading = true) {
|
|
|
|
|
})
|
|
|
|
|
rows = allPage.list
|
|
|
|
|
}
|
|
|
|
|
setStatistics(rows, total)
|
|
|
|
|
await fetchRunOverview(rows)
|
|
|
|
|
allDeviceRows.value = rows
|
|
|
|
|
const filteredRows = filterRowsByRunFilter(rows)
|
|
|
|
|
setStatistics(filteredRows, filteredRows.length)
|
|
|
|
|
await fetchRunOverview(filteredRows)
|
|
|
|
|
initialized.value = true
|
|
|
|
|
} catch (error) {
|
|
|
|
|
loadFailed.value = true
|
|
|
|
|
allDeviceRows.value = []
|
|
|
|
|
resetStatistics()
|
|
|
|
|
resetOperationOverview()
|
|
|
|
|
} finally {
|
|
|
|
|
@ -243,11 +362,32 @@ async function fetchOverview(showLoading = true) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadOrganizations() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getRunOverviewOrganizationList({})
|
|
|
|
|
organizationList.value = normalizeOrganizationList(res)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
organizationList.value = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function getDeviceRows(params) {
|
|
|
|
|
const res = await getIotDevicePage(params)
|
|
|
|
|
return normalizePageData(res)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function filterRowsByRunFilter(rows) {
|
|
|
|
|
if (runFilter.deviceId) {
|
|
|
|
|
return rows.filter((row) => String(row?.id) === String(runFilter.deviceId))
|
|
|
|
|
}
|
|
|
|
|
const deviceIds = getCurrentDeviceIds()
|
|
|
|
|
if (runFilter.groupId && deviceIds.length) {
|
|
|
|
|
const idSet = new Set(deviceIds.map((id) => String(id)))
|
|
|
|
|
return rows.filter((row) => idSet.has(String(row?.id)))
|
|
|
|
|
}
|
|
|
|
|
return rows
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setStatistics(rows, totalCount) {
|
|
|
|
|
const next = {
|
|
|
|
|
total: Number(totalCount || rows.length || 0),
|
|
|
|
|
@ -277,15 +417,12 @@ function resetStatistics() {
|
|
|
|
|
|
|
|
|
|
async function fetchRunOverview(rows) {
|
|
|
|
|
try {
|
|
|
|
|
const ids = rows
|
|
|
|
|
.map((item) => item?.id)
|
|
|
|
|
.filter((id) => id !== undefined && id !== null && String(id) !== '')
|
|
|
|
|
.join(',')
|
|
|
|
|
const range = buildTodayRange()
|
|
|
|
|
const ids = getRunOverviewDeviceIds(rows).join(',')
|
|
|
|
|
const range = normalizeTimeRange(runFilter.timeRange)
|
|
|
|
|
const res = await getDeviceRunOverview({
|
|
|
|
|
ids,
|
|
|
|
|
startTime: range.startTime,
|
|
|
|
|
endTime: range.endTime,
|
|
|
|
|
startTime: range[0],
|
|
|
|
|
endTime: range[1],
|
|
|
|
|
timelinePageNo: 1,
|
|
|
|
|
timelinePageSize: 1
|
|
|
|
|
})
|
|
|
|
|
@ -360,6 +497,123 @@ function buildFallbackSummary() {
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createDateRangeByQuickKey(key) {
|
|
|
|
|
const now = new Date()
|
|
|
|
|
const start = new Date(now)
|
|
|
|
|
const end = new Date(now)
|
|
|
|
|
if (key === 'yesterday') {
|
|
|
|
|
start.setDate(start.getDate() - 1)
|
|
|
|
|
end.setDate(end.getDate() - 1)
|
|
|
|
|
} else if (key === 'last7Days') {
|
|
|
|
|
start.setDate(start.getDate() - 6)
|
|
|
|
|
} else if (key === 'last30Days') {
|
|
|
|
|
start.setDate(start.getDate() - 29)
|
|
|
|
|
}
|
|
|
|
|
start.setHours(0, 0, 0, 0)
|
|
|
|
|
end.setHours(23, 59, 59, 999)
|
|
|
|
|
return [formatDateTime(start), formatDateTime(end)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeTimeRange(value) {
|
|
|
|
|
return Array.isArray(value) && value.length === 2 && value[0] && value[1]
|
|
|
|
|
? value
|
|
|
|
|
: createDateRangeByQuickKey('today')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRunOverviewDeviceIds(rows) {
|
|
|
|
|
const selectedIds = getCurrentDeviceIds()
|
|
|
|
|
if (selectedIds.length) return selectedIds
|
|
|
|
|
return rows
|
|
|
|
|
.map((item) => item?.id)
|
|
|
|
|
.filter((id) => id !== undefined && id !== null && String(id) !== '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getCurrentDeviceIds() {
|
|
|
|
|
if (runFilter.deviceId) return [runFilter.deviceId]
|
|
|
|
|
const options = collectDeviceOptionsByGroup(runFilter.groupId)
|
|
|
|
|
return options.map((item) => item.value).filter((value) => value !== undefined && value !== null && String(value) !== '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function collectDeviceOptionsByGroup(groupId) {
|
|
|
|
|
const deviceMap = new Map()
|
|
|
|
|
const pushDevice = (node) => {
|
|
|
|
|
if (!node?.dvId || !String(node.machineName || '').trim()) return
|
|
|
|
|
const id = String(node.dvId)
|
|
|
|
|
if (!deviceMap.has(id)) {
|
|
|
|
|
deviceMap.set(id, { label: String(node.machineName).trim(), value: id })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!groupId) {
|
|
|
|
|
organizationList.value.forEach(pushDevice)
|
|
|
|
|
return Array.from(deviceMap.values())
|
|
|
|
|
}
|
|
|
|
|
const childrenMap = organizationList.value.reduce((acc, item) => {
|
|
|
|
|
const parentKey = String(item.parentId ?? 0)
|
|
|
|
|
if (!acc[parentKey]) acc[parentKey] = []
|
|
|
|
|
acc[parentKey].push(item)
|
|
|
|
|
return acc
|
|
|
|
|
}, {})
|
|
|
|
|
const queue = [String(groupId)]
|
|
|
|
|
while (queue.length) {
|
|
|
|
|
const current = queue.shift()
|
|
|
|
|
const node = organizationList.value.find((item) => String(item.id) === current)
|
|
|
|
|
pushDevice(node)
|
|
|
|
|
;(childrenMap[current] || []).forEach((child) => queue.push(String(child.id)))
|
|
|
|
|
}
|
|
|
|
|
return Array.from(deviceMap.values())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeOrganizationList(res) {
|
|
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
|
|
const list = Array.isArray(root) ? root : Array.isArray(root?.list) ? root.list : Array.isArray(root?.data) ? root.data : []
|
|
|
|
|
return list.map((item) => ({
|
|
|
|
|
...item,
|
|
|
|
|
id: String(item.id ?? ''),
|
|
|
|
|
parentId: item.parentId != null ? String(item.parentId) : item.parentId,
|
|
|
|
|
name: item.name || item.label || String(item.id || '')
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onGroupChange(e) {
|
|
|
|
|
const item = groupPickerOptions.value[Number(e?.detail?.value || 0)]
|
|
|
|
|
if (!item) return
|
|
|
|
|
runFilter.groupId = item.id || ''
|
|
|
|
|
runFilter.deviceId = ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onDeviceFilterChange(e) {
|
|
|
|
|
const item = devicePickerOptions.value[Number(e?.detail?.value || 0)]
|
|
|
|
|
if (!item) return
|
|
|
|
|
runFilter.deviceId = item.value || ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleQuickRangeChange(key) {
|
|
|
|
|
runFilter.quickRange = key
|
|
|
|
|
runFilter.timeRange = createDateRangeByQuickKey(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openFilterDrawer() {
|
|
|
|
|
filterPopupRef.value?.open()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeFilterDrawer() {
|
|
|
|
|
filterPopupRef.value?.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function confirmFilterDrawer() {
|
|
|
|
|
closeFilterDrawer()
|
|
|
|
|
await fetchOverview()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function resetRunFilter() {
|
|
|
|
|
runFilter.groupId = ''
|
|
|
|
|
runFilter.deviceId = ''
|
|
|
|
|
runFilter.quickRange = 'today'
|
|
|
|
|
runFilter.timeRange = createDateRangeByQuickKey('today')
|
|
|
|
|
closeFilterDrawer()
|
|
|
|
|
await fetchOverview()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildTodayRange() {
|
|
|
|
|
const start = new Date()
|
|
|
|
|
start.setHours(0, 0, 0, 0)
|
|
|
|
|
@ -429,6 +683,14 @@ function openDeviceList(item) {
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-heading {
|
|
|
|
|
margin-bottom: 18rpx;
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
color: #0f172a;
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.summary-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
@ -508,21 +770,253 @@ function openDeviceList(item) {
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-row {
|
|
|
|
|
margin-top: 24rpx;
|
|
|
|
|
height: 72rpx;
|
|
|
|
|
.section-heading-row {
|
|
|
|
|
margin-bottom: 18rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 18rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-heading {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-filter-btn {
|
|
|
|
|
width: 66rpx;
|
|
|
|
|
height: 66rpx;
|
|
|
|
|
flex: 0 0 66rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-filter-btn:active {
|
|
|
|
|
background: #e5e7eb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-drawer {
|
|
|
|
|
width: 630rpx;
|
|
|
|
|
height: calc(100vh - var(--status-bar-height));
|
|
|
|
|
margin-top: var(--status-bar-height);
|
|
|
|
|
background: #f5f5f7;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border-radius: 28rpx 0 0 28rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.overview-filter-popup.right .uni-popup__content-transition) {
|
|
|
|
|
transform: none !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-header {
|
|
|
|
|
height: 104rpx;
|
|
|
|
|
padding: 18rpx 34rpx 0;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 10rpx;
|
|
|
|
|
color: #64748b;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-title {
|
|
|
|
|
color: #1f2937;
|
|
|
|
|
font-size: 34rpx;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-body {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
padding-bottom: 40rpx;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-text {
|
|
|
|
|
.drawer-section {
|
|
|
|
|
margin-bottom: 18rpx;
|
|
|
|
|
padding: 8rpx 28rpx;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-fields {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-field {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
min-height: 98rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
border-bottom: 1rpx solid #eceff3;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-field:last-child {
|
|
|
|
|
border-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-field-column {
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 14rpx;
|
|
|
|
|
padding: 24rpx 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-label {
|
|
|
|
|
width: 150rpx;
|
|
|
|
|
flex: 0 0 150rpx;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
color: #4b5563;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-field-column .drawer-label {
|
|
|
|
|
width: auto;
|
|
|
|
|
flex: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-picker-host {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-picker,
|
|
|
|
|
.drawer-date {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
flex: 1;
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 74rpx;
|
|
|
|
|
border: 0;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
background: #f7f8fb;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-picker {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
padding: 0 18rpx;
|
|
|
|
|
gap: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-picker-text {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
color: #111827;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-picker-text.placeholder {
|
|
|
|
|
color: #9ca3af;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 0 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date :deep(.uni-date),
|
|
|
|
|
.drawer-date :deep(.uni-date-editor),
|
|
|
|
|
.drawer-date :deep(.uni-date-editor--x),
|
|
|
|
|
.drawer-date :deep(.uni-date-x) {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date :deep(.uni-date-editor),
|
|
|
|
|
.drawer-date :deep(.uni-date-editor--x),
|
|
|
|
|
.drawer-date :deep(.uni-date-x) {
|
|
|
|
|
min-height: 74rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date :deep(.uni-date-editor--x),
|
|
|
|
|
.drawer-date :deep(.uni-date-x) {
|
|
|
|
|
border: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date :deep(.uni-date-range) {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-date :deep(.uni-date__x-input) {
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
color: #111827;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quick-range-row {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
gap: 14rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quick-range-btn {
|
|
|
|
|
height: 62rpx;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
background: #f1f5f9;
|
|
|
|
|
color: #64748b;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quick-range-btn.active {
|
|
|
|
|
background: #174b78;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-actions {
|
|
|
|
|
height: 126rpx;
|
|
|
|
|
padding: 18rpx 28rpx 24rpx;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
box-shadow: 0 -8rpx 24rpx rgba(17, 24, 39, 0.06);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-action {
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 72rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
border: 2rpx solid #174b78;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-action.reset {
|
|
|
|
|
border-radius: 12rpx 0 0 12rpx;
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
color: #174b78;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.drawer-action.confirm {
|
|
|
|
|
border-radius: 0 12rpx 12rpx 0;
|
|
|
|
|
background: #174b78;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hint {
|
|
|
|
|
|