|
|
- <template>
- <view class="container">
- <!-- 顶部绿色区域 -->
- <view class="top-section">
- <view class="header">
- <view class="back-icon" @tap="navigateBack">
- <uni-icons type="left" size="20"></uni-icons>
- </view>
- <text class="title">订单管理</text>
- </view>
-
- <!-- 状态标签栏 -->
- <view class="status-tabs">
- <view
- v-for="(tab, index) in statusTabs"
- :key="index"
- class="tab-item"
- :class="{ active: currentStatus === tab.value }"
- @tap="switchStatus(tab.value)"
- >
- <text>{{ tab.label }}</text>
- </view>
- </view>
- </view>
-
- <!-- 筛选栏 -->
- <view class="filter-bar">
- <view class="filter-item" @tap="showTimePicker">
- <text :class="{ 'text-green': showTimePickerModal || selectedTime }">{{ selectedTime || '处理时间' }}</text>
- <uni-icons :type="showTimePickerModal ? 'up' : 'down'" size="14" :color="showTimePickerModal || selectedTime ? '#00C853' : '#666'"></uni-icons>
- </view>
- <view class="search-area">
- <view
- class="search-box"
- :class="{ active: searchActive }"
- v-if="searchActive"
- >
- <uni-icons type="search" size="16" color="#999"></uni-icons>
- <input
- type="text"
- v-model="searchKey"
- placeholder="搜索订单号/用户名"
- focus
- />
- <uni-icons
- v-if="searchKey"
- type="clear"
- size="16"
- color="#bbb"
- @tap="searchKey = ''"
- ></uni-icons>
- <text class="cancel-btn" @tap="searchActive = false; searchKey = ''">取消</text>
- </view>
- <view
- class="search-icon"
- v-else
- @tap="searchActive = true"
- >
- <uni-icons type="search" size="20" color="#999"></uni-icons>
- </view>
- </view>
- </view>
-
- <!-- 时间选择器弹窗 -->
- <view
- v-if="showTimePickerModal"
- class="time-picker-overlay"
- @tap="closeTimePicker"
- @touchmove.stop.prevent
- >
- <view
- class="time-picker-wrapper"
- :style="{
- position: 'fixed',
- top: timePickerStyle.top + 'px',
- left: 0,
- width: '100vw'
- }"
- @tap.stop
- >
- <view class="time-type-header">
- <view
- class="type-item"
- :class="{ active: activeTimeType === 'start' }"
- @tap="switchTimeType('start')"
- >
- 开始时间
- <view class="active-line" v-if="activeTimeType === 'start'"></view>
- </view>
- <view class="type-divider">至</view>
- <view
- class="type-item"
- :class="{ active: activeTimeType === 'end' }"
- @tap="switchTimeType('end')"
- >
- 结束时间
- <view class="active-line" v-if="activeTimeType === 'end'"></view>
- </view>
- </view>
- <view class="time-picker-content">
- <view class="select-mask">
- <view class="select-line"></view>
- <view class="select-line"></view>
- </view>
- <picker-view
- class="picker-view"
- :value="currentDateIndexes"
- @change="handlePickerChange"
- :indicator-style="'height: 88rpx;'"
- >
- <picker-view-column>
- <view
- class="picker-item"
- v-for="year in yearOptions"
- :key="year"
- >
- {{year}}年
- </view>
- </picker-view-column>
- <picker-view-column>
- <view
- class="picker-item"
- v-for="month in monthOptions"
- :key="month"
- >
- {{month}}月
- </view>
- </picker-view-column>
- <picker-view-column>
- <view
- class="picker-item"
- v-for="day in dayOptions"
- :key="day"
- >
- {{day}}日
- </view>
- </picker-view-column>
- </picker-view>
- </view>
- <view class="time-picker-footer">
- <view class="btn btn-reset" @tap="resetTimePicker">重置</view>
- <view class="btn btn-confirm" @tap="confirmTimePicker">确认</view>
- </view>
- </view>
- </view>
-
- <!-- 订单列表 -->
- <scroll-view
- scroll-y
- class="order-list"
- @scrolltolower="loadMore"
- >
- <view
- v-for="(order, index) in filteredOrders"
- :key="index"
- class="order-item"
- >
- <view class="order-header">
- <text class="order-id">{{ order.orderId }}</text>
- <text class="status-tag" :class="order.status">{{ getStatusText(order.status) }}</text>
- </view>
- <view class="order-info">
- <view class="info-item">
- <text class="label">用户名称:</text>
- <text class="value">{{ order.userName }}</text>
- </view>
- <view class="info-item">
- <text class="label">电话:</text>
- <text class="value">{{ order.phone }}</text>
- </view>
- <view class="info-item">
- <text class="label">{{ order.status === 'appointed' ? '预约时间:' : '取件时间:' }}</text>
- <text class="value">{{ order.time }}</text>
- </view>
- </view>
- <view class="order-actions">
- <view
- class="action-btn reject"
- v-if="order.status === 'pending'"
- @tap="handleReject(order)"
- >
- <uni-icons type="undo" size="16" color="#666"></uni-icons>
- <text>驳回</text>
- </view>
- <view
- class="action-btn approve"
- v-if="order.status === 'pending'"
- @tap="handleApprove(order)"
- >
- <uni-icons type="checkmarkempty" size="16" color="#00C853"></uni-icons>
- <text>审批</text>
- </view>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
-
- <script>
- import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
-
- export default {
- mixins: [pullRefreshMixin],
- data() {
- return {
- // 状态标签配置
- statusTabs: [
- { label: '全部', value: 'all' },
- { label: '已预约', value: 'appointed' },
- { label: '待质检', value: 'pending' },
- { label: '待结款', value: 'waiting' },
- { label: '已驳回', value: 'rejected' }
- ],
- currentStatus: 'all',
- selectedTime: '',
- searchActive: false,
- searchKey: '',
- showTimePickerModal: false,
- timePickerStyle: {
- top: 0
- },
- activeTimeType: 'start',
- startDate: {
- year: '',
- month: '',
- day: ''
- },
- endDate: {
- year: '',
- month: '',
- day: ''
- },
- orderList: [
- {
- orderId: 'RE827381278615224',
- userName: '周小艺',
- phone: '138****1234',
- time: '周四 11:00~13:00',
- status: 'appointed'
- },
- {
- orderId: 'RE827381278615226',
- userName: '周小艺',
- phone: '138****1234',
- time: '2025-03-20 11:00',
- status: 'pending'
- },
- {
- orderId: 'RE827381278615225',
- userName: '周小艺',
- phone: '138****1234',
- time: '2025-03-20 12:00',
- status: 'rejected'
- },
- {
- orderId: 'RE827381278615226',
- userName: '周小艺',
- phone: '138****1234',
- time: '2025-03-20 12:00',
- status: 'waiting'
- }
- ],
- currentDateIndexes: [0, 0, 0], // 当前选中的年月日索引
- // 添加时间范围
- timeRange: {
- start: '',
- end: ''
- }
- }
- },
-
- computed: {
- // 年份选项
- yearOptions() {
- const years = []
- const currentYear = new Date().getFullYear()
- for (let i = 0; i < 6; i++) {
- years.push(currentYear + i)
- }
- return years
- },
-
- // 月份选项
- monthOptions() {
- return Array.from({length: 12}, (_, i) => i + 1)
- },
-
- // 日期选项
- dayOptions() {
- return Array.from({length: 31}, (_, i) => i + 1)
- },
-
- // 过滤后的订单列表
- filteredOrders() {
- let result = this.orderList
-
- // 根据搜索关键词筛选
- if (this.searchKey) {
- const keyword = this.searchKey.toLowerCase()
- result = result.filter(order => {
- return order.orderId.toLowerCase().includes(keyword) ||
- order.userName.toLowerCase().includes(keyword)
- })
- }
-
- // 根据状态筛选
- if (this.currentStatus !== 'all') {
- result = result.filter(order => order.status === this.currentStatus)
- }
-
- // 根据时间范围筛选
- if (this.timeRange.start && this.timeRange.end) {
- const startTime = new Date(this.timeRange.start).getTime()
- const endTime = new Date(this.timeRange.end).getTime()
-
- result = result.filter(order => {
- // 将订单时间转换为时间戳进行比较
- const orderTime = this.parseOrderTime(order.time)
- return orderTime >= startTime && orderTime <= endTime
- })
- }
-
- return result
- }
- },
-
- methods: {
- async onRefresh() {
- // 模拟刷新数据
- await new Promise(resolve => setTimeout(resolve, 1000))
- this.stopPullRefresh()
- },
- navigateBack() {
- uni.navigateBack()
- },
- // 切换状态
- switchStatus(status) {
- this.currentStatus = status
- },
-
- // 显示时间选择器
- showTimePicker() {
- const query = uni.createSelectorQuery().in(this);
- query.select('.filter-item').boundingClientRect(rect => {
- if (rect) {
- this.timePickerStyle = {
- top: rect.bottom
- }
- }
- this.showTimePickerModal = true;
- this.activeTimeType = 'start';
- }).exec();
- },
-
- // 关闭时间选择器
- closeTimePicker() {
- this.showTimePickerModal = false
- },
-
- // 切换时间类型
- switchTimeType(type) {
- this.activeTimeType = type
- // 更新索引到当前选中的时间
- const target = type === 'start' ? this.startDate : this.endDate
- this.currentDateIndexes = [
- this.yearOptions.indexOf(target.year) || 0,
- this.monthOptions.indexOf(target.month) || 0,
- this.dayOptions.indexOf(target.day) || 0
- ]
- },
-
- // 处理选择器变化
- handlePickerChange(e) {
- const values = e.detail.value
- const target = this.activeTimeType === 'start' ? this.startDate : this.endDate
-
- // 更新选中的日期
- target.year = this.yearOptions[values[0]]
- target.month = this.monthOptions[values[1]]
- target.day = this.dayOptions[values[2]]
-
- // 更新当前索引
- this.currentDateIndexes = values
- },
-
- // 重置时间选择
- resetTimePicker() {
- this.startDate = {
- year: '',
- month: '',
- day: ''
- }
- this.endDate = {
- year: '',
- month: '',
- day: ''
- }
- this.currentDateIndexes = [0, 0, 0]
- this.activeTimeType = 'start'
-
- // 清空时间范围和显示文本
- this.timeRange = {
- start: '',
- end: ''
- }
- this.selectedTime = ''
- },
-
- // 确认时间选择
- confirmTimePicker() {
- // 检查开始时间是否完整
- if (!this.startDate.year || !this.startDate.month || !this.startDate.day) {
- uni.showToast({
- title: '请选择完整的开始时间',
- icon: 'none'
- })
- return
- }
-
- // 检查结束时间是否完整
- if (!this.endDate.year || !this.endDate.month || !this.endDate.day) {
- uni.showToast({
- title: '请选择完整的结束时间',
- icon: 'none'
- })
- return
- }
-
- // 构建时间对象进行比较
- const startTime = new Date(this.startDate.year, this.startDate.month - 1, this.startDate.day)
- const endTime = new Date(this.endDate.year, this.endDate.month - 1, this.endDate.day)
-
- if (startTime > endTime) {
- uni.showToast({
- title: '开始时间不能大于结束时间',
- icon: 'none'
- })
- return
- }
-
- // 更新显示文本
- const start = `${this.startDate.year}年${this.startDate.month}月${this.startDate.day}日`
- const end = `${this.endDate.year}年${this.endDate.month}月${this.endDate.day}日`
- this.selectedTime = `${start} - ${end}`
-
- // 更新时间范围用于筛选
- this.timeRange = {
- start: `${this.startDate.year}-${this.startDate.month}-${this.startDate.day}`,
- end: `${this.endDate.year}-${this.endDate.month}-${this.endDate.day}`
- }
-
- this.showTimePickerModal = false
- },
-
- // 搜索处理
- handleSearch() {
- // 搜索已经通过计算属性自动处理,无需额外操作
- },
-
- // 加载更多
- loadMore() {
- // 实现加载更多逻辑
- },
-
- // 获取状态文本
- getStatusText(status) {
- const statusMap = {
- appointed: '已预约',
- pending: '待质检',
- waiting: '待结款',
- rejected: '已驳回'
- }
- return statusMap[status] || status
- },
-
- // 处理驳回
- handleReject(order) {
- uni.showModal({
- title: '提示',
- content: '确定要驳回该订单吗?',
- success: (res) => {
- if (res.confirm) {
- // 实现驳回逻辑
- }
- }
- })
- },
-
- // 处理审批
- handleApprove(order) {
- uni.showModal({
- title: '提示',
- content: '确定要审批通过该订单吗?',
- success: (res) => {
- if (res.confirm) {
- // 实现审批逻辑
- }
- }
- })
- },
-
- // 解析订单时间字符串为时间戳
- parseOrderTime(timeStr) {
- // 处理"周四 11:00~13:00"格式
- if (timeStr.includes('周')) {
- // 这里可以根据实际需求处理周几的转换
- return new Date().getTime() // 临时返回当前时间
- }
-
- // 处理"2025-03-20 11:00"格式
- return new Date(timeStr).getTime()
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .container {
- min-height: 100vh;
- background: #f5f5f5;
- }
-
- .top-section {
- background: #fff;
- padding: 60rpx 30rpx 20rpx;
- // border-radius: 0 0 30rpx 30rpx;
- box-shadow: 0 8rpx 16rpx rgba(0, 200, 83, 0.15);
- .header {
- // margin-bottom: 30rpx;
- display: flex;
- align-items: center;
- .title {
- font-family: PingFang SC;
- font-weight: 500;
- font-size: 16px;
- line-height: 140%;
- letter-spacing: 0%;
- text-align: center;
- vertical-align: middle;
- margin-left: 30%;
- font-weight: bold;
- }
- }
-
- .status-tabs {
- display: flex;
- background: rgba(255,255,255,0.1);
- border-radius: 12rpx;
- padding: 4rpx;
-
- .tab-item {
- flex: 1;
- text-align: center;
- padding: 16rpx 0;
- color: #353535;
- font-size: 28rpx;
-
- &.active {
- background: #fff;
- border-radius: 8rpx;
- color: #00C853;
- }
- }
- }
- }
-
- .filter-bar {
- display: flex;
- align-items: center;
- background: #fff;
- margin: 20rpx;
- border-radius: 12rpx;
- overflow: visible;
- padding: 0 0;
- position: relative;
- height: 64rpx;
- }
-
- .filter-item {
- display: flex;
- align-items: center;
- padding: 0 20rpx;
- font-size: 28rpx;
- color: #353535;
- border-right: 1px solid #eee;
-
- .text-green {
- color: #00C853;
- }
- }
-
- .search-area {
- flex: 1;
- width: 100%;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- position: static;
- overflow: visible;
- }
-
- .search-box {
- display: flex;
- align-items: center;
- width: 90%;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- height: 100%;
- background: #f5f5f5;
- border-radius: 12rpx;
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
- padding: 0 24rpx;
- z-index: 10;
- transform: translateX(100%);
- opacity: 0;
- transition: all 0.3s cubic-bezier(.4,0,.2,1);
- &.active {
- transform: translateX(0);
- opacity: 1;
- }
- input {
- flex: 1;
- font-size: 28rpx;
- margin: 0 10rpx;
- background: transparent;
- border: none;
- outline: none;
- }
- .cancel-btn {
- color: #999;
- font-size: 28rpx;
- margin-left: 16rpx;
- }
- }
-
- .search-icon {
- width: 64rpx;
- height: 64rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 32rpx;
- background: #fff;
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
- }
-
- .order-list {
- height: calc(100vh - 300rpx);
- width: 80%;
- margin: 0 auto;
- padding: 0 20rpx;
- }
-
- .order-item {
- background: #fff;
- border-radius: 12rpx;
- padding: 20rpx;
- margin-bottom: 20rpx;
-
- .order-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20rpx;
-
- .order-id {
- font-size: 32rpx;
- color: #333;
- font-weight: 500;
- }
-
- .status-tag {
- font-size: 24rpx;
- padding: 4rpx 16rpx;
- border-radius: 20rpx;
-
- &.appointed {
- color: #00C853;
- background: rgba(0, 200, 83, 0.1);
- }
-
- &.pending {
- color: #FF9800;
- background: rgba(255, 152, 0, 0.1);
- }
-
- &.waiting {
- color: #2196F3;
- background: rgba(33, 150, 243, 0.1);
- }
-
- &.rejected {
- color: #F44336;
- background: rgba(244, 67, 54, 0.1);
- }
- }
- }
-
- .order-info {
- .info-item {
- display: flex;
- margin-bottom: 10rpx;
-
- .label {
- color: #999;
- font-size: 28rpx;
- width: 160rpx;
- }
-
- .value {
- color: #333;
- font-size: 28rpx;
- flex: 1;
- }
- }
- }
-
- .order-actions {
- display: flex;
- justify-content: flex-end;
- margin-top: 20rpx;
- padding-top: 20rpx;
- border-top: 1px solid #f5f5f5;
-
- .action-btn {
- display: flex;
- align-items: center;
- padding: 10rpx 20rpx;
- margin-left: 20rpx;
- border-radius: 8rpx;
-
- text {
- font-size: 28rpx;
- margin-left: 8rpx;
- }
-
- &.reject {
- background: #f5f5f5;
- color: #666;
- }
-
- &.approve {
- background: rgba(0, 200, 83, 0.1);
- color: #00C853;
- }
- }
- }
- }
-
- .time-picker-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 100;
- background: rgba(0,0,0,0.08);
- }
-
- .time-picker-wrapper {
- background: #fff;
- border-radius: 16rpx;
- box-shadow: 0 8rpx 32rpx 0 rgba(0,0,0,0.10);
- z-index: 101;
- }
-
- .time-type-header {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 30rpx 0;
-
- .type-item {
- position: relative;
- padding: 0 40rpx;
- font-size: 32rpx;
- color: #999;
-
- &.active {
- color: #00C853;
- font-weight: bold;
- }
-
- .active-line {
- position: absolute;
- left: 50%;
- bottom: -10rpx;
- transform: translateX(-50%);
- width: 40rpx;
- height: 4rpx;
- background: #00C853;
- border-radius: 2rpx;
- }
- }
-
- .type-divider {
- color: #999;
- font-size: 28rpx;
- margin: 0 20rpx;
- }
- }
-
- .time-picker-content {
- position: relative;
- height: 440rpx;
-
- .select-mask {
- position: absolute;
- left: 0;
- right: 0;
- top: 50%;
- transform: translateY(-44rpx);
- height: 88rpx;
- z-index: 2;
- pointer-events: none;
-
- .select-line {
- position: absolute;
- left: 0;
- right: 0;
- height: 1px;
- background: rgba(0, 0, 0, 0.1);
-
- &:first-child {
- top: 0;
- }
-
- &:last-child {
- bottom: 0;
- }
- }
- }
-
- .picker-view {
- width: 100%;
- height: 100%;
-
- .picker-item {
- line-height: 88rpx;
- text-align: center;
- color: #999;
- font-size: 32rpx;
- }
- }
- }
-
- /* 选中项样式 */
- ::v-deep .uni-picker-view-indicator {
- height: 88rpx !important;
- }
-
- ::v-deep .uni-picker-view-mask {
- background-image: linear-gradient(180deg,
- rgba(255, 255, 255, 0.95) 0%,
- rgba(255, 255, 255, 0.6) 45%,
- rgba(255, 255, 255, 0) 50%,
- rgba(255, 255, 255, 0) 50%,
- rgba(255, 255, 255, 0.6) 55%,
- rgba(255, 255, 255, 0.95) 100%
- );
- }
-
- ::v-deep .uni-picker-view-indicator::before,
- ::v-deep .uni-picker-view-indicator::after {
- height: 1px;
- background-color: rgba(0, 0, 0, 0.1);
- }
-
- ::v-deep .uni-picker-view-content > view {
- color: #999;
- font-size: 32rpx;
- }
-
- ::v-deep .uni-picker-view-indicator view {
- color: #000 !important;
- font-weight: bold !important;
- }
-
- .time-picker-footer {
- padding: 20rpx;
- display: flex;
- justify-content: space-between;
- border-top: 1rpx solid #f5f5f5;
-
- .btn {
- flex: 1;
- height: 88rpx;
- line-height: 88rpx;
- text-align: center;
- border-radius: 44rpx;
- font-size: 32rpx;
- margin: 0 10rpx;
-
- &.btn-reset {
- background: #f8f8f8;
- color: #666;
- }
-
- &.btn-confirm {
- background: #00C853;
- color: #fff;
- }
- }
- }
- </style>
|