16个文件已修改
14个文件已添加
1805 ■■■■■ 已修改文件
H5/components/voiceInputPopup.vue 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/config/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/manifest.json 171 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/Appeal/Appeal.vue 733 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/add-progress/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/supervision/edit-supervision-progress.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/supervision/service.js 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/supervision/supervision-progress.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/supervision/supervision.vue 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/pages/work-detail/work-detail.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/cancel.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/close.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/delete.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/open.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/pause.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/start.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/step.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/stop.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/static/Appeal/yuyin.png 补丁 | 查看 | 原始文档 | blame | 历史
H5/uni_modules/mumu-recorder/changelog.md 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/uni_modules/mumu-recorder/components/mumu-recorder/mumu-recorder.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/uni_modules/mumu-recorder/package.json 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/uni_modules/mumu-recorder/readme.md 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/utils/request.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/config/env.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/appeal-management/statistics/index.jsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/appeal-management/statistics/service.js 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/logManagement/index.jsx 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
management/src/pages/logManagement/service.js 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
H5/components/voiceInputPopup.vue
New file
@@ -0,0 +1,276 @@
<template>
  <u-popup :show="show" mode="bottom" :closeOnClickOverlay="false" @close="closePopup" :zIndex="9999" :overlay="true"
    :safeAreaInsetBottom="true">
    <view class="voice-popup">
      <view class="header">
        <text class="pl-30">语音输入</text>
        <image src="/static/Appeal/close.png" class="w-34 h-34 mr-30" mode="" @click="closePopup"></image>
      </view>
      <view class="record-anim">
        <image src="/static/Appeal/step.png" class="w-153 h-119" mode=""></image>
      </view>
      <view class="timer">{{ time }}</view>
      <view class="btns">
        <image :src="getRecordButtonSrc" class="w-153 h-153 mr-14" mode="" @click="handlerOnCahnger"></image>
        <image src="/static/Appeal/cancel.png" class="w-153 h-153 mr-14" mode="" @click="onStop"></image>
      </view>
    </view>
    <mumu-recorder ref="mumuRecorder" @success="handlerSuccess" @error="handleError"></mumu-recorder>
  </u-popup>
</template>
<script>
import { getSignature } from '../pages/index/service'
import MumuRecorder from '@/uni_modules/mumu-recorder/components/mumu-recorder/mumu-recorder.vue'
export default {
  name: 'VoiceInputPopup',
  components: {
    MumuRecorder
  },
  props: {
    show: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      time: '00:00:00',
      isRecording: false,
      isPaused: false,
      tempFilePath: '',
      timer: null,
      seconds: 0,
      minutes: 0,
      hours: 0,
      recordSegments: [], // 存储录音片段
      currentSegment: null, // 当前录音片段
      status: false,
      recorder: null
    }
  },
  computed: {
    getRecordButtonSrc() {
      if (this.isPaused) {
        return '/static/Appeal/start.png'
      }
      return this.isRecording ? '/static/Appeal/stop.png' : '/static/Appeal/start.png'
    }
  },
  mounted() {
  },
  methods: {
    handlerSave() {
      let tag = document.createElement('a')
      tag.href = this.recorder.localUrl
      tag.download = '录音'
      tag.click()
    },
    handlerOnCahnger() {
      if (!this.isRecording && !this.isPaused) {
        // 开始新的录音
        this.startNewRecording()
      } else if (this.isRecording) {
        // 暂停当前录音
        this.pauseRecording()
      } else if (this.isPaused) {
        // 继续录音
        this.resumeRecording()
      }
    },
    startNewRecording() {
      this.isRecording = true
      this.isPaused = false
      this.startTimer()
      this.$refs.mumuRecorder.start()
    },
    pauseRecording() {
      this.isRecording = false
      this.isPaused = true
      this.stopTimer()
      this.$refs.mumuRecorder.stop()
    },
    resumeRecording() {
      this.isRecording = true
      this.isPaused = false
      this.startTimer()
      this.$refs.mumuRecorder.start()
    },
    handlerSuccess(res) {
      console.log('录音成功:', res)
      this.recorder = res
      // 保存当前录音片段
      if (res.localUrl) {
        this.recordSegments.push({
          url: res.localUrl,
          duration: this.seconds + this.minutes * 60 + this.hours * 3600
        })
      }
    },
    handlerError(code) {
      switch (code) {
        case '101':
          uni.showModal({
            content: '当前浏览器版本较低,请更换浏览器使用,推荐在微信中打开。'
          })
          break;
        case '201':
          uni.showModal({
            content: '麦克风权限被拒绝,请刷新页面后授权麦克风权限。'
          })
          break
        default:
          uni.showModal({
            content: '未知错误,请刷新页面重试'
          })
          break
      }
    },
    closePopup() {
      // 清空录音片段
      this.recordSegments = []
      // 重置计时器
      this.seconds = 0
      this.minutes = 0
      this.hours = 0
      this.updateTimeDisplay()
      // 关闭弹窗
      this.$emit('close')
    },
    startTimer() {
      this.stopTimer();
      this.timer = setInterval(() => {
        this.seconds++;
        if (this.seconds >= 60) {
          this.seconds = 0;
          this.minutes++;
          if (this.minutes >= 60) {
            this.minutes = 0;
            this.hours++;
          }
        }
        this.updateTimeDisplay();
      }, 1000);
    },
    stopTimer() {
      if (this.timer) {
        clearInterval(this.timer);
        this.timer = null;
      }
    },
    updateTimeDisplay() {
      this.time = `${String(this.hours).padStart(2, '0')}:${String(this.minutes).padStart(2, '0')}:${String(this.seconds).padStart(2, '0')}`;
    },
    onStop() {
      // 如果正在录音,先停止当前录音
      if (this.isRecording) {
        this.$refs.mumuRecorder.stop()
      }
      // 停止计时
      this.stopTimer()
      // 重置状态
      this.isRecording = false
      this.isPaused = false
      // 处理录音片段
      if (this.recordSegments.length > 0) {
        // 发送第一个录音片段的URL
        this.$emit('submit', this.recordSegments[0].url)
        this.closePopup()
      } else {
        uni.showToast({
          title: '未检测到录音文件',
          icon: 'none',
          duration: 2000
        })
      }
    }
  },
  beforeDestroy() {
    this.stopTimer();
  }
}
</script>
<style scoped lang="scss">
.voice-popup {
  background: #fff;
  border-radius: 24rpx 24rpx 0 0;
  padding: 40rpx 0 30rpx 0;
  position: relative;
  z-index: 9999;
  .header {
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    font-size: 32rpx;
    font-weight: bold;
    margin-bottom: 80rpx;
    text {
      flex: 1;
      text-align: center;
    }
    .u-icon {
      position: absolute;
      right: 30rpx;
      top: 0;
    }
  }
  .record-anim {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 57rpx;
    .bars {
      display: flex;
      align-items: flex-end;
      height: 60rpx;
      .bar {
        width: 10rpx;
        margin: 0 4rpx;
        background: linear-gradient(180deg, #ff4948 0%, #fc8d55 100%);
        border-radius: 6rpx;
        transition: height 0.3s;
      }
    }
  }
  .timer {
    text-align: center;
    font-size: 28rpx;
    color: #888;
  }
  .btns {
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 40rpx 67rpx 6rpx 67rpx;
  }
}
::v-deep .uni-toast {
  z-index: 10099 !important;
}
.uni-sample-toast {
  z-index: 10099 !important;
}
::v-deep .uni-toast .uni-sample-toast {
  z-index: 10099 !important;
}
/deep/ .u-transition.u-fade-enter-to.u-fade-enter-active {
  z-index: 997 !important;
}
</style>
H5/config/index.js
@@ -1,5 +1,5 @@
export default {
    BASE_URL: 'https://huacheng.psciio.com',
    // BASE_URL: 'http://192.168.110.188:6194',
    // BASE_URL: 'https://huacheng.psciio.com',
    BASE_URL: 'http://192.168.110.188:6194',
    imageUrl: 'https://huacheng.psciio.com/api/huacheng-applets/common/uploadimages',
}
H5/manifest.json
@@ -1,85 +1,88 @@
{
    "name" : "“三个身边”群众工作机制",
    "appid" : "__UNI__DB035F5",
    "description" : "",
    "versionName" : "1.0.0",
    "versionCode" : "100",
    "transformPx" : false,
    /* 5+App特有相关 */
    "app-plus" : {
        "usingComponents" : true,
        "nvueStyleCompiler" : "uni-app",
        "compilerVersion" : 3,
        "splashscreen" : {
            "alwaysShowBeforeRender" : true,
            "waiting" : true,
            "autoclose" : true,
            "delay" : 0
        },
        /* 模块配置 */
        "modules" : {},
        /* 应用发布信息 */
        "distribute" : {
            /* android打包配置 */
            "android" : {
                "permissions" : [
                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
                ]
            },
            /* ios打包配置 */
            "ios" : {},
            /* SDK配置 */
            "sdkConfigs" : {}
        }
    },
    "sassImplementationName" : "node-sass",
    /* 快应用特有相关 */
    "quickapp" : {},
    /* 小程序特有相关 */
    "mp-weixin" : {
        "appid" : "",
        "setting" : {
            "urlCheck" : false
        },
        "usingComponents" : true
    },
    "mp-alipay" : {
        "usingComponents" : true
    },
    "mp-baidu" : {
        "usingComponents" : true
    },
    "mp-toutiao" : {
        "usingComponents" : true
    },
    "uniStatistics" : {
        "enable" : false
    },
    "vueVersion" : "2",
    "h5" : {
        "template" : "index.html",
        "title" : "“三个身边”群众工作机制",
        "optimization" : {
            "treeShaking" : {
                "enable" : false
            }
        },
        "router" : {
            "base" : "./"
        }
    }
}
    "name": "“三个身边”群众工作机制",
    "appid": "__UNI__DB035F5",
    "description": "",
    "versionName": "1.0.0",
    "versionCode": "100",
    "transformPx": false,
    /* 5+App特有相关 */
    "app-plus": {
        "usingComponents": true,
        "nvueStyleCompiler": "uni-app",
        "compilerVersion": 3,
        "splashscreen": {
            "alwaysShowBeforeRender": true,
            "waiting": true,
            "autoclose": true,
            "delay": 0
        },
        /* 模块配置 */
        "modules": {},
        /* 应用发布信息 */
        "distribute": {
            /* android打包配置 */
            "android": {
                "permissions": [
                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
                ]
            },
            /* ios打包配置 */
            "ios": {},
            /* SDK配置 */
            "sdkConfigs": {}
        }
    },
    "sassImplementationName": "node-sass",
    /* 快应用特有相关 */
    "quickapp": {},
    /* 小程序特有相关 */
    "mp-weixin": {
        "appid": "",
        "setting": {
            "urlCheck": false
        },
        "usingComponents": true
    },
    "mp-alipay": {
        "usingComponents": true
    },
    "mp-baidu": {
        "usingComponents": true
    },
    "mp-toutiao": {
        "usingComponents": true
    },
    "uniStatistics": {
        "enable": false
    },
    "vueVersion": "2",
    "h5": {
        "template": "index.html",
        "title": "“三个身边”群众工作机制",
        "optimization": {
            "treeShaking": {
                "enable": false
            }
        },
        "devServer": {
            "https": true
        },
        "router": {
            "base": "./"
        }
    }
}
H5/package.json
@@ -1,5 +1,10 @@
{
  "dependencies": {
    "echarts": "^5.6.0"
  },
  "permission": {
    "scope.record": {
      "desc": "录音功能需要使用您的录音权限"
    }
  }
}
H5/pages/Appeal/Appeal.vue
@@ -3,10 +3,10 @@
        <view class="main">
            <view class="card  flex a-center h-108 j-between">
                <text class="w-108 fs-27 font-bold mr-85">发生时间</text>
                <view class=" flex a-center j-between flex1" @click="showDate=true">
                <view class=" flex a-center j-between flex1" @click="showDate = true">
                    <view class=" flex a-center j-between flex1">
                        <view class="flex1" v-if="time">
                            {{time}}
                            {{ time }}
                        </view>
                        <view class="flex1" v-else style="color: rgba(0,0,0,0.3);font-size:27rpx;">
                            请选择问题发生的时间
@@ -17,10 +17,10 @@
            </view>
            <view class="card  flex a-center h-108 j-between">
                <text class="w-108 fs-27 font-bold mr-85">问题类型</text>
                <view class=" flex a-center j-between flex1" @click="showList=true">
                <view class=" flex a-center j-between flex1" @click="showList = true">
                    <view class="flex1" v-if="problemType">
                        {{problemType}}
                        {{ problemType }}
                    </view>
                    <view class="flex1" v-else style="color: rgba(0,0,0,0.3);font-size:27rpx;">
                        请选择问题类型
@@ -45,7 +45,7 @@
                <view class=" flex a-center j-between flex1">
                    <view class="flex1 address line2" v-if="location">
                        {{location}}
                        {{ location }}
                    </view>
                    <view class="flex1" v-else style="color: rgba(0,0,0,0.3);font-size:27rpx;">
                        请选择问题发生具体地点
@@ -59,7 +59,29 @@
                    class="uni-input flex1" placeholder="请输入详细地址描述" />
            </view>
            <view class="mt-35">
                <text class="w-108 fs-27 font-bold mr-85">问题描述</text>
                <view class="flex a-center j-between">
                    <text class="w-108 fs-27 font-bold mr-85">问题描述</text>
                    <view class="flex a-center" @click="voiceInput">
                        <image src="/static/Appeal/yuyin.png" class="w-30 h-30 mr-11" mode=""></image>
                        <text class="fs-23 red">语音输入</text>
                    </view>
                </view>
                <view v-for="item, index in videoContent" :key="index">
                    <view class="pl-19 pr-19 pb-17 pt-17 flex j-between a-center br-8 pink">
                        <view class="flex1">
                            <text class="fs-27 font-bold">语音文件{{ index + 1 }}</text>
                        </view>
                        <view class="flex j-between a-center">
                            <image src="/static/Appeal/delete.png" class="w-27 h-27 mr-38" mode=""></image>
                            <image src="/static/Appeal/open.png" class="w-27 h-27" mode="" v-if="!item.playing"
                                @click.stop="onPlayRecording(index)"></image>
                            <image src="/static/Appeal/pause.png" class="w-27 h-27" mode="" v-else
                                @click.stop="onPausePlaying(index)"></image>
                        </view>
                    </view>
                </view>
                <view class="bg mt-27 fs-27">
                    <!-- <view class="h-94 " style="border-bottom: 2rpx solid rgba(0, 10, 26, 0.07);">
                        <input v-model="descriptionTitle" placeholder-style="color: rgba(0,0,0,0.3);font-size:27rpx;"
@@ -72,7 +94,7 @@
            <view class="mt-35">
                <text class="w-108 fs-27 font-bold mr-85">上传图片</text>
                <view class="mt-27 flex a-center flex" style="flex-wrap: wrap;">
                    <view class="img mr-15" v-for="item,index in images" :key="index">
                    <view class="img mr-15" v-for="item, index in images" :key="index">
                        <image :src="item" class="w-140 h-140" mode=""></image>
                        <view class="img-icon">
                            <image class="w-19 h-19" @click="previewImage(index)" src="/static/Appeal/amplify.png"
@@ -88,7 +110,7 @@
            <view class="mt-35">
                <text class="w-108 fs-27 font-bold mr-85">上传视频</text>
                <view class="mt-27 flex a-center flex" style="flex-wrap: wrap;">
                    <view class="img mr-15" v-for="item,index in videos" :key="index">
                    <view class="img mr-15" v-for="item, index in videos" :key="index">
                        <video :controls="false" :show-center-play-btn="false" :src="item" class="w-140 h-140"
                            mode=""></video>
                        <view class="img-icon">
@@ -117,363 +139,414 @@
            @confirm="confirmone" @close="close" @cancel="cancel"></u-datetime-picker>
        <u-picker :show="showList" :columns="columns" @confirm="confirmtwo" :closeOnClickOverlay="true" @close="close"
            @cancel="cancel"></u-picker>
        <!-- 语音输入弹窗 -->
        <voiceInputPopup :show="voiceInputShow" @update:show="voiceInputShow = $event" @close="closeVoiceInput"
            @submit="submitVoiceInput" />
    </view>
</template>
<script>
    import dayjs from '@/uni_modules/uview-ui/libs/util/dayjs.js';
    import {
        save,
        getproblem
    } from './service.js'
    import config from '@/config/index.js'
import dayjs from '@/uni_modules/uview-ui/libs/util/dayjs.js';
import {
    save,
    getproblem
} from './service.js'
import config from '@/config/index.js'
import voiceInputPopup from '@/components/voiceInputPopup.vue'
import {
        mapActions,
        mapState
    } from "vuex";
    export default {
        data() {
            return {
                showList: false,
                showDate: false,
                value1: Number(new Date()),
                columns: [
                    ['医疗', '教育', '就业', '住房', '养老']
                ],
                time: '',
                problemType: '',
                name: '',
                contactNumber: '',
                location: '',
                detailedAddress: '',
                descriptionTitle: '',
                descriptionContent: '',
                latitude: '',
                longitude: '',
                images: [],
                videos: [],
                userInfo: uni.getStorageSync('userInfo'), //个人信息
            };
export default {
    components: {
        voiceInputPopup
    },
    data() {
        return {
            showList: false,
            showDate: false,
            value1: Number(new Date()),
            columns: [
                ['医疗', '教育', '就业', '住房', '养老']
            ],
            time: '',
            problemType: '',
            name: '',
            contactNumber: '',
            location: '',
            detailedAddress: '',
            descriptionTitle: '',
            descriptionContent: '',
            videoContent: [],
            latitude: '',
            longitude: '',
            images: [],
            videos: [],
            userInfo: uni.getStorageSync('userInfo'), //个人信息
            voiceInputShow: false,
        };
    },
    computed: {
        ...mapState(["playFlag"]),
    },
    onReady() {
        uni.setNavigationBarTitle({
            title: '诉求录入'
        })
    },
    onLoad() {
        this.getproblem()
        this.time = dayjs().format('YYYY-MM-DD')
    },
    methods: {
        ...mapActions(["playRecording", "pausePlaying"]),
        onPlayRecording(index) {
            // 先处理本地播放状态
            this.videoContent.forEach((item, i) => {
                item.playing = i === index;
            });
            // 调用store的播放方法,传url
            this.playRecording(this.videoContent[index].url);
        },
        onReady() {
            uni.setNavigationBarTitle({
                title: '诉求录入'
        onPausePlaying(index) {
            this.videoContent[index].playing = false;
            this.pausePlaying(this.videoContent[index].url);
        },
        voiceInput() {
            this.voiceInputShow = true;
        },
        closeVoiceInput() {
            this.voiceInputShow = false;
        },
        submitVoiceInput(e) {
            console.log('eeeeeeeeeeeeeeeeeee',e)
            this.videoContent.push({url: e, playing: false});
            this.voiceInputShow = false;
        },
        previewImage(index) {
            uni.previewImage({
                urls: this.images,
                current: this.images[index],
                longPressActions: {
                    itemList: ['发送给朋友', '保存图片', '收藏'],
                    success: function (data) {
                    },
                    fail: function (err) {
                    }
                }
            });
        },
        deletimg(e) {
            this.images = this.images.filter(item => item != e)
        },
        deletvideo(e) {
            this.videos = this.videos.filter(item => item != e)
        },
        gotoPage(e) {
            uni.navigateTo({
                url: `/pages/work-detail/maxVideo?url=${e}`
            })
        },
        onLoad() {
            this.getproblem()
            this.time = dayjs().format('YYYY-MM-DD')
        getproblem() {
            getproblem().then((resp => {
                this.columns = [resp.data.map(item => {
                    return item.name
                })]
            }))
        },
        methods: {
            previewImage(index) {
                uni.previewImage({
                    urls: this.images,
                    current: this.images[index],
                    longPressActions: {
                        itemList: ['发送给朋友', '保存图片', '收藏'],
                        success: function(data) {
        submit(type) {
            const preciseRegex = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/;
            if (!this.time) {
                uni.showToast({
                    title: '请选择时间',
                    icon: 'none'
                })
                return
            } else if (!this.problemType) {
                uni.showToast({
                    title: '请选择问题类型',
                    icon: 'none'
                })
                return
            } else if (!this.name) {
                uni.showToast({
                    title: '请输入群众姓名',
                    icon: 'none'
                })
                return
            } else if (!this.contactNumber) {
                uni.showToast({
                    title: '请输入联系电话',
                    icon: 'none'
                })
                return
            } else if (!preciseRegex.test(this.contactNumber)) {
                uni.showToast({
                    title: '请输入正确的手机号',
                    icon: 'none'
                })
                return
            } else if (!this.location) {
                uni.showToast({
                    title: '请选择地点',
                    icon: 'none'
                })
                return
            } else if (!this.detailedAddress) {
                uni.showToast({
                    title: '请输入详细地址',
                    icon: 'none'
                })
                return
            }
            const data = {
                time: this.time,
                problemType: this.problemType,
                name: this.name,
                contactNumber: this.contactNumber,
                location: this.location,
                latitude: this.latitude,
                longitude: this.longitude,
                detailedAddress: this.detailedAddress,
                descriptionContent: this.descriptionContent,
                images: this.images.join(','),
                videos: this.videos.join(','),
            }
            // 问题上报
            if (type == 1) {
                uni.navigateTo({
                    url: `/pages/problemReporting/problemReporting?data=${JSON.stringify(data)}`
                })
                return
            }
            // 办理结果录入
            if (type == 2) {
                uni.navigateTo({
                    url: `/pages/result-entry/index?data=${JSON.stringify(data)}`
                })
                return
            }
            // 添加
            if (type == 3) {
                save(data).then(resp => {
                    if (resp.code == 200) {
                        uni.showToast({
                            title: '保存成功',
                            icon: 'none'
                        })
                        setTimeout(() => {
                            uni.navigateBack()
                        }, 1500)
                    }
                })
            }
        },
        confirmone(e) {
            this.time = dayjs(e.value).format('YYYY-MM-DD')
            this.showDate = false
        },
        confirmtwo(e) {
            this.problemType = e.value[0]
            this.showList = false
        },
        cancel() {
            this.showDate = false
            this.showList = false
        },
        close(e) {
            this.showDate = false
            this.showList = false
        },
        goTopagelocation() {
            uni.navigateTo({
                url: '/pages/location/location'
            })
        },
        uploadImg() {
            uni.chooseImage({
                count: 1, //默认9
                sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                sourceType: ['album'], //从相册选择
                success: (res) => {
                    uni.showLoading()
                    uni.uploadFile({
                        url: config.imageUrl,
                        filePath: res.tempFilePaths[0],
                        name: 'file',
                        header: {
                            'Authorization': uni.getStorageSync('token')
                        },
                        fail: function(err) {
                        success: (uploadFileRes) => {
                        }
                    }
                });
            },
            deletimg(e) {
                this.images = this.images.filter(item => item != e)
            },
            deletvideo(e) {
                this.videos = this.videos.filter(item => item != e)
            },
            gotoPage(e) {
                uni.navigateTo({
                    url: `/pages/work-detail/maxVideo?url=${e}`
                })
            },
            getproblem() {
                getproblem().then((resp => {
                    this.columns = [resp.data.map(item => {
                        return item.name
                    })]
                }))
            },
            submit(type) {
                const preciseRegex = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/;
                if (!this.time) {
                    uni.showToast({
                        title: '请选择时间',
                        icon: 'none'
                    })
                    return
                } else if (!this.problemType) {
                    uni.showToast({
                        title: '请选择问题类型',
                        icon: 'none'
                    })
                    return
                } else if (!this.name) {
                    uni.showToast({
                        title: '请输入群众姓名',
                        icon: 'none'
                    })
                    return
                } else if (!this.contactNumber) {
                    uni.showToast({
                        title: '请输入联系电话',
                        icon: 'none'
                    })
                    return
                } else if (!preciseRegex.test(this.contactNumber)) {
                    uni.showToast({
                        title: '请输入正确的手机号',
                        icon: 'none'
                    })
                    return
                } else if (!this.location) {
                    uni.showToast({
                        title: '请选择地点',
                        icon: 'none'
                    })
                    return
                } else if (!this.detailedAddress) {
                    uni.showToast({
                        title: '请输入详细地址',
                        icon: 'none'
                    })
                    return
                }
                const data = {
                    time: this.time,
                    problemType: this.problemType,
                    name: this.name,
                    contactNumber: this.contactNumber,
                    location: this.location,
                    latitude: this.latitude,
                    longitude: this.longitude,
                    detailedAddress: this.detailedAddress,
                    descriptionContent: this.descriptionContent,
                    images: this.images.join(','),
                    videos: this.videos.join(','),
                }
                // 问题上报
                if (type == 1) {
                    uni.navigateTo({
                        url: `/pages/problemReporting/problemReporting?data=${JSON.stringify(data)}`
                    })
                    return
                }
                // 办理结果录入
                if (type == 2) {
                    uni.navigateTo({
                        url: `/pages/result-entry/index?data=${JSON.stringify(data)}`
                    })
                    return
                }
                // 添加
                if (type == 3) {
                    save(data).then(resp => {
                        if (resp.code == 200) {
                            this.images = [...this.images, JSON.parse(uploadFileRes.data).data]
                            uni.hideLoading()
                        },
                        fail: () => {
                            uni.hideLoading()
                            uni.showToast({
                                title: '保存成功',
                                icon: 'none'
                                title: '上传失败',
                                icon: 'error'
                            })
                            setTimeout(() => {
                                uni.navigateBack()
                            }, 1500)
                        }
                    })
                }
            },
            confirmone(e) {
                this.time = dayjs(e.value).format('YYYY-MM-DD')
                this.showDate = false
            },
            confirmtwo(e) {
                this.problemType = e.value[0]
                this.showList = false
            },
            cancel() {
                this.showDate = false
                this.showList = false
            },
            close(e) {
                this.showDate = false
                this.showList = false
            },
            goTopagelocation() {
                uni.navigateTo({
                    url: '/pages/location/location'
                })
            },
            uploadImg() {
                uni.chooseImage({
                    count: 1, //默认9
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album'], //从相册选择
                    success: (res) => {
                        uni.showLoading()
                        uni.uploadFile({
                            url: config.imageUrl,
                            filePath: res.tempFilePaths[0],
                            name: 'file',
                            header: {
                                'Authorization': uni.getStorageSync('token')
                            },
                            success: (uploadFileRes) => {
                                this.images = [...this.images, JSON.parse(uploadFileRes.data).data]
                                uni.hideLoading()
                            },
                            fail: () => {
                                uni.hideLoading()
                                uni.showToast({
                                    title: '上传失败',
                                    icon: 'error'
                                })
                            }
            });
        },
        uploadVideo() {
            uni.chooseVideo({
                count: 1, //默认9
                sourceType: ['camera', 'album'],
                success: (res) => {
                    const videoExtensions = /\.(mp4|avi|rmvb)$/i;
                    if (!videoExtensions.test(res.name)) {
                        uni.showToast({
                            title: '请上传mp4, avi, rmvb格式的视频',
                            icon: 'none',
                            duration: 3000
                        })
                        return
                    }
                });
            },
            uploadVideo() {
                uni.chooseVideo({
                    count: 1, //默认9
                    sourceType: ['camera', 'album'],
                    success: (res) => {
                        const videoExtensions = /\.(mp4|avi|rmvb)$/i;
                        if (!videoExtensions.test(res.name)) {
                    uni.showLoading()
                    uni.uploadFile({
                        url: config.imageUrl,
                        filePath: res.tempFilePath,
                        name: 'file',
                        header: {
                            'Authorization': uni.getStorageSync('token')
                        },
                        success: (uploadFileRes) => {
                            this.videos = [...this.videos, JSON.parse(uploadFileRes.data).data]
                            uni.hideLoading()
                        },
                        fail: () => {
                            uni.hideLoading()
                            uni.showToast({
                                title: '请上传mp4, avi, rmvb格式的视频',
                                icon: 'none',
                                duration: 3000
                                title: '上传失败',
                                icon: 'error'
                            })
                            return
                        }
                        uni.showLoading()
                        uni.uploadFile({
                            url: config.imageUrl,
                            filePath: res.tempFilePath,
                            name: 'file',
                            header: {
                                'Authorization': uni.getStorageSync('token')
                            },
                            success: (uploadFileRes) => {
                                this.videos = [...this.videos, JSON.parse(uploadFileRes.data).data]
                                uni.hideLoading()
                            },
                            fail: () => {
                                uni.hideLoading()
                                uni.showToast({
                                    title: '上传失败',
                                    icon: 'error'
                                })
                            }
                        })
                    }
                });
            },
        }
                    })
                }
            });
        },
    }
}
</script>
<style lang="scss" scoped>
    .min-108 {
        min-height: 108rpx;
.min-108 {
    min-height: 108rpx;
}
.red {
    color: rgba(255, 73, 72, 1);
}
.pink {
    background: #FFF1F4;
    border-radius: 8rpx;
    margin-top: 19rpx;
}
.card {
    border-bottom: 2rpx solid rgba(0, 10, 26, 0.07);
}
.bg {
    background: #F8F8F8;
}
.content {
    padding: 38rpx 31rpx 162rpx 31rpx;
    .main {
        padding: 0 27rpx 48rpx 27rpx;
        background: #FFFFFF;
        box-shadow: 0rpx 0rpx 27rpx 0rpx rgba(0, 0, 0, 0.1);
        border-radius: 19rpx;
    }
}
    .card {
        border-bottom: 2rpx solid rgba(0, 10, 26, 0.07);
textarea {
    padding: 27rpx 31rpx 27rpx 31rpx;
    font-weight: 500;
}
.img {
    position: relative;
    .img-icon {
        height: 140rpx;
        line-height: 140rpx;
        position: absolute;
        top: 0rpx;
        left: 35rpx;
    }
}
    .bg {
        background: #F8F8F8;
    }
/deep/.u-popup__content {
    border-radius: 16rpx 16rpx 0rpx 0rpx;
}
    .content {
        padding: 38rpx 31rpx 162rpx 31rpx;
/deep/ .u-toolbar {
    border-bottom: 2rpx solid RGBA(243, 243, 243, 1);
}
        .main {
            padding: 0 27rpx 48rpx 27rpx;
            background: #FFFFFF;
            box-shadow: 0rpx 0rpx 27rpx 0rpx rgba(0, 0, 0, 0.1);
            border-radius: 19rpx;
        }
    }
/deep/ .u-toolbar__wrapper__cancel {
    font-weight: 400;
    font-size: 30rpx;
    color: #FF4948 !important;
}
    textarea {
        padding: 27rpx 31rpx 27rpx 31rpx;
        font-weight: 500;
    }
/deep/ .u-toolbar__wrapper__confirm {
    font-weight: 400;
    font-size: 30rpx;
    color: #FF4948 !important;
}
    .img {
        position: relative;
.footer {
    position: fixed;
    bottom: 0;
    left: 0;
    box-shadow: 0rpx -4rpx 27rpx 0rpx rgba(0, 0, 0, 0.08);
    width: calc(100% - 20rpx);
    background-color: #fff;
    display: flex;
    gap: 27rpx;
    padding: 33rpx 10rpx;
        .img-icon {
            height: 140rpx;
            line-height: 140rpx;
            position: absolute;
            top: 0rpx;
            left: 35rpx;
        }
    }
    /deep/.u-popup__content {
        border-radius: 16rpx 16rpx 0rpx 0rpx;
    }
    /deep/ .u-toolbar {
        border-bottom: 2rpx solid RGBA(243, 243, 243, 1);
    }
    /deep/ .u-toolbar__wrapper__cancel {
        font-weight: 400;
        font-size: 30rpx;
        color: #FF4948 !important;
    }
    /deep/ .u-toolbar__wrapper__confirm {
        font-weight: 400;
        font-size: 30rpx;
        color: #FF4948 !important;
    }
    .footer {
        position: fixed;
        bottom: 0;
        left: 0;
        box-shadow: 0rpx -4rpx 27rpx 0rpx rgba(0, 0, 0, 0.08);
        width: calc(100% - 20rpx);
        background-color: #fff;
    .cancel {
        width: 331rpx;
        height: 77rpx;
        border: 2rpx solid rgba(252, 141, 85, 1);
        border-radius: 38rpx;
        display: flex;
        gap: 27rpx;
        padding: 33rpx 10rpx;
        .cancel {
            width: 331rpx;
            height: 77rpx;
            border: 2rpx solid rgba(252, 141, 85, 1);
            border-radius: 38rpx;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #FF4948;
        }
        .ok {
            width: 331rpx;
            height: 77rpx;
            background: linear-gradient(270deg, #FC8D55 0%, #FF4948 100%);
            border-radius: 48rpx;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #FFFFFF;
        }
        align-items: center;
        justify-content: center;
        color: #FF4948;
    }
    .ok {
        width: 331rpx;
        height: 77rpx;
        background: linear-gradient(270deg, #FC8D55 0%, #FF4948 100%);
        border-radius: 48rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #FFFFFF;
    }
}
</style>
H5/pages/add-progress/index.vue
@@ -236,7 +236,7 @@
                                timeout: 1000 * 45,
                                name: 'file',
                                header: {
                                    Authorization: cuni.getStorageSync('token')
                                    Authorization: uni.getStorageSync('token')
                                },
                                success: (res) => {
                                    if (JSON.parse(res.data).code == 200) {
H5/pages/supervision/edit-supervision-progress.vue
@@ -74,7 +74,7 @@
            </view>
        </view>
        <view @click.stop="submit" class="mt-38 fs-35 lh-96 br-48 txt-center font-bold bgcolor6 color4 fixed"
            style="width: calc(100% - 62rpx);bottom: calc(env(safe-area-inset-bottom) + 10rpx);">确认添加</view>
            style="width: calc(100% - 62rpx);bottom: calc(env(safe-area-inset-bottom) + 10rpx);">确认</view>
        <view class="btn-box"></view>
        <view class="safe-box"></view>
    </view>
@@ -82,7 +82,7 @@
<script>
    import {
        saveProcess,
        editProgress,
        getComplaintDetail
    } from './service'
    import config from '@/config/index.js'
@@ -97,10 +97,16 @@
                video: [],
                againCklicFlag: true,
                rulsFlag: false,
                progressId: ''
            }
        },
        onLoad(params) {
            this.complaintId = params.id
            this.describe = JSON.parse(params.data).describe
            this.progressId = JSON.parse(params.data).id
            this.video = JSON.parse(params.data).video.split(',')
            this.imgUrls = JSON.parse(params.data).imgUrl.split(',')
            this.localImageUrls = JSON.parse(params.data).imgUrl.split(',')
            getComplaintDetail({
                id: params.id
            }).then(res => {
@@ -122,12 +128,13 @@
                    })
                }
                let obj = {
                    id: this.progressId,
                    complaintId: this.complaintId,
                    describe: this.describe,
                    imgUrl: this.imgUrls.join(','),
                    video: this.video.join(',')
                }
                saveProcess(obj).then(res => {
                editProgress(obj).then(res => {
                    if (res.code == 200) {
                        uni.showToast({
                            title: '提交成功',
H5/pages/supervision/service.js
@@ -1,11 +1,25 @@
import request from '@/utils/request.js'
// 工单列表
export const getList = (params) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/complaint/list`, params)
// 处理记录
export const getProcessingList = (data) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/supervise/processing-record-page`, data)
}
// 问题驳回池
export const getRejectList = (data) => {
    return request.post(`/api/huacheng-sangeshenbian/applet/supervise/reject-record-page`, data)
}
// 工单详情
// 详情
export const getComplaintDetail = (params) => {
    return request.get('/api/huacheng-sangeshenbian/applet/complaint/detail', params)
}
// 编辑办理进度
export const editProgress = (params) => {
    return request.put('/api/huacheng-sangeshenbian/applet/complaint-progress/edit', params)
}
// 删除办理进度
export const delProgress = (id) => {
    return request.delete(`/api/huacheng-sangeshenbian/applet/complaint-progress/${id}`)
}
H5/pages/supervision/supervision-progress.vue
@@ -99,7 +99,8 @@
<script>
    import dayjs from '../../uni_modules/uview-ui/libs/util/dayjs'
    import {
        getComplaintDetail
        getComplaintDetail,
        delProgress
    } from './service'
    export default {
        data() {
@@ -129,7 +130,6 @@
            getComplaintDetail({
                id: this.id
            }).then(res => {
                this.info = {
                    ...res.data
                }
@@ -137,7 +137,23 @@
        },
        methods: {
            deleteProgress() {
                delProgress(this.row.id).then(res => {
                    uni.showToast({
                        icon: 'none',
                        mask: true,
                        title: '删除成功'
                    })
                    this.$refs.customPopup.show = false
                    setTimeout(() => {
                        getComplaintDetail({
                            id: this.id
                        }).then(res => {
                            this.info = {
                                ...res.data
                            }
                        })
                    }, 1500)
                })
            },
            toDelete(item) {
                this.row = item
@@ -145,7 +161,7 @@
            },
            toEdit(item) {
                uni.navigateTo({
                    url: `/pages/supervision/edit-supervision-progress?id=${this.id}`
                    url: `/pages/supervision/edit-supervision-progress?id=${this.id}&data=${JSON.stringify(item)}`
                })
            },
            callPhone(phoneNumber) {
H5/pages/supervision/supervision.vue
@@ -2,25 +2,23 @@
    <view class="content">
        <view class="bgColor2 shadow1 pb-19" style="position: sticky;top: 0;z-index: 999;">
            <view class="h-96 flex a-center fs-27  j-between txt-center  font-w400 color1">
                <view @click="changeType('1')" class="flex1 bgColor2 relative"
                    :class="searchParams.type == '1' && 'color2 font-bold'">
                <view @click="changeType(1)" class="flex1 bgColor2 relative" :class="type == 1 && 'color2 font-bold'">
                    <view class="relative zIndex1000">
                        处理记录
                    </view>
                    <view v-if="searchParams.type == '1'" class="bgColor1"></view>
                    <view v-if="type == 1" class="bgColor1"></view>
                </view>
                <view @click="changeType('2')" class="flex1 bgColor2 relative"
                    :class="searchParams.type == '2' && 'color2 font-bold'">
                <view @click="changeType(2)" class="flex1 bgColor2 relative" :class="type == 2 && 'color2 font-bold'">
                    <view class="relative zIndex1000">
                        问题驳回池
                    </view>
                    <view v-if="searchParams.type == '2'" class="bgColor1"></view>
                    <view v-if="type == 2" class="bgColor1"></view>
                </view>
            </view>
            <view class="flex a-center j-between mx-27 pl-38 border2">
                <view class="flex a-center flex1">
                    <image src="../../static/search.png" mode="widthFix" class="w-31 h-31 shrink0 mr-13" />
                    <input v-model="searchParams.searchStr" class="fs-27 flex1" placeholder="输入姓名、联系电话关键字搜索" />
                    <input v-model="searchParams.keyword" class="fs-27 flex1" placeholder="输入姓名、联系电话关键字搜索" />
                </view>
                <view @click="searchList" class="fs-23 lh-69 txt-center px-29 br-48 my-4 mx-4 bgcolor88">搜索</view>
            </view>
@@ -101,18 +99,18 @@
                <view class="mt-31 border1">
                    <view class="left"></view>
                    <view class="right"></view>
                    <view v-if="searchParams.type == '1'">
                    <view v-if="type == 1">
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>添加办理进度数</view>
                            <view class="font-bold">10</view>
                            <view class="font-bold">{{item.progressCount}}</view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>已下派次数</view>
                            <view class="font-bold">10</view>
                            <view class="font-bold">{{item.assignmentCount}}</view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>已上报次数</view>
                            <view class="font-bold">10</view>
                            <view class="font-bold">{{item.reportCount}}</view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>创建时间</view>
@@ -122,31 +120,32 @@
                    <view v-else>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>申请时间</view>
                            <view class="font-bold">2025-09-09 11:09:09</view>
                            <view class="font-bold">{{ item.applyTime | formatTime }}</view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>申请人</view>
                            <view class="flex a-center">
                                <view class="font-bold">李雷 13987654321</view>
                                <image @click.stop="callPhone(item.contactNumber)" src="../../static/tell.png"
                                <view class="font-bold">{{item.reporter}} {{item.reporterPhone}}</view>
                                <image @click.stop="callPhone(item.reporterPhone)" src="../../static/tell.png"
                                    class="w-58 h-58 shrink0 ml-19" />
                            </view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>审批时间</view>
                            <view class="font-bold">2025-09-09 11:09:09</view>
                            <view class="font-bold">{{ item.auditTime | formatTime }}</view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view>审批人</view>
                            <view class="flex a-center">
                                <view class="font-bold">李雷 13987654321</view>
                                <image @click.stop="callPhone(item.contactNumber)" src="../../static/tell.png"
                                <view class="font-bold">{{item.auditorName}} {{item.auditorPhone}}</view>
                                <image @click.stop="callPhone(item.auditorPhone)" src="../../static/tell.png"
                                    class="w-58 h-58 shrink0 ml-19" />
                            </view>
                        </view>
                        <view class="flex a-center j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                        <view class="flex j-between lh-38 mt-29 ml-38 mr-23 fs-27">
                            <view class="shrink0">驳回理由</view>
                            <view style="max-width: 458rpx; text-align: right;font-weight: bold;">不同意当前申请不同意当前申请不同意当前申请
                            <view style="max-width: 458rpx; text-align: right;font-weight: bold;">
                                {{item.rejectReason}}
                            </view>
                        </view>
                    </view>
@@ -160,17 +159,17 @@
<script>
    import dayjs from '@/uni_modules/uview-ui/libs/util/dayjs.js'
    import {
        getList
        getProcessingList,
        getRejectList
    } from './service.js'
    export default {
        data() {
            return {
                active: 1,
                type: 1,
                searchParams: {
                    pageNum: 1,
                    pageSize: 10,
                    type: '1',
                    searchStr: ''
                    keyword: ''
                },
                list: [],
                status: 'loadMore',
@@ -185,7 +184,7 @@
                    '6': '上级驳回',
                    '7': '延期待审核',
                    '8': '已办结'
                }
                },
            }
        },
        onReachBottom() {
@@ -220,6 +219,14 @@
        },
        methods: {
            searchList() {
                if (this.searchParams.keyword == '') {
                    uni.showToast({
                        title: '请输入关键字搜索',
                        icon: 'none',
                        mask: true
                    })
                    return
                }
                this.searchParams.pageNum = 1
                this.fetchList(this.searchParams, (e) => {
                    this.list = e.records || []
@@ -242,24 +249,19 @@
            },
            fetchList(params, callback) {
                if (this.status == 'loading') return
                this.status = 'loading'
                getList(params).then(res => {
                this.status = 'loading';
                [getProcessingList, getRejectList][this.type - 1](params).then(res => {
                    if (res.code == 200) {
                        res.data.records.map(item => {
                            if (item.images) {
                                item.images = item.images.split(',')
                            }
                        })
                        callback(res.data)
                    }
                })
            },
            // 切换状态筛选
            changeType(type) {
                this.type = type
                this.searchParams = {
                    pageNum: 1,
                    pageSize: 10,
                    type, //全部:不传,上报待审核:0,正在办理:1, 办结:2
                }
                this.fetchList(this.searchParams, (e) => {
                    this.list = e.records || []
H5/pages/work-detail/work-detail.vue
@@ -165,9 +165,9 @@
            <!-- 问题描述 -->
            <view class="problem">
                <view class="title">问题描述</view>
                <view v-for="(item,index) in 2" :key="index"
                <view v-for="(item,index) in getVoiceFile(orderInfo.voiceFile)" :key="index"
                    class="flex a-center j-between py-17 px-19 br-8 bgcolor1 mb-19">
                    <view class="fs-27 lh-38">语音名字</view>
                    <view class="fs-27 lh-38">语音名字{{(index + 1) | numToWords}}</view>
                    <image v-if="!playFlag" @click.stop="playRecording(item)" src="../../static/24gf-playCircle@2x.png"
                        class="w-27 h-27 shrink0" />
                    <image v-else @click.stop="pausePlaying" src="../../static/pausePlaying.png"
@@ -432,6 +432,18 @@
            formatTime(val) {
                if (!val) return ''
                return dayjs(val).format('YYYY-MM-DD HH:mm:ss')
            },
            numToWords(val) {
                const words = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
                // 处理 0-10
                if (val >= 0 && val <= 10) return words[val];
                // 处理 11-99
                if (val > 10 && val < 100) {
                    const ten = Math.floor(val / 10);
                    const unit = val % 10;
                    return `${ten > 1 ? words[ten] : ''}十${unit > 0 ? words[unit] : ''}`;
                }
                return val; // 超过99返回原数字
            }
        },
        onShow() {
@@ -454,6 +466,10 @@
        },
        methods: {
            ...mapActions(["playRecording", "pausePlaying"]),
            getVoiceFile(voiceFile) {
                if (!voiceFile || voiceFile.length == 0) return []
                return voiceFile.split(',')
            },
            callPhone(phoneNumber) {
                uni.makePhoneCall({
                    phoneNumber
H5/static/Appeal/cancel.png
H5/static/Appeal/close.png
H5/static/Appeal/delete.png
H5/static/Appeal/open.png
H5/static/Appeal/pause.png
H5/static/Appeal/start.png
H5/static/Appeal/step.png
H5/static/Appeal/stop.png
H5/static/Appeal/yuyin.png
H5/uni_modules/mumu-recorder/changelog.md
New file
@@ -0,0 +1,4 @@
## 1.0.1(2022-06-11)
修复苹果手机在微信中无法获取音频长度问题
## 1.0.0(2022-06-10)
版本上线
H5/uni_modules/mumu-recorder/components/mumu-recorder/mumu-recorder.vue
New file
@@ -0,0 +1,113 @@
<template>
    <view class="recorder">
    </view>
</template>
<script>
    export default {
        data() {
            return {
                isUserMedia: false,
                stream: null,
                audio: null,
                recorder: null,
                chunks: [],
                startTime: 0
            }
        },
        mounted() {
            /**
             *     error 事件的返回状态
             *     100: 请在HTTPS环境中使用
             *     101: 浏览器不支持
             *  201: 用户拒绝授权
             *  500: 未知错误
             * */
            if (origin.indexOf('https') === -1) {
                this.$emit('error', '100')
                throw '请在 https 环境中使用本插件。'
            }
            if (!navigator.mediaDevices || !window.MediaRecorder) {
                this.$emit('error', '101')
                throw '当前浏览器不支持'
            }
            this.getRecorderManager()
        },
        methods: {
            getRecorderManager() {
                this.audio = document.createElement('audio')
                navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
                    this.isUserMedia = true
                    stream.getTracks().forEach((track) => {
                        track.stop()
                    })
                }).catch(err => {
                    this.onErrorHandler(err)
                })
            },
            start() {
                if (!this.isUserMedia) return console.log('设备不支持')
                navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
                    this.startTime = new Date().getTime()
                    this.stream = stream
                    this.recorder = new MediaRecorder(stream)
                    this.recorder.ondataavailable = this.getRecordingData
                    this.recorder.onstop = this.saveRecordingData
                    this.recorder.start()
                }).catch(err => {
                    this.onErrorHandler(err)
                })
            },
            stop() {
                this.recorder.stop()
                this.stream.getTracks().forEach((track) => {
                    track.stop()
                })
            },
            getRecordingData(e) {
                this.chunks.push(e.data)
            },
            saveRecordingData() {
                const blob = new Blob(this.chunks, { 'type': 'audio/mpeg' }),
                    localUrl = URL.createObjectURL(blob)
                const endTime = new Date().getTime()
                let duration = (endTime - this.startTime).toString().split('')
                duration.splice(duration.length - 2)
                duration.splice(duration.length - 1, 0, '.')
                duration = parseFloat(duration.join(''))
                const recorder = {
                    data: blob,
                    duration: duration,
                    localUrl: localUrl
                }
                this.$emit('success', recorder)
            },
            onErrorHandler(err) {
                console.log(err)
                if (err.name === 'NotAllowedError') {
                    this.$emit('error', '201')
                    throw '用户拒绝了当前的浏览器实例的访问请求'
                }
                if (err.name === 'NotReadableError') {
                    this.$emit('error', '101')
                    throw '当前浏览器不支持'
                }
                this.$emit('error', '500')
                throw '调用失败,原因不详'
            }
        },
        destroyed() {
            this.stop()
        }
    }
</script>
<style>
</style>
H5/uni_modules/mumu-recorder/package.json
New file
@@ -0,0 +1,87 @@
{
  "id": "mumu-recorder",
  "displayName": "h5录音组件,调用H5原生功能使用麦克风进行录音",
  "version": "1.0.1",
  "description": "演示案例中模仿了微信的长按发送语音,与普通录音demo。",
  "keywords": [
    "录音",
    "麦克风",
    "模仿微信"
],
  "repository": "",
  "engines": {
    "HBuilderX": "^3.1.0"
  },
  "dcloudext": {
    "category": [
        "前端组件",
        "通用组件"
    ],
    "sale": {
      "regular": {
        "price": "0.00"
      },
      "sourcecode": {
        "price": "0.00"
      }
    },
    "contact": {
      "qq": ""
    },
    "declaration": {
      "ads": "无",
      "data": "无",
      "permissions": "麦克风"
    },
    "npmurl": ""
  },
  "uni_modules": {
    "dependencies": [],
    "encrypt": [],
    "platforms": {
      "cloud": {
        "tcb": "y",
        "aliyun": "y"
      },
      "client": {
        "Vue": {
          "vue2": "y",
          "vue3": "y"
        },
        "App": {
          "app-vue": "n",
          "app-nvue": "n"
        },
        "H5-mobile": {
          "Safari": "y",
          "Android Browser": "y",
          "微信浏览器(Android)": "y",
          "QQ浏览器(Android)": "y"
        },
        "H5-pc": {
          "Chrome": "y",
          "IE": "n",
          "Edge": "y",
          "Firefox": "y",
          "Safari": "y"
        },
        "小程序": {
          "微信": "u",
          "阿里": "u",
          "百度": "u",
          "字节跳动": "u",
          "QQ": "u",
          "钉钉": "u",
          "快手": "u",
          "飞书": "u",
          "京东": "u",
          "小红书": "u"
        },
        "快应用": {
          "华为": "u",
          "联盟": "u"
        }
      }
    }
  }
}
H5/uni_modules/mumu-recorder/readme.md
New file
@@ -0,0 +1,117 @@
## 插件简绍
### 实现原理
> 通过 navigator.mediaDevices.getUserMedia(需要https环境) 这个api调用麦克风,获取到到音频流数据。
>
> 通过 MediaRecorder 这个构造函数对音频流进行接收,完成录制后会返回一个存储`Blob`内容的录制数据。
### 使用环境
需要https环境才能使用,本地测试可以在 manifest.json  中点击源码展示,找到h5 ,添加:"devServer" : { "https" : true}
**请勿使用 UC浏览器 与 夸克等阿里旗下的浏览器,发现他们使用的内核都较低,无法正常获取音频流,并且都有对接音频流截取的插件,导致无法正常获取音频流的数据。在微信中可以正常使用,推荐在微信内打开演示案例 **
需要https环境才能使用!!!
需要https环境才能使用!!!
需要https环境才能使用!!!
### 插件使用
**插件已支持 uni_modules 支持组件easycom,以下代码演示的是普通使用**
``` html
<!-- HTML -->
    <view>
    <audio :src='recorder.localUrl' v-if='recorder' name='本地录音' controls="true"></audio>
    <view @click='handlerOnCahnger'>
            {{!status?'开始录音':'结束录音'}}
        </view>
        <mumu-recorder ref='recorder' @success='handlerSuccess' @error='handlerError'></mumu-recorder>
    </view>
```
``` javascript
// js
    import MumuRecorder from '@/uni_modules/mumu-recorder/components/mumu-recorder/mumu-recorder.vue'
    export default {
        components: { MumuRecorder },
        data() {
            return {
                status: false,
                recorder: null
            }
        },
        onLoad() {
        },
        methods: {
            handlerSave() {
                let tag = document.createElement('a')
                tag.href = this.recorder.localUrl
                tag.download = '录音'
                tag.click()
            },
            handlerOnCahnger() {
                if (this.status) {
                    this.$refs.recorder.stop()
                } else {
                    this.$refs.recorder.start()
                }
                this.status = !this.status
            },
            handlerSuccess(res) {
                console.log(res)
                this.recorder = res
            },
            handlerError(code) {
                switch (code) {
                    case '101':
                        uni.showModal({
                            content: '当前浏览器版本较低,请更换浏览器使用,推荐在微信中打开。'
                        })
                        break;
                    case '201':
                        uni.showModal({
                            content: '麦克风权限被拒绝,请刷新页面后授权麦克风权限。'
                        })
                        break
                    default:
                        uni.showModal({
                            content: '未知错误,请刷新页面重试'
                        })
                        break
                }
            }
        }
    }
```
### 相关API
##### 组件内部方法($refs 调用)
| 方法名 | 说明     | 参数 |
| ------ | -------- | ---- |
| start  | 开始录音 | 无   |
| stop   | 结束录音 | 无   |
##### 事件(Events)
| 事件名  | 说明                 | 回调参数                                                     |
| ------- | -------------------- | ------------------------------------------------------------ |
| success | 停止录音后调用此事件 | 返回录音数据,是一个对象<br />{ data: 音频的 blob 数据,上传请使用这个            <br />duration: 当前音频长度<br/>localUrl: 当前音频的本地链接,可直接通过 audio 标签进行播放 } |
| error   | 组件内部发生错误     | 错误码:<100 当前不是https环境> <101 浏览器不支持> <201 麦克风权限被拒绝> <500  未知错误> |
### 案例演示
![enter description here](https://h5plugin.mumudev.top/public/recorder/qrcode.png)
## 支持作者
![支持作者](https://student.mumudev.top/wxMP.jpg)
H5/utils/request.js
@@ -19,9 +19,9 @@
    return new Promise(function(resolve, reject) {
        let token = uni.getStorageSync('token')
        // token =
        //     'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOjE2MjA0LCJ0eXBlIjoxLCJleHAiOjE3NDM1NTY3NzYsImNyZWF0ZWQiOjE3NDIyNjA3NzY2NTR9.SAmdlprtz_Z1cNSNB4ANYsKJFC7E7Jfxo6XbT9vpbO6zHBpTRfq_oyp9UlDQtHNiYgv-MuyqCBPw1-x88C_-8A'
        // uni.setStorageSync('token', token)
        token =
            'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOjE4OTU1MjI1Nzk1MDcwODEyMTgsInR5cGUiOjEsImV4cCI6MTc0Nzk4MzIyMiwiY3JlYXRlZCI6MTc0NjY4NzIyMjAxMX0.pmUfTkxkbBirDMbnMR1IaLsbSiiwHc366_yyAetCzTOWYxTNgmQlmvw26_W62NHLOebB_ZAEgZsPvkJcaLOoPg'
        uni.setStorageSync('token', token)
        let header = {
            'content-type': type ? 'application/x-www-form-urlencoded;charset=UTF-8' : 'application/json',
            'Authorization': token,
management/config/env.ts
@@ -1,7 +1,7 @@
export default {
  dev: {
    // SERVER_URL: 'http://192.168.110.188:6194',
    SERVER_URL: 'https://huacheng.psciio.com',
    SERVER_URL: 'http://192.168.110.106:6194',
    // SERVER_URL: 'https://huacheng.psciio.com',
  },
  test: {
    SERVER_URL: '',
management/src/pages/appeal-management/statistics/index.jsx
@@ -7,14 +7,12 @@
import { getList } from './service';
const Account = () => {
    const actionRef = useRef();
    const access = useAccess();
    const formRef = useRef();
    const columns = [
        {
            title: '述求号',
            dataIndex: 'reportUserName',
            dataIndex: 'serialNumber',
            order: 8,
        },
        {
@@ -29,30 +27,30 @@
        },
        {
            title: '申请人',
            dataIndex: 'name',
            dataIndex: 'applyUserName',
            order: 5,
        },
        {
            title: '申请时间',
            dataIndex: 'name',
            dataIndex: 'applyTime',
            hideInTable: true,
            valueType: 'dateRange',
            order: 3,
        },
        {
            title: '审批时间',
            dataIndex: 'name',
            dataIndex: 'examineTime',
            valueType: 'dateRange',
            order: 2,
        },
        {
            title: '审批人',
            dataIndex: 'name',
            dataIndex: 'examineUserName',
            order: 4,
        },
        {
            title: '驳回理由',
            dataIndex: 'contactNumber',
            dataIndex: 'remark',
            hideInSearch: true,
        },
        {
@@ -64,11 +62,13 @@
                1: '延期办理',
                2: '超时办理',
                3: '已办结',
                4: '上报待审核',
                4: '群众撤销',
                5: '上报待审核',
                6: '上级驳回',
                7: '延期待审核',
                8: '已评价',
                9: '延期驳回',
            },
            render: (text, record) => {
                return Number(record.status) == 0 ? '正在办理' : record.status == 1 ? '延期办理' : record.status == 2 ? '超时办理' : record.status == 3 ? '已办结' : record.status == 4 ? '上报待审核' : '已办结';
            }
        },
        {
            title: '操作',
@@ -102,20 +102,16 @@
            >
                <ProTable
                    rowKey="id"
                    actionRef={actionRef}
                    columns={columns}
                    formRef={formRef}
                    request={async (params) => {
                        if (params.time && params.time.length > 0) {
                            params.startTime = moment(params.time[0]).format('YYYY-MM-DD HH:mm:ss');
                            params.endTime = moment(params.time[1]).format('YYYY-MM-DD 23:59:59');
                            delete params.time
                        } else {
                            delete params.startTime
                            delete params.endTime
                        if (params.applyTime && params.applyTime.length > 0) {
                            params.applyTime = moment(params.applyTime[0]).format('YYYY-MM-DD') +
                                ' - ' + moment(params.applyTime[1]).format('YYYY-MM-DD')
                        }
                        if (params.examineTime && params.examineTime.length > 0) {
                            params.examineTime = moment(params.examineTime[0]).format('YYYY-MM-DD') +
                                ' - ' + moment(params.examineTime[1]).format('YYYY-MM-DD')
                        }
                        return buildProTableDataSource(getList, params);
                    }}
management/src/pages/appeal-management/statistics/service.js
@@ -1,17 +1,8 @@
import { request } from '@umijs/max';
// 获取诉求列表
export const getList = async (data) => {
    return request(`/api/huacheng-sangeshenbian/complaint/page`, {
        method: 'POST',
        data
    });
}
// 获取述求详情
export const getDetail = async (data) => {
    return request(`/api/huacheng-sangeshenbian/complaint/detail/${data.id}`, {
export const getList = async (params) => {
    return request(`/api/huacheng-sangeshenbian/complaint-reject/list`, {
        method: 'GET',
        data
        params
    });
}
management/src/pages/logManagement/index.jsx
@@ -14,24 +14,24 @@
    const columns = [
        {
            title: '操作时间',
            dataIndex: 'name',
            dataIndex: 'createTime',
            hideInTable: true,
            valueType: 'dateRange',
            order: 1,
        },
        {
            title: '操作用户',
            dataIndex: 'reportUserName',
            dataIndex: 'operatorName',
            order: 5,
        },
        {
            title: '联系电话',
            dataIndex: 'reportUserName',
            dataIndex: 'operatorPhone',
            order: 4,
        },
        {
            title: '操作类型',
            dataIndex: 'reportUserPhone',
            dataIndex: 'operatorCategory',
            order: 3,
            valueEnum: {
                1: '登录',
@@ -48,45 +48,41 @@
        },
        {
            title: '对象名称',
            dataIndex: 'name',
            dataIndex: 'targetName',
            hideInSearch: true,
        },
        {
            title: '所在IP',
            dataIndex: 'name',
            dataIndex: 'ip',
            order: 2,
        },
    ];
    return (
        <div>
            <PageContainer header={{
                breadcrumb: {},
            }}
                title={'日志记录'}
            >
                <ProTable
                    rowKey="id"
                    actionRef={actionRef}
                    columns={columns}
                    formRef={formRef}
                    request={async (params) => {
                        if (params.time && params.time.length > 0) {
                            params.startTime = moment(params.time[0]).format('YYYY-MM-DD HH:mm:ss');
                            params.endTime = moment(params.time[1]).format('YYYY-MM-DD 23:59:59');
                            delete params.time
                        } else {
                            delete params.startTime
                            delete params.endTime
                        }
                        return buildProTableDataSource(getList, params);
                    }}
                    search={{ labelWidth: 'auto', defaultCollapsed: false }}
                />
            </PageContainer>
        </div>
        <PageContainer header={{
            breadcrumb: {},
        }}
            title={'日志记录'}
        >
            <ProTable
                rowKey="id"
                actionRef={actionRef}
                columns={columns}
                formRef={formRef}
                request={async (params) => {
                    if (params.time && params.time.length > 0) {
                        params.startTime = moment(params.time[0]).format('YYYY-MM-DD HH:mm:ss');
                        params.endTime = moment(params.time[1]).format('YYYY-MM-DD 23:59:59');
                        delete params.time
                    } else {
                        delete params.startTime
                        delete params.endTime
                    }
                    return buildProTableDataSource(getList, params);
                }}
                search={{ labelWidth: 'auto', defaultCollapsed: false }}
            />
        </PageContainer>
    );
};
export default Account;
export default Account;
management/src/pages/logManagement/service.js
@@ -1,17 +1,8 @@
import { request } from '@umijs/max';
// 获取诉求列表
export const getList = async (data) => {
    return request(`/api/huacheng-sangeshenbian/complaint/page`, {
        method: 'POST',
        data
    });
}
// 获取述求详情
export const getDetail = async (data) => {
    return request(`/api/huacheng-sangeshenbian/complaint/detail/${data.id}`, {
export const getList = async (params) => {
    return request(`/api/huacheng-sangeshenbian/system-log/list`, {
        method: 'GET',
        data
        params
    });
}