Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/H5/threeSide
| | |
| | | <template> |
| | | <u-popup :show="show" mode="bottom" :closeOnClickOverlay="false" @close="closePopup" zIndex="10071"> |
| | | <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="" @click="onPlay"></image> |
| | | <image src="/static/Appeal/step.png" class="w-153 h-119" mode=""></image> |
| | | </view> |
| | | <view class="timer">{{ time }}</view> |
| | | <view class="btns"> |
| | | <image src="/static/Appeal/start.png" class="w-153 h-153 mr-14" mode="" @click="onPlay"></image> |
| | | <image src="/static/Appeal/stop.png" class="w-153 h-153 mr-14" mode="" @click="onPause"></image> |
| | | <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, |
| | |
| | | data() { |
| | | return { |
| | | time: '00:00:00', |
| | | barHeights: [20, 30, 40, 30, 20], // 可做动画 |
| | | isRecording: false, |
| | | isPaused: false, |
| | | tempFilePath: '', |
| | | timer: null, |
| | | seconds: 0, |
| | | minutes: 0, |
| | | hours: 0, |
| | | recordSegments: [], // 存储录音片段 |
| | | status: false, |
| | | recorder: null, |
| | | currentSegment: null // 当前录音片段 |
| | | } |
| | | }, |
| | | methods: { |
| | | closePopup() { |
| | | this.$emit('update:show', false) |
| | | computed: { |
| | | getRecordButtonSrc() { |
| | | if (this.isPaused) { |
| | | return '/static/Appeal/start.png' |
| | | } |
| | | return this.isRecording ? '/static/Appeal/stop.png' : '/static/Appeal/start.png' |
| | | } |
| | | }, |
| | | onPlay() {}, |
| | | onPause() {}, |
| | | onStop() {}, |
| | | 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() { |
| | | console.log('开始新的录音') |
| | | this.isRecording = true |
| | | this.isPaused = false |
| | | this.startTimer() |
| | | this.$refs.mumuRecorder.start() |
| | | }, |
| | | pauseRecording() { |
| | | console.log('暂停录音') |
| | | this.isRecording = false |
| | | this.isPaused = true |
| | | this.stopTimer() |
| | | this.$refs.mumuRecorder.stop() |
| | | }, |
| | | resumeRecording() { |
| | | console.log('继续录音') |
| | | this.isRecording = true |
| | | this.isPaused = false |
| | | this.startTimer() |
| | | this.$refs.mumuRecorder.start() |
| | | }, |
| | | handlerSuccess(res) { |
| | | console.log('录音成功回调:', res) |
| | | this.recorder = res |
| | | // 保存当前录音片段 |
| | | if (res.localUrl) { |
| | | console.log('当前录音片段URL:', res.localUrl) |
| | | console.log('当前已存储的片段数:', this.recordSegments.length) |
| | | console.log('当前片段:', this.currentSegment) |
| | | |
| | | // 只有当当前片段与上一个不同时才添加 |
| | | if (!this.currentSegment || this.currentSegment.url !== res.localUrl) { |
| | | console.log('添加新的录音片段') |
| | | this.currentSegment = { |
| | | url: res.localUrl, |
| | | data: res.data, |
| | | duration: this.seconds + this.minutes * 60 + this.hours * 3600 |
| | | } |
| | | this.recordSegments.push(this.currentSegment) |
| | | console.log('更新后的片段列表:', this.recordSegments) |
| | | } else { |
| | | console.log('跳过重复的录音片段') |
| | | } |
| | | } |
| | | }, |
| | | 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.currentSegment = null |
| | | // 重置计时器 |
| | | 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')}`; |
| | | }, |
| | | // 合并音频文件 |
| | | async mergeAudioFiles(audioUrls) { |
| | | try { |
| | | const audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| | | const audioBuffers = []; |
| | | |
| | | // 加载所有音频文件 |
| | | for (const url of audioUrls) { |
| | | const response = await fetch(url); |
| | | const arrayBuffer = await response.arrayBuffer(); |
| | | const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); |
| | | audioBuffers.push(audioBuffer); |
| | | } |
| | | |
| | | // 计算总时长 |
| | | const totalLength = audioBuffers.reduce((acc, buffer) => acc + buffer.length, 0); |
| | | |
| | | // 创建新的音频缓冲区 |
| | | const mergedBuffer = audioContext.createBuffer( |
| | | audioBuffers[0].numberOfChannels, |
| | | totalLength, |
| | | audioBuffers[0].sampleRate |
| | | ); |
| | | |
| | | // 合并音频数据 |
| | | let offset = 0; |
| | | for (const buffer of audioBuffers) { |
| | | for (let channel = 0; channel < buffer.numberOfChannels; channel++) { |
| | | const channelData = buffer.getChannelData(channel); |
| | | mergedBuffer.copyToChannel(channelData, channel, offset); |
| | | } |
| | | offset += buffer.length; |
| | | } |
| | | |
| | | // 将合并后的音频转换为 Blob |
| | | const wavBlob = await this.audioBufferToWav(mergedBuffer); |
| | | const mergedUrl = URL.createObjectURL(wavBlob); |
| | | |
| | | return mergedUrl; |
| | | } catch (error) { |
| | | console.error('合并音频失败:', error); |
| | | throw error; |
| | | } |
| | | }, |
| | | |
| | | // 将 AudioBuffer 转换为 WAV 格式 |
| | | audioBufferToWav(buffer) { |
| | | const numOfChan = buffer.numberOfChannels; |
| | | const length = buffer.length * numOfChan * 2; |
| | | const buffer2 = new ArrayBuffer(44 + length); |
| | | const view = new DataView(buffer2); |
| | | const channels = []; |
| | | let sample; |
| | | let offset = 0; |
| | | let pos = 0; |
| | | |
| | | // 写入 WAV 文件头 |
| | | setUint32(0x46464952); // "RIFF" |
| | | setUint32(36 + length); // 文件长度 |
| | | setUint32(0x45564157); // "WAVE" |
| | | setUint32(0x20746d66); // "fmt " chunk |
| | | setUint32(16); // 长度 = 16 |
| | | setUint16(1); // PCM (uncompressed) |
| | | setUint16(numOfChan); |
| | | setUint32(buffer.sampleRate); |
| | | setUint32(buffer.sampleRate * 2 * numOfChan); // avg. bytes/sec |
| | | setUint16(numOfChan * 2); // block-align |
| | | setUint16(16); // 16-bit |
| | | setUint32(0x61746164); // "data" - chunk |
| | | setUint32(length); // chunk length |
| | | |
| | | // 写入音频数据 |
| | | for (let i = 0; i < buffer.numberOfChannels; i++) { |
| | | channels.push(buffer.getChannelData(i)); |
| | | } |
| | | |
| | | while (pos < buffer.length) { |
| | | for (let i = 0; i < numOfChan; i++) { |
| | | sample = Math.max(-1, Math.min(1, channels[i][pos])); |
| | | sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; |
| | | view.setInt16(44 + offset, sample, true); |
| | | offset += 2; |
| | | } |
| | | pos++; |
| | | } |
| | | |
| | | return new Blob([buffer2], { type: 'audio/wav' }); |
| | | |
| | | function setUint16(data) { |
| | | view.setUint16(pos, data, true); |
| | | pos += 2; |
| | | } |
| | | |
| | | function setUint32(data) { |
| | | view.setUint32(pos, data, true); |
| | | pos += 4; |
| | | } |
| | | }, |
| | | |
| | | // 修改 onStop 方法 |
| | | async onStop() { |
| | | console.log('停止录音') |
| | | // 如果正在录音,先停止当前录音 |
| | | if (this.isRecording) { |
| | | this.$refs.mumuRecorder.stop() |
| | | } |
| | | |
| | | // 停止计时 |
| | | this.stopTimer() |
| | | |
| | | // 重置状态 |
| | | this.isRecording = false |
| | | this.isPaused = false |
| | | this.currentSegment = null |
| | | |
| | | // 处理录音片段 |
| | | if (this.recordSegments.length > 0) { |
| | | try { |
| | | uni.showLoading({ |
| | | title: '正在处理音频...' |
| | | }) |
| | | |
| | | // 获取所有录音片段的URL |
| | | const audioUrls = this.recordSegments.map(segment => segment.url) |
| | | console.log('准备合并的音频片段列表:', audioUrls) |
| | | console.log('音频片段数量:', audioUrls.length) |
| | | |
| | | // 合并音频文件 |
| | | const mergedUrl = await this.mergeAudioFiles(audioUrls) |
| | | |
| | | // 创建合并后的音频数据对象 |
| | | const mergedAudioData = { |
| | | url: this.recordSegments[this.recordSegments.length - 1].url, |
| | | data: this.recordSegments[this.recordSegments.length - 1].data, |
| | | duration: this.seconds + this.minutes * 60 + this.hours * 3600 |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: '录制成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }) |
| | | |
| | | setTimeout(() => { |
| | | this.$emit('submit', mergedAudioData) |
| | | }, 2000) |
| | | |
| | | uni.hideLoading() |
| | | this.closePopup() |
| | | } catch (error) { |
| | | console.error('音频合并失败:', error) |
| | | uni.hideLoading() |
| | | uni.showToast({ |
| | | title: '音频合并失败,请重试', |
| | | icon: 'none', |
| | | duration: 2000 |
| | | }) |
| | | } |
| | | } else { |
| | | uni.showToast({ |
| | | title: '未检测到录音文件', |
| | | icon: 'none', |
| | | duration: 2000 |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | this.stopTimer(); |
| | | } |
| | | } |
| | | </script> |
| | |
| | | background: #fff; |
| | | border-radius: 24rpx 24rpx 0 0; |
| | | padding: 40rpx 0 30rpx 0; |
| | | position: relative; |
| | | z-index: 9999; |
| | | |
| | | .header { |
| | | display: flex; |
| | | justify-content: center; |
| | |
| | | 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; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .timer { |
| | | text-align: center; |
| | | font-size: 28rpx; |
| | | color: #888; |
| | | // margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .btns { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 40rpx 67rpx 76rpx 67rpx; |
| | | // .u-button { |
| | | // width: 100rpx; |
| | | // height: 100rpx; |
| | | // border-radius: 50%; |
| | | // display: flex; |
| | | // justify-content: center; |
| | | // align-items: center; |
| | | // margin: 0 10rpx; |
| | | // } |
| | | 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> |
| | |
| | | "enable" : false |
| | | } |
| | | }, |
| | | "devServer": { |
| | | "https": true |
| | | }, |
| | | "router" : { |
| | | "base" : "./" |
| | | } |
| | |
| | | { |
| | | "dependencies": { |
| | | "echarts": "^5.6.0" |
| | | }, |
| | | "permission": { |
| | | "scope.record": { |
| | | "desc": "录音功能需要使用您的录音权限" |
| | | } |
| | | } |
| | | } |
| | |
| | | <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> |
| | |
| | | detailedAddress: '', |
| | | descriptionTitle: '', |
| | | descriptionContent: '', |
| | | videoContent: [ |
| | | { url: 'xxx1', playing: false }, |
| | | { url: 'xxx2', playing: false } |
| | | ], |
| | | videoContent: [], |
| | | latitude: '', |
| | | longitude: '', |
| | | images: [], |
| | | videos: [], |
| | | voiceFile: '',//语音文件多个逗号拼接 |
| | | userInfo: uni.getStorageSync('userInfo'), //个人信息 |
| | | voiceInputShow: false, |
| | | }; |
| | |
| | | this.voiceInputShow = false; |
| | | }, |
| | | submitVoiceInput(e) { |
| | | this.videoContent.push(e); |
| | | console.log('eeeeeeeeeeeeeeeeeee', e) |
| | | this.videoContent.push({ url: e.url, data: e.data, playing: false }); |
| | | this.voiceInputShow = false; |
| | | }, |
| | | previewImage(index) { |
| | |
| | | })] |
| | | })) |
| | | }, |
| | | submit(type) { |
| | | async submit(type) { |
| | | if (this.videoContent.length > 0) { |
| | | uni.showLoading({ |
| | | title: '正在上传语音文件...' |
| | | }); |
| | | |
| | | const uploadPromises = this.videoContent.map(item => { |
| | | return new Promise((resolve, reject) => { |
| | | console.log('item.data', item.data) |
| | | uni.uploadFile({ |
| | | url: config.imageUrl, |
| | | file: item.data, |
| | | name: 'file', |
| | | // fileType: 'audio/mpeg', |
| | | // filePath: item.url, |
| | | // name: 'file', |
| | | header: { |
| | | 'Content-Type': 'multipart/form-data', |
| | | 'Authorization': uni.getStorageSync('token') |
| | | }, |
| | | success: (uploadFileRes) => { |
| | | const response = JSON.parse(uploadFileRes.data); |
| | | if (response.code === 200) { |
| | | resolve(response.data); |
| | | } else { |
| | | reject(new Error('上传失败')); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | reject(error); |
| | | } |
| | | }); |
| | | }); |
| | | }); |
| | | |
| | | try { |
| | | const uploadedUrls = await Promise.all(uploadPromises); |
| | | this.voiceFile = uploadedUrls.join(','); |
| | | console.log('this.voiceFile', this.voiceFile) |
| | | uni.hideLoading(); |
| | | } catch (error) { |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: '语音文件上传失败', |
| | | icon: 'error' |
| | | }); |
| | | return; |
| | | } |
| | | } |
| | | 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({ |
| | |
| | | }) |
| | | return |
| | | } |
| | | |
| | | const data = { |
| | | time: this.time, |
| | | problemType: this.problemType, |
| | |
| | | descriptionContent: this.descriptionContent, |
| | | images: this.images.join(','), |
| | | videos: this.videos.join(','), |
| | | voiceFile: this.voiceFile, |
| | | } |
| | | // 问题上报 |
| | | if (type == 1) { |
| | |
| | | success: (res) => { |
| | | |
| | | uni.showLoading() |
| | | console.log('res.tempFilePaths[0]', res.tempFilePaths[0]) |
| | | uni.uploadFile({ |
| | | url: config.imageUrl, |
| | | filePath: res.tempFilePaths[0], |
| | |
| | | timeout: 1000 * 45, |
| | | name: 'file', |
| | | header: { |
| | | Authorization: cuni.getStorageSync('token') |
| | | Authorization: uni.getStorageSync('token') |
| | | }, |
| | | success: (res) => { |
| | | if (JSON.parse(res.data).code == 200) { |
| | |
| | | </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> |
| | |
| | | |
| | | <script> |
| | | import { |
| | | saveProcess, |
| | | editProgress, |
| | | getComplaintDetail |
| | | } from './service' |
| | | import config from '@/config/index.js' |
| | |
| | | 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 => { |
| | |
| | | }) |
| | | } |
| | | 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: '提交成功', |
| | |
| | | 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}`) |
| | | } |
| | |
| | | <script> |
| | | import dayjs from '../../uni_modules/uview-ui/libs/util/dayjs' |
| | | import { |
| | | getComplaintDetail |
| | | getComplaintDetail, |
| | | delProgress |
| | | } from './service' |
| | | export default { |
| | | data() { |
| | |
| | | getComplaintDetail({ |
| | | id: this.id |
| | | }).then(res => { |
| | | |
| | | this.info = { |
| | | ...res.data |
| | | } |
| | |
| | | }, |
| | | 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 |
| | |
| | | }, |
| | | 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) { |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | <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', |
| | |
| | | '6': '上级驳回', |
| | | '7': '延期待审核', |
| | | '8': '已办结' |
| | | } |
| | | }, |
| | | } |
| | | }, |
| | | onReachBottom() { |
| | |
| | | }, |
| | | 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 || [] |
| | |
| | | }, |
| | | 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 || [] |
| | |
| | | <!-- 问题描述 --> |
| | | <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" |
| | |
| | | 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() { |
| | |
| | | }, |
| | | methods: { |
| | | ...mapActions(["playRecording", "pausePlaying"]), |
| | | getVoiceFile(voiceFile) { |
| | | if (!voiceFile || voiceFile.length == 0) return [] |
| | | return voiceFile.split(',') |
| | | }, |
| | | callPhone(phoneNumber) { |
| | | uni.makePhoneCall({ |
| | | phoneNumber |
New file |
| | |
| | | ## 1.0.1(2022-06-11) |
| | | 修复苹果手机在微信中无法获取音频长度问题 |
| | | ## 1.0.0(2022-06-10) |
| | | 版本上线 |
New file |
| | |
| | | <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> |
New file |
| | |
| | | { |
| | | "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" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | ## 插件简绍 |
| | | |
| | | ### 实现原理 |
| | | |
| | | > 通过 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 未知错误> | |
| | | |
| | | ### 案例演示 |
| | | |
| | |  |
| | | |
| | | ## 支持作者 |
| | | |
| | |  |
| | |
| | | |
| | | 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, |
| | |
| | | access: '/system_setting/position_management', |
| | | }, |
| | | { |
| | | name: '角色管理', |
| | | name: '权限管理', |
| | | path: '/setting/role', |
| | | component: './setting/role', |
| | | access: '/system_setting/role_management', |
| | |
| | | import { buildProTableDataSource, sendRequest, showDelConfirm } from '@/utils/antdUtils'; |
| | | import { PageContainer, ProTable } from '@ant-design/pro-components'; |
| | | import { Button, Card, Select, Space, Descriptions, Divider, Steps, message } from 'antd'; |
| | | import { Button, Card, Select, Space, Descriptions, Divider, Steps, message, Modal, Form, Input, Upload, Popconfirm } from 'antd'; |
| | | import { useRef, useState, useEffect } from 'react'; |
| | | import { Access, useAccess } from 'umi' |
| | | import { history, useLocation } from "@umijs/max" |
| | | import { getDetail } from './service'; |
| | | import { getDetail, editProgress, deleteProgress } from './service'; |
| | | import moment from 'moment'; |
| | | import './index.less'; |
| | | import {downLoad } from '@/utils/utils'; |
| | | import {downLoad, customRequest } from '@/utils/utils'; |
| | | import { PlusOutlined } from '@ant-design/icons'; |
| | | |
| | | const Account = () => { |
| | | const actionRef = useRef(); |
| | |
| | | const [detail, setDetail] = useState({}); |
| | | const searchParams = new URLSearchParams(useLocation().search); |
| | | const id = searchParams.get('id'); |
| | | const [editModalVisible, setEditModalVisible] = useState(false); |
| | | const [editProgressData, setEditProgressData] = useState(null); |
| | | const [editForm] = Form.useForm(); |
| | | const [loading, setLoading] = useState(false); |
| | | const [imgFileList, setImgFileList] = useState([]); |
| | | const [videoFileList, setVideoFileList] = useState([]); |
| | | const [previewVisible, setPreviewVisible] = useState(false); |
| | | const [previewImage, setPreviewImage] = useState(''); |
| | | const [previewType, setPreviewType] = useState('image'); // 'image' or 'video' |
| | | const uploadConfig = { |
| | | name: 'file', |
| | | action: 'https://huacheng.psciio.com/api/huacheng-communitybackstage/communitypartybuilding/uploadimage', |
| | | headers: { |
| | | Authorization: 'Bearer ' + localStorage.getItem('token'), |
| | | }, |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | getDetail({ id: id }).then((res) => { |
| | |
| | | span: 4, |
| | | }, |
| | | ]; |
| | | |
| | | // 上传前校验 |
| | | const beforeUpload = (file) => { |
| | | return new Promise((resolve, reject) => { |
| | | if (file.name.includes(',')) { |
| | | message.warning('上传文件不能包含英文逗号(,)'); |
| | | return Upload.LIST_IGNORE; |
| | | } |
| | | setLoading(true); |
| | | resolve(file); |
| | | }); |
| | | }; |
| | | |
| | | // 图片上传change |
| | | const handleImgChange = ({ file, fileList: newFileList }) => { |
| | | if (file.status === 'error' || (file.status === 'done' && file.response && file.response.code !== 200)) { |
| | | setLoading(false); |
| | | setImgFileList([]); |
| | | message.error('上传失败'); |
| | | editForm.setFieldValue('imgUrl', []); |
| | | return; |
| | | } |
| | | if (file.status === 'done' && file.response && file.response.code === 200) { |
| | | setLoading(false); |
| | | message.success('上传成功'); |
| | | } |
| | | let urls = newFileList.map(item => { |
| | | if (item.status === 'done' && item.response && item.response.data) { |
| | | item.url = item.response.data; |
| | | } |
| | | return item.url || item.url; |
| | | }).filter(Boolean); |
| | | setImgFileList(newFileList); |
| | | editForm.setFieldValue('imgUrl', urls); |
| | | }; |
| | | |
| | | // 视频上传change |
| | | const handleVideoChange = ({ file, fileList: newFileList }) => { |
| | | if (file.status === 'error' || (file.status === 'done' && file.response && file.response.code !== 200)) { |
| | | setLoading(false); |
| | | setVideoFileList([]); |
| | | message.error('上传失败'); |
| | | editForm.setFieldValue('videoUrl', []); |
| | | return; |
| | | } |
| | | if (file.status === 'done' && file.response && file.response.code === 200) { |
| | | setLoading(false); |
| | | message.success('上传成功'); |
| | | } |
| | | let urls = newFileList.map(item => { |
| | | if (item.status === 'done' && item.response && item.response.data) { |
| | | item.url = item.response.data; |
| | | } |
| | | return item.url || item.url; |
| | | }).filter(Boolean); |
| | | setVideoFileList(newFileList); |
| | | editForm.setFieldValue('videoUrl', urls); |
| | | }; |
| | | |
| | | // 办理进度编辑提交 |
| | | const handleEditProgress = async () => { |
| | | try { |
| | | setLoading(true); |
| | | console.log('editProgressData', editProgressData); |
| | | |
| | | const values = await editForm.validateFields(); |
| | | await editProgress({ |
| | | complaintId: id, |
| | | describe: values.describe, |
| | | id: editProgressData.id, |
| | | imgUrl: (values.imgUrl || []).join(','), |
| | | video: (values.videoUrl || []).join(',') |
| | | }); |
| | | message.success('编辑成功'); |
| | | setEditModalVisible(false); |
| | | getDetail({ id }).then((res) => setDetail(res.data)); |
| | | } catch (e) { |
| | | // 校验或请求失败 |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | |
| | | // 办理进度删除 |
| | | const handleDeleteProgress = async (progressId) => { |
| | | try { |
| | | await deleteProgress({ id: progressId }); |
| | | message.success('删除成功'); |
| | | getDetail({ id }).then((res) => setDetail(res.data)); |
| | | } catch (e) { |
| | | message.error('删除失败'); |
| | | } |
| | | }; |
| | | |
| | | // 在弹窗打开时同步 fileList |
| | | useEffect(() => { |
| | | if (editModalVisible && editProgressData) { |
| | | setImgFileList((editProgressData.imgUrl ? editProgressData.imgUrl.split(',') : []).map((url, idx) => ({ uid: idx, url, status: 'done' }))); |
| | | setVideoFileList((editProgressData.video ? editProgressData.video.split(',') : []).map((url, idx) => ({ uid: idx, url, status: 'done' }))); |
| | | editForm.setFieldsValue({ |
| | | describe: editProgressData.describe, |
| | | imgUrl: editProgressData.imgUrl ? editProgressData.imgUrl.split(',') : [], |
| | | videoUrl: editProgressData.video ? editProgressData.video.split(',') : [], |
| | | }); |
| | | } |
| | | if (!editModalVisible) { |
| | | setImgFileList([]); |
| | | setVideoFileList([]); |
| | | } |
| | | }, [editModalVisible, editProgressData]); |
| | | |
| | | return ( |
| | | <div> |
| | | <PageContainer className={'appeal-management-detail'} header={{ breadcrumb: {} }} title="述求详情" > |
| | |
| | | <Descriptions title="办理进度" column={1}> |
| | | {detail.complaintProgresses?.length > 0 ? detail.complaintProgresses.map((item, index) => ( |
| | | <Descriptions.Item key={index}> |
| | | <Card style={{ width: '800px' }} title={item.createByName} extra={moment(item.createTime).format('YYYY-MM-DD HH:mm:ss')}> |
| | | <Card style={{ width: '800px', position: 'relative' }} title={item.createByName} extra={moment(item.createTime).format('YYYY-MM-DD HH:mm:ss')}> |
| | | <Descriptions column={1} > |
| | | <Descriptions.Item> |
| | | {item.describe} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item>{item.describe}</Descriptions.Item> |
| | | <Descriptions.Item label={'上传图片'}> |
| | | {item.imgUrl && (item.imgUrl || '').split(',').map((item, index) => ( |
| | | <img width={80} style={{ marginRight: '10px' }} height={80} src={item} key={index} alt="example" /> |
| | | {item.imgUrl && (item.imgUrl || '').split(',').map((img, idx) => ( |
| | | <img width={80} style={{ marginRight: '10px' }} height={80} src={img} key={idx} alt="example" /> |
| | | ))} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label={'上传视频'}> |
| | | {item.videoUrl && (item.videoUrl || '').split(',').map((item, index) => ( |
| | | <video width={280} style={{ marginRight: '10px' }} src={item} key={index} controls></video> |
| | | {item.video && item.video.split(',').map((video, idx) => ( |
| | | <video width={280} style={{ marginRight: '10px' }} src={video} key={idx} controls></video> |
| | | ))} |
| | | </Descriptions.Item> |
| | | </Descriptions> |
| | | <div style={{ position: 'absolute', right: 24, bottom: 16 }}> |
| | | <Button size="small" type="primary" style={{ marginRight: 8 }} onClick={() => { |
| | | setEditProgressData(item); |
| | | setEditModalVisible(true); |
| | | editForm.setFieldsValue({ |
| | | describe: item.describe, |
| | | imgUrl: item.imgUrl ? item.imgUrl.split(',') : [], |
| | | videoUrl: item.video ? item.video.split(',') : [], |
| | | }); |
| | | }}>编辑</Button> |
| | | <Popconfirm title="确定要删除该办理进度吗?" onConfirm={() => handleDeleteProgress(item.id)} okText="确定" cancelText="取消"> |
| | | <Button size="small" danger>删除</Button> |
| | | </Popconfirm> |
| | | </div> |
| | | </Card> |
| | | </Descriptions.Item> |
| | | )) : <Descriptions.Item span={4} >暂无办理进度</Descriptions.Item>} |
| | |
| | | </div> |
| | | </Card> |
| | | |
| | | <Modal |
| | | title="编辑办理进度" |
| | | open={editModalVisible} |
| | | onCancel={() => setEditModalVisible(false)} |
| | | onOk={handleEditProgress} |
| | | confirmLoading={loading} |
| | | destroyOnClose |
| | | width={600} |
| | | > |
| | | <Form form={editForm} layout="vertical" initialValues={{ describe: '', imgUrl: [], videoUrl: [] }}> |
| | | <Form.Item name="describe" label="办理进度描述" rules={[{ required: true, message: '请输入办理进度描述' }]}> |
| | | <Input.TextArea rows={4} maxLength={500} showCount /> |
| | | </Form.Item> |
| | | <Form.Item name="imgUrl" label="图片:"> |
| | | <Upload |
| | | {...uploadConfig} |
| | | listType="picture-card" |
| | | maxCount={9} |
| | | beforeUpload={beforeUpload} |
| | | onChange={handleImgChange} |
| | | onRemove={() => { |
| | | setImgFileList([]); |
| | | editForm.setFieldValue('imgUrl', []); |
| | | }} |
| | | showUploadList={{ showPreviewIcon: true }} |
| | | accept="image/*" |
| | | fileList={imgFileList} |
| | | onPreview={file => { |
| | | setPreviewType('image'); |
| | | setPreviewImage(file.url || file.thumbUrl); |
| | | setPreviewVisible(true); |
| | | }} |
| | | > |
| | | {imgFileList.length < 9 && <PlusOutlined />} |
| | | </Upload> |
| | | </Form.Item> |
| | | <Form.Item name="videoUrl" label="视频:"> |
| | | <Upload |
| | | {...uploadConfig} |
| | | listType="picture-card" |
| | | maxCount={9} |
| | | beforeUpload={beforeUpload} |
| | | onChange={handleVideoChange} |
| | | onRemove={() => { |
| | | setVideoFileList([]); |
| | | editForm.setFieldValue('videoUrl', []); |
| | | }} |
| | | showUploadList={{ showPreviewIcon: true }} |
| | | accept="video/*" |
| | | fileList={videoFileList} |
| | | onPreview={file => { |
| | | setPreviewType('video'); |
| | | setPreviewImage(file.url || file.thumbUrl); |
| | | setPreviewVisible(true); |
| | | }} |
| | | > |
| | | {videoFileList.length < 9 && <PlusOutlined />} |
| | | </Upload> |
| | | </Form.Item> |
| | | </Form> |
| | | </Modal> |
| | | |
| | | <Modal |
| | | open={previewVisible} |
| | | footer={null} |
| | | onCancel={() => setPreviewVisible(false)} |
| | | width={previewType === 'video' ? 800 : 600} |
| | | > |
| | | {previewType === 'image' ? ( |
| | | <img alt="预览" style={{ width: '100%' }} src={previewImage} /> |
| | | ) : ( |
| | | <video style={{ width: '100%' }} src={previewImage} controls autoPlay /> |
| | | )} |
| | | </Modal> |
| | | </PageContainer> |
| | | </div> |
| | | ); |
| | |
| | | data |
| | | }); |
| | | } |
| | | |
| | | //编辑办理进度 |
| | | export const editProgress = async (data) => { |
| | | return request(`/api/huacheng-sangeshenbian/complaint/update-progress`, { |
| | | method: 'POST', |
| | | data |
| | | }); |
| | | } |
| | | |
| | | //删除办理进度 |
| | | export const deleteProgress = async (data) => { |
| | | return request(`/api/huacheng-sangeshenbian/complaint/del-progress/${data.id}`, { |
| | | method: 'DELETE', |
| | | data |
| | | }); |
| | | } |
| | |
| | | import { getList } from './service'; |
| | | |
| | | const Account = () => { |
| | | const actionRef = useRef(); |
| | | const access = useAccess(); |
| | | const formRef = useRef(); |
| | | |
| | | const columns = [ |
| | | { |
| | | title: '述求号', |
| | | dataIndex: 'reportUserName', |
| | | dataIndex: 'serialNumber', |
| | | order: 8, |
| | | }, |
| | | { |
| | |
| | | }, |
| | | { |
| | | 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, |
| | | }, |
| | | { |
| | |
| | | 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: '操作', |
| | |
| | | > |
| | | <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); |
| | | }} |
| | |
| | | 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 |
| | | }); |
| | | } |
| | |
| | | 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: '登录', |
| | |
| | | }, |
| | | { |
| | | title: '对象名称', |
| | | dataIndex: 'name', |
| | | dataIndex: 'targetName', |
| | | hideInSearch: true, |
| | | }, |
| | | { |
| | | title: '所在IP', |
| | | dataIndex: 'name', |
| | | dataIndex: 'ip', |
| | | order: 2, |
| | | }, |
| | | ]; |
| | | return ( |
| | | <div> |
| | | <PageContainer header={{ |
| | | breadcrumb: {}, |
| | | }} |
| | |
| | | delete params.startTime |
| | | delete params.endTime |
| | | } |
| | | |
| | | |
| | | return buildProTableDataSource(getList, params); |
| | | }} |
| | | search={{ labelWidth: 'auto', defaultCollapsed: false }} |
| | | /> |
| | | </PageContainer> |
| | | </div> |
| | | ); |
| | | }; |
| | | |
| | |
| | | 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 |
| | | }); |
| | | } |
| | |
| | | } |
| | | }, |
| | | { |
| | | title: '创建时间', |
| | | dataIndex: 'createTime', |
| | | sorter: true, |
| | | hideInSearch: true, |
| | | }, |
| | | { |
| | | title: '更新时间', |
| | | dataIndex: 'updateTime', |
| | | sorter: true, |
| | | hideInSearch: true, |
| | | }, |
| | | { |
| | | title: '状态', |
| | | dataIndex: 'freezeStatus', |
| | | valueEnum: { |
| | |
| | | '1': { text: '已冻结' }, |
| | | '0': { text: '正常中' }, |
| | | } |
| | | }, |
| | | { |
| | | title: '身份证号', |
| | | dataIndex: 'idNumber', |
| | | }, |
| | | { |
| | | title: '操作', |
| | |
| | | showQuickJumper: true, |
| | | defaultPageSize: 10, |
| | | }} |
| | | request={(params) => { |
| | | request={(params, sorter) => { |
| | | console.log('weqweqeqwe', sorter); |
| | | if (sorter.createTime) { |
| | | params.createTimeSort = sorter.createTime === 'ascend' ? 2 : 1; |
| | | } |
| | | if (sorter.updateTime) { |
| | | params.updateTimeSort = sorter.updateTime === 'ascend' ? 2 : 1; |
| | | } |
| | | |
| | | params.communityId = params.community ? params.community[params.community.length - 1] : '' |
| | | params.auditStatus = 1 |
| | | setExcelParams(() => params) |
| | |
| | | getContainer={false} |
| | | width="20%" |
| | | destroyOnClose |
| | | title={detailType ? '角色详情' : data.id ? '编辑角色' : '添加角色'} |
| | | title={detailType ? '权限详情' : data.id ? '编辑权限' : '添加权限'} |
| | | open={visible} |
| | | onCancel={() => onCancel(false)} |
| | | afterClose={() => { |
| | |
| | | <Form layout="horizontal" {...formItemLayout} form={form} scrollToFirstError> |
| | | <Form.Item |
| | | name="name" |
| | | label="角色名称" |
| | | rules={[{ required: true, message: '请输入角色名称' }]} |
| | | label="权限名称" |
| | | rules={[{ required: true, message: '请输入权限名称' }]} |
| | | > |
| | | <Input disabled={detailType} placeholder='请输入角色名称' /> |
| | | <Input disabled={detailType} placeholder='请输入权限名称' /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | name="tree" |
| | |
| | | |
| | | const columns = [ |
| | | { |
| | | title: '角色名称', |
| | | title: '权限名称', |
| | | dataIndex: 'name', |
| | | }, |
| | | { |
| | |
| | | import { Form, Input, Modal, Tree, Button, Spin, Row, Col, Select, Radio, Cascader } from 'antd'; |
| | | import { Form, Input, Modal, Tree, Button, Spin, Row, Col, Select, Radio, Cascader, Card } from 'antd'; |
| | | import { forwardRef, useImperativeHandle, useState } from 'react'; |
| | | import { useEffect } from 'react'; |
| | | import { getDepartmentList, systemPostList, systemRoleList, getSystemUserInfo, getCityList, addSystemUserInfo, editSystemUserInfo } from '../service'; |
| | | import { DeleteOutlined } from '@ant-design/icons'; |
| | | import '../index.less' |
| | | import { getDepartmentList, systemPostList, systemRoleList, getSystemUserInfo, getCityList, addSystemUserInfo, editSystemUserInfo, getCascaderData } from '../service'; |
| | | import CryptoJS from 'crypto-js'; |
| | | const formItemLayout = { |
| | | labelCol: { span: 8 }, |
| | |
| | | //所属角色 |
| | | const [roleList, setRoleList] = useState([]) |
| | | //账号层级 |
| | | const [levelList, setLevelList] = useState([{ name: '市级账号', value: 1 }, { name: '区县账号', value: 2 }, { name: '街道账号', value: 3 }, { name: '社区账号', value: 4 }, { name: '党员账号', value: 5 }]) |
| | | const [levelList, setLevelList] = useState([]) |
| | | |
| | | //级联数据 |
| | | const [cascaderData, setCascaderData] = useState([]) |
| | | //选择的层级 |
| | | const [activeLevel, setActiveLevel] = useState('') |
| | | //所属区县 |
| | |
| | | const [streetList, setStreetList] = useState([]) |
| | | const [activeStreet, setActiveStreet] = useState({}) |
| | | //所属社区 |
| | | const [communityList, setCommunityList] = useState([]) |
| | | const [activeCommunity, setActiveCommunity] = useState({}) |
| | | |
| | | |
| | | |
| | | const [accountLevels, setAccountLevels] = useState([ |
| | | { area: [], isDiscipline: 0, options: [] } |
| | | ]); |
| | | |
| | | useEffect(() => { |
| | | // 获取单位 |
| | |
| | | useImperativeHandle(ref, () => { |
| | | return { |
| | | refreshData: (data, companyList) => { |
| | | getCountyList() |
| | | getCascaderDatas() |
| | | setOneCompanyList(() => companyList) |
| | | console.log('companyList', companyList) |
| | | |
| | | systemPostList({ pageNum: 1, pageSize: 10000 }).then(res => { |
| | | setPositionList(() => res.data.records) |
| | | }) |
| | | systemRoleList({ pageNum: 1, pageSize: 10000 }).then(res => { |
| | | setRoleList(() => res.data.records) |
| | | }) |
| | | // 权限判断 |
| | | let adminInfo = JSON.parse(localStorage.getItem('userInfo')) |
| | | setAdminLevel(() => adminInfo.accountLevel) |
| | | switch (adminInfo.accountLevel) { |
| | | case 1: |
| | | setLevelList(() => [{ name: '市级账号', value: 1 }, { name: '区县账号', value: 2 }, { name: '街道账号', value: 3 }, { name: '社区账号', value: 4 }, { name: '党员账号', value: 5 }]) |
| | | break; |
| | | case 2: |
| | | getStreetList(adminInfo.districtsCode) |
| | | setLevelList(() => [{ name: '区县账号', value: 2 }, { name: '街道账号', value: 3 }, { name: '社区账号', value: 4 }, { name: '党员账号', value: 5 }]) |
| | | this.$nextTick(() => { |
| | | form.setFieldsValue({ districtsCode: adminInfo.districtsCode }) |
| | | }) |
| | | |
| | | break; |
| | | case 3: |
| | | getStreetList(adminInfo.districtsCode) |
| | | getcommunityList(adminInfo.streetId) |
| | | setLevelList(() => [{ name: '街道账号', value: 3 }, { name: '社区账号', value: 4 }, { name: '党员账号', value: 5 }]) |
| | | form.setFieldsValue({ districtsCode: adminInfo.districtsCode }) |
| | | form.setFieldsValue({ streetId: adminInfo.streetId }) |
| | | break; |
| | | case 4: |
| | | getStreetList(adminInfo.districtsCode) |
| | | getcommunityList(adminInfo.streetId) |
| | | setLevelList(() => [{ name: '社区账号', value: 4 }, { name: '党员账号', value: 5 }]) |
| | | // this.$nextTick(() => { |
| | | form.setFieldsValue({ districtsCode: adminInfo.districtsCode }) |
| | | form.setFieldsValue({ streetId: adminInfo.streetId }) |
| | | form.setFieldsValue({ communityId: adminInfo.communityId * 1 }) |
| | | // }) |
| | | |
| | | |
| | | break; |
| | | case 5: |
| | | getStreetList(adminInfo.districtsCode) |
| | | getcommunityList(adminInfo.streetId) |
| | | setLevelList(() => [{ name: '党员账号', value: 5 }]) |
| | | form.setFieldsValue({ districtsCode: adminInfo.districtsCode }) |
| | | form.setFieldsValue({ streetId: adminInfo.streetId }) |
| | | form.setFieldsValue({ communityId: adminInfo.communityId * 1 }) |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | |
| | | |
| | | if (data.id) { |
| | | getInfo(data.id) |
| | |
| | | // 保存 |
| | | const okHandle = () => { |
| | | form.validateFields().then((values) => { |
| | | // 校验accountLevels必填 |
| | | if (!accountLevels.length || accountLevels.some(item => !item.area || item.area.length === 0)) { |
| | | if (window?.antd?.message?.error) { |
| | | window.antd.message.error('请完整选择账号所属层级区域!'); |
| | | } else { |
| | | alert('请完整选择账号所属层级区域!'); |
| | | } |
| | | return; |
| | | } |
| | | // 打印提交时的accountLevels数组 |
| | | console.log('提交时的accountLevels:', accountLevels); |
| | | // 组装 systemUserLevels,严格按照接口字段,优先用原始字段 |
| | | const systemUserLevels = accountLevels.map(item => { |
| | | // Get the first selected option's id as the level |
| | | const firstSelectedOption = item.selectedOptions?.[0]; |
| | | const level = firstSelectedOption?.id || item.level || ''; |
| | | |
| | | // 判断不同级别(假设市级别id为1,区县级别id为2,街道级别id为3,社区级别id为4) |
| | | const isCityLevel = level === '1'; |
| | | const isDistrictLevel = level === '2'; |
| | | const isStreetLevel = level === '3'; |
| | | const isCommunityLevel = level === '4'; |
| | | |
| | | return { |
| | | // 市级别:所有下级字段为空 |
| | | // 区县级别:街道和社区字段为空 |
| | | // 街道级别:社区字段为空 |
| | | // 社区级别:不做处理 |
| | | community: (isCityLevel || isDistrictLevel || isStreetLevel) ? '' : (item.community || item.selectedOptions?.[3]?.name || ''), |
| | | communityId: (isCityLevel || isDistrictLevel || isStreetLevel) ? '' : (item.communityId || item.selectedOptions?.[3]?.id || ''), |
| | | districts: isCityLevel ? '' : (item.districts || item.selectedOptions?.[1]?.name || ''), |
| | | districtsCode: isCityLevel ? '' : (item.districtsCode || item.selectedOptions?.[1]?.id || ''), |
| | | id: item.id || '', // 编辑时可用 |
| | | level: level, // Use the determined level value |
| | | status: 1, |
| | | street: (isCityLevel || isDistrictLevel) ? '' : (item.street || item.selectedOptions?.[2]?.name || ''), |
| | | streetId: (isCityLevel || isDistrictLevel) ? '' : (item.streetId || item.selectedOptions?.[2]?.id || ''), |
| | | superviseFlag: typeof item.isDiscipline === 'number' ? item.isDiscipline : (item.isDiscipline ? 1 : 0), |
| | | // systemUserId: 可选,如有需要补充 |
| | | }; |
| | | }); |
| | | values.systemUserLevels = systemUserLevels; |
| | | if(values.password){ |
| | | values.password = CryptoJS.MD5(values.password).toString(); |
| | | }else{ |
| | |
| | | onUpdate(values) |
| | | } else { |
| | | onSave(values); |
| | | setAccountLevels([{ area: [], isDiscipline: 0, options: [] }]); // 清空 |
| | | } |
| | | }); |
| | | }; |
| | | const getCountyList = (id) => { |
| | | getCityList({ id: '510400', tier: 2 }).then(res => { |
| | | setCountyList(() => res.data) |
| | | }) |
| | | } |
| | | const getStreetList = (id) => { |
| | | getCityList({ id: id, tier: 3 }).then(res => { |
| | | setStreetList(() => res.data) |
| | | }) |
| | | } |
| | | const getcommunityList = (id) => { |
| | | getCityList({ id: id, tier: 4 }).then(res => { |
| | | setCommunityList(() => res.data) |
| | | }) |
| | | } |
| | | const changeCountry = (value, label) => { |
| | | setActiveCounty(label) |
| | | getStreetList(value) |
| | | form.setFieldsValue({ streetId: '', communityId: '' }) |
| | | setActiveCommunity({ name: '', id: '' }) |
| | | setActiveStreet({ name: '', id: '' }) |
| | | |
| | | // 获取级联数据 |
| | | const getCascaderDatas = () => { |
| | | getCascaderData().then(res => { |
| | | // 处理级联数据 |
| | | const processedData = res.data.map(item => { |
| | | // 根据第一级类型处理数据 |
| | | if (item.children) { |
| | | // 区县级别 |
| | | if (item.id == 2) { // 假设type=2表示区县 |
| | | // 只保留第一级children |
| | | item.children = item.children.map(child => { |
| | | const { children, ...childWithoutChildren } = child; |
| | | return childWithoutChildren; |
| | | }); |
| | | } |
| | | const changeStreet = (value, label) => { |
| | | setActiveStreet(label) |
| | | getcommunityList(value) |
| | | form.setFieldsValue({ communityId: '' }) |
| | | setActiveCommunity({ name: '', id: '' }) |
| | | // 街道级别 |
| | | else if (item.id == 3) { // 假设type=3表示街道 |
| | | // 保留区县和街道两级 |
| | | item.children = item.children.map(child => { |
| | | if (child.children) { |
| | | child.children = child.children.map(street => { |
| | | const { children, ...streetWithoutChildren } = street; |
| | | return streetWithoutChildren; |
| | | }); |
| | | } |
| | | const changeCommunity = (value, label) => { |
| | | setActiveCommunity(label) |
| | | return child; |
| | | }); |
| | | } |
| | | // 社区级别 |
| | | else if (item.id == 4) { // 假设type=4表示社区 |
| | | // 保留所有层级数据 |
| | | return item; |
| | | } |
| | | } |
| | | return item; |
| | | }); |
| | | |
| | | let arr = JSON.parse(JSON.stringify(processedData)) |
| | | let accountLevels = localStorage.getItem('userInfo') |
| | | let accountLevel = JSON.parse(accountLevels).accountLevel |
| | | if (accountLevel == 2) { |
| | | arr = arr.filter(item => item.id != 1) |
| | | } else if (accountLevel == 3) { |
| | | arr = arr.filter(item => item.id != 1 && item.id != 2) |
| | | } else if (accountLevel == 4) { |
| | | arr = arr.filter(item => item.id != 1 && item.id != 2 && item.id != 3) |
| | | } |
| | | setCascaderData(() => arr); |
| | | }); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | const getInfo = (id) => { |
| | | getSystemUserInfo(id).then(res => { |
| | | if (res.data.districtsCode) { |
| | | setActiveStreet({ name: res.data.districts, id: res.data.districtsCode }) |
| | | getStreetList(res.data.districtsCode) |
| | | } |
| | | if (res.data.streetId) { |
| | | setActiveCounty({ name: res.data.street, id: res.data.streetId }) |
| | | getcommunityList(res.data.streetId) |
| | | } |
| | | if (res.data.communityId) { |
| | | setActiveCommunity({ name: res.data.community, id: res.data.communityId }) |
| | | } |
| | | // delete res.data.password |
| | | |
| | | let departmentId = [] |
| | |
| | | departmentId.push(res.data.fourDepartmentId) |
| | | } |
| | | res.data.DepartmentId = departmentId |
| | | console.log('departmentId', departmentId) |
| | | setActiveLevel(() => res.data.accountLevel) |
| | | |
| | | // 先获取级联数据再回显 |
| | | getCascaderData().then(cascadeRes => { |
| | | // 处理级联数据 |
| | | const processedData = cascadeRes.data.map(item => { |
| | | // 根据第一级类型处理数据 |
| | | if (item.children) { |
| | | // 区县级别 |
| | | if (item.id == 2) { // 假设type=2表示区县 |
| | | // 只保留第一级children |
| | | item.children = item.children.map(child => { |
| | | const { children, ...childWithoutChildren } = child; |
| | | return childWithoutChildren; |
| | | }); |
| | | } |
| | | // 街道级别 |
| | | else if (item.id == 3) { // 假设type=3表示街道 |
| | | // 保留区县和街道两级 |
| | | item.children = item.children.map(child => { |
| | | if (child.children) { |
| | | child.children = child.children.map(street => { |
| | | const { children, ...streetWithoutChildren } = street; |
| | | return streetWithoutChildren; |
| | | }); |
| | | } |
| | | return child; |
| | | }); |
| | | } |
| | | // 社区级别 |
| | | else if (item.id == 4) { // 假设type=4表示社区 |
| | | // 保留所有层级数据 |
| | | return item; |
| | | } |
| | | } |
| | | return item; |
| | | }); |
| | | |
| | | let arr = JSON.parse(JSON.stringify(processedData)) |
| | | let accountLevels = localStorage.getItem('userInfo') |
| | | let accountLevel = JSON.parse(accountLevels).accountLevel |
| | | if (accountLevel == 2) { |
| | | arr = arr.filter(item => item.id != 1) |
| | | } else if (accountLevel == 3) { |
| | | arr = arr.filter(item => item.id != 1 && item.id != 2) |
| | | } else if (accountLevel == 4) { |
| | | arr = arr.filter(item => item.id != 1 && item.id != 2 && item.id != 3) |
| | | } |
| | | setCascaderData(() => arr); |
| | | |
| | | // 回显accountLevels,area始终为4级,id类型统一 |
| | | if (res.data.systemUserLevels && Array.isArray(res.data.systemUserLevels)) { |
| | | const newAccountLevels = res.data.systemUserLevels.map(level => { |
| | | const toId = v => (v === undefined || v === null) ? undefined : String(v); |
| | | const area = [ |
| | | toId(level.level), |
| | | toId(level.twoLevelId || level.districtsCode), |
| | | toId(level.threeLevelId || level.streetId), |
| | | toId(level.fourLevelId || level.communityId) |
| | | ].filter(v => v !== undefined && v !== null && v !== ''); |
| | | return { |
| | | area, |
| | | isDiscipline: Number(level.superviseFlag) === 1 ? 1 : 0, |
| | | options: [], |
| | | selectedOptions: [ |
| | | level.level ? { id: toId(level.level), name: '' } : undefined, |
| | | level.twoLevelName ? { id: toId(level.twoLevelId), name: level.twoLevelName } : undefined, |
| | | level.threeLevelName ? { id: toId(level.threeLevelId), name: level.threeLevelName } : undefined, |
| | | level.fourLevelName ? { id: toId(level.fourLevelId), name: level.fourLevelName } : undefined, |
| | | ].filter(Boolean), |
| | | level: toId(level.level) || '', |
| | | community: level.community || '', |
| | | communityId: level.communityId || '', |
| | | districts: level.districts || '', |
| | | districtsCode: level.districtsCode || '', |
| | | id: level.id || '', |
| | | street: level.street || '', |
| | | streetId: level.streetId || '', |
| | | }; |
| | | }); |
| | | setAccountLevels(newAccountLevels.length ? newAccountLevels : [{ area: [], isDiscipline: 0, options: [], level: '', community: '', communityId: '', districts: '', districtsCode: '', id: '', street: '', streetId: '' }]); |
| | | } |
| | | }); |
| | | |
| | | form.setFieldsValue({ ...res.data, password: '' }) |
| | | }) |
| | |
| | | return false |
| | | } |
| | | |
| | | // 动态添加账号层级项 |
| | | const addAccountLevel = () => { |
| | | setAccountLevels([...accountLevels, { area: [], isDiscipline: 0, options: [] }]); |
| | | }; |
| | | // 删除账号层级项 |
| | | const removeAccountLevel = (idx) => { |
| | | if (accountLevels.length === 1) return; |
| | | setAccountLevels(accountLevels.filter((_, i) => i !== idx)); |
| | | }; |
| | | // 纪检委单选变更 |
| | | const handleDisciplineChange = (e, idx) => { |
| | | setAccountLevels(levels => { |
| | | const newLevels = [...levels]; |
| | | newLevels[idx].isDiscipline = e.target.value; |
| | | return newLevels; |
| | | }); |
| | | }; |
| | | |
| | | return ( |
| | | <Modal |
| | | getContainer={false} |
| | | width="65%" |
| | | width="75%" |
| | | destroyOnClose |
| | | title={data.type == 'detail' ? '人员详情' : data.type == 'edit' ? '编辑人员' : '添加人员'} |
| | | open={visible} |
| | | className='addAndEditModal' |
| | | onCancel={() => onCancel(false)} |
| | | afterClose={() => { |
| | | form.resetFields() |
| | |
| | | <Button key="back" onClick={() => onCancel(false)}>关闭</Button> |
| | | } |
| | | > |
| | | <Form layout="horizontal" {...formItemLayout} form={form} scrollToFirstError> |
| | | <Row> |
| | | <Form layout="horizontal" form={form} scrollToFirstError labelCol={{ span: 6 }} wrapperCol={{ span: 20 }}> |
| | | <Row gutter={16}> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="name" |
| | |
| | | <Input disabled={data.type == 'detail'} placeholder='请输入人员姓名' /> |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | <Row> |
| | | <Col span={16}> |
| | | <Form.Item |
| | | name="DepartmentId" |
| | | label="所属单位" |
| | | labelCol={{ span: 4 }} |
| | | rules={[{ required: true, message: '请选择所属单位' }]} |
| | | > |
| | | {/* <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={oneCompanyList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > */} |
| | | <Cascader |
| | | changeSelect |
| | | options={oneCompanyList} |
| | | fieldNames={{ value: 'key', label: 'name' }} |
| | | placeholder="请选择" |
| | | // displayRender={(label) => label[label.length - 1]} |
| | | changeOnSelect={true} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | {/* <Col span={8}> |
| | | <Form.Item |
| | | name="twoDepartmentId" |
| | | label="所属二级单位" |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={twoCompanyList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="threeDepartmentId" |
| | | label="所属三级单位" |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={threeCompanyList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> */} |
| | | </Row> |
| | | {/* <Row> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="fourDepartmentId" |
| | | label="所属四级单位" |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={fourCompanyList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> */} |
| | | <Row> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="systemPostId" |
| | |
| | | rules={[{ required: true, message: '请选择所属职位' }]} |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={positionList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="systemRoleId" |
| | | label="所属角色" |
| | | rules={[{ required: true, message: '请选择所属角色' }]} |
| | | label="后台权限" |
| | | rules={[{ required: true, message: '请选择后台权限' }]} |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={roleList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | <Row> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="accountLevel" |
| | | label="账号层级" |
| | | rules={[{ required: true, message: '请选择账号层级' }]} |
| | | > |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | onChange={(e) => setActiveLevel(e)} |
| | | disabled={data.type == 'detail'} |
| | | placeholder="请选择" |
| | | options={levelList} |
| | | fieldNames={{ label: 'name', value: 'value' }} |
| | | // filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={16}> |
| | | <Form.Item |
| | | label="是否管理员" |
| | | labelCol={{ span: 4 }} |
| | | wrapperCol={{ span: 20 }} |
| | | style={{ marginBottom: 0 }} |
| | | > |
| | | <div style={{ display: "flex", flex: 1 }}> |
| | | <Form.Item |
| | | name="isAdmin" |
| | | rules={[{ required: true, message: '是否管理员' }]} |
| | | > |
| | | <Radio.Group |
| | | disabled={data.type == 'detail'} |
| | | // style={style} |
| | | // onChange={onChange} |
| | | // value={value} |
| | | options={[{ value: 0, label: '否', }, { value: 1, label: '是', }, |
| | | |
| | | ]} |
| | | /> |
| | | </Form.Item> |
| | | <div style={{ fontSize: '12px', color: "rgba(0,0,0,0.5)", marginLeft: "10px" }}>管理员主要用于接收实现临期提醒,以及上级端登录</div> |
| | | </div> |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | {activeLevel != 1 && ( |
| | | <Row> |
| | | {[2, 3, 4, 5].includes(activeLevel) && ( |
| | | <Col span={8}> |
| | | <Row gutter={16}> |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name="districtsCode" |
| | | label="所属区县" |
| | | rules={[{ required: true, message: '请选择所属区县' }]} |
| | | > |
| | | {/* <Select |
| | | // onChange={changeCountry} |
| | | // value={activeCounty} |
| | | placeholder="请选择" |
| | | options={levelList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | > |
| | | </Select > */} |
| | | <Select |
| | | key="searchSelect" |
| | | allowClear |
| | | disabled={data.type == 'detail' || [2, 3, 4, 5].includes(adminLevel)} |
| | | onChange={changeCountry} |
| | | value={activeCounty.id} |
| | | placeholder="请选择" |
| | | options={countyList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | // filterOption={false} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | )} |
| | | {[3, 4, 5].includes(activeLevel) && ( |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="streetId" |
| | | label="所属街道" |
| | | rules={[{ required: true, message: '请选择所属街道' }]} |
| | | > |
| | | <Select |
| | | onChange={changeStreet} |
| | | disabled={!activeCounty || data.type == 'detail' || [3, 4, 5].includes(adminLevel)} |
| | | key="searchSelect" |
| | | allowClear |
| | | value={activeStreet.id} |
| | | placeholder="请选择" |
| | | options={streetList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | )} |
| | | |
| | | {[4, 5].includes(activeLevel) && ( |
| | | <Col span={8}> |
| | | <Form.Item |
| | | name="communityId" |
| | | label="所属社区" |
| | | rules={[{ required: true, message: '请选择所属社区' }]} |
| | | > |
| | | <Select |
| | | onChange={changeCommunity} |
| | | disabled={!activeStreet || data.type == 'detail' || [4, 5].includes(adminLevel)} |
| | | key="searchSelect" |
| | | allowClear |
| | | value={activeCommunity.id} |
| | | placeholder="请选择" |
| | | options={communityList} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | > |
| | | </Select > |
| | | </Form.Item> |
| | | </Col> |
| | | )} |
| | | </Row> |
| | | )} |
| | | |
| | | <Row> |
| | | <Col span={16}> |
| | | <Form.Item |
| | | label="联系方式" |
| | | label="账号所属层级" |
| | | colon={true} |
| | | required |
| | | labelCol={{ span: 4 }} |
| | | wrapperCol={{ span: 20 }} |
| | | labelCol={{ span: 2 }} |
| | | wrapperCol={{ span: 12 }} |
| | | style={{ marginBottom: 0 }} |
| | | > |
| | | <div style={{ display: "flex", flex: 1 }}> |
| | | <Form.Item |
| | | name="phone" |
| | | <Button type={'primary'} onClick={addAccountLevel} style={{ marginBottom: 8 }}>添加</Button> |
| | | {accountLevels.map((item, idx) => ( |
| | | <Card key={idx} style={{ marginBottom: 8 }} size='small' > |
| | | <div style={{ display: 'flex', alignItems: 'center' }}> |
| | | <Cascader |
| | | style={{ width: 320, marginRight: 16 }} |
| | | placeholder="请选择区域" |
| | | options={cascaderData} |
| | | fieldNames={{ label: 'name', value: 'id' }} |
| | | value={item.area} |
| | | onChange={(value, selectedOptions) => { |
| | | setAccountLevels(levels => { |
| | | const newLevels = [...levels]; |
| | | newLevels[idx] = { |
| | | ...newLevels[idx], |
| | | area: value, |
| | | selectedOptions: selectedOptions, |
| | | // 清空原有数据,确保新选择的数据生效 |
| | | community: '', |
| | | communityId: '', |
| | | districts: '', |
| | | districtsCode: '', |
| | | street: '', |
| | | streetId: '', |
| | | level: selectedOptions?.[0]?.id || '' |
| | | }; |
| | | return newLevels; |
| | | }); |
| | | }} |
| | | /> |
| | | |
| | | <div style={{ marginLeft: 16, marginTop: 22 }}> |
| | | <span style={{ marginRight: 8 }}>是否为纪检委账号:</span> |
| | | <Radio.Group |
| | | style={{ marginRight: 16 }} |
| | | value={item.isDiscipline ?? 0} |
| | | onChange={e => handleDisciplineChange(e, idx)} |
| | | options={[ |
| | | { label: '否', value: 0 }, |
| | | { label: '是', value: 1 } |
| | | ]} |
| | | /> |
| | | </div> |
| | | |
| | | {idx > 0 && <DeleteOutlined style={{ marginRight: 16 }} onClick={() => removeAccountLevel(idx)} disabled={accountLevels.length === 1} />} |
| | | |
| | | </div> |
| | | </Card> |
| | | ))} |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | <Row gutter={16}> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | label="联系方式" |
| | | name="phone" |
| | | extra={'联系方式将作为登录账号使用'} |
| | | rules={[{ |
| | | validator: (rule, value) => { |
| | | return new Promise((resolve, reject) => { |
| | |
| | | > |
| | | <Input disabled={data.type == 'detail'} placeholder='请输入联系方式' /> |
| | | </Form.Item> |
| | | <div style={{ fontSize: '12px', color: "rgba(0,0,0,0.5)", marginLeft: "10px" }}>联系方式将作为登录账号使用</div> |
| | | </div> |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | <Row> |
| | | <Row gutter={16}> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | required={data.type == 'add'} |
| | |
| | | <Input.Password disabled={data.type == 'detail'} placeholder='请输入' /> |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={16}> |
| | | </Row> |
| | | <Row gutter={16}> |
| | | <Col span={8}> |
| | | <Form.Item |
| | | label="确认密码" |
| | | required={data.type == 'add'} |
| | | labelCol={{ span: 4 }} |
| | | wrapperCol={{ span: 20 }} |
| | | style={{ marginBottom: 0 }} |
| | | > |
| | | <div style={{ display: "flex", flex: 1 }}> |
| | | <Form.Item |
| | | name="surePassword" |
| | | extra={'密码需要包含大小写字母,数字和特殊符号,且长度为8位以上'} |
| | | required={data.type == 'add'} |
| | | rules={[{ |
| | | validator: (rule, value) => { |
| | | return new Promise((resolve, reject) => { |
| | |
| | | resolve(''); |
| | | }); |
| | | }, |
| | | },]} |
| | | }]} |
| | | > |
| | | <Input.Password disabled={data.type == 'detail'} placeholder='请输入' /> |
| | | </Form.Item> |
| | | <div style={{ fontSize: '12px', color: "rgba(0,0,0,0.5)", marginLeft: "10px" }}>密码需要包含大小写字母,数字和特殊符号,且长度为8位以上</div> |
| | | </div> |
| | | </Form.Item> |
| | | </Col> |
| | | </Row> |
| | | |
| | | |
| | | </Form> |
| | | <Spin spinning={spinning} fullscreen /> |
| | |
| | | import { buildProTableDataSource, sendRequest, showDelConfirm, showConfirm } from '@/utils/antdUtils'; |
| | | import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'; |
| | | import { PageContainer, ProFormText, ProTable, QueryFilter } from '@ant-design/pro-components'; |
| | | import { Button, Cascader, Col, Menu, Row, Select, Space } from 'antd'; |
| | | import { PageContainer, ProFormText, ProTable, QueryFilter, ProFormSelect } from '@ant-design/pro-components'; |
| | | import { Button, Cascader, Col, Menu, Row, Select, Space, Form } from 'antd'; |
| | | import { useEffect, useRef, useState } from 'react'; |
| | | import { Access, useAccess } from 'umi'; |
| | | import AddAndEdit from './components/addAndEdit'; |
| | |
| | | } from './service'; |
| | | |
| | | const Role = () => { |
| | | const [form] = Form.useForm(); |
| | | const actionRef = useRef(); |
| | | const addViewRef = useRef(); |
| | | const addViewRef1 = useRef(); |
| | |
| | | }} |
| | | > |
| | | <span>{item.name}</span> |
| | | <div> |
| | | {/* <div> |
| | | {item.tier < 4 && ( |
| | | <Access accessible={access['/system_setting/unit_management/add']}> |
| | | <PlusOutlined |
| | |
| | | }} |
| | | /> |
| | | </Access> |
| | | </div> |
| | | </div> */} |
| | | </div > |
| | | ); |
| | | }; |
| | | const renderMenuItems = (items) => { |
| | | const renderMenuItems = (items, level = 0) => { |
| | | return items.map((item) => { |
| | | if (item.children && item.children.length > 0) { |
| | | return ( |
| | | <SubMenu |
| | | key={item.key} |
| | | title={node(item)} |
| | | onTitleClick={(item) => { |
| | | setUnitId(item.key); |
| | | actionRef.current.reload(); |
| | | onTitleClick={(e) => { |
| | | if (e && e.domEvent) { |
| | | e.domEvent.stopPropagation(); |
| | | } |
| | | setUnitId(item.id); |
| | | const values = form.getFieldsValue(); |
| | | actionRef.current.reload(values); |
| | | }} |
| | | > |
| | | {renderMenuItems(item.children)} |
| | | {renderMenuItems(item.children, level + 1)} |
| | | </SubMenu> |
| | | ); |
| | | } |
| | | return ( |
| | | <Menu.Item |
| | | onClick={(item) => { |
| | | setUnitId(item.key); |
| | | actionRef.current.reload(); |
| | | onClick={(e) => { |
| | | if (e && e.domEvent) { |
| | | e.domEvent.stopPropagation(); |
| | | } |
| | | const itemId = item.id || item.key; |
| | | setUnitId(itemId); |
| | | const values = form.getFieldsValue(); |
| | | actionRef.current.reload(values); |
| | | }} |
| | | key={item.key} |
| | | > |
| | |
| | | if (res.code == 200 && res.data) { |
| | | const traverseItems = (items) => { |
| | | return items.map((item) => { |
| | | if (!item.id) { |
| | | } |
| | | item.key = item.id; |
| | | item.title = '1'; |
| | | if (item.child && item.child.length > 0) { |
| | |
| | | return item; |
| | | }); |
| | | }; |
| | | setItems(traverseItems(res.data)); |
| | | const processedItems = traverseItems(res.data); |
| | | setItems(processedItems); |
| | | } |
| | | }); |
| | | }; |
| | |
| | | title: '联系方式', |
| | | dataIndex: 'phone', |
| | | }, |
| | | { |
| | | title: '所在单位', |
| | | dataIndex: 'departmentName', |
| | | hideInSearch: true, |
| | | renderFormItem: () => { |
| | | return ( |
| | | <Cascader |
| | | options={items} |
| | | fieldNames={{ value: 'key', label: 'name' }} |
| | | placeholder="请选择" |
| | | displayRender={(label) => label[label.length - 1]} |
| | | changeOnSelect={true} |
| | | /> |
| | | ); |
| | | }, |
| | | }, |
| | | { |
| | | hideInTable: true, |
| | | title: '所在单位', |
| | | dataIndex: 'departmentId', |
| | | renderFormItem: () => { |
| | | return ( |
| | | <Cascader |
| | | options={items} |
| | | fieldNames={{ value: 'key', label: 'name' }} |
| | | placeholder="请选择" |
| | | displayRender={(label) => label[label.length - 1]} |
| | | changeOnSelect={true} |
| | | /> |
| | | ); |
| | | }, |
| | | }, |
| | | // { |
| | | // title: '所在单位', |
| | | // dataIndex: 'departmentName', |
| | | // hideInSearch: true, |
| | | // renderFormItem: () => { |
| | | // return ( |
| | | // <Cascader |
| | | // options={items} |
| | | // fieldNames={{ value: 'key', label: 'name' }} |
| | | // placeholder="请选择" |
| | | // displayRender={(label) => label[label.length - 1]} |
| | | // changeOnSelect={true} |
| | | // /> |
| | | // ); |
| | | // }, |
| | | // }, |
| | | // { |
| | | // hideInTable: true, |
| | | // title: '所属权限', |
| | | // dataIndex: 'departmentId', |
| | | // renderFormItem: () => { |
| | | // return ( |
| | | // <Cascader |
| | | // options={items} |
| | | // fieldNames={{ value: 'key', label: 'name' }} |
| | | // placeholder="请选择" |
| | | // displayRender={(label) => label[label.length - 1]} |
| | | // changeOnSelect={true} |
| | | // /> |
| | | // ); |
| | | // }, |
| | | // }, |
| | | { |
| | | title: '所属职位', |
| | | dataIndex: 'systemPostName', |
| | | hideInSearch: true, |
| | | }, |
| | | { |
| | | title: '所属角色', |
| | | title: '后台权限', |
| | | dataIndex: 'systemRoleName', |
| | | hideInSearch: true, |
| | | }, |
| | | { |
| | | title: '所属角色', |
| | | title: '所属权限', |
| | | dataIndex: 'systemRoleId', |
| | | hideInTable: true, |
| | | renderFormItem: () => { |
| | |
| | | }, |
| | | }, |
| | | { |
| | | title: '账号层级', |
| | | title: '账号所属层级', |
| | | dataIndex: 'accountLevel', |
| | | // (1=市级账号,2=区县账号,3=街道账号,4=社区账号) |
| | | render: (text, record) => { |
| | | let role = ''; |
| | | switch (record.accountLevel) { |
| | | case 1: |
| | | role = '市'; |
| | | break; |
| | | case 2: |
| | | role = '区县'; |
| | | break; |
| | | case 3: |
| | | role = '街道'; |
| | | break; |
| | | case 4: |
| | | role = '社区'; |
| | | break; |
| | | case 5: |
| | | role = '党员'; |
| | | break; |
| | | default: |
| | | role = ''; |
| | | record.list.length>0&&record.list.map(item=>{ |
| | | item.str = '' |
| | | if(item.level==1){ |
| | | item.str = '市' |
| | | }else if(item.level==2){ |
| | | item.str = '区县' + '/' + item.districts |
| | | }else if(item.level==3){ |
| | | item.str = '街道' + '/' + item.districts + '/' + item.street |
| | | }else{ |
| | | item.str = '社区' + '/' + item.districts + '/' + item.street + '/' + item.community |
| | | } |
| | | return role; |
| | | return item.str; |
| | | }) |
| | | return record.list.map(item=>item.str).join('、'); |
| | | }, |
| | | valueEnum: { |
| | | 1: '市', |
| | |
| | | > |
| | | <div style={{ background: '#fff' }}> |
| | | <QueryFilter |
| | | form={form} |
| | | labelWidth={100} |
| | | onReset={(values) => { |
| | | fetchUnit(values); |
| | | setUnitId(''); |
| | |
| | | actionRef.current.reload(); |
| | | }} |
| | | > |
| | | <ProFormText name="name" label="单位名称" /> |
| | | <ProFormText name="name" label="组织结构名称" /> |
| | | <ProFormSelect name="type" label="筛选维度" options={[{ |
| | | label: '当前组织结构', |
| | | value: 1, |
| | | }, { |
| | | label: '当前及下级组织结构', |
| | | value: 2, |
| | | }]} /> |
| | | </QueryFilter> |
| | | </div> |
| | | <Row style={{ marginTop: 20, background: '#fff' }}> |
| | | <Col span={4}> |
| | | <Space style={{ margin: '10px 0' }}> |
| | | <span style={{ margin: '0 27px' }}>单位管理</span> |
| | | <Button |
| | | <span style={{ margin: '0 27px' }}>组织结构</span> |
| | | {/* <Button |
| | | type="primary" |
| | | onClick={() => { |
| | | addViewRef1.current.refreshData({}); |
| | |
| | | }} |
| | | > |
| | | 添加 |
| | | </Button> |
| | | </Button> */} |
| | | </Space> |
| | | |
| | | <Menu mode="inline">{renderMenuItems(items)}</Menu> |
| | | <Menu |
| | | mode="inline" |
| | | onClick={({ key, domEvent }) => { |
| | | domEvent.stopPropagation(); |
| | | }} |
| | | > |
| | | {renderMenuItems(items)} |
| | | </Menu> |
| | | </Col> |
| | | <Col span={20} style={{ minHeight: 650 }}> |
| | | <ProTable |
| | |
| | | if (params.departmentId) { |
| | | obj.departmentId = params.departmentId[params.departmentId.length - 1]; |
| | | } |
| | | if (form.getFieldValue('type')) { |
| | | obj.type = form.getFieldValue('type'); |
| | | } |
| | | return buildProTableDataSource(getList, obj); |
| | | }} |
| | | toolBarRender={(action, selectRows) => [ |
New file |
| | |
| | | .addAndEditModal{ |
| | | .ant-modal-body{ |
| | | padding: 0 !important; |
| | | } |
| | | } |
| | |
| | | method: 'PUT', |
| | | }); |
| | | } |
| | | |
| | | |
| | | //获取级联数据 |
| | | export const getCascaderData = async (data) => { |
| | | return request(`/api/huacheng-sangeshenbian/systemUser/getAdministrativeDivisionTwo`, { |
| | | method: 'GET', |
| | | }); |
| | | } |
| | |
| | | * @doc https://umijs.org/docs/max/request#配置 |
| | | */ |
| | | export const errorConfig: RequestConfig = { |
| | | baseURL: BASE_URL, |
| | | // baseURL: BASE_URL, |
| | | |
| | | // 请求拦截器 |
| | | requestInterceptors: [ |