| | |
| | | method: "get", |
| | | }); |
| | | } |
| | | // å¼å¸¸å¤çå表 |
| | | export function tracedeallist(data) { |
| | | return request({ |
| | | url: "/smartor/trace/tracedeallist", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // æ¹éå¤çå表 |
| | | export function tracelist(data) { |
| | | return request({ |
| | | url: "/smartor/trace/list", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // ä»»å¡å表修æ¹å¤ç |
| | | export function traceedit(data) { |
| | | return request({ |
| | | url: "/smartor/trace/edit", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | |
| | | data: data, |
| | | }); |
| | | } |
| | | // å»¶ç»æ¤çç»è®¡ |
| | | export function getContinueNerseCount(data) { |
| | | return request({ |
| | | url: "/smartor/serviceSubtask/getContinueNerseCount", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // 满æåº¦ç»è®¡è¯¦æ
|
| | | export function getSfStatisticsJoyInfo(data) { |
| | | return request({ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="audio-player"> |
| | | <!-- ä¸»ææ¾å¨ --> |
| | | <div class="audio-container" v-if="showDefaultPlayer"> |
| | | <audio |
| | | ref="audioElement" |
| | | :src="audioSource" |
| | | preload="metadata" |
| | | @timeupdate="updateTime" |
| | | @loadedmetadata="updateDuration" |
| | | @play="handlePlay" |
| | | @pause="handlePause" |
| | | @ended="handleEnded" |
| | | @error="handleError" |
| | | style="display: none" |
| | | ></audio> |
| | | |
| | | <!-- ææ¾æ§å¶ --> |
| | | <div class="audio-controls"> |
| | | <el-button |
| | | v-if="!isPlaying" |
| | | type="text" |
| | | icon="el-icon-video-play" |
| | | class="play-btn" |
| | | @click="playAudio" |
| | | :loading="loading" |
| | | ></el-button> |
| | | <el-button |
| | | v-else |
| | | type="text" |
| | | icon="el-icon-video-pause" |
| | | class="pause-btn" |
| | | @click="pauseAudio" |
| | | ></el-button> |
| | | |
| | | <!-- è¿åº¦æ¡ --> |
| | | <div class="progress-container" @click="seekAudio" @mousemove="updateHoverTime"> |
| | | <div class="progress-background"> |
| | | <!-- ç¼å²è¿åº¦ --> |
| | | <div |
| | | v-if="buffered.length > 0" |
| | | class="buffered-progress" |
| | | :style="{ width: `${bufferedPercent}%` }" |
| | | ></div> |
| | | |
| | | <!-- ææ¾è¿åº¦ --> |
| | | <div |
| | | class="played-progress" |
| | | :style="{ width: `${progressPercent}%` }" |
| | | ></div> |
| | | |
| | | <!-- æ¬åé¢è§ --> |
| | | <div |
| | | v-if="showHoverPreview" |
| | | class="hover-preview" |
| | | :style="{ left: `${hoverPercent}%` }" |
| | | > |
| | | <span class="hover-time">{{ formatTime(hoverTime) }}</span> |
| | | </div> |
| | | |
| | | <!-- ææ¾ç¹ --> |
| | | <div |
| | | class="playhead" |
| | | :style="{ left: `${progressPercent}%` }" |
| | | ></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æ¶é´æ¾ç¤º --> |
| | | <div class="time-display"> |
| | | <span class="current-time">{{ formatTime(currentTime) }}</span> |
| | | <span class="time-separator">/</span> |
| | | <span class="duration">{{ formatTime(duration) }}</span> |
| | | </div> |
| | | |
| | | <!-- é³éæ§å¶ --> |
| | | <div class="volume-control" v-if="showVolumeControl"> |
| | | <el-popover |
| | | placement="top" |
| | | width="40" |
| | | trigger="hover" |
| | | popper-class="volume-popover" |
| | | > |
| | | <div class="volume-slider-container" @click.stop> |
| | | <el-slider |
| | | v-model="volume" |
| | | vertical |
| | | height="100px" |
| | | :show-tooltip="false" |
| | | @input="changeVolume" |
| | | ></el-slider> |
| | | </div> |
| | | |
| | | <el-button |
| | | slot="reference" |
| | | type="text" |
| | | :icon="volumeIcon" |
| | | class="volume-btn" |
| | | @click.stop |
| | | ></el-button> |
| | | </el-popover> |
| | | </div> |
| | | |
| | | <!-- ææ¾é度 --> |
| | | <el-select |
| | | v-if="showPlaybackRate" |
| | | v-model="playbackRate" |
| | | size="mini" |
| | | class="playback-rate" |
| | | @change="changePlaybackRate" |
| | | > |
| | | <el-option |
| | | v-for="rate in playbackRates" |
| | | :key="rate" |
| | | :label="`${rate}x`" |
| | | :value="rate" |
| | | ></el-option> |
| | | </el-select> |
| | | |
| | | <!-- ä¸è½½æé® --> |
| | | <el-button |
| | | v-if="showDownload" |
| | | type="text" |
| | | icon="el-icon-download" |
| | | class="download-btn" |
| | | @click="downloadAudio" |
| | | title="ä¸è½½é³é¢" |
| | | ></el-button> |
| | | |
| | | <!-- æ´å¤åè½æé® --> |
| | | <el-dropdown |
| | | v-if="showMoreOptions" |
| | | trigger="click" |
| | | class="more-options" |
| | | > |
| | | <el-button type="text" icon="el-icon-more" class="more-btn"></el-button> |
| | | <el-dropdown-menu slot="dropdown"> |
| | | <el-dropdown-item @click.native="loopAudio = !loopAudio"> |
| | | <i :class="loopAudio ? 'el-icon-refresh' : 'el-icon-refresh-left'"></i> |
| | | {{ loopAudio ? 'å
³é循ç¯' : 'å¼å¯å¾ªç¯' }} |
| | | </el-dropdown-item> |
| | | <el-dropdown-item @click.native="resetAudio"> |
| | | <i class="el-icon-refresh-right"></i> |
| | | éç½® |
| | | </el-dropdown-item> |
| | | <el-dropdown-item v-if="showDetails" @click.native="showAudioInfo"> |
| | | <i class="el-icon-info"></i> |
| | | é³é¢ä¿¡æ¯ |
| | | </el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </el-dropdown> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- é³é¢ä¿¡æ¯å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | v-if="showDetailsDialog" |
| | | title="é³é¢ä¿¡æ¯" |
| | | :visible.sync="showDetailsDialog" |
| | | width="400px" |
| | | > |
| | | <div class="audio-info"> |
| | | <div class="info-item"> |
| | | <span class="label">æä»¶å¤§å°ï¼</span> |
| | | <span class="value">{{ fileSize }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">é³é¢æ¶é¿ï¼</span> |
| | | <span class="value">{{ formatTime(duration) }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">æä»¶æ ¼å¼ï¼</span> |
| | | <span class="value">{{ audioFormat }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">é³é¢å°åï¼</span> |
| | | <a :href="audioSource" target="_blank" class="audio-url">{{ truncateUrl(audioSource) }}</a> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- ç®æææ¾å¨ï¼ä»
æ¾ç¤ºææ¾æé®ï¼ --> |
| | | <div v-else class="simple-player"> |
| | | <el-button |
| | | v-if="!isPlaying" |
| | | type="text" |
| | | icon="el-icon-video-play" |
| | | size="small" |
| | | class="simple-play-btn" |
| | | @click="playAudio" |
| | | :loading="loading" |
| | | > |
| | | ææ¾å½é³ |
| | | </el-button> |
| | | <el-button |
| | | v-else |
| | | type="text" |
| | | icon="el-icon-video-pause" |
| | | size="small" |
| | | class="simple-pause-btn" |
| | | @click="pauseAudio" |
| | | > |
| | | æåææ¾ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'AudioPlayer', |
| | | props: { |
| | | // é³é¢æºå°å |
| | | audioSource: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºå®æ´ææ¾å¨ |
| | | showDefaultPlayer: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºé³éæ§å¶ |
| | | showVolumeControl: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºææ¾é度æ§å¶ |
| | | showPlaybackRate: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºä¸è½½æé® |
| | | showDownload: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºæ´å¤é项 |
| | | showMoreOptions: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // æ¯å¦æ¾ç¤ºé³é¢ä¿¡æ¯ |
| | | showDetails: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | |
| | | // åå§é³éï¼0-100ï¼ |
| | | initialVolume: { |
| | | type: Number, |
| | | default: 80, |
| | | validator: (value) => value >= 0 && value <= 100 |
| | | }, |
| | | |
| | | // åå§ææ¾é度 |
| | | initialPlaybackRate: { |
| | | type: Number, |
| | | default: 1.0 |
| | | }, |
| | | |
| | | // èªå¨ææ¾ |
| | | autoplay: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | |
| | | // å¾ªç¯ææ¾ |
| | | loop: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | // ææ¾ç¶æ |
| | | isPlaying: false, |
| | | isLoading: false, |
| | | loading: false, |
| | | error: false, |
| | | |
| | | // æ¶é´ç¸å
³ |
| | | currentTime: 0, |
| | | duration: 0, |
| | | buffered: [], |
| | | |
| | | // é³é |
| | | volume: this.initialVolume, |
| | | isMuted: false, |
| | | |
| | | // ææ¾é度 |
| | | playbackRate: this.initialPlaybackRate, |
| | | playbackRates: [0.5, 0.75, 1.0, 1.25, 1.5, 2.0], |
| | | |
| | | // å¾ªç¯ææ¾ |
| | | loopAudio: this.loop, |
| | | |
| | | // è¿åº¦æ¡æ¬å |
| | | hoverTime: 0, |
| | | hoverPercent: 0, |
| | | showHoverPreview: false, |
| | | |
| | | // é³é¢ä¿¡æ¯ |
| | | showDetailsDialog: false, |
| | | fileSize: 'æªç¥', |
| | | audioFormat: 'æªç¥', |
| | | |
| | | // çå¬å¨å¼ç¨ |
| | | resizeObserver: null |
| | | }; |
| | | }, |
| | | |
| | | computed: { |
| | | // ææ¾è¿åº¦ç¾åæ¯ |
| | | progressPercent() { |
| | | if (this.duration <= 0) return 0; |
| | | return (this.currentTime / this.duration) * 100; |
| | | }, |
| | | |
| | | // ç¼å²è¿åº¦ç¾åæ¯ |
| | | bufferedPercent() { |
| | | if (this.duration <= 0) return 0; |
| | | if (this.buffered.length === 0) return 0; |
| | | |
| | | const bufferedEnd = this.buffered.end(this.buffered.length - 1); |
| | | return (bufferedEnd / this.duration) * 100; |
| | | }, |
| | | |
| | | // é³é徿 |
| | | volumeIcon() { |
| | | if (this.isMuted || this.volume === 0) { |
| | | return 'el-icon-turn-off-microphone'; |
| | | } else if (this.volume <= 30) { |
| | | return 'el-icon-microphone'; |
| | | } else if (this.volume <= 70) { |
| | | return 'el-icon-microphone'; |
| | | } else { |
| | | return 'el-icon-microphone'; |
| | | } |
| | | } |
| | | }, |
| | | |
| | | watch: { |
| | | // çå¬é³é¢æºåå |
| | | audioSource(newSource) { |
| | | if (newSource) { |
| | | this.resetAudio(); |
| | | this.loadAudio(); |
| | | } |
| | | }, |
| | | |
| | | // çå¬èªå¨ææ¾ |
| | | autoplay(newVal) { |
| | | if (newVal && this.audioSource) { |
| | | this.playAudio(); |
| | | } |
| | | }, |
| | | |
| | | // çå¬å¾ªç¯ææ¾ |
| | | loopAudio(newVal) { |
| | | this.$emit('loop-change', newVal); |
| | | } |
| | | }, |
| | | |
| | | mounted() { |
| | | if (this.audioSource) { |
| | | this.loadAudio(); |
| | | } |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | this.resizeObserver = new ResizeObserver(() => { |
| | | this.updateBuffered(); |
| | | }); |
| | | |
| | | if (this.$refs.audioElement) { |
| | | this.resizeObserver.observe(this.$refs.audioElement); |
| | | } |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | if (this.resizeObserver) { |
| | | this.resizeObserver.disconnect(); |
| | | } |
| | | |
| | | // 忢é³é¢ææ¾ |
| | | this.pauseAudio(); |
| | | }, |
| | | |
| | | methods: { |
| | | // å è½½é³é¢ |
| | | async loadAudio() { |
| | | this.loading = true; |
| | | this.error = false; |
| | | |
| | | try { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | // 设置é³é |
| | | audio.volume = this.volume / 100; |
| | | audio.playbackRate = this.playbackRate; |
| | | audio.loop = this.loopAudio; |
| | | |
| | | // å°è¯è·åæä»¶ä¿¡æ¯ |
| | | this.getAudioInfo(); |
| | | |
| | | // å¦æè®¾ç½®äºèªå¨ææ¾ï¼å¼å§ææ¾ |
| | | if (this.autoplay) { |
| | | await this.playAudio(); |
| | | } |
| | | } catch (error) { |
| | | console.error('å è½½é³é¢å¤±è´¥:', error); |
| | | this.error = true; |
| | | this.$emit('error', error); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // ææ¾é³é¢ |
| | | async playAudio() { |
| | | try { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | await audio.play(); |
| | | this.isPlaying = true; |
| | | this.$emit('play'); |
| | | } catch (error) { |
| | | console.error('ææ¾å¤±è´¥:', error); |
| | | this.error = true; |
| | | this.isPlaying = false; |
| | | this.$emit('error', error); |
| | | |
| | | // 妿æ¯ç¨æ·äº¤äºå¼èµ·çéè¯¯ï¼æç¤ºç¨æ· |
| | | if (error.name === 'NotAllowedError') { |
| | | this.$message.warning('请æå¨ç¹å»ææ¾æé®å¼å§ææ¾'); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // æåé³é¢ |
| | | pauseAudio() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | audio.pause(); |
| | | this.isPlaying = false; |
| | | this.$emit('pause'); |
| | | }, |
| | | |
| | | // è·³è½¬å°æå®æ¶é´ |
| | | seekAudio(event) { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio || this.duration <= 0) return; |
| | | |
| | | const progressContainer = event.currentTarget; |
| | | const rect = progressContainer.getBoundingClientRect(); |
| | | const x = event.clientX - rect.left; |
| | | const width = rect.width; |
| | | |
| | | const percent = Math.max(0, Math.min(1, x / width)); |
| | | const time = percent * this.duration; |
| | | |
| | | audio.currentTime = time; |
| | | this.currentTime = time; |
| | | this.$emit('seek', time); |
| | | }, |
| | | |
| | | // æ´æ°æ¬åæ¶é´ |
| | | updateHoverTime(event) { |
| | | const progressContainer = event.currentTarget; |
| | | const rect = progressContainer.getBoundingClientRect(); |
| | | const x = event.clientX - rect.left; |
| | | const width = rect.width; |
| | | |
| | | const percent = Math.max(0, Math.min(1, x / width)); |
| | | this.hoverPercent = percent * 100; |
| | | this.hoverTime = percent * this.duration; |
| | | this.showHoverPreview = true; |
| | | }, |
| | | |
| | | // éèæ¬åé¢è§ |
| | | hideHoverPreview() { |
| | | this.showHoverPreview = false; |
| | | }, |
| | | |
| | | // æ´æ°æ¶é´ |
| | | updateTime() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | this.currentTime = audio.currentTime; |
| | | this.$emit('timeupdate', this.currentTime); |
| | | }, |
| | | |
| | | // æ´æ°æ»æ¶é¿ |
| | | updateDuration() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | this.duration = audio.duration; |
| | | this.updateBuffered(); |
| | | this.$emit('loadedmetadata', this.duration); |
| | | }, |
| | | |
| | | // æ´æ°ç¼å²æ°æ® |
| | | updateBuffered() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | this.buffered = audio.buffered; |
| | | }, |
| | | |
| | | // å¤çææ¾ |
| | | handlePlay() { |
| | | this.isPlaying = true; |
| | | this.$emit('play'); |
| | | }, |
| | | |
| | | // å¤çæå |
| | | handlePause() { |
| | | this.isPlaying = false; |
| | | this.$emit('pause'); |
| | | }, |
| | | |
| | | // å¤çææ¾ç»æ |
| | | handleEnded() { |
| | | this.isPlaying = false; |
| | | this.currentTime = 0; |
| | | this.$emit('ended'); |
| | | }, |
| | | |
| | | // å¤çé误 |
| | | handleError(event) { |
| | | console.error('é³é¢ææ¾é误:', event); |
| | | this.error = true; |
| | | this.isPlaying = false; |
| | | this.$emit('error', event); |
| | | }, |
| | | |
| | | // æ¹åé³é |
| | | changeVolume(value) { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | audio.volume = value / 100; |
| | | this.volume = value; |
| | | this.isMuted = value === 0; |
| | | this.$emit('volume-change', value); |
| | | }, |
| | | |
| | | // éé³/åæ¶éé³ |
| | | toggleMute() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | this.isMuted = !this.isMuted; |
| | | audio.muted = this.isMuted; |
| | | this.$emit('mute-change', this.isMuted); |
| | | }, |
| | | |
| | | // æ¹åææ¾é度 |
| | | changePlaybackRate(rate) { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | audio.playbackRate = rate; |
| | | this.playbackRate = rate; |
| | | this.$emit('playbackrate-change', rate); |
| | | }, |
| | | |
| | | // éç½®é³é¢ |
| | | resetAudio() { |
| | | const audio = this.$refs.audioElement; |
| | | if (!audio) return; |
| | | |
| | | audio.currentTime = 0; |
| | | this.currentTime = 0; |
| | | this.pauseAudio(); |
| | | this.$emit('reset'); |
| | | }, |
| | | |
| | | // ä¸è½½é³é¢ |
| | | downloadAudio() { |
| | | if (!this.audioSource) { |
| | | this.$message.warning('é³é¢å°åæ æ'); |
| | | return; |
| | | } |
| | | |
| | | const link = document.createElement('a'); |
| | | link.href = this.audioSource; |
| | | link.download = this.getFileNameFromUrl(this.audioSource); |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | |
| | | this.$emit('download'); |
| | | }, |
| | | |
| | | // æ¾ç¤ºé³é¢ä¿¡æ¯ |
| | | async showAudioInfo() { |
| | | this.showDetailsDialog = true; |
| | | await this.getAudioInfo(); |
| | | }, |
| | | |
| | | // è·åé³é¢ä¿¡æ¯ |
| | | async getAudioInfo() { |
| | | if (!this.audioSource) return; |
| | | |
| | | try { |
| | | // è·åæä»¶å¤§å° |
| | | const response = await fetch(this.audioSource, { method: 'HEAD' }); |
| | | const contentLength = response.headers.get('content-length'); |
| | | |
| | | if (contentLength) { |
| | | this.fileSize = this.formatFileSize(contentLength); |
| | | } |
| | | |
| | | // è·åæä»¶æ ¼å¼ |
| | | const url = this.audioSource.toLowerCase(); |
| | | if (url.endsWith('.mp3')) this.audioFormat = 'MP3'; |
| | | else if (url.endsWith('.wav')) this.audioFormat = 'WAV'; |
| | | else if (url.endsWith('.ogg')) this.audioFormat = 'OGG'; |
| | | else if (url.endsWith('.m4a')) this.audioFormat = 'M4A'; |
| | | else if (url.endsWith('.aac')) this.audioFormat = 'AAC'; |
| | | else this.audioFormat = 'æªç¥æ ¼å¼'; |
| | | |
| | | } catch (error) { |
| | | console.error('è·åé³é¢ä¿¡æ¯å¤±è´¥:', error); |
| | | this.fileSize = 'æªç¥'; |
| | | this.audioFormat = 'æªç¥'; |
| | | } |
| | | }, |
| | | |
| | | // æ ¼å¼åæ¶é´ |
| | | formatTime(time) { |
| | | if (isNaN(time) || time < 0) return '00:00'; |
| | | |
| | | const hours = Math.floor(time / 3600); |
| | | const minutes = Math.floor((time % 3600) / 60); |
| | | const seconds = Math.floor(time % 60); |
| | | |
| | | if (hours > 0) { |
| | | return `${hours.toString().padStart(2, '0')}:${minutes |
| | | .toString() |
| | | .padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; |
| | | } else { |
| | | return `${minutes.toString().padStart(2, '0')}:${seconds |
| | | .toString() |
| | | .padStart(2, '0')}`; |
| | | } |
| | | }, |
| | | |
| | | // æ ¼å¼åæä»¶å¤§å° |
| | | formatFileSize(bytes) { |
| | | if (bytes === 0) return '0 B'; |
| | | |
| | | const k = 1024; |
| | | const sizes = ['B', 'KB', 'MB', 'GB']; |
| | | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| | | |
| | | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
| | | }, |
| | | |
| | | // æªæURLæ¾ç¤º |
| | | truncateUrl(url, maxLength = 40) { |
| | | if (!url) return ''; |
| | | if (url.length <= maxLength) return url; |
| | | |
| | | const start = url.substring(0, maxLength / 2 - 3); |
| | | const end = url.substring(url.length - maxLength / 2 + 3); |
| | | return start + '...' + end; |
| | | }, |
| | | |
| | | // ä»URLè·åæä»¶å |
| | | getFileNameFromUrl(url) { |
| | | if (!url) return 'audio'; |
| | | |
| | | const fileName = url.split('/').pop(); |
| | | return fileName || 'audio'; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .audio-player { |
| | | width: 100%; |
| | | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; |
| | | |
| | | .audio-container { |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | padding: 12px 16px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | border: 1px solid #ebeef5; |
| | | |
| | | .audio-controls { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | |
| | | .play-btn, |
| | | .pause-btn { |
| | | width: 36px; |
| | | height: 36px; |
| | | padding: 0; |
| | | font-size: 20px; |
| | | color: #409EFF; |
| | | |
| | | &:hover { |
| | | color: #66b1ff; |
| | | } |
| | | |
| | | &:active { |
| | | color: #3a8ee6; |
| | | } |
| | | } |
| | | |
| | | .progress-container { |
| | | flex: 1; |
| | | height: 36px; |
| | | display: flex; |
| | | align-items: center; |
| | | cursor: pointer; |
| | | |
| | | .progress-background { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 6px; |
| | | background: #e4e7ed; |
| | | border-radius: 3px; |
| | | overflow: visible; |
| | | |
| | | .buffered-progress { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | height: 100%; |
| | | background: #c0c4cc; |
| | | border-radius: 3px; |
| | | transition: width 0.2s ease; |
| | | } |
| | | |
| | | .played-progress { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | height: 100%; |
| | | background: #409EFF; |
| | | border-radius: 3px; |
| | | transition: width 0.2s ease; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .hover-preview { |
| | | position: absolute; |
| | | top: -30px; |
| | | width: 1px; |
| | | height: 20px; |
| | | background: #909399; |
| | | transform: translateX(-50%); |
| | | z-index: 3; |
| | | pointer-events: none; |
| | | |
| | | .hover-time { |
| | | position: absolute; |
| | | bottom: 100%; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | background: rgba(0, 0, 0, 0.8); |
| | | color: white; |
| | | padding: 2px 6px; |
| | | border-radius: 3px; |
| | | font-size: 12px; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | &::after { |
| | | content: ''; |
| | | position: absolute; |
| | | top: 100%; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | width: 0; |
| | | height: 0; |
| | | border-left: 4px solid transparent; |
| | | border-right: 4px solid transparent; |
| | | border-top: 4px solid rgba(0, 0, 0, 0.8); |
| | | } |
| | | } |
| | | |
| | | .playhead { |
| | | position: absolute; |
| | | top: 50%; |
| | | width: 12px; |
| | | height: 12px; |
| | | background: #fff; |
| | | border: 2px solid #409EFF; |
| | | border-radius: 50%; |
| | | transform: translate(-50%, -50%); |
| | | z-index: 4; |
| | | cursor: pointer; |
| | | transition: all 0.2s ease; |
| | | |
| | | &:hover { |
| | | transform: translate(-50%, -50%) scale(1.2); |
| | | box-shadow: 0 0 8px rgba(64, 158, 255, 0.5); |
| | | } |
| | | } |
| | | } |
| | | |
| | | &:hover { |
| | | .progress-background { |
| | | height: 8px; |
| | | |
| | | .playhead { |
| | | transform: translate(-50%, -50%) scale(1.1); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .time-display { |
| | | min-width: 100px; |
| | | font-size: 12px; |
| | | color: #606266; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 2px; |
| | | |
| | | .current-time { |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .time-separator { |
| | | opacity: 0.6; |
| | | } |
| | | |
| | | .duration { |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | |
| | | .volume-control { |
| | | .volume-btn { |
| | | padding: 0; |
| | | width: 24px; |
| | | height: 24px; |
| | | font-size: 16px; |
| | | color: #606266; |
| | | |
| | | &:hover { |
| | | color: #409EFF; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .playback-rate { |
| | | width: 60px; |
| | | |
| | | ::v-deep .el-input__inner { |
| | | height: 24px; |
| | | line-height: 24px; |
| | | padding: 0 5px; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .download-btn, |
| | | .more-btn { |
| | | padding: 0; |
| | | width: 24px; |
| | | height: 24px; |
| | | font-size: 16px; |
| | | color: #606266; |
| | | |
| | | &:hover { |
| | | color: #409EFF; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .simple-player { |
| | | .simple-play-btn, |
| | | .simple-pause-btn { |
| | | padding: 4px 8px; |
| | | font-size: 12px; |
| | | color: #409EFF; |
| | | |
| | | &:hover { |
| | | color: #66b1ff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .audio-info { |
| | | .info-item { |
| | | margin-bottom: 12px; |
| | | display: flex; |
| | | |
| | | .label { |
| | | width: 80px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | } |
| | | |
| | | .value { |
| | | flex: 1; |
| | | color: #606266; |
| | | } |
| | | |
| | | .audio-url { |
| | | color: #409EFF; |
| | | text-decoration: none; |
| | | |
| | | &:hover { |
| | | text-decoration: underline; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // é³éå¼¹çªæ ·å¼ |
| | | ::v-deep .volume-popover { |
| | | padding: 10px; |
| | | min-width: 40px; |
| | | |
| | | .volume-slider-container { |
| | | height: 100px; |
| | | display: flex; |
| | | justify-content: center; |
| | | |
| | | .el-slider { |
| | | height: 100%; |
| | | |
| | | .el-slider__runway { |
| | | background: #e4e7ed; |
| | | } |
| | | |
| | | .el-slider__bar { |
| | | background: #409EFF; |
| | | } |
| | | |
| | | .el-slider__button { |
| | | border-color: #409EFF; |
| | | width: 12px; |
| | | height: 12px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | icon="el-icon-check" |
| | | @click="handleBatchSubmit" |
| | | :loading="batchProcessing" |
| | | :disabled="selectedExceptionIds.length === 0" |
| | | > |
| | | æ¹éæäº¤å¤ç |
| | | æ¹éæäº¤å¤ç ({{ selectedExceptionIds.length }}) |
| | | </el-button> |
| | | <el-button type="warning" icon="el-icon-back" @click="handleGoBack"> |
| | | è¿åå¼å¸¸å表 |
| | |
| | | > |
| | | <el-form-item label="è´è´£ç§å®¤"> |
| | | <el-select |
| | | v-model="filterParams.deptId" |
| | | v-model="filterParams.todeptcode" |
| | | placeholder="è¯·éæ©ç§å®¤" |
| | | clearable |
| | | filterable |
| | | style="width: 200px" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å¤çç¶æ"> |
| | | <el-select |
| | | v-model="filterParams.status" |
| | | v-model="filterParams.handleFlag" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | clearable |
| | | style="width: 200px" |
| | | > |
| | | <el-option label="å¾
å¤ç" :value="0" /> |
| | | <el-option label="å¤çä¸" :value="1" /> |
| | | <el-option label="å·²å¤ç" :value="2" /> |
| | | <el-option label="æªå¤ç" :value="'0'" /> |
| | | <el-option label="å·²å¤ç" :value="'1'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="满æåº¦ç±»å"> |
| | | <el-select |
| | | v-model="filterParams.templateType" |
| | | placeholder="è¯·éæ©æ¨¡æ¿ç±»å" |
| | | clearable |
| | | style="width: 200px" |
| | | > |
| | | <el-option label="è¯é³æ¨¡æ¿" :value="1" /> |
| | | <el-option label="é®å·æ¨¡æ¿" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | |
| | | :border="true" |
| | | style="width: 100%" |
| | | @selection-change="handleSelectionChange" |
| | | row-key="id" |
| | | class="exception-table" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | |
| | | |
| | | <el-table-column |
| | | label="è´è´£ç§å®¤" |
| | | prop="responsibilityDept" |
| | | width="120" |
| | | prop="todeptname" |
| | | width="200" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <el-tag type="primary">{{ row.responsibilityDept }}</el-tag> |
| | | <el-tag type="primary" v-if="row.todeptname">{{ |
| | | row.todeptname |
| | | }}</el-tag> |
| | | <span v-else class="no-data">æªåé
</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="䏿»¡æè¯¦æ
" |
| | | prop="unsatisfactoryDetail" |
| | | min-width="200" |
| | | align="center" |
| | | > |
| | | <el-table-column label="䏿»¡æè¯¦æ
" min-width="250" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="detail-content"> |
| | | {{ row.unsatisfactoryDetail }} |
| | | <div class="question-text"> |
| | | <strong>é®é¢ï¼</strong>{{ row.questiontext }} |
| | | </div> |
| | | <div class="answer-text"> |
| | | <strong>åçï¼</strong>{{ row.asrtext || "æ åç" }} |
| | | </div> |
| | | <div class="matched-text" v-if="row.matchedtext"> |
| | | <strong>è§£æå¼ï¼</strong>{{ row.matchedtext }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-table-column label="æ£è
ä¿¡æ¯" width="300" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="patient-info"> |
| | | <div class="patient-item"> |
| | | <span class="label">å§åï¼</span> |
| | | <span class="value">{{ row.patientName }}</span> |
| | | <div class="patient-row"> |
| | | <div class="patient-item"> |
| | | <span class="label">å§åï¼</span> |
| | | <span class="value">{{ row.patdescJson.sendname }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">æ§å«ï¼</span> |
| | | <span class="value">{{ |
| | | row.patdescJson.sex |
| | | }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">å¹´é¾ï¼</span> |
| | | <span class="value">{{ row.patdescJson.age }}å²</span> |
| | | </div> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">æ§å«ï¼</span> |
| | | <span class="value">{{ |
| | | row.gender === 1 ? "ç·" : "女" |
| | | }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">å¹´é¾ï¼</span> |
| | | <span class="value">{{ row.age }}å²</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">çµè¯ï¼</span> |
| | | <span class="value">{{ row.phone }}</span> |
| | | <div class="patient-row"> |
| | | <div class="patient-item full-width"> |
| | | <span class="label">çµè¯ï¼</span> |
| | | <span class="value">{{ row.patdescJson.phone }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="åºé¢ä¿¡æ¯" width="250" align="center"> |
| | | <el-table-column label="å¡«åä¿¡æ¯" width="180" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="discharge-info"> |
| | | <div class="fill-info"> |
| | | <div class="info-item"> |
| | | <span class="label">ç§å®¤ï¼</span> |
| | | <span class="value">{{ row.dischargeDept }}</span> |
| | | <span class="label">å¡«æ¥æ¶é´ï¼</span> |
| | | <span class="value time">{{ |
| | | formatDateTime(row.createTime) |
| | | }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">ç
åºï¼</span> |
| | | <span class="value">{{ row.dischargeWard }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">å¡«åæ¶é´ï¼</span> |
| | | <span class="value time">{{ row.fillTime }}</span> |
| | | <div v-if="row.recordurl" class="info-item"> |
| | | <el-button |
| | | type="text" |
| | | size="small" |
| | | @click="handlePlayAudio(row.recordurl)" |
| | | icon="el-icon-headset" |
| | | > |
| | | ææ¾å½é³ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | <el-table-column |
| | | label="å¤çç¶æ" |
| | | prop="processStatus" |
| | | prop="handleFlag" |
| | | width="100" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <el-tag :type="getStatusTagType(row.processStatus)" effect="dark"> |
| | | {{ getStatusText(row.processStatus) }} |
| | | <el-tag :type="getStatusTagType(row.handleFlag)" effect="dark"> |
| | | {{ getStatusText(row.handleFlag) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="ææ°å¤çä¿¡æ¯" width="180" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.handleTime" class="handle-info"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤ç人ï¼</span> |
| | | <span class="value">{{ row.handleBy || "ç³»ç»" }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">å¤çæ¶é´ï¼</span> |
| | | <span class="value time">{{ |
| | | formatDateTime(row.handleTime) |
| | | }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">å¤ç说æï¼</span> |
| | | <span class="value">{{ row.handledesc }}</span> |
| | | </div> |
| | | </div> |
| | | <span v-else class="no-data">æªå¤ç</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | |
| | | size="small" |
| | | icon="el-icon-edit" |
| | | @click="handleProcess(row)" |
| | | :disabled="row.processStatus === 2" |
| | | :disabled="row.handleFlag === '1'" |
| | | > |
| | | å¤ç |
| | | </el-button> |
| | |
| | | label-width="100px" |
| | | size="medium" |
| | | > |
| | | <el-form-item label="å¤çç¶æ" prop="status"> |
| | | <el-form-item label="å¤çç¶æ" prop="handleFlag"> |
| | | <el-select |
| | | v-model="processForm.status" |
| | | v-model="processForm.handleFlag" |
| | | placeholder="è¯·éæ©å¤çç¶æ" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="å¤çä¸" :value="1" /> |
| | | <el-option label="å·²å¤ç" :value="2" /> |
| | | <el-option label="已驳å" :value="3" /> |
| | | <el-option label="å·²å¤ç" :value="'1'" /> |
| | | <el-option label="åæ¶å¤ç" :value="'0'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="æ¥å¤ç§å®¤" prop="reportDepts"> |
| | | <el-form-item label="æ¥å¤ç§å®¤" prop="ccdepts"> |
| | | <el-select |
| | | v-model="processForm.reportDepts" |
| | | v-model="processForm.ccdepts" |
| | | placeholder="è¯·éæ©æ¥å¤ç§å®¤" |
| | | multiple |
| | | filterable |
| | | collapse-tags |
| | | style="width: 100%" |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç夿³¨" prop="remark"> |
| | | <el-form-item label="å¤çç»æ" prop="handleresult"> |
| | | <el-select |
| | | v-model="processForm.handleresult" |
| | | placeholder="è¯·éæ©å¤çç»æ" |
| | | style="width: 100%" |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | > |
| | | <el-option label="已解å³" value="resolved" /> |
| | | <el-option label="已解é" value="explained" /> |
| | | <el-option label="已转交" value="transferred" /> |
| | | <el-option label="éæ¹è¿" value="improvement" /> |
| | | <el-option label="已驳å" value="rejected" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç说æ" prop="handledesc"> |
| | | <el-input |
| | | v-model="processForm.remark" |
| | | v-model="processForm.handledesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¤ç夿³¨ï¼æå¤500åï¼" |
| | | placeholder="请è¾å
¥å¤ç说æï¼æå¤500åï¼" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="éä»¶ä¸ä¼ "> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :on-preview="handlePreview" |
| | | :on-remove="handleRemove" |
| | | :before-remove="beforeRemove" |
| | | :limit="3" |
| | | :on-exceed="handleExceed" |
| | | :file-list="fileList" |
| | | > |
| | | <el-button size="small" type="primary">ç¹å»ä¸ä¼ </el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | æ¯æä¸ä¼ å¾çãææ¡£çéä»¶ï¼å个æä»¶ä¸è¶
è¿10MB |
| | | </div> |
| | | </el-upload> |
| | | <el-form-item |
| | | label="æç»æè§" |
| | | prop="finaloption" |
| | | v-if="hasQualityPermission" |
| | | > |
| | | <el-input |
| | | v-model="processForm.finaloption" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥æç»å¤çæè§ï¼æå¤300åï¼" |
| | | maxlength="300" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <span slot="footer" class="dialog-footer"> |
| | |
| | | label-width="100px" |
| | | size="medium" |
| | | > |
| | | <el-form-item label="å¤çç¶æ" prop="status"> |
| | | <el-form-item label="å¤çç¶æ" prop="handleFlag"> |
| | | <el-select |
| | | v-model="batchProcessForm.status" |
| | | v-model="batchProcessForm.handleFlag" |
| | | placeholder="è¯·éæ©å¤çç¶æ" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="å¤çä¸" :value="1" /> |
| | | <el-option label="å·²å¤ç" :value="2" /> |
| | | <el-option label="已驳å" :value="3" /> |
| | | <el-option label="å·²å¤ç" :value="'1'" /> |
| | | <el-option label="åæ¶å¤ç" :value="'0'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="æ¥å¤ç§å®¤" prop="reportDepts"> |
| | | <el-form-item label="æ¥å¤ç§å®¤" prop="ccdepts"> |
| | | <el-select |
| | | v-model="batchProcessForm.reportDepts" |
| | | v-model="batchProcessForm.ccdepts" |
| | | placeholder="è¯·éæ©æ¥å¤ç§å®¤" |
| | | multiple |
| | | filterable |
| | | collapse-tags |
| | | style="width: 100%" |
| | | :disabled="batchProcessForm.handleFlag !== '1'" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç夿³¨" prop="remark"> |
| | | <el-form-item label="å¤çç»æ" prop="handleresult"> |
| | | <el-select |
| | | v-model="batchProcessForm.handleresult" |
| | | placeholder="è¯·éæ©å¤çç»æ" |
| | | style="width: 100%" |
| | | :disabled="batchProcessForm.handleFlag !== '1'" |
| | | > |
| | | <el-option label="已解å³" value="resolved" /> |
| | | <el-option label="已解é" value="explained" /> |
| | | <el-option label="已转交" value="transferred" /> |
| | | <el-option label="éæ¹è¿" value="improvement" /> |
| | | <el-option label="已驳å" value="rejected" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç说æ" prop="handledesc"> |
| | | <el-input |
| | | v-model="batchProcessForm.remark" |
| | | v-model="batchProcessForm.handledesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¤ç夿³¨ï¼æå¤500åï¼" |
| | | placeholder="请è¾å
¥å¤ç说æï¼æå¤500åï¼" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :disabled="batchProcessForm.handleFlag !== '1'" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | @click="submitBatchProcess" |
| | | :loading="batchProcessing" |
| | | > |
| | | æ¹éæäº¤ |
| | | æ¹éæäº¤ ({{ selectedExceptionIds.length }}) |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <!-- è¿åº¦å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="æ¹éå¤çè¿åº¦" |
| | | :visible.sync="batchProgress.visible" |
| | | width="400px" |
| | | :close-on-click-modal="false" |
| | | :show-close="false" |
| | | :close-on-press-escape="false" |
| | | > |
| | | <div class="progress-content"> |
| | | <el-progress |
| | | :percentage="batchProgress.percentage" |
| | | :status="batchProgress.percentage === 100 ? 'success' : ''" |
| | | /> |
| | | <div class="progress-info"> |
| | | å·²å¤ç {{ batchProgress.processed }}/{{ batchProgress.total }} æ¡è®°å½ |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- å¼å¸¸è¯¦æ
å¼¹æ¡ --> |
| | | <Details-anomaly |
| | | :visible="detailDialogVisible" |
| | | :record-id="selectedRecordId" |
| | | :title="detailDialogTitle" |
| | | :record-data="selectedRecordData" |
| | | @update:visible="handleDetailDialogClose" |
| | | @processed="handleProcessed" |
| | | @close="handleDetailDialogClose" |
| | | /> |
| | | |
| | | <!-- å½é³ææ¾å¨ --> |
| | | <audio |
| | | v-if="audioUrl" |
| | | :src="audioUrl" |
| | | ref="audioPlayer" |
| | | controls |
| | | style="display: none" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import DetailsAnomaly from "./components/DetailsAnomaly.vue"; |
| | | import { tracelist, traceedit } from "@/api/AiCentre/index"; |
| | | import dayjs from "dayjs"; |
| | | import { deptTreeSelect } from "@/api/system/user"; |
| | | |
| | | export default { |
| | | name: "BatchProcess", |
| | | components: { |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | // æ·»å 以䏿°æ® |
| | | // 详æ
å¼¹æ¡ç¸å
³ |
| | | detailDialogVisible: false, |
| | | selectedRecordId: null, |
| | | selectedRecordData: null, |
| | | detailDialogTitle: "å¼å¸¸åé¦è¯¦æ
", |
| | | |
| | | // é³é¢ææ¾ |
| | | audioUrl: "", |
| | | |
| | | // å½åå¤ççå¼å¸¸ID |
| | | currentExceptionId: null, |
| | | |
| | |
| | | |
| | | // è¿æ»¤åæ° |
| | | filterParams: { |
| | | deptId: "", |
| | | status: "", |
| | | todeptcode: "", |
| | | handleFlag: "", |
| | | templateType: null, |
| | | scriptids: null, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }, |
| | |
| | | processing: false, |
| | | batchProcessing: false, |
| | | |
| | | // æéæ§å¶ |
| | | hasQualityPermission: false, // æ¯å¦å
·æè´¨ç®¡æé |
| | | |
| | | // ç§å®¤å表 |
| | | deptList: [ |
| | | { id: 1, name: "å¿è¡ç®¡å
ç§" }, |
| | | { id: 2, name: "ç¥ç»å
ç§" }, |
| | | { id: 3, name: "æ®å¤ç§" }, |
| | | { id: 4, name: "骨ç§" }, |
| | | { id: 5, name: "å¦äº§ç§" }, |
| | | { id: 6, name: "å¿ç§" }, |
| | | { id: 7, name: "æ¥è¯ç§" }, |
| | | { id: 8, name: "å¼å¸å
ç§" }, |
| | | { id: 9, name: "æ¶åå
ç§" }, |
| | | { id: 10, name: "å
åæ³ç§" }, |
| | | ], |
| | | deptList: [], |
| | | |
| | | // å¼å¸¸åè¡¨æ°æ® |
| | | exceptionList: [], |
| | |
| | | // å¤çå¯¹è¯æ¡ |
| | | processDialogVisible: false, |
| | | processForm: { |
| | | status: "", |
| | | reportDepts: [], |
| | | remark: "", |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | finaloption: "", |
| | | }, |
| | | batchProgress: { |
| | | visible: false, |
| | | percentage: 0, |
| | | processed: 0, |
| | | total: 0, |
| | | }, |
| | | processRules: { |
| | | status: [ |
| | | handleFlag: [ |
| | | { required: true, message: "è¯·éæ©å¤çç¶æ", trigger: "change" }, |
| | | ], |
| | | remark: [ |
| | | { required: true, message: "请è¾å
¥å¤ç夿³¨", trigger: "blur" }, |
| | | handleresult: [ |
| | | { |
| | | min: 5, |
| | | max: 500, |
| | | message: "夿³¨é¿åº¦å¨ 5 å° 500 个å符", |
| | | required: true, |
| | | message: "è¯·éæ©å¤çç»æ", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (this.processForm.handleFlag === "1" && !value) { |
| | | callback(new Error("è¯·éæ©å¤çç»æ")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | handledesc: [ |
| | | { |
| | | required: true, |
| | | message: "请è¾å
¥å¤ç说æ", |
| | | trigger: "blur", |
| | | validator: (rule, value, callback) => { |
| | | if ( |
| | | this.processForm.handleFlag === "1" && |
| | | (!value || value.trim().length < 3) |
| | | ) { |
| | | callback(new Error("å¤ç说æè³å°3个å符")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | fileList: [], |
| | | |
| | | // æ¹éå¤çå¯¹è¯æ¡ |
| | | batchDialogVisible: false, |
| | | batchProcessForm: { |
| | | status: "", |
| | | reportDepts: [], |
| | | remark: "", |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | }, |
| | | }; |
| | | }, |
| | | |
| | | created() { |
| | | // ä»è·¯ç±åæ°è·åé®é¢ID |
| | | this.filterParams.scriptids = this.$route.query.questionId || this.$route.query.questionIds||null; |
| | | // if (this.$route.query.questionId) { |
| | | // } else if (this.$route.query.questionIds) { |
| | | // console.log( |
| | | // this.$route.query.questionIds, |
| | | // "this.$route.query.questionIds" |
| | | // ); |
| | | |
| | | this.filterParams.templateType = Number(this.$route.query.type)||null; |
| | | |
| | | // this.filterParams.scriptid = null; |
| | | // } |
| | | this.hasQualityPermission = this.checkQualityPermission(); |
| | | }, |
| | | |
| | | mounted() { |
| | | this.loadExceptionList(); |
| | | this.getDeptOptions(); |
| | | }, |
| | | |
| | | methods: { |
| | | // å è½½å¼å¸¸å表 |
| | | async loadExceptionList() { |
| | | this.loading = true; |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | formatDateTime(dateTime) { |
| | | if (!dateTime) return ""; |
| | | try { |
| | | // Mock æ°æ® |
| | | await new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | this.exceptionList = [ |
| | | { |
| | | id: 1, |
| | | responsibilityDept: "å¿è¡ç®¡å
ç§", |
| | | unsatisfactoryDetail: |
| | | "å»çæ¥æ¿æ¶é´å¤ªçï¼æ²éä¸å¤å
åï¼å¯¹ç
æ
è§£éä¸å¤è¯¦ç»", |
| | | patientName: "å¼ å
ç", |
| | | gender: 1, |
| | | age: 45, |
| | | phone: "138****1234", |
| | | dischargeDept: "å¿è¡ç®¡å
ç§", |
| | | dischargeWard: "å
ç§ä¸ç
åº", |
| | | fillTime: "2024-01-15 10:30:25", |
| | | processStatus: 0, |
| | | questionnaireId: 1001, |
| | | }, |
| | | { |
| | | id: 2, |
| | | responsibilityDept: "ç¥ç»å
ç§", |
| | | unsatisfactoryDetail: |
| | | "æ¤å£«æéææ¯ä¸ä½³ï¼æäºä¸æ¬¡ææåï¼ä¸æåº¦ä¸å¤èå¿", |
| | | patientName: "æå¥³å£«", |
| | | gender: 0, |
| | | age: 38, |
| | | phone: "139****5678", |
| | | dischargeDept: "ç¥ç»å
ç§", |
| | | dischargeWard: "å
ç§äºç
åº", |
| | | fillTime: "2024-01-14 16:20:10", |
| | | processStatus: 0, |
| | | questionnaireId: 1002, |
| | | }, |
| | | { |
| | | id: 3, |
| | | responsibilityDept: "æ®å¤ç§", |
| | | unsatisfactoryDetail: "æ¯åæ¢è¯ä¸åæ¶ï¼ä¼¤å£ç¼çæ¶æ²¡æåæ¶å¤ç", |
| | | patientName: "çå
ç", |
| | | gender: 1, |
| | | age: 52, |
| | | phone: "137****9012", |
| | | dischargeDept: "æ®å¤ç§", |
| | | dischargeWard: "å¤ç§ä¸ç
åº", |
| | | fillTime: "2024-01-13 09:15:45", |
| | | processStatus: 1, |
| | | questionnaireId: 1003, |
| | | }, |
| | | { |
| | | id: 4, |
| | | responsibilityDept: "骨ç§", |
| | | unsatisfactoryDetail: "åº·å¤æå¯¼ä¸å¤ä¸ä¸ï¼å¯¹æ¢å¤è¿ç¨æè¿°ä¸æ¸
æ¥", |
| | | patientName: "å女士", |
| | | gender: 0, |
| | | age: 65, |
| | | phone: "136****3456", |
| | | dischargeDept: "骨ç§", |
| | | dischargeWard: "å¤ç§äºç
åº", |
| | | fillTime: "2024-01-12 14:40:30", |
| | | processStatus: 0, |
| | | questionnaireId: 1004, |
| | | }, |
| | | { |
| | | id: 5, |
| | | responsibilityDept: "å¦äº§ç§", |
| | | unsatisfactoryDetail: |
| | | "äº§åæ£æ¥æéæ¶é´è¿é¿ï¼çå¾
æé´æ²¡æä¼æ¯åº§ä½", |
| | | patientName: "é女士", |
| | | gender: 0, |
| | | age: 28, |
| | | phone: "135****7890", |
| | | dischargeDept: "å¦äº§ç§", |
| | | dischargeWard: "å¦äº§ç§ç
åº", |
| | | fillTime: "2024-01-11 11:25:15", |
| | | processStatus: 2, |
| | | questionnaireId: 1005, |
| | | }, |
| | | { |
| | | id: 6, |
| | | responsibilityDept: "å¿ç§", |
| | | unsatisfactoryDetail: |
| | | "å¿ç«¥ç¨è¯åéäº¤ä»£ä¸æ¸
æ°ï¼ç¨è¯æ³¨æäºé¡¹æ²¡æè¯´æ", |
| | | patientName: "èµµå®å®", |
| | | gender: 1, |
| | | age: 5, |
| | | phone: "134****1234", |
| | | dischargeDept: "å¿ç§", |
| | | dischargeWard: "å¿ç§ç
åº", |
| | | fillTime: "2024-01-10 15:50:20", |
| | | processStatus: 0, |
| | | questionnaireId: 1006, |
| | | }, |
| | | { |
| | | id: 7, |
| | | responsibilityDept: "æ¥è¯ç§", |
| | | unsatisfactoryDetail: "æ¥è¯çå¾
æ¶é´è¿é¿ï¼ç
æ
没æå¾å°åæ¶è¯ä¼°", |
| | | patientName: "åå
ç", |
| | | gender: 1, |
| | | age: 40, |
| | | phone: "133****5678", |
| | | dischargeDept: "æ¥è¯ç§", |
| | | dischargeWard: "æ¥è¯ç
åº", |
| | | fillTime: "2024-01-09 10:15:40", |
| | | processStatus: 0, |
| | | questionnaireId: 1007, |
| | | }, |
| | | { |
| | | id: 8, |
| | | responsibilityDept: "å¼å¸å
ç§", |
| | | unsatisfactoryDetail: "å»çå¼è¯è¾å¤ï¼è´¹ç¨è¾é«ï¼æ²¡æè¯´æå¿
è¦æ§", |
| | | patientName: "å¨å¥³å£«", |
| | | gender: 0, |
| | | age: 55, |
| | | phone: "132****9012", |
| | | dischargeDept: "å¼å¸å
ç§", |
| | | dischargeWard: "å
ç§ä¸ç
åº", |
| | | fillTime: "2024-01-08 13:30:55", |
| | | processStatus: 1, |
| | | questionnaireId: 1008, |
| | | }, |
| | | ]; |
| | | this.total = this.exceptionList.length; |
| | | resolve(); |
| | | }, 500); |
| | | const date = new Date(dateTime); |
| | | if (isNaN(date.getTime())) { |
| | | return dateTime; |
| | | } |
| | | return ( |
| | | date.toLocaleDateString().replace(/\//g, "-") + |
| | | " " + |
| | | date.toTimeString().split(" ")[0] |
| | | ); |
| | | } catch (error) { |
| | | console.error("æ¥ææ ¼å¼åé误:", error); |
| | | return dateTime; |
| | | } |
| | | }, |
| | | /** æ¥è¯¢ç§å®¤å表 */ |
| | | getDeptOptions() { |
| | | deptTreeSelect() |
| | | .then((res) => { |
| | | if (res.code == 200) { |
| | | this.deptList = this.flattenArray(res.data) || []; |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("è·åç§å®¤å表失败:", error); |
| | | this.$message.error("è·åç§å®¤å表失败"); |
| | | }); |
| | | } finally { |
| | | this.loading = false; |
| | | }, |
| | | flattenArray(multiArray) { |
| | | let result = []; |
| | | |
| | | function flatten(element) { |
| | | if (element.children && element.children.length > 0) { |
| | | element.children.forEach((child) => flatten(child)); |
| | | } else { |
| | | let item = JSON.parse(JSON.stringify(element)); |
| | | result.push(item); |
| | | } |
| | | } |
| | | |
| | | multiArray.forEach((element) => flatten(element)); |
| | | return result; |
| | | }, |
| | | // è§£ææ£è
æè¿°ä¿¡æ¯ |
| | | parsePatDesc(patdesc) { |
| | | if (!patdesc) return []; |
| | | |
| | | try { |
| | | const parts = patdesc.split("|"); |
| | | const items = []; |
| | | |
| | | if (parts[0]) items.push({ label: "å§å", value: parts[0] }); |
| | | if (parts[1]) items.push({ label: "çµè¯", value: parts[1] }); |
| | | if (parts[2]) items.push({ label: "ç§å®¤", value: parts[2] }); |
| | | |
| | | return items; |
| | | } catch (error) { |
| | | console.error("è§£ææ£è
ä¿¡æ¯å¤±è´¥:", error); |
| | | return []; |
| | | } |
| | | }, |
| | | |
| | | // æ£æ¥è´¨ç®¡æé |
| | | checkQualityPermission() { |
| | | // è¿éå¯ä»¥æ ¹æ®å®é
æéç³»ç»å®ç° |
| | | const userRoles = this.$store.getters.roles || []; |
| | | return ( |
| | | userRoles.includes("quality_manager") || userRoles.includes("admin") |
| | | ); |
| | | }, |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | getStatusTagType(status) { |
| | | switch (status) { |
| | | case 0: |
| | | return "warning"; // å¾
å¤ç |
| | | case 1: |
| | | return "primary"; // å¤çä¸ |
| | | case 2: |
| | | getStatusTagType(handleFlag) { |
| | | switch (handleFlag) { |
| | | case "0": |
| | | return "warning"; // æªå¤ç |
| | | case "1": |
| | | return "success"; // å·²å¤ç |
| | | default: |
| | | return "info"; |
| | |
| | | }, |
| | | |
| | | // è·åç¶æææ¬ |
| | | getStatusText(status) { |
| | | switch (status) { |
| | | case 0: |
| | | return "å¾
å¤ç"; |
| | | case 1: |
| | | return "å¤çä¸"; |
| | | case 2: |
| | | getStatusText(handleFlag) { |
| | | switch (handleFlag) { |
| | | case "0": |
| | | return "æªå¤ç"; |
| | | case "1": |
| | | return "å·²å¤ç"; |
| | | default: |
| | | return "æªç¥"; |
| | | } |
| | | }, |
| | | |
| | | // ææ¾å½é³ |
| | | handlePlayAudio(url) { |
| | | this.audioUrl = url; |
| | | this.$nextTick(() => { |
| | | const audioPlayer = this.$refs.audioPlayer; |
| | | if (audioPlayer) { |
| | | audioPlayer.play().catch((error) => { |
| | | console.error("ææ¾å¤±è´¥:", error); |
| | | this.$message.error("é³é¢ææ¾å¤±è´¥"); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // æå»ºæ¥è¯¢åæ° |
| | | buildQueryParams() { |
| | | const params = { |
| | | pageNum: this.filterParams.pageNum, |
| | | pageSize: this.filterParams.pageSize, |
| | | }; |
| | | |
| | | if (this.filterParams.todeptcode) { |
| | | params.todeptcode = this.filterParams.todeptcode; |
| | | } |
| | | |
| | | if (this.filterParams.handleFlag !== "") { |
| | | params.handleFlag = this.filterParams.handleFlag; |
| | | } |
| | | |
| | | if (this.filterParams.templateType) { |
| | | params.templateType = this.filterParams.templateType; |
| | | } |
| | | |
| | | // if (this.filterParams.scriptid) { |
| | | // params.scriptid = this.filterParams.scriptid; |
| | | // } |
| | | if (this.filterParams.scriptids) { |
| | | params.scriptids = this.filterParams.scriptids.split(","); |
| | | } |
| | | |
| | | return params; |
| | | }, |
| | | |
| | | // å è½½å¼å¸¸å表 |
| | | async loadExceptionList() { |
| | | this.loading = true; |
| | | try { |
| | | const params = this.buildQueryParams(); |
| | | const response = await tracelist(params); |
| | | |
| | | if (response && response.code === 200) { |
| | | this.exceptionList = response.rows || []; |
| | | this.total = response.total || 0; |
| | | } else { |
| | | this.exceptionList = []; |
| | | this.total = 0; |
| | | this.$message.error(response?.msg || "å è½½å¼å¸¸å表失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("å è½½å¼å¸¸å表失败:", error); |
| | | this.$message.error("å è½½å¼å¸¸å表失败ï¼è¯·ç¨åéè¯"); |
| | | this.exceptionList = []; |
| | | this.total = 0; |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | |
| | | // éç½®çé |
| | | handleResetFilter() { |
| | | this.filterParams = { |
| | | deptId: "", |
| | | status: "", |
| | | todeptcode: "", |
| | | handleFlag: "", |
| | | templateType: "", |
| | | scriptids: null, // ä¿çé®é¢ID |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | |
| | | this.$message.warning("请å
éæ©è¦å¤ççå¼å¸¸åé¦"); |
| | | return; |
| | | } |
| | | |
| | | // éç½®æ¹éå¤ç表å |
| | | this.batchProcessForm = { |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | }; |
| | | |
| | | this.batchDialogVisible = true; |
| | | }, |
| | | |
| | |
| | | // æ¥ç详æ
|
| | | handleViewDetail(row) { |
| | | this.selectedRecordId = row.id; |
| | | this.detailDialogTitle = `${row.patientName} - å¼å¸¸åé¦è¯¦æ
`; |
| | | this.selectedRecordData = row; |
| | | |
| | | // çæå¼¹æ¡æ é¢ |
| | | let title = "å¼å¸¸åé¦è¯¦æ
"; |
| | | if (row.patdesc) { |
| | | const patientName = row.patdescJson.sendname; |
| | | if (patientName) { |
| | | title = `${patientName} - ${title}`; |
| | | } |
| | | } |
| | | this.detailDialogTitle = title; |
| | | |
| | | this.detailDialogVisible = true; |
| | | }, |
| | | |
| | | // å¤ç详æ
å¼¹æ¡å
³é |
| | | handleDetailDialogClose() { |
| | | this.detailDialogVisible = false; |
| | | this.selectedRecordId = null; |
| | | }, // å¤ç宿åçåè° |
| | | this.selectedRecordData = null; |
| | | }, |
| | | |
| | | // å¤ç宿åçåè° |
| | | handleProcessed() { |
| | | // éæ°å è½½æ°æ® |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | | // å¤çå个å¼å¸¸ |
| | | handleProcess(row) { |
| | | this.currentExceptionId = row.id; |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | this.processForm = { |
| | | status: row.processStatus === 0 ? 1 : row.processStatus, |
| | | reportDepts: [], |
| | | remark: "", |
| | | handleFlag: row.handleFlag === "0" ? "1" : "0", |
| | | ccdepts: row.ccdepts ? row.ccdepts.split(",") : [], |
| | | handleresult: row.handleresult || "", |
| | | handledesc: row.handledesc || "", |
| | | finaloption: row.finaloption || "", |
| | | }; |
| | | |
| | | this.processDialogVisible = true; |
| | | }, |
| | | |
| | | // æäº¤å¤ç |
| | | async submitProcess() { |
| | | this.$refs.processForm.validate(async (valid) => { |
| | | if (valid) { |
| | | this.processing = true; |
| | | try { |
| | | // Mock APIè°ç¨ |
| | | await new Promise((resolve) => setTimeout(resolve, 1000)); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | |
| | | this.$message.success("å¤çæäº¤æå"); |
| | | this.processDialogVisible = false; |
| | | this.loadExceptionList(); |
| | | } finally { |
| | | this.processing = false; |
| | | } |
| | | this.processing = true; |
| | | |
| | | try { |
| | | // åå¤æäº¤æ°æ® |
| | | const submitData = { |
| | | id: this.currentExceptionId, |
| | | handleFlag: this.processForm.handleFlag, |
| | | handleresult: this.processForm.handleresult, |
| | | handledesc: this.processForm.handledesc, |
| | | finaloption: this.processForm.finaloption, |
| | | handleBy: this.$store.state.user.nickName, |
| | | handleTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), |
| | | // å°æ°ç»è½¬æ¢ä¸ºéå·åéçå符串 |
| | | ccdepts: Array.isArray(this.processForm.ccdepts) |
| | | ? this.processForm.ccdepts.join(",") |
| | | : this.processForm.ccdepts, |
| | | }; |
| | | // TODO: è¿ééè¦è°ç¨å®é
çå¤çæ¥å£ |
| | | await traceedit(submitData); |
| | | |
| | | // await new Promise((resolve) => setTimeout(resolve, 1000)); |
| | | |
| | | this.$message.success("å¤çæäº¤æå"); |
| | | this.processDialogVisible = false; |
| | | this.loadExceptionList(); |
| | | } catch (error) { |
| | | console.error("å¤çæäº¤å¤±è´¥:", error); |
| | | this.$message.error("å¤çæäº¤å¤±è´¥ï¼è¯·ç¨åéè¯"); |
| | | } finally { |
| | | this.processing = false; |
| | | } |
| | | }); |
| | | }, |
| | |
| | | // æäº¤æ¹éå¤ç |
| | | async submitBatchProcess() { |
| | | this.$refs.batchProcessForm.validate(async (valid) => { |
| | | if (valid) { |
| | | this.batchProcessing = true; |
| | | try { |
| | | // Mock APIè°ç¨ |
| | | await new Promise((resolve) => setTimeout(resolve, 1500)); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | |
| | | this.$message.success( |
| | | `å·²æ¹éå¤ç ${this.selectedExceptionIds.length} æ¡å¼å¸¸åé¦` |
| | | this.batchProcessing = true; |
| | | // æ¾ç¤ºè¿åº¦æ¡ |
| | | this.batchProgress = { |
| | | visible: true, |
| | | percentage: 0, |
| | | processed: 0, |
| | | total: this.selectedExceptionIds.length, |
| | | }; |
| | | try { |
| | | // å夿¹éæäº¤æ°æ® |
| | | const processData = { |
| | | handleFlag: this.batchProcessForm.handleFlag, |
| | | handleresult: this.batchProcessForm.handleresult, |
| | | handledesc: this.batchProcessForm.handledesc, |
| | | ccdepts: Array.isArray(this.batchProcessForm.ccdepts) |
| | | ? this.batchProcessForm.ccdepts.join(",") |
| | | : this.batchProcessForm.ccdepts, |
| | | }; |
| | | |
| | | // æ§å¶å¹¶åæ° |
| | | const CONCURRENT_LIMIT = 10; // åæ¶æå¤3ä¸ªè¯·æ± |
| | | const totalCount = this.selectedExceptionIds.length; |
| | | const results = []; |
| | | let successCount = 0; |
| | | let failCount = 0; |
| | | |
| | | this.$message.info(`å¼å§æ¹éå¤ç ${totalCount} æ¡è®°å½...`); |
| | | |
| | | // åç»å¤ç |
| | | for ( |
| | | let i = 0; |
| | | i < this.selectedExceptionIds.length; |
| | | i += CONCURRENT_LIMIT |
| | | ) { |
| | | const batchIds = this.selectedExceptionIds.slice( |
| | | i, |
| | | i + CONCURRENT_LIMIT |
| | | ); |
| | | this.batchDialogVisible = false; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | } finally { |
| | | this.batchProcessing = false; |
| | | |
| | | // å¹¶åå¤çå½åæ¹æ¬¡ |
| | | const batchPromises = batchIds.map((id) => |
| | | traceedit({ |
| | | id: id, |
| | | ...processData, |
| | | }) |
| | | .then((result) => ({ |
| | | id, |
| | | success: result && result.code === 200, |
| | | error: result?.msg, |
| | | })) |
| | | .catch((error) => ({ |
| | | id, |
| | | success: false, |
| | | error: error.message, |
| | | })) |
| | | ); |
| | | |
| | | const batchResults = await Promise.all(batchPromises); |
| | | results.push(...batchResults); |
| | | |
| | | // æ´æ°ç»è®¡ |
| | | batchResults.forEach((result) => { |
| | | if (result.success) { |
| | | successCount++; |
| | | } else { |
| | | failCount++; |
| | | console.error(`å¤çè®°å½ ${result.id} 失败:`, result.error); |
| | | } |
| | | }); |
| | | // æ´æ°è¿åº¦ |
| | | this.batchProgress.processed = i + 1; |
| | | this.batchProgress.percentage = Math.round( |
| | | ((i + 1) / totalCount) * 100 |
| | | ); |
| | | // æ¾ç¤ºè¿åº¦ |
| | | console.log( |
| | | `è¿åº¦: ${Math.min( |
| | | i + CONCURRENT_LIMIT, |
| | | totalCount |
| | | )}/${totalCount}` |
| | | ); |
| | | } |
| | | |
| | | // å¤çç»ææç¤º |
| | | if (successCount === totalCount) { |
| | | this.$message.success(`å·²æåå¤çå
¨é¨ ${totalCount} æ¡å¼å¸¸åé¦`); |
| | | } else { |
| | | this.$message.warning( |
| | | `å·²å¤ç ${successCount} æ¡ï¼å¤±è´¥ ${failCount} æ¡å¼å¸¸åé¦` |
| | | ); |
| | | } |
| | | |
| | | this.batchDialogVisible = false; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | } catch (error) { |
| | | console.error("æ¹éå¤ç失败:", error); |
| | | this.$message.error("æ¹éå¤ç失败ï¼è¯·ç¨åéè¯"); |
| | | } finally { |
| | | this.batchProcessing = false; |
| | | this.batchProgress.visible = false; |
| | | } |
| | | }); |
| | | }, |
| | |
| | | handlePageChange(page) { |
| | | this.filterParams.pageNum = page; |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | | // æä»¶ä¸ä¼ ç¸å
³æ¹æ³ |
| | | handlePreview(file) { |
| | | console.log("é¢è§æä»¶:", file); |
| | | }, |
| | | |
| | | handleRemove(file, fileList) { |
| | | console.log("ç§»é¤æä»¶:", file, fileList); |
| | | }, |
| | | |
| | | beforeRemove(file) { |
| | | return this.$confirm(`ç¡®å®ç§»é¤ ${file.name}ï¼`); |
| | | }, |
| | | |
| | | handleExceed(files, fileList) { |
| | | this.$message.warning( |
| | | `å½åéå¶éæ© 3 个æä»¶ï¼æ¬æ¬¡éæ©äº ${files.length} 个æä»¶ï¼å
±éæ©äº ${ |
| | | files.length + fileList.length |
| | | } 个æä»¶` |
| | | ); |
| | | }, |
| | | }, |
| | | }; |
| | |
| | | } |
| | | |
| | | .detail-content { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | line-height: 1.5; |
| | | text-align: left; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | |
| | | .question-text { |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .answer-text { |
| | | color: #f56c6c; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .matched-text { |
| | | color: #e6a23c; |
| | | font-style: italic; |
| | | } |
| | | |
| | | strong { |
| | | color: #606266; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | |
| | | .patient-info { |
| | | .patient-item { |
| | | .patient-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 5px; |
| | | padding: 2px 0; |
| | | margin-bottom: 8px; |
| | | |
| | | .label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 40px; |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 13px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | text-align: right; |
| | | .patient-item { |
| | | flex: 1; |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | padding: 0 5px; |
| | | |
| | | &.full-width { |
| | | flex: 1 0 100%; |
| | | margin-left: 0; |
| | | margin-right: 0; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | margin-right: 5px; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 12px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | text-align: right; |
| | | word-break: break-all; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .discharge-info { |
| | | .fill-info, |
| | | .handle-info { |
| | | font-size: 12px; |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | margin-bottom: 5px; |
| | | padding: 2px 0; |
| | | |
| | | .label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 50px; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 13px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | text-align: right; |
| | | // text-align: right; |
| | | flex: 1; |
| | | |
| | | &.time { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | font-size: 11px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-data { |
| | | color: #909399; |
| | | font-style: italic; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .pagination-section { |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">æ£è
å§åï¼</span> |
| | | <span class="value">{{ currentRecord.patientName }}</span> |
| | | <span class="label">é®é¢å
容ï¼</span> |
| | | <span class="value">{{ currentRecord.questiontext || 'æ ' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">æ§å«ï¼</span> |
| | | <span class="value">{{ currentRecord.gender === 1 ? 'ç·' : '女' }}</span> |
| | | <span class="label">åçå
容ï¼</span> |
| | | <span class="value">{{ currentRecord.asrtext || 'æ åç' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å¹´é¾ï¼</span> |
| | | <span class="value">{{ currentRecord.age }}å²</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">èç³»æ¹å¼ï¼</span> |
| | | <span class="value">{{ currentRecord.phone }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">åºé¢ç§å®¤ï¼</span> |
| | | <span class="value">{{ currentRecord.dischargeDept }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">åºé¢ç
åºï¼</span> |
| | | <span class="value">{{ currentRecord.dischargeWard }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å¡«åæ¶é´ï¼</span> |
| | | <span class="value">{{ currentRecord.fillTime }}</span> |
| | | <span class="label">è§£æå¼ï¼</span> |
| | | <span class="value">{{ currentRecord.matchedtext || 'æ ' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">è´è´£ç§å®¤ï¼</span> |
| | | <el-tag type="primary">{{ currentRecord.responsibilityDept }}</el-tag> |
| | | <el-tag v-if="currentRecord.todeptname" type="primary">{{ currentRecord.todeptname }}</el-tag> |
| | | <span v-else class="value">æªåé
</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤çç¶æï¼</span> |
| | | <el-tag |
| | | :type="getStatusTagType(currentRecord.processStatus)" |
| | | :type="getStatusTagType(currentRecord.handleFlag)" |
| | | effect="dark" |
| | | > |
| | | {{ getStatusText(currentRecord.processStatus) }} |
| | | {{ getStatusText(currentRecord.handleFlag) }} |
| | | </el-tag> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">模æ¿ç±»åï¼</span> |
| | | <el-tag :type="currentRecord.templateType === 1 ? 'primary' : 'success'"> |
| | | {{ currentRecord.templateType === 1 ? 'è¯é³æ¨¡æ¿' : 'é®å·æ¨¡æ¿' }} |
| | | </el-tag> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å建æ¶é´ï¼</span> |
| | | <span class="value">{{ formatDateTime(currentRecord.createTime) }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤çæ¶é´ï¼</span> |
| | | <span class="value">{{ currentRecord.handleTime ? formatDateTime(currentRecord.handleTime) : 'æªå¤ç' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤ç人ï¼</span> |
| | | <span class="value">{{ currentRecord.handleBy || 'æªå¤ç' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="currentRecord.handleresult"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤çç»æï¼</span> |
| | | <span class="value">{{ getHandleresultText(currentRecord.handleresult) }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="16" v-if="currentRecord.handledesc"> |
| | | <div class="info-item"> |
| | | <span class="label">å¤ç说æï¼</span> |
| | | <span class="value">{{ currentRecord.handledesc }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="24" v-if="currentRecord.finaloption"> |
| | | <div class="info-item"> |
| | | <span class="label">æç»æè§ï¼</span> |
| | | <span class="value">{{ currentRecord.finaloption }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="currentRecord.recordurl"> |
| | | <div class="info-item"> |
| | | <span class="label">å½é³å°åï¼</span> |
| | | <el-button |
| | | type="text" |
| | | size="small" |
| | | icon="el-icon-headset" |
| | | @click="handlePlayAudio(currentRecord.recordurl)" |
| | | > |
| | | ææ¾å½é³ |
| | | </el-button> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8" v-if="currentRecord.ccdepts"> |
| | | <div class="info-item"> |
| | | <span class="label">æéç§å®¤ï¼</span> |
| | | <span class="value">{{ currentRecord.ccdepts }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- é®å·è¯¦æ
--> |
| | | <div class="questionnaire-section"> |
| | | <div class="section-title">é®å·å¡«å详æ
</div> |
| | | <div class="questionnaire-content"> |
| | | <div class="question-item" v-for="(question, index) in questionnaireData" :key="index"> |
| | | <div class="question-header"> |
| | | <span class="question-index">{{ index + 1 }}.</span> |
| | | <span class="question-text">{{ question.question }}</span> |
| | | <el-tag |
| | | size="mini" |
| | | :type="question.type === 1 ? 'primary' : 'success'" |
| | | class="question-type" |
| | | > |
| | | {{ question.type === 1 ? 'åéé¢' : 'å¤éé¢' }} |
| | | </el-tag> |
| | | </div> |
| | | <div class="question-options"> |
| | | <el-radio-group |
| | | v-model="question.answer" |
| | | v-if="question.type === 1" |
| | | disabled |
| | | > |
| | | <el-radio |
| | | v-for="option in question.options" |
| | | :key="option.value" |
| | | :label="option.value" |
| | | :class="{ 'unsatisfactory-option': isUnsatisfactoryOption(option.value) }" |
| | | <!-- é®å·/è¯é³è¯¦æ
--> |
| | | <div class="content-container" v-if="templateData.length > 0"> |
| | | <el-tabs v-model="activeName" type="border-card"> |
| | | <!-- é®å·é访详æ
--> |
| | | <el-tab-pane name="wj" v-if="currentRecord.templateType === 2"> |
| | | <span slot="label"><i class="el-icon-notebook-1"></i> é®å·é访详æ
</span> |
| | | <div class="CONTENT"> |
| | | <div class="title">{{ currentRecord.questiontext || 'é®å·è¯¦æ
' }}</div> |
| | | <div class="preview-left" v-if="!isVoiceTemplate"> |
| | | <div |
| | | class="topic-dev" |
| | | v-for="(item, index) in templateData" |
| | | :key="item.id" |
| | | > |
| | | {{ option.text }} |
| | | </el-radio> |
| | | </el-radio-group> |
| | | <el-checkbox-group |
| | | v-model="question.answer" |
| | | v-else |
| | | disabled |
| | | > |
| | | <el-checkbox |
| | | v-for="option in question.options" |
| | | :key="option.value" |
| | | :label="option.value" |
| | | :class="{ 'unsatisfactory-option': isUnsatisfactoryOption(option.value) }" |
| | | > |
| | | {{ option.text }} |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | <!-- åé --> |
| | | <div |
| | | :class="getTopicClass(item)" |
| | | :key="index" |
| | | v-if="item.scriptType == 1 && !item.astrict" |
| | | > |
| | | <div class="dev-text"> |
| | | {{ index + 1 }}ã[åé]<span>{{ item.scriptContent }}</span> |
| | | </div> |
| | | <div class="dev-xx"> |
| | | <el-radio-group v-model="item.scriptResult" disabled> |
| | | <el-radio |
| | | v-for="(option, optionIndex) in item.svyTaskTemplateTargetoptions" |
| | | :class="getOptionClass(option)" |
| | | :key="optionIndex" |
| | | :label="option.optioncontent" |
| | | >{{ option.optioncontent }}</el-radio> |
| | | </el-radio-group> |
| | | </div> |
| | | <div |
| | | v-if="item.showAppendInput || item.answerps" |
| | | class="append-input-container" |
| | | > |
| | | <el-input |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥å
·ä½ä¿¡æ¯" |
| | | v-model="item.answerps" |
| | | readonly |
| | | ></el-input> |
| | | </div> |
| | | <div v-show="item.prompt"> |
| | | <el-alert :title="item.prompt" type="warning"></el-alert> |
| | | </div> |
| | | </div> |
| | | <!-- å¤é --> |
| | | <div |
| | | :class="item.isabnormal ? 'scriptTopic-isabnormal' : 'scriptTopic-dev'" |
| | | :key="index" |
| | | v-if="item.scriptType == 2 && !item.astrict" |
| | | > |
| | | <div class="dev-text"> |
| | | {{ index + 1 }}ã[å¤é]<span>{{ item.scriptContent }}</span> |
| | | </div> |
| | | <div class="dev-xx"> |
| | | <el-checkbox-group v-model="item.scriptResult" disabled> |
| | | <el-checkbox |
| | | :class="option.isabnormal ? 'red-star' : ''" |
| | | v-for="(option, optionIndex) in item.svyTaskTemplateTargetoptions" |
| | | :key="optionIndex" |
| | | :label="option.optioncontent" |
| | | > |
| | | {{ option.optioncontent }} |
| | | </el-checkbox> |
| | | </el-checkbox-group> |
| | | </div> |
| | | <div v-show="item.prompt && item.scriptResult[0]"> |
| | | <el-alert :title="item.prompt" type="warning"></el-alert> |
| | | </div> |
| | | </div> |
| | | <!-- 填空 --> |
| | | <div |
| | | class="scriptTopic-dev" |
| | | :key="index" |
| | | v-if="item.scriptType == 4 && !item.astrict" |
| | | > |
| | | <div class="dev-text"> |
| | | {{ index + 1 }}ã[é®ç]<span>{{ item.scriptContent }}</span> |
| | | <span v-if="item.valueType == 3">(åªè½è¾å
¥æ°å)</span> |
| | | </div> |
| | | <div class="dev-xx" v-if="item.valueType == 3"> |
| | | <el-input |
| | | type="text" |
| | | placeholder="请è¾å
¥çæ¡" |
| | | v-model="item.scriptResult" |
| | | readonly |
| | | ></el-input> |
| | | </div> |
| | | <div class="dev-xx" v-else> |
| | | <el-input |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥çæ¡" |
| | | v-model="item.scriptResult" |
| | | readonly |
| | | ></el-input> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-if="question.additional" class="additional-remark"> |
| | | <div class="remark-label">è¡¥å
说æï¼</div> |
| | | <div class="remark-content">{{ question.additional }}</div> |
| | | </el-tab-pane> |
| | | |
| | | <!-- è¯é³é访详æ
--> |
| | | <el-tab-pane name="yy" v-if="currentRecord.templateType === 1"> |
| | | <span slot="label"><i class="el-icon-headset"></i> è¯é³é访详æ
</span> |
| | | <div class="borderdiv"> |
| | | <div class="title">{{ taskName }}</div> |
| | | <div class="voice-audio" v-if="voiceAudioUrl"> |
| | | 宿´è¯é³ï¼ |
| | | <audio-player |
| | | :audio-source="voiceAudioUrl" |
| | | ></audio-player> |
| | | </div> |
| | | <div class="preview-left" v-if="voiceData.length > 0"> |
| | | <div v-for="(item, index) in voiceData" :key="index"> |
| | | <div class="leftside"> |
| | | <i class="el-icon-phone-outline"></i> |
| | | <span>{{ item.questiontext || 'é®é¢å
容' }}</span> |
| | | </div> |
| | | <div class="offside"> |
| | | <i class="el-icon-user"></i> |
| | | <div class="offside-value"> |
| | | <el-input |
| | | type="textarea" |
| | | :autosize="{ minRows: 1 }" |
| | | v-model="item.asrtext" |
| | | readonly |
| | | ></el-input> |
| | | <div v-if="item.questionvoice"> |
| | | <audio-player |
| | | :audio-source="item.questionvoice" |
| | | ></audio-player> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | |
| | | <!-- å¤çè®°å½ --> |
| | | <div class="process-section"> |
| | | <div class="process-section" v-if="processRecords.length > 0"> |
| | | <div class="section-title">å¤çè®°å½</div> |
| | | <div class="process-timeline" v-if="processRecords.length > 0"> |
| | | <div class="process-timeline"> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="(record, index) in processRecords" |
| | | :key="index" |
| | | :timestamp="record.time" |
| | | :timestamp="formatDateTime(record.handleTime || record.createTime)" |
| | | placement="top" |
| | | > |
| | | <el-card> |
| | | <div class="process-item"> |
| | | <div class="process-header"> |
| | | <span class="process-user">{{ record.user }}</span> |
| | | <span class="process-user">{{ record.handleBy || 'ç³»ç»' }}</span> |
| | | <el-tag |
| | | size="small" |
| | | :type="getStatusTagType(record.status)" |
| | | :type="getStatusTagType(record.handleFlag)" |
| | | > |
| | | {{ getStatusText(record.status) }} |
| | | {{ getStatusText(record.handleFlag) }} |
| | | </el-tag> |
| | | </div> |
| | | <div class="process-content"> |
| | | <div v-if="record.reportDepts && record.reportDepts.length > 0" class="process-depts"> |
| | | <span class="label">æ¥å¤ç§å®¤ï¼</span> |
| | | <div v-if="record.ccdepts" class="process-depts"> |
| | | <span class="label">æéç§å®¤ï¼</span> |
| | | <el-tag |
| | | v-for="dept in record.reportDepts" |
| | | v-for="dept in getDeptArray(record.ccdepts)" |
| | | :key="dept" |
| | | size="small" |
| | | type="info" |
| | |
| | | {{ dept }} |
| | | </el-tag> |
| | | </div> |
| | | <div v-if="record.remark" class="process-remark"> |
| | | <span class="label">å¤ç夿³¨ï¼</span> |
| | | <span class="content">{{ record.remark }}</span> |
| | | <div v-if="record.handleresult" class="process-remark"> |
| | | <span class="label">å¤çç»æï¼</span> |
| | | <span class="content">{{ getHandleresultText(record.handleresult) }}</span> |
| | | </div> |
| | | <div v-if="record.attachments && record.attachments.length > 0" class="process-attachments"> |
| | | <span class="label">éä»¶ï¼</span> |
| | | <el-button |
| | | v-for="file in record.attachments" |
| | | :key="file.id" |
| | | type="text" |
| | | size="small" |
| | | icon="el-icon-document" |
| | | @click="handlePreviewFile(file)" |
| | | > |
| | | {{ file.name }} |
| | | </el-button> |
| | | <div v-if="record.handledesc" class="process-remark"> |
| | | <span class="label">å¤ç说æï¼</span> |
| | | <span class="content">{{ record.handledesc }}</span> |
| | | </div> |
| | | <div v-if="record.finaloption" class="process-remark"> |
| | | <span class="label">æç»æè§ï¼</span> |
| | | <span class="content">{{ record.finaloption }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </div> |
| | | <div v-else class="no-record"> |
| | | ææ å¤çè®°å½ |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | type="primary" |
| | | icon="el-icon-edit" |
| | | @click="handleProcess" |
| | | v-if="currentRecord.processStatus !== 2" |
| | | v-if="currentRecord.handleFlag !== '1'" |
| | | > |
| | | å¤çå¼å¸¸ |
| | | </el-button> |
| | |
| | | label-width="100px" |
| | | size="medium" |
| | | > |
| | | <el-form-item label="å¤çç¶æ" prop="status"> |
| | | <el-form-item label="å¤çç¶æ" prop="handleFlag"> |
| | | <el-select |
| | | v-model="processForm.status" |
| | | v-model="processForm.handleFlag" |
| | | placeholder="è¯·éæ©å¤çç¶æ" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="å¤çä¸" :value="1" /> |
| | | <el-option label="å·²å¤ç" :value="2" /> |
| | | <el-option label="已驳å" :value="3" /> |
| | | <el-option label="å·²å¤ç" :value="'1'" /> |
| | | <el-option label="åæ¶å¤ç" :value="'0'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="æ¥å¤ç§å®¤" prop="reportDepts"> |
| | | <el-form-item label="æéç§å®¤" prop="ccdepts"> |
| | | <el-select |
| | | v-model="processForm.reportDepts" |
| | | placeholder="è¯·éæ©æ¥å¤ç§å®¤" |
| | | v-model="processForm.ccdepts" |
| | | placeholder="è¯·éæ©æéç§å®¤" |
| | | multiple |
| | | filterable |
| | | collapse-tags |
| | | style="width: 100%" |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç夿³¨" prop="remark"> |
| | | <el-form-item label="å¤çç»æ" prop="handleresult"> |
| | | <el-select |
| | | v-model="processForm.handleresult" |
| | | placeholder="è¯·éæ©å¤çç»æ" |
| | | style="width: 100%" |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | > |
| | | <el-option label="已解å³" value="resolved" /> |
| | | <el-option label="已解é" value="explained" /> |
| | | <el-option label="已转交" value="transferred" /> |
| | | <el-option label="éæ¹è¿" value="improvement" /> |
| | | <el-option label="已驳å" value="rejected" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="å¤ç说æ" prop="handledesc"> |
| | | <el-input |
| | | v-model="processForm.remark" |
| | | v-model="processForm.handledesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¤ç夿³¨ï¼æå¤500åï¼" |
| | | placeholder="请è¾å
¥å¤ç说æï¼æå¤500åï¼" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="éä»¶ä¸ä¼ "> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :on-preview="handleFilePreview" |
| | | :on-remove="handleFileRemove" |
| | | :before-remove="beforeFileRemove" |
| | | :limit="3" |
| | | :on-exceed="handleFileExceed" |
| | | :file-list="fileList" |
| | | > |
| | | <el-button size="small" type="primary">ç¹å»ä¸ä¼ </el-button> |
| | | <div slot="tip" class="el-upload__tip">æ¯æä¸ä¼ å¾çãææ¡£çéä»¶ï¼å个æä»¶ä¸è¶
è¿10MB</div> |
| | | </el-upload> |
| | | <el-form-item label="æç»æè§" prop="finaloption" v-if="hasQualityPermission"> |
| | | <el-input |
| | | v-model="processForm.finaloption" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥æç»å¤çæè§ï¼æå¤300åï¼" |
| | | maxlength="300" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <span slot="footer" class="dialog-footer"> |
| | |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | |
| | | <!-- å½é³ææ¾å¨ --> |
| | | <audio |
| | | v-if="audioUrl" |
| | | :src="audioUrl" |
| | | ref="audioPlayer" |
| | | controls |
| | | style="display: none" |
| | | /> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import { traceedit } from "@/api/AiCentre/index"; |
| | | import { getsearchrResults, getPersonVoices, getTaskservelist } from "@/api/AiCentre/index"; |
| | | import { deptTreeSelect } from "@/api/system/user"; |
| | | import AudioPlayer from "@/components/AudioPlayer"; // éè¦å建è¿ä¸ªé³é¢ææ¾ç»ä»¶ |
| | | |
| | | export default { |
| | | name: 'ExceptionDetailDialog', |
| | | components: { |
| | | AudioPlayer |
| | | }, |
| | | props: { |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | visible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | // è®°å½ID |
| | | recordId: { |
| | | type: [Number, String], |
| | | default: null |
| | | }, |
| | | // å¯¹è¯æ¡æ é¢ |
| | | recordData: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: 'å¼å¸¸åé¦è¯¦æ
' |
| | |
| | | // å½åè®°å½ |
| | | currentRecord: {}, |
| | | |
| | | // é®å·æ°æ® |
| | | questionnaireData: [], |
| | | // é®å·/è¯é³æ°æ® |
| | | activeName: 'wj', |
| | | taskName: '', |
| | | templateData: [], |
| | | voiceData: [], |
| | | voiceAudioUrl: '', |
| | | |
| | | // å¤çè®°å½ |
| | | processRecords: [], |
| | | |
| | | // ç§å®¤å表 |
| | | deptList: [ |
| | | { id: 1, name: 'å¿è¡ç®¡å
ç§' }, |
| | | { id: 2, name: 'ç¥ç»å
ç§' }, |
| | | { id: 3, name: 'æ®å¤ç§' }, |
| | | { id: 4, name: '骨ç§' }, |
| | | { id: 5, name: 'å¦äº§ç§' }, |
| | | { id: 6, name: 'å¿ç§' }, |
| | | { id: 7, name: 'æ¥è¯ç§' }, |
| | | { id: 8, name: 'å¼å¸å
ç§' } |
| | | ], |
| | | deptList: [], |
| | | |
| | | // å¤çå¯¹è¯æ¡ |
| | | processDialogVisible: false, |
| | | processing: false, |
| | | processForm: { |
| | | status: '', |
| | | reportDepts: [], |
| | | remark: '' |
| | | handleFlag: '', |
| | | ccdepts: [], |
| | | handleresult: '', |
| | | handledesc: '', |
| | | finaloption: '' |
| | | }, |
| | | processRules: { |
| | | status: [ |
| | | handleFlag: [ |
| | | { required: true, message: 'è¯·éæ©å¤çç¶æ', trigger: 'change' } |
| | | ], |
| | | remark: [ |
| | | { required: true, message: '请è¾å
¥å¤ç夿³¨', trigger: 'blur' }, |
| | | { min: 5, max: 500, message: '夿³¨é¿åº¦å¨ 5 å° 500 个å符', trigger: 'blur' } |
| | | handleresult: [ |
| | | { |
| | | required: true, |
| | | message: 'è¯·éæ©å¤çç»æ', |
| | | trigger: 'change', |
| | | validator: (rule, value, callback) => { |
| | | if (this.processForm.handleFlag === '1' && !value) { |
| | | callback(new Error('è¯·éæ©å¤çç»æ')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | ], |
| | | handledesc: [ |
| | | { |
| | | required: true, |
| | | message: '请è¾å
¥å¤ç说æ', |
| | | trigger: 'blur', |
| | | validator: (rule, value, callback) => { |
| | | if (this.processForm.handleFlag === '1' && (!value || value.trim().length < 3)) { |
| | | callback(new Error('å¤ç说æè³å°3个å符')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | }, |
| | | fileList: [], |
| | | |
| | | // é³é¢ææ¾ |
| | | audioUrl: '', |
| | | |
| | | // å è½½ç¶æ |
| | | loading: false |
| | | loading: false, |
| | | |
| | | // æéæ§å¶ |
| | | hasQualityPermission: false |
| | | }; |
| | | }, |
| | | |
| | |
| | | set(val) { |
| | | this.$emit('update:visible', val); |
| | | } |
| | | }, |
| | | |
| | | isVoiceTemplate() { |
| | | return this.currentRecord.templateType === 1; |
| | | } |
| | | }, |
| | | |
| | |
| | | visible: { |
| | | immediate: true, |
| | | handler(val) { |
| | | if (val && this.recordId) { |
| | | if (val) { |
| | | this.loadData(); |
| | | } else { |
| | | this.resetData(); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | recordData: { |
| | | immediate: true, |
| | | handler(val) { |
| | | if (val && Object.keys(val).length > 0) { |
| | | this.currentRecord = { ...val }; |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | // å è½½æ°æ® |
| | | async loadData() { |
| | | this.loading = true; |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | formatDateTime(dateTime) { |
| | | if (!dateTime) return ''; |
| | | try { |
| | | await Promise.all([ |
| | | this.loadRecordDetail(), |
| | | this.loadQuestionnaireData(), |
| | | this.loadProcessRecords() |
| | | ]); |
| | | } finally { |
| | | this.loading = false; |
| | | const date = new Date(dateTime); |
| | | if (isNaN(date.getTime())) { |
| | | return dateTime; |
| | | } |
| | | return date.toLocaleDateString().replace(/\//g, '-') + ' ' + |
| | | date.toTimeString().split(' ')[0]; |
| | | } catch (error) { |
| | | console.error('æ¥ææ ¼å¼åé误:', error); |
| | | return dateTime; |
| | | } |
| | | }, |
| | | |
| | | // å 载记å½è¯¦æ
|
| | | async loadRecordDetail() { |
| | | return new Promise(resolve => { |
| | | setTimeout(() => { |
| | | // æ ¹æ®ä¸åçrecordIdè¿åä¸åçmockæ°æ® |
| | | const mockRecords = { |
| | | 1: { |
| | | id: 1, |
| | | patientName: 'å¼ å
ç', |
| | | gender: 1, |
| | | age: 45, |
| | | phone: '13800138000', |
| | | dischargeDept: 'å¿è¡ç®¡å
ç§', |
| | | dischargeWard: 'å
ç§ä¸ç
åº', |
| | | fillTime: '2024-01-15 10:30:25', |
| | | responsibilityDept: 'å¿è¡ç®¡å
ç§', |
| | | processStatus: 0 |
| | | }, |
| | | 2: { |
| | | id: 2, |
| | | patientName: 'æå¥³å£«', |
| | | gender: 0, |
| | | age: 38, |
| | | phone: '13900139000', |
| | | dischargeDept: 'ç¥ç»å
ç§', |
| | | dischargeWard: 'å
ç§äºç
åº', |
| | | fillTime: '2024-01-14 16:20:10', |
| | | responsibilityDept: 'ç¥ç»å
ç§', |
| | | processStatus: 0 |
| | | }, |
| | | 3: { |
| | | id: 3, |
| | | patientName: 'çå
ç', |
| | | gender: 1, |
| | | age: 52, |
| | | phone: '13700137000', |
| | | dischargeDept: 'æ®å¤ç§', |
| | | dischargeWard: 'å¤ç§ä¸ç
åº', |
| | | fillTime: '2024-01-13 09:15:45', |
| | | responsibilityDept: 'æ®å¤ç§', |
| | | processStatus: 1 |
| | | } |
| | | }; |
| | | |
| | | this.currentRecord = mockRecords[this.recordId] || { |
| | | id: 1, |
| | | patientName: 'å¼ å
ç', |
| | | gender: 1, |
| | | age: 45, |
| | | phone: '13800138000', |
| | | dischargeDept: 'å¿è¡ç®¡å
ç§', |
| | | dischargeWard: 'å
ç§ä¸ç
åº', |
| | | fillTime: '2024-01-15 10:30:25', |
| | | responsibilityDept: 'å¿è¡ç®¡å
ç§', |
| | | processStatus: 0 |
| | | }; |
| | | resolve(); |
| | | }, 300); |
| | | }); |
| | | // æ£æ¥è´¨ç®¡æé |
| | | checkQualityPermission() { |
| | | const userRoles = this.$store.getters.roles || []; |
| | | return userRoles.includes('quality_manager') || userRoles.includes('admin'); |
| | | }, |
| | | |
| | | // å è½½é®å·æ°æ® |
| | | async loadQuestionnaireData() { |
| | | return new Promise(resolve => { |
| | | setTimeout(() => { |
| | | this.questionnaireData = [ |
| | | { |
| | | question: 'æ¨å¯¹å»æ¤äººåçæå¡æåº¦æ¯å¦æ»¡æï¼', |
| | | type: 1, |
| | | options: [ |
| | | { value: 'é常满æ', text: 'é常满æ' }, |
| | | { value: '满æ', text: '满æ' }, |
| | | { value: 'ä¸è¬', text: 'ä¸è¬' }, |
| | | { value: '䏿»¡æ', text: '䏿»¡æ' }, |
| | | { value: 'é叏䏿»¡æ', text: 'é叏䏿»¡æ' } |
| | | ], |
| | | answer: '䏿»¡æ', |
| | | additional: 'å»çæ¥æ¿æ¶é´å¤ªçï¼æ²éä¸å¤å
åï¼å¯¹ç
æ
è§£éä¸å¤è¯¦ç»' |
| | | }, |
| | | { |
| | | question: 'æ¨å¯¹å»ççè¯çæ°´å¹³åææ¯è½åè¯ä»·å¦ä½ï¼', |
| | | type: 1, |
| | | options: [ |
| | | { value: 'é常ä¸ä¸', text: 'é常ä¸ä¸' }, |
| | | { value: 'æ¯è¾ä¸ä¸', text: 'æ¯è¾ä¸ä¸' }, |
| | | { value: 'ä¸è¬', text: 'ä¸è¬' }, |
| | | { value: 'ä¸å¤ä¸ä¸', text: 'ä¸å¤ä¸ä¸' }, |
| | | { value: 'é常ä¸ä¸ä¸', text: 'é常ä¸ä¸ä¸' } |
| | | ], |
| | | answer: 'æ¯è¾ä¸ä¸', |
| | | additional: '' |
| | | }, |
| | | { |
| | | question: 'æ¨å¯¹å»é¢çç¯å¢åå«çç¶åµæ¯å¦æ»¡æï¼', |
| | | type: 1, |
| | | options: [ |
| | | { value: 'é常满æ', text: 'é常满æ' }, |
| | | { value: '满æ', text: '满æ' }, |
| | | { value: 'ä¸è¬', text: 'ä¸è¬' }, |
| | | { value: '䏿»¡æ', text: '䏿»¡æ' }, |
| | | { value: 'é叏䏿»¡æ', text: 'é叏䏿»¡æ' } |
| | | ], |
| | | answer: 'ä¸è¬', |
| | | additional: '' |
| | | }, |
| | | { |
| | | question: 'æ¨è®¤ä¸ºå»æ¤äººå䏿¨çæ²éæ¯å¦å
åï¼', |
| | | type: 1, |
| | | options: [ |
| | | { value: 'é常å
å', text: 'é常å
å' }, |
| | | { value: 'æ¯è¾å
å', text: 'æ¯è¾å
å' }, |
| | | { value: 'ä¸è¬', text: 'ä¸è¬' }, |
| | | { value: 'ä¸å¤å
å', text: 'ä¸å¤å
å' }, |
| | | { value: 'é常ä¸å
å', text: 'é常ä¸å
å' } |
| | | ], |
| | | answer: 'ä¸å¤å
å', |
| | | additional: 'å»ç讲解ç
æ
æ¶è¯éå¤ªå¿«ï¼æ²¡æç»è¶³å¤çæ¶é´æé®' |
| | | }, |
| | | { |
| | | question: 'æ¨å¯¹çå¾
å°±è¯åæ²»ççæ¶é´æ¯å¦æ»¡æï¼', |
| | | type: 1, |
| | | options: [ |
| | | { value: 'é常满æ', text: 'é常满æ' }, |
| | | { value: '满æ', text: '满æ' }, |
| | | { value: 'ä¸è¬', text: 'ä¸è¬' }, |
| | | { value: '䏿»¡æ', text: '䏿»¡æ' }, |
| | | { value: 'é叏䏿»¡æ', text: 'é叏䏿»¡æ' } |
| | | ], |
| | | answer: '䏿»¡æ', |
| | | additional: 'é¢çº¦ç9ç¹ï¼å®é
10ç¹æè§å°å»ç' |
| | | } |
| | | ]; |
| | | resolve(); |
| | | }, 300); |
| | | }); |
| | | // è·åç§å®¤å表 |
| | | async getDeptOptions() { |
| | | try { |
| | | const res = await deptTreeSelect(); |
| | | if (res.code == 200) { |
| | | this.deptList = this.flattenArray(res.data) || []; |
| | | } |
| | | } catch (error) { |
| | | console.error('è·åç§å®¤å表失败:', error); |
| | | } |
| | | }, |
| | | |
| | | // å è½½å¤çè®°å½ |
| | | async loadProcessRecords() { |
| | | return new Promise(resolve => { |
| | | setTimeout(() => { |
| | | this.processRecords = [ |
| | | { |
| | | id: 1, |
| | | time: '2024-01-15 14:20:30', |
| | | user: 'å¼ å»ç', |
| | | status: 1, // å¤çä¸ |
| | | reportDepts: ['å»å¡ç§', 'æ¤çé¨'], |
| | | remark: 'å·²æ¶å°åé¦ï¼æ£å¨å®æç¸å
³äººåæ ¸æ¥æ
åµ', |
| | | attachments: [ |
| | | { id: 1, name: 'åæ¥è°æ¥è®°å½.docx' }, |
| | | { id: 2, name: 'æ£è
æ²éè®°å½.jpg' } |
| | | ] |
| | | }, |
| | | { |
| | | id: 2, |
| | | time: '2024-01-15 10:45:12', |
| | | user: 'ç³»ç»', |
| | | status: 0, // å¾
å¤ç |
| | | remark: 'ç³»ç»èªå¨è¯å«ä¸ºå¼å¸¸åé¦ï¼å·²åé
å°è´£ä»»ç§å®¤', |
| | | attachments: [] |
| | | } |
| | | ]; |
| | | resolve(); |
| | | }, 300); |
| | | }); |
| | | }, |
| | | // å±å¹³æ°ç» |
| | | flattenArray(multiArray) { |
| | | let result = []; |
| | | |
| | | // 夿æ¯å¦ä¸ºä¸æ»¡æé项 |
| | | isUnsatisfactoryOption(value) { |
| | | const unsatisfactoryValues = [ |
| | | '䏿»¡æ', |
| | | 'é叏䏿»¡æ', |
| | | 'ä¸å¤ä¸ä¸', |
| | | 'é常ä¸ä¸ä¸', |
| | | 'ä¸å¤å
å', |
| | | 'é常ä¸å
å' |
| | | ]; |
| | | return unsatisfactoryValues.includes(value); |
| | | function flatten(element) { |
| | | if (element.children && element.children.length > 0) { |
| | | element.children.forEach((child) => flatten(child)); |
| | | } else { |
| | | let item = JSON.parse(JSON.stringify(element)); |
| | | result.push(item); |
| | | } |
| | | } |
| | | |
| | | multiArray.forEach((element) => flatten(element)); |
| | | return result; |
| | | }, |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | getStatusTagType(status) { |
| | | switch (status) { |
| | | case 0: return 'warning'; // å¾
å¤ç |
| | | case 1: return 'primary'; // å¤çä¸ |
| | | case 2: return 'success'; // å·²å¤ç |
| | | case 3: return 'danger'; // 已驳å |
| | | getStatusTagType(handleFlag) { |
| | | switch (handleFlag) { |
| | | case '0': return 'warning'; // æªå¤ç |
| | | case '1': return 'success'; // å·²å¤ç |
| | | default: return 'info'; |
| | | } |
| | | }, |
| | | |
| | | // è·åç¶æææ¬ |
| | | getStatusText(status) { |
| | | switch (status) { |
| | | case 0: return 'å¾
å¤ç'; |
| | | case 1: return 'å¤çä¸'; |
| | | case 2: return 'å·²å¤ç'; |
| | | case 3: return '已驳å'; |
| | | getStatusText(handleFlag) { |
| | | switch (handleFlag) { |
| | | case '0': return 'æªå¤ç'; |
| | | case '1': return 'å·²å¤ç'; |
| | | default: return 'æªç¥'; |
| | | } |
| | | }, |
| | | |
| | | // è·åå¤çç»æææ¬ |
| | | getHandleresultText(handleresult) { |
| | | const map = { |
| | | 'resolved': '已解å³', |
| | | 'explained': '已解é', |
| | | 'transferred': '已转交', |
| | | 'improvement': 'éæ¹è¿', |
| | | 'rejected': '已驳å' |
| | | }; |
| | | return map[handleresult] || handleresult; |
| | | }, |
| | | |
| | | // è·åç§å®¤æ°ç» |
| | | getDeptArray(ccdepts) { |
| | | if (!ccdepts) return []; |
| | | return ccdepts.split(','); |
| | | }, |
| | | |
| | | // ææ¾é³é¢ |
| | | handlePlayAudio(url) { |
| | | this.audioUrl = url; |
| | | this.$nextTick(() => { |
| | | const audioPlayer = this.$refs.audioPlayer; |
| | | if (audioPlayer) { |
| | | audioPlayer.play().catch(error => { |
| | | console.error('ææ¾å¤±è´¥:', error); |
| | | this.$message.error('é³é¢ææ¾å¤±è´¥'); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // è·å䏻颿 ·å¼ç±» |
| | | getTopicClass(item) { |
| | | if (item.isabnormal == 1) { |
| | | return "scriptTopic-isabnormal"; |
| | | } else if (item.isabnormal == 2) { |
| | | return "scriptTopic-warning"; |
| | | } else { |
| | | return "scriptTopic-dev"; |
| | | } |
| | | }, |
| | | |
| | | // è·åéé¡¹æ ·å¼ç±» |
| | | getOptionClass(items) { |
| | | if (items.isabnormal == 1) { |
| | | return "red-star"; |
| | | } else if (items.isabnormal == 2) { |
| | | return "yellow-star"; |
| | | } |
| | | return ""; |
| | | }, |
| | | |
| | | // å è½½æ°æ® |
| | | async loadData() { |
| | | this.loading = true; |
| | | try { |
| | | this.hasQualityPermission = this.checkQualityPermission(); |
| | | await this.getDeptOptions(); |
| | | |
| | | if (Object.keys(this.currentRecord).length === 0) { |
| | | this.currentRecord = this.recordData || {}; |
| | | } |
| | | |
| | | // 妿å½åè®°å½æ¯è¯é³æ¨¡æ¿ï¼å è½½è¯é³æ°æ® |
| | | if (this.currentRecord.templateType === 1) { |
| | | await this.loadVoiceData(); |
| | | this.activeName = 'yy'; |
| | | } else if (this.currentRecord.templateType === 2) { |
| | | await this.loadQuestionnaireData(); |
| | | this.activeName = 'wj'; |
| | | } |
| | | |
| | | await this.loadProcessRecords(); |
| | | } catch (error) { |
| | | console.error('å 载详æ
æ°æ®å¤±è´¥:', error); |
| | | this.$message.error('å è½½æ°æ®å¤±è´¥'); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | // éç½®æ°æ® |
| | | resetData() { |
| | | this.currentRecord = {}; |
| | | this.templateData = []; |
| | | this.voiceData = []; |
| | | this.processRecords = []; |
| | | this.voiceAudioUrl = ''; |
| | | this.taskName = ''; |
| | | this.activeName = 'wj'; |
| | | }, |
| | | |
| | | // å è½½é®å·æ°æ® |
| | | async loadQuestionnaireData() { |
| | | try { |
| | | // è¿ééè¦æ ¹æ®å®é
æ
åµè°ç¨æ¥å£è·åé®å·æ°æ® |
| | | // 妿recordDataä¸å·²ç»å
å«äºé®å·æ°æ®ï¼å¯ä»¥ç´æ¥ä½¿ç¨ |
| | | if (this.currentRecord.taskid && this.currentRecord.patid) { |
| | | const params = { |
| | | taskid: this.currentRecord.taskid, |
| | | patid: this.currentRecord.patid, |
| | | subId: this.currentRecord.subId || this.currentRecord.id, |
| | | isFinish: true |
| | | }; |
| | | |
| | | const res = await getsearchrResults(params); |
| | | if (res.code === 200 && res.data) { |
| | | this.templateData = res.data.scriptResult || []; |
| | | this.taskName = res.data.taskName || ''; |
| | | |
| | | // å¤çæ°æ®æ ¼å¼ |
| | | this.templateData.forEach((item) => { |
| | | if (item.scriptType == 2) item.scriptResult = []; |
| | | if (item.scriptResultId && item.scriptType != 2) { |
| | | item.isoption = 3; |
| | | item.scriptResult = item.scriptResult; |
| | | } else if (item.scriptResultId && item.scriptType == 2) { |
| | | item.scriptResult = item.scriptResult.split("&"); |
| | | item.isoption = 3; |
| | | } |
| | | }); |
| | | |
| | | this.overdata(); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('å è½½é®å·æ°æ®å¤±è´¥:', error); |
| | | } |
| | | }, |
| | | |
| | | // å¤çå¼å¸¸æ°æ® |
| | | overdata() { |
| | | this.templateData.forEach((item, index) => { |
| | | var obj = item.svyTaskTemplateTargetoptions.find( |
| | | (items) => items.optioncontent == item.scriptResult |
| | | ); |
| | | if (obj && obj.isabnormal) { |
| | | this.templateData[index].isabnormal = obj.isabnormal; |
| | | } |
| | | this.$forceUpdate(); |
| | | }); |
| | | }, |
| | | |
| | | // å è½½è¯é³æ°æ® |
| | | async loadVoiceData() { |
| | | try { |
| | | if (this.currentRecord.taskid && this.currentRecord.patid) { |
| | | const params = { |
| | | taskid: this.currentRecord.taskid, |
| | | patid: this.currentRecord.patid, |
| | | subId: this.currentRecord.subId || this.currentRecord.id |
| | | }; |
| | | |
| | | const res = await getPersonVoices(params); |
| | | if (res.code == 200) { |
| | | this.voiceData = res.data.serviceSubtaskDetails || []; |
| | | this.voiceAudioUrl = res.data.voice || ''; |
| | | this.taskName = res.data.taskName || ''; |
| | | this.templateData = res.data.filteredDetails || []; |
| | | |
| | | this.templateData.forEach((item) => { |
| | | if (item.targetvalue) { |
| | | item.scriptResult = item.targetvalue.split("&"); |
| | | } else { |
| | | item.scriptResult = []; |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error('å è½½è¯é³æ°æ®å¤±è´¥:', error); |
| | | } |
| | | }, |
| | | |
| | | // å è½½å¤çè®°å½ |
| | | async loadProcessRecords() { |
| | | try { |
| | | // è¿éå¯ä»¥æ ¹æ®recordIdå è½½å¤çåå² |
| | | // ææ¶ä½¿ç¨å½åè®°å½çå¤çä¿¡æ¯ |
| | | if (this.currentRecord.handleTime) { |
| | | this.processRecords = [{ |
| | | ...this.currentRecord, |
| | | time: this.currentRecord.handleTime |
| | | }]; |
| | | } |
| | | } catch (error) { |
| | | console.error('å è½½å¤çè®°å½å¤±è´¥:', error); |
| | | } |
| | | }, |
| | | |
| | | // å¤çå¼å¸¸ |
| | | handleProcess() { |
| | | this.processForm = { |
| | | status: this.currentRecord.processStatus === 0 ? 1 : this.currentRecord.processStatus, |
| | | reportDepts: [], |
| | | remark: '' |
| | | handleFlag: this.currentRecord.handleFlag === '0' ? '1' : '0', |
| | | ccdepts: this.currentRecord.ccdepts ? this.currentRecord.ccdepts.split(',') : [], |
| | | handleresult: this.currentRecord.handleresult || '', |
| | | handledesc: this.currentRecord.handledesc || '', |
| | | finaloption: this.currentRecord.finaloption || '' |
| | | }; |
| | | this.processDialogVisible = true; |
| | | }, |
| | |
| | | // æäº¤å¤ç |
| | | async submitProcess() { |
| | | this.$refs.processForm.validate(async (valid) => { |
| | | if (valid) { |
| | | this.processing = true; |
| | | try { |
| | | // Mock APIè°ç¨ |
| | | await new Promise(resolve => setTimeout(resolve, 1000)); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | |
| | | this.$message.success('å¤çæäº¤æå'); |
| | | this.processing = true; |
| | | try { |
| | | const submitData = { |
| | | id: this.currentRecord.id, |
| | | handleFlag: this.processForm.handleFlag, |
| | | handleresult: this.processForm.handleresult, |
| | | handledesc: this.processForm.handledesc, |
| | | finaloption: this.processForm.finaloption, |
| | | ccdepts: Array.isArray(this.processForm.ccdepts) |
| | | ? this.processForm.ccdepts.join(",") |
| | | : this.processForm.ccdepts |
| | | }; |
| | | |
| | | const res = await traceedit(submitData); |
| | | if (res.code === 200) { |
| | | this.$message.success("å¤çæäº¤æå"); |
| | | this.processDialogVisible = false; |
| | | |
| | | // éæ°å è½½æ°æ® |
| | | await this.loadData(); |
| | | // æ´æ°å½åè®°å½ |
| | | this.currentRecord = { |
| | | ...this.currentRecord, |
| | | ...submitData, |
| | | handleBy: this.$store.getters.name, // å½åç¨æ· |
| | | handleTime: new Date().toISOString().replace('T', ' ').substr(0, 19) |
| | | }; |
| | | |
| | | // éæ°å è½½å¤çè®°å½ |
| | | await this.loadProcessRecords(); |
| | | |
| | | // 触åç¶ç»ä»¶å·æ° |
| | | this.$emit('processed'); |
| | | } finally { |
| | | this.processing = false; |
| | | } else { |
| | | this.$message.error(res.msg || "å¤çæäº¤å¤±è´¥"); |
| | | } |
| | | } catch (error) { |
| | | console.error("å¤çæäº¤å¤±è´¥:", error); |
| | | this.$message.error("å¤çæäº¤å¤±è´¥ï¼è¯·ç¨åéè¯"); |
| | | } finally { |
| | | this.processing = false; |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // é¢è§æä»¶ |
| | | handlePreviewFile(file) { |
| | | this.$message.info(`é¢è§æä»¶: ${file.name}`); |
| | | }, |
| | | |
| | | // å¤çå¯¹è¯æ¡å
³é |
| | | handleClose() { |
| | | this.$emit('close'); |
| | | }, |
| | | |
| | | // æä»¶ä¸ä¼ ç¸å
³æ¹æ³ |
| | | handleFilePreview(file) { |
| | | console.log('é¢è§æä»¶:', file); |
| | | }, |
| | | |
| | | handleFileRemove(file, fileList) { |
| | | console.log('ç§»é¤æä»¶:', file, fileList); |
| | | }, |
| | | |
| | | beforeFileRemove(file) { |
| | | return this.$confirm(`ç¡®å®ç§»é¤ ${file.name}ï¼`); |
| | | }, |
| | | |
| | | handleFileExceed(files, fileList) { |
| | | this.$message.warning(`å½åéå¶éæ© 3 个æä»¶ï¼æ¬æ¬¡éæ©äº ${files.length} 个æä»¶ï¼å
±éæ©äº ${files.length + fileList.length} 个æä»¶`); |
| | | } |
| | | } |
| | | }; |
| | |
| | | font-size: 14px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | flex: 1; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .questionnaire-section { |
| | | .content-container { |
| | | margin-bottom: 20px; |
| | | padding: 20px; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | border: 1px solid #ebeef5; |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 15px; |
| | | padding-bottom: 10px; |
| | | border-bottom: 2px solid #409EFF; |
| | | ::v-deep .el-tabs__content { |
| | | padding: 20px; |
| | | background: #fff; |
| | | border-radius: 0 0 4px 4px; |
| | | } |
| | | |
| | | .questionnaire-content { |
| | | .question-item { |
| | | margin-bottom: 20px; |
| | | padding: 15px; |
| | | border-radius: 6px; |
| | | border: 1px solid #ebeef5; |
| | | transition: all 0.3s; |
| | | .CONTENT, .borderdiv { |
| | | padding: 20px; |
| | | background: #fff; |
| | | border-radius: 6px; |
| | | |
| | | &:hover { |
| | | border-color: #409EFF; |
| | | box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 0.1); |
| | | .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 10px; |
| | | border-bottom: 2px solid #409EFF; |
| | | } |
| | | |
| | | .voice-audio { |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10px; |
| | | |
| | | .audio-player { |
| | | flex: 1; |
| | | } |
| | | } |
| | | |
| | | .preview-left { |
| | | .topic-dev { |
| | | margin-bottom: 20px; |
| | | padding: 15px; |
| | | border-radius: 6px; |
| | | border: 1px solid #ebeef5; |
| | | background: #fff; |
| | | |
| | | .dev-text { |
| | | font-size: 15px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | margin-bottom: 15px; |
| | | line-height: 1.5; |
| | | |
| | | span { |
| | | color: #606266; |
| | | } |
| | | } |
| | | |
| | | .dev-xx { |
| | | ::v-deep .el-radio-group, |
| | | ::v-deep .el-checkbox-group { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | ::v-deep .el-radio, |
| | | ::v-deep .el-checkbox { |
| | | margin: 0; |
| | | padding: 8px 12px; |
| | | border-radius: 4px; |
| | | border: 1px solid #ebeef5; |
| | | transition: all 0.3s; |
| | | |
| | | &:hover { |
| | | background: #f5f7fa; |
| | | } |
| | | |
| | | &.red-star { |
| | | border-color: #f56c6c; |
| | | background: #fef0f0; |
| | | } |
| | | |
| | | &.yellow-star { |
| | | border-color: #e6a23c; |
| | | background: #fdf6ec; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .append-input-container { |
| | | margin-top: 15px; |
| | | } |
| | | |
| | | .el-alert { |
| | | margin-top: 10px; |
| | | } |
| | | } |
| | | |
| | | .question-header { |
| | | .leftside { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | padding-bottom: 10px; |
| | | border-bottom: 1px dashed #dcdfe6; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | font-size: 15px; |
| | | font-weight: 500; |
| | | color: #303133; |
| | | |
| | | .question-index { |
| | | font-weight: 600; |
| | | i { |
| | | color: #409EFF; |
| | | margin-right: 8px; |
| | | font-size: 15px; |
| | | } |
| | | } |
| | | |
| | | .offside { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | gap: 10px; |
| | | margin-bottom: 20px; |
| | | |
| | | i { |
| | | color: #67C23A; |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | .question-text { |
| | | .offside-value { |
| | | flex: 1; |
| | | font-size: 15px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .question-type { |
| | | margin-left: 10px; |
| | | } |
| | | } |
| | | |
| | | .question-options { |
| | | ::v-deep .el-radio-group { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | ::v-deep .el-checkbox-group { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15px; |
| | | } |
| | | |
| | | ::v-deep .el-radio, |
| | | ::v-deep .el-checkbox { |
| | | margin: 0; |
| | | padding: 8px 12px; |
| | | border-radius: 4px; |
| | | border: 1px solid #ebeef5; |
| | | transition: all 0.3s; |
| | | |
| | | &:hover { |
| | | background: #f5f7fa; |
| | | .el-textarea { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | &.unsatisfactory-option { |
| | | border-color: #e6a23c; |
| | | background: #fdf6ec; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .additional-remark { |
| | | margin-top: 15px; |
| | | padding: 12px; |
| | | background: #f0f9ff; |
| | | border-radius: 6px; |
| | | border-left: 4px solid #409EFF; |
| | | |
| | | .remark-label { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | font-weight: 500; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .remark-content { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | } |
| | |
| | | line-height: 1.5; |
| | | } |
| | | } |
| | | |
| | | .process-attachments { |
| | | .label { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | ::v-deep .el-button { |
| | | margin-right: 8px; |
| | | margin-bottom: 5px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-record { |
| | | text-align: center; |
| | | padding: 40px 0; |
| | | color: #909399; |
| | | font-style: italic; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | } |
| | | } |
| | | |
| | |
| | | gap: 10px; |
| | | } |
| | | } |
| | | |
| | | // å¼å¸¸æ ·å¼ |
| | | .scriptTopic-isabnormal { |
| | | border-color: #f56c6c !important; |
| | | background: #fef0f0 !important; |
| | | } |
| | | |
| | | .scriptTopic-warning { |
| | | border-color: #e6a23c !important; |
| | | background: #fdf6ec !important; |
| | | } |
| | | </style> |
| | |
| | | label-width="120px" |
| | | class="search-form" |
| | | > |
| | | <el-form-item label="满æåº¦æ¨¡æ¿" prop="templateId"> |
| | | <el-form-item label="满æåº¦ç±»å" prop="templateid"> |
| | | <el-select |
| | | v-model="queryParams.templateId" |
| | | v-model="queryParams.templateType" |
| | | placeholder="è¯·éæ©æ¨¡æ¿" |
| | | clearable |
| | | style="width: 200px" |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="责任ç§å®¤" prop="deptIds"> |
| | | <el-form-item label="责任ç§å®¤" prop="todeptcode"> |
| | | <el-select |
| | | v-model="queryParams.deptIds" |
| | | v-model="queryParams.todeptcode" |
| | | placeholder="è¯·éæ©è´£ä»»ç§å®¤" |
| | | clearable |
| | | filterable |
| | |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="ç»è®¡æ¶é´" prop="dateRange"> |
| | | <el-form-item label="å¤çæ¶é´" prop="handleTimeRange"> |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | type="daterange" |
| | | v-model="queryParams.handleTimeRange" |
| | | type="datetimerange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | value-format="yyyy-MM-dd" |
| | | start-placeholder="å¼å§æ¶é´" |
| | | end-placeholder="ç»ææ¶é´" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | :picker-options="pickerOptions" |
| | | style="width: 380px" |
| | | /> |
| | |
| | | > |
| | | æ¹éå¤ç ({{ selectedIds.length }}) |
| | | </el-button> |
| | | <el-button |
| | | type="info" |
| | | icon="el-icon-download" |
| | | @click="handleExport" |
| | | > |
| | | <el-button type="info" icon="el-icon-download" @click="handleExport"> |
| | | 导åºå¼å¸¸æ°æ® |
| | | </el-button> |
| | | <el-button |
| | |
| | | <!-- å¼å¸¸ç»è®¡æ¦è§ --> |
| | | <div class="overview-section"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-col :span="8"> |
| | | <el-card shadow="never" class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon" style="background: #f0f9ff;"> |
| | | <i class="el-icon-s-claim" style="color: #5788FE;"></i> |
| | | <div class="stat-icon" style="background: #f0f9ff"> |
| | | <i class="el-icon-s-claim" style="color: #5788fe"></i> |
| | | </div> |
| | | <div class="stat-info"> |
| | | <div class="stat-title">æ»å¼å¸¸æ°é</div> |
| | | <div class="stat-value">{{ overviewData.totalExceptionCount }}</div> |
| | | <div class="stat-value"> |
| | | {{ overviewData.totalExceptionCount }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-col :span="8"> |
| | | <el-card shadow="never" class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon" style="background: #f0f9ff;"> |
| | | <i class="el-icon-s-flag" style="color: #E6A23C;"></i> |
| | | <div class="stat-icon" style="background: #f0f9ff"> |
| | | <i class="el-icon-s-flag" style="color: #e6a23c"></i> |
| | | </div> |
| | | <div class="stat-info"> |
| | | <div class="stat-title">å¾
å¤çå¼å¸¸</div> |
| | |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-col :span="8"> |
| | | <el-card shadow="never" class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon" style="background: #f0f9ff;"> |
| | | <i class="el-icon-check" style="color: #67C23A;"></i> |
| | | <div class="stat-icon" style="background: #f0f9ff"> |
| | | <i class="el-icon-check" style="color: #67c23a"></i> |
| | | </div> |
| | | <div class="stat-info"> |
| | | <div class="stat-title">å·²å¤çå¼å¸¸</div> |
| | | <div class="stat-value">{{ overviewData.processedCount }}</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="never" class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-icon" style="background: #f0f9ff;"> |
| | | <i class="el-icon-s-order" style="color: #909399;"></i> |
| | | </div> |
| | | <div class="stat-info"> |
| | | <div class="stat-title">仿¥å¤çæ°</div> |
| | | <div class="stat-value">{{ overviewData.todayProcessedCount }}</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | |
| | | @selection-change="handleSelectionChange" |
| | | class="exception-table" |
| | | > |
| | | <el-table-column |
| | | type="selection" |
| | | width="55" |
| | | align="center" |
| | | /> |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | |
| | | <el-table-column |
| | | label="åºå·" |
| | |
| | | |
| | | <el-table-column |
| | | label="é¢ç®å
容" |
| | | prop="questionContent" |
| | | prop="questiontext" |
| | | min-width="300" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <div class="question-content"> |
| | | <span class="question-text">{{ row.questionContent }}</span> |
| | | <span class="question-text">{{ row.questiontext }}</span> |
| | | <div class="question-tags"> |
| | | <el-tag |
| | | size="mini" |
| | | :type="getQuestionTypeTag(row.questionType)" |
| | | :type="getTemplateTypeTag(row.templateType)" |
| | | > |
| | | {{ row.questionType === 1 ? 'åéé¢' : 'å¤éé¢' }} |
| | | </el-tag> |
| | | <el-tag |
| | | size="mini" |
| | | type="info" |
| | | > |
| | | {{ row.templateName }} |
| | | {{ row.templateType === 1 ? "è¯é³æ¨¡æ¿" : "é®å·æ¨¡æ¿" }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | |
| | | |
| | | <el-table-column |
| | | label="è´è´£ç§å®¤" |
| | | prop="responsibilityDepts" |
| | | prop="responsibleDept" |
| | | width="180" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <div class="dept-list"> |
| | | <el-tag |
| | | v-for="dept in row.responsibilityDepts" |
| | | :key="dept.id" |
| | | v-for="dept in row.responsibleDept" |
| | | :key="dept.deptCode" |
| | | size="small" |
| | | type="primary" |
| | | class="dept-tag" |
| | | > |
| | | {{ dept.name }} |
| | | {{ dept.deptName }} |
| | | </el-tag> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="å¡«åæ
åµ" |
| | | width="200" |
| | | align="center" |
| | | > |
| | | <el-table-column label="å¡«åæ
åµ" width="200" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="fill-statistics"> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">ææå¡«åï¼</span> |
| | | <span class="stat-value">{{ row.validFillCount }}</span> |
| | | <span class="stat-value">{{ |
| | | row.fillSituation.effectiveFillNum |
| | | }}</span> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <span class="stat-label">å¼å¸¸å¡«åï¼</span> |
| | | <span class="stat-value exception-count">{{ row.exceptionFillCount }}</span> |
| | | <span class="stat-value exception-count">{{ |
| | | row.fillSituation.exceptionFillNum |
| | | }}</span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="å¼å¸¸ä»»å¡" |
| | | width="280" |
| | | align="center" |
| | | > |
| | | <el-table-column label="å¼å¸¸ä»»å¡" width="280" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="exception-tasks"> |
| | | <div class="task-category"> |
| | | <div class="task-title">å·²å¤ç</div> |
| | | <div class="task-count processed">{{ row.processedCount }}</div> |
| | | <div class="task-count processed"> |
| | | {{ row.exceptionQuesNum.yesDeal }} |
| | | </div> |
| | | </div> |
| | | <div class="task-category"> |
| | | <div class="task-title">å¾
å¤ç</div> |
| | | <div class="task-count pending">{{ row.pendingCount }}</div> |
| | | <div class="task-count pending"> |
| | | {{ row.exceptionQuesNum.noDeal }} |
| | | </div> |
| | | </div> |
| | | <div class="task-category"> |
| | | <div class="task-title">å¼å¸¸æ»æ°</div> |
| | | <div class="task-count total">{{ row.totalExceptionCount }}</div> |
| | | <div class="task-count total"> |
| | | {{ row.exceptionQuesNum.all }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | <el-table-column |
| | | label="æè¿å¤ç" |
| | | prop="lastProcessTime" |
| | | prop="handleTime" |
| | | width="180" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.lastProcessTime" class="last-process"> |
| | | <div class="process-time">{{ row.lastProcessTime }}</div> |
| | | <div class="process-user">{{ row.lastProcessUser }}</div> |
| | | <div v-if="row.handleTime" class="last-process"> |
| | | <div class="process-time"> |
| | | {{ formatDateTime(row.handleTime) }} |
| | | </div> |
| | | <div class="process-user">{{ row.handleBy || "ç³»ç»å¤ç" }}</div> |
| | | </div> |
| | | <span v-else class="no-process">ææ å¤çè®°å½</span> |
| | | </template> |
| | |
| | | fixed="right" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <!-- <el-button |
| | | type="primary" |
| | | size="small" |
| | | icon="el-icon-view" |
| | | @click="handleViewDetail(row)" |
| | | > |
| | | 详æ
|
| | | </el-button> --> |
| | | <el-button |
| | | type="warning" |
| | | size="small" |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { tracedeallist } from "@/api/AiCentre/index"; |
| | | import { deptTreeSelect } from "@/api/system/user"; |
| | | |
| | | export default { |
| | | name: 'ExceptionList', |
| | | name: "ExceptionList", |
| | | data() { |
| | | return { |
| | | // æ¥è¯¢åæ° |
| | | queryParams: { |
| | | templateId: '', |
| | | deptIds: [], |
| | | dateRange: [], |
| | | todeptcode: [], // å¤çç§å®¤ç¼å·æ°ç» |
| | | todeptname: "", // å¤çç§å®¤åç§° |
| | | templateType: 2, // 任塿¨¡æ¿ID |
| | | handleStartTime: "", // å¤çå¼å§æ¶é´ |
| | | handleEndTime: "", // å¤çç»ææ¶é´ |
| | | handleTimeRange: [], // æ¶é´èå´ï¼ç¨äºçé¢å±ç¤º |
| | | pageNum: 1, |
| | | pageSize: 10 |
| | | pageSize: 10, |
| | | }, |
| | | |
| | | // å è½½ç¶æ |
| | |
| | | |
| | | // 模æ¿å表 |
| | | templateList: [ |
| | | { id: 1, name: 'åºé¢æ»¡æåº¦é®å·' }, |
| | | { id: 2, name: 'ä½é¢æ»¡æåº¦é®å·' }, |
| | | { id: 3, name: 'é¨è¯æ»¡æåº¦é®å·' }, |
| | | { id: 4, name: 'å¸¸ç¨æ»¡æåº¦é®å·' } |
| | | { id: 1, name: "è¯é³æ»¡æåº¦" }, |
| | | { id: 2, name: "é®å·æ»¡æåº¦" }, |
| | | // ä½ å¯ä»¥æ ¹æ®å®é
æ
åµä»æ¥å£è·å模æ¿å表 |
| | | ], |
| | | |
| | | // ç§å®¤å表 |
| | | deptList: [ |
| | | { id: 1, name: 'å¿è¡ç®¡å
ç§' }, |
| | | { id: 2, name: 'ç¥ç»å
ç§' }, |
| | | { id: 3, name: 'æ®å¤ç§' }, |
| | | { id: 4, name: '骨ç§' }, |
| | | { id: 5, name: 'å¦äº§ç§' }, |
| | | { id: 6, name: 'å¿ç§' }, |
| | | { id: 7, name: 'æ¥è¯ç§' }, |
| | | { id: 8, name: 'å¼å¸å
ç§' }, |
| | | { id: 9, name: 'æ¶åå
ç§' }, |
| | | { id: 10, name: 'å
åæ³ç§' }, |
| | | { id: 11, name: 'è¾å
ç§' }, |
| | | { id: 12, name: 'è¿ç¤ç§' } |
| | | ], |
| | | deptList: [], |
| | | |
| | | // å¼å¸¸åè¡¨æ°æ® |
| | | exceptionList: [], |
| | |
| | | totalExceptionCount: 0, |
| | | pendingCount: 0, |
| | | processedCount: 0, |
| | | todayProcessedCount: 0 |
| | | todayProcessedCount: 0, |
| | | }, |
| | | |
| | | // æ¥æéæ©å¨é项 |
| | | pickerOptions: { |
| | | shortcuts: [ |
| | | { |
| | | text: 'æè¿ä¸å¨', |
| | | text: "æè¿ä¸å¨", |
| | | onClick(picker) { |
| | | const end = new Date(); |
| | | const start = new Date(); |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); |
| | | picker.$emit('pick', [start, end]); |
| | | } |
| | | picker.$emit("pick", [start, end]); |
| | | }, |
| | | }, |
| | | { |
| | | text: 'æè¿ä¸ä¸ªæ', |
| | | text: "æè¿ä¸ä¸ªæ", |
| | | onClick(picker) { |
| | | const end = new Date(); |
| | | const start = new Date(); |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); |
| | | picker.$emit('pick', [start, end]); |
| | | } |
| | | picker.$emit("pick", [start, end]); |
| | | }, |
| | | }, |
| | | { |
| | | text: 'æè¿ä¸ä¸ªæ', |
| | | text: "æè¿ä¸ä¸ªæ", |
| | | onClick(picker) { |
| | | const end = new Date(); |
| | | const start = new Date(); |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); |
| | | picker.$emit('pick', [start, end]); |
| | | } |
| | | } |
| | | picker.$emit("pick", [start, end]); |
| | | }, |
| | | }, |
| | | ], |
| | | disabledDate(time) { |
| | | return time.getTime() > Date.now(); |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | }, |
| | | |
| | | mounted() { |
| | | this.loadData(); |
| | | this.getDeptOptions(); |
| | | }, |
| | | |
| | | methods: { |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | formatDateTime(dateTime) { |
| | | if (!dateTime) return ""; |
| | | const date = new Date(dateTime); |
| | | return ( |
| | | date.toLocaleDateString().replace(/\//g, "-") + |
| | | " " + |
| | | date.toTimeString().split(" ")[0] |
| | | ); |
| | | }, |
| | | |
| | | // è·å模æ¿ç±»åæ ç¾æ ·å¼ |
| | | getTemplateTypeTag(type) { |
| | | return type === 1 ? "primary" : "success"; |
| | | }, |
| | | |
| | | // æå»ºæ¥è¯¢åæ° |
| | | buildQueryParams() { |
| | | const params = { |
| | | pageNum: this.queryParams.pageNum, |
| | | pageSize: this.queryParams.pageSize, |
| | | }; |
| | | |
| | | // å¤çç§å®¤ç¼å· |
| | | if ( |
| | | this.queryParams.todeptcode && |
| | | this.queryParams.todeptcode.length > 0 |
| | | ) { |
| | | // æ¥å£å¯è½éè¦åç¬¦ä¸²æ ¼å¼çç§å®¤ç¼å·ï¼æ ¹æ®å®é
æ
åµè°æ´ |
| | | params.todeptcode = this.queryParams.todeptcode.join(","); |
| | | } |
| | | |
| | | // 模æ¿ID |
| | | if (this.queryParams.templateType) { |
| | | params.templateType = this.queryParams.templateType; |
| | | } |
| | | |
| | | // å¤çæ¶é´èå´ |
| | | if ( |
| | | this.queryParams.handleTimeRange && |
| | | this.queryParams.handleTimeRange.length === 2 |
| | | ) { |
| | | params.handleStartTime = this.queryParams.handleTimeRange[0]; |
| | | params.handleEndTime = this.queryParams.handleTimeRange[1]; |
| | | } |
| | | |
| | | return params; |
| | | }, |
| | | /** æ¥è¯¢ç§å®¤å表 */ |
| | | getDeptOptions() { |
| | | deptTreeSelect() |
| | | .then((res) => { |
| | | if (res.code == 200) { |
| | | this.deptList = this.flattenArray(res.data) || []; |
| | | } |
| | | }) |
| | | .catch((error) => { |
| | | console.error("è·åç§å®¤å表失败:", error); |
| | | this.$message.error("è·åç§å®¤å表失败"); |
| | | }); |
| | | }, |
| | | flattenArray(multiArray) { |
| | | let result = []; |
| | | |
| | | function flatten(element) { |
| | | if (element.children && element.children.length > 0) { |
| | | element.children.forEach((child) => flatten(child)); |
| | | } else { |
| | | let item = JSON.parse(JSON.stringify(element)); |
| | | result.push(item); |
| | | } |
| | | } |
| | | |
| | | multiArray.forEach((element) => flatten(element)); |
| | | return result; |
| | | }, |
| | | // å è½½æ°æ® |
| | | async loadData() { |
| | | this.loading = true; |
| | | try { |
| | | await Promise.all([ |
| | | this.loadExceptionList(), |
| | | this.loadOverviewData() |
| | | ]); |
| | | await Promise.all([this.loadExceptionList(), this.loadOverviewData()]); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | |
| | | |
| | | // å è½½å¼å¸¸å表 |
| | | async loadExceptionList() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | // Mock æ°æ® |
| | | this.exceptionList = [ |
| | | { |
| | | id: 1, |
| | | questionId: 101, |
| | | questionContent: 'æ¨å¯¹å»æ¤äººåçæå¡æåº¦æ¯å¦æ»¡æï¼', |
| | | questionType: 1, // 1: åéé¢, 2: å¤éé¢ |
| | | templateName: 'åºé¢æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 1, name: 'å¿è¡ç®¡å
ç§' }, |
| | | { id: 2, name: 'ç¥ç»å
ç§' } |
| | | ], |
| | | validFillCount: 145, |
| | | exceptionFillCount: 8, |
| | | processedCount: 5, |
| | | pendingCount: 3, |
| | | totalExceptionCount: 8, |
| | | lastProcessTime: '2024-01-15 10:30:25', |
| | | lastProcessUser: 'å¼ å»ç' |
| | | }, |
| | | { |
| | | id: 2, |
| | | questionId: 102, |
| | | questionContent: 'æ¨å¯¹å»ççè¯çæ°´å¹³åææ¯è½åè¯ä»·å¦ä½ï¼', |
| | | questionType: 1, |
| | | templateName: 'ä½é¢æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 3, name: 'æ®å¤ç§' }, |
| | | { id: 4, name: '骨ç§' } |
| | | ], |
| | | validFillCount: 120, |
| | | exceptionFillCount: 12, |
| | | processedCount: 8, |
| | | pendingCount: 4, |
| | | totalExceptionCount: 12, |
| | | lastProcessTime: '2024-01-14 16:20:10', |
| | | lastProcessUser: 'ææ¤å£«é¿' |
| | | }, |
| | | { |
| | | id: 3, |
| | | questionId: 103, |
| | | questionContent: 'æ¨å¯¹å»é¢çç¯å¢åå«çç¶åµæ¯å¦æ»¡æï¼', |
| | | questionType: 1, |
| | | templateName: 'é¨è¯æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 5, name: 'å¦äº§ç§' }, |
| | | { id: 6, name: 'å¿ç§' }, |
| | | { id: 7, name: 'æ¥è¯ç§' } |
| | | ], |
| | | validFillCount: 180, |
| | | exceptionFillCount: 15, |
| | | processedCount: 10, |
| | | pendingCount: 5, |
| | | totalExceptionCount: 15, |
| | | lastProcessTime: '2024-01-13 09:15:45', |
| | | lastProcessUser: 'ç主任' |
| | | }, |
| | | { |
| | | id: 4, |
| | | questionId: 104, |
| | | questionContent: 'æ¨è®¤ä¸ºå»æ¤äººå䏿¨çæ²éæ¯å¦å
åï¼', |
| | | questionType: 1, |
| | | templateName: 'å¸¸ç¨æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 8, name: 'å¼å¸å
ç§' }, |
| | | { id: 9, name: 'æ¶åå
ç§' } |
| | | ], |
| | | validFillCount: 95, |
| | | exceptionFillCount: 6, |
| | | processedCount: 4, |
| | | pendingCount: 2, |
| | | totalExceptionCount: 6, |
| | | lastProcessTime: '2024-01-12 14:40:30', |
| | | lastProcessUser: 'èµµå»ç' |
| | | }, |
| | | { |
| | | id: 5, |
| | | questionId: 105, |
| | | questionContent: 'æ¨å¯¹çå¾
å°±è¯åæ²»ççæ¶é´æ¯å¦æ»¡æï¼', |
| | | questionType: 1, |
| | | templateName: 'ä½é¢æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 10, name: 'å
åæ³ç§' }, |
| | | { id: 11, name: 'è¾å
ç§' } |
| | | ], |
| | | validFillCount: 200, |
| | | exceptionFillCount: 25, |
| | | processedCount: 15, |
| | | pendingCount: 10, |
| | | totalExceptionCount: 25, |
| | | lastProcessTime: '2024-01-11 11:25:15', |
| | | lastProcessUser: '忤士' |
| | | }, |
| | | { |
| | | id: 6, |
| | | questionId: 106, |
| | | questionContent: 'æ¨å¯¹å»é¢æ¶è´¹çéæåº¦ååçæ§è¯ä»·å¦ä½ï¼', |
| | | questionType: 1, |
| | | templateName: 'é¨è¯æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 12, name: 'è¿ç¤ç§' } |
| | | ], |
| | | validFillCount: 160, |
| | | exceptionFillCount: 18, |
| | | processedCount: 12, |
| | | pendingCount: 6, |
| | | totalExceptionCount: 18, |
| | | lastProcessTime: '2024-01-10 15:50:20', |
| | | lastProcessUser: 'å¨å»ç' |
| | | }, |
| | | { |
| | | id: 7, |
| | | questionId: 107, |
| | | questionContent: 'æ¨ä¼åäº²åæ¨èæä»¬å»é¢åï¼', |
| | | questionType: 1, |
| | | templateName: 'åºé¢æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 1, name: 'å¿è¡ç®¡å
ç§' }, |
| | | { id: 8, name: 'å¼å¸å
ç§' } |
| | | ], |
| | | validFillCount: 110, |
| | | exceptionFillCount: 7, |
| | | processedCount: 5, |
| | | pendingCount: 2, |
| | | totalExceptionCount: 7, |
| | | lastProcessTime: '2024-01-09 10:15:40', |
| | | lastProcessUser: 'å´ä¸»ä»»' |
| | | }, |
| | | { |
| | | id: 8, |
| | | questionId: 108, |
| | | questionContent: 'æ¨å¯¹ä»¥ä¸åªäºæ¹é¢æ¯è¾æ»¡æï¼å¤éï¼ï¼', |
| | | questionType: 2, |
| | | templateName: 'å¸¸ç¨æ»¡æåº¦é®å·', |
| | | responsibilityDepts: [ |
| | | { id: 2, name: 'ç¥ç»å
ç§' }, |
| | | { id: 3, name: 'æ®å¤ç§' }, |
| | | { id: 5, name: 'å¦äº§ç§' } |
| | | ], |
| | | validFillCount: 135, |
| | | exceptionFillCount: 9, |
| | | processedCount: 6, |
| | | pendingCount: 3, |
| | | totalExceptionCount: 9, |
| | | lastProcessTime: '2024-01-08 13:30:55', |
| | | lastProcessUser: 'éå»ç' |
| | | } |
| | | ]; |
| | | this.total = this.exceptionList.length; |
| | | resolve(); |
| | | }, 500); |
| | | }); |
| | | try { |
| | | const params = this.buildQueryParams(); |
| | | const response = await tracedeallist(params); |
| | | |
| | | if (response.code == 200) { |
| | | this.exceptionList = response.rows.detailTraceDealDTOList || []; |
| | | this.overviewData.totalExceptionCount = response.rows.totalException; |
| | | this.overviewData.pendingCount = response.rows.noDealException; |
| | | this.overviewData.processedCount = response.rows.yesDealException; |
| | | this.total = response.total || 0; |
| | | } else { |
| | | this.exceptionList = []; |
| | | this.total = 0; |
| | | } |
| | | } catch (error) { |
| | | console.error("å è½½å¼å¸¸å表失败:", error); |
| | | this.$message.error("å è½½å¼å¸¸å表失败ï¼è¯·ç¨åéè¯"); |
| | | this.exceptionList = []; |
| | | this.total = 0; |
| | | } |
| | | }, |
| | | |
| | | // å è½½æ¦è§æ°æ® |
| | | async loadOverviewData() { |
| | | return new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | // 计ç®ç»è®¡æ°æ® |
| | | const totalExceptionCount = this.exceptionList.reduce((sum, item) => sum + item.totalExceptionCount, 0); |
| | | const pendingCount = this.exceptionList.reduce((sum, item) => sum + item.pendingCount, 0); |
| | | const processedCount = this.exceptionList.reduce((sum, item) => sum + item.processedCount, 0); |
| | | try { |
| | | // 仿¥å£æ°æ®è®¡ç®ç»è®¡æ°æ® |
| | | const totalExceptionCount = this.exceptionList.reduce( |
| | | (sum, item) => sum + (item.exceptionQuesNum?.all || 0), |
| | | 0 |
| | | ); |
| | | const pendingCount = this.exceptionList.reduce( |
| | | (sum, item) => sum + (item.exceptionQuesNum?.noDeal || 0), |
| | | 0 |
| | | ); |
| | | const processedCount = this.exceptionList.reduce( |
| | | (sum, item) => sum + (item.exceptionQuesNum?.yesDeal || 0), |
| | | 0 |
| | | ); |
| | | |
| | | this.overviewData = { |
| | | totalExceptionCount, |
| | | pendingCount, |
| | | processedCount, |
| | | todayProcessedCount: 8 // 仿¥å¤çæ° mock |
| | | }; |
| | | resolve(); |
| | | }, 300); |
| | | }); |
| | | }, |
| | | // 计ç®ä»æ¥å¤çæ°ï¼è¿éå¯ä»¥æ ¹æ®å®é
éæ±è°æ´é»è¾ï¼ |
| | | const today = new Date().toISOString().split("T")[0]; |
| | | const todayProcessedCount = this.exceptionList.filter((item) => { |
| | | if (!item.handleTime) return false; |
| | | const handleDate = new Date(item.handleTime) |
| | | .toISOString() |
| | | .split("T")[0]; |
| | | return handleDate === today; |
| | | }).length; |
| | | |
| | | // è·åé¢ç®ç±»åæ ç¾æ ·å¼ |
| | | getQuestionTypeTag(type) { |
| | | return type === 1 ? 'primary' : 'success'; |
| | | this.overviewData = { |
| | | totalExceptionCount, |
| | | pendingCount, |
| | | processedCount, |
| | | todayProcessedCount, |
| | | }; |
| | | } catch (error) { |
| | | console.error("å è½½æ¦è§æ°æ®å¤±è´¥:", error); |
| | | this.overviewData = { |
| | | totalExceptionCount: 0, |
| | | pendingCount: 0, |
| | | processedCount: 0, |
| | | todayProcessedCount: 0, |
| | | }; |
| | | } |
| | | }, |
| | | |
| | | // å¤çæ¥è¯¢ |
| | |
| | | // å¤çéç½® |
| | | handleReset() { |
| | | this.$refs.queryForm.resetFields(); |
| | | this.queryParams.dateRange = []; |
| | | this.queryParams.handleTimeRange = []; |
| | | this.queryParams.pageNum = 1; |
| | | this.queryParams.todeptcode = []; // éç½®ç§å®¤éæ© |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // å¤çæ¹éå¤ç |
| | | handleBatchProcess() { |
| | | if (this.selectedIds.length === 0) { |
| | | this.$message.warning('请å
éæ©è¦å¤ççå¼å¸¸é¢ç®'); |
| | | this.$message.warning("请å
éæ©è¦å¤ççå¼å¸¸é¢ç®"); |
| | | return; |
| | | } |
| | | |
| | | // è·³è½¬å°æ¹éå¤çé¡µé¢ |
| | | this.$router.push({ |
| | | path: '/satisfaction/exception/batch-process', |
| | | path: "/Intelligentcenter/batch", |
| | | query: { |
| | | questionIds: this.selectedIds.join(',') |
| | | } |
| | | questionIds: this.selectedIds.join(","), |
| | | type: this.queryParams.templateType, |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | // å¤çå¯¼åº |
| | | handleExport() { |
| | | this.$message.success('导åºåè½å¼åä¸...'); |
| | | this.$message.success("导åºåè½å¼åä¸..."); |
| | | }, |
| | | |
| | | // å·æ°æ°æ® |
| | | refreshData() { |
| | | this.loadData(); |
| | | this.$message.success('æ°æ®å·²å·æ°'); |
| | | this.$message.success("æ°æ®å·²å·æ°"); |
| | | }, |
| | | |
| | | // å¤çéæ©åå |
| | | handleSelectionChange(selection) { |
| | | this.selectedIds = selection.map(item => item.questionId); |
| | | }, |
| | | |
| | | // å¤çæ¥ç详æ
|
| | | handleViewDetail(row) { |
| | | this.$router.push({ |
| | | path: '/satisfaction/exception/detail', |
| | | query: { |
| | | id: row.questionId |
| | | } |
| | | }); |
| | | this.selectedIds = selection.map((item) => item.scriptid); |
| | | }, |
| | | |
| | | // å¤çå个é¢ç®æ¹éå¤ç |
| | | handleBatchQuestion(row) { |
| | | this.$router.push({ |
| | | path: '/Intelligentcenter/batch', |
| | | path: "/Intelligentcenter/batch", |
| | | query: { |
| | | questionId: row.questionId |
| | | } |
| | | questionId: row.scriptid, |
| | | type: this.queryParams.templateType, |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | |
| | | handlePageChange(page) { |
| | | this.queryParams.pageNum = page; |
| | | this.loadExceptionList(); |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | |
| | | .page-header { |
| | | margin-bottom: 20px; |
| | | padding: 20px; |
| | | background: linear-gradient(135deg, #5788FE 0%, #66b1ff 100%); |
| | | background: linear-gradient(135deg, #5788fe 0%, #66b1ff 100%); |
| | | border-radius: 8px; |
| | | color: white; |
| | | |
| | |
| | | color: #303133; |
| | | margin-bottom: 8px; |
| | | line-height: 1.5; |
| | | text-align: left; |
| | | } |
| | | |
| | | .question-tags { |
| | | display: flex; |
| | | gap: 5px; |
| | | justify-content: center; |
| | | justify-content: flex-start; |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | &.total { |
| | | color: #5788FE; |
| | | color: #5788fe; |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | .process-user { |
| | | font-size: 13px; |
| | | color: #5788FE; |
| | | color: #5788fe; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- é项é
ç½®å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="é项å¼å¸¸ç¶æé
ç½®" |
| | | :visible.sync="optionDialogVisible" |
| | | width="700px" |
| | | center |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div v-if="editingQuestion" class="option-config-wrapper"> |
| | | <div class="dialog-header"> |
| | | <h4>{{ editingQuestion.scriptTopic || 'æ 主é¢' }}</h4> |
| | | <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p> |
| | | </div> |
| | | <!-- é项é
ç½®å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="é项å¼å¸¸ç¶æé
ç½®" |
| | | :visible.sync="optionDialogVisible" |
| | | width="700px" |
| | | center |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div v-if="editingQuestion" class="option-config-wrapper"> |
| | | <div class="dialog-header"> |
| | | <h4>{{ editingQuestion.scriptTopic || "æ 主é¢" }}</h4> |
| | | <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p> |
| | | </div> |
| | | |
| | | <div class="option-list"> |
| | | <el-alert |
| | | v-if="!currentOptions.some(opt => opt.isabnormal === 1)" |
| | | title="请è³å°è®¾ç½®ä¸ä¸ªå¼å¸¸éé¡¹ï¼æ 记为å¼å¸¸ï¼" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 20px;" |
| | | /> |
| | | <div class="option-list"> |
| | | <el-alert |
| | | v-if="!currentOptions.some((opt) => opt.isabnormal === 1)" |
| | | title="请è³å°è®¾ç½®ä¸ä¸ªå¼å¸¸éé¡¹ï¼æ 记为å¼å¸¸ï¼" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 20px" |
| | | /> |
| | | |
| | | <div v-for="(option, index) in currentOptions" :key="index" class="option-item"> |
| | | <el-form |
| | | :model="option" |
| | | :rules="optionRules" |
| | | ref="optionForm" |
| | | size="small" |
| | | class="option-form" |
| | | > |
| | | <el-row :gutter="12" align="middle"> |
| | | <el-col :span="2"> |
| | | <div class="option-index">#{{ index + 1 }}</div> |
| | | </el-col> |
| | | <div |
| | | v-for="(option, index) in currentOptions" |
| | | :key="index" |
| | | class="option-item" |
| | | > |
| | | <el-form |
| | | :model="option" |
| | | :rules="optionRules" |
| | | ref="optionForm" |
| | | size="small" |
| | | class="option-form" |
| | | > |
| | | <el-row :gutter="12" align="middle"> |
| | | <el-col :span="2"> |
| | | <div class="option-index">#{{ index + 1 }}</div> |
| | | </el-col> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item prop="targetvalue"> |
| | | <el-input |
| | | v-model="option.targetvalue" |
| | | placeholder="请è¾å
¥é项å
容" |
| | | clearable |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item prop="targetvalue"> |
| | | <el-input |
| | | v-model="option.targetvalue" |
| | | placeholder="请è¾å
¥é项å
容" |
| | | clearable |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="6"> |
| | | <el-form-item prop="isabnormal"> |
| | | <el-select |
| | | v-model="option.isabnormal" |
| | | placeholder="éæ©ç¶æ" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="status in abnormalOptions" |
| | | :key="status.value" |
| | | :label="status.label" |
| | | :value="status.value" |
| | | > |
| | | <el-tag :type="status.type" size="small">{{ status.label }}</el-tag> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item prop="isabnormal"> |
| | | <el-select |
| | | v-model="option.isabnormal" |
| | | placeholder="éæ©ç¶æ" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="status in abnormalOptions" |
| | | :key="status.value" |
| | | :label="status.label" |
| | | :value="status.value" |
| | | > |
| | | <el-tag :type="status.type" size="small">{{ |
| | | status.label |
| | | }}</el-tag> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | <el-col :span="4"> |
| | | <el-button |
| | | type="danger" |
| | | icon="el-icon-delete" |
| | | @click="removeOption(index)" |
| | | size="small" |
| | | circle |
| | | plain |
| | | /> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <el-col :span="4"> |
| | | <el-button |
| | | type="danger" |
| | | icon="el-icon-delete" |
| | | @click="removeOption(index)" |
| | | size="small" |
| | | circle |
| | | plain |
| | | /> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- <el-button |
| | | <!-- <el-button |
| | | type="primary" |
| | | icon="el-icon-plus" |
| | | @click="addNewOption" |
| | |
| | | > |
| | | æ·»å é项 |
| | | </el-button> --> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="optionDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveOptions" :loading="savingOptions"> |
| | | ä¿åé
ç½® |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="optionDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveOptions" :loading="savingOptions"> |
| | | ä¿åé
ç½® |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <!-- é¢ç®é¢è§å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="é¢ç®é¢è§" |
| | |
| | | this.voiceCategories.includes(q.scriptAssortid) |
| | | ).length; |
| | | } |
| | | return 0; |
| | | }, |
| | | // æ£æ¥é¢ç®æ¯å¦æå¼å¸¸é项 |
| | | hasAbnormalOption(question) { |
| | | return (question) => { |
| | | if (!question) return false; |
| | | |
| | | // é®å·æ¨¡æ¿ |
| | | if (this.templateForm.templateType === 1) { |
| | | const options = question.svyLibTemplateTargetoptions || []; |
| | | return options.some((opt) => opt.isabnormal === 1); |
| | | } |
| | | // è¯é³æ¨¡æ¿ |
| | | else if (this.templateForm.templateType === 2) { |
| | | const options = question.ivrLibaScriptTargetoptionList || []; |
| | | return options.some((opt) => opt.isabnormal === 1); |
| | | } |
| | | |
| | | return false; |
| | | }; |
| | | }, |
| | | // çéåçé¢ç®å表 |
| | | filteredQuestionList() { |
| | | let filtered = this.questionList; |
| | | console.log(this.questionnaireCategorys); |
| | | |
| | | // çéæ»¡æåº¦é¢ç® |
| | | if (this.templateForm.templateType === 1) { |
| | |
| | | if (this.queryParams.scriptTopic) { |
| | | const keyword = this.queryParams.scriptTopic.toLowerCase(); |
| | | filtered = filtered.filter( |
| | | (q) => q.scriptTopic && q.scriptTopic.toLowerCase().includes(keyword) |
| | | (q) => q.scriptTopic && q.criptTopic.toLowerCase().includes(keyword) |
| | | ); |
| | | } |
| | | |
| | |
| | | return new Promise((resolve) => { |
| | | getQtemplatelist({ pageSize: 1000 }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | console.log(res.rows, "res.rows"); |
| | | if (res.code == 200) { |
| | | console.log(res.rows, 2); |
| | | this.questionnaireTemplates = (res.rows || []).map((item) => ({ |
| | | id: item.svyid, |
| | | templateName: item.svyname, |
| | | isavailable: item.isavailable, |
| | | })); |
| | | console.log(this.followupTemplates, 3); |
| | | |
| | | } else { |
| | | this.$message.error(res.msg || "å è½½é®å·æ¨¡æ¿å¤±è´¥"); |
| | | } |
| | |
| | | return new Promise((resolve) => { |
| | | getFollowuplist({ pageSize: 1000 }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | console.log(res.rows, "res.rows"); |
| | | if (res.code == 200) { |
| | | console.log(res.rows, 2); |
| | | |
| | | this.followupTemplates = (res.rows || []).map((item) => ({ |
| | | id: item.id, |
| | | templateName: item.templateName, |
| | | isavailable: item.isavailable, |
| | | })); |
| | | console.log(this.followupTemplates, 3); |
| | | } else { |
| | | this.$message.error(res.msg || "å è½½è¯é³æ¨¡æ¿å¤±è´¥"); |
| | | } |
| | |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | console.log(index, "index"); |
| | | |
| | | if (index !== -1) { |
| | | const formRef = this.$refs.configForm && this.$refs.configForm[index]; |
| | | if (formRef) { |
| | |
| | | const changedItems = this.questionList.filter((q) => q.hasChanges); |
| | | this.changedCount = changedItems.length; |
| | | this.hasChanges = changedItems.length > 0; |
| | | |
| | | // å¼ºå¶æ´æ°è§å¾ |
| | | this.$forceUpdate(); |
| | | }, |
| | | |
| | | /** æ£æ¥é¢ç®æ¯å¦æå¼å¸¸é项 */ |
| | | checkHasAbnormalOptions(question) { |
| | | if (this.templateForm.templateType === 1) { |
| | | return (question.svyLibTemplateTargetoptions || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | return (question.ivrLibaScriptTargetoptionList || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } |
| | | return false; |
| | | }, |
| | | |
| | | /** ä¿åå个é¢ç®é
ç½® */ |
| | | async saveSingleConfig(question) { |
| | | if (!question.hasChanges) return; |
| | | async saveSingleConfig(question, skipAbnormalCheck = false) { |
| | | // æ£æ¥æ¯å¦æåæ´ |
| | | if (!question.hasChanges && !skipAbnormalCheck) { |
| | | this.$message.info("å½åé
ç½®æ åå"); |
| | | return; |
| | | } |
| | | |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | console.log(index, "filteredQuestionList"); |
| | | // æ£æ¥æ¯å¦æå¼å¸¸é项 |
| | | if (!skipAbnormalCheck && !this.checkHasAbnormalOptions(question)) { |
| | | this.$confirm( |
| | | "该é¢ç®æ²¡æè®¾ç½®å¼å¸¸é项ï¼å¿
é¡»å
é
ç½®å¼å¸¸é项æè½ä¿åãæ¯å¦ç«å³é
ç½®ï¼", |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "å»é
ç½®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | this.openOptionDialog(question); |
| | | }) |
| | | .catch(() => {}); |
| | | return; |
| | | } |
| | | |
| | | const index = this.questionList.findIndex((q) => q.id === question.id); |
| | | if (index === -1) return; |
| | | |
| | | const formRef = this.$refs.configForm && this.$refs.configForm[index]; |
| | | if (!formRef) return; |
| | | |
| | | const valid = await formRef.validate(); |
| | | if (!valid) { |
| | | // éªè¯è¡¨å |
| | | try { |
| | | await formRef.validate(); |
| | | } catch (error) { |
| | | this.$message.warning("请å
宿å¿
填项"); |
| | | return; |
| | | } |
| | |
| | | reportDeptName: reportDeptNames.join(","), |
| | | }; |
| | | |
| | | // 妿éè¦ï¼ä¹æ´æ°éé¡¹æ°æ® |
| | | if (question.hasChanges && this.templateForm.templateType === 1) { |
| | | questions[questionIndex].svyLibTemplateTargetoptions = |
| | | question.svyLibTemplateTargetoptions || []; |
| | | } else if ( |
| | | question.hasChanges && |
| | | this.templateForm.templateType === 2 |
| | | ) { |
| | | questions[questionIndex].ivrLibaScriptTargetoptionList = |
| | | question.ivrLibaScriptTargetoptionList || []; |
| | | } |
| | | |
| | | // æ´æ°æ¨¡æ¿ |
| | | updatedTemplateDetail[questionsField] = questions; |
| | | |
| | |
| | | } |
| | | }, |
| | | |
| | | /** å¤çä¿åæå */ |
| | | /** å¤çä¿åæå */ |
| | | handleSaveSuccess(question) { |
| | | // åæ¶æ´æ°é¢ç®é¡¶å±å段 |
| | |
| | | async handleBatchSave() { |
| | | if (!this.hasChanges || this.batchSaving) return; |
| | | |
| | | // è·åæåæ´çé¢ç® |
| | | const changedQuestions = this.questionList.filter((q) => q.hasChanges); |
| | | if (changedQuestions.length === 0) { |
| | | this.$message.info("没æéè¦ä¿åçé
ç½®åæ´"); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æé¢ç®ç¼ºå°å¼å¸¸é项 |
| | | const questionsWithoutAbnormal = changedQuestions.filter( |
| | | (q) => !this.checkHasAbnormalOptions(q) |
| | | ); |
| | | |
| | | if (questionsWithoutAbnormal.length > 0) { |
| | | this.$confirm( |
| | | `æ ${questionsWithoutAbnormal.length} 个é¢ç®æ²¡æè®¾ç½®å¼å¸¸é项ï¼å¿
é¡»é
ç½®å¼å¸¸é项åæè½ä¿åãæ¯å¦å
å»é
ç½®ï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "å»é
ç½®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | // æå¼ç¬¬ä¸ä¸ªæ²¡æå¼å¸¸é项çé¢ç®çé
ç½®å¯¹è¯æ¡ |
| | | this.openOptionDialog(questionsWithoutAbnormal[0]); |
| | | }) |
| | | .catch(() => {}); |
| | | return; |
| | | } |
| | | |
| | | this.$confirm("ç¡®å®è¦ä¿åææä¿®æ¹è¿çé
ç½®åï¼", "æ¹éä¿å", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | |
| | | .then(async () => { |
| | | this.batchSaving = true; |
| | | |
| | | const changedQuestions = this.questionList.filter( |
| | | (q) => q.hasChanges |
| | | ); |
| | | const results = []; |
| | | |
| | | for (const question of changedQuestions) { |
| | | try { |
| | | await this.saveSingleConfig(question); |
| | | // è·³è¿å¼å¸¸æ£æ¥ï¼å 为å¨ä¸é¢å·²ç»æ£æ¥è¿äº |
| | | await this.saveSingleConfig(question, true); |
| | | results.push({ |
| | | id: question.id, |
| | | success: |
| | | !question.hasChanges && |
| | | question.saveStatus?.type === "success", |
| | | success: !question.hasChanges, |
| | | }); |
| | | } catch (error) { |
| | | results.push({ |
| | | id: question.id, |
| | | success: false, |
| | | error: error.message, |
| | | }); |
| | | } |
| | | } |
| | |
| | | this.$message.warning( |
| | | `æåä¿å ${successCount} 个ï¼å¤±è´¥ ${failCount} 个` |
| | | ); |
| | | // å¯ä»¥æ¾ç¤ºå
·ä½åªäºå¤±è´¥äº |
| | | const failedQuestions = results |
| | | .filter((r) => !r.success) |
| | | .map((r) => r.id); |
| | | console.error("ä¿å失败çé¢ç®ID:", failedQuestions); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | |
| | | this.previewAnswer = ""; |
| | | this.previewVisible = true; |
| | | }, |
| | | /** æ£æ¥é¢ç®æ¯å¦æå¼å¸¸é项 */ |
| | | checkHasAbnormalOptions(question) { |
| | | if (this.templateForm.templateType === 1) { |
| | | return (question.svyLibTemplateTargetoptions || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | return (question.ivrLibaScriptTargetoptionList || []).some( |
| | | (opt) => opt.isabnormal === 1 |
| | | ); |
| | | } |
| | | return false; |
| | | }, |
| | | |
| | | /** æå¼é项管çå¯¹è¯æ¡ */ |
| | | /** ä¿®æ¹é项管çå¯¹è¯æ¡çæå¼æ¹æ³ï¼ä¿ååå§é项 */ |
| | | openOptionDialog(question) { |
| | | this.editingQuestion = question; |
| | | |
| | | // ä¿ååå§é项çå¿«ç
§ |
| | | if (this.templateForm.templateType === 1) { |
| | | this.editingQuestion.originalOptions = JSON.parse( |
| | | JSON.stringify(question.svyLibTemplateTargetoptions || []) |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | this.editingQuestion.originalOptions = JSON.parse( |
| | | JSON.stringify(question.ivrLibaScriptTargetoptionList || []) |
| | | ); |
| | | } |
| | | |
| | | // å¤å¶éé¡¹æ°æ® |
| | | if (this.templateForm.templateType === 1) { |
| | | this.currentOptions = JSON.parse( |
| | | JSON.stringify(question.svyLibTemplateTargetoptions || []) |
| | | ).map((opt) => ({ |
| | | ).map((opt, index) => ({ |
| | | ...opt, |
| | | id: opt.id, |
| | | id: opt.id || `temp_${Date.now()}_${index}`, |
| | | targetvalue: opt.optioncontent || "", |
| | | isabnormal: opt.isabnormal || 0, |
| | | })); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | this.currentOptions = JSON.parse( |
| | | JSON.stringify(question.ivrLibaScriptTargetoptionList || []) |
| | | ).map((opt) => ({ |
| | | ).map((opt, index) => ({ |
| | | ...opt, |
| | | id: opt.id || `temp_${Date.now()}_${index}`, |
| | | targetvalue: opt.targetvalue || "", |
| | | isabnormal: opt.isabnormal || 0, |
| | | })); |
| | |
| | | /** æ·»å æ°é项 */ |
| | | addNewOption() { |
| | | this.currentOptions.push({ |
| | | id: Date.now(), // 临æ¶ID |
| | | id: `temp_${Date.now()}_${this.currentOptions.length}`, |
| | | targetvalue: "", |
| | | isabnormal: 0, |
| | | isNew: true, |
| | |
| | | |
| | | /** å é¤é项 */ |
| | | removeOption(index) { |
| | | this.currentOptions.splice(index, 1); |
| | | this.$confirm("ç¡®å®è¦å é¤è¿ä¸ªé项åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | this.currentOptions.splice(index, 1); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | |
| | | /** ä¿åé项é
ç½® */ |
| | | async saveOptions() { |
| | | try { |
| | | // éªè¯å¿
填项 |
| | | for (const option of this.currentOptions) { |
| | | for (let i = 0; i < this.currentOptions.length; i++) { |
| | | const option = this.currentOptions[i]; |
| | | if (!option.targetvalue || option.targetvalue.trim() === "") { |
| | | this.$message.warning("è¯·å¡«åææé项å
容"); |
| | | this.$message.warning(`第 ${i + 1} 个é项å
容ä¸è½ä¸ºç©º`); |
| | | return; |
| | | } |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | // 夿é项æ¯å¦åçåå |
| | | let isOptionsChanged = false; |
| | | |
| | | if (this.templateForm.templateType === 1) { |
| | | const originalOptions = |
| | | this.editingQuestion.svyLibTemplateTargetoptions || []; |
| | | isOptionsChanged = this.checkOptionsChanged( |
| | | originalOptions, |
| | | this.currentOptions, |
| | | "questionnaire" |
| | | ); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | const originalOptions = |
| | | this.editingQuestion.ivrLibaScriptTargetoptionList || []; |
| | | isOptionsChanged = this.checkOptionsChanged( |
| | | originalOptions, |
| | | this.currentOptions, |
| | | "voice" |
| | | ); |
| | | } |
| | | |
| | | // ä¿åé»è¾ - æ´æ°é¢ç®å¯¹è±¡çéé¡¹æ°æ® |
| | | if (this.templateForm.templateType === 1) { |
| | | this.editingQuestion.svyLibTemplateTargetoptions = |
| | |
| | | ...opt, |
| | | optioncontent: opt.targetvalue, |
| | | isabnormal: opt.isabnormal, |
| | | // æ¸
é¤ä¸´æ¶å段 |
| | | targetvalue: undefined, |
| | | isNew: undefined, |
| | | })); |
| | | } else if (this.templateForm.templateType === 2) { |
| | | this.editingQuestion.ivrLibaScriptTargetoptionList = |
| | | this.currentOptions; |
| | | this.currentOptions.map((opt) => ({ |
| | | ...opt, |
| | | // æ¸
é¤ä¸´æ¶å段 |
| | | isNew: undefined, |
| | | })); |
| | | } |
| | | |
| | | // 触åé
ç½®åæ´æ£æ¥ |
| | | this.handleConfigChange(this.editingQuestion); |
| | | // 妿é项æååï¼å设置é¢ç®ä¸ºæåæ´ç¶æ |
| | | if (isOptionsChanged) { |
| | | this.editingQuestion.hasChanges = true; |
| | | this.updateChangedStatus(); |
| | | } |
| | | |
| | | this.$message.success("é项é
ç½®ä¿åæå"); |
| | | this.optionDialogVisible = false; |
| | |
| | | this.$message.error("ä¿åé项失败"); |
| | | } |
| | | }, |
| | | |
| | | /** ä¿®æ¹ä¿åå个é¢ç®é
ç½®æ¹æ³ï¼æ·»å å¼å¸¸éé¡¹æ£æ¥ */ |
| | | async saveSingleConfig(question) { |
| | | // æ£æ¥æ¯å¦æå¼å¸¸é项 |
| | | if (!this.checkHasAbnormalOptions(question)) { |
| | | this.$confirm("该é¢ç®æ²¡æè®¾ç½®å¼å¸¸éé¡¹ï¼æ¯å¦å
é
ç½®é项ï¼", "æç¤º", { |
| | | confirmButtonText: "å»é
ç½®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | this.openOptionDialog(question); |
| | | }) |
| | | .catch(() => {}); |
| | | return; |
| | | /** æ£æ¥é项æ¯å¦åçåå */ |
| | | checkOptionsChanged(originalOptions, newOptions, templateType) { |
| | | // 妿æ°éä¸åï¼åä¸å®ååäº |
| | | if (originalOptions.length !== newOptions.length) { |
| | | return true; |
| | | } |
| | | |
| | | // åæçä¿åé»è¾... |
| | | if (!question.hasChanges) return; |
| | | // æ¯è¾æ¯ä¸ªé项çå
容åå¼å¸¸ç¶æ |
| | | for (let i = 0; i < originalOptions.length; i++) { |
| | | const original = originalOptions[i]; |
| | | const current = newOptions[i]; |
| | | |
| | | const index = this.filteredQuestionList.findIndex( |
| | | (q) => q.id === question.id |
| | | ); |
| | | |
| | | if (index === -1) return; |
| | | |
| | | const formRef = this.$refs.configForm && this.$refs.configForm[index]; |
| | | if (!formRef) return; |
| | | |
| | | const valid = await formRef.validate(); |
| | | if (!valid) { |
| | | this.$message.warning("请å
宿å¿
填项"); |
| | | return; |
| | | } |
| | | |
| | | // ç»§ç»åæçä¿åé»è¾... |
| | | question.saving = true; |
| | | question.saveStatus = null; |
| | | |
| | | try { |
| | | // ... åæçä¿åé»è¾ä¸å |
| | | } catch (error) { |
| | | // ... é误å¤çä¸å |
| | | } finally { |
| | | question.saving = false; |
| | | } |
| | | }, |
| | | |
| | | /** æ¹éä¿åæ¶ä¹è¦æ£æ¥ */ |
| | | async handleBatchSave() { |
| | | if (!this.hasChanges || this.batchSaving) return; |
| | | |
| | | // æ£æ¥æææåæ´çé¢ç®æ¯å¦é½æå¼å¸¸é项 |
| | | const changedQuestions = this.questionList.filter((q) => q.hasChanges); |
| | | const questionsWithoutAbnormal = changedQuestions.filter( |
| | | (q) => !this.checkHasAbnormalOptions(q) |
| | | ); |
| | | |
| | | if (questionsWithoutAbnormal.length > 0) { |
| | | this.$confirm( |
| | | `æ ${questionsWithoutAbnormal.length} 个é¢ç®æ²¡æè®¾ç½®å¼å¸¸é项ï¼è¯·å
é
ç½®éé¡¹ãæ¯å¦ç»§ç»ï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç»§ç»", |
| | | cancelButtonText: "å»é
ç½®", |
| | | type: "warning", |
| | | if (templateType === "questionnaire") { |
| | | // é®å·æ¨¡æ¿æ¯è¾ |
| | | if ( |
| | | original.optioncontent !== current.targetvalue || |
| | | original.isabnormal !== current.isabnormal |
| | | ) { |
| | | return true; |
| | | } |
| | | ) |
| | | .then(() => { |
| | | // ç»§ç»æ§è¡æ¹éä¿å |
| | | this.executeBatchSave(changedQuestions); |
| | | }) |
| | | .catch(() => { |
| | | // å¯ä»¥å¨è¿é跳转å°ç¬¬ä¸ä¸ªæ²¡æå¼å¸¸é项çé¢ç® |
| | | if (questionsWithoutAbnormal.length > 0) { |
| | | this.openOptionDialog(questionsWithoutAbnormal[0]); |
| | | } |
| | | }); |
| | | } else { |
| | | this.executeBatchSave(changedQuestions); |
| | | } |
| | | }, |
| | | |
| | | /** æ§è¡æ¹éä¿å */ |
| | | async executeBatchSave(changedQuestions) { |
| | | this.$confirm("ç¡®å®è¦ä¿åææä¿®æ¹è¿çé
ç½®åï¼", "æ¹éä¿å", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(async () => { |
| | | this.batchSaving = true; |
| | | |
| | | const results = []; |
| | | for (const question of changedQuestions) { |
| | | try { |
| | | // è¿éè°ç¨ä¿®æ¹åçsaveSingleConfigæ¹æ³ |
| | | await this.saveSingleConfig(question); |
| | | results.push({ |
| | | id: question.id, |
| | | success: |
| | | !question.hasChanges && |
| | | question.saveStatus?.type === "success", |
| | | }); |
| | | } catch (error) { |
| | | results.push({ |
| | | id: question.id, |
| | | success: false, |
| | | }); |
| | | } |
| | | } else if (templateType === "voice") { |
| | | // è¯é³æ¨¡æ¿æ¯è¾ |
| | | if ( |
| | | original.targetvalue !== current.targetvalue || |
| | | original.isabnormal !== current.isabnormal |
| | | ) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.batchSaving = false; |
| | | // ... åç»å¤çä¸å |
| | | }) |
| | | .catch(() => { |
| | | this.batchSaving = false; |
| | | }); |
| | | return false; |
| | | }, |
| | | |
| | | /** è·åå¼å¸¸é项ç»è®¡ */ |
| | | getAbnormalStats(question) { |
| | | if (this.templateForm.templateType === 1) { |
| | |
| | | } |
| | | return { total: 0, abnormal: 0, warning: 0, normal: 0 }; |
| | | }, |
| | | |
| | | /** æç´¢ */ |
| | | handleQuery() { |
| | | // ä»
çéæ¾ç¤ºï¼ä¸éè¦éæ°å è½½ |
| | |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | <!-- <el-table-column |
| | | label="è¶å¿" |
| | | prop="trend" |
| | | align="center" |
| | |
| | | }}</span> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> --> |
| | | |
| | | <el-table-column |
| | | label="æä½" |
| | |
| | | Object.entries(apiData.rows).forEach(([typeName, typeStat]) => { |
| | | const sendCount = typeStat.subidAll || 0; |
| | | const receiveCount = typeStat.fillCountAll || 0; |
| | | const recoveryRate = typeStat.receiveRate || 0; |
| | | const recoveryRate = typeStat.receiveRate.toFixed(2) || 0; |
| | | |
| | | chartData.push({ |
| | | name: typeName, |
| | | value: recoveryRate * 100, // 转æ¢ä¸ºç¾åæ¯ |
| | | value: (recoveryRate * 100).toFixed(2), // 转æ¢ä¸ºç¾åæ¯ |
| | | sendCount: sendCount, |
| | | receiveCount: receiveCount, |
| | | averageScore: typeStat.averageScore || 0, |
| | |
| | | const response = await satisfactionGraph(params); |
| | | |
| | | if (response.code === 200) { |
| | | this.processTypeDetailData(response.data); |
| | | this.processTypeDetailData(response); |
| | | console.log(11); |
| | | |
| | | } else { |
| | | this.$message.error(response.msg || "è·åç±»åæç»æ°æ®å¤±è´¥"); |
| | | const mockData = await this.generateMockTypeDetail(); |
| | |
| | | Object.entries(apiData.rows).forEach(([typeName, typeStat], index) => { |
| | | const sendCount = typeStat.subidAll || 0; |
| | | const receiveCount = typeStat.fillCountAll || 0; |
| | | const recoveryRate = typeStat.receiveRate || 0; |
| | | const recoveryRate = typeStat.receiveRate.toFixed(2) || 0; |
| | | const averageScore = typeStat.averageScore || 0; |
| | | |
| | | typeDetail.push({ |
| | |
| | | }); |
| | | |
| | | this.typeDetailData = typeDetail; |
| | | console.log(this.typeDetailData,'this.typeDetailData'); |
| | | |
| | | this.calculateTypeSummary(typeDetail); |
| | | }, |
| | | |
| | |
| | | if (score >= 4.0) return "è¯å¥½"; |
| | | if (score >= 3.0) return "ä¸è¬"; |
| | | if (score >= 2.0) return "è¾å·®"; |
| | | return "å·®"; |
| | | return "æªç¥"; |
| | | }, |
| | | |
| | | // è·åè¶å¿ |
| | |
| | | // 渲æå¾è¡¨ |
| | | renderChart(chartData) { |
| | | if (!this.barChart) return; |
| | | if (!chartData || chartData.length === 0) { |
| | | const emptyOption = { |
| | | title: { |
| | | text: "ææ æ°æ®", |
| | | left: "center", |
| | | top: "center", |
| | | textStyle: { |
| | | color: "#999", |
| | | fontSize: 16, |
| | | fontWeight: "normal" |
| | | } |
| | | }, |
| | | xAxis: { show: false }, |
| | | yAxis: { show: false } |
| | | }; |
| | | this.barChart.setOption(emptyOption); |
| | | return; |
| | | } |
| | | if (!chartData || chartData.length === 0) { |
| | | const emptyOption = { |
| | | title: { |
| | | text: "ææ æ°æ®", |
| | | left: "center", |
| | | top: "center", |
| | | textStyle: { |
| | | color: "#999", |
| | | fontSize: 16, |
| | | fontWeight: "normal", |
| | | }, |
| | | }, |
| | | xAxis: { show: false }, |
| | | yAxis: { show: false }, |
| | | }; |
| | | this.barChart.setOption(emptyOption); |
| | | return; |
| | | } |
| | | const option = { |
| | | title: { |
| | | text: "", |
| | |
| | | <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${ |
| | | data.color |
| | | };margin-right:5px;"></span> |
| | | å¡«æ¥æ¯ä¾: <strong>${data.value.toFixed(1)}%</strong> |
| | | å¡«æ¥æ¯ä¾: <strong>${data.value}%</strong> |
| | | </div> |
| | | <div style="margin: 2px 0;"> |
| | | <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span> |
| | |
| | | |
| | | // æ ¼å¼åç¾åæ¯ |
| | | formatPercent(value) { |
| | | if (value === null || value === undefined) return "-"; |
| | | const num = parseFloat(value); |
| | | if (isNaN(num)) return "-"; |
| | | // 妿å¼å°äº1ï¼è®¤ä¸ºæ¯å°æ°æ¯ä¾ï¼éè¦ä¹ä»¥100 |
| | | const percentValue = num < 1 ? num * 100 : num; |
| | | return `${percentValue.toFixed(2)}%`; |
| | | if (value === null || value === undefined) return "-"; |
| | | const num = parseFloat(value); |
| | | if (isNaN(num)) return "-"; |
| | | // 妿å¼å°äº1ï¼è®¤ä¸ºæ¯å°æ°æ¯ä¾ï¼éè¦ä¹ä»¥100 |
| | | const percentValue = num < 1 ? num * 100 : num; |
| | | return `${percentValue.toFixed(2)}%`; |
| | | }, |
| | | |
| | | // è·ååæ¶çæ ·å¼ç±» |
| | |
| | | è¯å¥½: "primary", |
| | | ä¸è¬: "warning", |
| | | è¾å·®: "danger", |
| | | å·®: "info", |
| | | æªç¥: "info", |
| | | }; |
| | | return levelMap[level] || "info"; |
| | | }, |
| | |
| | | value: 4, |
| | | label: "䏿§è¡", |
| | | }, |
| | | { |
| | | { |
| | | value: 5, |
| | | label: "åé失败", |
| | | }, |
| | |
| | | value: 6, |
| | | label: "已宿", |
| | | }, |
| | | { |
| | | { |
| | | value: 7, |
| | | label: "è¶
æ¶", |
| | | }, |
| | |
| | | /** å¯¼åºæé®æä½ */ |
| | | handleExport() { |
| | | console.log(this.topqueryParams); |
| | | |
| | | this.topqueryParams.pageSize = null; |
| | | this.download( |
| | | // "smartor/serviceSubtask/export", |
| | | "smartor/serviceSubtask/getSubtaskByDiagnameExport", |
| | |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-plus" |
| | | icon="el-icon-plus" |
| | | size="medium" |
| | | @click="handleAdd" |
| | | >æ°å¢</el-button |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="è¯æåç§°" |
| | | align="center" |
| | | key="leavediagname" |
| | | prop="leavediagname" |
| | | width="120" |
| | | :show-overflow-tooltip="true" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="ä»»å¡ç¶æ" |
| | | align="center" |
| | | key="sendstate" |
| | |
| | | >åé失败</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <el-tag type="success" :disable-transitions="false" |
| | | >已宿</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 7"> |
| | | <el-tag type="danger" :disable-transitions="false" |
| | | >è¶
æ¶</el-tag |
| | | > |
| | | <el-tag type="danger" :disable-transitions="false">è¶
æ¶</el-tag> |
| | | </div> |
| | | </el-tooltip> |
| | | </template> |
| | |
| | | > |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="è¯æåç§°" |
| | | align="center" |
| | | key="leavediagname" |
| | | prop="leavediagname" |
| | | width="120" |
| | | :show-overflow-tooltip="true" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访人å" |
| | | align="center" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row > |
| | | <el-row> |
| | | <el-col :span="8"> |
| | | <el-form-item label="è¿æ»¤å»ç" width="100" prop="filterDrname"> |
| | | <el-input |
| | |
| | | value: 4, |
| | | label: "䏿§è¡", |
| | | }, |
| | | { |
| | | { |
| | | value: 5, |
| | | label: "åé失败", |
| | | }, |
| | |
| | | value: 6, |
| | | label: "已宿", |
| | | }, |
| | | { |
| | | { |
| | | value: 7, |
| | | label: "è¶
æ¶", |
| | | }, |
| | |
| | | }); |
| | | }, |
| | | affiliation() { |
| | | this.topqueryParams.managementDoctorCode= store.getters.hisUserId; |
| | | this.topqueryParams.managementDoctorCode = store.getters.hisUserId; |
| | | |
| | | this.getList(1); |
| | | }, |
| | |
| | | .then((response) => { |
| | | console.log(response); |
| | | }) |
| | | .then(() => { |
| | | .then(() => { |
| | | this.getList(1); |
| | | this.$modal.msgSuccess("æ£è
è¿æ»¤æå"); |
| | | }); |
| | |
| | | }, |
| | | // 跳转详æ
页 |
| | | Seedetails(row) { |
| | | let type = ""; |
| | | let type = ""; |
| | | console.log(row, "rwo"); |
| | | if (row.type == 1) { |
| | | type = 1; |
| | | } |
| | | if (row.type == 1) { |
| | | type = 1; |
| | | } |
| | | this.$router.push({ |
| | | path: "/followvisit/record/detailpage/", |
| | | query: { |
| | |
| | | } |
| | | } |
| | | ::v-deep.leftvlue .el-card__body { |
| | | background: #F2F8FF; |
| | | color: #324A9B; |
| | | background: #f2f8ff; |
| | | color: #324a9b; |
| | | } |
| | | ::v-deep.leftvlue .el-card__body:hover { |
| | | background: #3664D9; |
| | | background: #3664d9; |
| | | color: #fff; |
| | | cursor: pointer; /* é¼ æ æ¬æµ®æ¶å为æå½¢ */ |
| | | } |
| | |
| | | </el-form> |
| | | <el-divider></el-divider> |
| | | <el-row :gutter="10" class="mb8"> |
| | | <!-- <el-col :span="1.5"> |
| | | <el-col :span="1.5"> |
| | | <div class="documentf"> |
| | | <div class="document"> |
| | | <el-button |
| | |
| | | > |
| | | </div> |
| | | </div> |
| | | </el-col> --> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="primary" |
| | |
| | | { |
| | | ...this.topqueryParams, |
| | | }, |
| | | `user_${new Date().getTime()}.xlsx` |
| | | `user_${new Date().getTime()}.xlsx`, |
| | | ); |
| | | }, |
| | | // å¼å¸¸å渲æ |
| | |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | aahandleOptionChange(a, b, c) { |
| | | const result = c.find((item) => item.optioncontent == a); |
| | | if (result.nextQuestion == 0) { |
| | | this.tableDatatop = this.tableDatatop.reduce((acc, item, i) => { |
| | | acc.push(i > b ? { ...item, astrict: 1 } : item); |
| | | return acc; |
| | | }, []); |
| | | } else { |
| | | this.tableDatatop = this.tableDatatop.reduce((acc, item, i) => { |
| | | acc.push(i > b ? { ...item, astrict: 0 } : item); |
| | | return acc; |
| | | }, []); |
| | | } |
| | | if (this.Voicetype) { |
| | | var obj = this.tableDatatop[b].ivrTaskScriptTargetoptionList.find( |
| | | (item) => item.optioncontent == a |
| | | ); |
| | | } else { |
| | | var obj = this.tableDatatop[b].svyTaskTemplateTargetoptions.find( |
| | | (item) => item.optioncontent == a |
| | | ); |
| | | } |
| | | if (obj.isabnormal) { |
| | | this.tableDatatop[b].isabnormal = true; |
| | | } else { |
| | | this.tableDatatop[b].isabnormal = false; |
| | | } |
| | | this.$forceUpdate(); |
| | | }, |
| | | |
| | | handleRadioToggles(questionItem, optionValue) { |
| | | if (!questionItem.matchedtext) { |
| | | questionItem.matchedtext == ""; |
| | |
| | | this.tableDatatop[questionIndex].showAppendInput = |
| | | selectedOptionObj.appendflag == 1; |
| | | console.log(this.tableDatatop); |
| | | |
| | | if ( |
| | | selectedOptionObj.nextQuestion !== undefined && |
| | | selectedOptionObj.nextQuestion !== null |
| | | ) { |
| | | this.tableDatatop[questionIndex].nextScriptno = |
| | | selectedOptionObj.nextQuestion; |
| | | } |
| | | // if (!this.tableDatatop[questionIndex].showAppendInput) { |
| | | // this.tableDatatop[questionIndex].answerps = ""; // æ¸
é¤éå ä¿¡æ¯ |
| | | // } |
| | |
| | | hiddenByEnd: index === questionIndex + 1 ? false : item.hiddenByEnd, |
| | | })); |
| | | } |
| | | 2; |
| | | |
| | | this.$forceUpdate(); |
| | | }, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog |
| | | title="é访ç»è®¡è¶å¿å¾" |
| | | :visible.sync="visible" |
| | | width="80%" |
| | | :close-on-click-modal="false" |
| | | @close="handleClose" |
| | | > |
| | | <div class="chart-container"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">éè®¿ç¶æåå¸</div> |
| | | <div id="pieChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">é访è¶å¿åæ</div> |
| | | <div id="barLineChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import * as echarts from 'echarts' |
| | | |
| | | export default { |
| | | name: 'ChartDialog', |
| | | props: { |
| | | visible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | data: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | activeTab: { |
| | | type: String, |
| | | default: 'first' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | pieChart: null, |
| | | barLineChart: null |
| | | } |
| | | }, |
| | | watch: { |
| | | visible(newVal) { |
| | | if (newVal) { |
| | | this.$nextTick(() => { |
| | | this.initCharts() |
| | | }) |
| | | } else { |
| | | this.destroyCharts() |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.visible) { |
| | | this.$nextTick(() => { |
| | | this.initCharts() |
| | | }) |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | this.destroyCharts() |
| | | }, |
| | | methods: { |
| | | initCharts() { |
| | | this.initPieChart() |
| | | this.initBarLineChart() |
| | | }, |
| | | |
| | | initPieChart() { |
| | | const pieDom = document.getElementById('pieChart') |
| | | if (!pieDom) return |
| | | |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose() |
| | | } |
| | | |
| | | this.pieChart = echarts.init(pieDom) |
| | | |
| | | // æ ¹æ®å½åtab计ç®é¥¼å¾æ°æ® |
| | | const pieData = this.getPieChartData() |
| | | |
| | | const pieOption = { |
| | | title: { |
| | | text: 'éè®¿ç¶æåå¸', |
| | | left: 'center', |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 16 |
| | | } |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item', |
| | | formatter: '{a} <br/>{b}: {c} ({d}%)' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left', |
| | | data: pieData.legendData, |
| | | textStyle: { |
| | | color: '#666' |
| | | } |
| | | }, |
| | | color: ['#FF9D4D', '#36B37E', '#FF5C5C'], |
| | | series: [ |
| | | { |
| | | name: 'éè®¿ç¶æ', |
| | | type: 'pie', |
| | | radius: ['40%', '70%'], |
| | | avoidLabelOverlap: true, |
| | | itemStyle: { |
| | | borderRadius: 10, |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {c} ({d}%)', |
| | | color: '#333' |
| | | }, |
| | | emphasis: { |
| | | label: { |
| | | show: true, |
| | | fontSize: '18', |
| | | fontWeight: 'bold' |
| | | }, |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | }, |
| | | data: pieData.seriesData |
| | | } |
| | | ] |
| | | } |
| | | |
| | | this.pieChart.setOption(pieOption) |
| | | window.addEventListener('resize', this.resizePieChart) |
| | | }, |
| | | |
| | | getPieChartData() { |
| | | let legendData = [] |
| | | let seriesData = [] |
| | | |
| | | if (this.activeTab === 'first') { |
| | | legendData = ['å¾
é访', 'é访æå', 'é访失败'] |
| | | const followUpData = { |
| | | pending: 0, |
| | | success: 0, |
| | | fail: 0 |
| | | } |
| | | |
| | | this.data.forEach((item) => { |
| | | followUpData.pending += item.pendingFollowUp || 0 |
| | | followUpData.success += item.followUpSuccess || 0 |
| | | followUpData.fail += item.followUpFail || 0 |
| | | }) |
| | | |
| | | seriesData = [ |
| | | { value: followUpData.pending, name: 'å¾
é访' }, |
| | | { value: followUpData.success, name: 'é访æå' }, |
| | | { value: followUpData.fail, name: 'é访失败' } |
| | | ] |
| | | } else if (this.activeTab === 'second') { |
| | | legendData = ['å¾
é访(忬¡)', 'é访æå(忬¡)', 'é访失败(忬¡)'] |
| | | const followUpData = { |
| | | pending: 0, |
| | | success: 0, |
| | | fail: 0 |
| | | } |
| | | |
| | | this.data.forEach((item) => { |
| | | followUpData.pending += item.pendingFollowUpAgain || 0 |
| | | followUpData.success += item.followUpSuccessAgain || 0 |
| | | followUpData.fail += item.followUpFailAgain || 0 |
| | | }) |
| | | |
| | | seriesData = [ |
| | | { value: followUpData.pending, name: 'å¾
é访(忬¡)' }, |
| | | { value: followUpData.success, name: 'é访æå(忬¡)' }, |
| | | { value: followUpData.fail, name: 'é访失败(忬¡)' } |
| | | ] |
| | | } else if (this.activeTab === 'continued') { |
| | | legendData = ['æ¤ç宿', 'æ¤çè¿è¡ä¸', 'æ¤çæªå¼å§'] |
| | | const careData = { |
| | | completed: 0, |
| | | inProgress: 0, |
| | | notStarted: 0 |
| | | } |
| | | |
| | | this.data.forEach((item) => { |
| | | careData.completed += item.careCompleted || 0 |
| | | careData.inProgress += item.careInProgress || 0 |
| | | careData.notStarted += item.careNotStarted || 0 |
| | | }) |
| | | |
| | | seriesData = [ |
| | | { value: careData.completed, name: 'æ¤ç宿' }, |
| | | { value: careData.inProgress, name: 'æ¤çè¿è¡ä¸' }, |
| | | { value: careData.notStarted, name: 'æ¤çæªå¼å§' } |
| | | ] |
| | | } |
| | | |
| | | return { legendData, seriesData } |
| | | }, |
| | | |
| | | initBarLineChart() { |
| | | const barDom = document.getElementById('barLineChart') |
| | | if (!barDom) return |
| | | |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose() |
| | | } |
| | | |
| | | this.barLineChart = echarts.init(barDom) |
| | | |
| | | // å夿°æ® |
| | | const chartData = this.getBarLineChartData() |
| | | |
| | | const option = { |
| | | title: { |
| | | text: `${chartData.title}è¶å¿`, |
| | | left: 'center', |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 16 |
| | | } |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'cross', |
| | | crossStyle: { |
| | | color: '#999' |
| | | } |
| | | } |
| | | }, |
| | | legend: { |
| | | data: chartData.legendData, |
| | | top: 'bottom', |
| | | textStyle: { |
| | | color: '#666' |
| | | } |
| | | }, |
| | | color: chartData.colors, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: chartData.categories, |
| | | axisLabel: { |
| | | interval: 0, |
| | | rotate: 30, |
| | | color: '#666' |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#ddd' |
| | | } |
| | | } |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'value', |
| | | name: chartData.yAxisName1, |
| | | min: 0, |
| | | axisLabel: { |
| | | color: '#666' |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#ddd' |
| | | } |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: '#f0f0f0' |
| | | } |
| | | } |
| | | }, |
| | | { |
| | | type: 'value', |
| | | name: 'ç¾åæ¯(%)', |
| | | min: 0, |
| | | max: 100, |
| | | axisLabel: { |
| | | color: '#666', |
| | | formatter: '{value}%' |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#ddd' |
| | | } |
| | | }, |
| | | splitLine: { |
| | | show: false |
| | | } |
| | | } |
| | | ], |
| | | series: chartData.series, |
| | | grid: { |
| | | top: '15%', |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '15%', |
| | | containLabel: true |
| | | } |
| | | } |
| | | |
| | | this.barLineChart.setOption(option) |
| | | window.addEventListener('resize', this.resizeBarLineChart) |
| | | }, |
| | | |
| | | getBarLineChartData() { |
| | | const categories = this.data.map( |
| | | (item) => item.leavehospitaldistrictname || item.deptname |
| | | ) |
| | | |
| | | let title = 'ç§å®¤/ç
åº' |
| | | let yAxisName1 = '人次' |
| | | let legendData = [] |
| | | let colors = [] |
| | | let series = [] |
| | | |
| | | if (this.activeTab === 'first') { |
| | | title = '馿¬¡é访' |
| | | yAxisName1 = '人次' |
| | | legendData = ['åºé¢äººæ¬¡', 'åºé访人次', 'é访ç(%)', 'åæ¶ç(%)'] |
| | | colors = ['#5470C6', '#91CC75', '#EE6666', '#9A60B4'] |
| | | |
| | | const dischargeData = this.data.map((item) => item.dischargeCount || 0) |
| | | const followUpData = this.data.map((item) => item.followUpNeeded || 0) |
| | | const followUpRateData = this.data.map((item) => { |
| | | if (!item.followUpRate) return 0 |
| | | const rateStr = String(item.followUpRate).replace('%', '') |
| | | return parseFloat(rateStr) || 0 |
| | | }) |
| | | const timelyRateData = this.data.map((item) => |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) : 0 |
| | | ) |
| | | |
| | | series = [ |
| | | { |
| | | name: 'åºé¢äººæ¬¡', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: dischargeData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: 'åºé访人次', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: followUpData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: 'é访ç(%)', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | data: followUpRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3 |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 80, |
| | | lineStyle: { |
| | | color: '#EE6666', |
| | | type: 'dashed' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | }, |
| | | { |
| | | name: 'åæ¶ç(%)', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | data: timelyRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3, |
| | | type: 'dotted' |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 90, |
| | | lineStyle: { |
| | | color: '#9A60B4', |
| | | type: 'dashed' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ] |
| | | } else if (this.activeTab === 'second') { |
| | | title = '忬¡é访' |
| | | yAxisName1 = '人次' |
| | | legendData = ['åºé¢äººæ¬¡', 'åºé访人次', 'é访ç(%)'] |
| | | colors = ['#5470C6', '#91CC75', '#EE6666'] |
| | | |
| | | const dischargeData = this.data.map((item) => item.dischargeCount || 0) |
| | | const followUpData = this.data.map((item) => item.followUpNeeded || 0) |
| | | const followUpRateAgainData = this.data.map((item) => { |
| | | if (!item.followUpRateAgain) return 0 |
| | | const rateStr = String(item.followUpRateAgain).replace('%', '') |
| | | return parseFloat(rateStr) || 0 |
| | | }) |
| | | |
| | | series = [ |
| | | { |
| | | name: 'åºé¢äººæ¬¡', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: dischargeData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: 'åºé访人次', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: followUpData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: 'é访ç(%)', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | data: followUpRateAgainData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3 |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 80, |
| | | lineStyle: { |
| | | color: '#EE6666', |
| | | type: 'dashed' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ] |
| | | } else if (this.activeTab === 'continued') { |
| | | title = 'å»¶ç»æ¤ç' |
| | | yAxisName1 = '人次' |
| | | legendData = ['å»¶ç»æ¤ç人次', 'æ¤ç宿', '宿ç(%)'] |
| | | colors = ['#5470C6', '#91CC75', '#EE6666'] |
| | | |
| | | const continuedCareData = this.data.map((item) => item.continuedCareCount || 0) |
| | | const careCompletedData = this.data.map((item) => item.careCompleted || 0) |
| | | const completionRateData = this.data.map((item) => { |
| | | if (!item.completionRate) return 0 |
| | | const rateStr = String(item.completionRate).replace('%', '') |
| | | return parseFloat(rateStr) || 0 |
| | | }) |
| | | |
| | | series = [ |
| | | { |
| | | name: 'å»¶ç»æ¤ç人次', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: continuedCareData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: 'æ¤ç宿', |
| | | type: 'bar', |
| | | barWidth: '25%', |
| | | data: careCompletedData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0] |
| | | } |
| | | }, |
| | | { |
| | | name: '宿ç(%)', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | data: completionRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3 |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 85, |
| | | lineStyle: { |
| | | color: '#EE6666', |
| | | type: 'dashed' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | |
| | | return { |
| | | title, |
| | | yAxisName1, |
| | | categories, |
| | | legendData, |
| | | colors, |
| | | series |
| | | } |
| | | }, |
| | | |
| | | resizePieChart() { |
| | | if (this.pieChart) { |
| | | this.pieChart.resize() |
| | | } |
| | | }, |
| | | |
| | | resizeBarLineChart() { |
| | | if (this.barLineChart) { |
| | | this.barLineChart.resize() |
| | | } |
| | | }, |
| | | |
| | | destroyCharts() { |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose() |
| | | this.pieChart = null |
| | | } |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose() |
| | | this.barLineChart = null |
| | | } |
| | | window.removeEventListener('resize', this.resizePieChart) |
| | | window.removeEventListener('resize', this.resizeBarLineChart) |
| | | }, |
| | | |
| | | handleClose() { |
| | | this.destroyCharts() |
| | | this.$emit('close') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .chart-container { |
| | | .chart-title { |
| | | text-align: center; |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | margin-bottom: 20px; |
| | | color: #333; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="continued-care"> |
| | | <div class="your-table-container"> |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="tableData" |
| | | :border="true" |
| | | show-summary |
| | | :summary-method="getSummaries" |
| | | > |
| | | <!-- è¡¨æ ¼åå®ä¹ --> |
| | | <el-table-column |
| | | label="åºå·" |
| | | type="index" |
| | | align="center" |
| | | width="60" |
| | | /> |
| | | |
| | | <el-table-column |
| | | label="ç
åºåç§°" |
| | | align="center" |
| | | prop="wardName" |
| | | width="200" |
| | | :show-overflow-tooltip="true" |
| | | /> |
| | | |
| | | <el-table-column |
| | | label="å·²å»¶ç»æ°é" |
| | | align="center" |
| | | prop="continuedCount" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="handleViewDetails(scope.row, 'continued', '已延ç»å表')" |
| | | > |
| | | <span class="button-zx">{{ scope.row.continuedCount }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="æªå»¶ç»æ°é" |
| | | align="center" |
| | | prop="unContinuedCount" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="handleViewDetails(scope.row, 'uncontinued', 'æªå»¶ç»å表')" |
| | | > |
| | | <span class="button-zx">{{ scope.row.unContinuedCount }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="å»¶ç»ç" |
| | | align="center" |
| | | prop="continuedRate" |
| | | width="120" |
| | | /> |
| | | |
| | | <!-- <el-table-column |
| | | label="æä½" |
| | | align="center" |
| | | width="150" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="small" |
| | | type="primary" |
| | | @click="handleRowClick(scope.row)" |
| | | > |
| | | æ¥çæ¤å£«è¯¦æ
|
| | | </el-button> |
| | | </template> |
| | | </el-table-column> --> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <!-- æ¤å£«è¯¦æ
å¼¹çª --> |
| | | <el-dialog |
| | | title="æ¤å£«å»¶ç»æ¤ç详æ
" |
| | | :visible.sync="nurseDialogVisible" |
| | | width="80%" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div v-if="currentWardData"> |
| | | <el-table |
| | | v-loading="nurseLoading" |
| | | :data="nurseData" |
| | | :border="true" |
| | | > |
| | | <el-table-column |
| | | label="æ¤å£«å§å" |
| | | prop="nurseName" |
| | | align="center" |
| | | width="120" |
| | | /> |
| | | <el-table-column |
| | | label="ç§å®¤" |
| | | prop="deptName" |
| | | align="center" |
| | | width="150" |
| | | /> |
| | | <el-table-column |
| | | label="å·²å»¶ç»æ°é" |
| | | prop="continuedCount" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="æªå»¶ç»æ°é" |
| | | prop="unContinuedCount" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="å»¶ç»ç" |
| | | prop="continuedRate" |
| | | align="center" |
| | | width="120" |
| | | /> |
| | | </el-table> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getContinueNerseCount, getNurseContinuedDetail } from "@/api/system/user"; |
| | | import ExcelJS from "exceljs"; |
| | | import { saveAs } from "file-saver"; |
| | | |
| | | export default { |
| | | name: "ContinuedCare", |
| | | props: { |
| | | queryParams: { |
| | | type: Object, |
| | | required: true, |
| | | }, |
| | | flatArrayhospit: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | flatArraydept: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | options: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | orgname: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | tableData: [], |
| | | loading: false, |
| | | nurseDialogVisible: false, |
| | | nurseLoading: false, |
| | | currentWardData: null, |
| | | nurseData: [], |
| | | originalData: {}, |
| | | totalData: { |
| | | continued: 0, |
| | | uncontinued: 0 |
| | | } |
| | | }; |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | | this.loading = true; |
| | | |
| | | const params = { |
| | | leavehospitaldistrictcodes: |
| | | this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.getAllWardCodes() |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.getAllDeptCodes() |
| | | : this.queryParams.deptcodes, |
| | | |
| | | }; |
| | | |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | |
| | | getContinueNerseCount(params) |
| | | .then((response) => { |
| | | this.originalData = response.data; |
| | | this.processData(response.data); |
| | | }) |
| | | .catch((error) => { |
| | | console.error("è·åå»¶ç»æ¤çæ°æ®å¤±è´¥:", error); |
| | | this.$message.error("è·åå»¶ç»æ¤çæ°æ®å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | |
| | | processData(data) { |
| | | this.totalData = { |
| | | continued: data.å·²å»¶ç»æ»æ°é || 0, |
| | | uncontinued: data.æªå»¶ç»æ»æ°é || 0 |
| | | }; |
| | | |
| | | const processedData = []; |
| | | |
| | | if (data.详æ
&& Array.isArray(data.详æ
)) { |
| | | data.详æ
.forEach((item) => { |
| | | // æåç
åºåç§°åæ°æ® |
| | | Object.keys(item).forEach(key => { |
| | | if (key.startsWith('已延ç»_')) { |
| | | const wardName = key.replace('已延ç»_', ''); |
| | | const continuedCount = item[key]; |
| | | const unContinuedCount = item[`æªå»¶ç»_${wardName}`] || 0; |
| | | const total = continuedCount + unContinuedCount; |
| | | const continuedRate = total > 0 ? ((continuedCount / total) * 100).toFixed(2) + '%' : '0.00%'; |
| | | |
| | | processedData.push({ |
| | | wardName, |
| | | continuedCount, |
| | | unContinuedCount, |
| | | continuedRate, |
| | | originalData: item |
| | | }); |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | // æåº |
| | | this.tableData = this.customSort(processedData); |
| | | }, |
| | | |
| | | getAllWardCodes() { |
| | | return this.flatArrayhospit |
| | | .filter((item) => item.value !== "all") |
| | | .map((item) => item.value); |
| | | }, |
| | | |
| | | getAllDeptCodes() { |
| | | return this.flatArraydept |
| | | .filter((item) => item.value !== "all") |
| | | .map((item) => item.value); |
| | | }, |
| | | |
| | | customSort(data) { |
| | | const order = [ |
| | | "ä¸", "äº", "ä¸", "å", "äº", "å
", "ä¸", "å
«", "ä¹", "å", |
| | | "åä¸", "åäº", "åä¸", "åå", "åäº", "åå
", "åä¸", "åå
«", "åä¹", "äºå", |
| | | "äºåä¸", "äºåäº", "äºåä¸", "äºåå", "äºåäº", "äºåå
", "äºåä¸", "äºåå
«", "äºåä¹", "ä¸å", |
| | | "ä¸åä¸", "ä¸åäº", "ä¸åä¸", "ä¸åå", "ä¸åäº", "ä¸åå
", "ä¸åä¸", "ä¸åå
«", "ä¸åä¹", "åå", |
| | | "ååä¸", "ååäº", "ååä¸", "ååå", "ååäº" |
| | | ]; |
| | | |
| | | return data.sort((a, b) => { |
| | | const getIndex = (name) => { |
| | | if (!name || typeof name !== "string") return -1; |
| | | const chineseMatch = name.match(/^(\d+)-/); |
| | | if (chineseMatch && chineseMatch[1]) { |
| | | const num = parseInt(chineseMatch[1], 10); |
| | | if (num >= 1 && num <= 45) { |
| | | return num - 1; |
| | | } |
| | | } |
| | | |
| | | // å°è¯å¹é
䏿æ°å |
| | | for (let i = 0; i < order.length; i++) { |
| | | if (name.includes(order[i])) { |
| | | return i; |
| | | } |
| | | } |
| | | return -1; |
| | | }; |
| | | |
| | | const indexA = getIndex(a.wardName); |
| | | const indexB = getIndex(b.wardName); |
| | | |
| | | if (indexA === -1 && indexB === -1) { |
| | | return (a.wardName || "").localeCompare(b.wardName || ""); |
| | | } |
| | | if (indexA === -1) return 1; |
| | | if (indexB === -1) return -1; |
| | | return indexA - indexB; |
| | | }); |
| | | }, |
| | | |
| | | getSummaries(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | |
| | | if (index === 1) { // ç
åºåç§°å |
| | | sums[index] = "/"; |
| | | return; |
| | | } |
| | | |
| | | if (index === 2) { // å·²å»¶ç»æ°éå计 |
| | | const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0); |
| | | sums[index] = this.formatNumber(totalContinued); |
| | | } else if (index === 3) { // æªå»¶ç»æ°éå计 |
| | | const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0); |
| | | sums[index] = this.formatNumber(totalUnContinued); |
| | | } else if (index === 4) { // å»¶ç»çå¹³åå¼ |
| | | const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0); |
| | | const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0); |
| | | const total = totalContinued + totalUnContinued; |
| | | const avgRate = total > 0 ? ((totalContinued / total) * 100).toFixed(2) + '%' : '0.00%'; |
| | | sums[index] = avgRate; |
| | | } else { |
| | | sums[index] = ""; |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | |
| | | formatNumber(num) { |
| | | if (isNaN(num)) return "-"; |
| | | return Number.isInteger(num) ? num.toString() : num.toFixed(0); |
| | | }, |
| | | |
| | | handleViewDetails(row, type, titleSuffix) { |
| | | const title = `${row.wardName}${titleSuffix}`; |
| | | // è¿ééè¦æ ¹æ®ä½ çå®é
æ
åµè·å详æ
æ°æ® |
| | | // ä½ å¯ä»¥ä»row.originalDataä¸è·åï¼æè
è°ç¨æ°çAPI |
| | | const detailData = this.getMockDetailData(row, type); |
| | | this.$emit("view-details", detailData, title); |
| | | }, |
| | | |
| | | handleRowClick(row) { |
| | | this.currentWardData = row; |
| | | this.nurseDialogVisible = true; |
| | | this.loadNurseData(row.wardName); |
| | | }, |
| | | |
| | | async loadNurseData(wardName) { |
| | | this.nurseLoading = true; |
| | | try { |
| | | const params = { |
| | | wardName: wardName, |
| | | startTime: this.queryParams.startTime, |
| | | endTime: this.queryParams.endTime |
| | | }; |
| | | |
| | | const response = await getNurseContinuedDetail(params); |
| | | this.nurseData = this.processNurseData(response.data); |
| | | } catch (error) { |
| | | console.error("è·åæ¤å£«è¯¦æ
失败:", error); |
| | | this.$message.error("è·åæ¤å£«è¯¦æ
失败"); |
| | | } finally { |
| | | this.nurseLoading = false; |
| | | } |
| | | }, |
| | | |
| | | processNurseData(data) { |
| | | // æ ¹æ®ä½ çå®é
APIååºç»æå¤çæ°æ® |
| | | // è¿éæ¯ä¸ä¸ªç¤ºä¾ |
| | | return [ |
| | | { |
| | | nurseName: "æ¤å£«A", |
| | | deptName: this.currentWardData.wardName, |
| | | continuedCount: Math.floor(this.currentWardData.continuedCount * 0.3), |
| | | unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.3), |
| | | continuedRate: "75.00%" |
| | | }, |
| | | { |
| | | nurseName: "æ¤å£«B", |
| | | deptName: this.currentWardData.wardName, |
| | | continuedCount: Math.floor(this.currentWardData.continuedCount * 0.4), |
| | | unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.4), |
| | | continuedRate: "80.00%" |
| | | }, |
| | | { |
| | | nurseName: "æ¤å£«C", |
| | | deptName: this.currentWardData.wardName, |
| | | continuedCount: Math.floor(this.currentWardData.continuedCount * 0.3), |
| | | unContinuedCount: Math.floor(this.currentWardData.unContinuedCount * 0.3), |
| | | continuedRate: "70.00%" |
| | | } |
| | | ]; |
| | | }, |
| | | |
| | | getMockDetailData(row, type) { |
| | | // 模æè¯¦æ
æ°æ®ï¼å®é
åºè¯¥è°ç¨API |
| | | const detailData = []; |
| | | const count = type === 'continued' ? row.continuedCount : row.unContinuedCount; |
| | | |
| | | for (let i = 1; i <= Math.min(count, 10); i++) { |
| | | detailData.push({ |
| | | sendname: `æ£è
${i}`, |
| | | taskName: `${row.wardName}å»¶ç»æ¤ç`, |
| | | sendstate: type === 'continued' ? 6 : 2, |
| | | preachform: ["人工é访", "çµè¯é访"], |
| | | visitTime: "2024-01-15 10:00:00", |
| | | finishtime: type === 'continued' ? "2024-01-15 11:00:00" : "", |
| | | endtime: "2024-01-10 09:00:00", |
| | | nurseName: "æ¤å£«A", |
| | | drname: "å»çA", |
| | | excep: 1, |
| | | suggest: 2, |
| | | templatename: "å»¶ç»æ¤çæå¡æ¨¡æ¿", |
| | | remark: type === 'continued' ? "å·²å®æå»¶ç»æ¤ç" : "æªå¼å§å»¶ç»æ¤ç", |
| | | bankcardno: "已宿" |
| | | }); |
| | | } |
| | | |
| | | return detailData; |
| | | }, |
| | | |
| | | async exportTable() { |
| | | try { |
| | | let dateRangeString = ""; |
| | | let sheetNameSuffix = ""; |
| | | |
| | | if ( |
| | | this.queryParams.dateRange && |
| | | this.queryParams.dateRange.length === 2 |
| | | ) { |
| | | const startDateStr = this.queryParams.dateRange[0]; |
| | | const endDateStr = this.queryParams.dateRange[1]; |
| | | const formatDateForDisplay = (dateTimeStr) => { |
| | | return dateTimeStr.split(" ")[0]; |
| | | }; |
| | | const startDateFormatted = formatDateForDisplay(startDateStr); |
| | | const endDateFormatted = formatDateForDisplay(endDateStr); |
| | | dateRangeString = `${startDateFormatted}è³${endDateFormatted}`; |
| | | sheetNameSuffix = `${startDateFormatted}è³${endDateFormatted}`; |
| | | } else { |
| | | const now = new Date(); |
| | | const currentMonth = now.getMonth() + 1; |
| | | dateRangeString = `${currentMonth}æ`; |
| | | sheetNameSuffix = `${currentMonth}æ`; |
| | | } |
| | | |
| | | const excelName = `å»¶ç»æ¤çç»è®¡è¡¨_${dateRangeString}.xlsx`; |
| | | const worksheetName = `å»¶ç»æ¤çç»è®¡_${sheetNameSuffix}`; |
| | | |
| | | if (!this.tableData || this.tableData.length === 0) { |
| | | this.$message.warning("ææ å»¶ç»æ¤çæ°æ®å¯å¯¼åº"); |
| | | return false; |
| | | } |
| | | |
| | | const workbook = new ExcelJS.Workbook(); |
| | | const worksheet = workbook.addWorksheet(worksheetName); |
| | | |
| | | this.buildExportSheet(worksheet, sheetNameSuffix); |
| | | |
| | | const buffer = await workbook.xlsx.writeBuffer(); |
| | | const blob = new Blob([buffer], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | saveAs(blob, excelName); |
| | | |
| | | this.$message.success("å¯¼åºæå"); |
| | | return true; |
| | | } catch (error) { |
| | | console.error("导åºå¤±è´¥:", error); |
| | | this.$message.error(`导åºå¤±è´¥: ${error.message}`); |
| | | return false; |
| | | } |
| | | }, |
| | | |
| | | buildExportSheet(worksheet, sheetNameSuffix) { |
| | | const titleStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 16, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFE6F3FF" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const headerStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 11, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const cellStyle = { |
| | | font: { name: "å®ä½", size: 10, color: { argb: "FF000000" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const summaryStyle = { |
| | | font: { |
| | | name: "å®ä½", |
| | | size: 10, |
| | | bold: true, |
| | | color: { argb: "FF409EFF" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | // æ·»å æ é¢è¡ |
| | | worksheet.mergeCells(1, 1, 1, 6); |
| | | const titleCell = worksheet.getCell(1, 1); |
| | | titleCell.value = `å»¶ç»æ¤çç»è®¡è¡¨_${sheetNameSuffix}`; |
| | | titleCell.style = titleStyle; |
| | | worksheet.getRow(1).height = 35; |
| | | |
| | | // 表头 |
| | | const headers = ["åºå·", "ç
åºåç§°", "å·²å»¶ç»æ°é", "æªå»¶ç»æ°é", "å»¶ç»ç", "æä½"]; |
| | | |
| | | headers.forEach((header, index) => { |
| | | const cell = worksheet.getCell(2, index + 1); |
| | | cell.value = header; |
| | | cell.style = headerStyle; |
| | | }); |
| | | |
| | | worksheet.getRow(2).height = 25; |
| | | |
| | | // æ°æ®è¡ |
| | | this.tableData.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow( |
| | | [ |
| | | rowIndex + 1, |
| | | item.wardName, |
| | | item.continuedCount, |
| | | item.unContinuedCount, |
| | | item.continuedRate, |
| | | "æ¥çæ¤å£«è¯¦æ
" |
| | | ], |
| | | rowIndex + 3 |
| | | ); |
| | | |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle; |
| | | }); |
| | | dataRow.height = 24; |
| | | }); |
| | | |
| | | // åè®¡è¡ |
| | | const totalContinued = this.tableData.reduce((sum, item) => sum + item.continuedCount, 0); |
| | | const totalUnContinued = this.tableData.reduce((sum, item) => sum + item.unContinuedCount, 0); |
| | | const total = totalContinued + totalUnContinued; |
| | | const totalRate = total > 0 ? ((totalContinued / total) * 100).toFixed(2) + '%' : '0.00%'; |
| | | |
| | | const summaryRow = worksheet.addRow([ |
| | | "å计", |
| | | "/", |
| | | totalContinued, |
| | | totalUnContinued, |
| | | totalRate, |
| | | "/" |
| | | ]); |
| | | |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle; |
| | | }); |
| | | summaryRow.height = 28; |
| | | |
| | | // å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, |
| | | { width: 30 }, |
| | | { width: 15 }, |
| | | { width: 15 }, |
| | | { width: 12 }, |
| | | { width: 15 } |
| | | ]; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .continued-care { |
| | | .your-table-container { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog |
| | | :title="title" |
| | | :visible.sync="visible" |
| | | v-loading="loading" |
| | | width="70%" |
| | | :close-on-click-modal="false" |
| | | @close="handleClose" |
| | | > |
| | | <div class="detail-dialog"> |
| | | <div style="margin-bottom: 16px; display: flex; align-items: center"> |
| | | <span style="margin-right: 10px; font-weight: bold">æ£è
å§åæ¥è¯¢:</span> |
| | | <el-input |
| | | v-model="searchName" |
| | | placeholder="请è¾å
¥æ£è
å§åè¿è¡çé" |
| | | clearable |
| | | style="width: 300px" |
| | | @input="handleSearch" |
| | | @clear="handleSearch" |
| | | /> |
| | | <span style="margin-left: 10px; color: rgb(35, 81, 233); font-size: 16px"> |
| | | å
± {{ displayList.length }} æ¡è®°å½ |
| | | </span> |
| | | </div> |
| | | |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <div class="data-list" ref="dataList" @scroll="handleScroll" v-loading="loading"> |
| | | <el-table :data="currentDisplayList" height="660" style="width: 100%"> |
| | | <el-table-column prop="sendname" align="center" label="å§å" width="100" /> |
| | | <el-table-column prop="taskName" align="center" width="200" show-overflow-tooltip label="ä»»å¡åç§°" /> |
| | | |
| | | <el-table-column prop="sendstate" align="center" width="200" label="ä»»å¡ç¶æ"> |
| | | <template slot-scope="scope"> |
| | | <el-tag |
| | | :type="getStateTagType(scope.row.sendstate)" |
| | | :disable-transitions="false" |
| | | > |
| | | {{ getStateText(scope.row.sendstate) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="任塿§è¡æ¹å¼" |
| | | align="center" |
| | | key="preachform" |
| | | prop="preachform" |
| | | width="160" |
| | | :show-overflow-tooltip="true" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <span v-for="(item, index) in scope.row.preachform" :key="index"> |
| | | {{ item }}{{ index < scope.row.preachform.length - 1 ? 'ã' : '' }} |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | prop="visitTime" |
| | | align="center" |
| | | label="åºé访æ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | prop="finishtime" |
| | | align="center" |
| | | label="éè®¿å®ææ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column label="åºé¢æ¥æ" width="200" align="center" key="endtime" prop="endtime"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="责任æ¤å£«" width="120" align="center" key="nurseName" prop="nurseName" /> |
| | | <el-table-column label="主治å»ç" width="120" align="center" key="drname" prop="drname" /> |
| | | |
| | | <el-table-column label="ç»æç¶æ" align="center" key="excep" prop="excep" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_yujing" :value="scope.row.excep" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="å¤çæè§" align="center" key="suggest" prop="suggest" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_suggest" :value="scope.row.suggest" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="templatename" align="center" label="æå¡æ¨¡æ¿" width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="remark" align="center" label="æå¡è®°å½" width="200" show-overflow-tooltip /> |
| | | |
| | | <el-table-column prop="bankcardno" align="center" label="å¼å«ç¶æ" width="210" /> |
| | | |
| | | <el-table-column label="æä½" fixed="right" align="center" width="200" class-name="small-padding fixed-width"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleDetailsGo(scope.row)"> |
| | | <span class="button-zx"> |
| | | <i class="el-icon-s-order"></i>æ¥ç |
| | | </span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'DetailDialog', |
| | | dicts: ['sys_yujing', 'sys_suggest'], |
| | | props: { |
| | | visible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | data: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | searchName: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | localSearchName: '', |
| | | displayList: [], |
| | | currentDisplayList: [], |
| | | loadIndex: 0, |
| | | pageSize: 100, |
| | | isLoading: false |
| | | } |
| | | }, |
| | | watch: { |
| | | data: { |
| | | immediate: true, |
| | | handler(newData) { |
| | | this.initializeData(newData) |
| | | } |
| | | }, |
| | | searchName(newVal) { |
| | | this.localSearchName = newVal |
| | | this.handleSearch() |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.data && this.data.length > 0) { |
| | | this.initializeData(this.data) |
| | | } |
| | | }, |
| | | methods: { |
| | | initializeData(data) { |
| | | this.displayList = [...data] |
| | | this.formatPreachformData() |
| | | this.loadIndex = 0 |
| | | this.currentDisplayList = [] |
| | | this.$nextTick(() => { |
| | | this.loadMoreData() |
| | | }) |
| | | }, |
| | | |
| | | formatPreachformData() { |
| | | this.displayList.forEach((item) => { |
| | | if (item.preachform) { |
| | | if (item.endtime) { |
| | | item.preachformson = item.preachform |
| | | const idArray = item.preachform.split(',') |
| | | |
| | | item.preachform = idArray.map((value) => { |
| | | const checkboxlist = this.$store.getters.checkboxlist |
| | | const foundItem = checkboxlist.find((item) => item.value == value) |
| | | return foundItem ? foundItem.label : null |
| | | }).filter(label => label !== null) |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | handleSearch() { |
| | | if (!this.localSearchName.trim()) { |
| | | this.displayList = [...this.data] |
| | | this.formatPreachformData() |
| | | } else { |
| | | const keyword = this.localSearchName.toLowerCase() |
| | | this.displayList = this.data.filter((item) => { |
| | | return item.sendname && item.sendname.toLowerCase().includes(keyword) |
| | | }) |
| | | this.formatPreachformData() |
| | | } |
| | | |
| | | this.loadIndex = 0 |
| | | this.currentDisplayList = [] |
| | | this.$nextTick(() => { |
| | | this.loadMoreData() |
| | | }) |
| | | |
| | | this.$emit('search', this.localSearchName) |
| | | }, |
| | | |
| | | loadMoreData() { |
| | | if (this.isLoading || this.loadIndex >= this.displayList.length) return |
| | | |
| | | this.isLoading = true |
| | | |
| | | setTimeout(() => { |
| | | const nextChunk = this.displayList.slice( |
| | | this.loadIndex, |
| | | this.loadIndex + this.pageSize |
| | | ) |
| | | this.currentDisplayList = this.currentDisplayList.concat(nextChunk) |
| | | this.loadIndex += this.pageSize |
| | | this.isLoading = false |
| | | }, 200) |
| | | }, |
| | | |
| | | handleScroll(event) { |
| | | const scrollContainer = event.target |
| | | const isAtBottom = |
| | | scrollContainer.scrollTop + scrollContainer.clientHeight >= |
| | | scrollContainer.scrollHeight - 10 |
| | | |
| | | if ( |
| | | isAtBottom && |
| | | !this.isLoading && |
| | | this.loadIndex < this.displayList.length |
| | | ) { |
| | | this.loadMoreData() |
| | | } |
| | | }, |
| | | |
| | | getStateTagType(state) { |
| | | const stateMap = { |
| | | 1: 'primary', // 表åå·²é¢å |
| | | 2: 'primary', // å¾
é访 |
| | | 3: 'success', // 表åå·²åé |
| | | 4: 'info', // 䏿§è¡ |
| | | 5: 'danger', // åé失败 |
| | | 6: 'success' // 已宿 |
| | | } |
| | | return stateMap[state] || 'info' |
| | | }, |
| | | |
| | | getStateText(state) { |
| | | const stateTextMap = { |
| | | 1: '表åå·²é¢å', |
| | | 2: 'å¾
é访', |
| | | 3: '表åå·²åé', |
| | | 4: '䏿§è¡', |
| | | 5: 'åé失败', |
| | | 6: '已宿' |
| | | } |
| | | return stateTextMap[state] || 'æªç¥ç¶æ' |
| | | }, |
| | | |
| | | formatTime(time) { |
| | | if (!time) return '' |
| | | return this.parseTime(time) |
| | | }, |
| | | |
| | | handleDetailsGo(row) { |
| | | this.$emit('details-go', row) |
| | | }, |
| | | |
| | | handleClose() { |
| | | this.$emit('close') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .detail-dialog { |
| | | .data-list { |
| | | max-height: 800px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="first-follow-up"> |
| | | <div class="your-table-container"> |
| | | <el-table |
| | | ref="exportTable" |
| | | id="exportTableid" |
| | | v-loading="loading" |
| | | :data="tableData" |
| | | :border="true" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="handleRowClick" |
| | | :row-key="getRowKey" |
| | | show-summary |
| | | :summary-method="getSummaries" |
| | | :expand-row-keys="expands" |
| | | > |
| | | <!-- å±å¼è¡ç®å¤´å --> |
| | | <el-table-column type="expand"> |
| | | <template slot-scope="props"> |
| | | <el-table |
| | | :data="props.row.doctorStats" |
| | | border |
| | | style="width: 95%; margin: 0 auto" |
| | | class="inner-table" |
| | | show-summary |
| | | :summary-method="getInnerSummaries" |
| | | > |
| | | <el-table-column label="å»çå§å" prop="drname" align="center" /> |
| | | <el-table-column label="ç§å®¤" width="120" prop="deptname" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" prop="dischargeCount" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount" /> |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" /> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" /> |
| | | |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUp" prop="needFollowUp" /> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUp" prop="pendingFollowUp" /> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccess" prop="followUpSuccess" /> |
| | | <el-table-column label="é访失败" align="center" key="followUpFail" prop="followUpFail" /> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRate" prop="followUpRate" /> |
| | | <el-table-column v-if="orgname != '丽水å¸ä¸å»é¢'" label="åæ¶ç" align="center" width="120" key="rate" prop="rate"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleSeeDetails(scope.row)"> |
| | | <span class="button-zx">{{ (Number(scope.row.rate) * 100).toFixed(2) }}%</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manual" prop="manual" /> |
| | | <el-table-column label="çä¿¡" align="center" key="sms" prop="sms" /> |
| | | <el-table-column label="微信" align="center" key="weChat" prop="weChat" /> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <!-- è¡¨æ ¼åå®ä¹ --> |
| | | <el-table-column |
| | | label="åºé¢ç
åº" |
| | | align="center" |
| | | sortable |
| | | key="leavehospitaldistrictname" |
| | | prop="leavehospitaldistrictname" |
| | | width="150" |
| | | :show-overflow-tooltip="true" |
| | | :sort-method="sortChineseNumber" |
| | | /> |
| | | <el-table-column label="ç§å®¤" align="center" key="deptname" prop="deptname" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount" /> |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" /> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" /> |
| | | |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUp" prop="needFollowUp"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'needFollowUpInfo', 'éé访å表')"> |
| | | <span class="button-zx">{{ scope.row.needFollowUp }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUp" prop="pendingFollowUp"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'pendingFollowUpInfo', 'å¾
é访å表')"> |
| | | <span class="button-zx">{{ scope.row.pendingFollowUp }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccess" prop="followUpSuccess"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpSuccessInfo', 'é访æåå表')"> |
| | | <span class="button-zx">{{ scope.row.followUpSuccess }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFail" prop="followUpFail"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpFailInfo', 'é访失败å表')"> |
| | | <span class="button-zx">{{ scope.row.followUpFail }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRate" prop="followUpRate" /> |
| | | <el-table-column v-if="orgname != '丽水å¸ä¸å»é¢'" label="åæ¶ç" align="center" width="120" key="rate" prop="rate"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleSeeDetails(scope.row)"> |
| | | <span class="button-zx">{{ (Number(scope.row.rate) * 100).toFixed(2) }}%</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manual" prop="manual"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'manualInfo', '人工é访å表')"> |
| | | <span class="button-zx">{{ scope.row.manual }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="sms" prop="sms"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'smsInfo', 'çä¿¡é访å表')"> |
| | | <span class="button-zx">{{ scope.row.sms }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChat" prop="weChat"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'weChatInfo', '微信é访å表')"> |
| | | <span class="button-zx">{{ scope.row.weChat }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | |
| | | <!-- é访æ
åµåï¼ä»
丽水å¸ä¸å»é¢æ¾ç¤ºï¼ --> |
| | | <el-table-column v-if="orgname == '丽水å¸ä¸å»é¢'" align="center" label="é访æ
åµ"> |
| | | <el-table-column label="æ£å¸¸è¯é³" align="center" width="100" key="taskSituation1" prop="taskSituation1" /> |
| | | <el-table-column label="æ£è
ææ¥ææè®¿" align="center" width="100" key="taskSituation2" prop="taskSituation2" /> |
| | | <el-table-column label="é¢è®¿æè
æ¥è¯" align="center" width="100" key="taskSituation3" prop="taskSituation3" /> |
| | | <el-table-column label="微信é访" align="center" width="100" key="taskSituation4" prop="taskSituation4" /> |
| | | <el-table-column label="é访çµè¯ä¸æ£ç¡®" align="center" width="100" key="taskSituation5" prop="taskSituation5" /> |
| | | <el-table-column label="å
¶ä»æ
åµä¸å®é访" align="center" width="100" key="taskSituation6" prop="taskSituation6" /> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getSfStatistics, selectTimelyRate } from "@/api/system/user"; |
| | | import ExcelJS from "exceljs"; |
| | | import { saveAs } from "file-saver"; |
| | | |
| | | export default { |
| | | name: 'FirstFollowUp', |
| | | props: { |
| | | queryParams: { |
| | | type: Object, |
| | | required: true |
| | | }, |
| | | flatArrayhospit: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | flatArraydept: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | options: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | orgname: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | tableData: [], |
| | | loading: false, |
| | | expands: [], |
| | | ids: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | | this.loading = true |
| | | const params = { |
| | | ...this.queryParams, |
| | | visitCount: 1, |
| | | leavehospitaldistrictcodes: this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.getAllWardCodes() |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.getAllDeptCodes() |
| | | : this.queryParams.deptcodes |
| | | } |
| | | |
| | | delete params.leavehospitaldistrictcodes.all |
| | | delete params.deptcodes.all |
| | | |
| | | getSfStatistics(params) |
| | | .then(response => { |
| | | this.tableData = this.customSort(response.data) |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·å馿¬¡éè®¿æ°æ®å¤±è´¥:", error) |
| | | this.$message.error("è·å馿¬¡éè®¿æ°æ®å¤±è´¥") |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | |
| | | getAllWardCodes() { |
| | | return this.flatArrayhospit |
| | | .filter(item => item.value !== 'all') |
| | | .map(item => item.value) |
| | | }, |
| | | |
| | | getAllDeptCodes() { |
| | | return this.flatArraydept |
| | | .filter(item => item.value !== 'all') |
| | | .map(item => item.value) |
| | | }, |
| | | |
| | | customSort(data) { |
| | | const order = [ |
| | | "ä¸","äº","ä¸","å","äº","å
","ä¸","å
«","ä¹","å", |
| | | "åä¸","åäº","åä¸","åå","åäº","åå
","åä¸","åå
«","åä¹","äºå", |
| | | "äºåä¸","äºåäº","äºåä¸","äºåå","äºåäº","äºåå
","äºåä¸","äºåå
«","äºåä¹","ä¸å", |
| | | "ä¸åä¸","ä¸åäº","ä¸åä¸","ä¸åå","ä¸åäº","ä¸åå
","ä¸åä¸","ä¸åå
«","ä¸åä¹","åå", |
| | | "ååä¸","ååäº","ååä¸","ååå","ååäº" |
| | | ] |
| | | |
| | | return data.sort((a, b) => { |
| | | const getIndex = (name) => { |
| | | if (!name || typeof name !== "string") return -1 |
| | | const chineseMatch = name.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/) |
| | | if (chineseMatch && chineseMatch[1]) { |
| | | return order.indexOf(chineseMatch[1]) |
| | | } |
| | | const arabicMatch = name.match(/^(\d+)/) |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10) |
| | | if (num >= 1 && num <= 45) { |
| | | return num - 1 |
| | | } |
| | | } |
| | | return -1 |
| | | } |
| | | |
| | | const indexA = getIndex(a.leavehospitaldistrictname) |
| | | const indexB = getIndex(b.leavehospitaldistrictname) |
| | | |
| | | if (indexA === -1 && indexB === -1) { |
| | | return (a.leavehospitaldistrictname || "").localeCompare(b.leavehospitaldistrictname || "") |
| | | } |
| | | if (indexA === -1) return 1 |
| | | if (indexB === -1) return -1 |
| | | return indexA - indexB |
| | | }) |
| | | }, |
| | | |
| | | sortChineseNumber(aRow, bRow) { |
| | | const a = aRow.leavehospitaldistrictname |
| | | const b = bRow.leavehospitaldistrictname |
| | | |
| | | const chineseNumMap = { |
| | | ä¸:1,äº:2,ä¸:3,å:4,äº:5,å
:6,ä¸:7,å
«:8,ä¹:9,å:10, |
| | | åä¸:11,åäº:12,åä¸:13,åå:14,åäº:15,åå
:16,åä¸:17,åå
«:18,åä¹:19,äºå:20, |
| | | äºåä¸:21,äºåäº:22,äºåä¸:23,äºåå:24,äºåäº:25,äºåå
:26,äºåä¸:27,äºåå
«:28,äºåä¹:29,ä¸å:30, |
| | | ä¸åä¸:31,ä¸åäº:32,ä¸åä¸:33,ä¸åå:34,ä¸åäº:35,ä¸åå
:36,ä¸åä¸:37,ä¸åå
«:38,ä¸åä¹:39,åå:40, |
| | | ååä¸:41,ååäº:42,ååä¸:43,ååå:44,ååäº:45 |
| | | } |
| | | |
| | | const getNumberFromText = (text) => { |
| | | if (!text || typeof text !== "string") return -1 |
| | | const match = text.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/) |
| | | if (match && match[1]) { |
| | | const chineseNum = match[1] |
| | | return chineseNumMap[chineseNum] !== undefined ? chineseNumMap[chineseNum] : -1 |
| | | } |
| | | const arabicMatch = text.match(/^(\d+)/) |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10) |
| | | return num >= 1 && num <= 45 ? num : -1 |
| | | } |
| | | return -1 |
| | | } |
| | | |
| | | const numA = getNumberFromText(a) |
| | | const numB = getNumberFromText(b) |
| | | |
| | | if (numA === -1 && numB === -1) { |
| | | return (a || "").localeCompare(b || "") |
| | | } |
| | | if (numA === -1) return 1 |
| | | if (numB === -1) return -1 |
| | | return numA - numB |
| | | }, |
| | | |
| | | getRowKey(row) { |
| | | return row.statisticaltype === 1 ? row.leavehospitaldistrictcode : row.deptcode |
| | | }, |
| | | |
| | | handleRowClick(row) { |
| | | if (this.expands.includes(this.getRowKey(row))) { |
| | | this.expands = [] |
| | | return |
| | | } |
| | | |
| | | const params = { |
| | | ...this.queryParams, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.getAllDeptCodes() |
| | | : this.queryParams.deptcodes, |
| | | leavehospitaldistrictcodes: [row.leavehospitaldistrictcode], |
| | | drcode: "1", |
| | | visitCount: 1 |
| | | } |
| | | |
| | | delete params.leavehospitaldistrictcodes.all |
| | | delete params.deptcodes.all |
| | | |
| | | if (!row.doctorStats) { |
| | | this.loading = true |
| | | getSfStatistics(params).then((res) => { |
| | | this.$set(row, "doctorStats", res.data) |
| | | this.expands = [this.getRowKey(row)] |
| | | this.loading = false |
| | | }) |
| | | } else { |
| | | this.expands = [this.getRowKey(row)] |
| | | } |
| | | }, |
| | | |
| | | getSummaries(param) { |
| | | const { columns, data } = param |
| | | const sums = [] |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计" |
| | | return |
| | | } |
| | | if (index === 1 || index === 2) { |
| | | sums[index] = "/" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "followUpRate" || column.property === "rate") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property] |
| | | if (!value || value === "-" || value === "0%") return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100 |
| | | return isNaN(numValue) ? null : numValue |
| | | } else { |
| | | const numValue = parseFloat(value) |
| | | return isNaN(numValue) ? null : numValue |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0) |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length |
| | | sums[index] = (average * 100).toFixed(2) + "%" |
| | | } else { |
| | | sums[index] = "0.00%" |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property] |
| | | if (value === "-" || value === "" || value === null) return 0 |
| | | return Number(value) || 0 |
| | | }) |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0) |
| | | sums[index] = this.formatNumber(sums[index]) |
| | | } else { |
| | | sums[index] = "-" |
| | | } |
| | | } |
| | | }) |
| | | |
| | | return sums |
| | | }, |
| | | |
| | | getInnerSummaries(param) { |
| | | const { columns, data } = param |
| | | const sums = [] |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å°è®¡" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "drname" || column.property === "deptname") { |
| | | sums[index] = "-" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "followUpRate" || column.property === "rate") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property] |
| | | if (!value || value === "-" || value === "0%") return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100 |
| | | return isNaN(numValue) ? null : numValue |
| | | } else { |
| | | const numValue = parseFloat(value) |
| | | return isNaN(numValue) ? null : numValue |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0) |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length |
| | | sums[index] = (average * 100).toFixed(2) + "%" |
| | | } else { |
| | | sums[index] = "0.00%" |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property] |
| | | if (value === "-" || value === "" || value === null) return 0 |
| | | return Number(value) || 0 |
| | | }) |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0) |
| | | sums[index] = this.formatNumber(sums[index]) |
| | | } else { |
| | | sums[index] = "-" |
| | | } |
| | | } |
| | | }) |
| | | |
| | | return sums |
| | | }, |
| | | |
| | | formatNumber(num) { |
| | | if (isNaN(num)) return "-" |
| | | return Number.isInteger(num) ? num.toString() : num.toFixed(0) |
| | | }, |
| | | |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map((item) => item.tagid) |
| | | }, |
| | | |
| | | handleViewDetails(row, infoKey, titleSuffix) { |
| | | const title = `${row.leavehospitaldistrictname || row.deptname}${titleSuffix}` |
| | | this.$emit('view-details', row[infoKey], title) |
| | | }, |
| | | |
| | | handleSeeDetails(row) { |
| | | this.$emit('see-details', row) |
| | | }, |
| | | |
| | | async exportTable() { |
| | | try { |
| | | let dateRangeString = "" |
| | | let sheetNameSuffix = "" |
| | | |
| | | if (this.queryParams.dateRange && this.queryParams.dateRange.length === 2) { |
| | | const startDateStr = this.queryParams.dateRange[0] |
| | | const endDateStr = this.queryParams.dateRange[1] |
| | | const formatDateForDisplay = (dateTimeStr) => { |
| | | return dateTimeStr.split(" ")[0] |
| | | } |
| | | const startDateFormatted = formatDateForDisplay(startDateStr) |
| | | const endDateFormatted = formatDateForDisplay(endDateStr) |
| | | dateRangeString = `${startDateFormatted}è³${endDateFormatted}` |
| | | sheetNameSuffix = `${startDateFormatted}è³${endDateFormatted}` |
| | | } else { |
| | | const now = new Date() |
| | | const currentMonth = now.getMonth() + 1 |
| | | dateRangeString = `${currentMonth}æ` |
| | | sheetNameSuffix = `${currentMonth}æ` |
| | | } |
| | | |
| | | const excelName = `馿¬¡åºé¢é访ç»è®¡è¡¨_${dateRangeString}.xlsx` |
| | | const worksheetName = `馿¬¡é访ç»è®¡_${sheetNameSuffix}` |
| | | |
| | | if (!this.tableData || this.tableData.length === 0) { |
| | | this.$message.warning("ææ é¦æ¬¡éè®¿æ°æ®å¯å¯¼åº") |
| | | return false |
| | | } |
| | | |
| | | const workbook = new ExcelJS.Workbook() |
| | | const worksheet = workbook.addWorksheet(worksheetName) |
| | | |
| | | // æå»ºè¡¨æ ¼ |
| | | this.buildExportSheet(worksheet, sheetNameSuffix) |
| | | |
| | | const buffer = await workbook.xlsx.writeBuffer() |
| | | const blob = new Blob([buffer], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
| | | }) |
| | | saveAs(blob, excelName) |
| | | |
| | | this.$message.success("å¯¼åºæå") |
| | | return true |
| | | } catch (error) { |
| | | console.error("导åºå¤±è´¥:", error) |
| | | this.$message.error(`导åºå¤±è´¥: ${error.message}`) |
| | | return false |
| | | } |
| | | }, |
| | | |
| | | buildExportSheet(worksheet, sheetNameSuffix) { |
| | | const titleStyle = { |
| | | font: { name: "微软é
é»", size: 16, bold: true, color: { argb: "FF000000" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFE6F3FF" } }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const headerStyle = { |
| | | font: { name: "微软é
é»", size: 11, bold: true, color: { argb: "FF000000" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const cellStyle = { |
| | | font: { name: "å®ä½", size: 10, color: { argb: "FF000000" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const summaryStyle = { |
| | | font: { name: "å®ä½", size: 10, bold: true, color: { argb: "FF409EFF" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | // æ·»å æ é¢è¡ |
| | | worksheet.mergeCells(1, 1, 1, 16) |
| | | const titleCell = worksheet.getCell(1, 1) |
| | | titleCell.value = `馿¬¡åºé¢é访ç»è®¡è¡¨_${sheetNameSuffix}` |
| | | titleCell.style = titleStyle |
| | | worksheet.getRow(1).height = 35 |
| | | |
| | | // 表头 |
| | | const secondRowHeaders = [ |
| | | "", "åºé¢ç
åº", "ç§å®¤", "åºé¢äººæ¬¡", "æ éé访人次", "åºé访人次", |
| | | "éé访", "å¾
é访", "é访æå", "é访失败", "é访ç", "åæ¶ç", "人工", "çä¿¡", "微信" |
| | | ] |
| | | |
| | | secondRowHeaders.forEach((header, index) => { |
| | | const cell = worksheet.getCell(3, index + 1) |
| | | cell.value = header |
| | | cell.style = headerStyle |
| | | }) |
| | | |
| | | // åå¹¶åå
æ ¼ |
| | | for (let i = 1; i <= 6; i++) { |
| | | worksheet.mergeCells(2, i, 3, i) |
| | | const cell = worksheet.getCell(2, i) |
| | | cell.style = headerStyle |
| | | } |
| | | |
| | | worksheet.getCell(2, 1).value = "" |
| | | worksheet.getCell(2, 2).value = "åºé¢ç
åº" |
| | | worksheet.getCell(2, 3).value = "ç§å®¤" |
| | | worksheet.getCell(2, 4).value = "åºé¢äººæ¬¡" |
| | | worksheet.getCell(2, 5).value = "æ éé访人次" |
| | | worksheet.getCell(2, 6).value = "åºé访人次" |
| | | |
| | | worksheet.mergeCells(2, 7, 2, 15) |
| | | worksheet.getCell(2, 7).value = "馿¬¡åºé¢é访" |
| | | worksheet.getCell(2, 7).style = headerStyle |
| | | |
| | | worksheet.getRow(2).height = 28 |
| | | worksheet.getRow(3).height = 25 |
| | | |
| | | // æ°æ®è¡ |
| | | this.tableData.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow([ |
| | | "", |
| | | item.leavehospitaldistrictname || "", |
| | | item.deptname || "", |
| | | item.dischargeCount || 0, |
| | | item.nonFollowUp || 0, |
| | | item.followUpNeeded || 0, |
| | | item.needFollowUp || 0, |
| | | item.pendingFollowUp || 0, |
| | | item.followUpSuccess || 0, |
| | | item.followUpFail || 0, |
| | | item.followUpRate || "0%", |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%", |
| | | item.manual || 0, |
| | | item.sms || 0, |
| | | item.weChat || 0 |
| | | ], rowIndex + 4) |
| | | |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle |
| | | }) |
| | | dataRow.height = 24 |
| | | }) |
| | | |
| | | // åè®¡è¡ |
| | | const summaries = this.getExportSummaries() |
| | | const summaryRow = worksheet.addRow(summaries) |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle |
| | | if (colNumber === 1) { |
| | | cell.value = "å计" |
| | | } |
| | | }) |
| | | summaryRow.height = 28 |
| | | |
| | | // å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, { width: 20 }, { width: 15 }, { width: 12 }, { width: 12 }, { width: 12 }, |
| | | { width: 10 }, { width: 10 }, { width: 10 }, { width: 10 }, { width: 12 }, { width: 12 }, |
| | | { width: 8 }, { width: 8 }, { width: 8 } |
| | | ] |
| | | }, |
| | | |
| | | getExportSummaries() { |
| | | const summaries = ["å计", "/", "/", 0, 0, 0, 0, 0, 0, 0, "0%", "0%", 0, 0, 0] |
| | | |
| | | this.tableData.forEach((item) => { |
| | | summaries[3] += Number(item.dischargeCount) || 0 |
| | | summaries[4] += Number(item.nonFollowUp) || 0 |
| | | summaries[5] += Number(item.followUpNeeded) || 0 |
| | | summaries[6] += Number(item.needFollowUp) || 0 |
| | | summaries[7] += Number(item.pendingFollowUp) || 0 |
| | | summaries[8] += Number(item.followUpSuccess) || 0 |
| | | summaries[9] += Number(item.followUpFail) || 0 |
| | | summaries[12] += Number(item.manual) || 0 |
| | | summaries[13] += Number(item.sms) || 0 |
| | | summaries[14] += Number(item.weChat) || 0 |
| | | }) |
| | | |
| | | const followUpRateValues = this.tableData |
| | | .map((item) => this.extractPercentageValue(item.followUpRate)) |
| | | .filter((value) => value !== null) |
| | | |
| | | const rateValues = this.tableData |
| | | .map((item) => this.extractPercentageValue(item.rate)) |
| | | .filter((value) => value !== null) |
| | | |
| | | if (followUpRateValues.length > 0) { |
| | | const avgFollowUpRate = followUpRateValues.reduce((sum, val) => sum + val, 0) / followUpRateValues.length |
| | | summaries[10] = (avgFollowUpRate * 100).toFixed(2) + "%" |
| | | } |
| | | |
| | | if (rateValues.length > 0) { |
| | | const avgRate = rateValues.reduce((sum, val) => sum + val, 0) / rateValues.length |
| | | summaries[11] = (avgRate * 100).toFixed(2) + "%" |
| | | } |
| | | |
| | | summaries[3] = this.formatNumber(summaries[3]) |
| | | summaries[4] = this.formatNumber(summaries[4]) |
| | | summaries[5] = this.formatNumber(summaries[5]) |
| | | summaries[6] = this.formatNumber(summaries[6]) |
| | | summaries[7] = this.formatNumber(summaries[7]) |
| | | summaries[8] = this.formatNumber(summaries[8]) |
| | | summaries[9] = this.formatNumber(summaries[9]) |
| | | summaries[12] = this.formatNumber(summaries[12]) |
| | | summaries[13] = this.formatNumber(summaries[13]) |
| | | summaries[14] = this.formatNumber(summaries[14]) |
| | | |
| | | return summaries |
| | | }, |
| | | |
| | | extractPercentageValue(value) { |
| | | if (!value) return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const num = parseFloat(value.replace("%", "")) |
| | | return isNaN(num) ? null : num / 100 |
| | | } |
| | | const num = parseFloat(value) |
| | | return isNaN(num) ? null : num |
| | | }, |
| | | |
| | | selectTimelyRate(row, dateRange) { |
| | | const params = { |
| | | ...this.patientqueryParams, |
| | | starttime: this.parseTime(dateRange[0]), |
| | | endtime: this.parseTime(dateRange[1]), |
| | | deptcode: row.deptcode |
| | | } |
| | | return selectTimelyRate(params) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .first-follow-up { |
| | | .your-table-container { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="second-follow-up"> |
| | | <div class="your-table-container"> |
| | | <el-table |
| | | ref="exportTableSecond" |
| | | id="exportTableidSecond" |
| | | v-loading="loading" |
| | | :data="tableData" |
| | | :border="true" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="handleRowClick" |
| | | :row-key="getRowKey" |
| | | show-summary |
| | | :summary-method="getSummaries" |
| | | :expand-row-keys="expands" |
| | | > |
| | | <!-- å±å¼è¡ç®å¤´å --> |
| | | <el-table-column type="expand"> |
| | | <template slot-scope="props"> |
| | | <el-table |
| | | :data="props.row.doctorStats" |
| | | border |
| | | style="width: 95%; margin: 0 auto" |
| | | class="inner-table" |
| | | show-summary |
| | | :summary-method="getInnerSummaries" |
| | | > |
| | | <el-table-column label="å»çå§å" prop="drname" align="center" /> |
| | | <el-table-column label="ç§å®¤" width="120" prop="deptname" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" prop="dischargeCount" align="center" /> |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" /> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" /> |
| | | |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUpAgain" prop="needFollowUpAgain" /> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUpAgain" prop="pendingFollowUpAgain" /> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccessAgain" prop="followUpSuccessAgain" /> |
| | | <el-table-column label="é访失败" align="center" key="followUpFailAgain" prop="followUpFailAgain" /> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRateAgain" prop="followUpRateAgain" /> |
| | | <el-table-column label="人工" align="center" key="manualAgain" prop="manualAgain" /> |
| | | <el-table-column label="çä¿¡" align="center" key="smsAgain" prop="smsAgain" /> |
| | | <el-table-column label="微信" align="center" key="weChatAgain" prop="weChatAgain" /> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <!-- è¡¨æ ¼åå®ä¹ --> |
| | | <el-table-column |
| | | label="åºé¢ç
åº" |
| | | align="center" |
| | | sortable |
| | | key="leavehospitaldistrictname" |
| | | prop="leavehospitaldistrictname" |
| | | width="150" |
| | | :show-overflow-tooltip="true" |
| | | :sort-method="sortChineseNumber" |
| | | /> |
| | | <el-table-column label="ç§å®¤" align="center" key="deptname" prop="deptname" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount" /> |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" /> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" /> |
| | | |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUpAgain" prop="needFollowUpAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'needFollowUpAgainInfo', '忬¡é访éé访å表')"> |
| | | <span class="button-zx">{{ scope.row.needFollowUpAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUpAgain" prop="pendingFollowUpAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'pendingFollowUpAgainInfo', '忬¡é访å¾
é访å表')"> |
| | | <span class="button-zx">{{ scope.row.pendingFollowUpAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccessAgain" prop="followUpSuccessAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpSuccessAgainInfo', '忬¡é访é访æåå表')"> |
| | | <span class="button-zx">{{ scope.row.followUpSuccessAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFailAgain" prop="followUpFailAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpFailAgainInfo', '忬¡é访é访失败å表')"> |
| | | <span class="button-zx">{{ scope.row.followUpFailAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRateAgain" prop="followUpRateAgain" /> |
| | | <el-table-column label="人工" align="center" key="manualAgain" prop="manualAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'manualAgainInfo', '忬¡é访人工é访å表')"> |
| | | <span class="button-zx">{{ scope.row.manualAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="smsAgain" prop="smsAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'smsAgainInfo', '忬¡é访çä¿¡é访å表')"> |
| | | <span class="button-zx">{{ scope.row.smsAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChatAgain" prop="weChatAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'weChatAgainInfo', '忬¡é访微信é访å表')"> |
| | | <span class="button-zx">{{ scope.row.weChatAgain }}</span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getSfStatistics } from "@/api/system/user"; |
| | | import ExcelJS from "exceljs"; |
| | | import { saveAs } from "file-saver"; |
| | | |
| | | export default { |
| | | name: 'SecondFollowUp', |
| | | props: { |
| | | queryParams: { |
| | | type: Object, |
| | | required: true |
| | | }, |
| | | flatArrayhospit: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | flatArraydept: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | options: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | orgname: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | tableData: [], |
| | | loading: false, |
| | | expands: [], |
| | | ids: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | | this.loading = true |
| | | const params = { |
| | | ...this.queryParams, |
| | | visitCount: 2, |
| | | leavehospitaldistrictcodes: this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.getAllWardCodes() |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.getAllDeptCodes() |
| | | : this.queryParams.deptcodes |
| | | } |
| | | |
| | | delete params.leavehospitaldistrictcodes.all |
| | | delete params.deptcodes.all |
| | | |
| | | getSfStatistics(params) |
| | | .then(response => { |
| | | this.tableData = this.customSort(response.data) |
| | | }) |
| | | .catch(error => { |
| | | console.error("è·å忬¡éè®¿æ°æ®å¤±è´¥:", error) |
| | | this.$message.error("è·å忬¡éè®¿æ°æ®å¤±è´¥") |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | |
| | | getAllWardCodes() { |
| | | return this.flatArrayhospit |
| | | .filter(item => item.value !== 'all') |
| | | .map(item => item.value) |
| | | }, |
| | | |
| | | getAllDeptCodes() { |
| | | return this.flatArraydept |
| | | .filter(item => item.value !== 'all') |
| | | .map(item => item.value) |
| | | }, |
| | | |
| | | customSort(data) { |
| | | const order = [ |
| | | "ä¸","äº","ä¸","å","äº","å
","ä¸","å
«","ä¹","å", |
| | | "åä¸","åäº","åä¸","åå","åäº","åå
","åä¸","åå
«","åä¹","äºå", |
| | | "äºåä¸","äºåäº","äºåä¸","äºåå","äºåäº","äºåå
","äºåä¸","äºåå
«","äºåä¹","ä¸å", |
| | | "ä¸åä¸","ä¸åäº","ä¸åä¸","ä¸åå","ä¸åäº","ä¸åå
","ä¸åä¸","ä¸åå
«","ä¸åä¹","åå", |
| | | "ååä¸","ååäº","ååä¸","ååå","ååäº" |
| | | ] |
| | | |
| | | return data.sort((a, b) => { |
| | | const getIndex = (name) => { |
| | | if (!name || typeof name !== "string") return -1 |
| | | const chineseMatch = name.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/) |
| | | if (chineseMatch && chineseMatch[1]) { |
| | | return order.indexOf(chineseMatch[1]) |
| | | } |
| | | const arabicMatch = name.match(/^(\d+)/) |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10) |
| | | if (num >= 1 && num <= 45) { |
| | | return num - 1 |
| | | } |
| | | } |
| | | return -1 |
| | | } |
| | | |
| | | const indexA = getIndex(a.leavehospitaldistrictname) |
| | | const indexB = getIndex(b.leavehospitaldistrictname) |
| | | |
| | | if (indexA === -1 && indexB === -1) { |
| | | return (a.leavehospitaldistrictname || "").localeCompare(b.leavehospitaldistrictname || "") |
| | | } |
| | | if (indexA === -1) return 1 |
| | | if (indexB === -1) return -1 |
| | | return indexA - indexB |
| | | }) |
| | | }, |
| | | |
| | | sortChineseNumber(aRow, bRow) { |
| | | const a = aRow.leavehospitaldistrictname |
| | | const b = bRow.leavehospitaldistrictname |
| | | |
| | | const chineseNumMap = { |
| | | ä¸:1,äº:2,ä¸:3,å:4,äº:5,å
:6,ä¸:7,å
«:8,ä¹:9,å:10, |
| | | åä¸:11,åäº:12,åä¸:13,åå:14,åäº:15,åå
:16,åä¸:17,åå
«:18,åä¹:19,äºå:20, |
| | | äºåä¸:21,äºåäº:22,äºåä¸:23,äºåå:24,äºåäº:25,äºåå
:26,äºåä¸:27,äºåå
«:28,äºåä¹:29,ä¸å:30, |
| | | ä¸åä¸:31,ä¸åäº:32,ä¸åä¸:33,ä¸åå:34,ä¸åäº:35,ä¸åå
:36,ä¸åä¸:37,ä¸åå
«:38,ä¸åä¹:39,åå:40, |
| | | ååä¸:41,ååäº:42,ååä¸:43,ååå:44,ååäº:45 |
| | | } |
| | | |
| | | const getNumberFromText = (text) => { |
| | | if (!text || typeof text !== "string") return -1 |
| | | const match = text.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/) |
| | | if (match && match[1]) { |
| | | const chineseNum = match[1] |
| | | return chineseNumMap[chineseNum] !== undefined ? chineseNumMap[chineseNum] : -1 |
| | | } |
| | | const arabicMatch = text.match(/^(\d+)/) |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10) |
| | | return num >= 1 && num <= 45 ? num : -1 |
| | | } |
| | | return -1 |
| | | } |
| | | |
| | | const numA = getNumberFromText(a) |
| | | const numB = getNumberFromText(b) |
| | | |
| | | if (numA === -1 && numB === -1) { |
| | | return (a || "").localeCompare(b || "") |
| | | } |
| | | if (numA === -1) return 1 |
| | | if (numB === -1) return -1 |
| | | return numA - numB |
| | | }, |
| | | |
| | | getRowKey(row) { |
| | | return row.statisticaltype === 1 ? row.leavehospitaldistrictcode : row.deptcode |
| | | }, |
| | | |
| | | handleRowClick(row) { |
| | | if (this.expands.includes(this.getRowKey(row))) { |
| | | this.expands = [] |
| | | return |
| | | } |
| | | |
| | | const params = { |
| | | ...this.queryParams, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.getAllDeptCodes() |
| | | : this.queryParams.deptcodes, |
| | | leavehospitaldistrictcodes: [row.leavehospitaldistrictcode], |
| | | drcode: "1", |
| | | visitCount: 2 |
| | | } |
| | | |
| | | delete params.leavehospitaldistrictcodes.all |
| | | delete params.deptcodes.all |
| | | |
| | | if (!row.doctorStats) { |
| | | this.loading = true |
| | | getSfStatistics(params).then((res) => { |
| | | this.$set(row, "doctorStats", res.data) |
| | | this.expands = [this.getRowKey(row)] |
| | | this.loading = false |
| | | }) |
| | | } else { |
| | | this.expands = [this.getRowKey(row)] |
| | | } |
| | | }, |
| | | |
| | | getSummaries(param) { |
| | | const { columns, data } = param |
| | | const sums = [] |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计" |
| | | return |
| | | } |
| | | if (index === 1 || index === 2) { |
| | | sums[index] = "/" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "followUpRateAgain") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property] |
| | | if (!value || value === "-" || value === "0%") return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100 |
| | | return isNaN(numValue) ? null : numValue |
| | | } else { |
| | | const numValue = parseFloat(value) |
| | | return isNaN(numValue) ? null : numValue |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0) |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length |
| | | sums[index] = (average * 100).toFixed(2) + "%" |
| | | } else { |
| | | sums[index] = "0.00%" |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property] |
| | | if (value === "-" || value === "" || value === null) return 0 |
| | | return Number(value) || 0 |
| | | }) |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0) |
| | | sums[index] = this.formatNumber(sums[index]) |
| | | } else { |
| | | sums[index] = "-" |
| | | } |
| | | } |
| | | }) |
| | | |
| | | return sums |
| | | }, |
| | | |
| | | getInnerSummaries(param) { |
| | | const { columns, data } = param |
| | | const sums = [] |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å°è®¡" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "drname" || column.property === "deptname") { |
| | | sums[index] = "-" |
| | | return |
| | | } |
| | | |
| | | if (column.property === "followUpRateAgain") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property] |
| | | if (!value || value === "-" || value === "0%") return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100 |
| | | return isNaN(numValue) ? null : numValue |
| | | } else { |
| | | const numValue = parseFloat(value) |
| | | return isNaN(numValue) ? null : numValue |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0) |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length |
| | | sums[index] = (average * 100).toFixed(2) + "%" |
| | | } else { |
| | | sums[index] = "0.00%" |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property] |
| | | if (value === "-" || value === "" || value === null) return 0 |
| | | return Number(value) || 0 |
| | | }) |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0) |
| | | sums[index] = this.formatNumber(sums[index]) |
| | | } else { |
| | | sums[index] = "-" |
| | | } |
| | | } |
| | | }) |
| | | |
| | | return sums |
| | | }, |
| | | |
| | | formatNumber(num) { |
| | | if (isNaN(num)) return "-" |
| | | return Number.isInteger(num) ? num.toString() : num.toFixed(0) |
| | | }, |
| | | |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map((item) => item.tagid) |
| | | }, |
| | | |
| | | handleViewDetails(row, infoKey, titleSuffix) { |
| | | const title = `${row.leavehospitaldistrictname || row.deptname}${titleSuffix}` |
| | | this.$emit('view-details', row[infoKey], title) |
| | | }, |
| | | |
| | | async exportTable() { |
| | | try { |
| | | let dateRangeString = "" |
| | | let sheetNameSuffix = "" |
| | | |
| | | if (this.queryParams.dateRange && this.queryParams.dateRange.length === 2) { |
| | | const startDateStr = this.queryParams.dateRange[0] |
| | | const endDateStr = this.queryParams.dateRange[1] |
| | | const formatDateForDisplay = (dateTimeStr) => { |
| | | return dateTimeStr.split(" ")[0] |
| | | } |
| | | const startDateFormatted = formatDateForDisplay(startDateStr) |
| | | const endDateFormatted = formatDateForDisplay(endDateStr) |
| | | dateRangeString = `${startDateFormatted}è³${endDateFormatted}` |
| | | sheetNameSuffix = `${startDateFormatted}è³${endDateFormatted}` |
| | | } else { |
| | | const now = new Date() |
| | | const currentMonth = now.getMonth() + 1 |
| | | dateRangeString = `${currentMonth}æ` |
| | | sheetNameSuffix = `${currentMonth}æ` |
| | | } |
| | | |
| | | const excelName = `忬¡åºé¢é访ç»è®¡è¡¨_${dateRangeString}.xlsx` |
| | | const worksheetName = `忬¡é访ç»è®¡_${sheetNameSuffix}` |
| | | |
| | | if (!this.tableData || this.tableData.length === 0) { |
| | | this.$message.warning("ææ åæ¬¡éè®¿æ°æ®å¯å¯¼åº") |
| | | return false |
| | | } |
| | | |
| | | const workbook = new ExcelJS.Workbook() |
| | | const worksheet = workbook.addWorksheet(worksheetName) |
| | | |
| | | // æå»ºè¡¨æ ¼ |
| | | this.buildExportSheet(worksheet, sheetNameSuffix) |
| | | |
| | | const buffer = await workbook.xlsx.writeBuffer() |
| | | const blob = new Blob([buffer], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" |
| | | }) |
| | | saveAs(blob, excelName) |
| | | |
| | | this.$message.success("å¯¼åºæå") |
| | | return true |
| | | } catch (error) { |
| | | console.error("导åºå¤±è´¥:", error) |
| | | this.$message.error(`导åºå¤±è´¥: ${error.message}`) |
| | | return false |
| | | } |
| | | }, |
| | | |
| | | buildExportSheet(worksheet, sheetNameSuffix) { |
| | | const titleStyle = { |
| | | font: { name: "微软é
é»", size: 16, bold: true, color: { argb: "FF000000" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFE6F3FF" } }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const headerStyle = { |
| | | font: { name: "微软é
é»", size: 11, bold: true, color: { argb: "FF000000" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const cellStyle = { |
| | | font: { name: "å®ä½", size: 10, color: { argb: "FF000000" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | const summaryStyle = { |
| | | font: { name: "å®ä½", size: 10, bold: true, color: { argb: "FF409EFF" } }, |
| | | fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } } |
| | | } |
| | | } |
| | | |
| | | // æ·»å æ é¢è¡ |
| | | worksheet.mergeCells(1, 1, 1, 15) |
| | | const titleCell = worksheet.getCell(1, 1) |
| | | titleCell.value = `忬¡åºé¢é访ç»è®¡è¡¨_${sheetNameSuffix}` |
| | | titleCell.style = titleStyle |
| | | worksheet.getRow(1).height = 35 |
| | | |
| | | // 表头 |
| | | const secondRowHeaders = [ |
| | | "", "åºé¢ç
åº", "ç§å®¤", "åºé¢äººæ¬¡", "æ éé访人次", "åºé访人次", |
| | | "éé访", "å¾
é访", "é访æå", "é访失败", "é访ç", "人工", "çä¿¡", "微信" |
| | | ] |
| | | |
| | | secondRowHeaders.forEach((header, index) => { |
| | | const cell = worksheet.getCell(3, index + 1) |
| | | cell.value = header |
| | | cell.style = headerStyle |
| | | }) |
| | | |
| | | // åå¹¶åå
æ ¼ |
| | | for (let i = 1; i <= 6; i++) { |
| | | worksheet.mergeCells(2, i, 3, i) |
| | | const cell = worksheet.getCell(2, i) |
| | | cell.style = headerStyle |
| | | } |
| | | |
| | | worksheet.getCell(2, 1).value = "" |
| | | worksheet.getCell(2, 2).value = "åºé¢ç
åº" |
| | | worksheet.getCell(2, 3).value = "ç§å®¤" |
| | | worksheet.getCell(2, 4).value = "åºé¢äººæ¬¡" |
| | | worksheet.getCell(2, 5).value = "æ éé访人次" |
| | | worksheet.getCell(2, 6).value = "åºé访人次" |
| | | |
| | | worksheet.mergeCells(2, 7, 2, 14) |
| | | worksheet.getCell(2, 7).value = "忬¡åºé¢é访" |
| | | worksheet.getCell(2, 7).style = headerStyle |
| | | |
| | | worksheet.getRow(2).height = 28 |
| | | worksheet.getRow(3).height = 25 |
| | | |
| | | // æ°æ®è¡ |
| | | this.tableData.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow([ |
| | | "", |
| | | item.leavehospitaldistrictname || "", |
| | | item.deptname || "", |
| | | item.dischargeCount || 0, |
| | | item.nonFollowUp || 0, |
| | | item.followUpNeeded || 0, |
| | | item.needFollowUpAgain || 0, |
| | | item.pendingFollowUpAgain || 0, |
| | | item.followUpSuccessAgain || 0, |
| | | item.followUpFailAgain || 0, |
| | | item.followUpRateAgain || "0%", |
| | | item.manualAgain || 0, |
| | | item.smsAgain || 0, |
| | | item.weChatAgain || 0 |
| | | ], rowIndex + 4) |
| | | |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle |
| | | }) |
| | | dataRow.height = 24 |
| | | }) |
| | | |
| | | // åè®¡è¡ |
| | | const summaries = this.getExportSummaries() |
| | | const summaryRow = worksheet.addRow(summaries) |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle |
| | | if (colNumber === 1) { |
| | | cell.value = "å计" |
| | | } |
| | | }) |
| | | summaryRow.height = 28 |
| | | |
| | | // å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, { width: 20 }, { width: 15 }, { width: 12 }, { width: 12 }, { width: 12 }, |
| | | { width: 10 }, { width: 10 }, { width: 10 }, { width: 10 }, { width: 12 }, |
| | | { width: 8 }, { width: 8 }, { width: 8 } |
| | | ] |
| | | }, |
| | | |
| | | getExportSummaries() { |
| | | const summaries = ["å计", "/", "/", 0, 0, 0, 0, 0, 0, 0, "0%", 0, 0, 0] |
| | | |
| | | this.tableData.forEach((item) => { |
| | | summaries[3] += Number(item.dischargeCount) || 0 |
| | | summaries[4] += Number(item.nonFollowUp) || 0 |
| | | summaries[5] += Number(item.followUpNeeded) || 0 |
| | | summaries[6] += Number(item.needFollowUpAgain) || 0 |
| | | summaries[7] += Number(item.pendingFollowUpAgain) || 0 |
| | | summaries[8] += Number(item.followUpSuccessAgain) || 0 |
| | | summaries[9] += Number(item.followUpFailAgain) || 0 |
| | | summaries[11] += Number(item.manualAgain) || 0 |
| | | summaries[12] += Number(item.smsAgain) || 0 |
| | | summaries[13] += Number(item.weChatAgain) || 0 |
| | | }) |
| | | |
| | | const followUpRateAgainValues = this.tableData |
| | | .map((item) => this.extractPercentageValue(item.followUpRateAgain)) |
| | | .filter((value) => value !== null) |
| | | |
| | | if (followUpRateAgainValues.length > 0) { |
| | | const avgFollowUpRateAgain = followUpRateAgainValues.reduce((sum, val) => sum + val, 0) / followUpRateAgainValues.length |
| | | summaries[10] = (avgFollowUpRateAgain * 100).toFixed(2) + "%" |
| | | } |
| | | |
| | | summaries[3] = this.formatNumber(summaries[3]) |
| | | summaries[4] = this.formatNumber(summaries[4]) |
| | | summaries[5] = this.formatNumber(summaries[5]) |
| | | summaries[6] = this.formatNumber(summaries[6]) |
| | | summaries[7] = this.formatNumber(summaries[7]) |
| | | summaries[8] = this.formatNumber(summaries[8]) |
| | | summaries[9] = this.formatNumber(summaries[9]) |
| | | summaries[11] = this.formatNumber(summaries[11]) |
| | | summaries[12] = this.formatNumber(summaries[12]) |
| | | summaries[13] = this.formatNumber(summaries[13]) |
| | | |
| | | return summaries |
| | | }, |
| | | |
| | | extractPercentageValue(value) { |
| | | if (!value) return null |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const num = parseFloat(value.replace("%", "")) |
| | | return isNaN(num) ? null : num / 100 |
| | | } |
| | | const num = parseFloat(value) |
| | | return isNaN(num) ? null : num |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .second-follow-up { |
| | | .your-table-container { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog |
| | | title="æªåæ¶é访æ£è
æå¡" |
| | | :visible.sync="visible" |
| | | v-loading="loading" |
| | | width="70%" |
| | | :close-on-click-modal="false" |
| | | @close="handleClose" |
| | | > |
| | | <div class="timely-rate-dialog"> |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <!-- æç´¢è¡¨å --> |
| | | <el-form |
| | | :model="queryParams" |
| | | ref="queryForm" |
| | | size="small" |
| | | :inline="true" |
| | | label-width="98px" |
| | | class="search-form" |
| | | > |
| | | <el-form-item label="æ£è
ï¼"> |
| | | <el-input |
| | | v-model="queryParams.name" |
| | | placeholder="请è¾å
¥æ£è
å§å" |
| | | @keyup.enter.native="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="æ£è
è¯æï¼"> |
| | | <el-input |
| | | v-model="queryParams.leavediagname" |
| | | placeholder="请è¾å
¥æ£è
è¯æ" |
| | | @keyup.enter.native="handleSearch" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-search" |
| | | size="medium" |
| | | @click="handleSearch" |
| | | > |
| | | æç´¢ |
| | | </el-button> |
| | | <el-button |
| | | icon="el-icon-refresh" |
| | | size="medium" |
| | | @click="resetQuery" |
| | | > |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- æ£è
å表 --> |
| | | <el-table :data="data" style="width: 100%" v-loading="loading"> |
| | | <el-table-column prop="sendname" align="center" label="å§å" width="100" /> |
| | | <el-table-column prop="taskName" align="center" width="200" show-overflow-tooltip label="ä»»å¡åç§°" /> |
| | | |
| | | <el-table-column prop="sendstate" align="center" width="200" label="ä»»å¡ç¶æ"> |
| | | <template slot-scope="scope"> |
| | | <el-tag |
| | | :type="getStateTagType(scope.row.sendstate)" |
| | | :disable-transitions="false" |
| | | > |
| | | {{ getStateText(scope.row.sendstate) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="visitTime" align="center" label="åºé访æ¶é´" width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="finishtime" align="center" label="éè®¿å®ææ¶é´" width="200" show-overflow-tooltip /> |
| | | |
| | | <el-table-column label="åºé¢æ¥æ" width="200" align="center" key="endtime" prop="endtime"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="责任æ¤å£«" width="120" align="center" key="nurseName" prop="nurseName" /> |
| | | <el-table-column label="主治å»ç" width="120" align="center" key="drname" prop="drname" /> |
| | | |
| | | <el-table-column label="ç»æç¶æ" align="center" key="excep" prop="excep" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_yujing" :value="scope.row.excep" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="å¤çæè§" align="center" key="suggest" prop="suggest" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_suggest" :value="scope.row.suggest" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="templatename" align="center" label="æå¡æ¨¡æ¿" width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="remark" align="center" label="æå¡è®°å½" width="200" show-overflow-tooltip /> |
| | | |
| | | <el-table-column prop="bankcardno" align="center" label="å¼å«ç¶æ" width="210" /> |
| | | |
| | | <el-table-column label="æä½" fixed="right" align="center" width="200" class-name="small-padding fixed-width"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="handleDetailsGo(scope.row)"> |
| | | <span class="button-zx"> |
| | | <i class="el-icon-s-order"></i>æ¥ç |
| | | </span> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-row> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pn" |
| | | :limit.sync="queryParams.ps" |
| | | @pagination="handlePagination" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import Pagination from '@/components/Pagination' |
| | | |
| | | export default { |
| | | name: 'TimelyRateDialog', |
| | | components: { |
| | | Pagination |
| | | }, |
| | | dicts: ['sys_yujing', 'sys_suggest'], |
| | | props: { |
| | | visible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | data: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | total: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | queryParams: { |
| | | type: Object, |
| | | default: () => ({ |
| | | pn: 1, |
| | | ps: 10 |
| | | }) |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | localQueryParams: { ...this.queryParams } |
| | | } |
| | | }, |
| | | watch: { |
| | | queryParams: { |
| | | deep: true, |
| | | handler(newParams) { |
| | | this.localQueryParams = { ...newParams } |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.localQueryParams = { ...this.queryParams } |
| | | }, |
| | | methods: { |
| | | handleSearch() { |
| | | this.$emit('search', this.localQueryParams) |
| | | }, |
| | | |
| | | resetQuery() { |
| | | this.localQueryParams = { |
| | | pn: 1, |
| | | ps: 10, |
| | | name: '', |
| | | leavediagname: '' |
| | | } |
| | | this.$emit('search', this.localQueryParams) |
| | | }, |
| | | |
| | | handlePagination(pagination) { |
| | | this.localQueryParams.pn = pagination.page |
| | | this.localQueryParams.ps = pagination.limit |
| | | this.$emit('search', this.localQueryParams) |
| | | }, |
| | | |
| | | getStateTagType(state) { |
| | | const stateMap = { |
| | | 1: 'primary', |
| | | 2: 'primary', |
| | | 3: 'success', |
| | | 4: 'info', |
| | | 5: 'danger', |
| | | 6: 'success' |
| | | } |
| | | return stateMap[state] || 'info' |
| | | }, |
| | | |
| | | getStateText(state) { |
| | | const stateTextMap = { |
| | | 1: '表åå·²é¢å', |
| | | 2: 'å¾
é访', |
| | | 3: '表åå·²åé', |
| | | 4: '䏿§è¡', |
| | | 5: 'åé失败', |
| | | 6: '已宿' |
| | | } |
| | | return stateTextMap[state] || 'æªç¥ç¶æ' |
| | | }, |
| | | |
| | | formatTime(time) { |
| | | if (!time) return '' |
| | | return this.parseTime(time) |
| | | }, |
| | | |
| | | handleDetailsGo(row) { |
| | | this.$emit('details-go', row) |
| | | }, |
| | | |
| | | handleClose() { |
| | | this.$emit('close') |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .timely-rate-dialog { |
| | | .search-form { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // å
¨å±æ ·å¼ |
| | | .follow-up-statistics, |
| | | .first-follow-up, |
| | | .second-follow-up, |
| | | .continued-care { |
| | | .your-table-container { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .button-zx { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | |
| | | // ç¾ååè®¡è¡æ ·å¼ |
| | | ::v-deep .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | |
| | | .cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å
é¨è¡¨æ ¼åè®¡è¡æ ·å¼ |
| | | ::v-deep .inner-table .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #ecf5ff; |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | |
| | | .cell { |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ç¾åæ¯åæ®µç¹æ®æ ·å¼ |
| | | ::v-deep .el-table__footer .el-table__cell[data-field="followUpRate"] .cell, |
| | | ::v-deep .el-table__footer .el-table__cell[data-field="rate"] .cell, |
| | | ::v-deep .el-table__footer .el-table__cell[data-field="followUpRateAgain"] .cell, |
| | | ::v-deep .el-table__footer .el-table__cell[data-field="completionRate"] .cell { |
| | | color: #e6a23c !important; |
| | | font-weight: 700 !important; |
| | | } |
| | | |
| | | // å
å±å»çè¡¨æ ¼æ ·å¼ |
| | | .inner-table { |
| | | ::v-deep .el-table__header-wrapper { |
| | | background-color: #f0f7ff !important; |
| | | |
| | | th { |
| | | background-color: #f0f7ff !important; |
| | | } |
| | | } |
| | | |
| | | ::v-deep .el-table__body-wrapper { |
| | | tr { |
| | | background-color: #f9fbfe !important; |
| | | |
| | | &:hover { |
| | | background-color: #e6f1ff !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | ::v-deep .el-table--border { |
| | | border-color: #d9e8ff !important; |
| | | |
| | | td, th { |
| | | border-color: #d9e8ff !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* ä½¿è¡ææåæé */ |
| | | ::v-deep .el-table__row { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | /* å±å¼è¡æ ·å¼ */ |
| | | ::v-deep .el-table__expanded-cell { |
| | | padding: 10px 0 !important; |
| | | background: #f8f8f8; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="Questionnairemanagement"> |
| | | <div class="leftvlue"> |
| | | <div class="leftvlue-bg"> |
| | | <el-row :gutter="20"> |
| | | <!--æ ç¾æ°æ®--> |
| | | <el-col :span="24" :xs="24"> |
| | | <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" |
| | | label-width="98px"> |
| | | <el-form-item label="ç»è®¡ç±»å" prop="userName"> |
| | | <el-select v-model="queryParams.statisticaltype" placeholder="è¯·éæ©ç»è®¡ç±»å"> |
| | | <el-option v-for="item in Statisticallist" :key="item.value" :label="item.label" :value="item.value"> |
| | | </el-option> |
| | | </el-select> |
| | | <el-select style="margin-left: 10px" v-if="queryParams.statisticaltype == 1" |
| | | v-model="queryParams.leavehospitaldistrictcodes" size="medium" multiple filterable |
| | | placeholder="è¯·éæ©ç
åº"> |
| | | <el-option v-for="item in flatArrayhospit" :key="item.value" :label="item.label" :value="item.value"> |
| | | </el-option> |
| | | </el-select> |
| | | <el-select v-else-if="queryParams.statisticaltype == 2" v-model="queryParams.deptcodes" size="medium" |
| | | multiple filterable placeholder="è¯·éæ©ç§å®¤"> |
| | | <el-option v-for="item in flatArraydept" :key="item.value" :label="item.label" :value="item.value"> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="æå¡ç±»å" prop="userName"> |
| | | <el-select v-model="queryParams.serviceType" multiple placeholder="è¯·éæ©"> |
| | | <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label-width="200" label="åºé访æ¶é´èå´" prop="userName"> |
| | | <el-date-picker v-model="queryParams.dateRange" value-format="yyyy-MM-dd HH:mm:ss" type="daterange" |
| | | range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" |
| | | :default-time="['00:00:00', '23:59:59']"> |
| | | </el-date-picker> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="medium" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" size="medium" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | <el-button type="warning" plain icon="el-icon-download" size="medium" @click="exportTable">导åº</el-button> |
| | | <el-button type="primary" plain icon="el-icon-data-line" size="medium" |
| | | @click="showChartDialog">ç»è®¡è¶å¿å¾</el-button> |
| | | </el-form> |
| | | |
| | | <!-- æ°å¢ï¼Tabæ ç¾é¡µ --> |
| | | <el-tabs v-model="activeTab" @tab-click="handleTabClick"> |
| | | <el-tab-pane label="馿¬¡é访" name="first"> |
| | | <div class="your-table-container"> |
| | | <el-table ref="exportTable" id="exportTableid" v-loading="loading" :data="firstFollowUpList" |
| | | :border="true" @selection-change="handleSelectionChange" @expand-change="handleRowClick" |
| | | :row-key="getRowKey" show-summary :summary-method="getSummaries" :expand-row-keys="expands"> |
| | | <!-- å±å¼è¡ç®å¤´å --> |
| | | <el-table-column type="expand"> |
| | | <template slot-scope="props"> |
| | | <el-table :data="props.row.doctorStats" border style="width: 95%; margin: 0 auto" |
| | | class="inner-table" show-summary :summary-method="getInnerSummaries"> |
| | | <el-table-column label="å»çå§å" prop="drname" align="center" /> |
| | | <el-table-column label="ç§å®¤" width="120" prop="deptname" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" prop="dischargeCount" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount"> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" |
| | | prop="nonFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" |
| | | prop="followUpNeeded"> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUp" prop="needFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUp" prop="pendingFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccess" prop="followUpSuccess"> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFail" prop="followUpFail"> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRate" |
| | | prop="followUpRate"> |
| | | </el-table-column> |
| | | <el-table-column v-if="orgname != '丽水å¸ä¸å»é¢'" label="åæ¶ç" align="center" width="120" |
| | | key="rate" prop="rate"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="Seedetails(scope.row)"><span |
| | | class="button-zx">{{ |
| | | (Number(scope.row.rate) * 100).toFixed(2) |
| | | }}%</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manual" prop="manual"> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="sms" prop="sms"> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChat" prop="weChat"> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºé¢ç
åº" align="center" sortable key="leavehospitaldistrictname" |
| | | prop="leavehospitaldistrictname" width="150" :show-overflow-tooltip="true" |
| | | :sort-method="sortChineseNumber" /> |
| | | <el-table-column label="ç§å®¤" align="center" key="deptname" prop="deptname" |
| | | :show-overflow-tooltip="true" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount"> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" |
| | | prop="followUpNeeded"> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUp" prop="needFollowUp"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.needFollowUpInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | 'éé访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.needFollowUp |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUp" prop="pendingFollowUp"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.pendingFollowUpInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | 'å¾
é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.pendingFollowUp |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccess" prop="followUpSuccess"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.followUpSuccessInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | 'é访æåå表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.followUpSuccess |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFail" prop="followUpFail"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.followUpFailInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | 'é访失败å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.followUpFail |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRate" prop="followUpRate"> |
| | | </el-table-column> |
| | | <el-table-column v-if="orgname != '丽水å¸ä¸å»é¢'" label="åæ¶ç" align="center" width="120" key="rate" |
| | | prop="rate"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="Seedetails(scope.row)"><span class="button-zx">{{ |
| | | (Number(scope.row.rate) * 100).toFixed(2) |
| | | }}%</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manual" prop="manual"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.manualInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '人工é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.manual |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="sms" prop="sms"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.smsInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | 'çä¿¡é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.sms |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChat" prop="weChat"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.weChatInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '微信é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.weChat |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | |
| | | <!-- é访æ
åµåï¼ä»
丽水å¸ä¸å»é¢æ¾ç¤ºï¼ --> |
| | | <el-table-column v-if="orgname == '丽水å¸ä¸å»é¢'" align="center" label="é访æ
åµ"> |
| | | <el-table-column label="æ£å¸¸è¯é³" align="center" width="100" key="taskSituation1" |
| | | prop="taskSituation1"> |
| | | </el-table-column><el-table-column label="æ£è
ææ¥ææè®¿" align="center" width="100" key="taskSituation2" |
| | | prop="taskSituation2"> |
| | | </el-table-column><el-table-column label="é¢è®¿æè
æ¥è¯" align="center" width="100" key="taskSituation3" |
| | | prop="taskSituation3"> |
| | | </el-table-column><el-table-column label="微信é访" align="center" width="100" key="taskSituation4" |
| | | prop="taskSituation4"> |
| | | </el-table-column><el-table-column label="é访çµè¯ä¸æ£ç¡®" align="center" width="100" key="taskSituation5" |
| | | prop="taskSituation5"> |
| | | </el-table-column><el-table-column label="å
¶ä»æ
åµä¸å®é访" align="center" width="100" |
| | | key="taskSituation6" prop="taskSituation6"> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="忬¡é访" name="second"> |
| | | <div class="your-table-container"> |
| | | <el-table ref="exportTableSecond" id="exportTableidSecond" v-loading="loadingSecond" |
| | | :data="secondFollowUpList" :border="true" @selection-change="handleSelectionChangeSecond" |
| | | @expand-change="handleRowClickSecond" :row-key="getRowKey" show-summary |
| | | :summary-method="getSummariesSecond" :expand-row-keys="expandsSecond"> |
| | | <!-- å±å¼è¡ç®å¤´å --> |
| | | <el-table-column type="expand"> |
| | | <template slot-scope="props"> |
| | | <el-table :data="props.row.doctorStats" border style="width: 95%; margin: 0 auto" |
| | | class="inner-table" show-summary :summary-method="getInnerSummariesSecond"> |
| | | <el-table-column label="å»çå§å" prop="drname" align="center" /> |
| | | <el-table-column label="ç§å®¤" width="120" prop="deptname" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" prop="dischargeCount" align="center" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount"> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" |
| | | prop="nonFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" |
| | | prop="followUpNeeded"> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUpAgain" |
| | | prop="needFollowUpAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUpAgain" |
| | | prop="pendingFollowUpAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccessAgain" |
| | | prop="followUpSuccessAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFailAgain" |
| | | prop="followUpFailAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRateAgain" |
| | | prop="followUpRateAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manualAgain" prop="manualAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="smsAgain" prop="smsAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChatAgain" prop="weChatAgain"> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºé¢ç
åº" align="center" sortable key="leavehospitaldistrictname" |
| | | prop="leavehospitaldistrictname" width="150" :show-overflow-tooltip="true" |
| | | :sort-method="sortChineseNumber" /> |
| | | <el-table-column label="ç§å®¤" align="center" key="deptname" prop="deptname" |
| | | :show-overflow-tooltip="true" /> |
| | | <el-table-column label="åºé¢äººæ¬¡" align="center" key="dischargeCount" prop="dischargeCount"> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="æ éé访人次" align="center" width="100" key="nonFollowUp" prop="nonFollowUp"> |
| | | </el-table-column> |
| | | <el-table-column label="åºé访人次" align="center" width="100" key="followUpNeeded" |
| | | prop="followUpNeeded"> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column label="éé访" align="center" key="needFollowUpAgain" prop="needFollowUpAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.needFollowUpAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访éé访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.needFollowUpAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¾
é访" align="center" key="pendingFollowUpAgain" |
| | | prop="pendingFollowUpAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.pendingFollowUpAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访å¾
é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.pendingFollowUpAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访æå" align="center" key="followUpSuccessAgain" |
| | | prop="followUpSuccessAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.followUpSuccessAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访é访æåå表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.followUpSuccessAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访失败" align="center" key="followUpFailAgain" prop="followUpFailAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.followUpFailAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访é访失败å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.followUpFailAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é访ç" align="center" width="120" key="followUpRateAgain" |
| | | prop="followUpRateAgain"> |
| | | </el-table-column> |
| | | <el-table-column label="人工" align="center" key="manualAgain" prop="manualAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.manualAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访人工é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.manualAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çä¿¡" align="center" key="smsAgain" prop="smsAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.smsAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访çä¿¡é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.smsAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="微信" align="center" key="weChatAgain" prop="weChatAgain"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click=" |
| | | viewDetails( |
| | | scope.row.weChatAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访微信é访å表' |
| | | ) |
| | | "><span class="button-zx">{{ |
| | | scope.row.weChatAgain |
| | | }}</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | <!-- ç»è®¡è¶å¿å¾å¼¹çª --> |
| | | <el-dialog title="é访ç»è®¡è¶å¿å¾" :visible.sync="chartDialogVisible" width="80%" :close-on-click-modal="false"> |
| | | <div class="chart-container"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">éè®¿ç¶æåå¸</div> |
| | | <div id="pieChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">é访è¶å¿åæ</div> |
| | | <div id="barLineChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-dialog> |
| | | <el-dialog title="æªåæ¶é访æ£è
æå¡" :visible.sync="SeedetailsVisible" v-loading="Seedloading" width="70%" |
| | | :close-on-click-modal="false"> |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <!--ç¨æ·æ°æ®--> |
| | | <el-form :model="patientqueryParams" ref="queryForm" size="small" :inline="true" label-width="98px"> |
| | | <el-form-item label="æ£è
ï¼"> |
| | | <el-input v-model="patientqueryParams.name" @keyup.enter.native="handleQuery"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æ£è
è¯æï¼"> |
| | | <el-input v-model="patientqueryParams.leavediagname" @keyup.enter.native="handleQuery"></el-input> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="medium" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" size="medium" @click="resetQuery">åæ¶å建</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- éæ©æ£è
å表 --> |
| | | <el-table :data="logsheetlist" style="width: 100%"> |
| | | <el-table-column prop="sendname" align="center" label="å§å" width="100"> |
| | | </el-table-column> |
| | | <el-table-column prop="taskName" align="center" width="200" show-overflow-tooltip label="ä»»å¡åç§°"> |
| | | </el-table-column> |
| | | <el-table-column prop="sendstate" align="center" width="200" label="ä»»å¡ç¶æ"> |
| | | <template slot-scope="scope"> |
| | | <div v-if="scope.row.sendstate == 1"> |
| | | <el-tag type="primary" :disable-transitions="false">表åå·²é¢å</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 2"> |
| | | <el-tag type="primary" :disable-transitions="false">å¾
é访</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 3"> |
| | | <el-tag type="success" :disable-transitions="false">表åå·²åé</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 4"> |
| | | <el-tag type="info" :disable-transitions="false">䏿§è¡</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 5"> |
| | | <el-tag type="danger" :disable-transitions="false">åé失败</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <el-tag type="success" :disable-transitions="false">已宿</el-tag> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="visitTime" align="center" label="åºé访æ¶é´" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column prop="finishtime" align="center" label="éè®¿å®ææ¶é´" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column label="åºé¢æ¥æ" width="200" align="center" key="endtime" prop="endtime"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template></el-table-column> |
| | | <el-table-column label="责任æ¤å£«" width="120" align="center" key="nurseName" prop="nurseName" /> |
| | | <el-table-column label="主治å»ç" width="120" align="center" key="drname" prop="drname" /> |
| | | |
| | | <el-table-column label="ç»æç¶æ" align="center" key="excep" prop="excep" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_yujing" :value="scope.row.excep" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¤çæè§" align="center" key="suggest" prop="suggest" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_suggest" :value="scope.row.suggest" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="templatename" align="center" label="æå¡æ¨¡æ¿" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" align="center" label="æå¡è®°å½" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="bankcardno" align="center" label="å¼å«ç¶æ" width="210"> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" fixed="right" align="center" width="200" |
| | | class-name="small-padding fixed-width"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="SeedetailsgGo(scope.row)"><span class="button-zx"><i |
| | | class="el-icon-s-order"></i>æ¥ç</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-row> |
| | | <pagination v-show="patienttotal > 0 && this.patientqueryParams.allhosp != 6" :total="patienttotal" |
| | | :page.sync="patientqueryParams.pn" :limit.sync="patientqueryParams.ps" @pagination="Seedetailstion" /> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- å类详æ
--> |
| | | <el-dialog :title="infotitle" :visible.sync="infotitleVisible" v-loading="infotitloading" width="70%" |
| | | :close-on-click-modal="false"> |
| | | <div style="margin-bottom: 16px; display: flex; align-items: center"> |
| | | <span style="margin-right: 10px; font-weight: bold">æ£è
å§åæ¥è¯¢:</span> |
| | | <el-input v-model="searchName" placeholder="请è¾å
¥æ£è
å§åè¿è¡çé" clearable style="width: 300px" @input="handleSearch" |
| | | @clear="handleSearch"> |
| | | </el-input> |
| | | <span style="margin-left: 10px; color: rgb(35, 81, 233); font-size: 16px"> |
| | | å
± {{ infotitlelist.length }} æ¡è®°å½ |
| | | </span> |
| | | </div> |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <!-- éæ©æ£è
å表 --> |
| | | <div class="data-list" ref="dataList" @scroll="handleScroll" v-loading="infotitloading"> |
| | | <el-table :data="currentDisplayList" height="660" style="width: 100%"> |
| | | <el-table-column prop="sendname" align="center" label="å§å" width="100"> |
| | | </el-table-column> |
| | | <el-table-column prop="taskName" align="center" width="200" show-overflow-tooltip label="ä»»å¡åç§°"> |
| | | </el-table-column> |
| | | <el-table-column prop="sendstate" align="center" width="200" label="ä»»å¡ç¶æ"> |
| | | <template slot-scope="scope"> |
| | | <div v-if="scope.row.sendstate == 1"> |
| | | <el-tag type="primary" :disable-transitions="false">表åå·²é¢å</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 2"> |
| | | <el-tag type="primary" :disable-transitions="false">å¾
é访</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 3"> |
| | | <el-tag type="success" :disable-transitions="false">表åå·²åé</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 4"> |
| | | <el-tag type="info" :disable-transitions="false">䏿§è¡</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 5"> |
| | | <el-tag type="danger" :disable-transitions="false">åé失败</el-tag> |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <el-tag type="success" :disable-transitions="false">已宿</el-tag> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="任塿§è¡æ¹å¼" align="center" key="preachform" prop="preachform" width="160" |
| | | :show-overflow-tooltip="true"> |
| | | <template slot-scope="scope"> |
| | | <span v-for="item in scope.row.preachform">{{ item }}ã |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="visitTime" align="center" label="åºé访æ¶é´" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column prop="finishtime" align="center" label="éè®¿å®ææ¶é´" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column label="åºé¢æ¥æ" width="200" align="center" key="endtime" prop="endtime"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template></el-table-column> |
| | | <el-table-column label="责任æ¤å£«" width="120" align="center" key="nurseName" prop="nurseName" /> |
| | | <el-table-column label="主治å»ç" width="120" align="center" key="drname" prop="drname" /> |
| | | |
| | | <el-table-column label="ç»æç¶æ" align="center" key="excep" prop="excep" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_yujing" :value="scope.row.excep" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¤çæè§" align="center" key="suggest" prop="suggest" width="120"> |
| | | <template slot-scope="scope"> |
| | | <dict-tag :options="dict.type.sys_suggest" :value="scope.row.suggest" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="templatename" align="center" label="æå¡æ¨¡æ¿" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" align="center" label="æå¡è®°å½" width="200" show-overflow-tooltip> |
| | | </el-table-column> |
| | | |
| | | <el-table-column prop="bankcardno" align="center" label="å¼å«ç¶æ" width="210"> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" fixed="right" align="center" width="200" |
| | | class-name="small-padding fixed-width"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="medium" type="text" @click="SeedetailsgGo(scope.row)"><span class="button-zx"><i |
| | | class="el-icon-s-order"></i>æ¥ç</span></el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | toamendtag, |
| | | addapitag, |
| | | deletetag, |
| | | changetagcategory, |
| | | } from "@/api/system/label"; |
| | | import store from "@/store"; |
| | | import { getSfStatistics, selectTimelyRate } from "@/api/system/user"; |
| | | import * as XLSX from "xlsx"; |
| | | import FileSaver from "file-saver"; |
| | | import ExcelJS from "exceljs"; |
| | | import { saveAs } from "file-saver"; |
| | | import Treeselect from "@riophae/vue-treeselect"; |
| | | import "@riophae/vue-treeselect/dist/vue-treeselect.css"; |
| | | |
| | | const shortcuts = [ |
| | | { |
| | | text: "ä»å¤©", |
| | | onClick(picker) { |
| | | picker.$emit("pick", new Date()); |
| | | }, |
| | | }, |
| | | { |
| | | text: "æ¨å¤©", |
| | | onClick(picker) { |
| | | const date = new Date(); |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24); |
| | | picker.$emit("pick", date); |
| | | }, |
| | | }, |
| | | { |
| | | text: "ä¸å¨å", |
| | | onClick(picker) { |
| | | const date = new Date(); |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24 * 7); |
| | | picker.$emit("pick", date); |
| | | }, |
| | | }, |
| | | ]; |
| | | |
| | | export default { |
| | | name: "Percentage", |
| | | dicts: ["sys_normal_disable", "sys_user_sex"], |
| | | components: { Treeselect }, |
| | | data() { |
| | | return { |
| | | // æ°å¢ï¼Tabæ ç¾é¡µæ§å¶ |
| | | activeTab: "first", // å½åæ¿æ´»çtabï¼first-馿¬¡é访ï¼second-忬¡é访 |
| | | |
| | | // åç¦»çæ°æ®å表 |
| | | firstFollowUpList: [], // 馿¬¡éè®¿æ°æ® |
| | | secondFollowUpList: [], // 忬¡éè®¿æ°æ® |
| | | |
| | | // å离çå è½½ç¶æ |
| | | loading: false, // 馿¬¡éè®¿è¡¨æ ¼å è½½ç¶æ |
| | | loadingSecond: false, // 忬¡éè®¿è¡¨æ ¼å è½½ç¶æ |
| | | |
| | | // å离çå±å¼ç¶æ |
| | | expands: [], // 馿¬¡éè®¿è¡¨æ ¼å±å¼è¡ |
| | | expandsSecond: [], // 忬¡éè®¿è¡¨æ ¼å±å¼è¡ |
| | | |
| | | // å离çéæ©ç¶æ |
| | | ids: [], // 馿¬¡é访éä¸é¡¹ |
| | | idsSecond: [], // 忬¡é访éä¸é¡¹ |
| | | |
| | | orgname: "", |
| | | infotitlelist: [], |
| | | currentDisplayList: [], |
| | | loadIndex: 0, |
| | | pageSize: 100, |
| | | isLoading: false, |
| | | |
| | | Seedloading: false, |
| | | chartDialogVisible: false, |
| | | infotitleVisible: false, |
| | | searchName: "", |
| | | infotitloading: false, |
| | | infotitle: "", |
| | | pieChart: null, |
| | | barLineChart: null, |
| | | |
| | | single: true, |
| | | multiple: true, |
| | | showSearch: true, |
| | | idds: "", |
| | | total: 0, |
| | | flatArrayhospit: [], |
| | | flatArraydept: [], |
| | | patienttotal: 0, |
| | | logsheetlist: [], |
| | | Statisticallist: [ |
| | | { |
| | | label: "ç
åºç»è®¡", |
| | | value: 1, |
| | | }, |
| | | { |
| | | label: "ç§å®¤ç»è®¡", |
| | | value: 2, |
| | | }, |
| | | ], |
| | | patientqueryParams: { |
| | | pn: 1, |
| | | ps: 10, |
| | | }, |
| | | amendtag: false, |
| | | lstamendtag: false, |
| | | scavisible: false, |
| | | deleteVisible: false, |
| | | deletefenl: "é«è¡å", |
| | | tagform: { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | }, |
| | | classifyform: { |
| | | categoryname: "", |
| | | }, |
| | | title: "", |
| | | open: false, |
| | | dateRange: [], |
| | | postOptions: [], |
| | | roleOptions: [], |
| | | allDeptCodes: [], |
| | | allWardCodes: [], |
| | | checkboxlist: [], |
| | | form: {}, |
| | | forms: { |
| | | name: "", |
| | | }, |
| | | numberlb: 22, |
| | | dialogFormVisible: false, |
| | | lstamendtagVisible: false, |
| | | goQRCodeVisible: false, |
| | | sidecolumnval: "", |
| | | propss: { multiple: true }, |
| | | SeedetailsVisible: false, |
| | | options: store.getters.tasktypes, |
| | | pickerOptions: { |
| | | disabledDate(time) { |
| | | return time.getTime() < Date.now() - 3600 * 1000 * 24; |
| | | }, |
| | | shortcuts: shortcuts, |
| | | }, |
| | | pickerOptionsa: { |
| | | disabledDate(time) { |
| | | return time.getTime() > Date.now(); |
| | | }, |
| | | shortcuts: shortcuts, |
| | | }, |
| | | queryParams: { |
| | | serviceType: [2], |
| | | dateRange: [], |
| | | statisticaltype: 1, |
| | | leavehospitaldistrictcodes: ["all"], |
| | | deptcodes: [], |
| | | visitCount: 1, // æ°å¢ï¼é访次æ°åæ°ï¼1-馿¬¡ï¼2-忬¡ |
| | | }, |
| | | columns: [ |
| | | { key: 0, label: `æ ç¾ç¼å·`, visible: true }, |
| | | { key: 1, label: `æ ç¾åç§°`, visible: true }, |
| | | { key: 2, label: `æ ç¾æµç§°`, visible: true }, |
| | | { key: 3, label: `é¨é¨`, visible: true }, |
| | | { key: 4, label: `ææºå·ç `, visible: true }, |
| | | { key: 5, label: `ç¶æ`, visible: true }, |
| | | { key: 6, label: `å建æ¶é´`, visible: true }, |
| | | ], |
| | | }; |
| | | }, |
| | | watch: {}, |
| | | created() { |
| | | this.getDeptTree(); |
| | | this.getFirstFollowUpList(); // é»è®¤å è½½é¦æ¬¡éè®¿æ°æ® |
| | | this.checkboxlist = store.getters.checkboxlist; |
| | | this.orgname = localStorage.getItem("orgname"); |
| | | }, |
| | | |
| | | methods: { |
| | | /** æ¥è¯¢é¦æ¬¡é访å表 */ |
| | | async getFirstFollowUpList() { |
| | | this.loading = true; |
| | | this.queryParams.visitCount = 1; // 设置é访次æ°ä¸ºé¦æ¬¡ |
| | | |
| | | const params = { |
| | | ...this.queryParams, |
| | | leavehospitaldistrictcodes: |
| | | this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.allWardCodes |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | }; |
| | | |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | |
| | | try { |
| | | const response = await getSfStatistics(params); |
| | | this.firstFollowUpList = this.customSort(response.data); |
| | | this.total = response.total; |
| | | } catch (error) { |
| | | console.error("è·å馿¬¡éè®¿æ°æ®å¤±è´¥:", error); |
| | | this.$message.error("è·å馿¬¡éè®¿æ°æ®å¤±è´¥"); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | /** æ¥è¯¢å次é访å表 */ |
| | | async getSecondFollowUpList() { |
| | | this.loadingSecond = true; |
| | | this.queryParams.visitCount = 2; // 设置é访次æ°ä¸ºå次 |
| | | |
| | | const params = { |
| | | ...this.queryParams, |
| | | leavehospitaldistrictcodes: |
| | | this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.allWardCodes |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | }; |
| | | |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | |
| | | try { |
| | | const response = await getSfStatistics(params); |
| | | this.secondFollowUpList = this.customSort(response.data); |
| | | this.total = response.total; |
| | | } catch (error) { |
| | | console.error("è·å忬¡éè®¿æ°æ®å¤±è´¥:", error); |
| | | this.$message.error("è·å忬¡éè®¿æ°æ®å¤±è´¥"); |
| | | } finally { |
| | | this.loadingSecond = false; |
| | | } |
| | | }, |
| | | |
| | | /** Tab忢äºä»¶ */ |
| | | handleTabClick(tab) { |
| | | if (tab.name === "first") { |
| | | if (this.firstFollowUpList.length === 0) { |
| | | this.getFirstFollowUpList(); |
| | | } |
| | | } else if (tab.name === "second") { |
| | | if (this.secondFollowUpList.length === 0) { |
| | | this.getSecondFollowUpList(); |
| | | } |
| | | } |
| | | }, |
| | | sortChineseNumber(aRow, bRow) { |
| | | const a = aRow.leavehospitaldistrictname; |
| | | const b = bRow.leavehospitaldistrictname; |
| | | |
| | | // 䏿æ°åå°é¿æä¼¯æ°åçæ å°ï¼æ©å±å°45ï¼ |
| | | const chineseNumMap = { |
| | | ä¸: 1, |
| | | äº: 2, |
| | | ä¸: 3, |
| | | å: 4, |
| | | äº: 5, |
| | | å
: 6, |
| | | ä¸: 7, |
| | | å
«: 8, |
| | | ä¹: 9, |
| | | å: 10, |
| | | åä¸: 11, |
| | | åäº: 12, |
| | | åä¸: 13, |
| | | åå: 14, |
| | | åäº: 15, |
| | | åå
: 16, |
| | | åä¸: 17, |
| | | åå
«: 18, |
| | | åä¹: 19, |
| | | äºå: 20, |
| | | äºåä¸: 21, |
| | | äºåäº: 22, |
| | | äºåä¸: 23, |
| | | äºåå: 24, |
| | | äºåäº: 25, |
| | | äºåå
: 26, |
| | | äºåä¸: 27, |
| | | äºåå
«: 28, |
| | | äºåä¹: 29, |
| | | ä¸å: 30, |
| | | ä¸åä¸: 31, |
| | | ä¸åäº: 32, |
| | | ä¸åä¸: 33, |
| | | ä¸åå: 34, |
| | | ä¸åäº: 35, |
| | | ä¸åå
: 36, |
| | | ä¸åä¸: 37, |
| | | ä¸åå
«: 38, |
| | | ä¸åä¹: 39, |
| | | åå: 40, |
| | | ååä¸: 41, |
| | | ååäº: 42, |
| | | ååä¸: 43, |
| | | ååå: 44, |
| | | ååäº: 45, |
| | | }; |
| | | |
| | | // æå䏿æ°å |
| | | const getNumberFromText = (text) => { |
| | | if (!text || typeof text !== "string") return -1; |
| | | |
| | | // å¹é
䏿æ°åï¼æ¯æä¸å°ååäº |
| | | const match = text.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/); |
| | | |
| | | if (match && match[1]) { |
| | | const chineseNum = match[1]; |
| | | return chineseNumMap[chineseNum] !== undefined |
| | | ? chineseNumMap[chineseNum] |
| | | : -1; |
| | | } |
| | | |
| | | // å¦ææ²¡æå¹é
å°ä¸ææ°åï¼å°è¯å¹é
é¿æä¼¯æ°å |
| | | const arabicMatch = text.match(/^(\d+)/); |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10); |
| | | return num >= 1 && num <= 45 ? num : -1; |
| | | } |
| | | |
| | | return -1; |
| | | }; |
| | | |
| | | const numA = getNumberFromText(a); |
| | | const numB = getNumberFromText(b); |
| | | |
| | | // å¤çæ æ³è§£æçæ
åµ |
| | | if (numA === -1 && numB === -1) { |
| | | return (a || "").localeCompare(b || ""); |
| | | } |
| | | if (numA === -1) return 1; |
| | | if (numB === -1) return -1; |
| | | |
| | | return numA - numB; |
| | | }, |
| | | // æç´¢å¤ç彿° |
| | | handleSearch() { |
| | | if (!this.searchName.trim()) { |
| | | // 妿æç´¢æ¡ä¸ºç©ºï¼æ¾ç¤ºæææ°æ® |
| | | this.currentDisplayList = [...this.infotitlelist]; |
| | | } else { |
| | | // æ ¹æ®æ£è
å§åè¿è¡çéï¼ä¸åºå大å°åï¼ |
| | | const keyword = this.searchName.toLowerCase(); |
| | | this.currentDisplayList = this.infotitlelist.filter((item) => { |
| | | return item.sendname && item.sendname.toLowerCase().includes(keyword); |
| | | }); |
| | | } |
| | | }, |
| | | customSort(data) { |
| | | // å®ä¹æ¨ææçç
åºé¡ºåºï¼æ©å±å°ååäºï¼ |
| | | const order = [ |
| | | "ä¸", |
| | | "äº", |
| | | "ä¸", |
| | | "å", |
| | | "äº", |
| | | "å
", |
| | | "ä¸", |
| | | "å
«", |
| | | "ä¹", |
| | | "å", |
| | | "åä¸", |
| | | "åäº", |
| | | "åä¸", |
| | | "åå", |
| | | "åäº", |
| | | "åå
", |
| | | "åä¸", |
| | | "åå
«", |
| | | "åä¹", |
| | | "äºå", |
| | | "äºåä¸", |
| | | "äºåäº", |
| | | "äºåä¸", |
| | | "äºåå", |
| | | "äºåäº", |
| | | "äºåå
", |
| | | "äºåä¸", |
| | | "äºåå
«", |
| | | "äºåä¹", |
| | | "ä¸å", |
| | | "ä¸åä¸", |
| | | "ä¸åäº", |
| | | "ä¸åä¸", |
| | | "ä¸åå", |
| | | "ä¸åäº", |
| | | "ä¸åå
", |
| | | "ä¸åä¸", |
| | | "ä¸åå
«", |
| | | "ä¸åä¹", |
| | | "åå", |
| | | "ååä¸", |
| | | "ååäº", |
| | | "ååä¸", |
| | | "ååå", |
| | | "ååäº", |
| | | ]; |
| | | |
| | | return data.sort((a, b) => { |
| | | // æåç
åºåç§°ä¸ç䏿æ°åé¨å |
| | | const getIndex = (name) => { |
| | | if (!name || typeof name !== "string") return -1; |
| | | |
| | | // å¹é
䏿æ°å |
| | | const chineseMatch = name.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/); |
| | | if (chineseMatch && chineseMatch[1]) { |
| | | return order.indexOf(chineseMatch[1]); |
| | | } |
| | | |
| | | // å¹é
é¿æä¼¯æ°å |
| | | const arabicMatch = name.match(/^(\d+)/); |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10); |
| | | if (num >= 1 && num <= 45) { |
| | | return num - 1; // å 为æ°ç»ç´¢å¼ä»0å¼å§ |
| | | } |
| | | } |
| | | |
| | | return -1; |
| | | }; |
| | | |
| | | const indexA = getIndex(a.leavehospitaldistrictname); |
| | | const indexB = getIndex(b.leavehospitaldistrictname); |
| | | |
| | | // æåºé»è¾ |
| | | if (indexA === -1 && indexB === -1) { |
| | | return (a.leavehospitaldistrictname || "").localeCompare( |
| | | b.leavehospitaldistrictname || "" |
| | | ); |
| | | } |
| | | if (indexA === -1) return 1; |
| | | if (indexB === -1) return -1; |
| | | return indexA - indexB; |
| | | }); |
| | | }, |
| | | getRowKey(row) { |
| | | return row.statisticaltype === 1 |
| | | ? row.leavehospitaldistrictcode |
| | | : row.deptcode; |
| | | }, |
| | | |
| | | // å¤çè¡ç¹å»å±å¼ |
| | | handleRowClick(row) { |
| | | console.log(row, "row"); |
| | | |
| | | // 妿已ç»å±å¼åæ¶èµ· |
| | | if (this.expands.includes(this.getRowKey(row))) { |
| | | this.expands = []; |
| | | return; |
| | | } |
| | | // å¤çæ¥è¯¢åæ° |
| | | const params = { |
| | | ...this.queryParams, |
| | | // 妿鿩äº"å
¨é¨"ï¼åä¼ ææç
åº/ç§å®¤ä»£ç |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | leavehospitaldistrictcodes: [row.leavehospitaldistrictcode], |
| | | drcode: "1", |
| | | visitCount: 1, // è®¾ç½®ä¸ºé¦æ¬¡é访 |
| | | }; |
| | | |
| | | // ç§»é¤å¯è½åå¨ç"all"å¼ |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | // å¦æè¯¥è¡è¿æ²¡æå è½½å»çæ°æ®ï¼åå è½½ |
| | | if (!row.doctorStats) { |
| | | this.loading = true; |
| | | getSfStatistics(params).then((res) => { |
| | | this.$set(row, "doctorStats", res.data); |
| | | this.expands = [this.getRowKey(row)]; |
| | | this.loading = false; |
| | | }); |
| | | } else { |
| | | this.expands = [this.getRowKey(row)]; |
| | | } |
| | | }, |
| | | getSummaries(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (index === 1 || index === 2) { |
| | | sums[index] = "/"; |
| | | return; |
| | | } |
| | | |
| | | // 对ç¾åæ¯åæ®µç¹æ®å¤ç - åå¹³åå¼ |
| | | if ( |
| | | column.property === "followUpRate" || |
| | | column.property === "rate" || |
| | | column.property === "followUpRateAgain" |
| | | ) { |
| | | // æåææææç¾åæ¯å¼å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | |
| | | // å¤ç带ç¾åå·çæ°æ® |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | // å»é¤ç¾åå·å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | // å¤çå·²ç»æ¯å°æ°çæ°æ® |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); // è¿æ»¤ænullå0å¼ |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | // æ®éæ°ååæ®µ - æ±å |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | |
| | | // å
é¨è¡¨æ ¼å计è¡è®¡ç®æ¹æ³ |
| | | getInnerSummaries(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å°è®¡"; |
| | | return; |
| | | } |
| | | |
| | | if (column.property === "drname" || column.property === "deptname") { |
| | | sums[index] = "-"; |
| | | return; |
| | | } |
| | | |
| | | // 对ç¾åæ¯åæ®µç¹æ®å¤ç - åå¹³åå¼ |
| | | if (column.property === "followUpRate" || column.property === "rate") { |
| | | // æåææææç¾åæ¯å¼å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | |
| | | // å¤ç带ç¾åå·çæ°æ® |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | // å»é¤ç¾åå·å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | // å¤çå·²ç»æ¯å°æ°çæ°æ® |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | // æ®éæ°ååæ®µ - æ±å |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | |
| | | // è¾
婿¹æ³ï¼æåç¾åæ¯æ°å¼ |
| | | extractPercentageValue(value) { |
| | | if (!value) return null; |
| | | |
| | | if (typeof value === "string") { |
| | | // å¤ç带ç¾åå·çå符串 |
| | | if (value.includes("%")) { |
| | | const num = parseFloat(value.replace("%", "")); |
| | | return isNaN(num) ? null : num / 100; |
| | | } |
| | | // å¤ç纯æ°åå符串 |
| | | const num = parseFloat(value); |
| | | return isNaN(num) ? null : num; |
| | | } |
| | | |
| | | // å¤çæ°åç±»å |
| | | return typeof value === "number" ? value : null; |
| | | }, |
| | | |
| | | // æ°åæ ¼å¼åæ¹æ³ |
| | | formatNumber(num) { |
| | | if (isNaN(num)) return "-"; |
| | | return Number.isInteger(num) ? num.toString() : num.toFixed(0); |
| | | }, |
| | | /** ä¿®æ¹æ ç¾ */ |
| | | handleUpdate(row) { |
| | | console.log(row, "ä¿®æ¹æ ç¾"); |
| | | this.lstamendtagVisible = true; |
| | | this.lstamendtag = true; |
| | | this.tagform = { |
| | | isupload: row.isupload, |
| | | tagname: row.tagname, |
| | | tagcategoryid: row.tagcategoryid, |
| | | tagdescription: row.tagdescription, |
| | | tagid: row.tagid, |
| | | }; |
| | | }, |
| | | // è·åç§å®¤æ |
| | | getDeptTree() { |
| | | // ç§å®¤å表 |
| | | this.flatArraydept = store.getters.belongDepts.map((dept) => { |
| | | return { |
| | | label: dept.deptName, |
| | | value: dept.deptCode, |
| | | }; |
| | | }); |
| | | // å卿æç§å®¤ä»£ç |
| | | this.allDeptCodes = store.getters.belongDepts.map( |
| | | (dept) => dept.deptCode |
| | | ); |
| | | |
| | | // ç
åºå表 |
| | | this.flatArrayhospit = store.getters.belongWards.map((ward) => { |
| | | return { |
| | | label: ward.districtName, |
| | | value: ward.districtCode, |
| | | }; |
| | | }); |
| | | |
| | | // å卿æç
åºä»£ç |
| | | this.allWardCodes = store.getters.belongWards.map( |
| | | (ward) => ward.districtCode |
| | | ); |
| | | this.flatArraydept.push({ label: "å
¨é¨", value: "all" }); |
| | | this.flatArrayhospit.push({ label: "å
¨é¨", value: "all" }); |
| | | }, |
| | | flattenArray(multiArray) { |
| | | let result = []; |
| | | |
| | | // éå½å½æ°ï¼ç¨äºå°å¤çº§æ°ç»è½¬æ¢ä¸ºä¸ç»´æ°ç»ï¼åªå
嫿åºå±çå
ç´ |
| | | function flatten(element) { |
| | | // 妿å½åå
ç´ æåå
ç´ ï¼ç»§ç»éå½ |
| | | if (element.children && element.children.length > 0) { |
| | | element.children.forEach((child) => flatten(child)); |
| | | } else { |
| | | // å
éå
ç´ ä»¥é¿å
ä¿®æ¹åå§æ°æ® |
| | | let item = JSON.parse(JSON.stringify(element)); |
| | | result.push(item); // å°æåºå±çå
ç´ æ·»å å°ç»ææ°ç» |
| | | } |
| | | } |
| | | |
| | | // ä»é¡¶å±å
ç´ å¼å§éå½ |
| | | multiArray.forEach((element) => flatten(element)); |
| | | return result; // è¿ååªå
嫿åºå±å
ç´ çä¸ç»´æ°ç» |
| | | }, |
| | | addladeltag() { |
| | | this.lstamendtagVisible = true; |
| | | this.lstamendtag = false; |
| | | this.tagform = { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | tagid: "", |
| | | }; |
| | | }, |
| | | Seedetails(row) { |
| | | this.SeedetailsVisible = true; |
| | | this.Seedloading = true; |
| | | this.patientqueryParams.starttime = this.parseTime( |
| | | this.queryParams.dateRange[0] |
| | | ); |
| | | this.patientqueryParams.endtime = this.parseTime( |
| | | this.queryParams.dateRange[1] |
| | | ); |
| | | this.patientqueryParams.deptcode = row.deptcode; |
| | | selectTimelyRate(this.patientqueryParams).then((response) => { |
| | | this.logsheetlist = response.data.detail; |
| | | this.patienttotal = response.data.total; |
| | | this.Seedloading = false; |
| | | }); |
| | | }, |
| | | Seedetailstion() { |
| | | selectTimelyRate(this.patientqueryParams).then((response) => { |
| | | this.logsheetlist = response.data.detail; |
| | | this.patienttotal = response.data.total; |
| | | this.Seedloading = false; |
| | | }); |
| | | }, |
| | | viewDetails(row, title) { |
| | | this.infotitleVisible = true; |
| | | this.infotitle = title; |
| | | this.infotitlelist = row; // å设rowå°±æ¯éè¦å±ç¤ºçè¯¦ç»æ°ç» |
| | | console.log(this.infotitlelist, "this.infotitlelist"); |
| | | |
| | | this.infotitlelist.forEach((item) => { |
| | | let idArray = null; |
| | | |
| | | if (item.preachform) { |
| | | if (item.endtime) { |
| | | item.preachformson = item.preachform; |
| | | idArray = item.preachform.split(","); |
| | | } |
| | | |
| | | item.preachform = idArray.map((value) => { |
| | | // æ¥æ¾id对åºç对象 |
| | | const item = this.checkboxlist.find((item) => item.value == value); |
| | | // 妿æ¾å°å¯¹åºçidï¼è¿ålabelå¼ï¼å¦åè¿ånull |
| | | return item ? item.label : null; |
| | | }); |
| | | } |
| | | }); |
| | | // åå§åå è½½ |
| | | this.loadIndex = 0; |
| | | this.currentDisplayList = []; |
| | | this.$nextTick(() => { |
| | | this.loadMoreData(); |
| | | }); |
| | | }, |
| | | loadMoreData() { |
| | | if (this.isLoading) return; |
| | | this.isLoading = true; |
| | | |
| | | // 模æå¼æ¥å è½½ï¼å®é
å¯è½æ¯ç´æ¥åçæ¬å°æ°æ® |
| | | setTimeout(() => { |
| | | console.log(this.infotitlelist, "this.infotitlelist"); |
| | | |
| | | const nextChunk = this.infotitlelist.slice( |
| | | this.loadIndex, |
| | | this.loadIndex + this.pageSize |
| | | ); |
| | | this.currentDisplayList = this.currentDisplayList.concat(nextChunk); |
| | | this.loadIndex += this.pageSize; |
| | | this.isLoading = false; |
| | | }, 200); |
| | | }, |
| | | handleScroll(event) { |
| | | const scrollContainer = event.target; |
| | | // 夿æ¯å¦æ»å¨å°åºé¨ |
| | | const isAtBottom = |
| | | scrollContainer.scrollTop + scrollContainer.clientHeight >= |
| | | scrollContainer.scrollHeight - 10; |
| | | |
| | | if ( |
| | | isAtBottom && |
| | | !this.isLoading && |
| | | this.loadIndex < this.infotitlelist.length |
| | | ) { |
| | | this.loadMoreData(); |
| | | } |
| | | }, |
| | | SeedetailsgGo(row) { |
| | | this.SeedetailsVisible = false; |
| | | let type = ""; |
| | | if (row.preachformson && row.preachformson.includes("3")) { |
| | | type = 1; |
| | | } |
| | | setTimeout(() => { |
| | | this.$router.push({ |
| | | path: "/followvisit/record/detailpage/", |
| | | query: { |
| | | taskid: row.taskid, |
| | | patid: row.patid, |
| | | id: row.id, |
| | | Voicetype: type, |
| | | // visitCount: this.topqueryParams.visitCount, |
| | | }, |
| | | }); |
| | | }, 300); |
| | | }, |
| | | // æ·»å /ä¿®æ¹æ ç¾ |
| | | Maintenancetag() { |
| | | if (this.lstamendtag) { |
| | | toamendtag(this.addDateRange(this.tagform)).then((response) => { |
| | | console.log(response); |
| | | this.getList(); |
| | | }); |
| | | } else { |
| | | addapitag(this.addDateRange(this.tagform)).then((response) => { |
| | | console.log(response); |
| | | this.getList(); |
| | | }); |
| | | } |
| | | this.tagform = { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | tagid: "", |
| | | }; |
| | | }, |
| | | routerErr(row) { |
| | | console.log(row, "跳转å¼å¸¸"); |
| | | this.$router.push({ |
| | | path: "/followvisit/discharge", |
| | | query: { |
| | | errtype: 1, |
| | | leavehospitaldistrictcode: row.leavehospitaldistrictcode, |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | // 表åéç½® |
| | | reset() { |
| | | this.form = { |
| | | userId: undefined, |
| | | deptId: undefined, |
| | | userName: undefined, |
| | | nickName: undefined, |
| | | password: undefined, |
| | | phonenumber: undefined, |
| | | email: undefined, |
| | | sex: undefined, |
| | | status: "0", |
| | | remark: undefined, |
| | | postIds: [], |
| | | roleIds: [], |
| | | }; |
| | | this.resetForm("form"); |
| | | }, |
| | | // æ ç¾ç¶æä¿®æ¹ |
| | | handleStatusChange(row) { |
| | | console.log(row.isupload); |
| | | let text = row.isupload === "0" ? "å¯ç¨" : "åç¨"; |
| | | this.$modal |
| | | .confirm('确认è¦"' + text + '""' + row.tagname + '"æ ç¾åï¼') |
| | | .then(function () { |
| | | return changetagcategory(row.tagid, row.isupload); |
| | | }) |
| | | .then(() => { |
| | | this.$modal.msgSuccess(text + "æå"); |
| | | }) |
| | | .catch(function () { |
| | | row.isupload = row.isupload === "0" ? "1" : "0"; |
| | | }); |
| | | }, |
| | | /** æç´¢æé®æä½ - ä¿®æ¹ä¸ºæç´¢å½åæ¿æ´»çtabæ°æ® */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | if (!this.queryParams.dateRange) this.queryParams.dateRange = []; |
| | | if (this.queryParams.statisticaltype == 1) { |
| | | this.queryParams.deptcodes = []; |
| | | } else if (this.queryParams.statisticaltype == 2) { |
| | | this.queryParams.leavehospitaldistrictcodes = []; |
| | | } |
| | | |
| | | this.queryParams.startTime = this.parseTime( |
| | | this.queryParams.dateRange[0] |
| | | ); |
| | | this.queryParams.endTime = this.parseTime(this.queryParams.dateRange[1]); |
| | | |
| | | // æ ¹æ®å½åæ¿æ´»çtabå è½½å¯¹åºæ°æ® |
| | | if (this.activeTab === "first") { |
| | | this.getFirstFollowUpList(); |
| | | } else { |
| | | this.getSecondFollowUpList(); |
| | | } |
| | | }, |
| | | |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.queryParams.dateRange = []; |
| | | this.queryParams.leavehospitaldistrictcodes = []; |
| | | this.handleQuery(); |
| | | }, |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map((item) => item.tagid); |
| | | this.single = selection.length != 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | console.log(row, "å é¤å¼¹çª"); |
| | | const tagids = row.tagid || this.ids; |
| | | console.log(tagids); |
| | | const tagname = row.tagname; |
| | | this.$modal |
| | | .confirm( |
| | | tagname |
| | | ? 'æ¯å¦ç¡®è®¤å 餿 ç¾å称为"' + tagname + '"çæ°æ®é¡¹ï¼' |
| | | : "æ¯å¦ç¡®è®¤å é¤éä¸çæ°æ®é¡¹ï¼" |
| | | ) |
| | | .then(function () { |
| | | return deletetag(tagids); |
| | | }) |
| | | .then(() => { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }) |
| | | .catch(() => { }); |
| | | }, |
| | | // å¯¼åºæ¹æ³ |
| | | |
| | | async exportTable() { |
| | | try { |
| | | // 1. è·åå¹¶æ ¼å¼åæ¥æèå´ |
| | | let dateRangeString = ""; |
| | | let sheetNameSuffix = ""; |
| | | |
| | | if ( |
| | | this.queryParams.dateRange && |
| | | this.queryParams.dateRange.length === 2 |
| | | ) { |
| | | const startDateStr = this.queryParams.dateRange[0]; |
| | | const endDateStr = this.queryParams.dateRange[1]; |
| | | const formatDateForDisplay = (dateTimeStr) => { |
| | | return dateTimeStr.split(" ")[0]; |
| | | }; |
| | | const startDateFormatted = formatDateForDisplay(startDateStr); |
| | | const endDateFormatted = formatDateForDisplay(endDateStr); |
| | | dateRangeString = `${startDateFormatted}è³${endDateFormatted}`; |
| | | sheetNameSuffix = `${startDateFormatted}è³${endDateFormatted}`; |
| | | } else { |
| | | const now = new Date(); |
| | | const currentMonth = now.getMonth() + 1; |
| | | dateRangeString = `${currentMonth}æ`; |
| | | sheetNameSuffix = `${currentMonth}æ`; |
| | | } |
| | | |
| | | // 2. æ ¹æ®å½åæ¿æ´»çtabç¡®å®å¯¼åºçæ°æ® |
| | | const isFirstFollowUp = this.activeTab === "first"; |
| | | let excelName, worksheetName, dataToExport; |
| | | |
| | | if (isFirstFollowUp) { |
| | | excelName = `馿¬¡åºé¢é访ç»è®¡è¡¨_${dateRangeString}.xlsx`; |
| | | worksheetName = `馿¬¡é访ç»è®¡_${sheetNameSuffix}`; |
| | | dataToExport = this.firstFollowUpList; |
| | | |
| | | if (!dataToExport || dataToExport.length === 0) { |
| | | this.$message.warning("ææ é¦æ¬¡éè®¿æ°æ®å¯å¯¼åº"); |
| | | return false; |
| | | } |
| | | } else { |
| | | excelName = `忬¡åºé¢é访ç»è®¡è¡¨_${dateRangeString}.xlsx`; |
| | | worksheetName = `忬¡é访ç»è®¡_${sheetNameSuffix}`; |
| | | dataToExport = this.secondFollowUpList; |
| | | |
| | | if (!dataToExport || dataToExport.length === 0) { |
| | | this.$message.warning("ææ åæ¬¡éè®¿æ°æ®å¯å¯¼åº"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 3. å建工ä½ç°¿åå·¥ä½è¡¨ |
| | | const workbook = new ExcelJS.Workbook(); |
| | | const worksheet = workbook.addWorksheet(worksheetName); |
| | | |
| | | // 4. æå»ºè¡¨æ ¼ |
| | | if (isFirstFollowUp) { |
| | | this.buildFirstFollowUpExportSheet( |
| | | worksheet, |
| | | dataToExport, |
| | | sheetNameSuffix |
| | | ); |
| | | } else { |
| | | this.buildSecondFollowUpExportSheet( |
| | | worksheet, |
| | | dataToExport, |
| | | sheetNameSuffix |
| | | ); |
| | | } |
| | | |
| | | // 5. çæå¹¶ä¸è½½æä»¶ |
| | | const buffer = await workbook.xlsx.writeBuffer(); |
| | | const blob = new Blob([buffer], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | saveAs(blob, excelName); |
| | | |
| | | this.$message.success("å¯¼åºæå"); |
| | | return true; |
| | | } catch (error) { |
| | | console.error("导åºå¤±è´¥:", error); |
| | | this.$message.error(`导åºå¤±è´¥: ${error.message}`); |
| | | return false; |
| | | } |
| | | }, |
| | | /** æå»ºé¦æ¬¡é访导åºè¡¨æ ¼ */ |
| | | buildFirstFollowUpExportSheet(worksheet, data, sheetNameSuffix) { |
| | | const titleStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 16, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFE6F3FF" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const headerStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 11, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const cellStyle = { |
| | | font: { name: "å®ä½", size: 10, color: { argb: "FF000000" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const summaryStyle = { |
| | | font: { |
| | | name: "å®ä½", |
| | | size: 10, |
| | | bold: true, |
| | | color: { argb: "FF409EFF" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | // 1. æ·»å æ»æ é¢è¡ |
| | | worksheet.mergeCells(1, 1, 1, 16); // åå¹¶A1å°P1 |
| | | const titleCell = worksheet.getCell(1, 1); |
| | | titleCell.value = `馿¬¡åºé¢é访ç»è®¡è¡¨_${sheetNameSuffix}`; |
| | | titleCell.style = titleStyle; |
| | | worksheet.getRow(1).height = 35; |
| | | |
| | | // 2. å建表头 |
| | | const secondRowHeaders = [ |
| | | "", // A2 å±å¼åå ä½ |
| | | "åºé¢ç
åº", |
| | | "ç§å®¤", |
| | | "åºé¢äººæ¬¡", |
| | | "æ éé访人次", |
| | | "åºé访人次", // B2 to F2 |
| | | // 馿¬¡åºé¢é访å表头 |
| | | "éé访", |
| | | "å¾
é访", |
| | | "é访æå", |
| | | "é访失败", |
| | | "é访ç", |
| | | "åæ¶ç", |
| | | "人工", |
| | | "çä¿¡", |
| | | "微信", |
| | | ]; |
| | | |
| | | // æ·»å 第äºè¡ |
| | | secondRowHeaders.forEach((header, index) => { |
| | | const cell = worksheet.getCell(3, index + 1); |
| | | cell.value = header; |
| | | cell.style = headerStyle; |
| | | }); |
| | | |
| | | // 3. åå¹¶åå
æ ¼ |
| | | // åå¹¶ A2:A3, B2:B3, C2:C3, D2:D3, E2:E3, F2:F3 |
| | | for (let i = 1; i <= 6; i++) { |
| | | worksheet.mergeCells(2, i, 3, i); |
| | | const cell = worksheet.getCell(2, i); |
| | | cell.style = headerStyle; |
| | | } |
| | | |
| | | // 设置第ä¸è¡åå¹¶åå
æ ¼çå¼ |
| | | worksheet.getCell(2, 1).value = ""; |
| | | worksheet.getCell(2, 2).value = "åºé¢ç
åº"; |
| | | worksheet.getCell(2, 3).value = "ç§å®¤"; |
| | | worksheet.getCell(2, 4).value = "åºé¢äººæ¬¡"; |
| | | worksheet.getCell(2, 5).value = "æ éé访人次"; |
| | | worksheet.getCell(2, 6).value = "åºé访人次"; |
| | | |
| | | // 4. åå¹¶"馿¬¡åºé¢é访"æ é¢ |
| | | worksheet.mergeCells(2, 7, 2, 15); // G2:O2 |
| | | worksheet.getCell(2, 7).value = "馿¬¡åºé¢é访"; |
| | | worksheet.getCell(2, 7).style = headerStyle; |
| | | |
| | | // 5. 设置è¡é« |
| | | worksheet.getRow(2).height = 28; |
| | | worksheet.getRow(3).height = 25; |
| | | |
| | | // 6. æ·»å æ°æ®è¡ |
| | | data.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow( |
| | | [ |
| | | "", // å±å¼å |
| | | item.leavehospitaldistrictname || "", |
| | | item.deptname || "", |
| | | item.dischargeCount || 0, |
| | | item.nonFollowUp || 0, |
| | | item.followUpNeeded || 0, |
| | | // 馿¬¡åºé¢éè®¿æ°æ® |
| | | item.needFollowUp || 0, |
| | | item.pendingFollowUp || 0, |
| | | item.followUpSuccess || 0, |
| | | item.followUpFail || 0, |
| | | item.followUpRate || "0%", |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%", |
| | | item.manual || 0, |
| | | item.sms || 0, |
| | | item.weChat || 0, |
| | | ], |
| | | rowIndex + 4 |
| | | ); |
| | | |
| | | // åºç¨æ°æ®è¡æ ·å¼ |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle; |
| | | }); |
| | | dataRow.height = 24; |
| | | }); |
| | | |
| | | // 7. æ·»å åè®¡è¡ |
| | | const summaries = this.getFirstFollowUpSummaries(data); |
| | | const summaryRow = worksheet.addRow(summaries); |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle; |
| | | if (colNumber === 1) { |
| | | cell.value = "å计"; |
| | | } |
| | | }); |
| | | summaryRow.height = 28; |
| | | |
| | | // 8. 设置å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, // å±å¼å |
| | | { width: 20 }, // åºé¢ç
åº |
| | | { width: 15 }, // ç§å®¤ |
| | | { width: 12 }, // åºé¢äººæ¬¡ |
| | | { width: 12 }, // æ éé访人次 |
| | | { width: 12 }, // åºé访人次 |
| | | // 馿¬¡åºé¢é访å |
| | | { width: 10 }, // éé访 |
| | | { width: 10 }, // å¾
é访 |
| | | { width: 10 }, // é访æå |
| | | { width: 10 }, // é访失败 |
| | | { width: 12 }, // é访ç |
| | | { width: 12 }, // åæ¶ç |
| | | { width: 8 }, // 人工 |
| | | { width: 8 }, // çä¿¡ |
| | | { width: 8 }, // 微信 |
| | | ]; |
| | | }, |
| | | |
| | | /** 馿¬¡éè®¿æ°æ®å计è¡è®¡ç® */ |
| | | getFirstFollowUpSummaries(data) { |
| | | const summaries = [ |
| | | "å计", |
| | | "/", |
| | | "/", |
| | | 0, |
| | | 0, |
| | | 0, |
| | | 0, |
| | | 0, |
| | | 0, |
| | | 0, |
| | | "0%", |
| | | "0%", |
| | | 0, |
| | | 0, |
| | | 0, |
| | | ]; |
| | | |
| | | data.forEach((item) => { |
| | | // æ°å¼å段æ±å |
| | | summaries[3] += Number(item.dischargeCount) || 0; |
| | | summaries[4] += Number(item.nonFollowUp) || 0; |
| | | summaries[5] += Number(item.followUpNeeded) || 0; |
| | | summaries[6] += Number(item.needFollowUp) || 0; |
| | | summaries[7] += Number(item.pendingFollowUp) || 0; |
| | | summaries[8] += Number(item.followUpSuccess) || 0; |
| | | summaries[9] += Number(item.followUpFail) || 0; |
| | | summaries[12] += Number(item.manual) || 0; |
| | | summaries[13] += Number(item.sms) || 0; |
| | | summaries[14] += Number(item.weChat) || 0; |
| | | }); |
| | | |
| | | // 计ç®ç¾åæ¯å段çå¹³åå¼ |
| | | const followUpRateValues = data |
| | | .map((item) => this.extractPercentageValue(item.followUpRate)) |
| | | .filter((value) => value !== null); |
| | | |
| | | const rateValues = data |
| | | .map((item) => this.extractPercentageValue(item.rate)) |
| | | .filter((value) => value !== null); |
| | | |
| | | if (followUpRateValues.length > 0) { |
| | | const avgFollowUpRate = |
| | | followUpRateValues.reduce((sum, val) => sum + val, 0) / |
| | | followUpRateValues.length; |
| | | summaries[10] = (avgFollowUpRate * 100).toFixed(2) + "%"; |
| | | } |
| | | |
| | | if (rateValues.length > 0) { |
| | | const avgRate = |
| | | rateValues.reduce((sum, val) => sum + val, 0) / rateValues.length; |
| | | summaries[11] = (avgRate * 100).toFixed(2) + "%"; |
| | | } |
| | | |
| | | // æ ¼å¼åæ°å |
| | | summaries[3] = this.formatNumber(summaries[3]); |
| | | summaries[4] = this.formatNumber(summaries[4]); |
| | | summaries[5] = this.formatNumber(summaries[5]); |
| | | summaries[6] = this.formatNumber(summaries[6]); |
| | | summaries[7] = this.formatNumber(summaries[7]); |
| | | summaries[8] = this.formatNumber(summaries[8]); |
| | | summaries[9] = this.formatNumber(summaries[9]); |
| | | summaries[12] = this.formatNumber(summaries[12]); |
| | | summaries[13] = this.formatNumber(summaries[13]); |
| | | summaries[14] = this.formatNumber(summaries[14]); |
| | | |
| | | return summaries; |
| | | }, |
| | | |
| | | /** æå»ºå次é访导åºè¡¨æ ¼ */ |
| | | buildSecondFollowUpExportSheet(worksheet, data, sheetNameSuffix) { |
| | | const titleStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 16, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFE6F3FF" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const headerStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 11, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center", wrapText: true }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const cellStyle = { |
| | | font: { name: "å®ä½", size: 10, color: { argb: "FF000000" } }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const summaryStyle = { |
| | | font: { |
| | | name: "å®ä½", |
| | | size: 10, |
| | | bold: true, |
| | | color: { argb: "FF409EFF" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { vertical: "middle", horizontal: "center" }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | // 1. æ·»å æ»æ é¢è¡ |
| | | worksheet.mergeCells(1, 1, 1, 15); // åå¹¶A1å°O1 |
| | | const titleCell = worksheet.getCell(1, 1); |
| | | titleCell.value = `忬¡åºé¢é访ç»è®¡è¡¨_${sheetNameSuffix}`; |
| | | titleCell.style = titleStyle; |
| | | worksheet.getRow(1).height = 35; |
| | | |
| | | // 2. å建表头 |
| | | const secondRowHeaders = [ |
| | | "", // A2 å±å¼åå ä½ |
| | | "åºé¢ç
åº", |
| | | "ç§å®¤", |
| | | "åºé¢äººæ¬¡", |
| | | "æ éé访人次", |
| | | "åºé访人次", // B2 to F2 |
| | | // 忬¡åºé¢é访å表头 |
| | | "éé访", |
| | | "å¾
é访", |
| | | "é访æå", |
| | | "é访失败", |
| | | "é访ç", |
| | | "人工", |
| | | "çä¿¡", |
| | | "微信", |
| | | ]; |
| | | |
| | | // æ·»å 第äºè¡ |
| | | secondRowHeaders.forEach((header, index) => { |
| | | const cell = worksheet.getCell(3, index + 1); |
| | | cell.value = header; |
| | | cell.style = headerStyle; |
| | | }); |
| | | |
| | | // 3. åå¹¶åå
æ ¼ |
| | | // åå¹¶ A2:A3, B2:B3, C2:C3, D2:D3, E2:E3, F2:F3 |
| | | for (let i = 1; i <= 6; i++) { |
| | | worksheet.mergeCells(2, i, 3, i); |
| | | const cell = worksheet.getCell(2, i); |
| | | cell.style = headerStyle; |
| | | } |
| | | |
| | | // 设置第ä¸è¡åå¹¶åå
æ ¼çå¼ |
| | | worksheet.getCell(2, 1).value = ""; |
| | | worksheet.getCell(2, 2).value = "åºé¢ç
åº"; |
| | | worksheet.getCell(2, 3).value = "ç§å®¤"; |
| | | worksheet.getCell(2, 4).value = "åºé¢äººæ¬¡"; |
| | | worksheet.getCell(2, 5).value = "æ éé访人次"; |
| | | worksheet.getCell(2, 6).value = "åºé访人次"; |
| | | |
| | | // 4. åå¹¶"忬¡åºé¢é访"æ é¢ |
| | | worksheet.mergeCells(2, 7, 2, 14); // G2:N2 |
| | | worksheet.getCell(2, 7).value = "忬¡åºé¢é访"; |
| | | worksheet.getCell(2, 7).style = headerStyle; |
| | | |
| | | // 5. 设置è¡é« |
| | | worksheet.getRow(2).height = 28; |
| | | worksheet.getRow(3).height = 25; |
| | | |
| | | // 6. æ·»å æ°æ®è¡ |
| | | data.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow( |
| | | [ |
| | | "", // å±å¼å |
| | | item.leavehospitaldistrictname || "", |
| | | item.deptname || "", |
| | | item.dischargeCount || 0, |
| | | item.nonFollowUp || 0, |
| | | item.followUpNeeded || 0, |
| | | // 忬¡åºé¢éè®¿æ°æ® |
| | | item.needFollowUpAgain || 0, |
| | | item.pendingFollowUpAgain || 0, |
| | | item.followUpSuccessAgain || 0, |
| | | item.followUpFailAgain || 0, |
| | | item.followUpRateAgain || "0%", |
| | | item.manualAgain || 0, |
| | | item.smsAgain || 0, |
| | | item.weChatAgain || 0, |
| | | ], |
| | | rowIndex + 4 |
| | | ); |
| | | |
| | | // åºç¨æ°æ®è¡æ ·å¼ |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle; |
| | | }); |
| | | dataRow.height = 24; |
| | | }); |
| | | |
| | | // 7. æ·»å åè®¡è¡ |
| | | const summaries = this.getSecondFollowUpSummaries(data); |
| | | const summaryRow = worksheet.addRow(summaries); |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle; |
| | | if (colNumber === 1) { |
| | | cell.value = "å计"; |
| | | } |
| | | }); |
| | | summaryRow.height = 28; |
| | | |
| | | // 8. 设置å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, // å±å¼å |
| | | { width: 20 }, // åºé¢ç
åº |
| | | { width: 15 }, // ç§å®¤ |
| | | { width: 12 }, // åºé¢äººæ¬¡ |
| | | { width: 12 }, // æ éé访人次 |
| | | { width: 12 }, // åºé访人次 |
| | | // 忬¡åºé¢é访å |
| | | { width: 10 }, // éé访 |
| | | { width: 10 }, // å¾
é访 |
| | | { width: 10 }, // é访æå |
| | | { width: 10 }, // é访失败 |
| | | { width: 12 }, // é访ç |
| | | { width: 8 }, // 人工 |
| | | { width: 8 }, // çä¿¡ |
| | | { width: 8 }, // 微信 |
| | | ]; |
| | | }, |
| | | |
| | | /** 忬¡éè®¿æ°æ®å计è¡è®¡ç® */ |
| | | getSecondFollowUpSummaries(data) { |
| | | const summaries = ["å计", "/", "/", 0, 0, 0, 0, 0, 0, 0, "0%", 0, 0, 0]; |
| | | |
| | | data.forEach((item) => { |
| | | // æ°å¼å段æ±å |
| | | summaries[3] += Number(item.dischargeCount) || 0; |
| | | summaries[4] += Number(item.nonFollowUp) || 0; |
| | | summaries[5] += Number(item.followUpNeeded) || 0; |
| | | summaries[6] += Number(item.needFollowUpAgain) || 0; |
| | | summaries[7] += Number(item.pendingFollowUpAgain) || 0; |
| | | summaries[8] += Number(item.followUpSuccessAgain) || 0; |
| | | summaries[9] += Number(item.followUpFailAgain) || 0; |
| | | summaries[11] += Number(item.manualAgain) || 0; |
| | | summaries[12] += Number(item.smsAgain) || 0; |
| | | summaries[13] += Number(item.weChatAgain) || 0; |
| | | }); |
| | | |
| | | // 计ç®é访çç¾åæ¯å段çå¹³åå¼ |
| | | const followUpRateAgainValues = data |
| | | .map((item) => this.extractPercentageValue(item.followUpRateAgain)) |
| | | .filter((value) => value !== null); |
| | | |
| | | if (followUpRateAgainValues.length > 0) { |
| | | const avgFollowUpRateAgain = |
| | | followUpRateAgainValues.reduce((sum, val) => sum + val, 0) / |
| | | followUpRateAgainValues.length; |
| | | summaries[10] = (avgFollowUpRateAgain * 100).toFixed(2) + "%"; |
| | | } |
| | | |
| | | // æ ¼å¼åæ°å |
| | | summaries[3] = this.formatNumber(summaries[3]); |
| | | summaries[4] = this.formatNumber(summaries[4]); |
| | | summaries[5] = this.formatNumber(summaries[5]); |
| | | summaries[6] = this.formatNumber(summaries[6]); |
| | | summaries[7] = this.formatNumber(summaries[7]); |
| | | summaries[8] = this.formatNumber(summaries[8]); |
| | | summaries[9] = this.formatNumber(summaries[9]); |
| | | summaries[11] = this.formatNumber(summaries[11]); |
| | | summaries[12] = this.formatNumber(summaries[12]); |
| | | summaries[13] = this.formatNumber(summaries[13]); |
| | | |
| | | return summaries; |
| | | }, |
| | | |
| | | /** 忬¡éè®¿è¡¨æ ¼çå计è¡è®¡ç®æ¹æ³ */ |
| | | getSummariesSecond(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (index === 1 || index === 2) { |
| | | sums[index] = "/"; |
| | | return; |
| | | } |
| | | |
| | | if (column.property === "followUpRateAgain") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | /** 忬¡é访å
é¨è¡¨æ ¼å计è¡è®¡ç®æ¹æ³ */ |
| | | getInnerSummariesSecond(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å°è®¡"; |
| | | return; |
| | | } |
| | | |
| | | if (column.property === "drname" || column.property === "deptname") { |
| | | sums[index] = "-"; |
| | | return; |
| | | } |
| | | |
| | | if (column.property === "followUpRateAgain") { |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | /** 忬¡éè®¿è¡¨æ ¼çè¡ç¹å»å±å¼ */ |
| | | handleRowClickSecond(row) { |
| | | if (this.expandsSecond.includes(this.getRowKey(row))) { |
| | | this.expandsSecond = []; |
| | | return; |
| | | } |
| | | |
| | | const params = { |
| | | ...this.queryParams, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | leavehospitaldistrictcodes: [row.leavehospitaldistrictcode], |
| | | drcode: "1", |
| | | visitCount: 2, // è®¾ç½®ä¸ºåæ¬¡é访 |
| | | }; |
| | | |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | |
| | | if (!row.doctorStats) { |
| | | this.loadingSecond = true; |
| | | getSfStatistics(params).then((res) => { |
| | | this.$set(row, "doctorStats", res.data); |
| | | this.expandsSecond = [this.getRowKey(row)]; |
| | | this.loadingSecond = false; |
| | | }); |
| | | } else { |
| | | this.expandsSecond = [this.getRowKey(row)]; |
| | | } |
| | | }, |
| | | |
| | | /** 忬¡éè®¿è¡¨æ ¼çå¤éæ¡é䏿°æ® */ |
| | | handleSelectionChangeSecond(selection) { |
| | | this.idsSecond = selection.map((item) => item.tagid); |
| | | this.single = selection.length != 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | // æ¾ç¤ºå¾è¡¨å¼¹çª |
| | | |
| | | showChartDialog() { |
| | | this.chartDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.initPieChart(); |
| | | this.initBarLineChart(); |
| | | }); |
| | | }, |
| | | // å¨methodsä¸ä¿®æ¹ç»è®¡æ¹æ³ |
| | | showChartDialog() { |
| | | this.chartDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | console.log(this.userList, "this.userList"); |
| | | |
| | | this.initCharts(); |
| | | }); |
| | | }, |
| | | |
| | | // æ°å¢åå§åå¾è¡¨æ¹æ³ |
| | | initCharts() { |
| | | this.initPieChart(); |
| | | this.initBarLineChart(); |
| | | }, |
| | | |
| | | // åå§åé¥¼å¾ |
| | | initPieChart() { |
| | | const echarts = require("echarts"); |
| | | const pieDom = document.getElementById("pieChart"); |
| | | if (!pieDom) return; |
| | | |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose(); |
| | | } |
| | | |
| | | this.pieChart = echarts.init(pieDom); |
| | | |
| | | // 计ç®é¥¼å¾æ°æ® |
| | | const followUpData = { |
| | | pending: 0, |
| | | success: 0, |
| | | fail: 0, |
| | | }; |
| | | |
| | | this.userList.forEach((item) => { |
| | | followUpData.pending += item.pendingFollowUp || 0; |
| | | followUpData.success += item.followUpSuccess || 0; |
| | | followUpData.fail += item.followUpFail || 0; |
| | | }); |
| | | |
| | | // ä½¿ç¨æ´ç¾è§çé¢è²æ¹æ¡ |
| | | const pieOption = { |
| | | title: { |
| | | text: "éè®¿ç¶æåå¸", |
| | | left: "center", |
| | | textStyle: { |
| | | color: "#333", |
| | | fontSize: 16, |
| | | }, |
| | | }, |
| | | tooltip: { |
| | | trigger: "item", |
| | | formatter: "{a} <br/>{b}: {c} ({d}%)", |
| | | }, |
| | | legend: { |
| | | orient: "vertical", |
| | | left: "left", |
| | | data: ["å¾
é访", "é访æå", "é访失败"], |
| | | textStyle: { |
| | | color: "#666", |
| | | }, |
| | | }, |
| | | color: ["#FF9D4D", "#36B37E", "#FF5C5C"], // æ°çé
è²æ¹æ¡ |
| | | series: [ |
| | | { |
| | | name: "éè®¿ç¶æ", |
| | | type: "pie", |
| | | radius: ["40%", "70%"], |
| | | avoidLabelOverlap: true, |
| | | itemStyle: { |
| | | borderRadius: 10, |
| | | borderColor: "#fff", |
| | | borderWidth: 2, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | formatter: "{b}: {c} ({d}%)", |
| | | color: "#333", |
| | | }, |
| | | emphasis: { |
| | | label: { |
| | | show: true, |
| | | fontSize: "18", |
| | | fontWeight: "bold", |
| | | }, |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | data: [ |
| | | { |
| | | value: followUpData.pending, |
| | | name: "å¾
é访", |
| | | }, |
| | | { |
| | | value: followUpData.success, |
| | | name: "é访æå", |
| | | }, |
| | | { |
| | | value: followUpData.fail, |
| | | name: "é访失败", |
| | | }, |
| | | ], |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | this.pieChart.setOption(pieOption); |
| | | window.addEventListener("resize", this.resizePieChart); |
| | | }, |
| | | |
| | | // åå§åæ±ç¶æçº¿å¾ |
| | | initBarLineChart() { |
| | | const echarts = require("echarts"); |
| | | const barDom = document.getElementById("barLineChart"); |
| | | if (!barDom) return; |
| | | |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose(); |
| | | } |
| | | |
| | | this.barLineChart = echarts.init(barDom); |
| | | |
| | | // å夿°æ® |
| | | const categories = this.userList.map( |
| | | (item) => item.leavehospitaldistrictname || item.deptname |
| | | ); |
| | | |
| | | const dischargeData = this.userList.map( |
| | | (item) => item.dischargeCount || 0 |
| | | ); |
| | | const followUpData = this.userList.map( |
| | | (item) => item.followUpNeeded || 0 |
| | | ); |
| | | |
| | | // æ°å¢ä¸¤æ¡æçº¿æ°æ® |
| | | const followUpRateData = this.userList.map((item) => { |
| | | if (!item.followUpRate) return 0; |
| | | // 廿ç¾åå·å¹¶è½¬ä¸ºæ°å |
| | | const rateStr = String(item.followUpRate).replace("%", ""); |
| | | return parseFloat(rateStr) || 0; |
| | | }); |
| | | |
| | | const timelyRateData = this.userList.map((item) => |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) : 0 |
| | | ); |
| | | |
| | | const option = { |
| | | title: { |
| | | text: "ç§å®¤/ç
åºé访è¶å¿", |
| | | left: "center", |
| | | textStyle: { |
| | | color: "#333", |
| | | fontSize: 16, |
| | | }, |
| | | }, |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "cross", |
| | | crossStyle: { |
| | | color: "#999", |
| | | }, |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["åºé¢äººæ¬¡", "åºé访人次", "é访ç(%)", "åæ¶ç(%)"], |
| | | top: "bottom", |
| | | textStyle: { |
| | | color: "#666", |
| | | }, |
| | | }, |
| | | color: ["#5470C6", "#91CC75", "#EE6666", "#9A60B4"], // æ°å¢ç´«è²ç¨äºåæ¶ç |
| | | xAxis: { |
| | | type: "category", |
| | | data: categories, |
| | | axisLabel: { |
| | | interval: 0, |
| | | rotate: 30, |
| | | color: "#666", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: "value", |
| | | name: "人次", |
| | | min: 0, |
| | | axisLabel: { |
| | | color: "#666", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: "#f0f0f0", |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | type: "value", |
| | | name: "ç¾åæ¯(%)", |
| | | min: 0, |
| | | max: 100, |
| | | axisLabel: { |
| | | color: "#666", |
| | | formatter: "{value}%", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: false, |
| | | }, |
| | | }, |
| | | ], |
| | | series: [ |
| | | { |
| | | name: "åºé¢äººæ¬¡", |
| | | type: "bar", |
| | | barWidth: "25%", |
| | | data: dischargeData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0], |
| | | }, |
| | | }, |
| | | { |
| | | name: "åºé访人次", |
| | | type: "bar", |
| | | barWidth: "25%", |
| | | data: followUpData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0], |
| | | }, |
| | | }, |
| | | { |
| | | name: "é访ç(%)", |
| | | type: "line", |
| | | yAxisIndex: 1, |
| | | data: followUpRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 80, |
| | | lineStyle: { |
| | | color: "#EE6666", |
| | | type: "dashed", |
| | | }, |
| | | // label: { |
| | | // position: 'end', |
| | | // formatter: 'ç®æ 80%' |
| | | // } |
| | | }, |
| | | ], |
| | | }, |
| | | }, |
| | | { |
| | | name: "åæ¶ç(%)", |
| | | type: "line", |
| | | yAxisIndex: 1, |
| | | data: timelyRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3, |
| | | type: "dotted", // 使ç¨è线åºå |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 90, |
| | | lineStyle: { |
| | | color: "#9A60B4", |
| | | type: "dashed", |
| | | }, |
| | | // label: { |
| | | // position: 'end', |
| | | // formatter: 'ç®æ 90%' |
| | | // } |
| | | }, |
| | | ], |
| | | }, |
| | | }, |
| | | ], |
| | | grid: { |
| | | top: "15%", |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "15%", |
| | | containLabel: true, |
| | | }, |
| | | }; |
| | | |
| | | this.barLineChart.setOption(option); |
| | | window.addEventListener("resize", this.resizeBarLineChart); |
| | | }, |
| | | |
| | | // å¾è¡¨ååºå¼è°æ´æ¹æ³ |
| | | resizePieChart() { |
| | | if (this.pieChart) { |
| | | this.pieChart.resize(); |
| | | } |
| | | }, |
| | | |
| | | resizeBarLineChart() { |
| | | if (this.barLineChart) { |
| | | this.barLineChart.resize(); |
| | | } |
| | | }, |
| | | |
| | | // å¨ç»ä»¶éæ¯æ¶æ¸
ç |
| | | beforeDestroy() { |
| | | // ç§»é¤äºä»¶çå¬ |
| | | window.removeEventListener("resize", this.resizePieChart); |
| | | window.removeEventListener("resize", this.resizeBarLineChart); |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose(); |
| | | this.pieChart = null; |
| | | } |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose(); |
| | | this.barLineChart = null; |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | ::v-deep .el-tabs__header { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | ::v-deep .el-tabs__item { |
| | | font-size: 16px; |
| | | padding: 0 20px; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | } |
| | | |
| | | ::v-deep .el-tabs__active-bar { |
| | | height: 3px; |
| | | } |
| | | |
| | | /* Tabå
容åºåæ ·å¼ */ |
| | | .el-tab-pane { |
| | | .your-table-container { |
| | | margin-top: 10px; |
| | | } |
| | | } |
| | | |
| | | .sidecolumn { |
| | | width: 180px; |
| | | min-height: 100vh; |
| | | text-align: center; |
| | | // display: flex; |
| | | margin-top: 20px; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | background: #edf1f7; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | |
| | | .sidecolumn-top { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | |
| | | .top-wj { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .top-tj { |
| | | font-size: 18px; |
| | | |
| | | color: rgb(0, 89, 255); |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | .center-ss { |
| | | margin-top: 30px; |
| | | |
| | | .input-with-select { |
| | | height: 40px !important; |
| | | } |
| | | } |
| | | |
| | | .bottom-fl { |
| | | margin-top: 30px; |
| | | display: center !important; |
| | | } |
| | | } |
| | | |
| | | .qrcode-dialo { |
| | | text-align: center; |
| | | // display: flex; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | background: #edf1f7; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | |
| | | .qrcode-text { |
| | | font-size: 20px; |
| | | |
| | | span { |
| | | margin-left: 20px; |
| | | } |
| | | } |
| | | |
| | | .qrcode-img { |
| | | width: 300px; |
| | | height: 400px; |
| | | } |
| | | } |
| | | |
| | | ::v-deep.el-tabs--left, |
| | | .el-tabs--right { |
| | | overflow: hidden; |
| | | align-items: center; |
| | | display: flex; |
| | | } |
| | | |
| | | ::v-deep.el-input--medium .el-input__inner { |
| | | height: 40px !important; |
| | | } |
| | | |
| | | ::v-deep.el-tabs--right .el-tabs__active-bar.is-right { |
| | | height: 40px; |
| | | width: 5px; |
| | | left: 0; |
| | | } |
| | | |
| | | ::v-deep.el-tabs--right .el-tabs__item.is-right { |
| | | display: block; |
| | | text-align: left; |
| | | font-size: 20px; |
| | | } |
| | | |
| | | // ç¾ååè®¡è¡æ ·å¼ |
| | | ::v-deep .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | |
| | | .cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å
é¨è¡¨æ ¼åè®¡è¡æ ·å¼ |
| | | ::v-deep .inner-table .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #ecf5ff; |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | |
| | | .cell { |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ç¾åæ¯åæ®µç¹æ®æ ·å¼ |
| | | .your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="followUpRate"] .cell, |
| | | .your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="rate"] .cell, |
| | | .your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="followUpRateAgain"] .cell { |
| | | color: #e6a23c !important; |
| | | font-weight: 700 !important; |
| | | } |
| | | |
| | | .leftvlue { |
| | | // display: flex; |
| | | // flex: 1; |
| | | // width: 80%; |
| | | // margin-top: 20px; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | background: #ffff; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | |
| | | .mulsz { |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | |
| | | /* ä½¿è¡ææåæé */ |
| | | .el-table__row { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | /* å
å±å»çè¡¨æ ¼æ ·å¼ */ |
| | | .inner-table { |
| | | |
| | | // è¡¨å¤´èæ¯è² |
| | | ::v-deep .el-table__header-wrapper { |
| | | background-color: #f0f7ff !important; |
| | | |
| | | th { |
| | | background-color: #f0f7ff !important; |
| | | } |
| | | } |
| | | |
| | | // è¡¨æ ¼è¡èæ¯è² |
| | | ::v-deep .el-table__body-wrapper { |
| | | tr { |
| | | background-color: #f9fbfe !important; |
| | | |
| | | &:hover { |
| | | background-color: #e6f1ff !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // è¾¹æ¡é¢è² |
| | | ::v-deep .el-table--border { |
| | | border-color: #d9e8ff !important; |
| | | |
| | | td, |
| | | th { |
| | | border-color: #d9e8ff !important; |
| | | } |
| | | } |
| | | |
| | | // æé©¬çº¹ææ |
| | | ::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td { |
| | | background-color: #f5f9ff !important; |
| | | } |
| | | } |
| | | |
| | | /* å±å¼è¡æ ·å¼ */ |
| | | .el-table__expanded-cell { |
| | | padding: 10px 0 !important; |
| | | background: #f8f8f8; |
| | | } |
| | | |
| | | .document { |
| | | width: 100px; |
| | | height: 50px; |
| | | } |
| | | |
| | | .data-list { |
| | | max-height: 800px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .documentf { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .button-text { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | |
| | | .button-textck { |
| | | color: rgb(39, 167, 67); |
| | | } |
| | | |
| | | .button-textxg { |
| | | color: rgb(35, 81, 233); |
| | | } |
| | | |
| | | .button-textsc { |
| | | color: rgb(235, 23, 23); |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="Questionnairemanagement"> |
| | | <div class="leftvlue"> |
| | | <div class="leftvlue-bg"> |
| | | <el-row :gutter="20"> |
| | | <!--æ ç¾æ°æ®--> |
| | | <el-col :span="24" :xs="24"> |
| | | <el-form |
| | | :model="queryParams" |
| | | ref="queryForm" |
| | | size="small" |
| | | :inline="true" |
| | | v-show="showSearch" |
| | | label-width="98px" |
| | | > |
| | | <el-form-item label="ç»è®¡ç±»å" prop="userName"> |
| | | <el-select |
| | | v-model="queryParams.statisticaltype" |
| | | placeholder="è¯·éæ©ç»è®¡ç±»å" |
| | | > |
| | | <el-option |
| | | v-for="item in Statisticallist" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | <el-select |
| | | style="margin-left: 10px" |
| | | v-if="queryParams.statisticaltype == 1" |
| | | v-model="queryParams.leavehospitaldistrictcodes" |
| | | size="medium" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç
åº" |
| | | > |
| | | <el-option |
| | | v-for="item in flatArrayhospit" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | <el-select |
| | | v-else-if="queryParams.statisticaltype == 2" |
| | | v-model="queryParams.deptcodes" |
| | | size="medium" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç§å®¤" |
| | | > |
| | | <el-option |
| | | v-for="item in flatArraydept" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <div class="follow-up-statistics"> |
| | | <!-- æç´¢è¡¨ååºå --> |
| | | <div class="search-section"> |
| | | <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="98px"> |
| | | <el-form-item label="ç»è®¡ç±»å" prop="userName"> |
| | | <el-select v-model="queryParams.statisticaltype" placeholder="è¯·éæ©ç»è®¡ç±»å"> |
| | | <el-option v-for="item in Statisticallist" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | <el-select |
| | | style="margin-left: 10px" |
| | | v-if="queryParams.statisticaltype == 1" |
| | | v-model="queryParams.leavehospitaldistrictcodes" |
| | | size="medium" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç
åº" |
| | | > |
| | | <el-option v-for="item in flatArrayhospit" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | <el-select |
| | | v-else-if="queryParams.statisticaltype == 2" |
| | | v-model="queryParams.deptcodes" |
| | | size="medium" |
| | | multiple |
| | | filterable |
| | | placeholder="è¯·éæ©ç§å®¤" |
| | | > |
| | | <el-option v-for="item in flatArraydept" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="ç»è®¡ç±»å" prop="userName"> |
| | | <el-select |
| | | v-model="queryParams.serviceType" |
| | | multiple |
| | | placeholder="è¯·éæ©" |
| | | > |
| | | <el-option |
| | | v-for="item in options" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item |
| | | label-width="200" |
| | | label="åºé访æ¶é´èå´" |
| | | prop="userName" |
| | | > |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | :default-time="['00:00:00', '23:59:59']" |
| | | > |
| | | > |
| | | </el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="æå¡ç±»å" prop="userName"> |
| | | <el-select v-model="queryParams.serviceType" multiple placeholder="è¯·éæ©"> |
| | | <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-search" |
| | | size="medium" |
| | | @click="handleQuery" |
| | | >æç´¢</el-button |
| | | > |
| | | <el-button |
| | | icon="el-icon-refresh" |
| | | size="medium" |
| | | @click="resetQuery" |
| | | >éç½®</el-button |
| | | > |
| | | </el-form-item> |
| | | <el-col :span="19"> |
| | | <el-button |
| | | type="warning" |
| | | plain |
| | | icon="el-icon-download" |
| | | size="medium" |
| | | @click="exportTable" |
| | | >导åº</el-button |
| | | > |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | icon="el-icon-data-line" |
| | | size="medium" |
| | | @click="showChartDialog" |
| | | >ç»è®¡è¶å¿å¾</el-button |
| | | > |
| | | </el-col> |
| | | </el-form> |
| | | <div class="your-table-container"> |
| | | <el-table |
| | | ref="exportTable" |
| | | id="exportTableid" |
| | | v-loading="loading" |
| | | :data="userList" |
| | | :border="true" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="handleRowClick" |
| | | :row-key="getRowKey" |
| | | show-summary |
| | | :summary-method="getSummaries" |
| | | :expand-row-keys="expands" |
| | | > |
| | | <!-- å±å¼è¡ç®å¤´å --> |
| | | <el-table-column type="expand"> |
| | | <template slot-scope="props"> |
| | | <el-table |
| | | :data="props.row.doctorStats" |
| | | border |
| | | style="width: 95%; margin: 0 auto" |
| | | class="inner-table" |
| | | show-summary |
| | | :summary-method="getInnerSummaries" |
| | | > |
| | | <el-table-column |
| | | label="å»çå§å" |
| | | prop="drname" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="ç§å®¤" |
| | | width="120" |
| | | prop="deptname" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="åºé¢äººæ¬¡" |
| | | prop="dischargeCount" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="åºé¢äººæ¬¡" |
| | | align="center" |
| | | key="dischargeCount" |
| | | prop="dischargeCount" |
| | | > |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="æ éé访人次" |
| | | align="center" |
| | | width="100" |
| | | key="nonFollowUp" |
| | | prop="nonFollowUp" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºé访人次" |
| | | align="center" |
| | | width="100" |
| | | key="followUpNeeded" |
| | | prop="followUpNeeded" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column |
| | | label="éé访" |
| | | align="center" |
| | | key="needFollowUp" |
| | | prop="needFollowUp" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¾
é访" |
| | | align="center" |
| | | key="pendingFollowUp" |
| | | prop="pendingFollowUp" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访æå" |
| | | align="center" |
| | | key="followUpSuccess" |
| | | prop="followUpSuccess" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访失败" |
| | | align="center" |
| | | key="followUpFail" |
| | | prop="followUpFail" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访ç" |
| | | align="center" |
| | | width="120" |
| | | key="followUpRate" |
| | | prop="followUpRate" |
| | | > |
| | | <!-- <template slot-scope="scope"> |
| | | <span |
| | | >{{ |
| | | (Number(scope.row.followUpRate) * 100).toFixed(2) |
| | | }}%</span |
| | | > |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åæ¶ç" |
| | | align="center" |
| | | width="120" |
| | | key="rate" |
| | | prop="rate" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="Seedetails(scope.row)" |
| | | ><span class="button-zx" |
| | | >{{ |
| | | (Number(scope.row.rate) * 100).toFixed(2) |
| | | }}%</span |
| | | ></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="人工" |
| | | align="center" |
| | | key="manual" |
| | | prop="manual" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="çä¿¡" |
| | | align="center" |
| | | key="sms" |
| | | prop="sms" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="微信" |
| | | align="center" |
| | | key="weChat" |
| | | prop="weChat" |
| | | > |
| | | </el-table-column> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column |
| | | label="éé访" |
| | | align="center" |
| | | key="needFollowUpAgain" |
| | | prop="needFollowUpAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¾
é访" |
| | | align="center" |
| | | key="pendingFollowUpAgain" |
| | | prop="pendingFollowUpAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访æå" |
| | | align="center" |
| | | key="followUpSuccessAgain" |
| | | prop="followUpSuccessAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访失败" |
| | | align="center" |
| | | key="followUpFailAgain" |
| | | prop="followUpFailAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访ç" |
| | | align="center" |
| | | width="120" |
| | | key="followUpRateAgain" |
| | | prop="followUpRateAgain" |
| | | > |
| | | <!-- <template slot-scope="scope"> |
| | | <span |
| | | >{{ |
| | | (Number(scope.row.FollowUpRateAgain) * 100).toFixed(2) |
| | | }}%</span |
| | | > |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="人工" |
| | | align="center" |
| | | key="manualAgain" |
| | | prop="manualAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="çä¿¡" |
| | | align="center" |
| | | key="smsAgain" |
| | | prop="smsAgain" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="微信" |
| | | align="center" |
| | | key="weChatAgain" |
| | | prop="weChatAgain" |
| | | > |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºé¢ç
åº" |
| | | align="center" |
| | | sortable |
| | | key="leavehospitaldistrictname" |
| | | prop="leavehospitaldistrictname" |
| | | width="150" |
| | | :show-overflow-tooltip="true" |
| | | :sort-method="sortChineseNumber" |
| | | /> |
| | | <el-table-column |
| | | label="ç§å®¤" |
| | | align="center" |
| | | key="deptname" |
| | | prop="deptname" |
| | | :show-overflow-tooltip="true" |
| | | /> |
| | | <el-table-column |
| | | label="åºé¢äººæ¬¡" |
| | | align="center" |
| | | key="dischargeCount" |
| | | prop="dischargeCount" |
| | | > |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | label="æ éé访人次" |
| | | align="center" |
| | | width="100" |
| | | key="nonFollowUp" |
| | | prop="nonFollowUp" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºé访人次" |
| | | align="center" |
| | | width="100" |
| | | key="followUpNeeded" |
| | | prop="followUpNeeded" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column align="center" label="馿¬¡åºé¢é访"> |
| | | <el-table-column |
| | | label="éé访" |
| | | align="center" |
| | | key="needFollowUp" |
| | | prop="needFollowUp" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.needFollowUpInfo, |
| | | scope.row.leavehospitaldistrictname + 'éé访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.needFollowUp |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¾
é访" |
| | | align="center" |
| | | key="pendingFollowUp" |
| | | prop="pendingFollowUp" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.pendingFollowUpInfo, |
| | | scope.row.leavehospitaldistrictname + 'å¾
é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.pendingFollowUp |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访æå" |
| | | align="center" |
| | | key="followUpSuccess" |
| | | prop="followUpSuccess" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.followUpSuccessInfo, |
| | | scope.row.leavehospitaldistrictname + 'é访æåå表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.followUpSuccess |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访失败" |
| | | align="center" |
| | | key="followUpFail" |
| | | prop="followUpFail" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.followUpFailInfo, |
| | | scope.row.leavehospitaldistrictname + 'é访失败å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.followUpFail |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访ç" |
| | | align="center" |
| | | width="120" |
| | | key="followUpRate" |
| | | prop="followUpRate" |
| | | > |
| | | <!-- <template slot-scope="scope"> |
| | | <span |
| | | >{{ |
| | | (Number(scope.row.followUpRate) * 100).toFixed(2) |
| | | }}%</span |
| | | > |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åæ¶ç" |
| | | align="center" |
| | | width="120" |
| | | key="rate" |
| | | prop="rate" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="Seedetails(scope.row)" |
| | | ><span class="button-zx" |
| | | >{{ |
| | | (Number(scope.row.rate) * 100).toFixed(2) |
| | | }}%</span |
| | | ></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="人工" |
| | | align="center" |
| | | key="manual" |
| | | prop="manual" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.manualInfo, |
| | | scope.row.leavehospitaldistrictname + '人工é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.manual |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="çä¿¡" |
| | | align="center" |
| | | key="sms" |
| | | prop="sms" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.smsInfo, |
| | | scope.row.leavehospitaldistrictname + 'çä¿¡é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.sms |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="微信" |
| | | align="center" |
| | | key="weChat" |
| | | prop="weChat" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.weChatInfo, |
| | | scope.row.leavehospitaldistrictname + '微信é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.weChat |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="忬¡åºé¢é访"> |
| | | <el-table-column |
| | | label="éé访" |
| | | align="center" |
| | | key="needFollowUpAgain" |
| | | prop="needFollowUpAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.needFollowUpAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访éé访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.needFollowUpAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¾
é访" |
| | | align="center" |
| | | key="pendingFollowUpAgain" |
| | | prop="pendingFollowUpAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.pendingFollowUpAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访å¾
é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.pendingFollowUpAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访æå" |
| | | align="center" |
| | | key="followUpSuccessAgain" |
| | | prop="followUpSuccessAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.followUpSuccessAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访é访æåå表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.followUpSuccessAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访失败" |
| | | align="center" |
| | | key="followUpFailAgain" |
| | | prop="followUpFailAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.followUpFailAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访é访失败å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.followUpFailAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="é访ç" |
| | | align="center" |
| | | width="120" |
| | | key="followUpRateAgain" |
| | | prop="followUpRateAgain" |
| | | > |
| | | <!-- <template slot-scope="scope"> |
| | | <span |
| | | >{{ |
| | | (Number(scope.row.FollowUpRateAgain) * 100).toFixed(2) |
| | | }}%</span |
| | | > |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="人工" |
| | | align="center" |
| | | key="manualAgain" |
| | | prop="manualAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.manualAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访人工é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.manualAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="çä¿¡" |
| | | align="center" |
| | | key="smsAgain" |
| | | prop="smsAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.smsAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访çä¿¡é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.smsAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="微信" |
| | | align="center" |
| | | key="weChatAgain" |
| | | prop="weChatAgain" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click=" |
| | | viewDetails( |
| | | scope.row.weChatAgainInfo, |
| | | scope.row.leavehospitaldistrictname + |
| | | '忬¡é访微信é访å表' |
| | | ) |
| | | " |
| | | ><span class="button-zx">{{ |
| | | scope.row.weChatAgain |
| | | }}</span></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table-column> |
| | | <el-table-column |
| | | v-if="orgname == '丽水å¸ä¸å»é¢'" |
| | | align="center" |
| | | label="é访æ
åµ" |
| | | > |
| | | <el-table-column |
| | | label="æ£å¸¸è¯é³" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation1" |
| | | prop="taskSituation1" |
| | | > |
| | | </el-table-column |
| | | ><el-table-column |
| | | label="æ£è
ææ¥ææè®¿" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation2" |
| | | prop="taskSituation2" |
| | | > |
| | | </el-table-column |
| | | ><el-table-column |
| | | label="é¢è®¿æè
æ¥è¯" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation3" |
| | | prop="taskSituation3" |
| | | > |
| | | </el-table-column |
| | | ><el-table-column |
| | | label="微信é访" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation4" |
| | | prop="taskSituation4" |
| | | > |
| | | </el-table-column |
| | | ><el-table-column |
| | | label="é访çµè¯ä¸æ£ç¡®" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation5" |
| | | prop="taskSituation5" |
| | | > |
| | | </el-table-column |
| | | ><el-table-column |
| | | label="å
¶ä»æ
åµä¸å®é访" |
| | | align="center" |
| | | width="100" |
| | | key="taskSituation6" |
| | | prop="taskSituation6" |
| | | > |
| | | </el-table-column> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <!-- <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> --> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | <!-- ç»è®¡è¶å¿å¾å¼¹çª --> |
| | | <el-dialog |
| | | title="é访ç»è®¡è¶å¿å¾" |
| | | :visible.sync="chartDialogVisible" |
| | | width="80%" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div class="chart-container"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">éè®¿ç¶æåå¸</div> |
| | | <div id="pieChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="chart-title">é访è¶å¿åæ</div> |
| | | <div id="barLineChart" style="width: 100%; height: 400px"></div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </el-dialog> |
| | | <el-dialog |
| | | title="æªåæ¶é访æ£è
æå¡" |
| | | :visible.sync="SeedetailsVisible" |
| | | v-loading="Seedloading" |
| | | width="70%" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <!--ç¨æ·æ°æ®--> |
| | | <el-form |
| | | :model="patientqueryParams" |
| | | ref="queryForm" |
| | | size="small" |
| | | :inline="true" |
| | | label-width="98px" |
| | | > |
| | | <el-form-item label="æ£è
ï¼"> |
| | | <el-input |
| | | v-model="patientqueryParams.name" |
| | | @keyup.enter.native="handleQuery" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æ£è
è¯æï¼"> |
| | | <el-input |
| | | v-model="patientqueryParams.leavediagname" |
| | | @keyup.enter.native="handleQuery" |
| | | ></el-input> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-search" |
| | | size="medium" |
| | | @click="handleQuery" |
| | | >æç´¢</el-button |
| | | > |
| | | <el-button |
| | | icon="el-icon-refresh" |
| | | size="medium" |
| | | @click="resetQuery" |
| | | >åæ¶å建</el-button |
| | | > |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- éæ©æ£è
å表 --> |
| | | <el-table :data="logsheetlist" style="width: 100%"> |
| | | <el-table-column |
| | | prop="sendname" |
| | | align="center" |
| | | label="å§å" |
| | | width="100" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="taskName" |
| | | align="center" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | label="ä»»å¡åç§°" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="sendstate" |
| | | align="center" |
| | | width="200" |
| | | label="ä»»å¡ç¶æ" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <div v-if="scope.row.sendstate == 1"> |
| | | <el-tag type="primary" :disable-transitions="false" |
| | | >表åå·²é¢å</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 2"> |
| | | <el-tag type="primary" :disable-transitions="false" |
| | | >å¾
é访</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 3"> |
| | | <el-tag type="success" :disable-transitions="false" |
| | | >表åå·²åé</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 4"> |
| | | <el-tag type="info" :disable-transitions="false" |
| | | >䏿§è¡</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 5"> |
| | | <el-tag type="danger" :disable-transitions="false" |
| | | >åé失败</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <el-tag type="success" :disable-transitions="false" |
| | | >已宿</el-tag |
| | | > |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="visitTime" |
| | | align="center" |
| | | label="åºé访æ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="finishtime" |
| | | align="center" |
| | | label="éè®¿å®ææ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºé¢æ¥æ" |
| | | width="200" |
| | | align="center" |
| | | key="endtime" |
| | | prop="endtime" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template></el-table-column |
| | | > |
| | | <el-table-column |
| | | label="责任æ¤å£«" |
| | | width="120" |
| | | align="center" |
| | | key="nurseName" |
| | | prop="nurseName" |
| | | /> |
| | | <el-table-column |
| | | label="主治å»ç" |
| | | width="120" |
| | | align="center" |
| | | key="drname" |
| | | prop="drname" |
| | | /> |
| | | |
| | | <el-table-column |
| | | label="ç»æç¶æ" |
| | | align="center" |
| | | key="excep" |
| | | prop="excep" |
| | | width="120" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <dict-tag |
| | | :options="dict.type.sys_yujing" |
| | | :value="scope.row.excep" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¤çæè§" |
| | | align="center" |
| | | key="suggest" |
| | | prop="suggest" |
| | | width="120" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <dict-tag |
| | | :options="dict.type.sys_suggest" |
| | | :value="scope.row.suggest" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | prop="templatename" |
| | | align="center" |
| | | label="æå¡æ¨¡æ¿" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="remark" |
| | | align="center" |
| | | label="æå¡è®°å½" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | prop="bankcardno" |
| | | align="center" |
| | | label="å¼å«ç¶æ" |
| | | width="210" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="æä½" |
| | | fixed="right" |
| | | align="center" |
| | | width="200" |
| | | class-name="small-padding fixed-width" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="SeedetailsgGo(scope.row)" |
| | | ><span class="button-zx" |
| | | ><i class="el-icon-s-order"></i>æ¥ç</span |
| | | ></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-row> |
| | | <pagination |
| | | v-show="patienttotal > 0 && this.patientqueryParams.allhosp != 6" |
| | | :total="patienttotal" |
| | | :page.sync="patientqueryParams.pn" |
| | | :limit.sync="patientqueryParams.ps" |
| | | @pagination="Seedetailstion" |
| | | <el-form-item label-width="200" label="åºé访æ¶é´èå´" prop="userName"> |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | :default-time="['00:00:00', '23:59:59']" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- å类详æ
--> |
| | | <el-dialog |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="medium" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" size="medium" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | |
| | | <el-button type="warning" plain icon="el-icon-download" size="medium" @click="handleExport">导åº</el-button> |
| | | <el-button type="primary" plain icon="el-icon-data-line" size="medium" @click="showChartDialog">ç»è®¡è¶å¿å¾</el-button> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- Tab忢åºå --> |
| | | <div class="tab-section"> |
| | | <el-tabs v-model="activeTab" @tab-click="handleTabClick"> |
| | | <el-tab-pane label="馿¬¡é访" name="first"> |
| | | <FirstFollowUp |
| | | ref="firstFollowUp" |
| | | :query-params="queryParams" |
| | | :flat-array-hospit="flatArrayhospit" |
| | | :flat-array-dept="flatArraydept" |
| | | :options="options" |
| | | :orgname="orgname" |
| | | @view-details="viewDetails" |
| | | @see-details="Seedetails" |
| | | /> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="忬¡é访" name="second"> |
| | | <SecondFollowUp |
| | | ref="secondFollowUp" |
| | | :query-params="queryParams" |
| | | :flat-array-hospit="flatArrayhospit" |
| | | :flat-array-dept="flatArraydept" |
| | | :options="options" |
| | | :orgname="orgname" |
| | | @view-details="viewDetails" |
| | | /> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="å»¶ç»æ¤çç»è®¡" name="continued" v-if="orgname == 'çç«åå¾·ç¿ èé¢åº'"> |
| | | <ContinuedCare |
| | | ref="continuedCare" |
| | | :query-params="queryParams" |
| | | :flat-array-hospit="flatArrayhospit" |
| | | :flat-array-dept="flatArraydept" |
| | | :options="options" |
| | | :orgname="orgname" |
| | | @view-details="viewDetails" |
| | | /> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | |
| | | <!-- å¼¹çªåºå --> |
| | | <ChartDialog |
| | | :visible="chartDialogVisible" |
| | | :data="chartData" |
| | | :active-tab="activeTab" |
| | | @close="chartDialogVisible = false" |
| | | /> |
| | | |
| | | <DetailDialog |
| | | :visible="infotitleVisible" |
| | | :title="infotitle" |
| | | :visible.sync="infotitleVisible" |
| | | v-loading="infotitloading" |
| | | width="70%" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div style="margin-bottom: 16px; display: flex; align-items: center"> |
| | | <span style="margin-right: 10px; font-weight: bold">æ£è
å§åæ¥è¯¢:</span> |
| | | <el-input |
| | | v-model="searchName" |
| | | placeholder="请è¾å
¥æ£è
å§åè¿è¡çé" |
| | | clearable |
| | | style="width: 300px" |
| | | @input="handleSearch" |
| | | @clear="handleSearch" |
| | | > |
| | | </el-input> |
| | | <span |
| | | style="margin-left: 10px; color: rgb(35, 81, 233); font-size: 16px" |
| | | > |
| | | å
± {{ infotitlelist.length }} æ¡è®°å½ |
| | | </span> |
| | | </div> |
| | | <div class="examine-jic"> |
| | | <div class="jic-value"> |
| | | <el-row :gutter="20"> |
| | | <!-- éæ©æ£è
å表 --> |
| | | <div |
| | | class="data-list" |
| | | ref="dataList" |
| | | @scroll="handleScroll" |
| | | v-loading="infotitloading" |
| | | > |
| | | <el-table |
| | | :data="currentDisplayList" |
| | | height="660" |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column |
| | | prop="sendname" |
| | | align="center" |
| | | label="å§å" |
| | | width="100" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="taskName" |
| | | align="center" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | label="ä»»å¡åç§°" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="sendstate" |
| | | align="center" |
| | | width="200" |
| | | label="ä»»å¡ç¶æ" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <div v-if="scope.row.sendstate == 1"> |
| | | <el-tag type="primary" :disable-transitions="false" |
| | | >表åå·²é¢å</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 2"> |
| | | <el-tag type="primary" :disable-transitions="false" |
| | | >å¾
é访</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 3"> |
| | | <el-tag type="success" :disable-transitions="false" |
| | | >表åå·²åé</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 4"> |
| | | <el-tag type="info" :disable-transitions="false" |
| | | >䏿§è¡</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 5"> |
| | | <el-tag type="danger" :disable-transitions="false" |
| | | >åé失败</el-tag |
| | | > |
| | | </div> |
| | | <div v-if="scope.row.sendstate == 6"> |
| | | <el-tag type="success" :disable-transitions="false" |
| | | >已宿</el-tag |
| | | > |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="任塿§è¡æ¹å¼" |
| | | align="center" |
| | | key="preachform" |
| | | prop="preachform" |
| | | width="160" |
| | | :show-overflow-tooltip="true" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <span v-for="item in scope.row.preachform" |
| | | >{{ item }}ã |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="visitTime" |
| | | align="center" |
| | | label="åºé访æ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="finishtime" |
| | | align="center" |
| | | label="éè®¿å®ææ¶é´" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="åºé¢æ¥æ" |
| | | width="200" |
| | | align="center" |
| | | key="endtime" |
| | | prop="endtime" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatTime(scope.row.endtime) }}</span> |
| | | </template></el-table-column |
| | | > |
| | | <el-table-column |
| | | label="责任æ¤å£«" |
| | | width="120" |
| | | align="center" |
| | | key="nurseName" |
| | | prop="nurseName" |
| | | /> |
| | | <el-table-column |
| | | label="主治å»ç" |
| | | width="120" |
| | | align="center" |
| | | key="drname" |
| | | prop="drname" |
| | | /> |
| | | :data="infotitlelist" |
| | | :search-name="searchName" |
| | | @close="infotitleVisible = false" |
| | | @search="handleSearch" |
| | | @details-go="SeedetailsgGo" |
| | | /> |
| | | |
| | | <el-table-column |
| | | label="ç»æç¶æ" |
| | | align="center" |
| | | key="excep" |
| | | prop="excep" |
| | | width="120" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <dict-tag |
| | | :options="dict.type.sys_yujing" |
| | | :value="scope.row.excep" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="å¤çæè§" |
| | | align="center" |
| | | key="suggest" |
| | | prop="suggest" |
| | | width="120" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <dict-tag |
| | | :options="dict.type.sys_suggest" |
| | | :value="scope.row.suggest" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | prop="templatename" |
| | | align="center" |
| | | label="æå¡æ¨¡æ¿" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="remark" |
| | | align="center" |
| | | label="æå¡è®°å½" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | prop="bankcardno" |
| | | align="center" |
| | | label="å¼å«ç¶æ" |
| | | width="210" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="æä½" |
| | | fixed="right" |
| | | align="center" |
| | | width="200" |
| | | class-name="small-padding fixed-width" |
| | | > |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="medium" |
| | | type="text" |
| | | @click="SeedetailsgGo(scope.row)" |
| | | ><span class="button-zx" |
| | | ><i class="el-icon-s-order"></i>æ¥ç</span |
| | | ></el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <TimelyRateDialog |
| | | :visible="SeedetailsVisible" |
| | | :loading="Seedloading" |
| | | :data="logsheetlist" |
| | | :total="patienttotal" |
| | | :query-params="patientqueryParams" |
| | | @close="SeedetailsVisible = false" |
| | | @search="Seedetailstion" |
| | | @details-go="SeedetailsgGo" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | toamendtag, |
| | | addapitag, |
| | | deletetag, |
| | | changetagcategory, |
| | | } from "@/api/system/label"; |
| | | import store from "@/store"; |
| | | import { getSfStatistics, selectTimelyRate } from "@/api/system/user"; |
| | | import * as XLSX from "xlsx"; |
| | | import FileSaver from "file-saver"; |
| | | import ExcelJS from "exceljs"; |
| | | import { saveAs } from "file-saver"; |
| | | import Treeselect from "@riophae/vue-treeselect"; |
| | | import "@riophae/vue-treeselect/dist/vue-treeselect.css"; |
| | | const shortcuts = [ |
| | | { |
| | | text: "ä»å¤©", |
| | | onClick(picker) { |
| | | picker.$emit("pick", new Date()); |
| | | }, |
| | | }, |
| | | { |
| | | text: "æ¨å¤©", |
| | | onClick(picker) { |
| | | const date = new Date(); |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24); |
| | | picker.$emit("pick", date); |
| | | }, |
| | | }, |
| | | { |
| | | text: "ä¸å¨å", |
| | | onClick(picker) { |
| | | const date = new Date(); |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24 * 7); |
| | | picker.$emit("pick", date); |
| | | }, |
| | | }, |
| | | ]; |
| | | import FirstFollowUp from './components/FirstFollowUp.vue' |
| | | import SecondFollowUp from './components/SecondFollowUp.vue' |
| | | import ContinuedCare from './components/ContinuedCare.vue' |
| | | import ChartDialog from './components/ChartDialog.vue' |
| | | import DetailDialog from './components/DetailDialog.vue' |
| | | import TimelyRateDialog from './components/TimelyRateDialog.vue' |
| | | |
| | | |
| | | export default { |
| | | name: "Percentage", |
| | | dicts: ["sys_normal_disable", "sys_user_sex"], |
| | | components: { Treeselect }, |
| | | name: 'FollowUpStatistics', |
| | | components: { |
| | | FirstFollowUp, |
| | | SecondFollowUp, |
| | | ContinuedCare, |
| | | ChartDialog, |
| | | DetailDialog, |
| | | TimelyRateDialog |
| | | }, |
| | | data() { |
| | | return { |
| | | topactiveName: "Local", //é¡¶é¨éæ© |
| | | activeName: "first", //ä¾§è¾¹éæ© |
| | | orgname: "", |
| | | expands: [], |
| | | infotitlelist: [], |
| | | currentDisplayList: [], // å½åæ¾ç¤ºçæ°æ® |
| | | loadIndex: 0, // å½åå·²å è½½çæ°æ®ç´¢å¼ |
| | | pageSize: 100, // æ¯æ¬¡å è½½çæ°æ®é |
| | | isLoading: false, // 鲿¢æ»å¨æ¶éå¤å è½½ |
| | | // é®ç½©å± |
| | | loading: false, |
| | | Seedloading: false, |
| | | chartDialogVisible: false, |
| | | infotitleVisible: false, |
| | | searchName: "", // æç´¢å
³é®è¯ |
| | | infotitloading: false, |
| | | infotitle: "", |
| | | pieChart: null, |
| | | barLineChart: null, |
| | | // é䏿°ç» |
| | | ids: [], |
| | | // éå个ç¦ç¨ |
| | | single: true, |
| | | // éå¤ä¸ªç¦ç¨ |
| | | multiple: true, |
| | | // æ¾ç¤ºæç´¢æ¡ä»¶ |
| | | showSearch: true, |
| | | idds: "", //åç±»id |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | flatArrayhospit: [], |
| | | flatArraydept: [], |
| | | patienttotal: 0, |
| | | logsheetlist: [], |
| | | activeTab: 'first', |
| | | orgname: localStorage.getItem('orgname') || '', |
| | | Statisticallist: [ |
| | | { |
| | | label: "ç
åºç»è®¡", |
| | | value: 1, |
| | | }, |
| | | { |
| | | label: "ç§å®¤ç»è®¡", |
| | | value: 2, |
| | | }, |
| | | { label: 'ç
åºç»è®¡', value: 1 }, |
| | | { label: 'ç§å®¤ç»è®¡', value: 2 } |
| | | ], |
| | | patientqueryParams: { |
| | | pn: 1, |
| | | ps: 10, |
| | | }, |
| | | amendtag: false, //æ¯å¦ä¿®æ¹ç±»å« |
| | | lstamendtag: false, //æ¯å¦ä¿®æ¹æ ç¾ |
| | | scavisible: false, //å é¤å¼¹æ¡ |
| | | deleteVisible: false, //åç±»å é¤å¼¹æ¡ |
| | | deletefenl: "é«è¡å", //å é¤é¡¹ |
| | | //ä¿®æ¹æ·»å æ ç¾å¼¹æ¡æ°æ® |
| | | tagform: { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | }, |
| | | classifyform: { |
| | | categoryname: "", |
| | | }, |
| | | // æ ç¾è¡¨æ ¼æ°æ® |
| | | userList: [], |
| | | // å¼¹åºå±æ é¢ |
| | | title: "", |
| | | // æ¯å¦æ¾ç¤ºå¼¹åºå± |
| | | open: false, |
| | | // æ¥æèå´ |
| | | dateRange: [], |
| | | // å²ä½é项 |
| | | postOptions: [], |
| | | // è§è²é项 |
| | | roleOptions: [], |
| | | // å卿æç§å®¤ä»£ç |
| | | allDeptCodes: [], |
| | | // å卿æç
åºä»£ç |
| | | allWardCodes: [], |
| | | checkboxlist: [], |
| | | // 表ååæ° |
| | | form: {}, |
| | | forms: { |
| | | name: "", |
| | | }, |
| | | numberlb: 22, |
| | | dialogFormVisible: false, //æ·»å ãä¿®æ¹ç±»å«å¼¹æ¡ |
| | | lstamendtagVisible: false, //æ·»å ãä¿®æ¹æ ç¾å¼¹æ¡ |
| | | goQRCodeVisible: false, //äºç»´ç å¼¹æ¡ |
| | | sidecolumnval: "", //ç±»å«æç´¢ |
| | | propss: { multiple: true }, |
| | | SeedetailsVisible: false, |
| | | options: store.getters.tasktypes, |
| | | pickerOptions: { |
| | | disabledDate(time) { |
| | | return time.getTime() < Date.now() - 3600 * 1000 * 24; |
| | | }, |
| | | shortcuts: shortcuts, |
| | | }, |
| | | pickerOptionsa: { |
| | | disabledDate(time) { |
| | | return time.getTime() > Date.now(); |
| | | }, |
| | | shortcuts: shortcuts, |
| | | }, |
| | | // æ¥è¯¢æ ç¾åè¡¨åæ° |
| | | options: this.$store.getters.tasktypes, |
| | | queryParams: { |
| | | serviceType: [2], |
| | | dateRange: [], |
| | | statisticaltype: 1, |
| | | leavehospitaldistrictcodes: ["all"], // é»è®¤éä¸å
¨é¨ç
åº |
| | | deptcodes: [], // é»è®¤éä¸å
¨é¨ç§å®¤ |
| | | leavehospitaldistrictcodes: ['all'], |
| | | deptcodes: [] |
| | | }, |
| | | // åä¿¡æ¯ |
| | | columns: [ |
| | | { key: 0, label: `æ ç¾ç¼å·`, visible: true }, |
| | | { key: 1, label: `æ ç¾åç§°`, visible: true }, |
| | | { key: 2, label: `æ ç¾æµç§°`, visible: true }, |
| | | { key: 3, label: `é¨é¨`, visible: true }, |
| | | { key: 4, label: `ææºå·ç `, visible: true }, |
| | | { key: 5, label: `ç¶æ`, visible: true }, |
| | | { key: 6, label: `å建æ¶é´`, visible: true }, |
| | | ], |
| | | }; |
| | | flatArrayhospit: [], |
| | | flatArraydept: [], |
| | | allDeptCodes: [], |
| | | allWardCodes: [], |
| | | showSearch: true, |
| | | |
| | | // å¼¹çªç¸å
³ç¶æ |
| | | chartDialogVisible: false, |
| | | chartData: [], |
| | | infotitleVisible: false, |
| | | SeedetailsVisible: false, |
| | | searchName: '', |
| | | infotitle: '', |
| | | infotitlelist: [], |
| | | patienttotal: 0, |
| | | logsheetlist: [], |
| | | Seedloading: false, |
| | | patientqueryParams: { |
| | | pn: 1, |
| | | ps: 10 |
| | | } |
| | | } |
| | | }, |
| | | watch: {}, |
| | | created() { |
| | | this.getDeptTree(); |
| | | this.getList(); |
| | | this.checkboxlist = store.getters.checkboxlist; |
| | | this.orgname = localStorage.getItem("orgname"); |
| | | this.getDeptTree() |
| | | }, |
| | | |
| | | methods: { |
| | | /** æ¥è¯¢æ ç¾å表 */ |
| | | getList() { |
| | | // å¤çæ¥è¯¢åæ° |
| | | const params = { |
| | | ...this.queryParams, |
| | | // 妿鿩äº"å
¨é¨"ï¼åä¼ ææç
åº/ç§å®¤ä»£ç |
| | | leavehospitaldistrictcodes: |
| | | this.queryParams.leavehospitaldistrictcodes.includes("all") |
| | | ? this.allWardCodes |
| | | : this.queryParams.leavehospitaldistrictcodes, |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | }; |
| | | this.loading = true; |
| | | // ç§»é¤å¯è½åå¨ç"all"å¼ |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | getSfStatistics(params).then((response) => { |
| | | this.loading = false; |
| | | |
| | | this.total = response.total; |
| | | // this.userList = response.data; |
| | | this.userList = this.customSort(response.data); |
| | | }); |
| | | }, |
| | | sortChineseNumber(aRow, bRow) { |
| | | const a = aRow.leavehospitaldistrictname; |
| | | const b = bRow.leavehospitaldistrictname; |
| | | |
| | | // 䏿æ°åå°é¿æä¼¯æ°åçæ å°ï¼æ©å±å°45ï¼ |
| | | const chineseNumMap = { |
| | | ä¸: 1, |
| | | äº: 2, |
| | | ä¸: 3, |
| | | å: 4, |
| | | äº: 5, |
| | | å
: 6, |
| | | ä¸: 7, |
| | | å
«: 8, |
| | | ä¹: 9, |
| | | å: 10, |
| | | åä¸: 11, |
| | | åäº: 12, |
| | | åä¸: 13, |
| | | åå: 14, |
| | | åäº: 15, |
| | | åå
: 16, |
| | | åä¸: 17, |
| | | åå
«: 18, |
| | | åä¹: 19, |
| | | äºå: 20, |
| | | äºåä¸: 21, |
| | | äºåäº: 22, |
| | | äºåä¸: 23, |
| | | äºåå: 24, |
| | | äºåäº: 25, |
| | | äºåå
: 26, |
| | | äºåä¸: 27, |
| | | äºåå
«: 28, |
| | | äºåä¹: 29, |
| | | ä¸å: 30, |
| | | ä¸åä¸: 31, |
| | | ä¸åäº: 32, |
| | | ä¸åä¸: 33, |
| | | ä¸åå: 34, |
| | | ä¸åäº: 35, |
| | | ä¸åå
: 36, |
| | | ä¸åä¸: 37, |
| | | ä¸åå
«: 38, |
| | | ä¸åä¹: 39, |
| | | åå: 40, |
| | | ååä¸: 41, |
| | | ååäº: 42, |
| | | ååä¸: 43, |
| | | ååå: 44, |
| | | ååäº: 45, |
| | | }; |
| | | |
| | | // æå䏿æ°å |
| | | const getNumberFromText = (text) => { |
| | | if (!text || typeof text !== "string") return -1; |
| | | |
| | | // å¹é
䏿æ°åï¼æ¯æä¸å°ååäº |
| | | const match = text.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/); |
| | | |
| | | if (match && match[1]) { |
| | | const chineseNum = match[1]; |
| | | return chineseNumMap[chineseNum] !== undefined |
| | | ? chineseNumMap[chineseNum] |
| | | : -1; |
| | | } |
| | | |
| | | // å¦ææ²¡æå¹é
å°ä¸ææ°åï¼å°è¯å¹é
é¿æä¼¯æ°å |
| | | const arabicMatch = text.match(/^(\d+)/); |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10); |
| | | return num >= 1 && num <= 45 ? num : -1; |
| | | } |
| | | |
| | | return -1; |
| | | }; |
| | | |
| | | const numA = getNumberFromText(a); |
| | | const numB = getNumberFromText(b); |
| | | |
| | | // å¤çæ æ³è§£æçæ
åµ |
| | | if (numA === -1 && numB === -1) { |
| | | return (a || "").localeCompare(b || ""); |
| | | } |
| | | if (numA === -1) return 1; |
| | | if (numB === -1) return -1; |
| | | |
| | | return numA - numB; |
| | | }, |
| | | // æç´¢å¤ç彿° |
| | | handleSearch() { |
| | | if (!this.searchName.trim()) { |
| | | // 妿æç´¢æ¡ä¸ºç©ºï¼æ¾ç¤ºæææ°æ® |
| | | this.currentDisplayList = [...this.infotitlelist]; |
| | | } else { |
| | | // æ ¹æ®æ£è
å§åè¿è¡çéï¼ä¸åºå大å°åï¼ |
| | | const keyword = this.searchName.toLowerCase(); |
| | | this.currentDisplayList = this.infotitlelist.filter((item) => { |
| | | return item.sendname && item.sendname.toLowerCase().includes(keyword); |
| | | }); |
| | | } |
| | | }, |
| | | customSort(data) { |
| | | // å®ä¹æ¨ææçç
åºé¡ºåºï¼æ©å±å°ååäºï¼ |
| | | const order = [ |
| | | "ä¸", |
| | | "äº", |
| | | "ä¸", |
| | | "å", |
| | | "äº", |
| | | "å
", |
| | | "ä¸", |
| | | "å
«", |
| | | "ä¹", |
| | | "å", |
| | | "åä¸", |
| | | "åäº", |
| | | "åä¸", |
| | | "åå", |
| | | "åäº", |
| | | "åå
", |
| | | "åä¸", |
| | | "åå
«", |
| | | "åä¹", |
| | | "äºå", |
| | | "äºåä¸", |
| | | "äºåäº", |
| | | "äºåä¸", |
| | | "äºåå", |
| | | "äºåäº", |
| | | "äºåå
", |
| | | "äºåä¸", |
| | | "äºåå
«", |
| | | "äºåä¹", |
| | | "ä¸å", |
| | | "ä¸åä¸", |
| | | "ä¸åäº", |
| | | "ä¸åä¸", |
| | | "ä¸åå", |
| | | "ä¸åäº", |
| | | "ä¸åå
", |
| | | "ä¸åä¸", |
| | | "ä¸åå
«", |
| | | "ä¸åä¹", |
| | | "åå", |
| | | "ååä¸", |
| | | "ååäº", |
| | | "ååä¸", |
| | | "ååå", |
| | | "ååäº", |
| | | ]; |
| | | |
| | | return data.sort((a, b) => { |
| | | // æåç
åºåç§°ä¸ç䏿æ°åé¨å |
| | | const getIndex = (name) => { |
| | | if (!name || typeof name !== "string") return -1; |
| | | |
| | | // å¹é
䏿æ°å |
| | | const chineseMatch = name.match(/^([ä¸äºä¸åäºå
ä¸å
«ä¹å]+)/); |
| | | if (chineseMatch && chineseMatch[1]) { |
| | | return order.indexOf(chineseMatch[1]); |
| | | } |
| | | |
| | | // å¹é
é¿æä¼¯æ°å |
| | | const arabicMatch = name.match(/^(\d+)/); |
| | | if (arabicMatch && arabicMatch[1]) { |
| | | const num = parseInt(arabicMatch[1], 10); |
| | | if (num >= 1 && num <= 45) { |
| | | return num - 1; // å 为æ°ç»ç´¢å¼ä»0å¼å§ |
| | | } |
| | | } |
| | | |
| | | return -1; |
| | | }; |
| | | |
| | | const indexA = getIndex(a.leavehospitaldistrictname); |
| | | const indexB = getIndex(b.leavehospitaldistrictname); |
| | | |
| | | // æåºé»è¾ |
| | | if (indexA === -1 && indexB === -1) { |
| | | return (a.leavehospitaldistrictname || "").localeCompare( |
| | | b.leavehospitaldistrictname || "" |
| | | ); |
| | | } |
| | | if (indexA === -1) return 1; |
| | | if (indexB === -1) return -1; |
| | | return indexA - indexB; |
| | | }); |
| | | }, |
| | | getRowKey(row) { |
| | | return row.statisticaltype === 1 |
| | | ? row.leavehospitaldistrictcode |
| | | : row.deptcode; |
| | | }, |
| | | |
| | | // å¤çè¡ç¹å»å±å¼ |
| | | handleRowClick(row) { |
| | | console.log(row, "row"); |
| | | |
| | | // 妿已ç»å±å¼åæ¶èµ· |
| | | if (this.expands.includes(this.getRowKey(row))) { |
| | | this.expands = []; |
| | | return; |
| | | } |
| | | // å¤çæ¥è¯¢åæ° |
| | | const params = { |
| | | ...this.queryParams, |
| | | // 妿鿩äº"å
¨é¨"ï¼åä¼ ææç
åº/ç§å®¤ä»£ç |
| | | deptcodes: this.queryParams.deptcodes.includes("all") |
| | | ? this.allDeptCodes |
| | | : this.queryParams.deptcodes, |
| | | leavehospitaldistrictcodes: [row.leavehospitaldistrictcode], |
| | | drcode: "1", |
| | | }; |
| | | |
| | | // ç§»é¤å¯è½åå¨ç"all"å¼ |
| | | delete params.leavehospitaldistrictcodes.all; |
| | | delete params.deptcodes.all; |
| | | // å¦æè¯¥è¡è¿æ²¡æå è½½å»çæ°æ®ï¼åå è½½ |
| | | if (!row.doctorStats) { |
| | | this.loading = true; |
| | | getSfStatistics(params).then((res) => { |
| | | this.$set(row, "doctorStats", res.data); |
| | | this.expands = [this.getRowKey(row)]; |
| | | this.loading = false; |
| | | }); |
| | | } else { |
| | | this.expands = [this.getRowKey(row)]; |
| | | } |
| | | }, |
| | | getSummaries(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å计"; |
| | | return; |
| | | } |
| | | if (index === 1 || index === 2) { |
| | | sums[index] = "/"; |
| | | return; |
| | | } |
| | | |
| | | // 对ç¾åæ¯åæ®µç¹æ®å¤ç - åå¹³åå¼ |
| | | if ( |
| | | column.property === "followUpRate" || |
| | | column.property === "rate" || |
| | | column.property === "followUpRateAgain" |
| | | ) { |
| | | // æåææææç¾åæ¯å¼å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | |
| | | // å¤ç带ç¾åå·çæ°æ® |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | // å»é¤ç¾åå·å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | // å¤çå·²ç»æ¯å°æ°çæ°æ® |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); // è¿æ»¤ænullå0å¼ |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | // æ®éæ°ååæ®µ - æ±å |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | |
| | | // å
é¨è¡¨æ ¼å计è¡è®¡ç®æ¹æ³ |
| | | getInnerSummaries(param) { |
| | | const { columns, data } = param; |
| | | const sums = []; |
| | | |
| | | columns.forEach((column, index) => { |
| | | if (index === 0) { |
| | | sums[index] = "å°è®¡"; |
| | | return; |
| | | } |
| | | |
| | | if (column.property === "drname" || column.property === "deptname") { |
| | | sums[index] = "-"; |
| | | return; |
| | | } |
| | | |
| | | // 对ç¾åæ¯åæ®µç¹æ®å¤ç - åå¹³åå¼ |
| | | if (column.property === "followUpRate" || column.property === "rate") { |
| | | // æåææææç¾åæ¯å¼å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const percentageValues = data |
| | | .map((item) => { |
| | | const value = item[column.property]; |
| | | if (!value || value === "-" || value === "0%") return null; |
| | | |
| | | // å¤ç带ç¾åå·çæ°æ® |
| | | if (typeof value === "string" && value.includes("%")) { |
| | | // å»é¤ç¾åå·å¹¶è½¬æ¢ä¸ºå°æ° |
| | | const numValue = parseFloat(value.replace("%", "")) / 100; |
| | | return isNaN(numValue) ? null : numValue; |
| | | } else { |
| | | // å¤çå·²ç»æ¯å°æ°çæ°æ® |
| | | const numValue = parseFloat(value); |
| | | return isNaN(numValue) ? null : numValue; |
| | | } |
| | | }) |
| | | .filter((value) => value !== null && value !== 0); |
| | | |
| | | if (percentageValues.length > 0) { |
| | | const average = |
| | | percentageValues.reduce((sum, value) => sum + value, 0) / |
| | | percentageValues.length; |
| | | sums[index] = (average * 100).toFixed(2) + "%"; |
| | | } else { |
| | | sums[index] = "0.00%"; |
| | | } |
| | | } else { |
| | | // æ®éæ°ååæ®µ - æ±å |
| | | const values = data.map((item) => { |
| | | const value = item[column.property]; |
| | | if (value === "-" || value === "" || value === null) return 0; |
| | | return Number(value) || 0; |
| | | }); |
| | | |
| | | if (!values.every((value) => isNaN(value))) { |
| | | sums[index] = values.reduce((prev, curr) => prev + curr, 0); |
| | | sums[index] = this.formatNumber(sums[index]); |
| | | } else { |
| | | sums[index] = "-"; |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return sums; |
| | | }, |
| | | |
| | | // è¾
婿¹æ³ï¼æåç¾åæ¯æ°å¼ |
| | | extractPercentageValue(value) { |
| | | if (!value) return null; |
| | | |
| | | if (typeof value === "string") { |
| | | // å¤ç带ç¾åå·çå符串 |
| | | if (value.includes("%")) { |
| | | const num = parseFloat(value.replace("%", "")); |
| | | return isNaN(num) ? null : num / 100; |
| | | } |
| | | // å¤ç纯æ°åå符串 |
| | | const num = parseFloat(value); |
| | | return isNaN(num) ? null : num; |
| | | } |
| | | |
| | | // å¤çæ°åç±»å |
| | | return typeof value === "number" ? value : null; |
| | | }, |
| | | |
| | | // æ°åæ ¼å¼åæ¹æ³ |
| | | formatNumber(num) { |
| | | if (isNaN(num)) return "-"; |
| | | return Number.isInteger(num) ? num.toString() : num.toFixed(0); |
| | | }, |
| | | /** ä¿®æ¹æ ç¾ */ |
| | | handleUpdate(row) { |
| | | console.log(row, "ä¿®æ¹æ ç¾"); |
| | | this.lstamendtagVisible = true; |
| | | this.lstamendtag = true; |
| | | this.tagform = { |
| | | isupload: row.isupload, |
| | | tagname: row.tagname, |
| | | tagcategoryid: row.tagcategoryid, |
| | | tagdescription: row.tagdescription, |
| | | tagid: row.tagid, |
| | | }; |
| | | }, |
| | | // è·åç§å®¤æ |
| | | getDeptTree() { |
| | | // ç§å®¤å表 |
| | | this.flatArraydept = store.getters.belongDepts.map((dept) => { |
| | | this.flatArraydept = this.$store.getters.belongDepts.map((dept) => { |
| | | return { |
| | | label: dept.deptName, |
| | | value: dept.deptCode, |
| | | }; |
| | | }); |
| | | // å卿æç§å®¤ä»£ç |
| | | this.allDeptCodes = store.getters.belongDepts.map( |
| | | (dept) => dept.deptCode |
| | | ); |
| | | value: dept.deptCode |
| | | } |
| | | }) |
| | | this.allDeptCodes = this.$store.getters.belongDepts.map((dept) => dept.deptCode) |
| | | |
| | | // ç
åºå表 |
| | | this.flatArrayhospit = store.getters.belongWards.map((ward) => { |
| | | this.flatArrayhospit = this.$store.getters.belongWards.map((ward) => { |
| | | return { |
| | | label: ward.districtName, |
| | | value: ward.districtCode, |
| | | }; |
| | | }); |
| | | |
| | | // å卿æç
åºä»£ç |
| | | this.allWardCodes = store.getters.belongWards.map( |
| | | (ward) => ward.districtCode |
| | | ); |
| | | this.flatArraydept.push({ label: "å
¨é¨", value: "all" }); |
| | | this.flatArrayhospit.push({ label: "å
¨é¨", value: "all" }); |
| | | }, |
| | | flattenArray(multiArray) { |
| | | let result = []; |
| | | |
| | | // éå½å½æ°ï¼ç¨äºå°å¤çº§æ°ç»è½¬æ¢ä¸ºä¸ç»´æ°ç»ï¼åªå
嫿åºå±çå
ç´ |
| | | function flatten(element) { |
| | | // 妿å½åå
ç´ æåå
ç´ ï¼ç»§ç»éå½ |
| | | if (element.children && element.children.length > 0) { |
| | | element.children.forEach((child) => flatten(child)); |
| | | } else { |
| | | // å
éå
ç´ ä»¥é¿å
ä¿®æ¹åå§æ°æ® |
| | | let item = JSON.parse(JSON.stringify(element)); |
| | | result.push(item); // å°æåºå±çå
ç´ æ·»å å°ç»ææ°ç» |
| | | value: ward.districtCode |
| | | } |
| | | }) |
| | | this.allWardCodes = this.$store.getters.belongWards.map((ward) => ward.districtCode) |
| | | |
| | | this.flatArraydept.push({ label: 'å
¨é¨', value: 'all' }) |
| | | this.flatArrayhospit.push({ label: 'å
¨é¨', value: 'all' }) |
| | | }, |
| | | |
| | | handleTabClick(tab) { |
| | | this.activeTab = tab.name |
| | | this.loadCurrentTabData() |
| | | }, |
| | | |
| | | loadCurrentTabData() { |
| | | switch (this.activeTab) { |
| | | case 'first': |
| | | this.$refs.firstFollowUp.loadData() |
| | | break |
| | | case 'second': |
| | | this.$refs.secondFollowUp.loadData() |
| | | break |
| | | case 'continued': |
| | | this.$refs.continuedCare.loadData() |
| | | break |
| | | } |
| | | }, |
| | | |
| | | handleQuery() { |
| | | this.queryParams.startTime = this.parseTime(this.queryParams.dateRange[0]) |
| | | this.queryParams.endTime = this.parseTime(this.queryParams.dateRange[1]) |
| | | |
| | | if (this.queryParams.statisticaltype == 1) { |
| | | this.queryParams.deptcodes = [] |
| | | } else if (this.queryParams.statisticaltype == 2) { |
| | | this.queryParams.leavehospitaldistrictcodes = [] |
| | | } |
| | | |
| | | // ä»é¡¶å±å
ç´ å¼å§éå½ |
| | | multiArray.forEach((element) => flatten(element)); |
| | | return result; // è¿ååªå
嫿åºå±å
ç´ çä¸ç»´æ°ç» |
| | | this.loadCurrentTabData() |
| | | }, |
| | | addladeltag() { |
| | | this.lstamendtagVisible = true; |
| | | this.lstamendtag = false; |
| | | this.tagform = { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | tagid: "", |
| | | }; |
| | | |
| | | resetQuery() { |
| | | this.queryParams.dateRange = [] |
| | | this.queryParams.leavehospitaldistrictcodes = [] |
| | | this.handleQuery() |
| | | }, |
| | | Seedetails(row) { |
| | | this.SeedetailsVisible = true; |
| | | this.Seedloading = true; |
| | | this.patientqueryParams.starttime = this.parseTime( |
| | | this.queryParams.dateRange[0] |
| | | ); |
| | | this.patientqueryParams.endtime = this.parseTime( |
| | | this.queryParams.dateRange[1] |
| | | ); |
| | | this.patientqueryParams.deptcode = row.deptcode; |
| | | selectTimelyRate(this.patientqueryParams).then((response) => { |
| | | this.logsheetlist = response.data.detail; |
| | | this.patienttotal = response.data.total; |
| | | this.Seedloading = false; |
| | | }); |
| | | |
| | | async handleExport() { |
| | | switch (this.activeTab) { |
| | | case 'first': |
| | | await this.$refs.firstFollowUp.exportTable() |
| | | break |
| | | case 'second': |
| | | await this.$refs.secondFollowUp.exportTable() |
| | | break |
| | | case 'continued': |
| | | await this.$refs.continuedCare.exportTable() |
| | | break |
| | | } |
| | | }, |
| | | Seedetailstion() { |
| | | selectTimelyRate(this.patientqueryParams).then((response) => { |
| | | this.logsheetlist = response.data.detail; |
| | | this.patienttotal = response.data.total; |
| | | this.Seedloading = false; |
| | | }); |
| | | |
| | | showChartDialog() { |
| | | this.chartData = this.getCurrentTabData() |
| | | this.chartDialogVisible = true |
| | | }, |
| | | |
| | | getCurrentTabData() { |
| | | switch (this.activeTab) { |
| | | case 'first': |
| | | return this.$refs.firstFollowUp.tableData |
| | | case 'second': |
| | | return this.$refs.secondFollowUp.tableData |
| | | case 'continued': |
| | | return this.$refs.continuedCare.tableData |
| | | default: |
| | | return [] |
| | | } |
| | | }, |
| | | |
| | | viewDetails(row, title) { |
| | | this.infotitleVisible = true; |
| | | this.infotitle = title; |
| | | this.infotitlelist = row; // å设rowå°±æ¯éè¦å±ç¤ºçè¯¦ç»æ°ç» |
| | | console.log(this.infotitlelist, "this.infotitlelist"); |
| | | |
| | | this.infotitlelist.forEach((item) => { |
| | | let idArray = null; |
| | | |
| | | if (item.preachform) { |
| | | if (item.endtime) { |
| | | item.preachformson = item.preachform; |
| | | idArray = item.preachform.split(","); |
| | | } |
| | | |
| | | item.preachform = idArray.map((value) => { |
| | | // æ¥æ¾id对åºç对象 |
| | | const item = this.checkboxlist.find((item) => item.value == value); |
| | | // 妿æ¾å°å¯¹åºçidï¼è¿ålabelå¼ï¼å¦åè¿ånull |
| | | return item ? item.label : null; |
| | | }); |
| | | } |
| | | }); |
| | | // åå§åå è½½ |
| | | this.loadIndex = 0; |
| | | this.currentDisplayList = []; |
| | | this.$nextTick(() => { |
| | | this.loadMoreData(); |
| | | }); |
| | | this.infotitle = title |
| | | this.infotitlelist = row |
| | | this.infotitleVisible = true |
| | | }, |
| | | loadMoreData() { |
| | | if (this.isLoading) return; |
| | | this.isLoading = true; |
| | | |
| | | // 模æå¼æ¥å è½½ï¼å®é
å¯è½æ¯ç´æ¥åçæ¬å°æ°æ® |
| | | setTimeout(() => { |
| | | console.log(this.infotitlelist, "this.infotitlelist"); |
| | | Seedetails(row) { |
| | | this.SeedetailsVisible = true |
| | | this.Seedloading = true |
| | | |
| | | const nextChunk = this.infotitlelist.slice( |
| | | this.loadIndex, |
| | | this.loadIndex + this.pageSize |
| | | ); |
| | | this.currentDisplayList = this.currentDisplayList.concat(nextChunk); |
| | | this.loadIndex += this.pageSize; |
| | | this.isLoading = false; |
| | | }, 200); |
| | | this.$refs.firstFollowUp.selectTimelyRate(row, this.queryParams.dateRange) |
| | | .then(response => { |
| | | this.logsheetlist = response.data.detail |
| | | this.patienttotal = response.data.total |
| | | this.Seedloading = false |
| | | }) |
| | | }, |
| | | handleScroll(event) { |
| | | const scrollContainer = event.target; |
| | | // 夿æ¯å¦æ»å¨å°åºé¨ |
| | | const isAtBottom = |
| | | scrollContainer.scrollTop + scrollContainer.clientHeight >= |
| | | scrollContainer.scrollHeight - 10; |
| | | |
| | | if ( |
| | | isAtBottom && |
| | | !this.isLoading && |
| | | this.loadIndex < this.infotitlelist.length |
| | | ) { |
| | | this.loadMoreData(); |
| | | } |
| | | Seedetailstion() { |
| | | this.$refs.firstFollowUp.selectTimelyRate(this.patientqueryParams) |
| | | .then(response => { |
| | | this.logsheetlist = response.data.detail |
| | | this.patienttotal = response.data.total |
| | | }) |
| | | }, |
| | | |
| | | SeedetailsgGo(row) { |
| | | this.SeedetailsVisible = false; |
| | | let type = ""; |
| | | if (row.preachformson && row.preachformson.includes("3")) { |
| | | type = 1; |
| | | this.SeedetailsVisible = false |
| | | let type = '' |
| | | if (row.preachformson && row.preachformson.includes('3')) { |
| | | type = 1 |
| | | } |
| | | setTimeout(() => { |
| | | this.$router.push({ |
| | | path: "/followvisit/record/detailpage/", |
| | | path: '/followvisit/record/detailpage/', |
| | | query: { |
| | | taskid: row.taskid, |
| | | patid: row.patid, |
| | | id: row.id, |
| | | Voicetype: type, |
| | | // visitCount: this.topqueryParams.visitCount, |
| | | }, |
| | | }); |
| | | }, 300); |
| | | }, |
| | | // æ·»å /ä¿®æ¹æ ç¾ |
| | | Maintenancetag() { |
| | | if (this.lstamendtag) { |
| | | toamendtag(this.addDateRange(this.tagform)).then((response) => { |
| | | console.log(response); |
| | | this.getList(); |
| | | }); |
| | | } else { |
| | | addapitag(this.addDateRange(this.tagform)).then((response) => { |
| | | console.log(response); |
| | | this.getList(); |
| | | }); |
| | | } |
| | | this.tagform = { |
| | | isupload: "", |
| | | tagname: "", |
| | | tagcategoryid: "", |
| | | tagdescription: "", |
| | | tagid: "", |
| | | }; |
| | | }, |
| | | routerErr(row) { |
| | | console.log(row, "跳转å¼å¸¸"); |
| | | this.$router.push({ |
| | | path: "/followvisit/discharge", |
| | | query: { |
| | | errtype: 1, |
| | | leavehospitaldistrictcode: row.leavehospitaldistrictcode, |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | // 表åéç½® |
| | | reset() { |
| | | this.form = { |
| | | userId: undefined, |
| | | deptId: undefined, |
| | | userName: undefined, |
| | | nickName: undefined, |
| | | password: undefined, |
| | | phonenumber: undefined, |
| | | email: undefined, |
| | | sex: undefined, |
| | | status: "0", |
| | | remark: undefined, |
| | | postIds: [], |
| | | roleIds: [], |
| | | }; |
| | | this.resetForm("form"); |
| | | }, |
| | | // æ ç¾ç¶æä¿®æ¹ |
| | | handleStatusChange(row) { |
| | | console.log(row.isupload); |
| | | let text = row.isupload === "0" ? "å¯ç¨" : "åç¨"; |
| | | this.$modal |
| | | .confirm('确认è¦"' + text + '""' + row.tagname + '"æ ç¾åï¼') |
| | | .then(function () { |
| | | return changetagcategory(row.tagid, row.isupload); |
| | | }) |
| | | .then(() => { |
| | | this.$modal.msgSuccess(text + "æå"); |
| | | }) |
| | | .catch(function () { |
| | | row.isupload = row.isupload === "0" ? "1" : "0"; |
| | | }); |
| | | }, |
| | | /** æç´¢æé®æä½ */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | if (!this.queryParams.dateRange) this.queryParams.dateRange = []; |
| | | if (this.queryParams.statisticaltype == 1) { |
| | | this.queryParams.deptcodes = []; |
| | | } else if (this.queryParams.statisticaltype == 2) { |
| | | this.queryParams.leavehospitaldistrictcodes = []; |
| | | } |
| | | console.log(this.queryParams.dateRange); |
| | | |
| | | this.queryParams.startTime = this.parseTime( |
| | | this.queryParams.dateRange[0] |
| | | ); |
| | | this.queryParams.endTime = this.parseTime(this.queryParams.dateRange[1]); |
| | | this.getList(); |
| | | }, |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.queryParams.dateRange = []; |
| | | this.queryParams.leavehospitaldistrictcodes = []; |
| | | this.handleQuery(); |
| | | }, |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map((item) => item.tagid); |
| | | this.single = selection.length != 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | console.log(row, "å é¤å¼¹çª"); |
| | | const tagids = row.tagid || this.ids; |
| | | console.log(tagids); |
| | | const tagname = row.tagname; |
| | | this.$modal |
| | | .confirm( |
| | | tagname |
| | | ? 'æ¯å¦ç¡®è®¤å 餿 ç¾å称为"' + tagname + '"çæ°æ®é¡¹ï¼' |
| | | : "æ¯å¦ç¡®è®¤å é¤éä¸çæ°æ®é¡¹ï¼" |
| | | ) |
| | | .then(function () { |
| | | return deletetag(tagids); |
| | | }) |
| | | .then(() => { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | // å¯¼åºæ¹æ³ |
| | | // æ¿æ¢æ¨åæ¥ç exportTable æ¹æ³ |
| | | async exportTable() { |
| | | try { |
| | | // 1. è·åå¹¶æ ¼å¼åæ¥æèå´ |
| | | let dateRangeString = ""; // ç¨äºæä»¶å |
| | | let sheetNameSuffix = ""; // ç¨äºå·¥ä½è¡¨å |
| | | |
| | | // æ£æ¥æ¯å¦åå¨éä¸çæ¥æèå´ |
| | | if ( |
| | | this.queryParams.dateRange && |
| | | this.queryParams.dateRange.length === 2 |
| | | ) { |
| | | const startDateStr = this.queryParams.dateRange[0]; // å¼å§æ¥æå符串ï¼ä¾å¦ "2026-01-01 00:00:00" |
| | | const endDateStr = this.queryParams.dateRange[1]; // ç»ææ¥æå符串 |
| | | |
| | | // æ ¼å¼åæ¥æä¸º YYYY-MM-DDï¼å»ææ¶é´é¨åï¼ |
| | | const formatDateForDisplay = (dateTimeStr) => { |
| | | return dateTimeStr.split(" ")[0]; // åç©ºæ ¼åçé¨åï¼å³ "YYYY-MM-DD" |
| | | }; |
| | | |
| | | const startDateFormatted = formatDateForDisplay(startDateStr); |
| | | const endDateFormatted = formatDateForDisplay(endDateStr); |
| | | |
| | | // æå»ºæ¥æèå´å符串 |
| | | dateRangeString = `${startDateFormatted}è³${endDateFormatted}`; |
| | | sheetNameSuffix = `${startDateFormatted}è³${endDateFormatted}`; |
| | | } else { |
| | | // å¦ææ²¡æéæ©æ¥æèå´ï¼å使ç¨å½åæä»½ä½ä¸ºå¤éæ¹æ¡ |
| | | const now = new Date(); |
| | | const currentMonth = now.getMonth() + 1; |
| | | dateRangeString = `${currentMonth}æ`; |
| | | sheetNameSuffix = `${currentMonth}æ`; |
| | | } |
| | | |
| | | // 2. 卿æå»ºæä»¶ååå·¥ä½è¡¨å |
| | | const excelName = `åºé¢é访ç»è®¡è¡¨_${dateRangeString}.xlsx`; |
| | | const worksheetName = `é访ç»è®¡_${sheetNameSuffix}`; // å·¥ä½è¡¨åä¸è½è¶
è¿31个å符[2](@ref) |
| | | // å建æ°çå·¥ä½ç°¿åå·¥ä½è¡¨ |
| | | const workbook = new ExcelJS.Workbook(); |
| | | const worksheet = workbook.addWorksheet(worksheetName); // 使ç¨å¨æå·¥ä½è¡¨å |
| | | // å®ä¹æ ·å¼ï¼æ°å¢æ»æ 颿 ·å¼ï¼ |
| | | const titleStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 16, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFE6F3FF" }, |
| | | }, |
| | | alignment: { |
| | | vertical: "middle", |
| | | horizontal: "center", |
| | | wrapText: true, |
| | | }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | // å®ä¹æ ·å¼ |
| | | const headerStyle = { |
| | | font: { |
| | | name: "微软é
é»", |
| | | size: 11, |
| | | bold: true, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { |
| | | vertical: "middle", |
| | | horizontal: "center", |
| | | wrapText: true, |
| | | }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const cellStyle = { |
| | | font: { |
| | | name: "å®ä½", |
| | | size: 10, |
| | | color: { argb: "FF000000" }, |
| | | }, |
| | | alignment: { |
| | | vertical: "middle", |
| | | horizontal: "center", |
| | | }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | |
| | | const summaryStyle = { |
| | | font: { |
| | | name: "å®ä½", |
| | | size: 10, |
| | | bold: true, |
| | | color: { argb: "FF409EFF" }, |
| | | }, |
| | | fill: { |
| | | type: "pattern", |
| | | pattern: "solid", |
| | | fgColor: { argb: "FFF5F7FA" }, |
| | | }, |
| | | alignment: { |
| | | vertical: "middle", |
| | | horizontal: "center", |
| | | }, |
| | | border: { |
| | | top: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | left: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | bottom: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | right: { style: "thin", color: { argb: "FFD0D0D0" } }, |
| | | }, |
| | | }; |
| | | // 1. æ·»å æ»æ é¢è¡ï¼ç¬¬ä¸è¡ï¼ |
| | | worksheet.mergeCells(1, 1, 1, 23); // åå¹¶A1å°W1çææå[1,4](@ref) |
| | | const titleCell = worksheet.getCell(1, 1); |
| | | titleCell.value = `${sheetNameSuffix}åºé¢é访ç»è®¡è¡¨`; // ä½¿ç¨æä»¶åä½ä¸ºæ»æ é¢ |
| | | titleCell.style = titleStyle; |
| | | worksheet.getRow(1).height = 35; // è®¾ç½®æ»æ é¢è¡é« |
| | | // 1. é¦å
ï¼å建并设置第äºè¡ï¼å表头ï¼çææåå
æ ¼ |
| | | const secondRowHeaders = [ |
| | | "", // A2 å±å¼åå ä½ï¼å
¶å¼å°ç±ç¬¬ä¸è¡åå¹¶åç主åå
æ ¼å³å®ï¼ |
| | | "åºé¢ç
åº", |
| | | "ç§å®¤", |
| | | "åºé¢äººæ¬¡", |
| | | "æ éé访人次", |
| | | "åºé访人次", // B2 to F2 |
| | | // 馿¬¡åºé¢é访å表头 |
| | | "éé访", |
| | | "å¾
é访", |
| | | "é访æå", |
| | | "é访失败", |
| | | "é访ç", |
| | | "åæ¶ç", |
| | | "人工", |
| | | "çä¿¡", |
| | | "微信", |
| | | // 忬¡åºé¢é访å表头 |
| | | "éé访", |
| | | "å¾
é访", |
| | | "é访æå", |
| | | "é访失败", |
| | | "é访ç", |
| | | "人工", |
| | | "çä¿¡", |
| | | "微信", |
| | | ]; |
| | | |
| | | // æ·»å 第äºè¡ï¼å第ä¸è¡ä¸ç§»ï¼ |
| | | secondRowHeaders.forEach((header, index) => { |
| | | const cell = worksheet.getCell(3, index + 1); // æ¹ä¸ºç¬¬3è¡ |
| | | cell.value = header; |
| | | cell.style = headerStyle; |
| | | }); |
| | | |
| | | // 3. è°æ´ååå¹¶åå
æ ¼ä½ç½®ï¼å第1è¡åå¹¶åå
æ ¼ä¸ç§»å°ç¬¬2è¡ï¼ |
| | | // åå¹¶ A2:A3 |
| | | worksheet.mergeCells(2, 1, 3, 1); |
| | | worksheet.getCell(2, 1).value = ""; |
| | | worksheet.getCell(2, 1).style = headerStyle; |
| | | |
| | | // åå¹¶ B2:B3 |
| | | worksheet.mergeCells(2, 2, 3, 2); |
| | | worksheet.getCell(2, 2).value = "åºé¢ç
åº"; |
| | | worksheet.getCell(2, 2).style = headerStyle; |
| | | |
| | | // åå¹¶ C2:C3 |
| | | worksheet.mergeCells(2, 3, 3, 3); |
| | | worksheet.getCell(2, 3).value = "ç§å®¤"; |
| | | worksheet.getCell(2, 3).style = headerStyle; |
| | | |
| | | // åå¹¶ D2:D3 |
| | | worksheet.mergeCells(2, 4, 3, 4); |
| | | worksheet.getCell(2, 4).value = "åºé¢äººæ¬¡"; |
| | | worksheet.getCell(2, 4).style = headerStyle; |
| | | |
| | | // åå¹¶ E2:E3 |
| | | worksheet.mergeCells(2, 5, 3, 5); |
| | | worksheet.getCell(2, 5).value = "æ éé访人次"; |
| | | worksheet.getCell(2, 5).style = headerStyle; |
| | | |
| | | // åå¹¶ F2:F3 |
| | | worksheet.mergeCells(2, 6, 3, 6); |
| | | worksheet.getCell(2, 6).value = "åºé访人次"; |
| | | worksheet.getCell(2, 6).style = headerStyle; |
| | | |
| | | // 4. è°æ´æ¨ªååå¹¶æ é¢ä½ç½®ï¼ä¸ç§»å°ç¬¬2è¡ï¼ |
| | | // 馿¬¡åºé¢é访ï¼åå¹¶G2:O2ï¼ |
| | | worksheet.mergeCells(2, 7, 2, 15); // G2:O2 |
| | | worksheet.getCell(2, 7).value = "馿¬¡åºé¢é访"; |
| | | worksheet.getCell(2, 7).style = headerStyle; |
| | | |
| | | // 忬¡åºé¢é访ï¼åå¹¶P2:W2ï¼ |
| | | worksheet.mergeCells(2, 16, 2, 23); // P2:W2 |
| | | worksheet.getCell(2, 16).value = "忬¡åºé¢é访"; |
| | | worksheet.getCell(2, 16).style = headerStyle; |
| | | |
| | | // 5. 设置è¡é« |
| | | worksheet.getRow(1).height = 35; // æ»æ é¢è¡é« |
| | | worksheet.getRow(2).height = 28; // å第ä¸è¡ä¸ç§» |
| | | worksheet.getRow(3).height = 25; // å第äºè¡ä¸ç§» |
| | | |
| | | // 6. æ·»å æ°æ®è¡ï¼æ³¨æè¡ç´¢å¼éè¦+1ï¼å 为ä¸é¢æå
¥äºä¸è¡ï¼ |
| | | this.userList.forEach((item, rowIndex) => { |
| | | const dataRow = worksheet.addRow( |
| | | [ |
| | | "", // å±å¼å |
| | | item.leavehospitaldistrictname || "", |
| | | item.deptname || "", |
| | | item.dischargeCount || 0, |
| | | item.nonFollowUp || 0, |
| | | item.followUpNeeded || 0, |
| | | // 馿¬¡åºé¢éè®¿æ°æ® |
| | | item.needFollowUp || 0, |
| | | item.pendingFollowUp || 0, |
| | | item.followUpSuccess || 0, |
| | | item.followUpFail || 0, |
| | | item.followUpRate || "0%", |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%", |
| | | item.manual || 0, |
| | | item.sms || 0, |
| | | item.weChat || 0, |
| | | // 忬¡åºé¢éè®¿æ°æ® |
| | | item.needFollowUpAgain || 0, |
| | | item.pendingFollowUpAgain || 0, |
| | | item.followUpSuccessAgain || 0, |
| | | item.followUpFailAgain || 0, |
| | | item.followUpRateAgain || "0%", |
| | | item.manualAgain || 0, |
| | | item.smsAgain || 0, |
| | | item.weChatAgain || 0, |
| | | ], |
| | | rowIndex + 4 |
| | | ); // ä»ç¬¬4è¡å¼å§æ·»å æ°æ®ï¼å第3è¡ï¼ |
| | | |
| | | // åºç¨æ°æ®è¡æ ·å¼ |
| | | dataRow.eachCell((cell) => { |
| | | cell.style = cellStyle; |
| | | }); |
| | | dataRow.height = 24; |
| | | }); |
| | | |
| | | // æ·»å åè®¡è¡ |
| | | const summaries = this.getSummaries({ |
| | | columns: [ |
| | | { property: "" }, |
| | | { property: "leavehospitaldistrictname" }, |
| | | { property: "deptname" }, |
| | | { property: "dischargeCount" }, |
| | | { property: "nonFollowUp" }, |
| | | { property: "followUpNeeded" }, |
| | | { property: "needFollowUp" }, |
| | | { property: "pendingFollowUp" }, |
| | | { property: "followUpSuccess" }, |
| | | { property: "followUpFail" }, |
| | | { property: "followUpRate" }, |
| | | { property: "rate" }, |
| | | { property: "manual" }, |
| | | { property: "sms" }, |
| | | { property: "weChat" }, |
| | | { property: "needFollowUpAgain" }, |
| | | { property: "pendingFollowUpAgain" }, |
| | | { property: "followUpSuccessAgain" }, |
| | | { property: "followUpFailAgain" }, |
| | | { property: "followUpRateAgain" }, |
| | | { property: "manualAgain" }, |
| | | { property: "smsAgain" }, |
| | | { property: "weChatAgain" }, |
| | | ], |
| | | data: this.userList, |
| | | }); |
| | | |
| | | const summaryRow = worksheet.addRow(summaries); |
| | | summaryRow.eachCell((cell, colNumber) => { |
| | | cell.style = summaryStyle; |
| | | // 第ä¸åæ¾ç¤º"å计" |
| | | if (colNumber === 1) { |
| | | cell.value = "å计"; |
| | | Voicetype: type |
| | | } |
| | | }); |
| | | summaryRow.height = 28; |
| | | |
| | | // 设置å宽 |
| | | worksheet.columns = [ |
| | | { width: 8 }, // å±å¼å |
| | | { width: 20 }, // åºé¢ç
åº |
| | | { width: 15 }, // ç§å®¤ |
| | | { width: 12 }, // åºé¢äººæ¬¡ |
| | | { width: 12 }, // æ éé访人次 |
| | | { width: 12 }, // åºé访人次 |
| | | // 馿¬¡åºé¢é访å |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 12 }, |
| | | { width: 12 }, |
| | | { width: 8 }, |
| | | { width: 8 }, |
| | | { width: 8 }, |
| | | // 忬¡åºé¢é访å |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 10 }, |
| | | { width: 12 }, |
| | | { width: 8 }, |
| | | { width: 8 }, |
| | | { width: 8 }, |
| | | ]; |
| | | |
| | | // çæå¹¶ä¸è½½æä»¶ |
| | | const buffer = await workbook.xlsx.writeBuffer(); |
| | | const blob = new Blob([buffer], { |
| | | type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | | }); |
| | | saveAs(blob, excelName); |
| | | |
| | | this.$message.success("å¯¼åºæå"); |
| | | return true; |
| | | } catch (error) { |
| | | console.error("导åºå¤±è´¥:", error); |
| | | this.$message.error(`导åºå¤±è´¥: ${error.message}`); |
| | | return false; |
| | | } |
| | | }) |
| | | }, 300) |
| | | }, |
| | | |
| | | // æ¾ç¤ºå¾è¡¨å¼¹çª |
| | | |
| | | showChartDialog() { |
| | | this.chartDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.initPieChart(); |
| | | this.initBarLineChart(); |
| | | }); |
| | | }, |
| | | // å¨methodsä¸ä¿®æ¹ç»è®¡æ¹æ³ |
| | | showChartDialog() { |
| | | this.chartDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | console.log(this.userList, "this.userList"); |
| | | |
| | | this.initCharts(); |
| | | }); |
| | | }, |
| | | |
| | | // æ°å¢åå§åå¾è¡¨æ¹æ³ |
| | | initCharts() { |
| | | this.initPieChart(); |
| | | this.initBarLineChart(); |
| | | }, |
| | | |
| | | // åå§åé¥¼å¾ |
| | | initPieChart() { |
| | | const echarts = require("echarts"); |
| | | const pieDom = document.getElementById("pieChart"); |
| | | if (!pieDom) return; |
| | | |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose(); |
| | | } |
| | | |
| | | this.pieChart = echarts.init(pieDom); |
| | | |
| | | // 计ç®é¥¼å¾æ°æ® |
| | | const followUpData = { |
| | | pending: 0, |
| | | success: 0, |
| | | fail: 0, |
| | | }; |
| | | |
| | | this.userList.forEach((item) => { |
| | | followUpData.pending += item.pendingFollowUp || 0; |
| | | followUpData.success += item.followUpSuccess || 0; |
| | | followUpData.fail += item.followUpFail || 0; |
| | | }); |
| | | |
| | | // ä½¿ç¨æ´ç¾è§çé¢è²æ¹æ¡ |
| | | const pieOption = { |
| | | title: { |
| | | text: "éè®¿ç¶æåå¸", |
| | | left: "center", |
| | | textStyle: { |
| | | color: "#333", |
| | | fontSize: 16, |
| | | }, |
| | | }, |
| | | tooltip: { |
| | | trigger: "item", |
| | | formatter: "{a} <br/>{b}: {c} ({d}%)", |
| | | }, |
| | | legend: { |
| | | orient: "vertical", |
| | | left: "left", |
| | | data: ["å¾
é访", "é访æå", "é访失败"], |
| | | textStyle: { |
| | | color: "#666", |
| | | }, |
| | | }, |
| | | color: ["#FF9D4D", "#36B37E", "#FF5C5C"], // æ°çé
è²æ¹æ¡ |
| | | series: [ |
| | | { |
| | | name: "éè®¿ç¶æ", |
| | | type: "pie", |
| | | radius: ["40%", "70%"], |
| | | avoidLabelOverlap: true, |
| | | itemStyle: { |
| | | borderRadius: 10, |
| | | borderColor: "#fff", |
| | | borderWidth: 2, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | formatter: "{b}: {c} ({d}%)", |
| | | color: "#333", |
| | | }, |
| | | emphasis: { |
| | | label: { |
| | | show: true, |
| | | fontSize: "18", |
| | | fontWeight: "bold", |
| | | }, |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | data: [ |
| | | { |
| | | value: followUpData.pending, |
| | | name: "å¾
é访", |
| | | }, |
| | | { |
| | | value: followUpData.success, |
| | | name: "é访æå", |
| | | }, |
| | | { |
| | | value: followUpData.fail, |
| | | name: "é访失败", |
| | | }, |
| | | ], |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | this.pieChart.setOption(pieOption); |
| | | window.addEventListener("resize", this.resizePieChart); |
| | | }, |
| | | |
| | | // åå§åæ±ç¶æçº¿å¾ |
| | | initBarLineChart() { |
| | | const echarts = require("echarts"); |
| | | const barDom = document.getElementById("barLineChart"); |
| | | if (!barDom) return; |
| | | |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose(); |
| | | } |
| | | |
| | | this.barLineChart = echarts.init(barDom); |
| | | |
| | | // å夿°æ® |
| | | const categories = this.userList.map( |
| | | (item) => item.leavehospitaldistrictname || item.deptname |
| | | ); |
| | | |
| | | const dischargeData = this.userList.map( |
| | | (item) => item.dischargeCount || 0 |
| | | ); |
| | | const followUpData = this.userList.map( |
| | | (item) => item.followUpNeeded || 0 |
| | | ); |
| | | |
| | | // æ°å¢ä¸¤æ¡æçº¿æ°æ® |
| | | const followUpRateData = this.userList.map((item) => { |
| | | if (!item.followUpRate) return 0; |
| | | // 廿ç¾åå·å¹¶è½¬ä¸ºæ°å |
| | | const rateStr = String(item.followUpRate).replace("%", ""); |
| | | return parseFloat(rateStr) || 0; |
| | | }); |
| | | |
| | | const timelyRateData = this.userList.map((item) => |
| | | item.rate ? (Number(item.rate) * 100).toFixed(2) : 0 |
| | | ); |
| | | |
| | | const option = { |
| | | title: { |
| | | text: "ç§å®¤/ç
åºé访è¶å¿", |
| | | left: "center", |
| | | textStyle: { |
| | | color: "#333", |
| | | fontSize: 16, |
| | | }, |
| | | }, |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "cross", |
| | | crossStyle: { |
| | | color: "#999", |
| | | }, |
| | | }, |
| | | }, |
| | | legend: { |
| | | data: ["åºé¢äººæ¬¡", "åºé访人次", "é访ç(%)", "åæ¶ç(%)"], |
| | | top: "bottom", |
| | | textStyle: { |
| | | color: "#666", |
| | | }, |
| | | }, |
| | | color: ["#5470C6", "#91CC75", "#EE6666", "#9A60B4"], // æ°å¢ç´«è²ç¨äºåæ¶ç |
| | | xAxis: { |
| | | type: "category", |
| | | data: categories, |
| | | axisLabel: { |
| | | interval: 0, |
| | | rotate: 30, |
| | | color: "#666", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: "value", |
| | | name: "人次", |
| | | min: 0, |
| | | axisLabel: { |
| | | color: "#666", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: "#f0f0f0", |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | type: "value", |
| | | name: "ç¾åæ¯(%)", |
| | | min: 0, |
| | | max: 100, |
| | | axisLabel: { |
| | | color: "#666", |
| | | formatter: "{value}%", |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: "#ddd", |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | show: false, |
| | | }, |
| | | }, |
| | | ], |
| | | series: [ |
| | | { |
| | | name: "åºé¢äººæ¬¡", |
| | | type: "bar", |
| | | barWidth: "25%", |
| | | data: dischargeData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0], |
| | | }, |
| | | }, |
| | | { |
| | | name: "åºé访人次", |
| | | type: "bar", |
| | | barWidth: "25%", |
| | | data: followUpData, |
| | | itemStyle: { |
| | | borderRadius: [4, 4, 0, 0], |
| | | }, |
| | | }, |
| | | { |
| | | name: "é访ç(%)", |
| | | type: "line", |
| | | yAxisIndex: 1, |
| | | data: followUpRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 80, |
| | | lineStyle: { |
| | | color: "#EE6666", |
| | | type: "dashed", |
| | | }, |
| | | // label: { |
| | | // position: 'end', |
| | | // formatter: 'ç®æ 80%' |
| | | // } |
| | | }, |
| | | ], |
| | | }, |
| | | }, |
| | | { |
| | | name: "åæ¶ç(%)", |
| | | type: "line", |
| | | yAxisIndex: 1, |
| | | data: timelyRateData, |
| | | symbolSize: 8, |
| | | lineStyle: { |
| | | width: 3, |
| | | type: "dotted", // 使ç¨è线åºå |
| | | }, |
| | | markLine: { |
| | | silent: true, |
| | | data: [ |
| | | { |
| | | yAxis: 90, |
| | | lineStyle: { |
| | | color: "#9A60B4", |
| | | type: "dashed", |
| | | }, |
| | | // label: { |
| | | // position: 'end', |
| | | // formatter: 'ç®æ 90%' |
| | | // } |
| | | }, |
| | | ], |
| | | }, |
| | | }, |
| | | ], |
| | | grid: { |
| | | top: "15%", |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "15%", |
| | | containLabel: true, |
| | | }, |
| | | }; |
| | | |
| | | this.barLineChart.setOption(option); |
| | | window.addEventListener("resize", this.resizeBarLineChart); |
| | | }, |
| | | |
| | | // å¾è¡¨ååºå¼è°æ´æ¹æ³ |
| | | resizePieChart() { |
| | | if (this.pieChart) { |
| | | this.pieChart.resize(); |
| | | } |
| | | }, |
| | | |
| | | resizeBarLineChart() { |
| | | if (this.barLineChart) { |
| | | this.barLineChart.resize(); |
| | | } |
| | | }, |
| | | |
| | | // å¨ç»ä»¶éæ¯æ¶æ¸
ç |
| | | beforeDestroy() { |
| | | // ç§»é¤äºä»¶çå¬ |
| | | window.removeEventListener("resize", this.resizePieChart); |
| | | window.removeEventListener("resize", this.resizeBarLineChart); |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | if (this.pieChart) { |
| | | this.pieChart.dispose(); |
| | | this.pieChart = null; |
| | | } |
| | | if (this.barLineChart) { |
| | | this.barLineChart.dispose(); |
| | | this.barLineChart = null; |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | handleSearch() { |
| | | // æç´¢é»è¾ |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .sidecolumn { |
| | | width: 180px; |
| | | min-height: 100vh; |
| | | text-align: center; |
| | | // display: flex; |
| | | margin-top: 20px; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | background: #edf1f7; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | .sidecolumn-top { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | .top-wj { |
| | | font-size: 20px; |
| | | } |
| | | .top-tj { |
| | | font-size: 18px; |
| | | |
| | | color: rgb(0, 89, 255); |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .center-ss { |
| | | margin-top: 30px; |
| | | .input-with-select { |
| | | height: 40px !important; |
| | | } |
| | | } |
| | | .bottom-fl { |
| | | margin-top: 30px; |
| | | display: center !important; |
| | | } |
| | | } |
| | | .qrcode-dialo { |
| | | text-align: center; |
| | | // display: flex; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | background: #edf1f7; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | .qrcode-text { |
| | | font-size: 20px; |
| | | span { |
| | | margin-left: 20px; |
| | | } |
| | | } |
| | | .qrcode-img { |
| | | width: 300px; |
| | | height: 400px; |
| | | } |
| | | } |
| | | ::v-deep.el-tabs--left, |
| | | .el-tabs--right { |
| | | overflow: hidden; |
| | | align-items: center; |
| | | display: flex; |
| | | } |
| | | ::v-deep.el-input--medium .el-input__inner { |
| | | height: 40px !important; |
| | | } |
| | | ::v-deep.el-tabs--right .el-tabs__active-bar.is-right { |
| | | height: 40px; |
| | | width: 5px; |
| | | left: 0; |
| | | } |
| | | ::v-deep.el-tabs--right .el-tabs__item.is-right { |
| | | display: block; |
| | | text-align: left; |
| | | font-size: 20px; |
| | | } |
| | | // ç¾ååè®¡è¡æ ·å¼ |
| | | ::v-deep .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #f5f7fa; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | |
| | | .cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å
é¨è¡¨æ ¼åè®¡è¡æ ·å¼ |
| | | ::v-deep .inner-table .el-table__footer { |
| | | .el-table__cell { |
| | | background-color: #ecf5ff; |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | |
| | | .cell { |
| | | font-weight: 500; |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ç¾åæ¯åæ®µç¹æ®æ ·å¼ |
| | | .your-table-container |
| | | ::v-deep |
| | | .el-table__footer |
| | | .el-table__cell[data-field="followUpRate"] |
| | | .cell, |
| | | .your-table-container |
| | | ::v-deep |
| | | .el-table__footer |
| | | .el-table__cell[data-field="rate"] |
| | | .cell, |
| | | .your-table-container |
| | | ::v-deep |
| | | .el-table__footer |
| | | .el-table__cell[data-field="followUpRateAgain"] |
| | | .cell { |
| | | color: #e6a23c !important; |
| | | font-weight: 700 !important; |
| | | } |
| | | |
| | | .leftvlue { |
| | | // display: flex; |
| | | // flex: 1; |
| | | // width: 80%; |
| | | // margin-top: 20px; |
| | | margin: 20px; |
| | | padding: 30px; |
| | | .follow-up-statistics { |
| | | padding: 20px; |
| | | background: #ffff; |
| | | border: 1px solid #dcdfe6; |
| | | -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), |
| | | 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | .mulsz { |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | /* ä½¿è¡ææåæé */ |
| | | .el-table__row { |
| | | cursor: pointer; |
| | | } |
| | | /* å
å±å»çè¡¨æ ¼æ ·å¼ */ |
| | | .inner-table { |
| | | // è¡¨å¤´èæ¯è² |
| | | ::v-deep .el-table__header-wrapper { |
| | | background-color: #f0f7ff !important; |
| | | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 0 6px 0 rgba(0, 0, 0, 0.04); |
| | | |
| | | th { |
| | | background-color: #f0f7ff !important; |
| | | .search-section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .tab-section { |
| | | ::v-deep .el-tabs__header { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | ::v-deep .el-tabs__item { |
| | | font-size: 16px; |
| | | padding: 0 20px; |
| | | height: 40px; |
| | | line-height: 40px; |
| | | } |
| | | |
| | | ::v-deep .el-tabs__active-bar { |
| | | height: 3px; |
| | | } |
| | | } |
| | | |
| | | // è¡¨æ ¼è¡èæ¯è² |
| | | ::v-deep .el-table__body-wrapper { |
| | | tr { |
| | | background-color: #f9fbfe !important; |
| | | |
| | | &:hover { |
| | | background-color: #e6f1ff !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // è¾¹æ¡é¢è² |
| | | ::v-deep .el-table--border { |
| | | border-color: #d9e8ff !important; |
| | | |
| | | td, |
| | | th { |
| | | border-color: #d9e8ff !important; |
| | | } |
| | | } |
| | | |
| | | // æé©¬çº¹ææ |
| | | ::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td { |
| | | background-color: #f5f9ff !important; |
| | | } |
| | | } |
| | | /* å±å¼è¡æ ·å¼ */ |
| | | .el-table__expanded-cell { |
| | | padding: 10px 0 !important; |
| | | background: #f8f8f8; |
| | | } |
| | | .document { |
| | | width: 100px; |
| | | height: 50px; |
| | | } |
| | | .data-list { |
| | | max-height: 800px; |
| | | overflow-y: auto; |
| | | } |
| | | .documentf { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | .button-text { |
| | | color: rgb(70, 204, 238); |
| | | } |
| | | .button-textck { |
| | | color: rgb(39, 167, 67); |
| | | } |
| | | .button-textxg { |
| | | color: rgb(35, 81, 233); |
| | | } |
| | | .button-textsc { |
| | | color: rgb(235, 23, 23); |
| | | } |
| | | </style> |
| | |
| | | [process.env.VUE_APP_BASE_API]: { |
| | | // target: `https://www.health-y.cn/lssf`, |
| | | // target: `http://192.168.100.10:8096`, |
| | | // target: `http://192.168.100.10:8094`,//çç«åå¾· |
| | | target: `http://192.168.100.10:8094`,//çç«åå¾· |
| | | // target: `http://192.168.100.10:8095`,//æ°å |
| | | target:`http://localhost:8095`, |
| | | // target: `http://192.168.100.10:8098`,//å¸ä¸ |
| | | // target:`http://localhost:8095`, |
| | | // target:`http://35z1t16164.qicp.vip`, |
| | | // target: `http://192.168.100.183:8095`, |
| | | // target: `http://192.168.101.166:8093`, |