|
|
@@ -1,433 +1,970 @@
|
|
|
<template>
|
|
|
+
|
|
|
<view class="income-page">
|
|
|
+
|
|
|
<!-- 筛选栏 -->
|
|
|
+
|
|
|
<view class="filter-bar">
|
|
|
- <view class="filter-item" @click="openFilterModal">
|
|
|
+
|
|
|
+ <view class="filter-item" @click="openServiceModal">
|
|
|
+
|
|
|
<text>{{ serviceFilterLabel }}</text>
|
|
|
+
|
|
|
<text class="arrow">▼</text>
|
|
|
+
|
|
|
</view>
|
|
|
- <view class="filter-item" @click="openFilterModal">
|
|
|
+
|
|
|
+ <view class="filter-item" @click="openStatusModal">
|
|
|
+
|
|
|
<text>{{ statusFilterLabel }}</text>
|
|
|
+
|
|
|
<text class="arrow">▼</text>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</view>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
<!-- 日期 & 合计 -->
|
|
|
+
|
|
|
<view class="summary-row">
|
|
|
+
|
|
|
<view class="date-range" @click="openDatePicker">
|
|
|
+
|
|
|
<text>{{ dateRangeText }}</text>
|
|
|
+
|
|
|
<text class="arrow">▼</text>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
<text class="total" @click="onTotalClick">合计: {{ totalText }}</text>
|
|
|
+
|
|
|
</view>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
<!-- 收入列表 -->
|
|
|
+
|
|
|
<view class="income-list" v-if="displayList.length">
|
|
|
+
|
|
|
<view class="income-card" v-for="item in displayList" :key="item.id">
|
|
|
- <view class="card-left">
|
|
|
- <view class="category-icon"></view>
|
|
|
- <view class="card-info">
|
|
|
- <view class="info-top">
|
|
|
- <text class="category-name">{{ item.categoryName }}</text>
|
|
|
- </view>
|
|
|
- <text class="service-name">{{ item.serviceName }}</text>
|
|
|
- <text class="order-time">{{ item.completionTime }}</text>
|
|
|
+
|
|
|
+ <view class="card-row card-row-top">
|
|
|
+
|
|
|
+ <view class="category-wrap">
|
|
|
+
|
|
|
+ <view class="category-icon"></view>
|
|
|
+
|
|
|
+ <text class="category-name">{{ item.categoryName }}</text>
|
|
|
+
|
|
|
</view>
|
|
|
- </view>
|
|
|
- <view class="card-right">
|
|
|
+
|
|
|
<text class="settle-status" :class="item.status">
|
|
|
- {{ item.status === 'settled' ? '已结算' : '待结算' }}
|
|
|
+
|
|
|
+ {{ item.status === 'settled' ? '已结算' : '未结算' }}
|
|
|
+
|
|
|
</text>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-row card-row-middle">
|
|
|
+
|
|
|
+ <text class="service-name">{{ item.serviceName }}</text>
|
|
|
+
|
|
|
<text class="income-amount">+{{ formatAmount(item.incomeAmount) }}</text>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="card-row card-row-bottom">
|
|
|
+
|
|
|
+ <text class="order-time">{{ item.completionTime }}</text>
|
|
|
+
|
|
|
<text class="order-total">{{ formatAmount(item.orderTotal) }}</text>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
<view class="empty" v-else>暂无收入记录</view>
|
|
|
|
|
|
- <!-- 筛选弹窗 -->
|
|
|
- <view class="modal-mask" v-if="showFilterModal" @click="showFilterModal = false">
|
|
|
+
|
|
|
+
|
|
|
+ <!-- 服务分类弹窗 -->
|
|
|
+
|
|
|
+ <view class="modal-mask" v-if="showServiceModal" @click="closeServiceModal">
|
|
|
+
|
|
|
<view class="filter-panel" @click.stop>
|
|
|
- <view class="panel-section">
|
|
|
- <text class="section-title">服务分类</text>
|
|
|
- <view class="tag-list">
|
|
|
- <view
|
|
|
- v-for="cat in serviceCategories"
|
|
|
- :key="cat.id"
|
|
|
- class="tag"
|
|
|
- :class="{ active: draftCategoryId === cat.id }"
|
|
|
- @click="draftCategoryId = cat.id"
|
|
|
- >{{ cat.name }}</view>
|
|
|
- </view>
|
|
|
+
|
|
|
+ <view class="panel-header">
|
|
|
+
|
|
|
+ <text class="panel-title">服务分类</text>
|
|
|
+
|
|
|
</view>
|
|
|
- <view class="panel-section">
|
|
|
- <text class="section-title">结算状态</text>
|
|
|
- <view class="tag-list">
|
|
|
- <view
|
|
|
- v-for="opt in settleOptions"
|
|
|
- :key="opt.key"
|
|
|
- class="tag"
|
|
|
- :class="{ active: draftStatus === opt.key }"
|
|
|
- @click="draftStatus = opt.key"
|
|
|
- >{{ opt.label }}</view>
|
|
|
- </view>
|
|
|
+
|
|
|
+ <view class="tag-grid">
|
|
|
+
|
|
|
+ <view
|
|
|
+
|
|
|
+ v-for="cat in serviceCategories"
|
|
|
+
|
|
|
+ :key="cat.id"
|
|
|
+
|
|
|
+ class="tag"
|
|
|
+
|
|
|
+ :class="{ active: draftCategoryId === cat.id }"
|
|
|
+
|
|
|
+ @click="draftCategoryId = cat.id"
|
|
|
+
|
|
|
+ >{{ cat.name }}</view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="panel-btns">
|
|
|
+
|
|
|
+ <view class="btn cancel" @click="closeServiceModal">取消</view>
|
|
|
+
|
|
|
+ <view class="btn confirm" @click="confirmService">确定</view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <!-- 结算状态弹窗 -->
|
|
|
+
|
|
|
+ <view class="modal-mask" v-if="showStatusModal" @click="closeStatusModal">
|
|
|
+
|
|
|
+ <view class="filter-panel" @click.stop>
|
|
|
+
|
|
|
+ <view class="panel-header">
|
|
|
+
|
|
|
+ <text class="panel-title">结算状态</text>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="tag-row">
|
|
|
+
|
|
|
+ <view
|
|
|
+
|
|
|
+ v-for="opt in settleOptions"
|
|
|
+
|
|
|
+ :key="opt.key"
|
|
|
+
|
|
|
+ class="tag"
|
|
|
+
|
|
|
+ :class="{ active: draftStatus === opt.key }"
|
|
|
+
|
|
|
+ @click="draftStatus = opt.key"
|
|
|
+
|
|
|
+ >{{ opt.label }}</view>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
<view class="panel-btns">
|
|
|
- <view class="btn cancel" @click="showFilterModal = false">取消</view>
|
|
|
- <view class="btn confirm" @click="confirmFilter">确定</view>
|
|
|
+
|
|
|
+ <view class="btn cancel" @click="closeStatusModal">取消</view>
|
|
|
+
|
|
|
+ <view class="btn confirm" @click="confirmStatus">确定</view>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</view>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
<!-- 日期选择 -->
|
|
|
+
|
|
|
<u-datetime-picker
|
|
|
+
|
|
|
:show="showDatePicker"
|
|
|
+
|
|
|
v-model="pickerValue"
|
|
|
+
|
|
|
mode="date"
|
|
|
+
|
|
|
@confirm="onDateConfirm"
|
|
|
+
|
|
|
@cancel="showDatePicker = false"
|
|
|
+
|
|
|
></u-datetime-picker>
|
|
|
+
|
|
|
</view>
|
|
|
+
|
|
|
</template>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
<script>
|
|
|
+
|
|
|
import {
|
|
|
+
|
|
|
SERVICE_CATEGORIES,
|
|
|
+
|
|
|
SETTLE_STATUS_OPTIONS,
|
|
|
+
|
|
|
MOCK_INCOME_LIST,
|
|
|
+
|
|
|
formatIncomeTotal,
|
|
|
+
|
|
|
formatDateYmd,
|
|
|
+
|
|
|
getMockDateRange,
|
|
|
+
|
|
|
parseTimeStr,
|
|
|
+
|
|
|
} from './mock.js'
|
|
|
|
|
|
+
|
|
|
+
|
|
|
export default {
|
|
|
+
|
|
|
data() {
|
|
|
+
|
|
|
const range = getMockDateRange()
|
|
|
+
|
|
|
return {
|
|
|
+
|
|
|
allList: [...MOCK_INCOME_LIST],
|
|
|
+
|
|
|
serviceCategories: SERVICE_CATEGORIES,
|
|
|
+
|
|
|
settleOptions: SETTLE_STATUS_OPTIONS,
|
|
|
+
|
|
|
categoryId: 'all',
|
|
|
+
|
|
|
settleStatus: 'all',
|
|
|
+
|
|
|
draftCategoryId: 'all',
|
|
|
+
|
|
|
draftStatus: 'all',
|
|
|
+
|
|
|
dateStart: range.start,
|
|
|
+
|
|
|
dateEnd: range.end,
|
|
|
+
|
|
|
dateStartTs: range.startTs,
|
|
|
+
|
|
|
dateEndTs: range.endTs,
|
|
|
- showFilterModal: false,
|
|
|
+
|
|
|
+ showServiceModal: false,
|
|
|
+
|
|
|
+ showStatusModal: false,
|
|
|
+
|
|
|
showDatePicker: false,
|
|
|
+
|
|
|
pickerValue: Date.now(),
|
|
|
+
|
|
|
datePickTarget: 'start',
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
computed: {
|
|
|
+
|
|
|
serviceFilterLabel() {
|
|
|
+
|
|
|
if (this.categoryId === 'all') return '全部服务'
|
|
|
+
|
|
|
const cat = this.serviceCategories.find(c => c.id === this.categoryId)
|
|
|
+
|
|
|
return cat ? cat.name : '全部服务'
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
statusFilterLabel() {
|
|
|
- if (this.settleStatus === 'all') return '结算状态'
|
|
|
+
|
|
|
+ if (this.settleStatus === 'all') return '全部状态'
|
|
|
+
|
|
|
const opt = this.settleOptions.find(o => o.key === this.settleStatus)
|
|
|
- return opt ? opt.label : '结算状态'
|
|
|
+
|
|
|
+ return opt ? opt.label : '全部状态'
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
dateRangeText() {
|
|
|
- return `${this.dateStart} 至 ${this.dateEnd}`
|
|
|
+
|
|
|
+ return `${this.dateStart} - ${this.dateEnd}`
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
displayList() {
|
|
|
+
|
|
|
let list = [...this.allList]
|
|
|
+
|
|
|
if (this.categoryId !== 'all') {
|
|
|
+
|
|
|
list = list.filter(item => item.categoryId === this.categoryId)
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
if (this.settleStatus === 'settled') {
|
|
|
+
|
|
|
list = list.filter(item => item.status === 'settled')
|
|
|
+
|
|
|
} else if (this.settleStatus === 'unsettled') {
|
|
|
+
|
|
|
list = list.filter(item => item.status === 'unsettled')
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
list = list.filter(item => {
|
|
|
+
|
|
|
const ts = parseTimeStr(item.completionTime)
|
|
|
+
|
|
|
return ts >= this.dateStartTs && ts <= this.dateEndTs + 86400000 - 1
|
|
|
+
|
|
|
})
|
|
|
+
|
|
|
return list.sort((a, b) => parseTimeStr(b.completionTime) - parseTimeStr(a.completionTime))
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
totalAmount() {
|
|
|
+
|
|
|
return this.displayList.reduce((sum, item) => sum + Number(item.incomeAmount || 0), 0)
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
totalText() {
|
|
|
+
|
|
|
return formatIncomeTotal(this.totalAmount)
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
methods: {
|
|
|
+
|
|
|
formatAmount(val) {
|
|
|
+
|
|
|
return Number(val || 0).toFixed(2)
|
|
|
+
|
|
|
},
|
|
|
- openFilterModal() {
|
|
|
+
|
|
|
+ openServiceModal() {
|
|
|
+
|
|
|
this.draftCategoryId = this.categoryId
|
|
|
- this.draftStatus = this.settleStatus
|
|
|
- this.showFilterModal = true
|
|
|
+
|
|
|
+ this.showServiceModal = true
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ closeServiceModal() {
|
|
|
+
|
|
|
+ this.showServiceModal = false
|
|
|
+
|
|
|
},
|
|
|
- confirmFilter() {
|
|
|
+
|
|
|
+ confirmService() {
|
|
|
+
|
|
|
this.categoryId = this.draftCategoryId
|
|
|
+
|
|
|
+ this.showServiceModal = false
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ openStatusModal() {
|
|
|
+
|
|
|
+ this.draftStatus = this.settleStatus
|
|
|
+
|
|
|
+ this.showStatusModal = true
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ closeStatusModal() {
|
|
|
+
|
|
|
+ this.showStatusModal = false
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ confirmStatus() {
|
|
|
+
|
|
|
this.settleStatus = this.draftStatus
|
|
|
- this.showFilterModal = false
|
|
|
+
|
|
|
+ this.showStatusModal = false
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
openDatePicker() {
|
|
|
+
|
|
|
this.datePickTarget = 'start'
|
|
|
+
|
|
|
this.pickerValue = this.dateStartTs
|
|
|
+
|
|
|
this.showDatePicker = true
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
onDateConfirm(e) {
|
|
|
+
|
|
|
const ts = e.value
|
|
|
+
|
|
|
const formatted = formatDateYmd(new Date(ts))
|
|
|
+
|
|
|
if (this.datePickTarget === 'start') {
|
|
|
+
|
|
|
this.dateStart = formatted
|
|
|
+
|
|
|
this.dateStartTs = ts
|
|
|
+
|
|
|
this.datePickTarget = 'end'
|
|
|
+
|
|
|
this.pickerValue = this.dateEndTs
|
|
|
+
|
|
|
this.showDatePicker = false
|
|
|
+
|
|
|
this.$nextTick(() => {
|
|
|
+
|
|
|
this.showDatePicker = true
|
|
|
+
|
|
|
})
|
|
|
+
|
|
|
} else {
|
|
|
+
|
|
|
this.dateEnd = formatted
|
|
|
+
|
|
|
this.dateEndTs = ts
|
|
|
+
|
|
|
if (this.dateStartTs > this.dateEndTs) {
|
|
|
+
|
|
|
const tmpS = this.dateStart
|
|
|
+
|
|
|
const tmpSt = this.dateStartTs
|
|
|
+
|
|
|
this.dateStart = this.dateEnd
|
|
|
+
|
|
|
this.dateEnd = tmpS
|
|
|
+
|
|
|
this.dateStartTs = this.dateEndTs
|
|
|
+
|
|
|
this.dateEndTs = tmpSt
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
this.showDatePicker = false
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
onTotalClick() {
|
|
|
+
|
|
|
uni.showToast({ title: '收入明细待对接', icon: 'none' })
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
},
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
+
|
|
|
+
|
|
|
<style lang="scss" scoped>
|
|
|
+
|
|
|
.income-page {
|
|
|
+
|
|
|
min-height: 100vh;
|
|
|
+
|
|
|
background: #f5f5f5;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.filter-bar {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
background: #fff;
|
|
|
- border-bottom: 1rpx solid #eee;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.filter-item {
|
|
|
+
|
|
|
flex: 1;
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
align-items: center;
|
|
|
+
|
|
|
justify-content: center;
|
|
|
- padding: 24rpx 0;
|
|
|
+
|
|
|
+ padding: 28rpx 0;
|
|
|
+
|
|
|
font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
+
|
|
|
+ color: #666;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.arrow {
|
|
|
- font-size: 20rpx;
|
|
|
- color: #999;
|
|
|
+
|
|
|
+ font-size: 18rpx;
|
|
|
+
|
|
|
+ color: #bbb;
|
|
|
+
|
|
|
margin-left: 8rpx;
|
|
|
+
|
|
|
+ transform: scale(0.85);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.summary-row {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
align-items: center;
|
|
|
+
|
|
|
justify-content: space-between;
|
|
|
+
|
|
|
padding: 20rpx 32rpx;
|
|
|
- background: #fff;
|
|
|
- border-bottom: 1rpx solid #eee;
|
|
|
+
|
|
|
+ background: #f5f5f5;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.date-range {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
align-items: center;
|
|
|
+
|
|
|
font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
+
|
|
|
+ color: #666;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.total {
|
|
|
+
|
|
|
font-size: 26rpx;
|
|
|
- color: #333;
|
|
|
- font-weight: 500;
|
|
|
+
|
|
|
+ color: #666;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.income-list {
|
|
|
- padding: 16rpx 0;
|
|
|
+
|
|
|
+ padding: 0 24rpx 32rpx;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.income-card {
|
|
|
+
|
|
|
+ background: #fff;
|
|
|
+
|
|
|
+ border-radius: 16rpx;
|
|
|
+
|
|
|
+ padding: 28rpx 24rpx;
|
|
|
+
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.card-row {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
justify-content: space-between;
|
|
|
- background: #fff;
|
|
|
- padding: 28rpx 32rpx;
|
|
|
- border-bottom: 1rpx solid #f5f5f5;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-.card-left {
|
|
|
+
|
|
|
+
|
|
|
+.card-row-top {
|
|
|
+
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.category-wrap {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
flex: 1;
|
|
|
+
|
|
|
overflow: hidden;
|
|
|
+
|
|
|
+ margin-right: 16rpx;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.category-icon {
|
|
|
- width: 72rpx;
|
|
|
- height: 72rpx;
|
|
|
- background: #eee;
|
|
|
- border-radius: 8rpx;
|
|
|
+
|
|
|
+ width: 40rpx;
|
|
|
+
|
|
|
+ height: 40rpx;
|
|
|
+
|
|
|
+ background: #3d4f5f;
|
|
|
+
|
|
|
+ border-radius: 50%;
|
|
|
+
|
|
|
flex-shrink: 0;
|
|
|
- margin-right: 20rpx;
|
|
|
-}
|
|
|
|
|
|
-.card-info {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
+ margin-right: 12rpx;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.category-name {
|
|
|
+
|
|
|
font-size: 28rpx;
|
|
|
+
|
|
|
color: #333;
|
|
|
+
|
|
|
font-weight: 500;
|
|
|
-}
|
|
|
|
|
|
-.service-name {
|
|
|
- display: block;
|
|
|
- font-size: 26rpx;
|
|
|
- color: #666;
|
|
|
- margin-top: 8rpx;
|
|
|
overflow: hidden;
|
|
|
+
|
|
|
text-overflow: ellipsis;
|
|
|
+
|
|
|
white-space: nowrap;
|
|
|
-}
|
|
|
|
|
|
-.order-time {
|
|
|
- display: block;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
- margin-top: 12rpx;
|
|
|
}
|
|
|
|
|
|
-.card-right {
|
|
|
- text-align: right;
|
|
|
- flex-shrink: 0;
|
|
|
- margin-left: 16rpx;
|
|
|
-}
|
|
|
+
|
|
|
|
|
|
.settle-status {
|
|
|
- display: block;
|
|
|
- font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
- margin-bottom: 8rpx;
|
|
|
+
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ font-size: 22rpx;
|
|
|
+
|
|
|
+ padding: 4rpx 16rpx;
|
|
|
+
|
|
|
+ border-radius: 6rpx;
|
|
|
+
|
|
|
+
|
|
|
|
|
|
&.unsettled {
|
|
|
- color: #666;
|
|
|
+
|
|
|
+ color: #fa8c16;
|
|
|
+
|
|
|
+ background: #fff7e6;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ &.settled {
|
|
|
+
|
|
|
+ color: #52c41a;
|
|
|
+
|
|
|
+ background: #f6ffed;
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.card-row-middle {
|
|
|
+
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.service-name {
|
|
|
+
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ font-size: 28rpx;
|
|
|
+
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ text-overflow: ellipsis;
|
|
|
+
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ margin-right: 16rpx;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.income-amount {
|
|
|
- display: block;
|
|
|
+
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
font-size: 32rpx;
|
|
|
+
|
|
|
color: #e54d42;
|
|
|
+
|
|
|
font-weight: 600;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+.card-row-bottom {
|
|
|
+
|
|
|
+ align-items: flex-end;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+.order-time {
|
|
|
+
|
|
|
+ font-size: 24rpx;
|
|
|
+
|
|
|
+ color: #bbb;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
.order-total {
|
|
|
- display: block;
|
|
|
+
|
|
|
font-size: 24rpx;
|
|
|
- color: #999;
|
|
|
- margin-top: 4rpx;
|
|
|
+
|
|
|
+ color: #bbb;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.empty {
|
|
|
+
|
|
|
text-align: center;
|
|
|
+
|
|
|
padding: 120rpx 0;
|
|
|
+
|
|
|
font-size: 28rpx;
|
|
|
+
|
|
|
color: #999;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.modal-mask {
|
|
|
+
|
|
|
position: fixed;
|
|
|
+
|
|
|
left: 0;
|
|
|
+
|
|
|
top: 0;
|
|
|
+
|
|
|
right: 0;
|
|
|
+
|
|
|
bottom: 0;
|
|
|
+
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
+
|
|
|
z-index: 999;
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
align-items: flex-end;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.filter-panel {
|
|
|
+
|
|
|
width: 100%;
|
|
|
+
|
|
|
background: #fff;
|
|
|
+
|
|
|
border-radius: 24rpx 24rpx 0 0;
|
|
|
- padding: 32rpx 32rpx calc(env(safe-area-inset-bottom) + 32rpx);
|
|
|
+
|
|
|
+ padding: 0 32rpx calc(env(safe-area-inset-bottom) + 32rpx);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-.panel-section {
|
|
|
- margin-bottom: 32rpx;
|
|
|
+
|
|
|
+
|
|
|
+.panel-header {
|
|
|
+
|
|
|
+ padding: 32rpx 0 28rpx;
|
|
|
+
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-.section-title {
|
|
|
+
|
|
|
+
|
|
|
+.panel-title {
|
|
|
+
|
|
|
display: block;
|
|
|
- font-size: 28rpx;
|
|
|
+
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ font-size: 30rpx;
|
|
|
+
|
|
|
color: #333;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 20rpx;
|
|
|
+
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-.tag-list {
|
|
|
+
|
|
|
+
|
|
|
+.tag-grid {
|
|
|
+
|
|
|
display: flex;
|
|
|
+
|
|
|
flex-wrap: wrap;
|
|
|
- gap: 16rpx;
|
|
|
+
|
|
|
+ gap: 20rpx;
|
|
|
+
|
|
|
+ padding: 32rpx 0 40rpx;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ .tag {
|
|
|
+
|
|
|
+ width: calc((100% - 40rpx) / 3);
|
|
|
+
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+.tag-row {
|
|
|
+
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ gap: 20rpx;
|
|
|
+
|
|
|
+ padding: 32rpx 0 40rpx;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ .tag {
|
|
|
+
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
.tag {
|
|
|
- padding: 12rpx 28rpx;
|
|
|
+
|
|
|
+ padding: 18rpx 0;
|
|
|
+
|
|
|
font-size: 26rpx;
|
|
|
- color: #666;
|
|
|
- border: 1rpx solid #ddd;
|
|
|
+
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ border: 1rpx solid #e8e8e8;
|
|
|
+
|
|
|
border-radius: 8rpx;
|
|
|
+
|
|
|
background: #fff;
|
|
|
|
|
|
+
|
|
|
+
|
|
|
&.active {
|
|
|
- color: #0879ff;
|
|
|
- border-color: #0879ff;
|
|
|
- background: #f0f7ff;
|
|
|
+
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ border-color: #00c08b;
|
|
|
+
|
|
|
+ background: #e8f8f4;
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.panel-btns {
|
|
|
+
|
|
|
display: flex;
|
|
|
- gap: 20rpx;
|
|
|
- margin-top: 16rpx;
|
|
|
+
|
|
|
+ gap: 24rpx;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
.btn {
|
|
|
+
|
|
|
flex: 1;
|
|
|
- height: 80rpx;
|
|
|
- line-height: 80rpx;
|
|
|
+
|
|
|
+ height: 88rpx;
|
|
|
+
|
|
|
+ line-height: 88rpx;
|
|
|
+
|
|
|
text-align: center;
|
|
|
- border-radius: 8rpx;
|
|
|
+
|
|
|
+ border-radius: 44rpx;
|
|
|
+
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
+
|
|
|
+
|
|
|
&.cancel {
|
|
|
- border: 1rpx solid #ddd;
|
|
|
- color: #666;
|
|
|
+
|
|
|
+ background: #f5f5f5;
|
|
|
+
|
|
|
+ color: #333;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
&.confirm {
|
|
|
- background: #0879ff;
|
|
|
+
|
|
|
+ background: #333;
|
|
|
+
|
|
|
color: #fff;
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
+
|
|
|
</style>
|
|
|
+
|