1382 lines
31 KiB
Vue
1382 lines
31 KiB
Vue
<template>
|
||
<view class="query-page query-layout" :class="`theme-${themeMode}`">
|
||
<view class="query-header">
|
||
<view>
|
||
<text class="query-title">记录查询</text>
|
||
<text class="query-subtitle">快速定位扫码、整机、异常与维修记录</text>
|
||
</view>
|
||
<u-icon name="search" color="#2f6cf6" size="34" />
|
||
</view>
|
||
|
||
<view class="query-tabs">
|
||
<view v-for="item in queryTypes" :key="item.value" class="query-tab" :class="{ 'is-active': activeQueryType === item.value }" @tap="handleRecordTypeChange(item.value)">
|
||
<u-icon :name="item.icon" :color="activeQueryType === item.value ? '#ffffff' : '#9aa4b4'" size="28" />
|
||
<text>{{ item.label }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="query-search-row">
|
||
<view class="query-input-wrap">
|
||
<u-input v-model="keyword" :input-mode="pdaMode ? 'none' : 'text'" clearable prefix-icon="search" placeholder="请输入单号、商品编码或操作人" placeholder-style="color: #a0a7b5;" @focus="handleKeywordInputFocus" @confirm="handleSearch" />
|
||
</view>
|
||
<view class="camera-button" :class="{ 'is-scanning': isScanning }" @tap="scanKeyword">
|
||
<u-icon name="scan" color="#ffffff" size="32" />
|
||
</view>
|
||
<view class="query-button" @tap="handleSearch">查询</view>
|
||
</view>
|
||
|
||
<view class="advanced-trigger" @tap="showAdvanced = !showAdvanced">
|
||
<view class="advanced-title">
|
||
<u-icon name="list" color="#687386" size="28" />
|
||
<text>高级筛选</text>
|
||
<text class="advanced-state">{{ showAdvanced ? '收起' : '可选' }}</text>
|
||
</view>
|
||
<u-icon :name="showAdvanced ? 'arrow-up' : 'arrow-down'" color="#9aa4b4" size="24" />
|
||
</view>
|
||
|
||
<view v-if="showAdvanced" class="advanced-panel">
|
||
<view class="filter-select-row" @tap="showDatePicker = true">
|
||
<text class="filter-input-label">时间区间</text>
|
||
<text class="filter-select-value">{{ dateRangeLabel }}</text>
|
||
<u-icon name="arrow-right" color="#a0a7b5" size="24" />
|
||
</view>
|
||
<view class="filter-select-row" @tap="chooseProcess">
|
||
<text class="filter-input-label">工序</text>
|
||
<text class="filter-select-value">{{ processLabel }}</text>
|
||
<u-icon name="arrow-right" color="#a0a7b5" size="24" />
|
||
</view>
|
||
<view class="filter-select-row" :class="{ 'is-disabled': !activeProcess }" @tap="chooseStep">
|
||
<text class="filter-input-label">步骤</text>
|
||
<text class="filter-select-value">{{ stepLabel }}</text>
|
||
<u-icon name="arrow-right" color="#a0a7b5" size="24" />
|
||
</view>
|
||
<view class="filter-actions">
|
||
<text @tap="resetFilter">重置条件</text>
|
||
<text class="filter-confirm" @tap="handleSearch">确定筛选</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="result-header">
|
||
<text>{{ hasSearched ? `找到 ${recordTotal} 条结果` : '请输入条件后点击查询' }}</text>
|
||
<text>{{ currentTypeLabel }}</text>
|
||
</view>
|
||
<view v-if="hasSearched && recordLoading" class="query-loading">正在查询,请稍候…</view>
|
||
|
||
<view v-if="hasSearched && filteredRecords.length && !recordLoading" class="record-list">
|
||
<view v-for="item in filteredRecords" :key="item.id" class="record-card compact-record" @tap="showRecordDetail(item)" @click="showRecordDetail(item)">
|
||
<view class="record-top">
|
||
<view class="record-number">
|
||
<u-icon name="list" color="#44c653" size="28" />
|
||
<text class="record-main">{{ item.mainNo }}</text>
|
||
</view>
|
||
<view class="record-type-pill">{{ getRecordTypeLabel(item) }}</view>
|
||
</view>
|
||
<view class="record-detail-grid">
|
||
<view class="record-detail"><text>岗位:</text><text>{{ item.position }}</text></view>
|
||
<view class="record-detail"><text>步骤:</text><text>{{ item.process }} / {{ item.step }}</text></view>
|
||
<view class="record-detail"><text>状态:</text><text class="record-status" :class="item.statusClass">{{ item.statusLabel }}</text></view>
|
||
<view class="record-detail"><text>操作员:</text><text>{{ item.operator }}</text></view>
|
||
</view>
|
||
<view class="record-footer">
|
||
<text>{{ item.scanTime }}</text>
|
||
<u-icon name="arrow-right" color="#c1c7d0" size="24" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<u-empty v-else-if="hasSearched && !recordLoading" mode="data" text="暂无查询结果" />
|
||
|
||
<view v-if="detailVisible && selectedRecord" class="detail-mask" @tap="closeRecordDetail">
|
||
<view class="detail-sheet" @tap.stop>
|
||
<view class="sheet-handle"></view>
|
||
<view class="detail-sheet-header">
|
||
<text>录入数据明细</text>
|
||
<u-icon name="close" color="#7d8798" size="30" @tap="closeRecordDetail" />
|
||
</view>
|
||
<text class="sheet-main-no">{{ selectedRecord.mainNo }}</text>
|
||
<view class="sheet-detail-grid">
|
||
<view class="sheet-detail-item"><text>记录类型</text><text>{{ getRecordTypeLabel(selectedRecord) }}</text></view>
|
||
<view class="sheet-detail-item"><text>录入模式</text><text>{{ selectedRecord.modeLabel }}</text></view>
|
||
<view class="sheet-detail-item"><text>岗位</text><text>{{ selectedRecord.position }}</text></view>
|
||
<view class="sheet-detail-item"><text>工序 / 步骤</text><text>{{ selectedRecord.process }} / {{ selectedRecord.step }}</text></view>
|
||
<view class="sheet-detail-item"><text>商品编码</text><text>{{ selectedRecord.productCode || '-' }}</text></view>
|
||
<view class="sheet-detail-item"><text>数量</text><text>{{ selectedRecord.quantity || '-' }}</text></view>
|
||
<view class="sheet-detail-item"><text>操作人</text><text>{{ selectedRecord.operator }}</text></view>
|
||
<view class="sheet-detail-item"><text>扫码时间</text><text>{{ selectedRecord.scanTime }}</text></view>
|
||
</view>
|
||
<view class="sheet-remark"><text>备注</text><text>{{ selectedRecord.remark }}</text></view>
|
||
</view>
|
||
</view>
|
||
<u-calendar v-model="showDatePicker" mode="range" @change="handleDateChange" />
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// UTS 原生扫码插件仅在 App-Plus 真机/自定义基座中解析,Web 环境不加载。
|
||
// #ifdef APP-PLUS
|
||
import { openCodeScan } from '@/uni_modules/mas-code-scan'
|
||
// #endif
|
||
|
||
import { getScanRegionSetting } from '@/utils/scan-settings.js'
|
||
import { queryProduceRecords } from '@/api/produce/query.js'
|
||
import { getProcessOptions, getStepOptions } from '@/api/produce/data-entry.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
keyword: '',
|
||
orderNo: '',
|
||
userCode: '',
|
||
startDate: '',
|
||
endDate: '',
|
||
showDatePicker: false,
|
||
pdaMode: false,
|
||
activeQueryType: 'all',
|
||
showAdvanced: false,
|
||
isScanning: false,
|
||
hasSearched: false,
|
||
recordLoading: false,
|
||
recordTotal: 0,
|
||
recordPage: 1,
|
||
recordLimit: 20,
|
||
activeProcess: '',
|
||
activeStep: '',
|
||
detailVisible: false,
|
||
selectedRecord: null,
|
||
queryTypes: [
|
||
{ label: '全部', value: 'all', icon: 'search' },
|
||
{ label: '订单', value: 'scan', icon: 'scan' },
|
||
{ label: '整机', value: 'product', icon: 'grid-fill' },
|
||
{ label: '异常', value: 'exception', icon: 'warning-fill' },
|
||
{ label: '维修', value: 'repair', icon: 'wrench' }
|
||
],
|
||
processOptions: [],
|
||
stepOptions: [],
|
||
records: []
|
||
}
|
||
},
|
||
computed: {
|
||
filteredRecords() {
|
||
return this.records.filter((item) => {
|
||
const matchType = this.activeQueryType === 'all' || item.queryType === this.activeQueryType
|
||
const matchProcess = !this.activeProcess || item.processId === String(this.activeProcess) || item.process === this.processLabel
|
||
const matchStep = !this.activeStep || item.stepId === String(this.activeStep) || item.step === this.stepLabel
|
||
return matchType && matchProcess && matchStep
|
||
})
|
||
},
|
||
currentTypeLabel() {
|
||
const current = this.queryTypes.find((item) => item.value === this.activeQueryType)
|
||
return current ? current.label : '全部类型'
|
||
},
|
||
processLabel() {
|
||
return this.processOptions.find((item) => String(item.value) === String(this.activeProcess))?.label || '全部工序'
|
||
},
|
||
stepLabel() {
|
||
return this.stepOptions.find((item) => String(item.value) === String(this.activeStep))?.label || '全部步骤'
|
||
},
|
||
dateRangeLabel() {
|
||
return this.startDate && this.endDate ? `${this.startDate} 至 ${this.endDate}` : '不限时间'
|
||
},
|
||
successCount() {
|
||
return this.filteredRecords.filter((item) => item.status === 'success').length
|
||
},
|
||
failedCount() {
|
||
return this.filteredRecords.filter((item) => item.status === 'failed').length
|
||
}
|
||
},
|
||
watch: {
|
||
filteredRecords: {
|
||
immediate: true,
|
||
handler(list) {
|
||
if (!list.length) {
|
||
this.selectedRecord = null
|
||
this.detailVisible = false
|
||
return
|
||
}
|
||
if (!this.selectedRecord || !list.some((item) => item.id === this.selectedRecord.id)) {
|
||
this.detailVisible = false
|
||
this.selectedRecord = list[0]
|
||
}
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
isLoggedIn() {
|
||
return Boolean(uni.getStorageSync('scanapp_token'))
|
||
},
|
||
requireLogin() {
|
||
if (this.isLoggedIn()) return true
|
||
uni.showToast({ title: '请先登录后再查询', icon: 'none' })
|
||
return false
|
||
},
|
||
mapRecord(item) {
|
||
const type = item.record_type || 'scan'
|
||
const resultName = String(item.result_name || '').trim()
|
||
const statusName = String(item.status_name || '').trim()
|
||
const status = resultName === '成功' ? 'success' : resultName === '失败' ? 'failed' : this.mapStatus(statusName)
|
||
return {
|
||
id: `${type}-${item.id}`,
|
||
queryType: type,
|
||
typeLabel: item.record_type_name || '记录',
|
||
mainNo: item.order_no || item.repair_no || '-',
|
||
position: item.position || '-',
|
||
process: item.process_name || '-',
|
||
step: item.step_name || '-',
|
||
processId: item.process_id || '',
|
||
stepId: item.step_id || '',
|
||
operator: item.operator || item.user_code || '未记录',
|
||
userCode: item.user_code || '',
|
||
status,
|
||
statusLabel: resultName || statusName || '已记录',
|
||
statusClass: status === 'failed' ? 'is-failed' : status === 'success' ? 'is-success' : '',
|
||
scanTime: item.event_date || this.formatEventDate(item.event_at),
|
||
remark: item.remark || '',
|
||
modeLabel: item.record_type_name || '记录',
|
||
productCode: item.product_code || '',
|
||
quantity: item.quantity ?? '',
|
||
exceptionReason: item.exception_reason || '',
|
||
exceptionContent: item.exception_content || '',
|
||
materialCategory: item.material_category || '',
|
||
faultDescription: item.fault_description || ''
|
||
}
|
||
},
|
||
formatEventDate(timestamp) {
|
||
const value = Number(timestamp || 0)
|
||
if (!value) return '-'
|
||
const date = new Date(value * 1000)
|
||
if (Number.isNaN(date.getTime())) return '-'
|
||
const pad = (part) => String(part).padStart(2, '0')
|
||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||
},
|
||
mapStatus(statusName) {
|
||
const statusMap = { 待处理: 'pending', 处理中: 'processing', 已完成: 'done', 已维修: 'repaired' }
|
||
return statusMap[statusName] || statusName || 'recorded'
|
||
},
|
||
async loadRecords() {
|
||
if (this.recordLoading) return
|
||
this.detailVisible = false
|
||
this.selectedRecord = null
|
||
this.recordLoading = true
|
||
const types = this.activeQueryType === 'all' ? ['scan', 'product', 'exception', 'repair'] : [this.activeQueryType]
|
||
const params = {
|
||
keyword: this.keyword.trim(),
|
||
order_no: this.orderNo.trim(),
|
||
user_code: this.userCode.trim(),
|
||
start_date: this.startDate,
|
||
end_date: this.endDate,
|
||
process_id: this.activeProcess,
|
||
step_id: this.activeStep,
|
||
page: this.recordPage,
|
||
limit: this.recordLimit
|
||
}
|
||
try {
|
||
const responses = await Promise.all(types.map((recordType) => queryProduceRecords({ ...params, record_type: recordType })))
|
||
const items = responses.flatMap((data) => Array.isArray(data?.items) ? data.items.map((item) => this.mapRecord(item)) : [])
|
||
items.sort((a, b) => String(b.scanTime).localeCompare(String(a.scanTime)))
|
||
this.records = items
|
||
this.recordTotal = responses.reduce((total, data) => total + Number(data?.total || 0), 0)
|
||
} catch (error) {
|
||
this.records = []
|
||
this.recordTotal = 0
|
||
uni.showToast({ title: '查询失败,请稍后重试', icon: 'none' })
|
||
} finally {
|
||
this.recordLoading = false
|
||
}
|
||
},
|
||
async loadProcessOptions() {
|
||
try {
|
||
const options = await getProcessOptions()
|
||
this.processOptions = Array.isArray(options) ? options : []
|
||
} catch (error) {
|
||
this.processOptions = []
|
||
}
|
||
},
|
||
chooseProcess() {
|
||
const labels = ['全部工序', ...this.processOptions.map((item) => item.label)]
|
||
uni.showActionSheet({ itemList: labels, success: async (result) => {
|
||
this.activeProcess = result.tapIndex === 0 ? '' : this.processOptions[result.tapIndex - 1]?.value || ''
|
||
this.activeStep = ''
|
||
this.stepOptions = []
|
||
if (this.activeProcess) {
|
||
try {
|
||
const options = await getStepOptions(this.activeProcess)
|
||
this.stepOptions = Array.isArray(options) ? options : []
|
||
} catch (error) {
|
||
this.stepOptions = []
|
||
}
|
||
}
|
||
} })
|
||
},
|
||
chooseStep() {
|
||
if (!this.activeProcess) {
|
||
uni.showToast({ title: '请先选择工序', icon: 'none' })
|
||
return
|
||
}
|
||
const labels = ['全部步骤', ...this.stepOptions.map((item) => item.label)]
|
||
uni.showActionSheet({ itemList: labels, success: (result) => {
|
||
this.activeStep = result.tapIndex === 0 ? '' : this.stepOptions[result.tapIndex - 1]?.value || ''
|
||
} })
|
||
},
|
||
handleDateChange(result) {
|
||
this.startDate = result.startDate || ''
|
||
this.endDate = result.endDate || ''
|
||
this.showDatePicker = false
|
||
},
|
||
handleRecordTypeChange(type) {
|
||
if (this.activeQueryType === type) return
|
||
this.activeQueryType = type
|
||
this.recordPage = 1
|
||
if (this.hasSearched) this.loadRecords()
|
||
},
|
||
getCurrentUserCode() {
|
||
const user = uni.getStorageSync('scanapp_user') || {}
|
||
return user.username || user.nickname || 'U001'
|
||
},
|
||
loadPdaMode() {
|
||
const settings = uni.getStorageSync(`scanapp_settings_${this.getCurrentUserCode()}`)
|
||
this.pdaMode = settings?.pdaMode === true
|
||
},
|
||
handleKeywordInputFocus() {
|
||
// #ifdef APP-PLUS
|
||
try {
|
||
const activity = plus.android.runtimeMainActivity()
|
||
const focusedView = activity.getWindow().getDecorView().findFocus()
|
||
if (focusedView) plus.android.invoke(focusedView, 'setShowSoftInputOnFocus', !this.pdaMode)
|
||
if (this.pdaMode) uni.hideKeyboard()
|
||
} catch (error) {
|
||
if (this.pdaMode) uni.hideKeyboard()
|
||
}
|
||
// #endif
|
||
},
|
||
handleSearch() {
|
||
const hasInput = this.keyword.trim() || this.orderNo.trim() || this.userCode.trim()
|
||
if (!hasInput) {
|
||
this.hasSearched = false
|
||
this.records = []
|
||
this.recordTotal = 0
|
||
this.selectedRecord = null
|
||
this.detailVisible = false
|
||
uni.showToast({ title: '请输入查询内容', icon: 'none' })
|
||
return
|
||
}
|
||
this.recordPage = 1
|
||
this.hasSearched = true
|
||
this.loadRecords()
|
||
},
|
||
resetFilter() {
|
||
this.keyword = ''
|
||
this.orderNo = ''
|
||
this.userCode = ''
|
||
this.startDate = ''
|
||
this.endDate = ''
|
||
this.activeQueryType = 'all'
|
||
this.activeProcess = ''
|
||
this.activeStep = ''
|
||
this.stepOptions = []
|
||
this.showAdvanced = false
|
||
this.detailVisible = false
|
||
this.recordPage = 1
|
||
this.hasSearched = false
|
||
this.records = []
|
||
this.recordTotal = 0
|
||
},
|
||
scanKeyword() {
|
||
if (this.isScanning) return
|
||
// #ifdef APP-PLUS
|
||
this.isScanning = true
|
||
openCodeScan({
|
||
smartZoom: true,
|
||
vibrate: true,
|
||
scanRegion: getScanRegionSetting(),
|
||
success: (result) => {
|
||
const value = String(result?.content || '').trim()
|
||
if (!value) return
|
||
this.keyword = value
|
||
this.handleSearch()
|
||
},
|
||
fail: (error) => {
|
||
const code = Number(error?.errCode)
|
||
if (code === 9021003) return
|
||
const messageMap = {
|
||
9021001: '当前环境不支持扫码,请在 Android 自定义调试基座或正式安装包中使用',
|
||
9021002: '相机权限被拒绝,请到系统设置中开启相机权限',
|
||
9021004: '未获取到有效扫码结果,请重试'
|
||
}
|
||
uni.showToast({ title: messageMap[code] || String(error?.errMsg || '扫码插件调用异常'), icon: 'none' })
|
||
},
|
||
complete: () => {
|
||
this.isScanning = false
|
||
}
|
||
})
|
||
// #endif
|
||
// #ifndef APP-PLUS
|
||
uni.showToast({ title: '请在 Android App 中使用扫码功能', icon: 'none' })
|
||
// #endif
|
||
},
|
||
showRecordDetail(item) {
|
||
if (!item) return
|
||
this.selectedRecord = item
|
||
this.detailVisible = true
|
||
},
|
||
closeRecordDetail() {
|
||
this.detailVisible = false
|
||
},
|
||
getRecordTypeLabel(item) {
|
||
return item.typeLabel || '记录'
|
||
}
|
||
},
|
||
onLoad() {
|
||
if (!this.requireLogin()) return
|
||
this.loadPdaMode()
|
||
this.loadProcessOptions()
|
||
},
|
||
onShow() {
|
||
if (!this.isLoggedIn()) return
|
||
this.loadPdaMode()
|
||
},
|
||
onHide() {
|
||
this.detailVisible = false
|
||
this.selectedRecord = null
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.query-page {
|
||
min-height: 100vh;
|
||
padding: 28rpx 24rpx 48rpx;
|
||
box-sizing: border-box;
|
||
background: linear-gradient(180deg, #eef6ff 0%, #f4f6fb 38%, #f4f6fb 100%);
|
||
}
|
||
|
||
.hero-card,
|
||
.filter-card,
|
||
.summary-card,
|
||
.detail-card,
|
||
.list-card {
|
||
margin-top: 24rpx;
|
||
padding: 28rpx;
|
||
border: 1rpx solid rgba(255, 255, 255, 0.72);
|
||
border-radius: 28rpx;
|
||
background: #ffffff;
|
||
box-shadow: 0 16rpx 42rpx rgba(23, 35, 61, 0.06);
|
||
}
|
||
|
||
.hero-card {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 0;
|
||
padding: 34rpx 30rpx;
|
||
background: linear-gradient(135deg, #1677ff 0%, #35bfa3 100%);
|
||
}
|
||
|
||
.hero-title,
|
||
.hero-subtitle,
|
||
.card-title,
|
||
.filter-label,
|
||
.summary-value,
|
||
.summary-label,
|
||
.detail-main,
|
||
.detail-sub,
|
||
.detail-key,
|
||
.detail-value,
|
||
.record-main,
|
||
.record-sub,
|
||
.record-key,
|
||
.record-value {
|
||
display: block;
|
||
}
|
||
|
||
.hero-title {
|
||
color: #ffffff;
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.hero-subtitle {
|
||
margin-top: 10rpx;
|
||
color: rgba(255, 255, 255, 0.82);
|
||
font-size: 22rpx;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.hero-icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 86rpx;
|
||
height: 86rpx;
|
||
border-radius: 28rpx;
|
||
background: rgba(255, 255, 255, 0.18);
|
||
}
|
||
|
||
.card-title {
|
||
color: #17233d;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.search-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.search-input-wrap {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.filter-group {
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.filter-label {
|
||
margin-bottom: 14rpx;
|
||
color: #5f6b7a;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.chip-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 14rpx;
|
||
}
|
||
|
||
.chip {
|
||
padding: 14rpx 20rpx;
|
||
border-radius: 999rpx;
|
||
background: #f3f6fb;
|
||
color: #5f6b7a;
|
||
font-size: 24rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.chip.is-active {
|
||
background: rgba(22, 119, 255, 0.12);
|
||
color: #1677ff;
|
||
}
|
||
|
||
.action-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16rpx;
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
.summary-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.summary-item {
|
||
padding: 22rpx 18rpx;
|
||
border-radius: 22rpx;
|
||
background: #f8fbff;
|
||
}
|
||
|
||
.summary-value {
|
||
color: #17233d;
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.summary-value.success {
|
||
color: #19be6b;
|
||
}
|
||
|
||
.summary-value.danger {
|
||
color: #e91f3f;
|
||
}
|
||
|
||
.summary-label {
|
||
margin-top: 8rpx;
|
||
color: #8a94a6;
|
||
font-size: 20rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.detail-card,
|
||
.list-card {
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
.detail-header,
|
||
.record-top {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.detail-main,
|
||
.record-main {
|
||
color: #17233d;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.detail-sub,
|
||
.record-sub {
|
||
margin-top: 6rpx;
|
||
color: #8a94a6;
|
||
font-size: 20rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.status-pill {
|
||
padding: 10rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 20rpx;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.status-pill.is-success {
|
||
background: rgba(25, 190, 107, 0.12);
|
||
color: #19be6b;
|
||
}
|
||
|
||
.status-pill.is-failed {
|
||
background: rgba(233, 31, 63, 0.12);
|
||
color: #e91f3f;
|
||
}
|
||
|
||
.detail-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16rpx;
|
||
margin-top: 22rpx;
|
||
}
|
||
|
||
.detail-item {
|
||
padding: 18rpx 18rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
background: #f8fbff;
|
||
}
|
||
|
||
.detail-key,
|
||
.record-key {
|
||
color: #8a94a6;
|
||
font-size: 20rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.detail-value,
|
||
.record-value {
|
||
margin-top: 8rpx;
|
||
color: #17233d;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.record-card {
|
||
padding: 22rpx 24rpx;
|
||
border-radius: 24rpx;
|
||
background: #f8fbff;
|
||
margin-top: 18rpx;
|
||
}
|
||
|
||
.record-card.is-selected {
|
||
border: 1rpx solid rgba(22, 119, 255, 0.22);
|
||
background: #f5f9ff;
|
||
}
|
||
|
||
.record-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16rpx;
|
||
margin-top: 18rpx;
|
||
}
|
||
|
||
.record-info {
|
||
padding: 16rpx 16rpx 18rpx;
|
||
border-radius: 18rpx;
|
||
background: #ffffff;
|
||
}
|
||
|
||
.query-page.query-layout {
|
||
padding: 24rpx 20rpx 40rpx;
|
||
background: #f5f7fb;
|
||
}
|
||
|
||
.query-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 4rpx 4rpx 20rpx;
|
||
}
|
||
|
||
.query-title,
|
||
.query-subtitle {
|
||
display: block;
|
||
}
|
||
|
||
.query-title {
|
||
color: #17233d;
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.query-subtitle {
|
||
margin-top: 8rpx;
|
||
color: #8a94a6;
|
||
font-size: 18rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.query-tabs {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 10rpx;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.query-tab {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6rpx;
|
||
height: 68rpx;
|
||
border: 1rpx solid transparent;
|
||
border-radius: 14rpx;
|
||
background: #edf0f5;
|
||
color: #7d8798;
|
||
font-size: 20rpx;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
}
|
||
|
||
.query-tab.is-active {
|
||
background: #2f6cf6;
|
||
color: #ffffff;
|
||
box-shadow: 0 10rpx 24rpx rgba(47, 108, 246, 0.22);
|
||
}
|
||
|
||
.query-search-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0;
|
||
margin-top: 16rpx;
|
||
padding: 12rpx;
|
||
border: 1rpx solid #e9edf4;
|
||
border-radius: 18rpx;
|
||
background: #ffffff;
|
||
box-shadow: 0 8rpx 20rpx rgba(23, 35, 61, 0.04);
|
||
}
|
||
|
||
.query-input-wrap {
|
||
flex: 1;
|
||
min-width: 0;
|
||
height: 64rpx;
|
||
padding: 0 16rpx;
|
||
border: 1rpx solid #e3e7ef;
|
||
border-radius: 12rpx;
|
||
background: #f6f8fb;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.camera-button,
|
||
.query-button {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 64rpx;
|
||
border-radius: 12rpx;
|
||
background: #2f6cf6;
|
||
color: #ffffff;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
box-shadow: 0 8rpx 18rpx rgba(47, 108, 246, 0.18);
|
||
}
|
||
|
||
.camera-button {
|
||
width: 64rpx;
|
||
margin-left: 16rpx;
|
||
flex: none;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.camera-button.is-scanning {
|
||
opacity: 0.58;
|
||
}
|
||
|
||
.query-button {
|
||
width: 104rpx;
|
||
margin-left: 16rpx;
|
||
flex: none;
|
||
}
|
||
|
||
.advanced-trigger {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 14rpx;
|
||
padding: 18rpx 16rpx;
|
||
border: 1rpx solid #e9edf4;
|
||
border-radius: 14rpx;
|
||
background: #ffffff;
|
||
}
|
||
|
||
.advanced-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
color: #2c313a;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
}
|
||
|
||
.advanced-state {
|
||
margin-left: 4rpx;
|
||
padding: 2rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
background: #eef5ff;
|
||
color: #3a82ff;
|
||
font-size: 18rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.advanced-panel {
|
||
margin-top: 10rpx;
|
||
padding: 20rpx 18rpx 16rpx;
|
||
border: 1rpx solid #e9edf4;
|
||
border-radius: 16rpx;
|
||
background: #ffffff;
|
||
box-shadow: 0 8rpx 20rpx rgba(23, 35, 61, 0.04);
|
||
}
|
||
|
||
.filter-input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
min-height: 64rpx;
|
||
margin-bottom: 12rpx;
|
||
padding: 0 14rpx;
|
||
border: 1rpx solid #e3e7ef;
|
||
border-radius: 12rpx;
|
||
background: #f6f8fb;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.filter-input-label {
|
||
width: 112rpx;
|
||
flex: none;
|
||
color: #5f6b7a;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.filter-input-row :deep(.u-input) {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.filter-select-row {
|
||
display: flex;
|
||
align-items: center;
|
||
min-height: 64rpx;
|
||
margin-bottom: 12rpx;
|
||
padding: 0 14rpx;
|
||
border: 1rpx solid #e3e7ef;
|
||
border-radius: 12rpx;
|
||
background: #f6f8fb;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.filter-select-row.is-disabled {
|
||
opacity: 0.55;
|
||
}
|
||
|
||
.filter-select-value {
|
||
flex: 1;
|
||
min-width: 0;
|
||
color: #2b313b;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.status-filter-label {
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.compact-chip-row {
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.chip {
|
||
padding: 12rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
background: #f1f4f8;
|
||
color: #6b778c;
|
||
font-size: 20rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.chip.is-active {
|
||
background: #2f6cf6;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.filter-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 12rpx;
|
||
color: #6b778c;
|
||
font-size: 20rpx;
|
||
}
|
||
|
||
.filter-confirm {
|
||
color: #2f6cf6;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.result-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 20rpx;
|
||
padding: 0 6rpx 8rpx;
|
||
color: #7d8798;
|
||
font-size: 18rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.query-loading {
|
||
padding: 28rpx 0;
|
||
color: #7d8798;
|
||
font-size: 20rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.record-list {
|
||
margin-top: 2rpx;
|
||
}
|
||
|
||
.record-card.compact-record {
|
||
margin-top: 10rpx;
|
||
padding: 20rpx 18rpx 16rpx;
|
||
border: 1rpx solid #edf0f5;
|
||
border-radius: 16rpx;
|
||
background: #ffffff;
|
||
box-shadow: 0 8rpx 18rpx rgba(23, 35, 61, 0.04);
|
||
}
|
||
|
||
.record-top {
|
||
align-items: center;
|
||
}
|
||
|
||
.record-number {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
min-width: 0;
|
||
}
|
||
|
||
.record-main {
|
||
max-width: 430rpx;
|
||
overflow: hidden;
|
||
color: #17233d;
|
||
font-size: 26rpx;
|
||
font-weight: 800;
|
||
line-height: 1.35;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.record-type-pill {
|
||
padding: 6rpx 12rpx;
|
||
border: 1rpx solid #b7ec8f;
|
||
border-radius: 999rpx;
|
||
background: #f8fff0;
|
||
color: #7ad63a;
|
||
font-size: 18rpx;
|
||
line-height: 1.35;
|
||
flex: none;
|
||
}
|
||
|
||
.record-detail-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 8rpx 18rpx;
|
||
margin-top: 16rpx;
|
||
padding-top: 14rpx;
|
||
border-top: 1rpx solid #f0f2f6;
|
||
}
|
||
|
||
.record-detail {
|
||
display: flex;
|
||
align-items: baseline;
|
||
font-size: 20rpx;
|
||
line-height: 1.55;
|
||
color: #7d8798;
|
||
}
|
||
|
||
.record-detail text:last-child {
|
||
color: #2b313b;
|
||
margin-left: 4rpx;
|
||
}
|
||
|
||
.record-status.is-success {
|
||
color: #7ad63a;
|
||
}
|
||
|
||
.record-status.is-failed {
|
||
color: #ff8a5b;
|
||
}
|
||
|
||
.record-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 14rpx;
|
||
padding-top: 10rpx;
|
||
border-top: 1rpx solid #f5f6f8;
|
||
color: #9aa4b4;
|
||
font-size: 18rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.detail-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
z-index: 999;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
justify-content: flex-end;
|
||
background: rgba(0, 0, 0, 0.32);
|
||
}
|
||
|
||
.sheet-handle {
|
||
width: 64rpx;
|
||
height: 8rpx;
|
||
margin: 0 auto 18rpx;
|
||
border-radius: 999rpx;
|
||
background: #dfe4ec;
|
||
}
|
||
|
||
.detail-sheet {
|
||
width: 100%;
|
||
max-height: 72vh;
|
||
overflow: auto;
|
||
padding: 22rpx 22rpx 30rpx;
|
||
border-radius: 24rpx 24rpx 0 0;
|
||
background: #ffffff;
|
||
box-shadow: 0 -16rpx 40rpx rgba(0, 0, 0, 0.08);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.detail-sheet-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
color: #17233d;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.sheet-main-no {
|
||
display: block;
|
||
margin-top: 12rpx;
|
||
color: #17233d;
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.sheet-detail-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 12rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.sheet-detail-item {
|
||
padding: 14rpx;
|
||
border-radius: 14rpx;
|
||
background: #f8fbff;
|
||
}
|
||
|
||
.sheet-detail-item text:first-child {
|
||
display: block;
|
||
color: #7d8798;
|
||
font-size: 18rpx;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.sheet-detail-item text:last-child {
|
||
display: block;
|
||
margin-top: 4rpx;
|
||
color: #17233d;
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.sheet-remark {
|
||
margin-top: 14rpx;
|
||
padding: 14rpx;
|
||
border-radius: 14rpx;
|
||
background: #f8fbff;
|
||
color: #7d8798;
|
||
font-size: 18rpx;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.sheet-remark text:last-child {
|
||
display: block;
|
||
margin-top: 4rpx;
|
||
color: #17233d;
|
||
font-size: 22rpx;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.theme-dark.query-page {
|
||
background: #151923;
|
||
}
|
||
|
||
.theme-dark .query-header,
|
||
.theme-dark .query-search-row,
|
||
.theme-dark .advanced-trigger,
|
||
.theme-dark .advanced-panel,
|
||
.theme-dark .record-card,
|
||
.theme-dark .detail-sheet {
|
||
border-color: #364152;
|
||
background: #222936;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.theme-dark .query-tab:not(.is-active),
|
||
.theme-dark .query-input-wrap,
|
||
.theme-dark .filter-input-row,
|
||
.theme-dark .filter-select-row,
|
||
.theme-dark .chip,
|
||
.theme-dark .record-info,
|
||
.theme-dark .sheet-detail-item,
|
||
.theme-dark .sheet-remark {
|
||
border-color: #364152;
|
||
background: #1c2431;
|
||
}
|
||
|
||
.theme-dark .query-title,
|
||
.theme-dark .advanced-title,
|
||
.theme-dark .filter-select-value,
|
||
.theme-dark .record-main,
|
||
.theme-dark .record-detail text:last-child,
|
||
.theme-dark .detail-sheet-header,
|
||
.theme-dark .sheet-main-no,
|
||
.theme-dark .sheet-detail-item text:last-child,
|
||
.theme-dark .sheet-remark text:last-child {
|
||
color: #f2f5fa;
|
||
}
|
||
|
||
.theme-dark .query-subtitle,
|
||
.theme-dark .result-header,
|
||
.theme-dark .filter-actions,
|
||
.theme-dark .record-detail,
|
||
.theme-dark .record-footer,
|
||
.theme-dark .sheet-detail-item text:first-child,
|
||
.theme-dark .sheet-remark {
|
||
color: #aeb9ca;
|
||
}
|
||
|
||
.theme-dark .record-detail-grid,
|
||
.theme-dark .record-footer {
|
||
border-color: #364152;
|
||
}
|
||
|
||
.theme-dark :deep(.u-input__input) {
|
||
color: #f2f5fa !important;
|
||
}
|
||
|
||
/* 查询页统一视觉层级:保持移动端紧凑、清晰的生产工具风格。 */
|
||
.query-page.query-layout {
|
||
padding: 24rpx 24rpx 56rpx;
|
||
background: linear-gradient(180deg, #edf5ff 0%, #f5f7fb 30%, #f5f7fb 100%);
|
||
}
|
||
|
||
.query-header {
|
||
padding: 24rpx 22rpx;
|
||
border: 1rpx solid #e5ebf4;
|
||
border-radius: 22rpx;
|
||
background: rgba(255, 255, 255, 0.94);
|
||
box-shadow: 0 12rpx 30rpx rgba(39, 74, 125, 0.08);
|
||
}
|
||
|
||
.query-title {
|
||
letter-spacing: 1rpx;
|
||
}
|
||
|
||
.query-tabs {
|
||
display: flex;
|
||
gap: 10rpx;
|
||
margin-top: 18rpx;
|
||
padding: 8rpx;
|
||
border: 1rpx solid #e5ebf4;
|
||
border-radius: 18rpx;
|
||
background: #ffffff;
|
||
box-shadow: 0 8rpx 22rpx rgba(39, 74, 125, 0.05);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.query-tab {
|
||
min-width: 120rpx;
|
||
flex: 1;
|
||
flex-shrink: 0;
|
||
border-radius: 12rpx;
|
||
transition: background 0.2s ease, color 0.2s ease;
|
||
}
|
||
|
||
.query-search-row {
|
||
margin-top: 18rpx;
|
||
padding: 10rpx;
|
||
border-radius: 18rpx;
|
||
box-shadow: 0 10rpx 26rpx rgba(39, 74, 125, 0.07);
|
||
}
|
||
|
||
.query-input-wrap {
|
||
background: #f7f9fc;
|
||
}
|
||
|
||
.camera-button,
|
||
.query-button {
|
||
box-shadow: 0 8rpx 18rpx rgba(47, 108, 246, 0.2);
|
||
}
|
||
|
||
.advanced-trigger,
|
||
.advanced-panel {
|
||
box-shadow: 0 8rpx 22rpx rgba(39, 74, 125, 0.05);
|
||
}
|
||
|
||
.advanced-trigger {
|
||
margin-top: 16rpx;
|
||
padding: 20rpx 18rpx;
|
||
}
|
||
|
||
.advanced-panel {
|
||
padding: 20rpx 18rpx 18rpx;
|
||
}
|
||
|
||
.filter-select-row {
|
||
margin-bottom: 14rpx;
|
||
background: #f8faff;
|
||
}
|
||
|
||
.filter-input-label {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.result-header {
|
||
margin-top: 24rpx;
|
||
padding: 0 8rpx 10rpx;
|
||
color: #738199;
|
||
}
|
||
|
||
.record-card.compact-record {
|
||
margin-top: 12rpx;
|
||
padding: 22rpx 20rpx 18rpx;
|
||
border-radius: 18rpx;
|
||
box-shadow: 0 10rpx 24rpx rgba(39, 74, 125, 0.06);
|
||
}
|
||
|
||
.record-detail-grid {
|
||
gap: 10rpx 16rpx;
|
||
margin-top: 18rpx;
|
||
padding-top: 16rpx;
|
||
}
|
||
|
||
.record-detail text:last-child {
|
||
min-width: 0;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.record-footer {
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.theme-dark.query-page.query-layout {
|
||
background: #151923;
|
||
}
|
||
|
||
.theme-dark .query-header,
|
||
.theme-dark .query-tabs {
|
||
border-color: #364152;
|
||
background: #222936;
|
||
}
|
||
|
||
.theme-dark .query-search-row,
|
||
.theme-dark .advanced-trigger,
|
||
.theme-dark .advanced-panel,
|
||
.theme-dark .record-card.compact-record {
|
||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.16);
|
||
}
|
||
|
||
/* 防止记录类型标签换行,并让输入文字在控件内垂直居中。 */
|
||
.query-tab {
|
||
min-width: 0;
|
||
padding: 0 4rpx;
|
||
flex: 1 1 0;
|
||
margin-right: 10rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.query-tabs {
|
||
gap: 0;
|
||
}
|
||
|
||
.query-tab:last-child {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.query-tab text {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.query-input-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 80rpx;
|
||
}
|
||
|
||
.camera-button,
|
||
.query-button {
|
||
height: 80rpx;
|
||
}
|
||
|
||
.camera-button {
|
||
width: 80rpx;
|
||
}
|
||
|
||
.query-button {
|
||
width: 128rpx;
|
||
}
|
||
|
||
.query-input-wrap :deep(.u-input) {
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.query-input-wrap :deep(.u-input__input) {
|
||
height: 100%;
|
||
line-height: 80rpx;
|
||
}
|
||
</style>
|