吉安感知网项目-前端
zhanghx
2026-07-31 4442c8e908d8ff01ed26fdd4ad0c68a29088e3e4
uniapps/work-app/src/subPackages/workDetail/HandleWorking/index.vue
@@ -1,381 +1,473 @@
<script setup>
import { onLoad } from '@dcloudio/uni-app'
onLoad(options => {
   console.log(options, '9090')
   // eventId.value = options?.id || ''
   // eventStatus.value = options?.eventStatus || ''
})
</script>
<template>
   <div class="workDetailContainer">
      <!-- 自定义导航栏 -->
      <u-navbar title="工单详情" :is-back="true" back-text="" :back-icon-size="40" @left-click="onBackClick">
         <template #right>
            <div v-if="isH5" class="tapShare-icon"><img src="/static/images/work/share.svg" @click="onShareClick" /></div>
            <div v-else class="share-btn" @click="onShareClick"></div>
         </template>
      </u-navbar>
      <div class="detailTop">打发打发打发打发</div>
   </div>
  <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">
               <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>
      <!--         <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 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 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>
<style lang="scss" scoped>
   .workDetailContainer {
      // 添加导航栏高度的padding-top,避免内容被遮挡
      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;
         }
      } // 分享按钮样式
      .share-btn {
         // #ifdef APP-PLUS
         width: 40rpx;
         height: 40rpx;
         background-image: url('/static/images/work/share.svg');
         background-size: contain;
         background-repeat: no-repeat;
         background-position: center;
         // #endif
      }
      .tapShare-icon {
         width: 40rpx;
         height: 40rpx;
         img {
            width: 40rpx;
            height: 40rpx;
         }
      }
      .detailTop {
         // #ifdef APP-PLUS
         padding-top: 60rpx;
         // #endif
         .image-container {
            position: relative;
            width: 100%;
            height: 410rpx;
<script setup>
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { backGdApi, addDisposeRecord, uploadFileAPI } from '@/api/work/index.js'
            .detailImage {
               width: 100%;
               height: 100%;
               display: block;
               object-fit: cover;
            }
            // 轮播图容器
            :deep(.u-swiper) {
               // 指示器容器 - 修改为居中对齐
               .u-swiper__indicator {
                  position: absolute;
                  left: 50%;
                  transform: translateX(-50%);
                  bottom: 20rpx;
                  display: flex;
                  justify-content: center;
                  align-items: center;
                  z-index: 10;
               }
               .u-swiper-indicator__wrapper__dot--active {
                  width: 5px !important;
               }
            }
         }
      }
const MAX_FILE_COUNT = 9
const eventId = ref('')
const eventStatus = ref(null)
const submitting = ref(false)
const uploading = ref(false)
const attachmentList = ref([])
const formData = ref({
  disposeContent: '',
  isHandling: true,
})
const navbarTitleStyle = {
  color: '#ffffff',
  fontSize: '34rpx',
  fontWeight: '500',
}
      .worderContainer {
         padding: 0 24rpx;
         padding-bottom: 4rpx;
         background: #f6f6f6;
         margin-top: 20rpx;
         height: calc(100vh - 530rpx);
         overflow: auto;
      }
      .workOrderContent {
         margin-top: 30epx;
         background-color: #fff;
         border-radius: 12rpx;
         padding: 20rpx;
         margin-bottom: 34rpx;
         .workOrderTitle {
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: bold;
            font-size: 32rpx;
            color: #222324;
         }
         .workOrderContainer {
            .orderRow {
               display: flex;
               justify-content: space-between;
               align-items: center;
               min-height: 96rpx;
               border-bottom: 1px solid #f5f5f5;
               color: #7b7b7b;
               .rowTitle {
                  font-family: Source Han Sans CN, Source Han Sans CN;
                  font-weight: 400;
                  font-size: 30rpx;
                  color: #222324;
                  white-space: nowrap;
               }
               .rowAddress {
                  font-family: Source Han Sans CN, Source Han Sans CN;
                  font-weight: 400;
                  font-size: 28rpx;
                  color: #1d6fe9;
                  text-decoration: underline;
                  text-align: right;
// 计算处置描述字数
const descriptionLength = computed(() => formData.value.disposeContent.length)
                  padding-top: 1px;
                  padding-left: 5px;
                  padding-right: 2px;
               }
               .status-picker {
                  .picker-value {
                     font-family: Source Han Sans CN, Source Han Sans CN;
                     font-weight: 400;
                     font-size: 28rpx;
                     color: #1d6fe9;
                     display: flex;
                     align-items: center;
                     gap: 8rpx;
                  }
                  .picker-arrow {
                     font-size: 20rpx;
                     color: #999;
                  }
               }
            }
         }
      }
// 读取当前工单标识
onLoad(options => {
  eventId.value = options?.id ?? ''
})
      .actionButton {
         display: flex;
         justify-content: center;
         align-items: center;
         box-sizing: border-box;
         width: 100%;
         height: 61px;
         padding: 0 24rpx;
// 返回上一页
function handleBack() {
  uni.navigateBack()
}
         border-radius: 6px 6px 6px 6px;
// 切换处置状态
// function handleStatusChange(event) {
//    formData.value.isHandling = event.detail.value
// }
         .btngroups {
            display: flex;
            align-items: center;
            width: 100%;
            min-width: 0;
            :deep(.u-button) {
               width: 0 !important;
               min-width: 0;
               margin: 0 !important;
               flex: 1;
               height: 76rpx !important;
            }
            &:not(.three-actions) {
               :deep(.u-button + .u-button) {
                  margin-left: 20rpx !important;
               }
            }
            &.three-actions {
               justify-content: center;
               :deep(.u-button) {
                  width: 210rpx !important;
                  flex: 0 0 210rpx;
               }
               :deep(.u-button:last-child) {
                  margin-left: 20rpx !important;
               }
            }
         }
      }
   }
// 提取文件名称
function getFileName(filePath = '') {
  const normalizedPath = filePath.replace(/\\/g, '/')
  return decodeURIComponent(normalizedPath.split('/').pop() || '未命名文件')
}
   .complete-modal-overlay {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      padding: 40rpx;
      background: rgba(0, 0, 0, 0.55);
   }
// 上传单个已选择文件
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 }
}
   .complete-modal {
      box-sizing: border-box;
      width: 620rpx;
      max-width: 100%;
      padding: 32rpx;
      background: #ffffff;
      border-radius: 12rpx;
   }
// 处理已选择文件
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
   .complete-modal-title {
      margin-bottom: 28rpx;
      font-size: 32rpx;
      font-weight: 500;
      color: #222324;
      text-align: center;
   }
  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',
  })
}
   .complete-input-wrapper {
      position: relative;
      box-sizing: border-box;
      height: 220rpx;
      padding-bottom: 38rpx;
      background: #f6f7f9;
      border: 1rpx solid #e5e7eb;
      border-radius: 6rpx;
   }
// 处理选择文件失败
function handleChooseFail(error) {
  if (error?.errMsg?.includes('cancel')) return
  uni.showToast({ title: '取消选择文件', icon: 'none' })
}
   .complete-input {
      box-sizing: border-box;
      width: 100%;
      height: 100%;
      padding: 20rpx;
      font-size: 28rpx;
      line-height: 1.5;
      color: #222324;
   }
// 选择附件
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,
  }
   .complete-word-count {
      position: absolute;
      right: 16rpx;
      bottom: 10rpx;
      font-size: 22rpx;
      color: #999999;
   }
  // #ifdef H5
  uni.chooseFile(options)
  // #endif
   .complete-modal-actions {
      display: flex;
      gap: 24rpx;
      margin-top: 30rpx;
   }
  // #ifdef MP-WEIXIN
  uni.chooseMessageFile(options)
  // #endif
   .complete-action-btn {
      height: 76rpx;
      margin: 0;
      flex: 1;
      font-size: 28rpx;
      line-height: 76rpx;
      border-radius: 6rpx;
   }
  // #ifdef APP-PLUS
  uni.chooseImage({ ...options, sourceType: ['album', 'camera'] })
  // #endif
}
   .complete-action-btn::after {
      border: 0;
   }
// 移除附件
function handleRemoveFile(index) {
  attachmentList.value.splice(index, 1)
}
   .complete-cancel-btn {
      color: #666666;
      background: #f1f2f4;
   }
// 格式化文件大小
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`
}
   .complete-confirm-btn {
      color: #ffffff;
      background: #1d6fe9;
   }
// 取消处置
function handleCancel() {
  uni.navigateBack()
}
   .complete-action-btn[disabled] {
      opacity: 0.65;
   }
// 提交处置信息
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
  }
   /* 分享弹出层样式 */
   .share-modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.6);
      display: flex;
      justify-content: flex-end;
      align-items: flex-end;
      z-index: 9999;
   }
  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' })
  }
}
   .share-modal-content {
      background-color: #fff;
      border-radius: 20rpx 20rpx 0 0;
      padding: 30rpx;
      width: 100%;
      max-height: 80vh;
      overflow-y: auto;
   }
// 返回按钮点击事件
const onBackClick = () => {
  uni.navigateBack()
}
   .share-modal-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 30rpx;
   }
onLoad(options => {
  console.log(options, '9090')
  eventId.value = options?.id || ''
  eventStatus.value = options?.eventStatus || ''
})
</script>
   .share-modal-title {
      font-size: 36rpx;
      font-weight: bold;
      color: #222324;
      text-align: center;
      flex: 1;
   }
<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;
    }
  }
   .share-modal-close {
      font-size: 60rpx;
      color: #999;
      line-height: 40rpx;
   }
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  height: calc(100vh);
  //padding-top: 88rpx;
  //background: #111827;
  //color: #f5f7fa;
   .share-modal-body {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
      padding-bottom: 30rpx;
   }
  //:deep(.u-navbar__content) {
  //   height: 88rpx !important;
  //   background: #151c29 !important;
  //}
}
   .share-modal-footer {
      display: flex;
      justify-content: space-around;
      padding-top: 30rpx;
      border-top: 1px solid #f0f0f0;
   }
.form-scroll {
   // #ifdef APP-PLUS
   padding-top: 60rpx;
   // #endif
  box-sizing: border-box;
  height: 0;
  flex: 1;
  padding: 0 24rpx 24rpx;
}
   .share-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin: 20rpx;
      width: 120rpx;
   }
.form-section {
  box-sizing: border-box;
  margin-bottom: 20rpx;
  background: #ffffff;
  border: 1rpx solid rgba(255, 255, 255, 0.03);
}
   .share-icon {
      width: 90rpx;
      height: 90rpx;
      border-radius: 50%;
      margin-bottom: 15rpx;
      background-size: contain;
      background-repeat: no-repeat;
      background-position: center;
   }
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 82rpx;
  padding: 0 22rpx;
  border-bottom: 1rpx solid #eeeeee;
}
   .share-icon.wechat {
      background-image: url('@/static/images/work/wechat.svg');
   }
.section-title {
  font-size: 30rpx;
  font-weight: 400;
  color: #222324;
}
   .share-icon.moments {
      background-image: url('@/static/images/work/moments.svg');
   }
.description-wrapper {
  position: relative;
}
   .share-icon.link {
      background-image: url('@/static/images/work/link.svg');
   }
   .share-name {
      font-size: 24rpx;
      color: #666;
      margin-top: 10rpx;
   }
.word-count {
  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;
}
:deep(.description-placeholder) {
  color: #999;
}
.status-section {
  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;
}
.upload-section {
  padding-bottom: 22rpx;
}
.upload-header {
  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;
}
.select-file-btn::after,
.action-btn::after {
  border: 0;
}
.custom-style {
  width: 276rpx;
  height: 76rpx;
  font-family: 'Source Han Sans CN';
  font-weight: 400;
  font-size: 28rpx;
}
.attachment-list {
  padding: 10rpx 22rpx 0;
}
.attachment-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 70rpx;
  border-bottom: 1rpx solid #eeeeee;
}
.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;
}
.remove-file-btn {
  width: 56rpx;
  font-size: 42rpx;
  line-height: 56rpx;
  color: #999;
  text-align: right;
}
.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;
}
.action-btn {
  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;
}
.confirm-btn {
  background: #00b578;
  border: 2rpx solid #00b578;
}
.confirm-btn[disabled] {
  color: rgba(255, 255, 255, 0.72);
  background: #087b58;
}
</style>