501 lines
11 KiB
Vue
501 lines
11 KiB
Vue
<template>
|
||
<view class="fault-add-page">
|
||
<image class="bg-image" src="/static/images/home_bg.png" mode="aspectFill" />
|
||
|
||
<view class="content">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="nav-bar-inner">
|
||
<image
|
||
class="nav-icon"
|
||
src="/static/images/ic_back.png"
|
||
mode="aspectFit"
|
||
@click="goBack"
|
||
/>
|
||
<text class="nav-title">新增故障</text>
|
||
<view class="nav-icon-placeholder" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 拍照区域 -->
|
||
<view class="photo-area">
|
||
<scroll-view class="photo-scroll" scroll-x>
|
||
<view class="photo-list">
|
||
<view class="photo-add-btn" @click="takePhoto">
|
||
<text class="plus-icon">+</text>
|
||
<text class="add-text">点击拍摄</text>
|
||
</view>
|
||
<image
|
||
class="photo-thumb"
|
||
v-for="(photo, index) in photoList"
|
||
:key="index"
|
||
:src="photo"
|
||
mode="aspectFill"
|
||
/>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 表单区域 -->
|
||
<view class="form-area">
|
||
<view class="form-group">
|
||
<text class="form-label">故障时间</text>
|
||
<view class="form-display">
|
||
<text class="display-text">{{ form.faultTime || '拍摄第一张照片后自动填充' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">人员</text>
|
||
<input
|
||
class="form-input"
|
||
v-model="form.personnel"
|
||
placeholder="请输入"
|
||
placeholder-class="input-placeholder"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">故障原因</text>
|
||
<input
|
||
class="form-input"
|
||
v-model="form.faultReason"
|
||
placeholder="请输入"
|
||
placeholder-class="input-placeholder"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">表显故障里程</text>
|
||
<input
|
||
class="form-input"
|
||
v-model="form.mileage"
|
||
placeholder="请输入"
|
||
placeholder-class="input-placeholder"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">所属光缆</text>
|
||
<view class="form-display">
|
||
<text class="display-text">{{ form.cableName }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">地点</text>
|
||
<view class="location-btn" @click="getLocation">
|
||
<text class="location-btn-text">点击获取当前经纬度</text>
|
||
</view>
|
||
<text class="location-text">当前经度:{{ form.longitude }} 当前纬度:{{ form.latitude }}</text>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">备注</text>
|
||
<textarea
|
||
class="form-textarea"
|
||
v-model="form.remark"
|
||
placeholder="请输入"
|
||
placeholder-class="input-placeholder"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部固定提交按钮 -->
|
||
<view class="bottom-bar">
|
||
<view class="submit-btn" :class="{ 'submit-btn-disabled': submitting }" @click.stop="handleSubmit">
|
||
<text class="submit-btn-text">{{ submitting ? '提交中...' : '提交故障' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { addFault } from '@/services/trunk'
|
||
import { addWatermark } from '@/utils/watermark'
|
||
|
||
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
|
||
const photoList = ref([])
|
||
const cableId = ref('')
|
||
const submitting = ref(false)
|
||
|
||
const form = reactive({
|
||
faultTime: '',
|
||
personnel: '',
|
||
faultReason: '',
|
||
mileage: '',
|
||
cableName: '',
|
||
latitude: 0,
|
||
longitude: 0,
|
||
remark: ''
|
||
})
|
||
|
||
function goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
function takePhoto() {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sourceType: ['camera'],
|
||
success(res) {
|
||
const tempPath = res.tempFilePaths[0]
|
||
photoList.value.push(tempPath)
|
||
// 第一张照片自动填充故障时间
|
||
if (photoList.value.length === 1) {
|
||
const now = new Date()
|
||
const y = now.getFullYear()
|
||
const m = String(now.getMonth() + 1).padStart(2, '0')
|
||
const d = String(now.getDate()).padStart(2, '0')
|
||
const h = String(now.getHours()).padStart(2, '0')
|
||
const min = String(now.getMinutes()).padStart(2, '0')
|
||
form.faultTime = `${y}/${m}/${d} ${h}:${min}`
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
function getLocation() {
|
||
// #ifdef H5
|
||
if (navigator.geolocation) {
|
||
navigator.geolocation.getCurrentPosition(
|
||
(pos) => {
|
||
form.latitude = pos.coords.latitude
|
||
form.longitude = pos.coords.longitude
|
||
uni.showToast({ title: '获取成功', icon: 'success' })
|
||
},
|
||
() => {
|
||
uni.showToast({ title: '获取位置失败,请检查浏览器定位权限', icon: 'none' })
|
||
},
|
||
{ enableHighAccuracy: true, timeout: 10000 }
|
||
)
|
||
} else {
|
||
uni.showToast({ title: '当前浏览器不支持定位', icon: 'none' })
|
||
}
|
||
// #endif
|
||
// #ifndef H5
|
||
doGetLocation()
|
||
// #endif
|
||
}
|
||
|
||
// #ifndef H5
|
||
function doGetLocation() {
|
||
uni.showLoading({ title: '定位中...', mask: true })
|
||
uni.getLocation({
|
||
type: 'gcj02',
|
||
isHighAccuracy: true,
|
||
highAccuracyExpireTime: 10000,
|
||
success(res) {
|
||
form.latitude = res.latitude
|
||
form.longitude = res.longitude
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '获取成功', icon: 'success' })
|
||
},
|
||
fail(err) {
|
||
uni.hideLoading()
|
||
const errMsg = (err.errMsg || '').toLowerCase()
|
||
if (errMsg.includes('deny') || errMsg.includes('auth') || errMsg.includes('permission')) {
|
||
uni.showModal({
|
||
title: '定位权限未开启',
|
||
content: '请在系统设置中允许本应用使用定位服务',
|
||
confirmText: '去设置',
|
||
success(modalRes) {
|
||
if (modalRes.confirm) {
|
||
uni.openSetting && uni.openSetting()
|
||
}
|
||
}
|
||
})
|
||
} else {
|
||
uni.showToast({ title: '获取位置失败,请检查GPS是否开启', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
// #endif
|
||
|
||
async function handleSubmit() {
|
||
if (photoList.value.length === 0) {
|
||
uni.showToast({ title: '请至少拍摄一张照片', icon: 'none' })
|
||
return
|
||
}
|
||
if (!cableId.value) {
|
||
uni.showToast({ title: '所属光缆信息缺失,无法提交', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (submitting.value) return
|
||
submitting.value = true
|
||
uni.showLoading({ title: '提交中...', mask: true })
|
||
|
||
try {
|
||
// 水印处理
|
||
const watermarkText = `${form.faultTime} ${form.personnel}`
|
||
const watermarkedPhotos = []
|
||
for (const photo of photoList.value) {
|
||
try {
|
||
const result = await addWatermark(photo, watermarkText)
|
||
watermarkedPhotos.push(result)
|
||
} catch (err) {
|
||
// 水印失败则使用原图
|
||
watermarkedPhotos.push(photo)
|
||
}
|
||
}
|
||
|
||
// 构建上传数据
|
||
const files = watermarkedPhotos.map((path, index) => ({
|
||
name: 'images',
|
||
uri: path
|
||
}))
|
||
|
||
const formData = {
|
||
files,
|
||
data: {
|
||
cableId: cableId.value,
|
||
faultTime: form.faultTime,
|
||
personnel: form.personnel,
|
||
faultReason: form.faultReason,
|
||
mileage: form.mileage,
|
||
latitude: String(form.latitude),
|
||
longitude: String(form.longitude),
|
||
remark: form.remark
|
||
}
|
||
}
|
||
|
||
const res = await addFault(formData)
|
||
if (res.code === 200) {
|
||
uni.showToast({ title: '提交成功', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
} else {
|
||
uni.showToast({ title: res.msg || '提交失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
uni.showToast({ title: '网络异常,请重试', icon: 'none' })
|
||
} finally {
|
||
uni.hideLoading()
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
onLoad((options) => {
|
||
if (options.cableId) {
|
||
cableId.value = options.cableId
|
||
}
|
||
if (options.cableName) {
|
||
form.cableName = decodeURIComponent(options.cableName)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.fault-add-page {
|
||
position: relative;
|
||
min-height: 100vh;
|
||
background-color: #F5F5F5;
|
||
padding-bottom: 120rpx;
|
||
}
|
||
|
||
.bg-image {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 500rpx;
|
||
z-index: 0;
|
||
}
|
||
|
||
.content {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.nav-bar {
|
||
width: 100%;
|
||
}
|
||
|
||
.nav-bar-inner {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.nav-icon {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
}
|
||
|
||
.nav-icon-placeholder {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
.photo-area {
|
||
padding: 24rpx;
|
||
}
|
||
|
||
.photo-scroll {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.photo-list {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.photo-add-btn {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
background: #fff;
|
||
border: 2rpx dashed #CCCCCC;
|
||
border-radius: 12rpx;
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.plus-icon {
|
||
font-size: 48rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.add-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.photo-thumb {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
border-radius: 12rpx;
|
||
margin-left: 16rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.form-area {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
font-weight: 500;
|
||
display: block;
|
||
}
|
||
|
||
.form-input {
|
||
height: 80rpx;
|
||
padding: 0 24rpx;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
border: 1rpx solid #E8E8E8;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.form-display {
|
||
height: 80rpx;
|
||
padding: 0 24rpx;
|
||
background: #F5F5F5;
|
||
border-radius: 12rpx;
|
||
border: 1rpx solid #E8E8E8;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.display-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.form-textarea {
|
||
min-height: 200rpx;
|
||
padding: 24rpx;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
border: 1rpx solid #E8E8E8;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
z-index: 0;
|
||
}
|
||
|
||
.input-placeholder {
|
||
color: #999;
|
||
}
|
||
|
||
.location-btn {
|
||
background: #1A73EC;
|
||
border-radius: 12rpx;
|
||
padding: 16rpx 0;
|
||
text-align: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.location-btn-text {
|
||
color: #fff;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.location-text {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
margin-top: 12rpx;
|
||
display: block;
|
||
}
|
||
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
padding: 24rpx;
|
||
background: #fff;
|
||
box-sizing: border-box;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: #1A73EC;
|
||
border-radius: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.submit-btn-text {
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.submit-btn-disabled {
|
||
background: #93bdf5;
|
||
}
|
||
</style>
|