feat(WorkOrderEdit): 实现工单编辑多步骤表单功能

添加工单编辑的多步骤表单功能,包括基础信息和出入证申请两个步骤:
1. 在基础信息步骤中添加作业负责人和作业班成员选择功能
2. 创建新的出入证申请组件,处理车辆信息和有效日期选择
3. 实现步骤间数据共享和导航功能
This commit is contained in:
liangbin 2026-01-16 16:12:39 +08:00
parent 158a652db7
commit 7880cb1051
3 changed files with 430 additions and 12 deletions

View File

@ -22,7 +22,8 @@
<view class="date-item">
<u-input v-model="startDateText" placeholder="开始日期" readonly>
<template #suffix>
<u-button type="primary" size="small" @click="showStartDatePicker = true">选择日期</u-button>
<u-button type="primary" size="small"
@click="showStartDatePicker = true">选择日期</u-button>
</template>
</u-input>
<up-datetime-picker :show="showStartDatePicker" v-model="startDate" mode="date"
@ -33,7 +34,8 @@
<view class="date-item">
<u-input v-model="endDateText" placeholder="结束日期" readonly>
<template #suffix>
<u-button type="primary" size="small" @click="showEndDatePicker = true">选择日期</u-button>
<u-button type="primary" size="small"
@click="showEndDatePicker = true">选择日期</u-button>
</template>
</u-input>
<up-datetime-picker :show="showEndDatePicker" v-model="endDate" mode="date"
@ -43,6 +45,28 @@
</view>
</view>
</view>
<view class="FormItem">
<view class="FormLableBox mustBox">作业负责人</view>
<view class="FormValueBox">
<u-input v-model="formData.ResponsiblePerson" placeholder="请输入作业负责人"></u-input>
</view>
</view>
<view class="FormItem">
<view class="FormLableBox FlexBox">
<view class="mustBox">作业班成员</view>
<view class="addBtn" @click="showMemberPicker = true">添加</view>
</view>
<view class="FormValueBox">
<view class="MemberBox">
<u-tag v-for="(member, index) in formData.MemberList" :key="member.id" closable shape="circle"
@close="RemoveMember(index)">
{{ member.name }}
</u-tag>
</view>
</view>
<u-picker :show="showMemberPicker" :options="MemberArr" keyName="id" @confirm="HandleMemberConfirm"
@cancel="showMemberPicker = false"></u-picker>
</view>
</view>
</view>
</template>
@ -71,12 +95,31 @@ const endDateMinTimestamp = computed(() => {
return minTimestamp.value;
});
const showMemberPicker = ref(false); //
//
const MemberArr = ref([
{
id: "123456", // ID
name: "张三", //
phone: "13800000000", //
},
{
id: "654321", // ID
name: "李四", //
phone: "13900000000", //
}
]); //
//
const formData = ref({
ProjectName: "", //
Location: "", //
SpecificAddress: "", //
period: [], // [, ]
ResponsiblePerson: "", //
MemberList: [], //
});
//
@ -84,11 +127,13 @@ defineExpose({
getFormData: () => formData.value,
});
//
//
onMounted(() => {
initTiandituMap();
});
//
const HandleStartDateConfirm = (e) => {
console.log('选中的开始日期:', e);
@ -141,6 +186,66 @@ const UpdatePeriod = () => {
console.log('作业周期:', formData.value.period);
};
//
const HandleMemberConfirm = (e) => {
console.log('选中的成员完整信息:', e);
console.log('e.value:', e.value);
console.log('e.value[0]:', e.value ? e.value[0] : 'undefined');
console.log('MemberArr:', MemberArr.value);
if (e.value && e.value.length > 0) {
const selectedMember = e.value[0];
console.log('选中的成员:', selectedMember);
console.log('selectedMember 的类型:', typeof selectedMember);
let memberInfo;
//
if (typeof selectedMember === 'object' && selectedMember !== null) {
// 使
memberInfo = selectedMember;
console.log('返回的是对象,直接使用:', memberInfo);
} else {
// MemberArr id
memberInfo = MemberArr.value.find(item => item.id === selectedMember);
console.log('返回的是字符串,从 MemberArr 根据id查找', memberInfo);
}
if (memberInfo && memberInfo.id) {
// id
const isExist = formData.value.MemberList.some(item => item.id === memberInfo.id);
console.log('成员是否已存在:', isExist);
console.log('当前成员列表:', formData.value.MemberList);
if (!isExist) {
formData.value.MemberList.push({
id: memberInfo.id,
name: memberInfo.name
});
console.log('添加成员成功:', memberInfo);
console.log('添加后的成员列表:', formData.value.MemberList);
} else {
uni.showToast({
title: '该成员已存在',
icon: 'none',
duration: 2000
});
}
} else {
console.log('未找到成员信息或成员信息无效');
}
} else {
console.log('e.value 为空或长度为0');
}
showMemberPicker.value = false;
};
//
const RemoveMember = (index) => {
formData.value.MemberList.splice(index, 1);
console.log('删除成员成功');
};
//
const initTiandituMap = () => {
try {
@ -195,6 +300,21 @@ const initTiandituMap = () => {
</script>
<style lang="scss" scoped>
.FlexBox {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
.addBtn {
font-size: 30rpx;
color: #2979ff;
font-weight: bold;
}
.MainBox {
padding: 20rpx;
background-color: #fff;
@ -214,8 +334,22 @@ const initTiandituMap = () => {
height: 400rpx;
}
.MemberBox {
height: 100rpx;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 20rpx;
flex-wrap: wrap;
border: 1rpx solid #e4e7ed;
padding: 20rpx;
border-radius: 10rpx;
}
.FormBox {
background-color: #f5f5f5;
// background-color: #f5f5f5;
height: 100%;
.FormItem {

View File

@ -0,0 +1,269 @@
<!-- 出入证申请 -->
<template>
<view class="MainBox">
<view class="FormBox">
<view class="FormItem">
<view class="FormLableBox">项目名称</view>
<view class="FormValueBox">
<u-input v-model="allData.BasicsInfo.ProjectName" placeholder="请输入项目名称" readonly></u-input>
</view>
</view>
<view class="FormItem">
<view class="FormLableBox">作业地点</view>
<view class="FormValueBox">
<u-input v-model="allData.BasicsInfo.SpecificAddress" placeholder="请输入作业地点" readonly></u-input>
</view>
</view>
<view class="FormItem">
<view class="FormLableBox">作业班成员</view>
<view class="FormValueBox">
<u-input v-model="allData.BasicsInfo.JobClassMembers" placeholder="请输入作业班成员" readonly></u-input>
</view>
</view>
<view class="FormItem">
<view class="FormLableBox mustBox">出入证有效日期</view>
<view class="FormValueBox">
<u-input v-model="formData.validTime" placeholder="请输入出入证有效日期" readonly>
<template #suffix>
<u-button text="选择日期" type="primary" size="small"
@click="showValidTimePicker = true"></u-button>
</template>
</u-input>
</view>
<up-datetime-picker :show="showValidTimePicker" v-model="formData.validTime" mode="date"
:minDate="minTimestamp" @confirm="HandleValidTimeConfirm" @cancel="showValidTimePicker = false"
format="YYYY-MM-DD"></up-datetime-picker>
</view>
<view class="FormItem">
<view class="FormLableBox FlexBox">
<view class="mustBox">随行车辆信息</view>
<view class="addBtn" @click="showCarPicker = true">添加</view>
</view>
<view class="FormValueBox">
<view class="CarListBox">
<view v-for="item in formData.CarList" :key="item.id" class="CarItem">
<view class="CarInfoBox">
<view class="CarTitleBox">{{ item.name }} {{ item.code }}</view>
<view class="CartypeBox">{{ item.type }}</view>
</view>
<view>
<u-button text="删除" type="primary" size="small"
@click="HandleDeleteCar(item.id)"></u-button>
</view>
</view>
</view>
</view>
<u-picker :show="showCarPicker" :columns="[CarArr]" keyName="name" @confirm="HandleCarConfirm"
@cancel="showCarPicker = false"></u-picker>
</view>
<view class="FormItem">
<view class="FormLableBox">附件上传</view>
<view class="FormValueBox">
<up-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
:maxCount="10"></up-upload>
</view>
</view>
</view>
</view>
</template>
<script setup>
import dayjs from "dayjs";
import { ref, computed, onMounted, defineExpose } from "vue";
const props = defineProps({
allData: {
type: Object,
default: () => { },
}
})
const showValidTimePicker = ref(false); //
const minTimestamp = ref(dayjs('2020-01-01').valueOf()); //
const showCarPicker = ref(false); //
//
const CarArr = ref([
{
id: 1,
name: '车辆1',
type: '皮卡车',
code: '京A123456',
},
{
id: 2,
name: '车辆2',
type: '轿车',
code: '京B123456',
},
]); //
//
const formData = ref({
validTime: '', //
CarList: [], //
})
//
defineExpose({
getFormData() {
return formData.value;
}
})
//
const HandleValidTimeConfirm = (e) => {
const selectedDate = (e.value && dayjs(e.value).isValid()) ? e.value : minTimestamp.value;
formData.value.validTime = dayjs(selectedDate).format('YYYY-MM-DD');
showValidTimePicker.value = false;
}
//
const HandleCarConfirm = (e) => {
console.log('选中的车辆:', e);
if (e.value && e.value.length > 0) {
const selectedCar = e.value[0];
console.log('选中的车辆:', selectedCar);
console.log('selectedCar 的类型:', typeof selectedCar);
let carInfo;
//
if (typeof selectedCar === 'object' && selectedCar !== null) {
// 使
carInfo = selectedCar;
console.log('返回的是对象,直接使用:', carInfo);
} else {
// CarArr id
carInfo = CarArr.value.find(item => item.id === selectedCar);
console.log('返回的是字符串,从 CarArr 根据id查找', carInfo);
}
if (carInfo && carInfo.id) {
// id
const isExist = formData.value.CarList.some(item => item.id === carInfo.id);
console.log('车辆是否已存在:', isExist);
if (!isExist) {
formData.value.CarList.push({
id: carInfo.id,
name: carInfo.name,
code: carInfo.code,
type: carInfo.type
});
console.log('添加车辆成功:', carInfo);
console.log('添加后的车辆列表:', formData.value.CarList);
} else {
uni.showToast({
title: '该车辆已存在',
icon: 'none',
duration: 2000
});
}
} else {
console.log('未找到车辆信息或车辆信息无效');
}
} else {
console.log('e.value 为空或长度为0');
}
showCarPicker.value = false;
};
//
const HandleDeleteCar = (carId) => {
const index = formData.value.CarList.findIndex(item => item.id === carId);
if (index !== -1) {
formData.value.CarList.splice(index, 1);
console.log('删除车辆成功');
}
};
</script>
<style lang="scss" scoped>
.FlexBox {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
.addBtn {
font-size: 30rpx;
color: #2979ff;
font-weight: bold;
}
.MainBox {
padding: 20rpx;
background-color: #fff;
height: 100%;
overflow: hidden;
}
.mustBox::after {
content: "*";
color: red;
font-size: 30rpx;
margin-left: 10rpx;
}
.CarListBox {
border: 1rpx solid #e4e7ed;
padding: 20rpx;
border-radius: 10rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
.CarItem {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
flex-wrap: wrap;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
padding: 20rpx;
border-radius: 10rpx;
.CarInfoBox {
.CarTitleBox {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.CartypeBox {
font-size: 24rpx;
color: #666;
}
}
}
}
.FormBox {
background-color: #f5f5f5;
height: 100%;
.FormItem {
padding: 20rpx;
background-color: #fff;
.FormLableBox {
font-size: 30rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
.FormValueBox {
font-size: 30rpx;
margin-top: 20rpx;
}
}
}
</style>

View File

@ -4,18 +4,19 @@
<view class="TopBox FlexBox">
<view class="iconBox FlexBox">
<!-- 返回上一页 -->
<u-icon name="arrow-leftward" size="24"></u-icon>
<u-icon name="arrow-leftward" size="24" @click="prevStep"></u-icon>
<!-- 返回主页 -->
<u-icon name="home" size="24"></u-icon>
</view>
<view class="Title">工单编辑</view>
<view class="Title">{{stepList[currentStep - 1].Title}}</view>
<view class="informBox">
<u-icon name="bell-fill" size="24"></u-icon>
<view class="MsgTxt"></view>
</view>
</view>
<view class="ContentBox">
<BasicsInfo ref="basicsInfoRef"></BasicsInfo>
<BasicsInfo ref="basicsInfoRef" :allData="allData" v-if="currentStep == 1"></BasicsInfo>
<GatePassInfo ref="gatePassInfoRef" :allData="allData" v-if="currentStep == 2"></GatePassInfo>
</view>
<view class="FootBox" v-for="item in stepList" :key="item.id">
<u-button :text="item.PrevBtnName" type="info"></u-button>
@ -26,9 +27,16 @@
<script setup>
import { ref } from 'vue'
import BasicsInfo from './compoents/BasicsInfo.vue'
import GatePassInfo from './compoents/GatePassInfo.vue'
const currentStep = ref(1)//
const basicsInfoRef = ref(null); //
//
const allData = ref({
BasicsInfo: {}, //
GatePassInfo: {}, //
});
//
const stepList = ref([
{
@ -51,9 +59,16 @@ const stepList = ref([
},
])
//
const prevStep = () => {
currentStep.value--;
}
//
const nextStep = () => {
console.log(basicsInfoRef.value.getFormData());
allData.value.BasicsInfo = basicsInfoRef.value.getFormData();
currentStep.value++;
}