Commit f0c1342c authored by gengchunlei's avatar gengchunlei

Merge branch 'chronic-master' of…

Merge branch 'chronic-master' of http://gitlab.yiboshi.com/nightkis1995/frontend-h5 into chronic-master
parents 69ac2ddd 4adfa9a3
import { fetchBase } from '../fetch.js'
// 查询指导列表
export function queryGuideList(params, loading) {
return fetchBase({ url: `/chronic-resident/v1/chronic-health-guidance/page`, body: params, loading })
}
// 慢病高危筛查详情
export function queryGuideDetail(id) {
return fetchBase({ url: `/chronic-resident/v1/chronic-health-guidance/detail/${id}`, loading: true })
}
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.6432 3.90404C7.26987 4.29571 7.26987 5.20837 6.6432 5.60004L1.53 8.79579C0.863951 9.21207 0 8.73323 0 7.94779V1.55629C0 0.770851 0.863951 0.292008 1.53 0.708288L6.6432 3.90404Z" fill="#5FC3B7"/>
</svg>
......@@ -38,7 +38,9 @@ export default {
cellVerticalPadding: '.15rem',
cellTextColor: '#262626',
fieldLabelColor: '#262626',
cellBorderColor: '#d9d9d9'
cellBorderColor: '#d9d9d9',
// 进度条
sliderActiveBackground: '#54CCBD',
}
}
},
......@@ -54,7 +56,7 @@ export default {
console.log(this.visible)
const res = await getDict()
this.store.$patch({ dict: res.data || {} })
const user = await getResidentByInfo({idCard: '21100319900901301X'})
const user = await getResidentByInfo({idCard: '510215192405265723'})
this.store.$patch({ userInfo: user.data || {} })
this.visible = true
......
<template>
<div class="p-1 flex flex-col mp3">
<div v-for="item in files" :key="item.annexId">
<div v-if="item.annexFileName" class="text-12 mb-1 text-ellipsis">{{ item.annexFileName }}</div>
<div class="flex items-center justify-between gap-x-2.5">
<div class="shrink-0 play-bt" @click="start(item)">
<doc-icon type="doc-play" />
</div>
<span class="shrink-0 time">{{timeFormat(player.duration)}}</span>
<div class="grow progress">
<van-slider v-model="player.currentTime" :max="sliderMax" :bar-height="6" :button-size="0"
@change="onProgress" />
<!-- <div :style="`width: ${progress}%`"></div> -->
</div>
<span class="shrink-0 time" :style="`opacity: ${player.currentTime ? 1 : 0}`">
{{ timeFormat(player.currentTime) }}</span>
</div>
</div>
<audio ref="audio" type="audio/mpeg" crossOrigin="anonymous" style="display: none"></audio>
</div>
</template>
<script>
import { musicPlayer } from './mp3.js'
import { showToast } from 'vant'
export default {
props: {
files: { default: () => [] }
},
data() {
return {
player: {},
// 正在播放的items
activeAudio: {},
}
},
computed: {
sliderMax() {
return this.player.duration ? Math.floor(this.player.duration) : 100
},
progress() {
if (!this.player.currentTime) return 0
const temp = this.player.currentTime / this.player.duration
return Math.round(temp * 1000) / 10
}
},
mounted() {
this.init()
},
methods: {
init() {
this.player = new musicPlayer(this.$refs.audio)
},
start(item) {
if (!item || !item.annexUrl) {
showToast('文件获取失败')
return
}
if (!this.player.audioCtx) {
this.player.init()
}
if (this.player.duration && item.annexId === this.activeAudio.annexId) {
if (this.player.playing) {
this.stop()
} else {
this.player.audioEl.play()
this.player.playing = true
}
return
}
this.player.setSrc(item.annexUrl)
this.activeAudio = item
this.player.playing = true
console.log('this.player', this.player)
},
stop() {
this.player.audioEl.pause()
this.player.playing = false
},
timeFormat(value) {
if (!value) {
return '00:00'
}
let date = Math.ceil(value)
let minutes = Math.floor(date / 60)
let seconds = date % 60
let format = '' + (minutes >= 10 ? minutes : '0' + minutes) + ':' + (seconds >= 10 ? seconds : '0' + seconds)
return format
},
onProgress(value) {
this.player.progressChange(value)
}
}
}
</script>
<style lang="less" scoped>
.mp3 {
background-color: #fff;
.play-bt {
display: inline-flex;
width: 24px;
height: 24px;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: #E4F2F0;
font-size: 10px;
padding-left: 2px;
}
.progress {
// height: 6px;
// border-radius: 4px;
// background: #EFF2F7;
// overflow: hidden;
// text-align: left;
// >div {
// height: 100%;
// background: #54CCBD;
// }
}
}
</style>
<template>
<div class="mp4">
<div class="flex flex-wrap justify-between video-list">
<div v-for="item in files" :key="item.annexId">
<div class="item">
<div class="shrink-0 play-bt" @click="start(item)">
<doc-icon type="doc-play" />
</div>
</div>
<div v-if="item.annexFileName" class="text-12 mb-1 text-ellipsis">{{ item.annexFileName }}</div>
</div>
</div>
<van-popup v-model:show="visible" :close-on-click-overlay="false" closeable>
<video controls width="100%" v-if="visible">
<source :src="activeVideo.annexUrl" type="video/mp4" />
播放失败!
</video>
</van-popup>
</div>
</template>
<script>
export default {
props: {
files: { default: () => [] }
},
data() {
return {
visible: false,
activeVideo: {}
}
},
methods: {
start(item) {
this.activeVideo = item
this.visible = true
}
}
}
</script>
<style lang="less" scoped>
.video-list {
>div {
width: calc(50% - 5px);
.item {
background: url('@/assets/image/residentWX/video-default.png') no-repeat;
height: .84rem;
display: flex;
align-items: center;
justify-content: center;
}
}
.play-bt {
display: inline-flex;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: #E4F2F0;
font-size: 15px;
padding-left: 2px;
}
}
</style>
export class musicPlayer {
constructor(audioEl) {
this.audioEl = audioEl
// 音频上下文
this.audioCtx = null
// 音源
this.audioSource = null
// 时长
this.duration = 0
// 当前播放时间
this.currentTime = 0
// 播放进度
this.progress = 0
// 声音
this.volume = 20
// AnalyserNode 接口表示了一个可以提供实时频域和时域分析信息的节点
this.analyser = null
// 音量节点
this.gainNode = null
// 缓冲进度
this.timeRange = null
// 是否播放中
this.playing = false
}
init() {
// 音频上下文
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)()
if (!this.audioCtx) {
throw new Error('audioCtx is null')
}
// 获取音频数据的节点
this.analyser = this.audioCtx.createAnalyser()
// 音量节点
this.gainNode = this.audioCtx.createGain()
this.gainNode.gain.value = 1
this.audioEl.volume = this.volume / 100
// 从<audio>或<video>元素生成的音频源
this.audioSource = this.audioCtx.createMediaElementSource(this.audioEl)
this.audioEl.ontimeupdate = () => {
this.currentTime = this.audioEl.currentTime
if (this.progress >= this.duration) return
this.progress = this.currentTime
}
this.audioEl.onloadedmetadata = () => {
// 时长
this.duration = this.audioEl.duration
console.log('onloadedmetadata', this.duration)
}
this.audioEl.onprogress = () => {
// 当浏览器正在下载音频/视频时
this.timeRange = this.audioEl.buffered
if (this.timeRange && this.timeRange.length > 0) {
console.log(
'buffered',
this.audioEl.buffered.start(0),
this.audioEl.buffered.end(0)
)
}
}
this.audioEl.oncanplay = () => {
console.log('可播放')
setTimeout(() => {
this.audioEl.play()
this.getAudioSource()
}, 100)
}
this.audioEl.onerror = (e) => {
console.error('加载出现错误', e)
this.onerror(e)
}
}
onerror() {}
onended() {}
setSrc(src) {
this.progress = 0
this.audioEl.src = src
}
// 调整进度
progressChange(value) {
this.audioEl.currentTime = value
}
// 调整音量
volumeChange(value) {
this.volume = value
this.audioEl.volume = this.volume / 100
}
// 音频波形处理
getAudioSource() {
// 节点链接到音源
this.audioSource.connect(this.analyser)
// 链接音量节点
this.analyser.connect(this.gainNode)
this.gainNode.connect(this.audioCtx.destination)
return
// 使用快速傅立叶变换(Fast Fourier Transform (FFT) )来捕获音频数据
// this.analyser.fftSize = 2048
this.analyser.fftSize = 256
let bufferLength = this.analyser.frequencyBinCount
let dataArray = new Uint8Array(bufferLength)
let c = this.boardEl
let canvasWidth = c.width
let canvasHeight = c.height
let ctx = c.getContext('2d')
let left = this
ctx.strokeStyle = 'rgba(81,167,255, .5)'
drawBar()
function drawBar() {
// 波形绘制
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
let barWidth = (canvasWidth / bufferLength) * 1
let barHeight = 0
let x = 0
left.analyser.getByteFrequencyData(dataArray)
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i] / 2
let tempColor = barHeight * 3 > 255 ? 255 : barHeight * 3
ctx.fillStyle = 'rgba(' + tempColor + ', 160, 255, .5)'
ctx.fillRect(x, canvasHeight - barHeight / 2, barWidth, barHeight)
x += barWidth + 0.1
}
requestAnimationFrame(drawBar)
}
}
}
<template>
<div class="flex flex-col" style="height: 100vh">
<div class="p-3 text-black text-center shrink-0 doc-nav-bar">
<span @click="onBack" class="text-12 back-bt">
<doc-icon type="doc-left2" />
</span>
<span>指导详情</span>
</div>
<div class="px-4 py-3 flex shrink-0 base-info">
<div class="flex w-full">
<img src="@/assets/image/residentWX/avatar.png" alt="" class="shrink-0"
style="width: .56rem">
<div class="grow flex flex-col pl-3">
<div class="item">
<span>指导单位:</span>
<span>{{ info.visitUnitName || '-' }}</span>
</div>
<div class="item">
<span>指导科室:</span>
<span>{{ info.visitOfficeName || '-' }}</span>
</div>
<div class="item">
<span>指导日期:</span>
<span>{{ info.visitDate || '-' }}</span>
</div>
</div>
</div>
</div>
<div class="p-3 grow cont-box">
<div class="p-3 h-full cont-inner">
<div class="flex justify-between collapse-head">
<span class="text-16">全部内容</span>
<span @click="toggleAll">
<span v-if="!collapseAll">展开全部</span>
<span v-else>展开收起</span>
<span :class="['ml-2 icon-down', { 'icon-down-expanded': collapseAll }]">
<doc-icon type="doc-down" />
</span>
</span>
</div>
<van-collapse v-model="activeCollapse" ref="collapse" class="doc-collapse"
@change="collapseChange">
<van-collapse-item v-for="collapse in collapseList" :key="collapse.name"
:title="collapse.title" :name="collapse.name">
<template #right-icon>
<doc-icon type="doc-down" />
</template>
<div>
<!-- <div style="color: #262626">指导内容</div> -->
<div v-if="contentList.length" class="flex flex-col card">
<!-- 文本 -->
<div v-for="item in contentList" :key="item.templateMode" class="mb-1 flex"
:style="`order: ${item.templateMode}`">
<span class="shrink-0 mr-1" v-if="item.templateModeTrans != '无'">{{ item.templateModeTrans }} :</span>
<span> {{ item.templateContent }}</span>
</div>
</div>
<div v-if="mp4List.length" class="card mt-2">
<Mp4 :files="mp4List"/>
</div>
<div v-if="mp3List.length" class="card mt-2">
<Mp3 :files="mp3List"/>
</div>
</div>
</van-collapse-item>
</van-collapse>
</div>
</div>
</div>
</template>
<script>
import { showNotify } from 'vant'
import { queryGuideDetail } from '@/api/residentWX/guide.js'
import { useStore } from '@/residentWX/store'
import Mp3 from '../components/mediaPlay/Mp3.vue'
import Mp4 from '../components/mediaPlay/Mp4.vue'
export default {
components: {
Mp3,
Mp4
},
data() {
return {
store: useStore(),
activeCollapse: [],
collapseList: [
{ title: '生活方式指导', name: '1' }
],
// 全部展开、收起
collapseAll: false,
info: {}
}
},
methods: {
onBack() {
this.$router.back()
},
},
computed: {
id() {
return this.$route.params.id
},
userInfo() {
return this.store.userInfo
},
// 文本内容
contentList() {
return this.info?.publicize?.contentList || []
},
// 文件内容
annexList() {
return this.info?.publicize?.annexList || []
},
mp3List() {
return this.annexList.filter(e => e.type == 2)
},
mp4List() {
return this.annexList.filter(e => e.type == 3)
}
},
created() {
if (!this.id) {
showNotify({ type: 'warning', message: '未获取到查询信息', duration: 0 })
return
}
this.init()
},
methods: {
init() {
queryGuideDetail(this.id).then(res => {
this.info = res.data || {}
})
},
// 折叠面板切换
collapseChange(val) {
if (val && val.length === this.collapseList.length) {
this.collapseAll = true
} else {
this.collapseAll = false
}
},
// 全部展开、收起
toggleAll() {
if (this.collapseAll) {
this.activeCollapse = []
} else {
this.activeCollapse = this.collapseList.map(e => e.name)
}
this.collapseAll = !this.collapseAll
},
onBack() {
this.$router.back()
},
}
}
</script>
<style lang="less" scoped>
@import url('../utils/common.less');
.base-info {
background: linear-gradient(to bottom, #DFF5F4 , #fff 50%);
>div {
align-items: flex-start;
}
.item {
margin-bottom: 2px;
>span:nth-child(1) {
color: #8C8C8C;
}
}
}
.cont-box {
background-color: #f9f9f9;
.cont-inner {
background: linear-gradient(to bottom, #DFF5F4 , #fff .6rem);
border-top-left-radius: .08rem;
border-top-right-radius: .08rem;
}
}
.collapse-head {
.icon-down {
vertical-align: middle;
font-size: .12rem;
.svg-icon {
transition: all .2s;
}
}
.icon-down-expanded {
.svg-icon {
transform: rotate(-180deg);
}
}
}
.card {
background-color: #F8FAFC;
padding: 4px 10px;
border-radius: 4px;
color: #4D5665;
}
</style>
......@@ -9,9 +9,10 @@
<van-tab v-for='item in tabList' :key='item.name'
:title='item.title' :name='item.name'></van-tab>
</van-tabs>
<div class='grow overflow-y-auto card-list' ref='list'>
<div class='grow overflow-y-auto' ref='list'>
<van-pull-refresh v-model='loadingRefresh' @refresh='onRefresh'
:disabled='isRefreshDisable'>
:disabled='isRefreshDisable'
class="h-full">
<van-list
v-model:loading='loading'
:finished='finished'
......@@ -19,22 +20,22 @@
:immediate-check='false'
@load='onMore'
>
<div v-for='item in list' :key='item.id' class='p-4 mt-3 card' @click.stop='toDetail(item)'>
<div class='flex items-center'>
<div class='detail-left'>日期</div>
<div class='detail-right'></div>
<div class="p-2 flex flex-col gap-y-2.5 card-list">
<div class="py-3 px-4 card" v-for='item in list' :key="item.id"
@click="toDetail(item)">
<div>
<span class="label">日期</span>
<span>{{ item.visitDate }}</span>
</div>
<div>
<span class="label">筛查单位</span>
<span>{{ item.visitUnitName }}</span>
</div>
<div>
<span class="label">筛查科室</span>
<span>{{ item.visitOfficeName }}</span>
</div>
</div>
<div class='flex items-center'>
<div class='detail-left'>指导单位</div>
<div class='detail-right'></div>
</div>
<div class='flex items-center'>
<div class='detail-left'>指导科室</div>
<div class='detail-right'></div>
</div>
<span class='px-3 float-bt'>健康指导</span>
<span class='px-3 float-bt'>健康宣教</span>
</div>
</van-list>
<div class='text-center shrink-0 empty' v-if='!list.length'>
......@@ -47,15 +48,23 @@
</template>
<script>
import { queryGuideList } from '@/api/residentWX/guide.js'
import { useStore } from '@/residentWX/store'
export default {
data() {
return {
activeTab: undefined,
store: useStore(),
activeTab: '2',
tabList: [
{ title: '健康指导', name: '2' },
{ title: '健康宣教', name: '3' },
],
list: [],
pagination: {
total: 0,
pageIndex: 1,
pageSize: 4
pageSize: 8
},
loading: false,
finished: false,
......@@ -64,63 +73,95 @@ export default {
}
},
computed: {
tabList() {
const list = [
{ title: '全部', name: '' },
{ title: '健康指导', name: '' },
{ title: '健康宣教', name: '' },
]
return list
}
userInfo() {
return this.store.userInfo
},
},
created() {
this.init()
},
mounted() {
const list = this.$refs.list
list.addEventListener('scroll', () => {
if (list.scrollTop > 0) {
this.isRefreshDisable = true
} else {
this.isRefreshDisable = false
}
})
},
methods: {
load() {
init() {
this.load()
},
load(loading = true) {
const query = {
visitWayRules: this.activeTab,
residentInfoId: this.userInfo.residentInfoId,
pageIndex: this.pagination.pageIndex,
pageSize: this.pagination.pageSize
}
queryGuideList(query, loading).then(res => {
if (this.pagination.pageIndex === 1) {
this.list = []
}
this.list = this.list.concat(res.data.dataList || [])
this.pagination.total = res.data.total || 0
this.finished = this.list.length >= this.pagination.total
}).finally(() => {
this.loading = false
this.loadingRefresh = false
})
},
onMore() {
this.pagination.pageIndex++
this.load()
},
onRefresh() {
this.list = []
this.pagination.pageIndex = 1
this.load(false)
},
tabChange() {
this.list = []
this.pagination.pageIndex = 1
this.load()
},
toDetail(record) {
if (!record) return
let path = `/residentWX/guide/list/${record.id}`
this.$router.push({ path })
}
}
}
</script>
<style lang="less" scoped>
.card-list {
padding-bottom: 76px;
@import url('../utils/common.less');
:deep(.van-tab__text) {
font-weight: 500;
}
.guide-list {
background-color: #f9f9f9;
}
.card-list {
padding-bottom: .3rem;
}
.card {
position: relative;
background-color: #fff;
.detail-left {
width: 8em;
color: #8C8C8C;
flex-shrink: 0;
border-radius: 12px;
>div {
position: relative;
display: flex;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
}
.float-bt {
position: absolute;
top: 10px;
right: 0;
background-color: var(--van-primary-color);
color: #fff;
border-top-left-radius: 40px;
border-bottom-left-radius: 40px;
height: 26px;
line-height: 26px;
.label {
min-width: 6em;
color: #8C8C8C;
}
}
</style>
......@@ -110,6 +110,9 @@ export default {
pageSize: this.pagination.pageSize
}
queryScreenList(query, loading).then(res => {
if (this.pagination.pageIndex === 1) {
this.list = []
}
this.list = this.list.concat(res.data.dataList || [])
this.pagination.total = res.data.total || 0
this.finished = this.list.length >= this.pagination.total
......@@ -123,12 +126,10 @@ export default {
this.load()
},
onRefresh() {
this.list = []
this.pagination.pageIndex = 1
this.load(false)
},
tabChange() {
this.list = []
this.pagination.pageIndex = 1
this.load()
},
......
......@@ -38,10 +38,10 @@
&::after {
display: none;
}
}
.svg-icon {
font-size: .12rem;
transition: all .2s;
.svg-icon {
font-size: .12rem;
transition: all .2s;
}
}
.van-collapse-item {
.van-collapse-item__content {
......
......@@ -130,15 +130,20 @@ const routes = [
name: 'residentWX-guide-list',
component: () => import(/* webpackChunkName: "residentWX-guide" */ '@/residentWX/guide/List.vue')
},
{
path: 'guide/list/:id',
name: 'residentWX-guide-Detail',
component: () => import(/* webpackChunkName: "residentWX-guide" */ '@/residentWX/guide/Detail.vue')
},
{
path: 'nim',
name: 'residentWX-nim',
component: () => import(/* webpackChunkName: "nim" */ '@/residentWX/nim/Index.vue'),
component: () => import(/* webpackChunkName: "residentWX-nim" */ '@/residentWX/nim/Index.vue'),
},
{
path: 'nim/:id',
name: 'residentWX-nim-session',
component: () => import(/* webpackChunkName: "nim" */ '@/residentWX/nim/Session.vue'),
component: () => import(/* webpackChunkName: "residentWX-nim" */ '@/residentWX/nim/Session.vue'),
}
]
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment