463 lines
10 KiB
Vue
463 lines
10 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="content">
|
||
<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" @click="showSelect = true">
|
||
<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="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: '',
|
||
showSelect: false,
|
||
categoryList: [],
|
||
workOrderLocation: '',
|
||
expectedTime: '',
|
||
uploadedImages: [],
|
||
uploadedVideos: [],
|
||
videoFormats: ['mp4', 'mov', 'avi', 'wmv', 'mpeg', '3gp', 'flv', 'mkv']
|
||
};
|
||
},
|
||
mounted() {
|
||
this.getOrderList();
|
||
},
|
||
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,
|
||
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 = [];
|
||
},
|
||
|
||
handleConfirm(e) {
|
||
this.workOrderCategory = e.value[0].label;
|
||
this.workOrderCategoryID = e.value[0].id;
|
||
this.showSelect = 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'
|
||
});
|
||
}
|
||
},
|
||
}
|
||
};
|
||
</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> |