Files
SmartParks_uniapp/pages/sys/workbench/order/order.vue
2025-08-22 17:27:27 +08:00

470 lines
12 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
scroll-y
class="order-list"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
refresher-background="#f7f7f7"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
@scroll="handleScroll"
>
<view v-for="(item, idx) in list" :key="idx" 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="!hasMore && list.length > 0" style="text-align:center;color:#999;font-size:26rpx;padding:20rpx;">
没有更多数据了
</view>
</scroll-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.onRefresh();
this.isManager = this.vuex_user.roles[0].roleId == 1
},
onShow() {
uni.$once('refreshData', () => {
this.onRefresh()
});
},
methods: {
async changeTab(idx) {
this.activeTab = idx;
console.log('t1',"11111111")
if (!this.tabLoaded[idx]) {
this.isRefreshing = true;
await this.onRefresh();
}
},
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" },
];
},
},
};
</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 {
margin: 32rpx 0 0 0;
padding: 0 24rpx;
flex: 1;
overflow-y: auto;
padding-bottom: 30rpx;
height: calc(100vh - 80rpx - 32rpx);
}
.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>