chuangte_bike_newxcx/page_shanghu/yunwei/index.vue

1014 lines
22 KiB
Vue
Raw Permalink 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="page">
<u-navbar title="换电工单" :border-bottom="false" :background="bgc" back-icon-color="#000" title-color='#000'
title-size='36' height='44'></u-navbar>
<!-- 顶部固定区域 -->
<view class="fixed-header">
<!-- 搜索栏 -->
<view class="search-box">
<view class="search-bar">
<view class="scan-btn" @click="btnsaoma">
<u-icon name="scan" size="40" color="#333"></u-icon>
</view>
<view class="divider"></view>
<input type="text" v-model="sn" placeholder="搜索车辆编号/换电工单" placeholder-class="placeholder-style" />
<view class="search-btn" @click="btnsousuo">
<u-icon name="search" size="40" color="#4C97E7"></u-icon>
</view>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-box">
<view class="tab-list">
<view class="tab-item" v-for="(item,index) in tablist" :key="index"
:class="{active: tabindex == index}" @click="btntab(item,index)">
{{item.name}}
<view class="active-indicator" v-if="tabindex == index"></view>
</view>
</view>
<view class="filter-option" @click="btndj">
<view class="custom-checkbox" :class="{checked: !chenchex}">
<u-icon name="checkmark" color="#fff" size="20" v-if="!chenchex"></u-icon>
</view>
<text>仅看我的</text>
</view>
</view>
</view>
<!-- 列表区域 -->
<scroll-view @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh"
:refresher-triggered="isRefreshing" class="list-container">
<!-- 占位元素,把内容顶下来 -->
<!-- <view style="height: 220rpx;"></view> -->
<view class="card" v-for="(item,index) in hdlist" :key="index" @click="btnitem(item)">
<!-- 卡片头部 -->
<view class="card-header">
<view class="order-info">
<text class="no">No.{{item.no}}</text>
</view>
<view class="status-badge" :class="'status-' + item.status">
<text v-if="item.status == 1">待接单</text>
<text v-else-if="item.status == 2">进行中</text>
<text v-else-if="item.status == 3">已完成</text>
</view>
</view>
<!-- 卡片内容 -->
<view class="card-body">
<view class="info-row time-row">
<u-icon name="clock" size="28" color="#999" style="margin-right: 8rpx;"></u-icon>
<text>{{item.createTime}}</text>
</view>
<view class="info-grid">
<view class="grid-item">
<text class="label">当前电量</text>
<text class="value highlight">{{item.createPower}}%</text>
</view>
<view class="grid-item">
<text class="label">车牌号</text>
<text class="value">{{item.deviceVehicleNum || '暂无'}}</text>
</view>
<view class="grid-item full-width">
<text class="label">设备编号</text>
<text class="value">{{item.deviceSn}}</text>
</view>
</view>
</view>
<!-- 卡片底部操作 -->
<view class="card-footer" v-if="item.status != 3">
<view class="spacer"></view>
<view class="action-btn primary" v-if="item.status == 1" @click.stop="btnjd(item,index)">
立即接单
</view>
<view class="action-btn success" v-if="item.status == 2" @click.stop="btnwc(item,index)">
确认完成
</view>
</view>
</view>
<view class="loading-text">
{{ total > hdlist.length ? '加载中...' : '当前没有更多工单咯' }}
</view>
<view style="height: 150rpx;"></view>
</scroll-view>
<!-- 悬浮添加按钮 -->
<view class="fab-add" @click="btnaddflag">
<u-icon name="plus" color="#fff" size="48"></u-icon>
</view>
<!-- 完成输入备注弹窗 -->
<view class="modal-mask" v-if="beizhuflag" @click="btnqx"></view>
<view class="custom-modal" v-if="beizhuflag">
<view class="modal-title">工单备注</view>
<textarea class="modal-textarea" v-model="finishRemark" placeholder="请输入完成备注信息..." fixed="true"></textarea>
<view class="modal-footer">
<view class="btn cancel" @click="btnqx">取消</view>
<view class="btn confirm" @click="btnqdwc">完成</view>
</view>
</view>
<!-- 新增工单弹窗 -->
<view class="modal-mask" v-if="addflag" @click="btnyc"></view>
<view class="add-popup" v-if="addflag">
<view class="popup-header">
<text class="title">添加换电工单</text>
<view class="close-btn" @click="btnyc">
<u-icon name="close" size="32" color="#999"></u-icon>
</view>
</view>
<scroll-view scroll-y class="popup-body">
<!-- 选择设备 -->
<view class="form-section">
<view class="section-title">选择设备</view>
<view class="search-input-group">
<view class="scan-mini" @click="btnsaoma">
<u-icon name="scan" size="36" color="#333"></u-icon>
</view>
<view class="v-divider"></view>
<input type="text" v-model="bianhao" placeholder="输入车辆编号/车牌号" />
<view class="add-btn-mini" @click="btnadd">添加</view>
</view>
<view class="selected-tags">
<view class="tag-item" v-for="(item,index) in addlist" :key="index">
<text>{{item.sn}} {{item.vehicleNum ? `(${item.vehicleNum})` : ''}}</text>
<u-icon name="close-circle-fill" color="#ff4d4f" size="32" @click="btndel(item)" style="margin-left: 10rpx;"></u-icon>
</view>
</view>
</view>
<!-- 派发人员 -->
<view class="form-section">
<view class="section-title">派发人员</view>
<view class="select-box" @click="btnry">
<text :class="{placeholder: renyuan === '请选择'}">{{renyuan}}</text>
<u-icon :name="renyuanflag ? 'arrow-up' : 'arrow-down'" color="#999" size="28"></u-icon>
</view>
<view class="dropdown-list" v-if="renyuanflag">
<view class="dropdown-item" v-for="(item,index) in xzlist" :key="index" @click="btnrenyuan(item)">
<text class="name">{{item.userName}}</text>
<text class="remark">{{item.remark || '无备注'}}</text>
</view>
</view>
</view>
<!-- 备注 -->
<view class="form-section">
<view class="section-title">备注信息</view>
<textarea class="form-textarea" v-model="txtcont" placeholder="请输入工单备注..." fixed="true"></textarea>
</view>
</scroll-view>
<view class="popup-footer">
<view class="submit-btn" @click="btnaddqd">确认创建</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
tablist: [{
name: '待换电',
type: '1'
}, {
name: '换电中',
type: '2'
}, {
name: '已完成',
type: '3'
}, {
name: '全部',
type: '4'
}],
bgc: {
backgroundColor: "#fff",
},
areaId: '',
chenchex: true, // 注意:原逻辑 true 是 "未选中" (check value=true 点击变 false), false 是 "选中"
tabindex: 0,
pageNum: 1,
hdlist: [],
total: 0,
isRefreshing: false,
status: 1,
userId: '',
beizhuflag: false,
wcid: '',
wcindex: '',
finishRemark: '',
sn: '',
txtcont: '',
xzlist: [],
renyuan: '请选择',
renyuanid: '',
renyuanflag: false,
bianhao: '',
addlist: [],
addflag: false
}
},
onLoad(option) {
this.areaId = option.areaId
this.getlist()
this.getshebei()
},
methods: {
// 点击确定添加工单
btnaddqd() {
let arrid = []
this.addlist.forEach(item => {
arrid.push(item.id)
})
let data = {
deviceIds: arrid,
receiveUserId: this.renyuanid,
createRemark: this.txtcont
}
this.$u.post(`/bst/powerWork`, data).then(res => {
if (res.code == 200) {
this.addflag = false
this.pageNum = 1
this.addlist = []
this.getlist()
uni.showToast({
title: '添加成功',
icon: 'success',
duration: 3000
})
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 3000
})
}
})
},
// 点击显示添加工单
btnaddflag() {
this.addflag = true
},
// 点击隐藏添加工单
btnyc() {
this.addflag = false
this.addlist = []
},
// 点击删除设备
btndel(item) {
const deviceId = item.id
this.addlist = this.addlist.filter(item => item.id !== deviceId)
console.log(this.addlist);
},
// 点击添加设备
btnadd() {
if (!this.bianhao) return;
this.$u.get(`/bst/device?keyword=${this.bianhao}`).then(res => {
if (res.code == 200) {
if (res.data) {
this.bianhao = ''
const isExist = this.addlist.some(item => item.id == res.data.id)
if (isExist) {
uni.showToast({
title: '该设备已存在',
icon: 'none',
duration: 3000
})
} else {
this.addlist.push(res.data)
console.log(this.addlist);
}
} else {
uni.showToast({
title: '未查询到该设备',
icon: 'none',
duration: 2000
})
}
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
},
// 点击显示人员列表
btnry() {
this.renyuanflag = !this.renyuanflag
},
// 点击选择派发人员
btnrenyuan(item) {
this.renyuan = item.userName
this.renyuanid = item.userId
this.renyuanflag = false
},
// 请求运营区所有人员
getshebei() {
this.$u.get(`/bst/areaJoin/list?areaId=${this.areaId}&pageNum=1&pageSize=999`).then(res => {
if (res.code == 200) {
this.xzlist = res.rows
}
})
},
btnqx() {
this.beizhuflag = false
},
// 点击跳转详情
btnitem(item) {
uni.navigateTo({
url: '/page_shanghu/yunwei/gongdanxq?id=' + item.id
})
},
// 点击进行搜索
btnsousuo() {
this.pageNum = 1
this.getlist()
},
// 点击扫码
btnsaoma() {
uni.scanCode({
onlyFromCamera: true,
scanType: ['qrCode'],
success: res => {
console.log(res)
function getQueryParam(url, paramName) {
let regex = new RegExp(`[?&]${paramName}=([^&]*)`)
let results = regex.exec(url)
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null
}
let sceneValue = res.result
let decodedValue = decodeURIComponent(sceneValue)
this.sn = getQueryParam(decodedValue, 's') || getQueryParam(decodedValue, 'sn')
this.bianhao = getQueryParam(decodedValue, 's') || getQueryParam(decodedValue, 'sn')
this.pageNum = 1
this.getlist()
},
fail: err => {
console.error('扫描失败:', err)
uni.showToast({
title: '扫描失败',
icon: 'none',
duration: 2000
})
}
})
},
// 点击完成
btnwc(item, index) {
this.beizhuflag = true
this.wcid = item.id
this.wcindex = index
// this.getlist() // 没必要重新获取列表
},
//点击确定完成
btnqdwc() {
let data = {
id: this.wcid,
finishRemark: this.finishRemark
}
this.$u.put(`/bst/powerWork/complete`, data).then(res => {
if (res.code == 200) {
uni.showToast({
title: '操作完成',
icon: 'success',
duration: 2000
})
this.hdlist[this.wcindex].status = 3
this.beizhuflag = false
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
},
// 点击接单
btnjd(item, index) {
let that = this
uni.showModal({
title: '提示',
content: '您是否要进行接单?',
showCancel: true,
success: function(res) {
if (res.confirm) {
let data = {
id: item.id
}
that.$u.put(`/bst/powerWork/receive`, data).then(res => {
if (res.code == 200) {
uni.showToast({
title: '接单成功',
icon: 'success',
duration: 2000
})
that.hdlist[index].status = 2
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
})
} else if (res.cancel) {
}
}
})
},
// 点击是否只查看自己的
btndj() {
this.chenchex = !this.chenchex
if (this.chenchex == false) {
this.userId = uni.getStorageSync('user').userId
this.pageNum = 1
this.getlist()
} else {
this.userId = ''
this.pageNum = 1
this.getlist()
}
console.log(this.chenchex);
},
// 点击切换tab
btntab(item, index) {
this.tabindex = index
this.status = item.type
this.pageNum = 1
this.getlist()
},
// 获取换电工单列表
getlist() {
this.$u.get(
`/bst/powerWork/list?pageNum=${this.pageNum}&pageSize=20&areaId=${this.areaId}&status=${this.status == 4 ? '' : this.status}&receiveUserId=${this.userId}&keyword=${this.sn}`
).then((res) => {
if (res.code === 200) {
this.total = res.total
if (this.pageNum == 1) {
this.hdlist = res.rows
} else {
this.hdlist = this.hdlist.concat(res.rows)
}
}
})
},
handqixing() {
console.log(1);
if (this.hdlist.length < this.total) {
this.pageNum++ // 这里需要增加页码
this.getlist()
}
},
// 下拉刷新
onRefresh() {
this.isRefreshing = true
setTimeout(() => {
this.isRefreshing = false
this.pageNum = 1
this.getlist()
}, 1000)
},
}
}
</script>
<style lang="scss">
page {
background: #F7F7F7;
}
.page {
width: 100%;
min-height: 100vh;
}
/* 固定头部 */
.fixed-header {
// position: fixed;
// top: 0;
/* 注意这里top设为0实际可能需要避开navbar高度或者navbar是占位的 */
/* u-navbar默认是fixed的如果不占位需要margin-top根据原代码推测这里需要用margin-top来避开navbar */
/* u-navbar :height="44" -> 88rpx. + statusbar height. */
/* 为了简单适配建议给list-container加padding-topheader用z-index覆盖 */
/* 这里我们将header放在navbar下面 */
// margin-top: calc(44px + var(--status-bar-height));
// left: 0;
width: 100%;
z-index: 10;
background-color: #fff;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
}
/* 搜索栏 */
.search-box {
padding: 20rpx 30rpx;
background: #fff;
.search-bar {
display: flex;
align-items: center;
background: #F5F7FA;
border-radius: 40rpx;
padding: 12rpx 24rpx;
height: 80rpx;
box-sizing: border-box;
.scan-btn {
padding: 10rpx;
}
.divider {
width: 2rpx;
height: 32rpx;
background: #DCDFE6;
margin: 0 20rpx;
}
input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.placeholder-style {
color: #999;
}
.search-btn {
padding: 10rpx;
}
}
}
/* 筛选栏 */
.filter-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
height: 88rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
.tab-list {
display: flex;
gap: 40rpx;
.tab-item {
font-size: 28rpx;
color: #666;
position: relative;
padding: 20rpx 0;
font-weight: 500;
&.active {
color: #4C97E7;
font-weight: 600;
font-size: 30rpx;
}
.active-indicator {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 6rpx;
background: #4C97E7;
border-radius: 4rpx;
}
}
}
.filter-option {
display: flex;
align-items: center;
font-size: 26rpx;
color: #666;
.custom-checkbox {
width: 32rpx;
height: 32rpx;
border: 2rpx solid #ccc;
border-radius: 6rpx;
margin-right: 12rpx;
display: flex;
align-items: center;
justify-content: center;
&.checked {
background: #4C97E7;
border-color: #4C97E7;
}
}
}
}
/* 列表容器 */
.list-container {
height: 75vh;
box-sizing: border-box;
padding: 0 24rpx;
padding-top: 24rpx;
}
/* 卡片样式 */
.card {
background: #FFFFFF;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 24rpx;
border-bottom: 1rpx solid #f5f5f5;
margin-bottom: 24rpx;
.no {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.status-badge {
padding: 6rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
&.status-1 {
background: #FFF0F0;
color: #FF4D4F;
}
&.status-2 {
background: #EBF4FF;
color: #4C97E7;
}
&.status-3 {
background: #F0F9EB;
color: #67C23A;
}
}
}
.card-body {
.info-row {
display: flex;
align-items: center;
font-size: 26rpx;
color: #999;
margin-bottom: 20rpx;
}
.info-grid {
display: flex;
flex-wrap: wrap;
background: #F8F9FB;
border-radius: 12rpx;
padding: 20rpx;
.grid-item {
width: 50%;
display: flex;
flex-direction: column;
margin-bottom: 16rpx;
&.full-width {
width: 100%;
margin-bottom: 0;
}
.label {
font-size: 24rpx;
color: #999;
margin-bottom: 8rpx;
}
.value {
font-size: 28rpx;
color: #333;
font-weight: 500;
word-break: break-all;
&.highlight {
color: #4C97E7;
font-weight: bold;
font-size: 32rpx;
}
}
}
}
}
.card-footer {
margin-top: 24rpx;
display: flex;
.spacer {
flex: 1;
}
.action-btn {
min-width: 160rpx;
height: 64rpx;
border-radius: 32rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: 500;
&.primary {
background: #4C97E7;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(76, 151, 231, 0.3);
}
&.success {
background: #67C23A;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(103, 194, 58, 0.3);
}
}
}
}
.loading-text {
text-align: center;
color: #999;
font-size: 24rpx;
padding: 30rpx 0;
}
/* 悬浮按钮 */
.fab-add {
position: fixed;
right: 40rpx;
bottom: 180rpx;
width: 110rpx;
height: 110rpx;
background: #4C97E7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(76, 151, 231, 0.4);
z-index: 100;
transition: all 0.2s;
&:active {
transform: scale(0.95);
}
}
/* 弹窗通用 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.6);
z-index: 990;
}
/* 备注弹窗 */
.custom-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 600rpx;
background: #fff;
border-radius: 24rpx;
padding: 40rpx;
z-index: 999;
.modal-title {
font-size: 34rpx;
font-weight: 600;
text-align: center;
margin-bottom: 30rpx;
}
.modal-textarea {
width: 100%;
height: 240rpx;
background: #F5F7FA;
border-radius: 12rpx;
padding: 20rpx;
box-sizing: border-box;
font-size: 28rpx;
}
.modal-footer {
display: flex;
gap: 30rpx;
margin-top: 40rpx;
.btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
&.cancel {
background: #F5F7FA;
color: #666;
}
&.confirm {
background: #4C97E7;
color: #fff;
}
}
}
}
/* 新增工单弹窗 */
.add-popup {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 80vh;
background: #fff;
border-radius: 30rpx 30rpx 0 0;
z-index: 999;
display: flex;
flex-direction: column;
.popup-header {
padding: 30rpx 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1rpx solid #f5f5f5;
.title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
}
.popup-body {
flex: 1;
padding: 30rpx 40rpx;
box-sizing: border-box;
overflow-y: auto;
}
.form-section {
margin-bottom: 40rpx;
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
.search-input-group {
display: flex;
align-items: center;
background: #F5F7FA;
border-radius: 12rpx;
padding: 10rpx 20rpx;
border: 1rpx solid #E4E7ED;
.v-divider {
width: 2rpx;
height: 30rpx;
background: #DCDFE6;
margin: 0 20rpx;
}
input {
flex: 1;
font-size: 28rpx;
}
.add-btn-mini {
background: #4C97E7;
color: #fff;
font-size: 24rpx;
padding: 10rpx 24rpx;
border-radius: 8rpx;
}
}
.selected-tags {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-top: 20rpx;
.tag-item {
background: #EBF4FF;
color: #4C97E7;
padding: 12rpx 24rpx;
border-radius: 30rpx;
font-size: 26rpx;
display: flex;
align-items: center;
}
}
.select-box {
background: #F5F7FA;
padding: 24rpx;
border-radius: 12rpx;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #333;
.placeholder {
color: #999;
}
}
.dropdown-list {
margin-top: 10rpx;
background: #fff;
border: 1rpx solid #E4E7ED;
border-radius: 12rpx;
max-height: 300rpx;
overflow-y: auto;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05);
.dropdown-item {
padding: 20rpx;
border-bottom: 1rpx solid #f5f5f5;
display: flex;
justify-content: space-between;
&:active {
background: #F5F7FA;
}
.name { font-size: 28rpx; color: #333; }
.remark { font-size: 24rpx; color: #999; }
}
}
.form-textarea {
width: 100%;
height: 200rpx;
background: #F5F7FA;
border-radius: 12rpx;
padding: 20rpx;
box-sizing: border-box;
font-size: 28rpx;
}
}
.popup-footer {
padding: 20rpx 40rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #f5f5f5;
.submit-btn {
width: 100%;
height: 88rpx;
background: #4C97E7;
border-radius: 44rpx;
color: #fff;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.9;
}
}
}
}
</style>