Files
SmartParks_uniapp/pages/sys/workbench/order/order.vue

493 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="order-container" @click="handleOutsideClick">
<!-- 筛选区 -->
<view class="filter">
<!-- 工单类型按钮 -->
<view class="filter-btn" @click.stop="togglePopup('type')">
{{ (!selectedFilters.type || selectedFilters.type.orderTypeName === '全部') ? '工单类型' : (selectedFilters.type.orderTypeName.length > 5 ? selectedFilters.type.orderTypeName.substring(0, 5) + '...' : selectedFilters.type.orderTypeName) }}
<image class="filter-img" src="/static/ic_down_arrow_g.png" />
<view v-if="activePopup === 'type'" class="dropdown">
<view class="dropdown-triangle"></view>
<view class="dropdown-list">
<view v-for="(item, index) in typeList" :key="index" class="dropdown-item"
@click.stop="selectFilter('type', item)">
{{ item.orderTypeName }}
</view>
</view>
</view>
</view>
<!-- 工单状态按钮 -->
<view v-if="activeTab == 1" class="filter-btn" @click.stop="togglePopup('status')">
{{ (!selectedFilters.status || selectedFilters.status.name === '全部') ? '工单状态' : (selectedFilters.status.name.length > 5 ? selectedFilters.status.name.substring(0, 5) + '...' : selectedFilters.status.name) }}
<image class="filter-img" src="/static/ic_down_arrow_g.png" />
<view v-if="activePopup === 'status'" class="dropdown">
<view class="dropdown-triangle"></view>
<view class="dropdown-list">
<view v-for="(item, index) in statusList" :key="index" class="dropdown-item"
@click.stop="selectFilter('status', item)">
{{ item.name }}
</view>
</view>
</view>
</view>
<!-- 处理人按钮 -->
<view v-if="isManager" class="filter-btn" @click.stop="togglePopup('handler')">
{{ (!selectedFilters.handler || selectedFilters.handler.name === '全部') ? '处理人' : (selectedFilters.handler.name.length > 5 ? selectedFilters.handler.name.substring(0, 5) + '...' : selectedFilters.handler.name) }}
<image class="filter-img" src="/static/ic_down_arrow_g.png" />
<view v-if="activePopup === 'handler'" class="dropdown">
<view class="dropdown-triangle"></view>
<view class="dropdown-list">
<view v-for="(item, index) in handlerList" :key="index" class="dropdown-item"
@click.stop="selectFilter('handler', item)">
{{ item.name }}
</view>
</view>
</view>
</view>
</view>
<!-- tab栏 -->
<view class="order-tabs">
<view v-for="(tab, idx) in tabs" :key="idx" :class="['order-tab', { active: idx === activeTab }]"
@click="changeTab(idx)">
{{ tab }}
<view v-if="idx === activeTab" class="tab-underline"></view>
</view>
</view>
<!-- 列表区 -->
<!-- 为每个标签页创建独立的scroll-view -->
<view class="order-list-container">
<scroll-view
v-for="(tab, idx) in tabs"
:key="idx"
v-show="idx === activeTab"
scroll-y
class="order-list"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
refresher-background="#f7f7f7"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
@scroll="handleScroll"
>
<view v-for="(item, index) in tabData[idx].list" :key="index" class="order-card" @click="goDetail(item)">
<view class="order-row">
<view class="order-no">工单号{{ item.orderNo }}</view>
<view class="order-status" :class="getStatusColor(item.status)">
{{ getStatusLabel(item.status) }}
</view>
</view>
<image class="order-line-image" src="/static/ic_my_repair_03.png" />
<view class="order-info">工单名称{{ item.orderName }}</view>
<view class="order-info">工单类型{{ item.typeName }}</view>
<view class="order-info">创建时间{{ item.createTime }}</view>
<view class="order-info">
{{ item.createTime }}-{{ item.planCompleTime }}
</view>
<view v-if="item.statusText === '已结束'" class="order-eval-btn eval-btn-right">
服务评价
</view>
</view>
<!-- 加载更多提示 -->
<view v-if="loading" style="text-align:center;color:#999;font-size:26rpx;padding:20rpx;">
加载中...
</view>
<view v-if="!tabData[idx].hasMore && tabData[idx].list.length > 0" style="text-align:center;color:#999;font-size:26rpx;padding:20rpx;">
没有更多数据了
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
tabs: ["待办", "全部"],
activeTab: 0,
// 每个 tab 分页数据
tabData: [
{ list: [], pageNum: 1, pageSize: 10, hasMore: true },
{ list: [], pageNum: 1, pageSize: 10, hasMore: true },
],
tabLoaded: [false, false],
isRefreshing: false, // 控制下拉刷新动画
loading: false,
lastScrollTop: 0,
isAddBtnHidden: false,
activePopup: null,
selectedFilters: {
type: null,
status: null,
handler: null,
},
typeList: [{ orderTypeName: '全部' }],
statusList: [],
handlerList: [{ name: "全部" }],
isManager: false,
};
},
computed: {
list() {
return this.tabData[this.activeTab].list;
},
hasMore() {
return this.tabData[this.activeTab].hasMore;
}
},
created() {
this.loadFilterData()
// 预加载所有标签页数据,避免切换时调用接口
this.loadAllTabsData();
this.isManager = this.vuex_user.roles[0].roleId == 1
},
onShow() {
uni.$once('refreshData', () => {
this.loadAllTabsData();
});
},
methods: {
async changeTab(idx) {
this.activeTab = idx;
},
async onRefresh() {
const tab = this.tabData[this.activeTab];
tab.pageNum = 1;
tab.hasMore = true;
this.isRefreshing = true;
await this.loadTabData(this.activeTab);
this.isRefreshing = false;
},
async onLoadMore() {
const tab = this.tabData[this.activeTab];
if (!tab.hasMore || this.loading) return;
tab.pageNum++;
this.loading = true;
await this.loadTabData(this.activeTab);
this.loading = false;
},
async loadTabData(idx) {
let params = {
pageNum: this.tabData[idx].pageNum,
pageSize: this.tabData[idx].pageSize,
};
if (this.selectedFilters.type && this.selectedFilters.type.id) {
params.type = this.selectedFilters.type.id;
}
if (this.selectedFilters.status && this.selectedFilters.status.id) {
params.status = this.selectedFilters.status.id;
}
if (this.selectedFilters.handler && this.selectedFilters.handler.value) {
params.handler = this.selectedFilters.handler.value;
}
if (idx == 0) {
params.status = "1,2";
}
let res = await this.$u.api.getOrderList2(params);
if (res.code == "200") {
if (this.tabData[idx].pageNum === 1) {
this.tabData[idx].list = res.rows;
} else {
this.tabData[idx].list = [...this.tabData[idx].list, ...res.rows];
}
// 判断是否还有更多数据
this.tabData[idx].hasMore = res.rows.length >= this.tabData[idx].pageSize;
}
this.$set(this.tabLoaded, idx, true);
},
// 其它方法保持不变
handleScroll(e) {
const scrollTop = e.detail.scrollTop;
if (Math.abs(scrollTop - this.lastScrollTop) < 20) return;
this.isAddBtnHidden = scrollTop > this.lastScrollTop && scrollTop > 50;
this.lastScrollTop = scrollTop;
},
getStatusLabel(status) {
const statusMap = {
0: "创建工单",
1: "已接单",
2: "已接单",
3: "处理中",
4: "已完成",
};
return statusMap[status] || "";
},
getStatusColor(status) {
const statusMap = {
0: "orange",
1: "doing",
2: "doing",
3: "doing",
4: "done",
};
return statusMap[status] || "";
},
goDetail(item) {
const itemStr = encodeURIComponent(JSON.stringify(item));
uni.navigateTo({
url: "/pages/sys/workbench/order/orderDetail?item=" + itemStr,
});
},
togglePopup(name) {
this.activePopup = this.activePopup === name ? null : name;
},
selectFilter(name, value) {
this.selectedFilters[name] = value;
this.activePopup = null;
this.onRefresh();
},
handleOutsideClick() {
if (this.activePopup !== null) {
this.activePopup = null;
}
},
async loadFilterData() {
let resType = await this.$u.api.getOrdersType();
if (resType.code === 200) {
this.typeList = [...this.typeList, ...resType.rows];
}
let handlers = await this.$u.api.getHandler2();
if (handlers.code === 200) {
this.handlerList = [...this.handlerList, ...handlers.data];
}
this.statusList = [
{ name: "全部" },
{ name: "创建工单", id: "0" },
{ name: "已接单", id: "1,2" },
{ name: "处理中", id: "3" },
{ name: "已完成", id: "4" },
];
},
// 添加预加载所有标签页数据的方法
async loadAllTabsData() {
// 并行加载所有标签页数据,提高加载速度
const loadPromises = this.tabData.map((tab, index) => {
return this.loadTabData(index);
});
await Promise.all(loadPromises);
// 标记所有标签页已加载
this.tabLoaded = this.tabData.map(() => true);
}
},
};
</script>
<style scoped>
.order-container {
height: 100vh;
background: #f7f7f7;
display: flex;
flex-direction: column;
}
.filter {
display: flex;
flex-direction: row;
background: #fff;
padding-left: 36rpx;
padding-top: 15rpx;
padding-bottom: 15rpx;
}
.filter-btn {
padding: 15rpx 22rpx;
background: #f7f7f7;
border-radius: 25rpx;
height: 58rpx;
color: #9a9a9a;
font-size: 28rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 24rpx;
position: relative;
cursor: pointer;
}
.filter-img {
width: 18rpx;
height: 10rpx;
margin-left: 8rpx;
}
.dropdown {
position: absolute;
top: calc(100% + 10rpx);
left: 50%;
transform: translateX(-50%);
background: #fff;
border-radius: 12rpx;
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
z-index: 999;
width: 200rpx;
}
/* 三角 */
.dropdown-triangle {
position: absolute;
top: -10rpx;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
border-bottom: 10rpx solid #fff;
filter: drop-shadow(0 1rpx 1rpx rgba(0, 0, 0, 0.05));
z-index: 1000;
}
.dropdown-list {
display: flex;
flex-direction: column;
}
.dropdown-item {
padding: 20rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1rpx solid #eee;
text-align: center;
}
.dropdown-item:last-child {
border-bottom: none;
}
.dropdown-item:hover {
background: #f5f5f5;
}
.order-tabs {
display: flex;
align-items: center;
justify-content: space-around;
background: #fff;
height: 80rpx;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
}
.order-tab {
flex: 1;
text-align: center;
font-size: 30rpx;
color: #888;
position: relative;
font-weight: 500;
padding: 0 0 10rpx 0;
cursor: pointer;
}
.order-tab.active {
color: #2186ff;
font-weight: bold;
}
.tab-underline {
width: 60rpx;
height: 6rpx;
background: #2186ff;
border-radius: 3rpx;
margin: 0 auto;
margin-top: 8rpx;
}
.order-list-container {
flex: 1;
position: relative;
}
.order-list {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 0 24rpx;
overflow-y: auto;
padding-bottom: 30rpx;
height: calc(100vh - 80rpx - 32rpx - 88rpx); /* 减去顶部筛选区域高度 */
}
.order-card {
background: #fff;
border-radius: 12rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
padding-top: 25rpx;
padding-bottom: 32rpx;
}
.order-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
margin-top: 25rpx;
margin-left: 19rpx;
margin-right: 50rpx;
}
.order-no {
font-size: 24rpx;
color: #0b0b0b;
font-weight: 500;
}
.order-status {
font-size: 24rpx;
font-weight: 500;
}
.order-line-image {
margin-left: 29rpx;
margin-right: 39rpx;
height: 2rpx;
margin-bottom: 29rpx;
}
.order-status.orange {
color: #f3ab44;
}
.order-status.doing {
color: #00c9aa;
}
.order-status.done {
color: #8a8a8a;
}
.order-info {
font-size: 24rpx;
color: #888;
margin-bottom: 30rpx;
margin-left: 47rpx;
}
.order-add-btn-fixed {
position: fixed;
right: 40rpx;
bottom: 80rpx;
width: 100rpx;
height: 100rpx;
z-index: 100;
transition: transform 0.3s ease;
transform: translateX(0);
}
.order-add-btn-fixed.hide {
transform: translateX(200%);
}
</style>