odf_new/odf-uniapp/pages/fault-add/index.vue
2026-03-14 14:34:48 +08:00

501 lines
11 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="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>