SmartParks_uniapp/pages/sys/workbench/order/order.vue
2025-08-13 17:31:09 +08:00

468 lines
11 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 }}
<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 === '全部' ? '工单状态' : selectedFilters.status }}
<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.name)">
{{ item.name }}
</view>
</view>
</view>
</view>
<!-- 处理人按钮 -->
<view class="filter-btn" @click.stop="togglePopup('handler')">
{{ !selectedFilters.handler || selectedFilters.handler === '全部' ? '处理人' : selectedFilters.handler }}
<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.name)">
{{ 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" @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>
</scroll-view>
<!-- 悬浮新增按钮 -->
<image v-if="false" src="/static/ic_my_repair_02.png" :class="['order-add-btn-fixed', { hide: isAddBtnHidden }]"
@click="addOrder" />
</view>
</template>
<script>
export default {
data() {
return {
tabs: ["待办", "全部"],
activeTab: 0,
tabData: [
[],
[]
],
tabLoaded: [false, false],
loading: false,
lastScrollTop: 0,
isAddBtnHidden: false,
activePopup: null, // 当前显示哪个弹窗null表示都关闭
selectedFilters: {
type: null,
status: null,
handler: null,
},
typeList: [{
'orderTypeName': '全部'
}],
statusList: [],
handlerList: [],
};
},
computed: {
list() {
return this.tabData[this.activeTab];
},
},
created() {
this.loadTabData(this.activeTab);
this.loadFilterData();
},
methods: {
goBack() {
uni.navigateBack();
},
addOrder() {
uni.navigateTo({
url: "/pages/sys/workbench/order/addOrder",
});
},
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;
},
async changeTab(idx) {
this.activeTab = idx;
if (!this.tabLoaded[idx]) {
await this.loadTabData(idx);
}
},
async loadTabData(idx) {
this.loading = true;
let params = {};
if (this.selectedFilters.type && this.selectedFilters.type.id) {
params.type = this.selectedFilters.type.id
}
let res = await this.$u.api.getOrderList(params);
if (res.code == "200") {
this.$set(this.tabData, idx, res.rows);
}
this.$set(this.tabLoaded, idx, true);
this.loading = false;
},
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) {
if (this.activePopup === name) {
this.activePopup = null;
} else {
this.activePopup = name;
}
},
selectFilter(name, value) {
this.selectedFilters[name] = value;
this.activePopup = null;
this.loadTabData(this.activeTab)
},
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];
}
this.statusList = [{
name: "全部"
},
{
name: "创建工单"
},
{
name: "已接单"
},
{
name: "处理中"
},
{
name: "已完成"
},
];
// 处理人
try {
let resHandler = await this.$u.api.getHandlers();
if (resHandler.code === "200" && Array.isArray(resHandler.data)) {
this.handlerList = resHandler.data;
} else {
this.handlerList = [{
name: "全部"
},
{
name: "张三"
},
{
name: "李四"
},
{
name: "王五"
},
];
}
} catch {
this.handlerList = [{
name: "全部"
},
{
name: "张三"
},
{
name: "李四"
},
{
name: "王五"
},
];
}
},
},
};
</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>