|
|
- <template>
- <view class="order-manage-container">
- <!-- 顶部导航栏 -->
- <view
- class="nav-bar"
- :style="{
- paddingTop: statusBarHeight + 'px',
- height: (statusBarHeight + navBarContentHeight) + 'px'
- }"
- >
- <uni-icons type="left" @tap="goBack" size="24" color="#222" />
- <text class="nav-title">{{ historyOrderMode ? '历史订单' : '订单管理' }}</text>
- </view>
-
- <!-- Tab栏 -->
- <scroll-view
- v-if="!historyOrderMode"
- class="order-tabs-scroll"
- scroll-x
- :style="{
- top: navBarRealHeight + 'px',
- height: tabBarHeight + 'px',
- position: 'fixed',
- left: 0,
- width: '100%',
- zIndex: 99
- }"
- >
- <view
- class="order-tabs"
- :style="{width: (tabs.length * 20) + '%'}"
- >
- <view
- v-for="(tab, idx) in tabs"
- :key="tab.value"
- :class="['tab-item', {active: currentTab === idx}]"
- @tap="onTabChange(idx)"
- >
- <view class="tab-label-wrap">
- {{ tab.label }}
- <view v-if="tab.count > 0" class="tab-badge">{{ tab.count }}</view>
- </view>
- </view>
- </view>
- </scroll-view>
-
- <!-- 搜索与筛选 -->
- <view
- v-if="!historyOrderMode"
- class="search-bar"
- :style="{
- position: 'fixed',
- zIndex: 10,
- top: (navBarRealHeight + tabBarHeight) + 'px',
- left: 0,
- width: '100vw',
- height: '40px'
- }"
- >
- <view class="search-bar-inner">
- <template v-if="!searchMode">
- <uni-icons class="search-icon" type="search" size="22" color="#999" @tap="onSearchIconClick" />
- <uni-icons class="scan-icon" type="scan" size="22" color="#999" @tap="scanCode" />
- </template>
- <template v-else>
- <view class="search-input-wrap">
- <uni-icons type="search" size="22" color="#999" />
- <input
- ref="searchInput"
- class="search-input"
- v-model="searchText"
- placeholder="请输入要查询的内容"
- placeholder-style="color:#ccc"
- />
- <uni-icons v-if="searchText" type="close" size="22" color="#ccc" @tap="onClearSearch" />
- </view>
- <text class="search-cancel" @tap="onCancelSearch">取消</text>
- </template>
- </view>
- </view>
-
- <!-- 订单卡片列表 -->
- <view class="order-list"
- :style="{paddingTop: (navBarRealHeight + (historyOrderMode ? 0 : (tabBarHeight + 16 + 40))) + 'px'}"
- >
- <view class="order-card" v-for="order in filteredOrders" :key="order.id" @tap="goToOrderDetail(order)">
- <view class="order-card-header">
- <text class="order-id">{{ order.orderNo }}</text>
- <view v-if="order.statusText === '不包邮'" class="order-status-tag red">{{ order.statusText }}</view>
- </view>
- <view class="order-info-wrapper">
- <view class="order-info">
- <view>
- <text class="info-label">用户名:</text>
- <text class="info-value">{{ order.userName }}</text>
- </view>
- <view>
- <text class="info-label">电话:</text>
- <text class="info-value">{{ order.phone }}</text>
- </view>
- <view v-if="order.appointTime">
- <text class="info-label">预约时间:</text>
- <text class="info-value">{{ order.appointTime }}</text>
- </view>
- <view v-if="order.cancelTime">
- <text class="info-label">取消时间:</text>
- <text class="info-value">{{ order.cancelTime }}</text>
- </view>
- <view v-if="order.qualityTime">
- <text class="info-label">质检时间:</text>
- <text class="info-value">{{ order.qualityTime }}</text>
- </view>
- </view>
- <view class="order-status-label-bar order-info-status" :class="order.statusClass">{{ order.statusLabel }}</view>
- </view>
- <view class="order-card-footer" v-if="order.actions && order.actions.length && !historyOrderMode">
- <view class="order-actions-bar">
- <view class="action-btn-bar" v-for="action in order.actions" :key="action.text" @tap="action.text === '审批' ? goToOrderDetail(order) : null">
- <uni-icons :type="action.icon" size="28" color="#666" />
- <text>{{ action.text }}</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 加载状态 -->
- <view v-if="isLoading && orderList.length === 0" class="loading-container">
- <uni-icons type="spinner-cycle" size="24" color="#999" class="loading-icon" />
- <text class="loading-text">加载中...</text>
- </view>
-
- <!-- 加载更多状态 -->
- <view v-if="loadingMore" class="load-more-container">
- <uni-icons type="spinner-cycle" size="20" color="#999" class="loading-icon" />
- <text class="load-more-text">加载更多...</text>
- </view>
-
- <!-- 到底提示 -->
- <view v-if="!hasMore && orderList.length > 0" class="no-more-container">
- <text class="no-more-text">已加载全部订单</text>
- </view>
-
- <!-- 空状态 -->
- <view v-if="!isLoading && orderList.length === 0" class="empty-container">
- <text class="empty-text">暂无订单数据</text>
- </view>
- </view>
- </view>
- </template>
-
- <script>
- import pullRefreshMixin from '../mixins/pullRefreshMixin.js'
- export default {
- mixins: [pullRefreshMixin],
- data() {
- return {
- statusBarHeight: 0,
- navBarContentHeight: 44,
- tabBarHeight: 48,
- navBarHeight: 44,
- navBarRealHeight: 44,
- tabs: [
- { label: '全部', value: '' , count: 0 },
- { label: '待审核', value: 0, count: 0 },
- { label: '已预约', value: 1, count: 0 },
- { label: '待质检', value: 2, count: 0 },
- { label: '已结款', value: 3, count: 0 },
- { label: '已驳回', value: 4, count: 0 },
- { label: '已取消', value: 5, count: 0 },
- ],
- currentTab: 0,
- orderList: [],
- searchMode: false,
- searchText: '',
- historyOrderMode: false,
- pageNo: 1,
- pageSize: 10,
- hasMore: true,
- isLoading: false,
- loadingMore: false,
- userId: '',
- reachBottomTimer: null
- }
- },
- onLoad(options) {
- const sys = uni.getSystemInfoSync();
- this.statusBarHeight = sys.statusBarHeight;
- this.$nextTick(() => {
- uni.createSelectorQuery().select('.nav-bar').boundingClientRect(rect => {
- if (rect) {
- this.navBarRealHeight = rect.height;
- }
- }).exec();
- });
- if (options && options.historyOrder) {
- this.historyOrderMode = true;
- }
- if (options && options.userId) {
- this.userId = options.userId;
- }
- this.fetchOrderList()
- this.fetchOrderStatusStatistics()
- },
- onShow() {
- this.fetchOrderStatusStatistics()
- },
- computed: {
- filteredOrders() {
- if (this.searchText) {
- const text = this.searchText.toLowerCase();
- return this.orderList.filter(order =>
- (order.orderNo && order.orderNo.toLowerCase().includes(text)) ||
- (order.userName && order.userName.toLowerCase().includes(text)) ||
- (order.phone && order.phone.toLowerCase().includes(text))
- );
- }
- // 现在通过接口参数获取对应Tab的数据,不需要客户端过滤
- return this.orderList;
- }
- },
- methods: {
- goBack() {
- uni.navigateBack()
- },
- onTabChange(idx) {
- this.currentTab = idx
- this.pageNo = 1
- this.hasMore = true
- this.orderList = []
- this.isLoading = false
- this.loadingMore = false
- this.fetchOrderList()
- },
- onSearchIconClick() {
- this.searchMode = true;
- this.$nextTick(() => {
- this.$refs.searchInput && this.$refs.searchInput.focus();
- });
- },
- onClearSearch() {
- this.searchText = '';
- },
- onCancelSearch() {
- this.searchText = '';
- this.searchMode = false;
- },
- goToOrderDetail(order) {
- // 新增:记录浏览记录
- this.$api && this.$api('adminOrderBrowseRecord', { orderIds: order.id }, res => {
- // 可选:处理返回结果或错误,但不影响后续跳转
-
- })
- uni.navigateTo({
- url: '/pages/manager/order-detail?id=' + order.id
- })
- },
- refreshData() {
- // TODO: 实现订单列表刷新逻辑,如重新请求接口
- },
- async onRefresh() {
- await this.refreshData && this.refreshData()
- },
- fetchOrderList(isLoadMore) {
- if (this.isLoading) return
- if (isLoadMore && !this.hasMore) return
- console.log(isLoadMore,'isLoadMore')
-
- if (isLoadMore) {
- this.loadingMore = true
- } else {
- this.isLoading = true
- }
-
- // 根据当前Tab获取对应的status参数
- const tabValue = this.tabs[this.currentTab].value
- let statusParam = tabValue // 直接用tabValue作为status参数
-
- const params = {
- pageNo: isLoadMore ? this.pageNo + 1 : 1,
- pageSize: this.pageSize,
- status: statusParam
- }
- console.log(params,'params')
- if (this.userId) {
- params.userId = this.userId;
- }
- this.$api && this.$api('getOrderList', params, res => {
- if (res && res.code === 200 && res.result && res.result.records) {
- console.log(res.result,'res.result.records')
- const newOrders = res.result.records.map(order => {
- const statusInfo = this.getOrderStatusInfo(order.status, order.state)
- return {
- id: order.id,
- orderNo: order.ordeNo,
- userName: order.name,
- phone: order.phone,
- appointTime: order.goTime,
- cancelTime: order.state === 3 ? order.updateTime : '',
- qualityTime: order.status === 2 && order.state === 1 ? order.updateTime : '',
- statusText: order.isBy === 'Y' ? statusInfo.label : '不包邮',
- statusClass: statusInfo.class,
- statusLabel: statusInfo.label,
- actions: this.getOrderActions(order.status, order.state),
- status: this.getOrderStatus(order.status, order.state)
- }
- })
-
- if (isLoadMore) {
- // 数据去重处理
- const existingIds = new Set(this.orderList.map(order => order.id))
- const uniqueNewOrders = newOrders.filter(order => !existingIds.has(order.id))
- this.orderList = [...this.orderList, ...uniqueNewOrders]
- this.pageNo = params.pageNo +1
- } else {
- this.orderList = newOrders
- this.pageNo = 1
- }
-
- // 判断是否还有更多数据
- this.hasMore = newOrders.length === this.pageSize
- } else {
- this.hasMore = false
- }
- this.isLoading = false
- this.loadingMore = false
- }, err => {
- console.error('获取订单列表失败:', err)
- this.isLoading = false
- this.loadingMore = false
- this.hasMore = false
- uni.showToast({
- title: '加载失败,请重试',
- icon: 'none'
- })
- })
- },
- getOrderStatusInfo(status, state) {
- // if (state === 3) {
- // return { label: '已取消', class: 'gray' }
- // }
- if ((status === 1&&state === 0) && state != 3 && state != 4) {
- return { label: '已预约', class: 'green' }
- } else if (state === 1 && status === 2) {
- return { label: '待质检', class: 'orange' }
- } else if (status === 3&& state === 2) {
- return { label: '已结款', class: 'blue' }
- } else if (state === 4) {
- return { label: '已驳回', class: 'red' }
- }else if (state === 3) {
- return { label: '已取消', class: 'Turquoise2' }
- }else if (state === 0 && status === 0) {
- return { label: '待审核', class: 'blue' }
- }
- return { label: '未知状态', class: 'gray' }
- },
- getOrderStatus(status, state) {
- // // 已取消状态
- // if (state === 3) return 4
-
- // 已预约状态 - 快递上门
- if (status === 1 ) return 1
-
- // 待质检状态 - 已取件
- if (state === 1) return 2
-
- // 已结款状态 - 现金打款
- if (status === 3 ) return 3
-
- // 已驳回状态 - 快递上门终止
- if (state ===4) return 4
-
- return -1
- },
- getOrderActions(status, state) {
- const actions = []
- // 只有待质检状态显示操作按钮
- if (status === 2 && state === 1) {
- actions.push({ icon: 'undo', text: '驳回' })
- actions.push({ icon: 'person', text: '审批' })
- }
- return actions
- },
- onLoadMore() {
- if (this.hasMore && !this.isLoading && !this.loadingMore) {
- this.fetchOrderList(true)
- }
- },
- scanCode() {
- uni.scanCode({
- scanType: ['qrCode'],
- success: (res) => {
- console.log('扫码结果:', res);
- // 这里可以根据扫码结果进行相应处理
- // 比如跳转到订单详情页
- if(res.result) {
- uni.navigateTo({
- url: '/pages/manager/order-detail?id=' + res.result
- })
- }
- },
- fail: (err) => {
- console.error('扫码失败:', err);
- uni.showToast({
- title: '扫码失败',
- icon: 'none'
- })
- }
- })
- },
- fetchOrderStatusStatistics() {
- const token = uni.getStorageSync('token') || '';
- this.$api && this.$api('orderStatusStatistics', { token }, res => {
- if (res.code === 200 && res.result) {
- const stat = res.result;
- this.tabs[1].count = stat.pendingAudit || 0; // 待审核
- this.tabs[2].count = stat.appointed || 0; // 已预约
- this.tabs[3].count = stat.waitingInspection || 0; // 待质检
- this.tabs[4].count = stat.completed || 0; // 已结款
- this.tabs[5].count = stat.rejected || 0; // 已驳回
- this.tabs[6].count = stat.cancelled || 0; // 已取消
- this.tabs[0].count = (stat.pendingAudit || 0) + (stat.appointed || 0) + (stat.waitingInspection || 0) + (stat.completed || 0) + (stat.rejected || 0) + (stat.cancelled || 0);
- }
- });
- },
- },
- onPullDownRefresh() {
- this.pageNo = 1;
- this.hasMore = true;
- this.orderList = [];
- this.isLoading = false;
- this.loadingMore = false;
- this.fetchOrderList();
- uni.stopPullDownRefresh();
- },
- onReachBottom() {
- // 防抖处理,避免频繁触发
- if (this.reachBottomTimer) {
- clearTimeout(this.reachBottomTimer)
- }
- this.reachBottomTimer = setTimeout(() => {
- this.onLoadMore()
- }, 100)
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .order-manage-container {
- background: #f8f8f8;
- min-height: 100vh;
- padding-bottom: 24px;
- }
- .nav-bar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: #fff;
- padding: 0 32rpx;
- box-sizing: border-box;
- .nav-title {
- flex: 1;
- text-align: center;
- font-size: 36rpx;
- font-weight: bold;
- color: #222;
- }
- .nav-icons {
- display: flex;
- align-items: center;
- gap: 32rpx;
- }
- }
- .order-tabs-scroll {
- position: fixed;
- left: 0;
- width: 100%;
- z-index: 99;
- background: #fff;
- border-bottom: 1px solid #f0f0f0;
- height: 96rpx;
- overflow-x: auto;
- white-space: nowrap;
- -webkit-overflow-scrolling: touch;
- &::-webkit-scrollbar {
- display: none;
- }
- }
- .order-tabs {
- display: flex;
- width: 100%;
- }
- .tab-item {
- flex: 1 0 0%;
- text-align: center;
- font-size: 34rpx;
- color: #bfbfbf;
- height: 96rpx;
- line-height: 96rpx;
- position: relative;
- font-weight: 500;
- transition: color 0.2s;
- letter-spacing: 0.5px;
- }
- .tab-item.active {
- color: #ffb400;
- font-weight: bold;
- }
- .tab-item.active::after {
- content: '';
- display: block;
- margin: 0 auto;
- margin-top: 2px;
- width: 22px;
- height: 3px;
- border-radius: 2px;
- background: #ffb400;
- }
- .search-bar {
- width: 100vw;
- height: 40px;
- position: fixed;
- z-index: 10;
- left: 0;
- top: 0;
- display: flex;
- align-items: center;
- }
- .search-bar-inner {
- margin: 0 16px;
- background: #fff;
- border-radius: 20px;
- height: 40px;
- flex: 1;
- display: flex;
- align-items: center;
- box-shadow: 0 2px 8px rgba(0,0,0,0.02);
- padding: 0 12px;
- justify-content: space-around;
- .search-icon {
- margin-right: 8px;
- }
- .scan-icon {
- margin-left: 8px;
- }
- }
- .search-input-wrap {
- display: flex;
- align-items: center;
- flex: 1;
- background: #f5f5f5;
- border-radius: 20px;
- height: 32px;
- margin: 0 0;
- padding: 0 8px;
- .search-input {
- flex: 1;
- border: none;
- outline: none;
- background: transparent;
- font-size: 15px;
- color: #222;
- margin-left: 8px;
- }
- }
- .search-cancel {
- margin-left: 8px;
- color: #999;
- font-size: 15px;
- line-height: 40px;
- }
- .order-list {
- margin: 0;
- padding-top: calc(var(--status-bar-height, 0px) + 44px + 44px + 16px);
- }
- .order-card {
- background: #fff;
- border-radius: 20px;
- margin: 0 16px 16px 16px;
- padding: 20px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.04);
- }
- .order-card-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 12px;
- .order-id {
- font-size: 16px;
- font-weight: bold;
- color: #222;
- }
- .order-status-tag {
- font-size: 14px;
- border-radius: 12px;
- padding: 2px 12px;
- &.green { background: #e6f9e6; color: #1ecb1e; }
- &.red { background: #ffeaea; color: #ff4d4f; }
- &.orange { background: #fff7e6; color: #ffb400; }
- &.blue { background: #e6f0ff; color: #409eff; }
- &.gray { background: #f5f5f5; color: #999; }
- &.Turquoise2 { background: #e0f7fa; color: #009fa8; } /* 新增已取消 */
- }
- }
- .order-info-wrapper {
- position: relative;
- .order-info-status {
- position: absolute;
- right: 0;
- bottom: 0;
- font-size: 14px;
- border-radius: 12px;
- padding: 2px 12px;
- &.green { background: #e6f9e6; color: #1ecb1e; }
- &.red { background: #ffeaea; color: #ff4d4f; }
- &.orange { background: #fff7e6; color: #ffb400; }
- &.blue { background: #e6f0ff; color: #409eff; }
- &.gray { background: #f5f5f5; color: #999; }
- &.Turquoise2 { background: #e0f7fa; color: #009fa8; } /* 新增已取消 */
- }
- }
- .order-info {
- font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
- font-weight: 400;
- font-size: 14px;
- line-height: 1.4;
- letter-spacing: 0;
- vertical-align: middle;
- color: #666;
- margin-bottom: 12px;
- view {
- margin-bottom: 4px;
- display: flex;
- align-items: center;
- }
- .info-label {
- color: #999;
- font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
- font-weight: 400;
- font-size: 14px;
- line-height: 1.4;
- margin-right: 4px;
- }
- .info-value {
- color: #222;
- font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
- font-weight: 400;
- font-size: 14px;
- line-height: 1.4;
- }
- }
- .order-card-footer {
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 0 -20px -20px -20px;
- padding: 0 20px;
- border-bottom-left-radius: 20px;
- border-bottom-right-radius: 20px;
- background: #fafbfc;
- min-height: 60px;
- position: relative;
- .order-actions-bar {
- display: flex;
- flex: 1;
- justify-content: center;
- align-items: center;
- gap: 48px;
- .action-btn-bar {
- display: flex;
- flex-direction: column;
- align-items: center;
- font-size: 14px;
- color: #666;
- margin-top: 8px;
- margin-bottom: 8px;
- uni-icons {
- margin-bottom: 2px;
- }
- }
- }
- }
-
- /* 加载状态样式 */
- .loading-container, .load-more-container, .no-more-container, .empty-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 40rpx 0;
- color: #999;
- }
-
- .loading-icon {
- animation: spin 1s linear infinite;
- margin-bottom: 16rpx;
- }
-
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
-
- .loading-text, .load-more-text {
- font-size: 28rpx;
- color: #999;
- }
-
- .no-more-text {
- font-size: 26rpx;
- color: #ccc;
- }
-
- .empty-container {
- padding: 120rpx 0;
- }
-
- .empty-text {
- font-size: 30rpx;
- color: #ccc;
- }
-
- .tab-label-wrap {
- position: relative;
- display: inline-block;
- }
- .tab-badge {
- position: absolute;
- top: 5px;
- right: -14px;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #ff4d4f;
- color: #fff;
- font-size: 12px;
- border-radius: 50%;
- min-width: 18px;
- height: 18px;
- padding: 0 5px;
- font-weight: bold;
- box-sizing: border-box;
- z-index: 1;
- }
- </style>
-
-
|