2025-08-01 09:34:21 +08:00

516 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="container">
<view class="content">
<!-- 选择区域 -->
<view class="form-item">
<view class="label" @click="showTypeSelect = true" style="margin-bottom: 0;">
<u-icon name="list" color="#3B8CFF" size="38"></u-icon>
选择区域
<view class="select-box">
{{ workOrderArea || '请选择' }}
<u-icon name="arrow-down" color="#999" size="30"></u-icon>
</view>
</view>
<u-picker
:show="showTypeSelect"
:columns="areaList"
keyName="title"
@confirm="handleTypeConfirm"
@cancel="showTypeSelect = false">
</u-picker>
</view>
<!-- 工单分类 -->
<view class="form-item">
<view class="label" @click="showSelect = true" style="margin-bottom: 0;">
<u-icon name="list" color="#3B8CFF" size="38"></u-icon>
工单类型
<view class="select-box">
{{ workOrderCategory || '请选择' }}
<u-icon name="arrow-down" color="#999" size="30"></u-icon>
</view>
</view>
<u-picker :show="showSelect" :columns="categoryList" keyName="label" @confirm="handleConfirm"
@cancel="handleCancel">
</u-picker>
</view>
<view class="form-item">
<view class="label">
<u-icon name="order" color="#3B8CFF" size="38"></u-icon>
工单内容
</view>
<textarea v-model="workOrderContent" placeholder="请输入工单详细内容..." class="textarea" />
<view class="voice-icon">
<u-icon name="mic" color="#409EFF" size="36"></u-icon>
</view>
</view>
<!-- 联系人信息 -->
<view class="form-item contact-info">
<view class="contact-row">
<view class="label">
<u-icon name="account" color="#3B8CFF" size="38"></u-icon>
联系人
</view>
<input v-model="concatName" placeholder="请输入联系人姓名" class="contact-input" />
</view>
<view class="contact-row">
<view class="label">
<u-icon name="phone" color="#3B8CFF" size="38"></u-icon>
电话
</view>
<input v-model="customerPhone" placeholder="请输入联系人电话" class="contact-input" type="number" />
</view>
</view>
<!-- 上传照片/视频 -->
<view class="form-item">
<view class="label">
<u-icon name="photo" color="#3B8CFF" size="38"></u-icon>
上传照片/视频
</view>
<view class="upload-area">
<u-upload :fileList="fileList" @afterRead="handleAfterRead" @delete="handleDelete" :maxCount="8"
width="160" height="160" accept="image/*,video/*" :previewFullImage="true">
</u-upload>
</view>
<text class="note">照片可识别工单内容支持分批上传但最大数量为8</text>
</view>
<!-- 工单地点 -->
<view class="form-item">
<view class="label">
<u-icon name="map" color="#3B8CFF" size="38"></u-icon>
工作地点
</view>
<textarea v-model="workOrderLocation" placeholder="请输入详细工作地点..." class="textarea" />
</view>
<!-- 提交按钮 -->
<view class="submit-button">
<u-button type="primary" @click="handleSubmit" shape="circle">提交工单</u-button>
</view>
</view>
<Footer></Footer>
</view>
</template>
<script>
import Footer from '@/components/footer_common.vue';
import {
get,
post
} from '@/utils/request';
import {
IMAGE_BASE_URL,
BASE_URL
} from '@/utils/config';
export default {
components: {
Footer
},
data() {
return {
workOrderContent: '',
concatName: '', // 新增联系人姓名字段
customerPhone: '', // 新增联系人电话字段
fileList: [],
workOrderCategory: '',
workOrderCategoryID: '',
workOrderArea:'',
workOrderAreaID:'',
showSelect: false,
showTypeSelect:false,
categoryList: [],
workOrderLocation: '',
expectedTime: '',
uploadedImages: [],
uploadedVideos: [],
videoFormats: ['mp4', 'mov', 'avi', 'wmv', 'mpeg', '3gp', 'flv', 'mkv'],
areaList:[],
};
},
mounted() {
this.getOrderList();
this.getOrderTypeList();
},
methods: {
handleBack() {
uni.navigateBack({
delta: 1
});
},
// 文件上传相关方法
handleAfterRead(event) {
const {
file
} = event;
let files = [].concat(file);
if (this.fileList.length + files.length > 8) {
uni.showToast({
title: '最多只能上传8个文件',
icon: 'none'
});
return;
}
files.forEach(item => {
const extension = this.getFileExtension(item.url);
const isVideo = this.videoFormats.includes(extension.toLowerCase());
this.fileList.push({
...item,
status: 'uploading',
message: '上传中...',
isVideo: isVideo
});
this.uploadFile(item, isVideo);
});
},
getFileExtension(filename) {
return filename.split('.').pop().split('?')[0];
},
handleDelete(event) {
const {
index
} = event;
const file = this.fileList[index];
if (file.isVideo) {
const videoIndex = this.uploadedVideos.indexOf(file.url);
if (videoIndex !== -1) this.uploadedVideos.splice(videoIndex, 1);
} else {
const imageIndex = this.uploadedImages.indexOf(file.url);
if (imageIndex !== -1) this.uploadedImages.splice(imageIndex, 1);
}
this.fileList.splice(index, 1);
},
async uploadFile(file, isVideo) {
try {
const fullUrl = await this.uploadAvatar(file.url);
const relativePath = fullUrl.replace(BASE_URL, '');
const fileIndex = this.fileList.findIndex(item => item.url === file.url);
if (fileIndex !== -1) {
this.fileList[fileIndex] = {
...this.fileList[fileIndex],
status: 'success',
message: '上传成功',
url: fullUrl,
relativeUrl: relativePath,
isVideo: isVideo
};
if (isVideo) {
this.uploadedVideos.push(relativePath);
} else {
this.uploadedImages.push(relativePath);
}
}
} catch (error) {
const fileIndex = this.fileList.findIndex(item => item.url === file.url);
if (fileIndex !== -1) {
this.fileList[fileIndex] = {
...this.fileList[fileIndex],
status: 'failed',
message: '上传失败'
};
}
uni.showToast({
title: `${isVideo ? '视频' : '图片'}上传失败: ${error.message}`,
icon: 'none'
});
}
},
uploadAvatar(filePath) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${BASE_URL}/api/v1/upload`,
filePath: filePath,
name: 'file',
header: {
'Authorization': `Bearer ${uni.getStorageSync('token')}`
},
success: (uploadRes) => {
try {
const res = JSON.parse(uploadRes.data);
if (res && res.success) {
resolve(`${IMAGE_BASE_URL}${res.data}`);
} else {
reject(new Error(res.message || '上传失败'));
}
} catch (e) {
reject(new Error('解析响应失败'));
}
},
fail: (err) => {
reject(new Error('上传失败: ' + JSON.stringify(err)));
}
});
});
},
async handleSubmit() {
if (!this.workOrderContent) {
uni.showToast({
title: '请填写工单内容',
icon: 'none'
});
return;
}
if (!this.concatName) {
uni.showToast({
title: '请填写联系人姓名',
icon: 'none'
});
return;
}
if (!this.customerPhone) {
uni.showToast({
title: '请填写联系电话',
icon: 'none'
});
return;
}
if (!this.workOrderCategory) {
uni.showToast({
title: '请选择工单类型',
icon: 'none'
});
return;
}
if (!this.workOrderLocation) {
uni.showToast({
title: '请填写工作地点',
icon: 'none'
});
return;
}
const submitData = {
title: this.workOrderContent,
concatName: this.concatName, // 新增联系人姓名
customerPhone: this.customerPhone, // 新增联系人电话
orderTypeId: this.workOrderCategoryID,
orderAreaId:this.workOrderAreaID,
address: this.workOrderLocation,
images: this.uploadedImages,
videos: this.uploadedVideos,
};
console.log('提交数据:', submitData);
// 调用API保存信息
const res = await post('/api/v1/app_auth/work-order', submitData);
uni.hideLoading();
if (res && res.success) {
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 2000
});
this.resetForm();
setTimeout(()=>{
uni.navigateTo({
url: `/pages/myTickets/index`,
});
},1000)
} else {
uni.showToast({
title: res?.message || '提交失败',
icon: 'none'
});
}
},
resetForm() {
this.workOrderContent = '';
this.concatName = '';
this.customerPhone = '';
this.workOrderCategoryID = '';
this.workOrderCategory = '';
this.workOrderLocation = '';
this.fileList = [];
this.uploadedImages = [];
this.uploadedVideos = [];
this.workOrderArea = '';
this.workOrderAreaID = '';
},
handleConfirm(e) {
this.workOrderCategory = e.value[0].label;
this.workOrderCategoryID = e.value[0].id;
this.showSelect = false;
},
handleTypeConfirm(e) {
this.workOrderArea = e.value[0].title;
this.workOrderAreaID = e.value[0].id;
this.showTypeSelect = false;
},
handleCancel() {
this.showSelect = false;
},
async getOrderList() {
try {
const res = await get('/api/v1/apps/work-order-type');
if (res?.success) {
this.categoryList = [
[...res.data]
]
}
} catch (err) {
console.error('获取工单类型失败:', err);
uni.showToast({
title: '获取工单类型失败',
icon: 'none'
});
}
},
// 获取工作区域
async getOrderTypeList(){
try {
const res = await get('/api/v1/apps/work-order-area');
if (res?.success) {
this.areaList = [
[...res.data]
]
}
} catch (err) {
console.error('获取工作区域失败:', err);
uni.showToast({
title: '获取工作区域失败',
icon: 'none'
});
}
}
}
};
</script>
<style lang="scss" scoped>
.container {
width: 100%;
min-height: 100vh;
background: #f5f7fa;
padding-bottom: 120rpx;
.content {
padding: 20rpx;
.form-item {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
.label {
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 24rpx;
display: flex;
align-items: center;
.u-icon {
margin-right: 12rpx;
}
}
.textarea {
width: 95%;
height: 180rpx;
padding: 16rpx;
background: #f8f8f8;
border-radius: 8rpx;
font-size: 28rpx;
}
.voice-icon {
position: absolute;
right: 40rpx;
bottom: 40rpx;
}
.select-box {
margin-left: auto;
color: #666;
display: flex;
align-items: center;
font-size: 28rpx;
.u-icon {
margin-left: 8rpx;
}
}
.upload-area {
margin-top: 16rpx;
}
.note {
font-size: 24rpx;
color: #999;
margin-top: 24rpx;
display: block;
}
}
.contact-info {
.contact-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.label {
margin-bottom: 0;
width: 160rpx;
flex-shrink: 0;
}
.contact-input {
flex: 1;
padding: 16rpx;
background: #f8f8f8;
border-radius: 8rpx;
font-size: 28rpx;
}
}
}
.submit-button {
margin-top: 40rpx;
.u-button {
height: 88rpx;
font-size: 32rpx;
}
}
}
}
</style>