493 lines
17 KiB
Vue
493 lines
17 KiB
Vue
<!-- 工单基础信息 -->
|
||
<template>
|
||
<view class="MainBox">
|
||
<view class="FormBox">
|
||
<view class="FormItem">
|
||
<view class="FormLableBox mustBox">项目名称</view>
|
||
<view class="FormValueBox">
|
||
<u-input v-model="formData.ProjectName" placeholder="请输入项目名称"></u-input>
|
||
</view>
|
||
</view>
|
||
<view class="FormItem">
|
||
<view class="FormLableBox mustBox">作业地点</view>
|
||
<view class="MapBox" id="tianditu-map"> </view>
|
||
<view class="FormValueBox">
|
||
<u-input v-model="formData.SpecificAddress" placeholder="请输入具体楼层或区域"></u-input>
|
||
</view>
|
||
</view>
|
||
<view class="FormItem">
|
||
<view class="FormLableBox mustBox">作业周期</view>
|
||
<view class="FormValueBox">
|
||
<view class="date-range-box">
|
||
<view class="date-item">
|
||
<u-input v-model="startDateText" placeholder="开始日期" readonly>
|
||
<template #suffix>
|
||
<u-button type="primary" size="small"
|
||
@click="showStartDatePicker = true">选择日期</u-button>
|
||
</template>
|
||
</u-input>
|
||
<up-datetime-picker :show="showStartDatePicker" v-model="startDate" mode="date"
|
||
:minDate="minTimestamp" @confirm="HandleStartDateConfirm"
|
||
@cancel="showStartDatePicker = false"></up-datetime-picker>
|
||
</view>
|
||
<view class="date-separator">至</view>
|
||
<view class="date-item">
|
||
<u-input v-model="endDateText" placeholder="结束日期" readonly>
|
||
<template #suffix>
|
||
<u-button type="primary" size="small"
|
||
@click="showEndDatePicker = true">选择日期</u-button>
|
||
</template>
|
||
</u-input>
|
||
<up-datetime-picker :show="showEndDatePicker" v-model="endDate" mode="date"
|
||
:minDate="minTimestamp" @confirm="HandleEndDateConfirm"
|
||
@cancel="showEndDatePicker = false"></up-datetime-picker>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="FormItem">
|
||
<view class="FormLableBox FlexBox">
|
||
<view class="mustBox">作业负责人</view>
|
||
<view class="addBtn" @click="showMemberPickerA = true">添加</view>
|
||
</view>
|
||
<view class="FormValueBox">
|
||
<u-input v-model="formData.supervisorName" placeholder="请输入作业负责人" readonly></u-input>
|
||
</view>
|
||
<u-picker :show="showMemberPickerA" :columns="[MemberArr]" keyName="name" @confirm="HandleMemberConfirmA"
|
||
@cancel="showMemberPickerA = false"></u-picker>
|
||
</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" :columns="[MemberArr]" keyName="name" @confirm="HandleMemberConfirm"
|
||
@cancel="showMemberPicker = false"></u-picker>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<script setup>
|
||
import { ref, computed, onMounted, watch, defineExpose } from "vue";
|
||
import dayjs from 'dayjs';
|
||
import { getUserList } from '@/api';
|
||
|
||
const props = defineProps({
|
||
allData: {
|
||
type: Object,
|
||
default: () => ({})
|
||
}
|
||
})
|
||
|
||
// 地图实例
|
||
let mapEntity = null;
|
||
// 当前标记点
|
||
let currentMarker = null;
|
||
|
||
const showMemberPickerA = ref(false); // 是否显示成员选择器1
|
||
const showStartDatePicker = ref(false); // 是否显示开始日期选择器
|
||
const showEndDatePicker = ref(false); // 是否显示结束日期选择器
|
||
const startDate = ref(null); // 开始日期时间戳
|
||
const endDate = ref(null); // 结束日期时间戳
|
||
const startDateText = ref(''); // 开始日期显示文本
|
||
const endDateText = ref(''); // 结束日期显示文本
|
||
const minTimestamp = ref(dayjs('2020-01-01').valueOf()); // 最小可选日期时间戳
|
||
|
||
// 结束日期的最小时间戳(不能小于开始日期)
|
||
const endDateMinTimestamp = computed(() => {
|
||
if (startDate.value) {
|
||
return startDate.value;
|
||
}
|
||
return minTimestamp.value;
|
||
});
|
||
|
||
const showMemberPicker = ref(false); // 是否显示成员选择器
|
||
//成员列表信息
|
||
const MemberArr = ref([]); // 作业班成员列表
|
||
|
||
|
||
|
||
// 表单信息
|
||
const formData = ref({
|
||
ProjectName: "", // 项目名称
|
||
Location: "", // 作业地点(经纬度)
|
||
SpecificAddress: "", // 具体楼层或区域
|
||
period: [], // 作业周期 [开始日期, 结束日期]
|
||
supervisorName: "", // 作业负责人
|
||
supervisorId: "", // 作业负责人ID
|
||
MemberList: [], // 作业班成员列表
|
||
});
|
||
|
||
// 更新作业周期
|
||
const UpdatePeriod = () => {
|
||
if (startDateText.value && endDateText.value) {
|
||
formData.value.period = [startDateText.value, endDateText.value];
|
||
} else if (startDateText.value) {
|
||
formData.value.period = [startDateText.value, ''];
|
||
} else if (endDateText.value) {
|
||
formData.value.period = ['', endDateText.value];
|
||
} else {
|
||
formData.value.period = [];
|
||
}
|
||
console.log('作业周期:', formData.value.period);
|
||
};
|
||
|
||
// 暴露方法给父组件调用
|
||
defineExpose({
|
||
getFormData: () => formData.value,
|
||
});
|
||
|
||
// 从 allData 中恢复之前填写的数据
|
||
const restoreFormData = (data) => {
|
||
if (!data || Object.keys(data).length === 0) return;
|
||
|
||
if (data.ProjectName) formData.value.ProjectName = data.ProjectName;
|
||
if (data.Location) formData.value.Location = data.Location;
|
||
if (data.SpecificAddress) formData.value.SpecificAddress = data.SpecificAddress;
|
||
if (data.supervisorName) formData.value.supervisorName = data.supervisorName;
|
||
if (data.supervisorId) formData.value.supervisorId = data.supervisorId;
|
||
|
||
if (data.MemberList && Array.isArray(data.MemberList)) {
|
||
formData.value.MemberList = [...data.MemberList];
|
||
}
|
||
if (data.period && Array.isArray(data.period) && data.period.length >= 2) {
|
||
if (data.period[0]) {
|
||
// 处理日期字符串,提取日期部分(处理 "2020-01-01 00:00:00" 格式)
|
||
const startDateStr = String(data.period[0]).split(' ')[0];
|
||
startDateText.value = startDateStr;
|
||
startDate.value = dayjs(startDateStr).isValid() ? dayjs(startDateStr).valueOf() : null;
|
||
}
|
||
if (data.period[1]) {
|
||
// 处理日期字符串,提取日期部分(处理 "2020-01-01 00:00:00" 格式)
|
||
const endDateStr = String(data.period[1]).split(' ')[0];
|
||
endDateText.value = endDateStr;
|
||
endDate.value = dayjs(endDateStr).isValid() ? dayjs(endDateStr).valueOf() : null;
|
||
}
|
||
// 更新 formData.period,确保数据同步
|
||
UpdatePeriod();
|
||
}
|
||
};
|
||
|
||
// 监听 allData.BasicsInfo 的变化,恢复表单数据
|
||
watch(() => props.allData?.BasicsInfo, (newData) => {
|
||
if (newData && Object.keys(newData).length > 0) {
|
||
restoreFormData(newData);
|
||
}
|
||
}, { immediate: true, deep: true });
|
||
|
||
// 获取作业班成员列表
|
||
const fetchUserList = async () => {
|
||
try {
|
||
const res = await getUserList();
|
||
console.log('获取用户列表成功:', res);
|
||
// 接口返回的数据结构为 { total: number, rows: [...], code: 200, msg: "查询成功" }
|
||
if (res && res.rows && Array.isArray(res.rows)) {
|
||
// 将用户数据转换为组件需要的格式 { id: userId, name: nickName || userName }
|
||
MemberArr.value = res.rows.map(user => ({
|
||
id: user.userId,
|
||
name: user.nickName || user.userName || ''
|
||
}));
|
||
console.log('转换后的成员列表:', MemberArr.value);
|
||
} else if (res && res.data && Array.isArray(res.data)) {
|
||
// 兼容其他可能的数据结构
|
||
MemberArr.value = res.data.map(user => ({
|
||
id: user.userId || user.id,
|
||
name: user.nickName || user.userName || user.name || ''
|
||
}));
|
||
} else if (Array.isArray(res)) {
|
||
// 兼容直接返回数组的情况
|
||
MemberArr.value = res.map(user => ({
|
||
id: user.userId || user.id,
|
||
name: user.nickName || user.userName || user.name || ''
|
||
}));
|
||
} else {
|
||
console.warn('用户列表数据格式不正确:', res);
|
||
uni.showToast({
|
||
title: '获取用户列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户列表失败:', error);
|
||
uni.showToast({
|
||
title: error.message || '获取用户列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
};
|
||
|
||
// 生命周期钩子函数
|
||
onMounted(() => {
|
||
initTiandituMap();
|
||
|
||
// 获取作业班成员列表
|
||
fetchUserList();
|
||
|
||
// 组件挂载后,从 allData 中恢复数据
|
||
if (props.allData?.BasicsInfo && Object.keys(props.allData.BasicsInfo).length > 0) {
|
||
restoreFormData(props.allData.BasicsInfo);
|
||
}
|
||
});
|
||
|
||
|
||
|
||
// 处理开始日期确认事件
|
||
const HandleStartDateConfirm = (e) => {
|
||
console.log('选中的开始日期:', e);
|
||
|
||
// 如果没有选择日期,使用默认值
|
||
const selectedDate = (e.value && dayjs(e.value).isValid()) ? e.value : minTimestamp.value;
|
||
|
||
startDateText.value = dayjs(selectedDate).format('YYYY-MM-DD');
|
||
startDate.value = selectedDate;
|
||
endDateText.value = '';
|
||
endDate.value = null;
|
||
showStartDatePicker.value = false;
|
||
UpdatePeriod();
|
||
};
|
||
|
||
// 处理结束日期确认事件
|
||
const HandleEndDateConfirm = (e) => {
|
||
console.log('选中的结束日期:', e);
|
||
|
||
// 如果没有选择日期,使用默认值
|
||
const selectedDate = (e.value && dayjs(e.value).isValid()) ? e.value : minTimestamp.value;
|
||
|
||
// 检查是否选择了开始日期
|
||
if (startDate.value && dayjs(selectedDate).isBefore(dayjs(startDate.value))) {
|
||
uni.showToast({
|
||
title: '结束日期不能小于开始日期',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
endDateText.value = dayjs(selectedDate).format('YYYY-MM-DD');
|
||
endDate.value = selectedDate;
|
||
showEndDatePicker.value = false;
|
||
UpdatePeriod();
|
||
};
|
||
|
||
// 处理作业负责人选择确认事件
|
||
const HandleMemberConfirmA = (e) =>{
|
||
console.log('选中的作业负责人完整信息:', e.value);
|
||
if (e.value) {
|
||
formData.value.supervisorName = e.value[0].name;
|
||
formData.value.supervisorId = e.value[0].id;
|
||
showMemberPickerA.value = false;
|
||
}
|
||
}
|
||
|
||
// 处理作业组成员选择确认事件
|
||
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 {
|
||
// 1. 创建地图容器
|
||
const container = document.getElementById("tianditu-map");
|
||
if (!container) {
|
||
console.error("地图容器未找到");
|
||
return;
|
||
}
|
||
// 2. 初始化地图(中心点设为北京,缩放级别12)
|
||
mapEntity = new T.Map("tianditu-map");
|
||
const centerPoint = new T.LngLat(116.403874, 39.914885);
|
||
mapEntity.centerAndZoom(centerPoint, 12);
|
||
|
||
// 3. 开启地图拖拽、缩放交互
|
||
mapEntity.enableDrag();
|
||
mapEntity.enableScrollWheelZoom();
|
||
|
||
// 4. 添加天地图图层(矢量图层 + 注记图层)
|
||
const vecLayer = new T.TileLayer("vec_w", { key: "你的天地图Key" });
|
||
const cvaLayer = new T.TileLayer("cva_w", { key: "你的天地图Key" });
|
||
const layerGroup = new T.LayerGroup([vecLayer, cvaLayer]);
|
||
mapEntity.addLayer(layerGroup);
|
||
|
||
// 5. 添加地图点击事件监听器
|
||
mapEntity.addEventListener("click", (e) => {
|
||
const lng = e.lnglat.getLng();
|
||
const lat = e.lnglat.getLat();
|
||
|
||
// 如果已存在标记点,先移除
|
||
if (currentMarker) {
|
||
mapEntity.removeOverLay(currentMarker);
|
||
}
|
||
|
||
// 创建新的标记点
|
||
const marker = new T.Marker(new T.LngLat(lng, lat));
|
||
mapEntity.addOverLay(marker);
|
||
currentMarker = marker;
|
||
|
||
// 将经纬度存储到Location字段
|
||
formData.value.Location = `${lng},${lat}`;
|
||
|
||
console.log("标记点位置:", lng, lat);
|
||
});
|
||
|
||
console.log("天地图初始化成功");
|
||
} catch (error) {
|
||
console.error("天地图初始化失败:", error);
|
||
}
|
||
};
|
||
|
||
|
||
</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: auto;
|
||
}
|
||
|
||
.mustBox::after {
|
||
content: "*";
|
||
color: red;
|
||
font-size: 30rpx;
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
.MapBox {
|
||
width: 100%;
|
||
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;
|
||
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;
|
||
|
||
.date-range-box {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20rpx;
|
||
flex: 1;
|
||
|
||
.date-item {
|
||
flex: 1;
|
||
}
|
||
|
||
.date-separator {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|