chore: initialize mini program frontend
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
const api = require('../../utils/api')
|
||||
const util = require('../../utils/util')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
user: null,
|
||||
mode: 'mine',
|
||||
title: '我的报工记录',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
status: '',
|
||||
showStatusFilter: true,
|
||||
rows: [],
|
||||
page: 1,
|
||||
pageInput: '1',
|
||||
totalPages: 1,
|
||||
refreshing: false,
|
||||
scrollTop: 0,
|
||||
expandedRecordId: '',
|
||||
detailExpandedAll: false,
|
||||
filterVoided: true,
|
||||
statusOptions: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '待审核', value: 'pending' },
|
||||
{ label: '已通过', value: 'approved' },
|
||||
],
|
||||
},
|
||||
onLoad(options) {
|
||||
const mode = options.mode || 'mine'
|
||||
const defaultDate = util.today()
|
||||
const startDate = mode === 'pending' ? (options.startDate || '') : (options.startDate || defaultDate)
|
||||
const endDate = mode === 'pending' ? (options.endDate || '') : (options.endDate || defaultDate)
|
||||
const titleMap = {
|
||||
audit: '审核记录',
|
||||
pending: '待审核清单',
|
||||
mine: '我的报工记录',
|
||||
}
|
||||
this.setData({
|
||||
mode,
|
||||
title: titleMap[mode] || '我的报工记录',
|
||||
startDate,
|
||||
endDate,
|
||||
status: mode === 'pending' ? 'pending' : (options.status || ''),
|
||||
showStatusFilter: mode !== 'pending',
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
this.load()
|
||||
},
|
||||
toggleDetails() {
|
||||
this.setData({
|
||||
detailExpandedAll: !this.data.detailExpandedAll,
|
||||
expandedRecordId: '',
|
||||
})
|
||||
},
|
||||
onScroll(e) {
|
||||
this.currentScrollTop = e.detail.scrollTop || 0
|
||||
},
|
||||
queryRecordTop(id) {
|
||||
return new Promise(resolve => {
|
||||
this.createSelectorQuery()
|
||||
.select(`#record-${id}`)
|
||||
.boundingClientRect(rect => {
|
||||
resolve(rect ? rect.top : null)
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
},
|
||||
restoreRecordPosition(id, beforeTop) {
|
||||
if (beforeTop === null || beforeTop === undefined) {
|
||||
return
|
||||
}
|
||||
wx.nextTick(() => {
|
||||
this.queryRecordTop(id).then(afterTop => {
|
||||
if (afterTop === null || afterTop === undefined) {
|
||||
return
|
||||
}
|
||||
const nextScrollTop = Math.max(
|
||||
0,
|
||||
(this.currentScrollTop || 0) + afterTop - beforeTop,
|
||||
)
|
||||
this.currentScrollTop = nextScrollTop
|
||||
this.setData({ scrollTop: nextScrollTop })
|
||||
})
|
||||
})
|
||||
},
|
||||
toggleRecordDetail(e) {
|
||||
const id = String(e.currentTarget.dataset.id || '')
|
||||
const switchingRecord = this.data.detailExpandedAll
|
||||
|| (!!this.data.expandedRecordId && this.data.expandedRecordId !== id)
|
||||
this.queryRecordTop(id).then(beforeTop => {
|
||||
this.setData({
|
||||
detailExpandedAll: false,
|
||||
expandedRecordId: this.data.expandedRecordId === id ? '' : id,
|
||||
}, () => {
|
||||
if (switchingRecord) {
|
||||
this.restoreRecordPosition(id, beforeTop)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
async load(page = this.data.page) {
|
||||
const user = api.getCurrentUser()
|
||||
if (!user) {
|
||||
this.setData({ rows: [], user })
|
||||
return
|
||||
}
|
||||
|
||||
const filters = {
|
||||
startDate: this.data.startDate,
|
||||
endDate: this.data.endDate,
|
||||
status: this.data.status,
|
||||
page,
|
||||
pageSize: 8,
|
||||
includeVoided: !this.data.filterVoided,
|
||||
}
|
||||
|
||||
if (this.data.mode === 'audit') {
|
||||
filters.reviewerPhone = user.phone
|
||||
} else if (this.data.mode === 'pending') {
|
||||
filters.status = 'pending'
|
||||
} else {
|
||||
filters.employeePhone = user.phone
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await api.listReports(filters)
|
||||
this.setData({
|
||||
user,
|
||||
rows: result.rows,
|
||||
page: result.page,
|
||||
pageInput: String(result.page || 1),
|
||||
totalPages: result.totalPages,
|
||||
expandedRecordId: '',
|
||||
detailExpandedAll: false,
|
||||
})
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message, icon: 'none' })
|
||||
}
|
||||
},
|
||||
async onPullDownRefresh() {
|
||||
this.setData({ refreshing: true })
|
||||
try {
|
||||
await this.load(1)
|
||||
} finally {
|
||||
this.setData({ refreshing: false })
|
||||
if (wx.stopPullDownRefresh) {
|
||||
wx.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
},
|
||||
onStartChange(e) {
|
||||
this.setData({ startDate: e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
onEndChange(e) {
|
||||
this.setData({ endDate: e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
shiftRangeDate(field, days) {
|
||||
const otherField = field === 'startDate' ? 'endDate' : 'startDate'
|
||||
const current = this.data[field]
|
||||
const other = this.data[otherField]
|
||||
const base = current || other || util.today()
|
||||
const updates = {
|
||||
[field]: util.addDays(base, days),
|
||||
page: 1,
|
||||
}
|
||||
if (!current && !other) {
|
||||
updates[otherField] = base
|
||||
}
|
||||
this.setData(updates)
|
||||
this.load(1)
|
||||
},
|
||||
decreaseStartDate() {
|
||||
this.shiftRangeDate('startDate', -1)
|
||||
},
|
||||
increaseEndDate() {
|
||||
this.shiftRangeDate('endDate', 1)
|
||||
},
|
||||
chooseStatus(e) {
|
||||
this.setData({
|
||||
status: e.currentTarget.dataset.value,
|
||||
page: 1,
|
||||
})
|
||||
this.load(1)
|
||||
},
|
||||
prevPage() {
|
||||
if (this.data.page > 1) {
|
||||
this.load(this.data.page - 1)
|
||||
}
|
||||
},
|
||||
nextPage() {
|
||||
if (this.data.page < this.data.totalPages) {
|
||||
this.load(this.data.page + 1)
|
||||
}
|
||||
},
|
||||
onPageInput(e) {
|
||||
this.setData({ pageInput: e.detail.value })
|
||||
},
|
||||
jumpToPage(e) {
|
||||
wx.hideKeyboard()
|
||||
const inputValue = e && e.detail && e.detail.value !== undefined ? e.detail.value : this.data.pageInput
|
||||
const target = Math.max(1, Math.min(Number(inputValue) || this.data.page, this.data.totalPages))
|
||||
if (target === this.data.page) {
|
||||
this.setData({ pageInput: String(this.data.page || 1) })
|
||||
return
|
||||
}
|
||||
this.load(target)
|
||||
},
|
||||
openPendingReview(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({
|
||||
url: `/pages/review/review?reportId=${id}`,
|
||||
})
|
||||
},
|
||||
toggleFilterVoided() {
|
||||
this.setData({ filterVoided: !this.data.filterVoided, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
onFilterVoidedChange(e) {
|
||||
this.setData({ filterVoided: !!e.detail.value, page: 1 })
|
||||
this.load(1)
|
||||
},
|
||||
async voidReport(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.showModal({
|
||||
title: '确认作废',
|
||||
content: '作废的记录将在30天后被系统彻底删除,彻底删除前可随时撤销作废,是否确定删除',
|
||||
cancelText: '否',
|
||||
confirmText: '是',
|
||||
confirmColor: '#c5221f',
|
||||
success: async res => {
|
||||
if (!res.confirm) {
|
||||
return
|
||||
}
|
||||
wx.showLoading({ title: '处理中' })
|
||||
try {
|
||||
await api.voidReport(id)
|
||||
wx.showToast({ title: '已作废', icon: 'success' })
|
||||
await this.load(this.data.page)
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message || '作废失败', icon: 'none' })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
async unvoidReport(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.showLoading({ title: '处理中' })
|
||||
try {
|
||||
await api.unvoidReport(id)
|
||||
wx.showToast({ title: '已撤销作废', icon: 'success' })
|
||||
await this.load(this.data.page)
|
||||
} catch (error) {
|
||||
wx.showToast({ title: error.message || '撤销失败', icon: 'none' })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
openEditReview(e) {
|
||||
if (this.data.mode === 'mine') {
|
||||
return
|
||||
}
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({
|
||||
url: `/pages/review/review?reportId=${id}`,
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "报工记录"
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<scroll-view
|
||||
class="page safe-bottom"
|
||||
scroll-y
|
||||
type="list"
|
||||
scroll-top="{{scrollTop}}"
|
||||
scroll-anchoring="{{true}}"
|
||||
refresher-enabled
|
||||
refresher-triggered="{{refreshing}}"
|
||||
bindscroll="onScroll"
|
||||
bindrefresherrefresh="onPullDownRefresh"
|
||||
>
|
||||
<view class="header">
|
||||
<view class="records-header-row">
|
||||
<view class="records-header-main">
|
||||
<text class="title">{{title}}</text>
|
||||
<text class="subtitle">支持按日期和状态筛选,点击记录查看明细</text>
|
||||
</view>
|
||||
<view class="top-action-group">
|
||||
<view class="void-filter-switch">
|
||||
<text>过滤作废</text>
|
||||
<switch checked="{{filterVoided}}" color="#1463ff" bindchange="onFilterVoidedChange" />
|
||||
</view>
|
||||
<button class="btn mini secondary detail-toggle" bindtap="toggleDetails">
|
||||
{{detailExpandedAll ? '收起明细' : '展开明细'}}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-bar date-filter-bar">
|
||||
<view class="date-filter-cell">
|
||||
<button class="date-step-btn" bindtap="decreaseStartDate">-</button>
|
||||
<picker mode="date" value="{{startDate}}" bindchange="onStartChange">
|
||||
<view class="picker">{{startDate || '开始日期'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="date-filter-cell end">
|
||||
<button class="date-step-btn" bindtap="increaseEndDate">+</button>
|
||||
<picker mode="date" value="{{endDate}}" bindchange="onEndChange">
|
||||
<view class="picker">{{endDate || '结束日期'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showStatusFilter}}" class="status-row">
|
||||
<button
|
||||
wx:for="{{statusOptions}}"
|
||||
wx:key="value"
|
||||
class="status-btn {{status === item.value ? 'active' : ''}}"
|
||||
data-value="{{item.value}}"
|
||||
bindtap="chooseStatus"
|
||||
>
|
||||
{{item.label}}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!rows.length}}" class="list-empty">暂无记录</view>
|
||||
<view
|
||||
wx:for="{{rows}}"
|
||||
wx:key="id"
|
||||
id="record-{{item.id}}"
|
||||
class="card record-card {{item.isSystemAutoSubmitted ? 'system-auto' : ''}} {{item.isVoided ? 'voided' : ''}} {{expandedRecordId == item.id || detailExpandedAll ? 'expanded' : ''}}"
|
||||
data-id="{{item.id}}"
|
||||
bindtap="toggleRecordDetail"
|
||||
>
|
||||
<text wx:if="{{item.isVoided}}" class="voided-watermark">作废</text>
|
||||
<text wx:elif="{{item.isModified}}" class="modified-watermark">更改</text>
|
||||
<text wx:if="{{item.isSystemAutoSubmitted}}" class="timeout-watermark">{{item.autoSubmitReason || '超时-系统自动提交'}}</text>
|
||||
<text wx:if="{{item.isCleaning}}" class="cleaning-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified ? 'lower-watermark' : ''}}">清洗</text>
|
||||
<text wx:if="{{item.hasMisc}}" class="misc-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified ? 'lower-watermark' : ''}}">包含杂活</text>
|
||||
<text wx:if="{{item.hasMultiPerson}}" class="multi-person-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified || item.hasMisc || item.isCleaning ? 'lower-watermark' : ''}}">多人协作</text>
|
||||
<text wx:if="{{item.hasContinuousDie}}" class="continuous-die-watermark {{item.isSystemAutoSubmitted || item.isVoided || item.isModified || item.hasMisc || item.isCleaning || item.hasMultiPerson ? 'lower-watermark' : ''}} {{item.hasMultiPerson && (item.isSystemAutoSubmitted || item.isVoided || item.isModified || item.hasMisc || item.isCleaning) ? 'third-watermark' : ''}}">连续模</text>
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<text class="value">{{item.employeeName}} · {{item.reportDate}}</text>
|
||||
<text class="subtitle">考勤点 {{item.attendancePointName || '-'}}</text>
|
||||
<text class="subtitle">{{item.startAtText}} 至 {{item.endAtText}}</text>
|
||||
<text class="subtitle">{{item.metrics.shiftDistributionText || ''}}</text>
|
||||
<view wx:if="{{item.correctionPairs.length}}" class="correction-list">
|
||||
<view wx:for="{{item.correctionPairs}}" wx:key="key" wx:for-item="pair" class="correction-pair">
|
||||
<text>{{pair.label}} </text>
|
||||
<text class="old-value">{{pair.oldValue}}</text>
|
||||
<text> {{pair.newValue}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="pill {{item.status === 'approved' ? 'success' : item.status === 'pending' ? 'warn' : ''}}">{{item.statusName}}</text>
|
||||
</view>
|
||||
<view wx:if="{{mode !== 'mine'}}" class="record-action-row">
|
||||
<button wx:if="{{mode === 'pending' && !item.isVoided && item.canEdit}}" class="btn secondary record-action-btn" data-id="{{item.id}}" catchtap="openPendingReview">进入审核</button>
|
||||
<button wx:if="{{mode === 'audit' && !item.isVoided && item.canEdit}}" class="btn secondary record-action-btn" data-id="{{item.id}}" catchtap="openEditReview">更改</button>
|
||||
<button wx:if="{{!item.isVoided && item.canVoid}}" class="btn danger record-action-btn" data-id="{{item.id}}" catchtap="voidReport">作废</button>
|
||||
<button wx:if="{{item.isVoided && item.canUnvoid}}" class="btn secondary record-action-btn" data-id="{{item.id}}" catchtap="unvoidReport">撤销作废</button>
|
||||
</view>
|
||||
<view wx:if="{{(detailExpandedAll || expandedRecordId == item.id) && item.items.length}}" class="record-detail-list">
|
||||
<text class="detail-section-title">报工明细</text>
|
||||
<view
|
||||
wx:for="{{item.items}}"
|
||||
wx:key="id"
|
||||
wx:for-item="detail"
|
||||
class="record-detail-item {{detail.isMultiPerson || detail.isContinuousDie ? 'marked-detail-item' : ''}} {{detail.isMultiPerson && detail.isContinuousDie ? 'stacked-detail-item' : ''}}"
|
||||
>
|
||||
<text wx:if="{{detail.isMultiPerson}}" class="detail-multi-stamp">多人协作</text>
|
||||
<text wx:if="{{detail.isContinuousDie}}" class="detail-continuous-stamp {{detail.isMultiPerson ? 'lower-detail-stamp' : ''}}">连续模</text>
|
||||
<view class="row detail-head">
|
||||
<view class="row-left">
|
||||
<text class="detail-product">{{detail.displayName || detail.productName || '-'}}</text>
|
||||
<text class="detail-meta">考勤点 {{detail.attendancePointName || item.attendancePointName || '-'}}</text>
|
||||
<text class="detail-meta">项目 {{detail.projectNo || '-'}} · 工序 {{detail.process || '-'}}</text>
|
||||
<text class="detail-meta">冲压方式 {{detail.stampingMethod || '-'}}</text>
|
||||
<text class="detail-meta">材料库存批次号 {{detail.rawMaterialBatchNo || '-'}}</text>
|
||||
</view>
|
||||
<text class="pill primary device-pill">{{detail.deviceNo || '-'}}</text>
|
||||
</view>
|
||||
<view class="detail-qty-grid">
|
||||
<view>
|
||||
<text class="label">{{detail.isMisc ? '处理杂活' : (detail.isCleaning ? '清洗数量' : '成品数量')}}</text>
|
||||
<text class="value">{{detail.isMisc ? '不计产量' : detail.goodQty}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!detail.isCleaning && !detail.isMisc}}">
|
||||
<text class="label">不良数量</text>
|
||||
<text class="value">{{detail.defectQty}}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="label">工时</text>
|
||||
<text class="value">{{detail.workTimeText}}</text>
|
||||
</view>
|
||||
<view wx:if="{{detail.isMisc}}">
|
||||
<text class="label">杂活事项</text>
|
||||
<text class="value">{{detail.remark || '待备注'}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!detail.isCleaning && !detail.isMisc}}">
|
||||
<text class="label">实际节拍</text>
|
||||
<text class="value {{detail.beatCompareClass}}">
|
||||
{{detail.actualBeat}} 秒
|
||||
<text wx:if="{{detail.beatReason}}" class="metric-reason">{{detail.beatReason}}</text>
|
||||
</text>
|
||||
</view>
|
||||
<view wx:if="{{!detail.isCleaning && !detail.isMisc}}">
|
||||
<text class="label">节拍对比</text>
|
||||
<text class="value {{detail.beatCompareClass}}">{{detail.paceText}}</text>
|
||||
</view>
|
||||
<view wx:if="{{!detail.isCleaning && !detail.isMisc}}">
|
||||
<text class="label">工作量对比</text>
|
||||
<text class="value {{detail.workloadCompareClass}}">{{detail.workloadText}}</text>
|
||||
</view>
|
||||
<view wx:if="{{detail.isContinuousDie}}">
|
||||
<text class="label">换料次数</text>
|
||||
<text class="value">{{detail.changeoverCount || 0}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{detail.correctionPairs.length}}" class="correction-list detail-corrections">
|
||||
<view wx:for="{{detail.correctionPairs}}" wx:key="key" wx:for-item="pair" class="correction-pair">
|
||||
<text>{{pair.label}} </text>
|
||||
<text class="old-value">{{pair.oldValue}}</text>
|
||||
<text> {{pair.newValue}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="button-row pager-row" wx:if="{{rows.length}}">
|
||||
<button class="btn secondary pager-nav" bindtap="prevPage" disabled="{{page <= 1}}">上一页</button>
|
||||
<view class="page-jump-control">
|
||||
<input class="page-jump-input" type="number" confirm-type="done" value="{{pageInput}}" bindinput="onPageInput" bindconfirm="jumpToPage" />
|
||||
<text class="page-total-label">/ {{totalPages}}</text>
|
||||
<text class="page-jump-action" bindtap="jumpToPage">跳</text>
|
||||
</view>
|
||||
<button class="btn secondary pager-nav" bindtap="nextPage" disabled="{{page >= totalPages}}">下一页</button>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -0,0 +1,284 @@
|
||||
.records-header-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.records-header-main {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.detail-toggle {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
flex: 0 0 auto;
|
||||
min-width: 150rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
margin: 4rpx 0 0;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 14rpx;
|
||||
color: #1463ff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.records-header-row .top-action-group {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 240rpx minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.records-header-row .void-filter-switch {
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
.records-header-row .detail-toggle {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 68rpx;
|
||||
line-height: 68rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.status-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 6rpx;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(180deg, #f7faff 0%, #e4ebf5 100%);
|
||||
box-shadow:
|
||||
inset 0 4rpx 8rpx rgba(22, 34, 50, 0.06),
|
||||
0 8rpx 0 rgba(205, 216, 229, 0.48),
|
||||
0 12rpx 18rpx rgba(22, 34, 50, 0.07);
|
||||
}
|
||||
|
||||
.status-btn {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
margin: 0;
|
||||
padding: 0 10rpx;
|
||||
border-radius: 14rpx;
|
||||
background: transparent;
|
||||
color: #475467;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.status-btn::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.status-btn.active {
|
||||
background: linear-gradient(180deg, #2876ff 0%, #1456c2 100%);
|
||||
color: #ffffff;
|
||||
box-shadow:
|
||||
0 7rpx 0 #0d3f93,
|
||||
0 12rpx 18rpx rgba(20, 99, 255, 0.2),
|
||||
inset 0 2rpx 0 rgba(255, 255, 255, 0.24);
|
||||
}
|
||||
|
||||
.record-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18rpx;
|
||||
padding: 4rpx 0;
|
||||
}
|
||||
|
||||
.record-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-color: #aebbd0;
|
||||
}
|
||||
|
||||
.cleaning-watermark {
|
||||
position: absolute;
|
||||
right: 28rpx;
|
||||
top: 18rpx;
|
||||
z-index: 1;
|
||||
padding: 8rpx 18rpx;
|
||||
border: 2rpx solid rgba(18, 163, 117, 0.55);
|
||||
border-radius: 12rpx;
|
||||
color: rgba(8, 116, 67, 0.72);
|
||||
background: rgba(226, 255, 243, 0.82);
|
||||
font-size: 30rpx;
|
||||
font-weight: 900;
|
||||
transform: rotate(10deg);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.record-card.expanded {
|
||||
border-color: #1463ff;
|
||||
box-shadow:
|
||||
0 0 0 2rpx rgba(255, 255, 255, 0.9),
|
||||
0 13rpx 0 rgba(20, 86, 194, 0.48),
|
||||
0 24rpx 36rpx rgba(20, 86, 194, 0.16),
|
||||
inset 0 0 0 1rpx rgba(20, 99, 255, 0.24),
|
||||
inset 0 2rpx 0 rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.pending-action-row,
|
||||
.record-action-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.review-entry-btn,
|
||||
.record-action-btn {
|
||||
width: 172rpx;
|
||||
height: 62rpx;
|
||||
line-height: 62rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.review-entry-btn,
|
||||
.record-action-btn.secondary {
|
||||
color: #1463ff;
|
||||
}
|
||||
|
||||
.record-detail-list {
|
||||
margin-top: 20rpx;
|
||||
padding: 18rpx;
|
||||
border: 2rpx solid #bfcadd;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f4f8fd 100%);
|
||||
box-shadow:
|
||||
inset 0 0 0 1rpx rgba(255, 255, 255, 0.86),
|
||||
0 8rpx 0 rgba(177, 191, 211, 0.58),
|
||||
0 14rpx 22rpx rgba(22, 34, 50, 0.08);
|
||||
}
|
||||
|
||||
.detail-section-title {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
color: #162232;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.record-detail-item {
|
||||
position: relative;
|
||||
padding: 18rpx;
|
||||
border: 2rpx solid #aebbd0;
|
||||
border-radius: 14rpx;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
|
||||
box-shadow:
|
||||
0 0 0 2rpx rgba(255, 255, 255, 0.82),
|
||||
0 8rpx 0 rgba(176, 190, 209, 0.64),
|
||||
0 14rpx 20rpx rgba(22, 34, 50, 0.08),
|
||||
inset 0 0 0 1rpx rgba(174, 187, 208, 0.34),
|
||||
inset 0 2rpx 0 rgba(255, 255, 255, 0.94);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.detail-multi-stamp {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
top: 14rpx;
|
||||
z-index: 2;
|
||||
padding: 7rpx 14rpx;
|
||||
border: 2rpx solid rgba(217, 119, 6, 0.62);
|
||||
border-radius: 10rpx;
|
||||
color: rgba(154, 74, 5, 0.88);
|
||||
background: rgba(255, 247, 232, 0.92);
|
||||
font-size: 23rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.detail-continuous-stamp {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
top: 14rpx;
|
||||
z-index: 2;
|
||||
padding: 7rpx 14rpx;
|
||||
border: 2rpx solid rgba(217, 119, 6, 0.62);
|
||||
border-radius: 10rpx;
|
||||
color: rgba(154, 74, 5, 0.88);
|
||||
background: rgba(255, 247, 232, 0.92);
|
||||
font-size: 23rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.detail-continuous-stamp.lower-detail-stamp {
|
||||
top: 56rpx;
|
||||
}
|
||||
|
||||
.record-detail-item:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.record-detail-item + .record-detail-item {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.record-detail-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.record-detail-item.multi-detail-item,
|
||||
.record-detail-item.marked-detail-item {
|
||||
padding-top: 56rpx;
|
||||
}
|
||||
|
||||
.record-detail-item.stacked-detail-item {
|
||||
padding-top: 96rpx;
|
||||
}
|
||||
|
||||
.detail-head {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.device-pill {
|
||||
max-width: 260rpx;
|
||||
height: auto;
|
||||
min-height: 44rpx;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.3;
|
||||
padding-top: 8rpx;
|
||||
padding-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.detail-product {
|
||||
display: block;
|
||||
color: #162232;
|
||||
font-size: 27rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
color: #667085;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.detail-qty-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14rpx 18rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user