吉安感知网项目-前端
chenyao
2026-08-04 35fb4a40571d69b85caede7348101d71789b5fb3
uniapps/work-app/src/subPackages/workDetail/HandleWorking/index-copy.vue
@@ -1,58 +1,55 @@
<template>
   <view class="handle-page">
      <u-navbar title="待办处置" :is-back="true" back-text="" :back-icon-size="40" @left-click="onBackClick"></u-navbar>
  <view class="handle-page">
    <u-navbar title="待办处置" :is-back="true" back-text="" :back-icon-size="40" @left-click="onBackClick"></u-navbar>
      <scroll-view class="form-scroll" scroll-y>
         <view class="form-section description-section">
            <view class="section-header">
               <text class="section-title">处置描述</text>
            </view>
            <view class="description-wrapper">
    <scroll-view class="form-scroll" scroll-y>
      <view class="form-section description-section">
        <view class="section-header">
          <text class="section-title">处置描述</text>
        </view>
        <view class="description-wrapper">
               <textarea
                  v-model="formData.disposeContent"
                  class="description-input"
                  maxlength="140"
                  placeholder="请输入"
                  placeholder-class="description-placeholder"
                  :show-confirm-bar="false"
               />
               <text class="word-count">{{ descriptionLength }}/140</text>
            </view>
         </view>
            v-model="formData.disposeContent"
            class="description-input"
            maxlength="140"
            placeholder="请输入"
            placeholder-class="description-placeholder"
            :show-confirm-bar="false"
          />
          <text class="word-count">{{ descriptionLength }}/140</text>
        </view>
      </view>
         <!--         <view class="form-section status-section">-->
         <!--            <text class="section-title">处置中</text>-->
         <!--            <switch class="status-switch" :checked="formData.isHandling" color="#00b578" @change="handleStatusChange" />-->
         <!--         </view>-->
      <!--         <view class="form-section status-section">-->
      <!--            <text class="section-title">处置中</text>-->
      <!--            <switch class="status-switch" :checked="formData.isHandling" color="#00b578" @change="handleStatusChange" />-->
      <!--         </view>-->
         <view class="form-section upload-section">
            <view class="section-header upload-header">
               <text class="section-title">附件上传</text>
            </view>
            <!--            <button class="select-file-btn" @click="handleChooseFile">选择文件</button>-->
            <u-button color="#1D6FE9" class="select-file-btn" :disabled="uploading" @click="handleChooseFile">
               {{ uploading ? '上传中...' : '选择文件' }}
            </u-button>
      <view class="form-section upload-section">
        <view class="section-header upload-header">
          <text class="section-title">附件上传</text>
        </view>
        <!--            <button class="select-file-btn" @click="handleChooseFile">选择文件</button>-->
        <u-button color="#1D6FE9" class="select-file-btn" :disabled="uploading" @click="handleChooseFile">
          {{ uploading ? '上传中...' : '选择文件' }}
        </u-button>
            <view v-if="attachmentList.length" class="attachment-list">
               <view v-for="(file, index) in attachmentList" :key="`${file.path}-${index}`" class="attachment-item">
                  <view class="file-info">
                     <text class="file-name">{{ file.name }}</text>
                     <text v-if="file.size" class="file-size">{{ formatFileSize(file.size) }}</text>
                  </view>
                  <text class="remove-file-btn" @click.stop="handleRemoveFile(index)">×</text>
               </view>
            </view>
         </view>
      </scroll-view>
        <view v-if="attachmentList.length" class="attachment-list">
          <view v-for="(file, index) in attachmentList" :key="`${file.path}-${index}`" class="attachment-item">
            <image class="attachment-image" :src="file.path" mode="aspectFill" @click="handlePreviewImage(index)" />
            <text class="remove-file-btn" @click.stop="handleRemoveFile(index)">×</text>
          </view>
        </view>
      </view>
    </scroll-view>
      <view class="action-footer">
         <u-button color="#AEAEAE" class="custom-style" @click="handleCancel">取消</u-button>
         <u-button color="#1D6FE9" class="custom-style" :disabled="submitting || uploading" @click="handleSubmit">
            {{ submitting ? '提交中...' : '确定' }}
         </u-button>
      </view>
   </view>
    <view class="action-footer">
      <u-button color="#AEAEAE" class="custom-style" @click="handleCancel">取消</u-button>
      <u-button color="#1D6FE9" class="custom-style" :disabled="submitting || uploading" @click="handleSubmit">
        {{ submitting ? '提交中...' : '确定' }}
      </u-button>
    </view>
  </view>
</template>
<script setup>
@@ -67,13 +64,13 @@
const uploading = ref(false)
const attachmentList = ref([])
const formData = ref({
   disposeContent: '',
   isHandling: true,
  disposeContent: '',
  isHandling: true,
})
const navbarTitleStyle = {
   color: '#ffffff',
   fontSize: '34rpx',
   fontWeight: '500',
  color: '#ffffff',
  fontSize: '34rpx',
  fontWeight: '500',
}
// 计算处置描述字数
@@ -81,12 +78,12 @@
// 读取当前工单标识
onLoad(options => {
   eventId.value = options?.id ?? ''
  eventId.value = options?.id ?? ''
})
// 返回上一页
function handleBack() {
   uni.navigateBack()
  uni.navigateBack()
}
// 切换处置状态
@@ -96,375 +93,372 @@
// 提取文件名称
function getFileName(filePath = '') {
   const normalizedPath = filePath.replace(/\\/g, '/')
   return decodeURIComponent(normalizedPath.split('/').pop() || '未命名文件')
  const normalizedPath = filePath.replace(/\\/g, '/')
  return decodeURIComponent(normalizedPath.split('/').pop() || '未命名文件')
}
// 上传单个已选择文件
async function uploadSelectedFile(file) {
   const response = await uploadFileAPI(file.path)
   const link = response?.data?.link || response?.data?.url || response?.link
   if (!link) throw new Error('上传接口未返回文件地址')
   return { ...file, link }
  const response = await uploadFileAPI(file.path)
  const link = response?.data?.link || response?.data?.url || response?.link
  if (!link) throw new Error('上传接口未返回文件地址')
  return { ...file, link }
}
// 处理已选择文件
async function handleChooseSuccess(result) {
   const selectedFiles = result.tempFiles?.length
      ? result.tempFiles
      : (result.tempFilePaths ?? []).map(path => ({ path }))
   const availableCount = MAX_FILE_COUNT - attachmentList.value.length
   const normalizedFiles = selectedFiles
      .slice(0, availableCount)
      .map(file => {
         const path = file.path || file.tempFilePath || ''
         return {
            name: file.name || getFileName(path),
            path,
            size: Number(file.size) || 0,
         }
      })
      .filter(file => file.path)
   if (!normalizedFiles.length) return
  const selectedFiles = result.tempFiles?.length
    ? result.tempFiles
    : (result.tempFilePaths ?? []).map(path => ({ path }))
  const availableCount = MAX_FILE_COUNT - attachmentList.value.length
  const normalizedFiles = selectedFiles
    .slice(0, availableCount)
    .map(file => {
      const path = file.path || file.tempFilePath || ''
      return {
        name: file.name || getFileName(path),
        path,
        size: Number(file.size) || 0,
      }
    })
    .filter(file => file.path)
  if (!normalizedFiles.length) return
   uploading.value = true
   uni.showLoading({ title: '上传中...', mask: true })
   let failedCount = 0
   try {
      const uploadResults = await Promise.all(
         normalizedFiles.map(async file => {
            try {
               return { success: true, file: await uploadSelectedFile(file) }
            } catch {
               return { success: false, file }
            }
         })
      )
      const uploadedFiles = uploadResults.filter(item => item.success).map(item => item.file)
      failedCount = uploadResults.length - uploadedFiles.length
      attachmentList.value.push(...uploadedFiles)
   } finally {
      uni.hideLoading()
      uploading.value = false
   }
   uni.showToast({
      title: failedCount ? `${failedCount}个文件上传失败` : '上传成功',
      icon: failedCount ? 'none' : 'success',
   })
  uploading.value = true
  uni.showLoading({ title: '上传中...', mask: true })
  let failedCount = 0
  try {
    const uploadResults = await Promise.all(
      normalizedFiles.map(async file => {
        try {
          return { success: true, file: await uploadSelectedFile(file) }
        } catch {
          return { success: false, file }
        }
      })
    )
    const uploadedFiles = uploadResults.filter(item => item.success).map(item => item.file)
    failedCount = uploadResults.length - uploadedFiles.length
    attachmentList.value.push(...uploadedFiles)
  } finally {
    uni.hideLoading()
    uploading.value = false
  }
  uni.showToast({
    title: failedCount ? `${failedCount}个文件上传失败` : '上传成功',
    icon: failedCount ? 'none' : 'success',
  })
}
// 处理选择文件失败
function handleChooseFail(error) {
   if (error?.errMsg?.includes('cancel')) return
   uni.showToast({ title: '选择文件失败', icon: 'none' })
  if (error?.errMsg?.includes('cancel')) return
  uni.showToast({ title: '取消选择文件', icon: 'none' })
}
// 选择附件
function handleChooseFile() {
   if (uploading.value) return
   const availableCount = MAX_FILE_COUNT - attachmentList.value.length
   if (availableCount <= 0) {
      uni.showToast({ title: `最多选择${MAX_FILE_COUNT}个附件`, icon: 'none' })
      return
   }
   const options = {
      count: availableCount,
      type: 'all',
      success: handleChooseSuccess,
      fail: handleChooseFail,
   }
  if (uploading.value) return
  const availableCount = MAX_FILE_COUNT - attachmentList.value.length
  if (availableCount <= 0) {
    uni.showToast({ title: `最多选择${MAX_FILE_COUNT}个附件`, icon: 'none' })
    return
  }
  const options = {
    count: availableCount,
    type: 'image',
    success: handleChooseSuccess,
    fail: handleChooseFail,
  }
   // #ifdef H5
   uni.chooseFile(options)
   // #endif
  // #ifdef H5
  uni.chooseFile(options)
  // #endif
   // #ifdef MP-WEIXIN
   uni.chooseMessageFile(options)
   // #endif
  // #ifdef MP-WEIXIN
  uni.chooseMessageFile(options)
  // #endif
   // #ifdef APP-PLUS
   uni.chooseImage({ ...options, sourceType: ['album', 'camera'] })
   // #endif
  // #ifdef APP-PLUS
  uni.chooseImage({ ...options, sourceType: ['album', 'camera'] })
  // #endif
}
// 移除附件
function handleRemoveFile(index) {
   attachmentList.value.splice(index, 1)
  attachmentList.value.splice(index, 1)
}
// 格式化文件大小
function formatFileSize(size) {
   if (size < 1024) return `${size} B`
   if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`
   return `${(size / 1024 / 1024).toFixed(1)} MB`
// 预览已选择图片
function handlePreviewImage(index) {
  uni.previewImage({
    current: index,
    urls: attachmentList.value.map(file => file.path),
  })
}
// 取消处置
function handleCancel() {
   uni.navigateBack()
  uni.navigateBack()
}
// 提交处置信息
async function handleSubmit() {
   if (uploading.value) {
      uni.showToast({ title: '请等待附件上传完成', icon: 'none' })
      return
   }
   const disposeContent = formData.value.disposeContent.trim()
   if (!disposeContent) {
      uni.showToast({ title: '请输入处置描述', icon: 'none' })
      return
   }
   if (!eventId.value) {
      uni.showToast({ title: '工单信息缺失', icon: 'none' })
      return
   }
  if (uploading.value) {
    uni.showToast({ title: '请等待附件上传完成', icon: 'none' })
    return
  }
  const disposeContent = formData.value.disposeContent.trim()
  if (!disposeContent) {
    uni.showToast({ title: '请输入处置描述', icon: 'none' })
    return
  }
  if (!eventId.value) {
    uni.showToast({ title: '工单信息缺失', icon: 'none' })
    return
  }
   submitting.value = true
   try {
      await addDisposeRecord({
         eventId: eventId.value,
         recordType: 1,
         disposeContent: formData.value.disposeContent,
         targetEventStatus: eventStatus.value == 1 ? 3 : eventStatus.value,
         updateEventStatus: true,
         attachUrl: attachmentList.value.map(file => file.link),
         // operateTime: '2026-07-30 14:00:00',
         // eventStatus: formData.value.isHandling ? '1' : '3',
         // handleDesc,
         // attachmentList: attachmentList.value.map(file => ({
         //    attachName: file.name,
         //    filePath: file.path,
         // })),
      })
      uni.showToast({ title: '提交成功', icon: 'success' })
      setTimeout(() => {
         submitting.value = false
         uni.navigateBack()
         // uni.navigateTo({
         //    url: `/subPackages/workDetail/HandleWork/index?id=${eventId.value}`,
         // })
      }, 1200)
   } catch {
      submitting.value = false
      uni.showToast({ title: '提交失败,请重试', icon: 'none' })
   }
  submitting.value = true
  try {
    await addDisposeRecord({
      eventId: eventId.value,
      recordType: 1,
      disposeContent: formData.value.disposeContent,
      targetEventStatus: eventStatus.value == 1 ? 3 : eventStatus.value,
      updateEventStatus: true,
      attachUrl: attachmentList.value.map(file => file.link),
      // operateTime: '2026-07-30 14:00:00',
      // eventStatus: formData.value.isHandling ? '1' : '3',
      // handleDesc,
      // attachmentList: attachmentList.value.map(file => ({
      //    attachName: file.name,
      //    filePath: file.path,
      // })),
    })
    uni.showToast({ title: '提交成功', icon: 'success' })
    setTimeout(() => {
      submitting.value = false
      uni.navigateBack()
      // uni.navigateTo({
      //    url: `/subPackages/workDetail/HandleWork/index?id=${eventId.value}`,
      // })
    }, 1200)
  } catch {
    submitting.value = false
    uni.showToast({ title: '提交失败,请重试', icon: 'none' })
  }
}
// 返回按钮点击事件
const onBackClick = () => {
   uni.navigateBack()
  uni.navigateBack()
}
onLoad(options => {
   console.log(options, '9090')
   eventId.value = options?.id || ''
   eventStatus.value = options?.eventStatus || ''
  console.log(options, '9090')
  eventId.value = options?.id || ''
  eventStatus.value = options?.eventStatus || ''
})
</script>
<style scoped lang="scss">
.handle-page {
   padding-top: 88rpx;
   :deep() {
      .u-navbar__content {
         background: url('https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_app_assets/images/user/bg.png')
            no-repeat !important;
         background-size: 100% !important;
         height: 88rpx !important;
      }
   }
  padding-top: 88rpx;
  :deep() {
    .u-navbar__content {
      background: url('https://wrj.shuixiongit.com/aiskyminio/cloud-bucket/ztzf_app_assets/images/user/bg.png')
      no-repeat !important;
      background-size: 100% !important;
      height: 88rpx !important;
    }
  }
   display: flex;
   flex-direction: column;
   box-sizing: border-box;
   height: calc(100vh);
   //padding-top: 88rpx;
   //background: #111827;
   //color: #f5f7fa;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  height: calc(100vh);
  //padding-top: 88rpx;
  //background: #111827;
  //color: #f5f7fa;
   //:deep(.u-navbar__content) {
   //   height: 88rpx !important;
   //   background: #151c29 !important;
   //}
  //:deep(.u-navbar__content) {
  //   height: 88rpx !important;
  //   background: #151c29 !important;
  //}
}
.form-scroll {
   box-sizing: border-box;
   height: 0;
   flex: 1;
   padding: 60rpx 24rpx 24rpx;
   // #ifdef APP-PLUS
   padding-top: 60rpx;
   // #endif
  box-sizing: border-box;
  height: 0;
  flex: 1;
  padding: 0 24rpx 24rpx;
}
.form-section {
   box-sizing: border-box;
   margin-bottom: 20rpx;
   background: #ffffff;
   border: 1rpx solid rgba(255, 255, 255, 0.03);
  box-sizing: border-box;
  margin-bottom: 20rpx;
  background: #ffffff;
  border: 1rpx solid rgba(255, 255, 255, 0.03);
}
.section-header {
   display: flex;
   align-items: center;
   justify-content: space-between;
   height: 82rpx;
   padding: 0 22rpx;
   border-bottom: 1rpx solid #eeeeee;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 82rpx;
  padding: 0 22rpx;
  border-bottom: 1rpx solid #eeeeee;
}
.section-title {
   font-size: 30rpx;
   font-weight: 400;
   color: #222324;
  font-size: 30rpx;
  font-weight: 400;
  color: #222324;
}
.description-wrapper {
   position: relative;
  position: relative;
}
.word-count {
   position: absolute;
   right: 22rpx;
   bottom: 16rpx;
   font-size: 24rpx;
   color: #999;
  position: absolute;
  right: 22rpx;
  bottom: 16rpx;
  font-size: 24rpx;
  color: #999;
}
.description-input {
   display: block;
   box-sizing: border-box;
   width: 100%;
   height: 350rpx;
   padding: 24rpx 22rpx;
   font-size: 30rpx;
   line-height: 1.6;
   color: #222324;
   background: transparent;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 350rpx;
  padding: 24rpx 22rpx;
  font-size: 30rpx;
  line-height: 1.6;
  color: #222324;
  background: transparent;
}
:deep(.description-placeholder) {
   color: #999;
  color: #999;
}
.status-section {
   display: flex;
   align-items: center;
   justify-content: space-between;
   height: 104rpx;
   padding: 0 20rpx 0 22rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 104rpx;
  padding: 0 20rpx 0 22rpx;
}
.status-switch {
   transform: scale(0.84);
   transform-origin: right center;
  transform: scale(0.84);
  transform-origin: right center;
}
.upload-section {
   padding-bottom: 22rpx;
  padding-bottom: 22rpx;
}
.upload-header {
   margin-bottom: 0;
  margin-bottom: 0;
}
.select-file-btn {
   width: 210rpx;
   height: 70rpx;
   margin: 0;
   font-size: 28rpx;
   line-height: 70rpx;
   //color: #ffffff;
   //background: #409eff;
   //border-radius: 4rpx;
  width: 210rpx;
  height: 70rpx;
  margin: 0;
  font-size: 28rpx;
  line-height: 70rpx;
  //color: #ffffff;
  //background: #409eff;
  //border-radius: 4rpx;
}
.select-file-btn::after,
.action-btn::after {
   border: 0;
  border: 0;
}
.custom-style {
   width: 276rpx;
   height: 76rpx;
   font-family: 'Source Han Sans CN';
   font-weight: 400;
   font-size: 28rpx;
  width: 276rpx;
  height: 76rpx;
  font-family: 'Source Han Sans CN';
  font-weight: 400;
  font-size: 28rpx;
}
.attachment-list {
   padding: 10rpx 22rpx 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16rpx;
  padding: 10rpx 22rpx 0;
}
.attachment-item {
   display: flex;
   align-items: center;
   justify-content: space-between;
   min-height: 70rpx;
   border-bottom: 1rpx solid #eeeeee;
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 8rpx;
}
.file-info {
   display: flex;
   align-items: center;
   width: 0;
   flex: 1;
}
.file-name {
   overflow: hidden;
   font-size: 26rpx;
   color: #222324;
   text-overflow: ellipsis;
   white-space: nowrap;
}
.file-size {
   margin-left: 14rpx;
   font-size: 22rpx;
   color: #999;
   white-space: nowrap;
.attachment-image {
  display: block;
  width: 100%;
  height: 100%;
}
.remove-file-btn {
   width: 56rpx;
   font-size: 42rpx;
   line-height: 56rpx;
   color: #999;
   text-align: right;
  position: absolute;
  top: 6rpx;
  right: 6rpx;
  width: 40rpx;
  height: 40rpx;
  font-size: 32rpx;
  line-height: 36rpx;
  color: #ffffff;
  text-align: center;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 50%;
}
.action-footer {
   display: flex;
   align-items: center;
   gap: 28rpx;
   box-sizing: border-box;
   padding: 22rpx 28rpx calc(22rpx + env(safe-area-inset-bottom));
   //background: #111827;
  display: flex;
  align-items: center;
  gap: 28rpx;
  box-sizing: border-box;
  padding: 22rpx 28rpx calc(22rpx + env(safe-area-inset-bottom));
  //background: #111827;
}
.action-btn {
   height: 82rpx;
   margin: 0;
   flex: 1;
   font-size: 30rpx;
   line-height: 80rpx;
   color: #ffffff;
   border-radius: 6rpx;
  height: 82rpx;
  margin: 0;
  flex: 1;
  font-size: 30rpx;
  line-height: 80rpx;
  color: #ffffff;
  border-radius: 6rpx;
}
.cancel-btn {
   color: #00b578;
   background: transparent;
   border: 2rpx solid #00b578;
  color: #00b578;
  background: transparent;
  border: 2rpx solid #00b578;
}
.confirm-btn {
   background: #00b578;
   border: 2rpx solid #00b578;
  background: #00b578;
  border: 2rpx solid #00b578;
}
.confirm-btn[disabled] {
   color: rgba(255, 255, 255, 0.72);
   background: #087b58;
  color: rgba(255, 255, 255, 0.72);
  background: #087b58;
}
</style>