展品维保小程序前端代码接口
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1015 lines
26 KiB

<template>
<view class="maintain-submit">
<!-- 修改状态 -->
<view class="status-item">
<!-- 维修状态和状态选择 -->
<view class="status-row">
<text class="status-label">维修状态</text>
<view class="status-select" @click="showStatusPicker">
<text class="status-value" :class="{ placeholder: !selectedStatus }">{{ selectedStatus || '故障中' }}</text>
<uv-icon name="arrow-down" size="18" color="#000"></uv-icon>
</view>
</view>
<!-- 修改状态按钮 -->
<button class="status-btn" :class="{ active: statusType === 'modify' }" @click="statusType = 'modify'">
<text class="status-text">修改状态</text>
</button>
</view>
<!-- 保修基本信息 -->
<view class="repair-info">
<view class="info-header">
<view class="red-line"></view>
<text class="info-title">报修基本信息</text>
</view>
<!-- 报修日期 -->
<view class="form-item">
<text class="label">报修日期</text>
<text class="value">{{ repairData.repairDate }}</text>
</view>
<!-- 故障紧急程度 -->
<view class="form-item">
<text class="label">故障紧急程度</text>
<text class="value">{{ repairData.urgencyLevel }}</text>
</view>
<!-- 故障情况描述 -->
<view class="form-item">
<text class="label">故障情况描述</text>
</view>
<!-- 故障情况描述文本区域 -->
<view class="content-text">
<!-- <text>{{ repairData.faultDescription }}</text> -->
<uv-textarea
v-model="repairData.faultDescription"
placeholder="展品故障描述"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
disabled
:textStyle="{ color: '#000' }"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 故障图片 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">故障图片</text>
</view>
<view class="image-container" v-if="!isCollapsed">
<image
class="uploaded-image"
v-for="(img, index) in repairData.imageList"
:key="index"
:src="img"
mode="aspectFill"
@click="previewImage(img)"
></image>
</view>
<!-- 故障首次发生时间 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">故障首次发生时间</text>
<text class="value">{{ repairData.firstOccurTime }}</text>
</view>
<!-- 发生频率 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">发生频率</text>
<text class="value red-text">{{ repairData.frequency }}</text>
</view>
<!-- 故障发生频率的触发条件 -->
<view class="content-text" v-if="!isCollapsed">
<uv-textarea
v-model="repairData.faultCondition"
placeholder="故障发生频率的触发条件"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
disabled
:textStyle="{ color: '#000' }"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- <view class="content-text" v-if="!isCollapsed">
<text>{{ repairData.faultCondition }}</text>
</view> -->
<!-- 是否影响使用 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">是否影响使用</text>
<text class="value red-text">{{ repairData.impactUsage }}</text>
</view>
<!-- 是否采取临时措施 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">是否采取临时措施</text>
<text class="value red-text">{{ repairData.hasTakenMeasures ? '是' : '否' }}</text>
</view>
<!-- 如有采取临时措施请填写措施说明 -->
<view class="content-text" v-if="!isCollapsed">
<!-- <text>{{ repairData.faultDescription }}</text> -->
<uv-textarea
v-model="repairData.temporaryMeasures"
placeholder="如果有采取临时措施请填写措施说明"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
disabled
:textStyle="{ color: '#000' }"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- <view class="content-text" v-if="!isCollapsed && repairData.hasTakenMeasures">
<text>{{ repairData.temporaryMeasures }}</text>
</view> -->
<!-- 是否影响体验 -->
<view class="form-item" v-if="!isCollapsed">
<text class="label">是否影响体验</text>
<text class="value red-text">{{ repairData.impactExperience ? '是' : '否' }}</text>
</view>
<view class="form-item" v-if="!isCollapsed">
<text class="label">备注</text>
</view>
<!-- 备注 -->
<view class="content-text" v-if="!isCollapsed">
<!-- <text>{{ repairData.faultDescription }}</text> -->
<uv-textarea
v-model="repairData.remark"
placeholder="备注"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
disabled
:textStyle="{ color: '#000' }"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 查看全部/收起按钮 -->
<view class="collapse-btn" @click="toggleCollapse">
<text class="collapse-text">{{ isCollapsed ? '查看全部' : '收起' }}</text>
<text class="collapse-icon">{{ isCollapsed ? '▼' : '▲' }}</text>
</view>
</view>
<!-- 处理内容 -->
<view class="process-content">
<view class="info-header">
<view class="red-line"></view>
<text class="info-title">处理内容</text>
</view>
<!-- 维修人 -->
<view class="form-item">
<text class="label">维修人</text>
<view class="input-area" >
<input
v-model="processData.repairPerson"
placeholder="请填写"
class="input-field"
ref="maintainerInput"
/>
</view>
</view>
<!-- 联系方式 -->
<view class="form-item">
<text class="label">联系方式</text>
<view class="input-area" >
<input
v-model="processData.contactPhone"
placeholder="请填写"
class="input-field"
ref="maintainerInput"
/>
</view>
</view>
<!-- 维修日期 -->
<view class="form-item" @click="showDatePicker">
<text class="label">维修日期</text>
<view class="select-area">
<text class="value" :class="{ placeholder: !processData.repairDate }">{{ processData.repairDate || '请选择' }}</text>
<uv-icon name="arrow-down" size="18" color="#000"></uv-icon>
</view>
</view>
<!-- 处理内容 -->
<view class="form-item">
<text class="label">处理内容</text>
</view>
<view class="textarea-container">
<uv-textarea
v-model="processData.processContent"
placeholder="请输入处理内容"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
disabled
:textStyle="{ color: '#000' }"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 上传图片 -->
<view class="form-item">
<text class="label">上传图片</text>
</view>
<view class="image-upload">
<view v-for="(img, index) in processData.imageList" :key="index" class="image-item">
<image :src="img" mode="aspectFill" @click="previewImage(img)"></image>
<view class="delete-btn" @click="deleteImage(index)">
<uv-icon name="close" size="12" color="#fff"></uv-icon>
</view>
</view>
<view class="upload-btn" @click="uploadImage">
<uv-icon name="camera" size="34" color="#C70019"></uv-icon>
</view>
</view>
<!-- 是否产生费用 -->
<view class="form-item">
<text class="label">是否产生费用</text>
<view class="radio-options">
<view
class="radio-item"
:class="{ active: processData.hasCost === true }"
@click="selectCost(true)"
>
<view class="radio-dot" :class="{ active: processData.hasCost === true }"></view>
<text :class="{ active: processData.hasCost === true }">是</text>
</view>
<view
class="radio-item"
:class="{ active: processData.hasCost === false }"
@click="selectCost(false)"
>
<view class="radio-dot" :class="{ active: processData.hasCost === false }"></view>
<text :class="{ active: processData.hasCost === false }">否</text>
</view>
</view>
</view>
<!-- 产生费用 -->
<view class="form-item" v-if="processData.hasCost">
<text class="label">产生费用</text>
<view class="input-area" >
<input
v-model="processData.totalCost"
placeholder="请输入费用"
class="input-field"
ref="maintainerInput"
/>
</view>
</view>
<!-- 费用详情表格 -->
<view class="cost-table" v-if="processData.hasCost">
<view class="table-header">
<text class="header-cell">费用名称</text>
<text class="header-cell">数量</text>
<text class="header-cell">金额</text>
<text class="header-cell"></text>
</view>
<view class="table-row" v-for="(item, index) in processData.costDetails" :key="index">
<view class="cell-input">
<uv-input
v-model="item.name"
placeholder="费用名称"
border="none"
:custom-style="{ backgroundColor: 'transparent', fontSize: '28rpx' }"
></uv-input>
</view>
<view class="cell-input">
<uv-input
v-model="item.quantity"
placeholder="数量"
border="none"
:custom-style="{ backgroundColor: 'transparent', fontSize: '28rpx' }"
></uv-input>
</view>
<view class="cell-input">
<uv-input
v-model="item.amount"
placeholder="金额"
border="none"
:custom-style="{ backgroundColor: 'transparent', fontSize: '28rpx' }"
></uv-input>
</view>
<view class="cell-action">
<view class="action-btn delete-btn" @click="deleteCostItem(index)" v-if="processData.costDetails.length > 1">
<uv-icon name="close" size="14" color="#fff"></uv-icon>
</view>
<view class="action-btn add-btn" @click="addCostItem" v-if="index === processData.costDetails.length - 1">
<uv-icon name="plus" size="14" color="#fff"></uv-icon>
</view>
</view>
</view>
</view>
<!-- 问题是否解决 -->
<view class="form-item">
<text class="label">问题是否解决</text>
<view class="radio-options">
<view
class="radio-item"
:class="{ active: processData.isResolved === true }"
@click="selectResolved(true)"
>
<view class="radio-dot" :class="{ active: processData.isResolved === true }"></view>
<text :class="{ active: processData.isResolved === true }">是</text>
</view>
<view
class="radio-item"
:class="{ active: processData.isResolved === false }"
@click="selectResolved(false)"
>
<view class="radio-dot" :class="{ active: processData.isResolved === false }"></view>
<text :class="{ active: processData.isResolved === false }">否</text>
</view>
</view>
</view>
<!-- 备注 -->
<view class="form-item">
<text class="label">备注</text>
</view>
<view class="textarea-container">
<uv-textarea
v-model="processData.remark"
placeholder="备注"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 立即维修按钮 -->
<view class="repair-btn-container">
<uv-button
type="primary"
text="立即维修"
:custom-style="{ backgroundColor: '#C70019', borderRadius: '25px' }"
@click="startRepair"
></uv-button>
</view>
</view>
<!-- 日期选择器 -->
<uv-picker
confirm-color="#C70019"
ref="datePicker"
:columns="dateColumns"
@confirm="confirmDate"
@cancel="cancelDate"
></uv-picker>
</view>
</template>
<script>
export default {
data() {
return {
statusType: 'modify',
selectedStatus: '故障中',
isCollapsed: true,
// 报修基本信息(只读)
repairData: {
repairDate: '2025/03/31',
urgencyLevel: '一般',
faultDescription: '展品故障情况',
imageList: [
'/static/商城_商品2.png',
'/static/商城_商品2.png'
],
firstOccurTime: '2025/03/31',
frequency: '持续性问题',
faultCondition: '故障发生频率的触发条件说明',
impactUsage: '是',
hasTakenMeasures: true,
temporaryMeasures: '已采取临时措施说明',
impactExperience: true,
remark: '备注信息'
},
// 处理内容(可编辑)
processData: {
repairPerson: '',
contactPhone: '',
repairDate: '',
processContent: '',
imageList: [],
hasCost: null,
totalCost: '',
costDetails: [
{ name: '', quantity: '', amount: '' }
],
isResolved: null,
remark: ''
},
// 日期选择器数据
dateColumns: []
}
},
onLoad() {
this.initDatePicker()
},
methods: {
// 切换折叠状态
toggleCollapse() {
this.isCollapsed = !this.isCollapsed
},
// 预览图片
previewImage(img) {
uni.previewImage({
urls: [img],
current: img
})
},
// 显示日期选择器
showDatePicker() {
this.$refs.datePicker.open()
},
// 确认日期
confirmDate(e) {
const { indexs, value } = e
this.processData.repairDate = value.join('/')
},
// 取消日期选择
cancelDate() {
// 取消选择
},
// 初始化日期选择器
initDatePicker() {
const currentYear = new Date().getFullYear()
const years = []
const months = []
const days = []
// 生成年份
for (let i = currentYear - 10; i <= currentYear + 10; i++) {
years.push(i + '年')
}
// 生成月份
for (let i = 1; i <= 12; i++) {
months.push(i.toString().padStart(2, '0') + '月')
}
// 生成日期
for (let i = 1; i <= 31; i++) {
days.push(i.toString().padStart(2, '0') + '日')
}
this.dateColumns = [years, months, days]
},
// 上传图片
async uploadImage() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
this.processData.imageList.push(result.url)
}
} catch (error) {
console.error('图片上传失败:', error)
uni.showToast({
title: '图片上传失败',
icon: 'error'
})
}
},
// 删除图片
deleteImage(index) {
this.processData.imageList.splice(index, 1)
},
// 选择是否产生费用
selectCost(value) {
this.processData.hasCost = value
if (!value) {
this.processData.totalCost = ''
this.processData.costDetails = [{ name: '', quantity: '', amount: '' }]
}
},
// 添加费用项
addCostItem() {
this.processData.costDetails.push({ name: '', quantity: '', amount: '' })
},
// 删除费用项
deleteCostItem(index) {
this.processData.costDetails.splice(index, 1)
},
// 选择问题是否解决
selectResolved(value) {
this.processData.isResolved = value
},
// 显示状态选择器
showStatusPicker() {
uni.showActionSheet({
itemList: ['故障中', '维修中', '已完成', '暂停维修'],
success: (res) => {
const statusList = ['故障中', '维修中', '已完成', '暂停维修']
this.selectedStatus = statusList[res.tapIndex]
}
})
},
// 立即维修
startRepair() {
// 验证必填项
if (!this.processData.repairPerson) {
uni.showToast({
title: '请输入维修人',
icon: 'none'
})
return
}
if (!this.processData.contactPhone) {
uni.showToast({
title: '请输入联系方式',
icon: 'none'
})
return
}
if (!this.processData.repairDate) {
uni.showToast({
title: '请选择维修日期',
icon: 'none'
})
return
}
if (!this.processData.processContent) {
uni.showToast({
title: '请输入处理内容',
icon: 'none'
})
return
}
if (this.processData.isResolved === null) {
uni.showToast({
title: '请选择问题是否解决',
icon: 'none'
})
return
}
// 提交数据
uni.showLoading({
title: '提交中...'
})
// 模拟提交
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '提交成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}, 2000)
}
}
}
</script>
<style lang="scss" scoped>
.maintain-submit {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 200rpx;
}
// 修改状态
.status-item {
margin: 18rpx;
background: #ffffff;
border-radius: 15rpx;
box-shadow: 0rpx 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
padding: 20rpx 40rpx;
.status-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
.status-label {
font-size: 30rpx;
color: $primary-text-color;
// font-weight: bold;
}
.status-select {
display: flex;
align-items: center;
gap: 23rpx;
padding: 12rpx 24rpx;
// background-color: #f5f5f5;
border-radius: 8rpx;
// min-width: 200rpx;
justify-content: space-between;
.status-value {
font-size: 28rpx;
color: $primary-text-color;
&.placeholder {
color: $secondary-text-color;
}
}
}
}
.status-btn {
display: flex;
align-items: center;
justify-content: center;
// padding: 16rpx 32rpx;
// border: 2rpx solid #ddd;
border-radius: full;
background-color: #fff;
width: 419rpx;
height: 78rpx;
border-radius: 39rpx;
&.active {
border-color: $primary-color;
background-color: $primary-color;
.status-text {
color: #fff;
}
}
.status-text {
font-size: 30rpx;
color: $secondary-text-color;
}
}
}
// 报修基本信息和处理内容通用样式
.repair-info, .process-content {
margin: 18rpx;
background: #ffffff;
border-radius: 15rpx;
box-shadow: 0rpx 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
padding: 30rpx;
.info-header {
display: flex;
align-items: center;
margin-bottom: 40rpx;
.red-line {
width: 9rpx;
height: 33rpx;
background-color: $primary-color;
margin-right: 7rpx;
border-radius: 5rpx;
}
.info-title {
font-size: 30rpx;
font-weight: bold;
color: $primary-text-color;
}
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 0;
border-bottom: 2rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.input-area {
flex: 1;
// background-color: #f5f5f5;
border-radius: 8rpx;
padding: 2rpx 24rpx;
margin-left: 24rpx;
.input-field {
width: 100%;
font-size: 30rpx;
color: $primary-text-color;
background: transparent;
border: none;
outline: none;
text-align: right;
&::placeholder {
color: $secondary-text-color;
}
}
}
.label {
font-size: 30rpx;
color: $primary-text-color;
flex-shrink: 0;
&.active {
font-weight: bold;
}
}
.value {
font-size: 30rpx;
color: $primary-text-color;
&.placeholder {
color: $secondary-text-color;
}
&.red-text {
color: $primary-color;
}
}
.select-area {
display: flex;
align-items: center;
gap: 16rpx;
}
}
.content-text {
// padding: 16rpx 0;
margin-bottom: 16rpx;
text {
font-size: 30rpx;
color: $secondary-text-color;
line-height: 1.5;
}
}
.image-container {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
margin: 16rpx 0;
.uploaded-image {
width: 160rpx;
height: 160rpx;
// border-radius: 8rpx;
}
}
.collapse-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
padding: 30rpx 0 0;
margin-top: 16rpx;
border-top: 2rpx solid #f0f0f0;
.collapse-text {
font-size: 28rpx;
color: $primary-color;
}
.collapse-icon {
font-size: 24rpx;
color: $primary-color;
}
}
}
// 处理内容特有样式
.process-content {
.textarea-container {
margin: 16rpx 0;
background-color: #f5f5f5;
border-radius: 8rpx;
padding: 8rpx 16rpx;
}
.image-upload {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
margin: 16rpx 0;
.upload-btn {
width: 160rpx;
height: 160rpx;
border: 2rpx dashed $primary-color;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 8rpx;
}
.image-item {
position: relative;
width: 160rpx;
height: 160rpx;
image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.delete-btn {
position: absolute;
top: -12rpx;
right: -12rpx;
width: 40rpx;
height: 40rpx;
background-color: #ff4757;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
}
}
.radio-options {
display: flex;
gap: 80rpx;
.radio-item {
display: flex;
align-items: center;
gap: 16rpx;
.radio-dot {
width: 32rpx;
height: 32rpx;
border: 4rpx solid #ddd;
border-radius: 50%;
position: relative;
&.active {
border-color: $primary-color;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16rpx;
height: 16rpx;
background-color: $primary-color;
border-radius: 50%;
}
}
}
text {
font-size: 30rpx;
color: $secondary-text-color;
&.active {
color: $primary-color;
}
}
}
}
// 费用表格样式
.cost-table {
margin: 16rpx 0;
border: 2rpx solid #f0f0f0;
border-radius: 8rpx;
overflow: hidden;
.table-header {
display: flex;
background-color: #f8f8f8;
.header-cell {
flex: 1;
padding: 24rpx 16rpx;
font-size: 28rpx;
font-weight: bold;
color: $primary-text-color;
text-align: left;
border-right: 2rpx solid #f0f0f0;
&:first-child {
flex: 2;
text-align: left;
}
&:last-child {
border-right: none;
}
}
}
.table-row {
display: flex;
border-top: 2rpx solid #f0f0f0;
.cell-input {
flex: 1;
padding: 8rpx;
border-right: 2rpx solid #f0f0f0;
&:first-child {
flex: 2;
}
&:last-child {
border-right: none;
}
}
.cell-action {
width: 120rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 8rpx;
.action-btn {
width: 36rpx;
height: 36rpx;
padding: 4rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
&.add-btn {
background-color: #1a9c10;
}
&.delete-btn {
background-color: $primary-color;
}
}
}
}
}
// 立即维修按钮
.repair-btn-container {
margin-top: 40rpx;
padding-top: 32rpx;
border-top: 2rpx solid #f0f0f0;
}
}
</style>