Browse Source

'修BUG'

main
hflllll 1 month ago
parent
commit
e4ca29e540
6 changed files with 1641 additions and 3 deletions
  1. +40
    -0
      api/modules/exhibit.js
  2. +12
    -0
      pages.json
  3. +30
    -2
      subPages/home/RAArecord.vue
  4. +809
    -0
      subPages/home/maintainChange.vue
  5. +749
    -0
      subPages/home/repairChange.vue
  6. +1
    -1
      utils/upload.js

+ 40
- 0
api/modules/exhibit.js View File

@ -11,6 +11,9 @@ export default {
})
},
// 查看维修记录列表
async queryRepairList(data) {
return http({
@ -91,5 +94,42 @@ export default {
})
},
// 查看维修记录详情
async queryRepairById(data) {
return http({
url: '/showpiece/queryRepairById',
method: 'GET',
data
})
},
// 保养- 查看保养记录详情
async queryMaintenanceById(data) {
return http({
url: '/showpiece/queryMaintenanceById',
method: 'GET',
data
})
},
// 修改保养记录
async updateMaintenance(data) {
return http({
url: '/showpiece/updateMaintenance',
method: 'POST',
data,
showLoading: true,
needToken: true
})
},
async updateRepair(data) {
return http({
url: '/showpiece/updateRepair',
method: 'POST',
data,
showLoading: true,
needToken: true
})
}
}

+ 12
- 0
pages.json View File

@ -56,6 +56,18 @@
"navigationBarTitleText": "报修/保养记录"
}
},
{
"path": "home/repairChange",
"style": {
"navigationBarTitleText": "报修修改"
}
},
{
"path": "home/maintainChange",
"style": {
"navigationBarTitleText": "保养修改"
}
},
{
"path": "repair/maintainSubmit",
"style": {


+ 30
- 2
subPages/home/RAArecord.vue View File

@ -236,6 +236,11 @@
</view>
</template>
<!-- 修改入口按钮 -->
<view class="edit-btn" @click="goToEdit(record, index)">
<text class="edit-text">修改</text>
</view>
<!-- 收起按钮 -->
<view class="collapse-btn" @click="toggleCollapse(index)">
<text class="collapse-text">{{ collapsedStates[index] ? '查看全部' : '收起' }}</text>
@ -357,9 +362,16 @@ export default {
toggleCollapse(index) {
this.$set(this.collapsedStates, index, !this.collapsedStates[index])
console.log('收起/展开费用详情', this.collapsedStates[index])
},
goToEdit(record) {
// tab
const pagePath = this.activeMainTab === 'repair' ? '/subPages/home/repairChange' : '/subPages/home/maintainChange'
uni.navigateTo({
url: `${pagePath}?id=${record.id}&showpieceId=${this.showpieceId}`
})
}
},
async onLoad(args){{
async onLoad(args){
this.showpieceId = args.id
const userRes = await this.$api.config.queryUserList()
@ -367,7 +379,7 @@ export default {
label: item.nickName,
id: item.id
}))]]
}}
}
}
</script>
@ -591,6 +603,22 @@ export default {
}
}
.edit-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0;
margin-top: 16rpx;
background-color: $primary-color;
border-radius: 8rpx;
.edit-text {
font-size: 28rpx;
color: #fff;
font-weight: 500;
}
}
.collapse-btn {
display: flex;
justify-content: center;


+ 809
- 0
subPages/home/maintainChange.vue View File

@ -0,0 +1,809 @@
<template>
<view class="maintenance-submit">
<!-- 保养基本信息 -->
<view class="maintenance-info">
<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="maintenanceName"
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: !maintenanceDate }">{{ maintenanceDate || '请选择' }}</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="stateFrontText"
placeholder="请填写保养前的设备内容"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 保养前图片 -->
<view class="image-upload">
<view v-for="(img, index) in stateFrontImage" :key="index" class="image-item">
<image :src="img" mode="aspectFill" @click="previewImage(img, stateFrontImage)"></image>
<view class="delete-btn" @click="deleteBeforeImage(index)">
<uv-icon name="close" size="12" color="#fff"></uv-icon>
</view>
</view>
<view class="upload-btn" @click="uploadBeforeImage">
<uv-icon name="camera" size="34" color="#C70019"></uv-icon>
</view>
</view>
<!-- 保养后状态 -->
<view class="form-item">
<text class="label">保养后状态</text>
</view>
<view class="textarea-container">
<uv-textarea
v-model="stateBackText"
placeholder="请填写保养后的设备内容"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 保养后图片 -->
<view class="image-upload">
<view v-for="(img, index) in stateBackImage" :key="index" class="image-item">
<image :src="img" mode="aspectFill" @click="previewImage(img, stateBackImage)"></image>
<view class="delete-btn" @click="deleteAfterImage(index)">
<uv-icon name="close" size="12" color="#fff"></uv-icon>
</view>
</view>
<view class="upload-btn" @click="uploadAfterImage">
<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: isExpend === '1' }"
@click="selectCost('1')"
>
<view class="radio-dot" :class="{ active: isExpend === '1' }"></view>
<text :class="{ active: isExpend === '1' }"></text>
</view>
<view
class="radio-item"
:class="{ active: isExpend === '0' }"
@click="selectCost('0')"
>
<view class="radio-dot" :class="{ active: isExpend === '0' }"></view>
<text :class="{ active: isExpend === '0' }"></text>
</view>
</view>
</view>
<!-- 产生费用 -->
<view class="form-item" v-if="isExpend === '1'">
<text class="label">产生费用</text>
<view class="input-area" >
<input
v-model="amount"
placeholder="请输入费用"
disabled
class="input-field"
/>
</view>
</view>
<!-- 费用详情表格 -->
<view class="cost-table" v-if="isExpend === '1'">
<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 costList" :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="金额"
type="digit"
border="none"
:custom-style="{ backgroundColor: 'transparent', fontSize: '28rpx' }"
></uv-input>
</view>
<view class="cell-action">
<view class="action-btn delete-btn" @click="removeCostItem(index)" v-if="costList.length > 1">
<uv-icon name="close" size="14" color="#fff"></uv-icon>
</view>
<view class="action-btn add-btn" @click="addCostItem" v-if="index === costList.length - 1">
<uv-icon name="plus" size="14" color="#fff"></uv-icon>
</view>
</view>
</view>
</view>
<!-- 附件信息 -->
<view class="form-item form-item-header">
<text class="label active">附件信息</text>
</view>
<!-- 保养备注 -->
<view class="form-item">
<text class="label">保养备注</text>
</view>
<view class="textarea-container">
<uv-textarea
v-model="remarkText"
placeholder="请填写备注"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
<!-- 附件图片 -->
<view class="image-upload">
<view v-for="(img, index) in remarkImage" :key="index" class="image-item">
<image :src="img" mode="aspectFill" @click="previewImage(img, remarkImage)"></image>
<view class="delete-btn" @click="deleteAttachment(index)">
<uv-icon name="close" size="12" color="#fff"></uv-icon>
</view>
</view>
<view class="upload-btn" @click="uploadAttachment">
<uv-icon name="camera" size="34" color="#C70019"></uv-icon>
</view>
</view>
<!-- 下次保养日期 -->
<view class="form-item" @click="showNextDatePicker">
<text class="label">下次保养日期</text>
<view class="select-area">
<text class="value" :class="{ placeholder: !nextMaintenanceDate }">{{ nextMaintenanceDate || '请选择' }}</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="remark"
placeholder="请填写备注"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
:custom-style="{ backgroundColor: '#f5f5f5' }"
></uv-textarea>
</view>
</view>
<!-- 提交按钮 -->
<view class="submit-container">
<uv-button
type="primary"
text="立即提交"
:disabled="submiting"
:custom-style="{ backgroundColor: '#C70019', borderRadius: '25px' }"
@click="submitMaintenance"
></uv-button>
</view>
<!-- 日期选择器 -->
<uv-datetime-picker
confirm-color="#C70019"
ref="datePicker"
mode="date"
v-model="timeValue"
@confirm="confirmDate"
></uv-datetime-picker>
<uv-datetime-picker
confirm-color="#C70019"
ref="nextDatePicker"
mode="date"
v-model="nextTimeValue"
@confirm="confirmNextDate"
></uv-datetime-picker>
</view>
</template>
<script>
export default {
data() {
return {
id: '',
timeValue: Number(new Date()),
nextTimeValue: Number(new Date()),
//
maintenanceName: '',
maintenanceDate: '',
stateFrontText: '',
stateFrontImage: [],
stateBackText: '',
stateBackImage: [],
isExpend: '0',
costList: [{ name: '', quantity: '', amount: '' }],
remarkText: '',
remarkImage: [],
nextMaintenanceDate: '',
remark: '',
showpieceId: '',
submiting: false
}
},
computed: {
//
amount() {
return this.costList.reduce((sum, item) => {
return sum + (Number(item.quantity) * parseFloat(item.amount || 0))
}, 0)
}
},
methods: {
//
showDatePicker() {
this.$refs.datePicker.open()
},
//
confirmDate(e) {
// uv-datetime-picker
this.maintenanceDate = this.$utils.formatTime(e.value)
},
//
showNextDatePicker() {
this.$refs.nextDatePicker.open()
},
//
confirmNextDate(e) {
// uv-datetime-picker
this.nextMaintenanceDate = this.$utils.formatTime(e.value)
},
//
selectCost(value) {
this.isExpend = value
if (value === '0') {
this.costList = [{ name: '', quantity: '', amount: '' }]
} else if (this.costList.length === 0) {
this.costList = [{ name: '', quantity: '', amount: '' }]
}
},
//
addCostItem() {
this.costList.push({ name: '', quantity: '', amount: '' })
},
//
removeCostItem(index) {
if (this.costList.length > 1) {
this.costList.splice(index, 1)
} else {
uni.showToast({ title: '至少保留一个费用项目', icon: 'none' })
}
},
//
async uploadBeforeImage() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
this.stateFrontImage.push(result.url)
}
} catch (error) {
console.error('图片上传失败:', error)
uni.showToast({
title: '图片上传失败',
icon: 'error'
})
}
},
//
deleteBeforeImage(index) {
this.stateFrontImage.splice(index, 1)
},
//
async uploadAfterImage() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
this.stateBackImage.push(result.url)
}
} catch (error) {
console.error('头像上传失败:', error)
uni.showToast({
title: '头像上传失败',
icon: 'error'
})
}
},
//
deleteAfterImage(index) {
this.stateBackImage.splice(index, 1)
},
//
async uploadAttachment() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
this.remarkImage.push(result.url)
}
} catch (error) {
console.error('头像上传失败:', error)
uni.showToast({
title: '头像上传失败',
icon: 'error'
})
}
},
//
deleteAttachment(index) {
this.remarkImage.splice(index, 1)
},
//
previewImage(url, imageList) {
uni.previewImage({
urls: imageList,
current: url
})
},
//
async submitMaintenance() {
//
if (!this.maintenanceName.trim()) {
uni.showToast({ title: '请填写保养人', icon: 'none' })
return
}
if (!this.stateFrontText.trim()) {
uni.showToast({ title: '请填写保养前状态', icon: 'none' })
return
}
if (!this.stateBackText.trim()) {
uni.showToast({ title: '请填写保养后状态', icon: 'none' })
return
}
if (this.isExpend === '1' && !this.amount){
uni.showToast({ title: '请填写消费', icon: 'none' })
return
}
// costList,,;
//
const formData = {
maintenanceName: this.maintenanceName,
maintenanceDate: this.maintenanceDate,
stateFrontText: this.stateFrontText,
stateFrontImage: this.stateFrontImage?.join(',') || '',
stateBackText: this.stateBackText,
stateBackImage: this.stateBackImage?.join(',') || '',
expenseList: this.costList.map(item => {
return `${item.name},${item.quantity},${item.amount}`
}).join(';'),
isExpend: this.isExpend,
amount: this.amount,
remarkText: this.remarkText,
remarkImage: this.remarkImage?.join(',') || '',
nextMaintenanceDate: this.nextMaintenanceDate,
remark: this.remark,
id: this.id
}
if (formData.isExpend === '0'){
delete formData.expenseList
}
this.submiting = true
try{
const subRes = await this.$api.exhibit.updateMaintenance(formData)
if(subRes.code == 200){
uni.showToast({ title: subRes.message, icon: 'success' })
//
setTimeout(() => {
uni.navigateBack()
}, 1000)
}else{
uni.showToast({ title: subRes.message, icon: 'none' })
}
}catch(err){
// uni.showToast({ title: err.message, icon: 'none' })
this.submiting = false
}
},
//
async queryMaintenanceById() {
try {
const res = await this.$api.exhibit.queryMaintenanceById({
maintenanceId: this.id
})
if (res.code == 200) {
const maintenanceInfo = res.result
//
this.maintenanceName = maintenanceInfo.maintenanceName || ''
this.maintenanceDate = maintenanceInfo.maintenanceDate || ''
this.stateFrontText = maintenanceInfo.stateFrontText || ''
//
this.stateFrontImage = maintenanceInfo.stateFrontImage ? maintenanceInfo.stateFrontImage.trim().split(',').filter(img => img.trim()) : []
this.stateBackText = maintenanceInfo.stateBackText || ''
this.stateBackImage = maintenanceInfo.stateBackImage ? maintenanceInfo.stateBackImage.trim().split(',').filter(img => img.trim()) : []
this.isExpend = maintenanceInfo.isExpend || '0'
this.remarkText = maintenanceInfo.remarkText || ''
this.remarkImage = maintenanceInfo.remarkImage ? maintenanceInfo.remarkImage.trim().split(',').filter(img => img.trim()) : []
this.nextMaintenanceDate = maintenanceInfo.nextMaintenanceDate || ''
this.remark = maintenanceInfo.remark || ''
//
if (maintenanceInfo.isExpend === '1' && maintenanceInfo.exhibitMaintenanceExpenses && maintenanceInfo.exhibitMaintenanceExpenses.length > 0){
this.costList = maintenanceInfo.exhibitMaintenanceExpenses.map(item => {
return {
name: item.title || '',
quantity: item.num || '',
amount: item.amount || ''
}
})
} else {
this.costList = [{ name: '', quantity: '', amount: '' }]
}
}
} catch (error) {
uni.showToast({ title: error.message, icon: 'none' })
}
}
},
onLoad(options) {
this.showpieceId = options.showpieceId
this.id = options.id
//
this.queryMaintenanceById()
},
}
</script>
<style lang="scss" scoped>
.maintenance-submit {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 200rpx;
}
.maintenance-info {
margin: 18rpx;
background: #ffffff;
border-radius: 15rpx;
box-shadow: 0rpx 3rpx 6rpx 0rpx rgba(0,0,0,0.16);
padding: 40rpx;
.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-header {
border-bottom: none;
margin-top: 20rpx;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 0;
border-bottom: 2rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.label {
font-size: 30rpx;
color: $primary-text-color;
flex-shrink: 0;
&.active {
font-weight: bold;
}
}
.value {
font-size: 30rpx;
color: $secondary-text-color;
&.placeholder {
color: $secondary-text-color;
}
}
.select-area {
display: flex;
align-items: center;
gap: 16rpx;
}
.input-area {
flex: 1;
// background-color: #f5f5f5;
border-radius: 8rpx;
padding: 16rpx 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;
}
}
}
.radio-options {
display: flex;
gap: 60rpx;
.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;
}
}
}
}
}
.textarea-container {
border-radius: 8rpx;
}
.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;
}
.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;
}
}
}
//
.cost-table {
margin-top: 24rpx;
border: 2rpx solid #f0f0f0;
border-radius: 8rpx;
overflow: hidden;
.table-header {
display: flex;
background-color: #f5f5f5;
padding: 16rpx 0;
.header-cell {
flex: 1;
text-align: center;
font-size: 28rpx;
font-weight: bold;
color: $primary-text-color;
&:first-child {
flex: 2;
}
&:last-child {
width: 120rpx;
flex: 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;
}
}
}
}
}
}
.submit-container {
position: fixed;
z-index: 999;
bottom: 0;
left: 0;
right: 0;
padding: 32rpx;
background-color: #fff;
border-top: 2rpx solid #f0f0f0;
}
</style>

+ 749
- 0
subPages/home/repairChange.vue View File

@ -0,0 +1,749 @@
<template>
<view class="repair-change">
<!-- 处理内容 -->
<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.repairName"
placeholder="请填写"
class="input-field"
ref="maintainerInput"
/>
</view>
</view>
<!-- 联系方式 -->
<view class="form-item">
<text class="label">联系方式</text>
<view class="input-area">
<input
v-model="processData.phone"
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.content"
placeholder="请输入处理内容"
:maxlength="200"
:show-confirm-bar="false"
height="60"
border="none"
: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 getImageList(processData.image)" :key="index" class="image-item">
<image :src="img.trim()" mode="aspectFill" @click="previewImage(img.trim())"></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.isExpend === '1' }"
@click="selectCost('1')"
>
<view class="radio-dot" :class="{ active: processData.isExpend === '1' }"></view>
<text :class="{ active: processData.isExpend === '1' }"></text>
</view>
<view
class="radio-item"
:class="{ active: processData.isExpend === '0' }"
@click="selectCost('0')"
>
<view class="radio-dot" :class="{ active: processData.isExpend === '0' }"></view>
<text :class="{ active: processData.isExpend === '0' }"></text>
</view>
</view>
</view>
<!-- 产生费用 -->
<view class="form-item" v-if="processData.isExpend === '1'">
<text class="label">产生费用</text>
<view class="input-area">
<input
v-model="amount"
placeholder="请输入费用"
disabled
class="input-field"
ref="maintainerInput"
/>
</view>
</view>
<!-- 费用详情表格 -->
<view class="cost-table" v-if="processData.isExpend === '1'">
<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.isFix === '1' }"
@click="selectResolved('1')"
>
<view class="radio-dot" :class="{ active: processData.isFix === '1' }"></view>
<text :class="{ active: processData.isFix === '1' }"></text>
</view>
<view
class="radio-item"
:class="{ active: processData.isFix === '0' }"
@click="selectResolved('0')"
>
<view class="radio-dot" :class="{ active: processData.isFix === '0' }"></view>
<text :class="{ active: processData.isFix === '0' }"></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-datetime-picker
confirm-color="#C70019"
ref="datePicker"
v-model="timeValue"
mode="date"
@confirm="confirmDate"
></uv-datetime-picker>
</view>
</template>
<script>
export default {
data() {
return {
timeValue: Number(new Date()),
id: '',
showpieceId: '',
//
processData: {
repairName: '',
phone: '',
repairDate: '',
content: '',
image: '',
isExpend: '0',
costDetails: [
{ name: '', quantity: '', amount: '' }
],
isFix: '0',
remark: ''
},
}
},
computed: {
//
amount() {
return this.processData.costDetails.reduce((sum, item) => {
return sum + (Number(item.quantity) * parseFloat(item.amount || 0))
}, 0)
}
},
onLoad(args) {
this.id = args.id
this.showpieceId = args.showpieceId
},
methods: {
//
getImageList(imageStr) {
if (!imageStr || typeof imageStr !== 'string') {
return []
}
return imageStr.split(',').filter(img => img && img.trim())
},
//
previewImage(img) {
uni.previewImage({
urls: [img],
current: img
})
},
//
showDatePicker() {
this.$refs.datePicker.open()
},
//
confirmDate(e) {
this.processData.repairDate = this.$utils.formatTime(e.value)
},
//
async uploadImage() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
// URL
if (this.processData.image) {
this.processData.image += ',' + result.url
} else {
this.processData.image = result.url
}
}
} catch (error) {
console.error('图片上传失败:', error)
uni.showToast({
title: '图片上传失败',
icon: 'error'
})
}
},
//
deleteImage(index) {
const imageList = this.getImageList(this.processData.image)
imageList.splice(index, 1)
this.processData.image = imageList.join(',')
},
//
selectCost(value) {
this.processData.isExpend = value
if (!value) {
this.processData.amount = 0
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.isFix = value
},
//
async startRepair() {
//
if (!this.processData.repairName) {
uni.showToast({
title: '请输入维修人',
icon: 'none'
})
return
}
if (!this.processData.phone) {
uni.showToast({
title: '请输入联系方式',
icon: 'none'
})
return
}
if (!this.processData.repairDate) {
uni.showToast({
title: '请选择维修日期',
icon: 'none'
})
return
}
if (!this.processData.content) {
uni.showToast({
title: '请输入处理内容',
icon: 'none'
})
return
}
if (this.processData.isFix === null) {
uni.showToast({
title: '请选择问题是否解决',
icon: 'none'
})
return
}
if (this.processData.isExpend === '1') {
// 0
if (!this.amount) {
uni.showToast({
title: '请添加费用明细',
icon: 'none'
})
return
}
}
const tempData = {...this.processData}
delete tempData.costDetails
const tempDataList = [...this.processData.costDetails]
if (this.processData.isExpend === '1') {
tempData.expenseList = tempDataList.map(item => `${item.name},${item.quantity},${item.amount}`).join(';')
} else {
delete tempData.expenseList
}
const submitRes = await this.$api.exhibit.updateRepair({
id: this.id,
...tempData,
amount: this.amount
})
if (submitRes.code === 200) {
uni.showToast({
title: submitRes.message,
icon: 'none'
})
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 400)
} else {
uni.showToast({
title: submitRes.message,
icon: 'none'
})
}
},
async getDetail(){
const res = await this.$api.exhibit.queryRepairById({ repairId: this.id })
if (res.code === 200) {
const detail = res.result
//
this.processData.repairName = detail.repairName || ''
this.processData.phone = detail.phone || ''
this.processData.repairDate = detail.repairDate || ''
this.processData.content = detail.content || ''
this.processData.remark = detail.remark || ''
//
this.processData.isFix = detail.isFix || '0'
this.processData.isExpend = detail.isExpend || '0'
//
if (detail.image && detail.image.trim()) {
//
this.processData.image = detail.image.trim().split(',').filter(img => img.trim()).map(img => img.trim()).join(',')
} else {
this.processData.image = ''
}
// amount
this.amount = detail.amount || 0
//
if (detail.exhibitRepairExpenseList && Array.isArray(detail.exhibitRepairExpenseList)) {
this.processData.costDetails = detail.exhibitRepairExpenseList.map(item => ({
name: item.title || '',
quantity: item.quantity || 1,
amount: item.amount || 0
}))
} else {
this.processData.costDetails = []
}
}
}
},
onLoad(args) {
this.id = args.id
this.getDetail()
},
}
</script>
<style lang="scss" scoped>
.repair-change {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 200rpx;
}
.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;
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;
}
}
.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>

+ 1
- 1
utils/upload.js View File

@ -98,7 +98,7 @@ const uploadImage = async (file) => {
console.error('OSS上传失败:', error)
return {
success: false,
error: error.message || 'OSS上传失败'
error: error.message || '上传失败'
}
}
}


Loading…
Cancel
Save