Commit c345acd0 authored by Lowry's avatar Lowry

Merge branch 'chronic-master' of…

Merge branch 'chronic-master' of http://gitlab.yiboshi.com/nightkis1995/frontend-h5 into chronic-master
parents b78c8d16 953c1652
import { fetchBase } from '../fetch.js'
// 根据居民ID查询服务医生
export function getServiceDoc(params) {
return fetchBase({ url: `/chronic-resident/v1/chronic-visit-record/service-doctor`, body: params, loading: true })
}
// 获取IM账号信息
export function getAccount(idCard) {
return fetchBase({ url: `/chronic-resident/v1/chronic-resident-im/refresh-token/${idCard}`, loading: true })
}
<svg viewBox="64 64 896 896" focusable="false"><path d="M854.6 288.7c6 6 9.4 14.1 9.4 22.6V928c0 17.7-14.3 32-32 32H192c-17.7 0-32-14.3-32-32V96c0-17.7 14.3-32 32-32h424.7c8.5 0 16.7 3.4 22.7 9.4l215.2 215.3zM790.2 326L602 137.8V326h188.2z" /></svg>
\ No newline at end of file
<svg viewBox="64 64 896 896" focusable="false"><path d="M696 480H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z" /><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" /></svg>
\ No newline at end of file
......@@ -2,6 +2,7 @@
import 'vant/es/toast/style/index'
import 'vant/es/notify/style/index'
import 'vant/es/dialog/style/index'
import 'vant/es/image-preview/style/index'
// 自定义svg 图标组件
import DocIcon from '@/components/docIcon/index'
......
<template>
<div class="file-view">
<!-- 文件预览 -->
<div v-if="!isImage" class="flex items-center">
<doc-icon type="file-filled" style="color: #999; font-size: 20px"/>
<a class="ml-2" @click.prevent="onDown">下载</a>
</div>
<van-image :src="file.url" v-else @click="viewImage">
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
</div>
</template>
<script>
export default {
props: {
file: {
type: Object,
default: () => ({})
}
},
emits: ['view-image'],
computed: {
isImage() {
const array = ['JPG', 'PNG', 'JEPG', 'GIF']
return array.includes(this.file.type) || array.includes(this.file.ext.toLocaleUpperCase())
}
},
methods: {
onDown() {
window.open(this.file.url, '_blank')
},
viewImage() {
this.$emit('view-image', this.file)
}
}
}
</script>
<style lang="less" scoped>
.file-view {
width: 100px;
}
</style>
<template>
<div class="flex flex-col nim-index" style="height: 100vh">
<div class="py-2 px-3 text-black text-center shrink-0 doc-nav-bar">
<span>专家在线咨询</span>
</div>
<img src="@/assets/image/residentWX/banner.png" alt="" class="shrink-0 w-full">
<div style="color: #8C8C8C;" class="py-2 px-3">以下为您提供慢病筛查的专家,可向其进行慢病咨询</div>
<div class="grow flex">
<div class="h-full list-l">
<div class="p-3 active">
咨询过的专家
</div>
<div v-for="item in orgList" :key="item.unitId"
:class="['p-3', { 'active': item.unitId === activeOrg.unitId }]"
@click="onOrgChange(item)">
<span>{{ item.unitName || '-' }}</span>
</div>
</div>
<div class="h-full p-3 list-r">
<div class="mb-3 p-4 card" v-for="item in innerDoctorList" :key="item.identityCard">
<div class="mb-3">
<span class="text-16 mr-2 font-semibold">{{item.staffName}}</span>
<span>{{item.officeName || '-'}}</span>
</div>
<div class="mb-3">{{item.unitName || '-'}}</div>
<div>
<van-button plain round type="primary"
@click="toSession(item)">咨询医生</van-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { useStore } from '@/residentWX/store'
import { getServiceDoc } from '@/api/residentWX/nim.js'
import { showFailToast } from 'vant'
export default {
data() {
return {
store: useStore(),
orgList: [],
doctorList: [],
activeOrg: {}
}
},
computed: {
userInfo() {
return this.store.userInfo
},
innerDoctorList() {
return this.doctorList.filter(e => e.unitId === this.activeOrg.unitId)
}
},
created() {
this.init()
},
methods: {
init() {
console.log(this.userInfo)
this.load()
},
load() {
getServiceDoc({ residentInfoId: this.userInfo.residentInfoId }).then(res => {
const list = res.data || []
const orgList = []
this.doctorList = list
list.forEach(e => {
if (orgList.some(e => e.unitId === e.unitId)) return
orgList.push({
unitId: e.unitId,
unitName: e.unitName
})
})
this.orgList = orgList
if (orgList.length) {
this.activeOrg = orgList[0]
}
})
},
onOrgChange(item) {
this.activeOrg = item || {}
},
// 聊天页面
toSession(item = {}) {
if (!item.identityCard) {
showFailToast('缺失医生信息')
return
}
const path = `/residentWX/nim/${item.identityCard}`
this.$router.push({
path,
query: {
name: item.staffName
}
})
}
},
}
</script>
<style lang="less" scoped>
@import url('../utils/common.less');
.nim-index {
background: #f8fafc;
}
.list-l {
width: 35%;
background: transparent;
>div {
line-height: 1.5;
}
.active {
background-color: #fff;
}
}
.list-r {
width: 65%;
background: #fff;
.card {
background-color: #E4E8EE;
background: linear-gradient(to bottom, #E5F3FF 0%, #E9FAFC 100%);
border-radius: 8px;
border: 1px solid #E4E8EE;
.van-button {
height: 26px;
line-height: 26px;
font-size: 14px;
}
}
}
</style>
<template>
<div class="flex flex-col session" style="height: 100vh">
<div class="py-2 px-3 text-black text-center shrink-0 head">
<div class="py-2 px-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>{{ targetId }}</span>
<span>{{ targetName }}</span>
</div>
<div class="p-3 grow overflow-y-auto content">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
finished-text="没有更多消息了"
direction="up"
:disabled="scrollDisabled"
@load="loadHistory"
......@@ -18,39 +18,75 @@
<div v-for="item in msgs" :key="item.time"
:class="['flex msg-row', item.from === accountId ? 'self' : 'target']">
<div class="shrink-0 avatar">
<img src="@/assets/image/nim/avatar-doctor.svg" alt="" v-show="item.from !== accountId">
<img src="@/assets/image/residentWX/avatar-doctor.svg" alt="" v-show="item.from !== accountId">
</div>
<div class="msg-cont">
<span v-if="item.type === 'text'">{{ item.text }}</span>
<FileView v-else-if="['image', 'file'].includes(item.type)" :file="item.file"
@viewImage="viewImage"/>
</div>
<div class="msg-cont">{{ item.text }}</div>
<div class="shrink-0 avatar">
<img src="@/assets/image/nim/avatar-man.png" alt="" v-show="item.from === accountId">
<img src="@/assets/image/residentWX/avatar-man.png" alt="" v-show="item.from === accountId">
</div>
</div>
</van-list>
<div ref="bottom"></div>
</div>
<div class="shrink-0 px-3 py-2 footer">
<van-cell-group>
<van-field
<van-field type="textarea" rows="1"
:autosize="{ minHeight: 24, maxHeight: 76 }"
maxlength="500"
v-model="inputValue"
center
>
<template #left-icon>
<div style="color: #555; font-size: .2rem" class="mr-1"
@click="() => visible = true">
<doc-icon type="plus-circle"/>
</div>
</template>
<template #button>
<van-button size="small" type="primary"
@click="sendMsg">发送</van-button>
</template>
</van-field>
</van-cell-group>
</div>
<input type="file"
:accept="sendType == 'image' ? '.jpg,.jpeg,.png,.gif' : '*'"
ref="fileElem"
style="display: none;"
@change="handleFiles"
:key="inputKey">
<van-action-sheet
v-model:show="visible"
:actions="actionsList"
cancel-text="取消"
close-on-click-action
@select="fileSelect"
/>
</div>
</template>
<script>
import { useStore } from '@/residentWX/store'
import NIM from '@yxim/nim-web-sdk/dist/SDK/NIM_Web_NIM.js'
import { showNotify, showToast } from 'vant'
import { showNotify, showToast, showImagePreview, showSuccessToast, showFailToast } from 'vant'
import { getAccount } from '@/api/residentWX/nim.js'
import FileView from './FileView.vue'
// 6c51376a55f54b2fa586d7b4c85757f8
const APPKEY = '8cee1ccf8ab46779976091db68a67955'
export default {
components: {
FileView
},
data() {
return {
store: useStore(),
accountId: '',
token: '',
msgs: [],
nim: null,
// 输入的信息
......@@ -62,15 +98,27 @@ export default {
// 是否加载完成
finished: false,
scrollDisabled: true,
// image file
sendType: 'image',
inputKey: 1,
// 上传功能面板
visible: false,
actionsList: [
{ name: '上传图片', type: 'image' },
{ name: '上传文件', type: 'file' }
]
}
},
computed: {
accountId() {
return this.$route.query.accountId
userInfo() {
return this.store.userInfo
},
// 聊天对象
targetName() {
return this.$route.query.name
},
targetId() {
return this.accountId === '18487350810' ? '18987175004' : '18487350810'
return `doc_${this.$route.params.id}`.toLocaleLowerCase()
}
},
created() {
......@@ -78,13 +126,27 @@ export default {
},
methods: {
init() {
getAccount(this.userInfo.idCard).then(res => {
console.log('getAccount ===========>', res)
const result = res.data || {}
this.accountId = result.accountId
this.token = result.yunxinToken
this.connect()
})
},
// 连接到im服务
connect() {
if (!this.accountId || !this.token) {
showNotify({ type: 'warning', message: '聊天服务连接失败', duration: 0 })
return
}
this.isConnect = false
this.finished = false
this.nim = NIM.getInstance({
debug: true,
appKey: '6c51376a55f54b2fa586d7b4c85757f8',
debug: false,
appKey: APPKEY,
account: this.accountId,
token: '123456',
token: this.token,
onconnect: () => {
console.log('连接成功 ================>')
this.isConnect = true
......@@ -101,12 +163,18 @@ export default {
console.log(obj.duration)
},
ondisconnect: (error) => {
showNotify({ type: 'warning', message: '连接失败', duration: 0 })
// showNotify({ type: 'warning', message: '连接失败', duration: 0 })
console.warn('连接失败 ===============>')
},
onmsg: (msg) => {
console.log('收到新消息===========>', msg);
this.msgs.push(msg)
this.toBottom()
},
onofflinemsgs: (obj) => {
console.log('收到离线消息===========>', obj);
this.msgs.push(...obj.msgs)
this.toBottom()
}
})
},
......@@ -116,6 +184,7 @@ export default {
showToast('不能发送空消息')
return
}
if (!this.nim) return
let msg = this.nim.sendText({
scene: 'p2p',
to: this.targetId,
......@@ -124,18 +193,18 @@ export default {
console.log('sendText ================>', error, msg)
}
})
this.msgs.push(msg)
this.pushMsg(msg)
this.inputValue = ''
this.toBottom()
},
pushMsg(msg) {
this.msgs.push(msg)
this.toBottom()
},
toBottom(behavior = 'smooth') {
const dom = this.$refs.bottom
setTimeout(() => {
dom && dom.scrollIntoView({block: 'start', behavior})
}, 100)
}, 300)
},
// 加载本地消息
getLocalMsgs() {
......@@ -163,24 +232,136 @@ export default {
loadHistory() {
this.getLocalMsgs()
},
onBack() {
handleFiles() {
let files = this.$refs['fileElem'].files
if (files.length <= 0) {
this.$message.info('未选中文件,请尝试重新选择')
return
}
if (files[0].size / 1024 / 1024 > 100) {
this.$message.info('文件大小不能超过100M')
return
}
this.upload(files[0])
},
upload(file, hash) {
if (!this.nim) return
// 先上传再发送
const tempMsg = {
from: this.accountId,
text: '上传中...',
time: +new Date(),
percentage: 0,
type: 'text'
}
this.pushMsg(tempMsg)
this.nim.previewFile({
type: this.sendType,
blob: file,
// fastPass: { md5: hash },
uploadprogress: function(obj) {
console.log('文件总大小: ' + obj.total + 'bytes');
console.log('已经上传的大小: ' + obj.loaded + 'bytes');
console.log('上传进度: ' + obj.percentage);
console.log('上传进度文本: ' + obj.percentageText);
tempMsg.percentage = obj.percentage
},
done: (error, file) => {
this.inputKey = Math.random().toString(16).substring(2, 6)
if (!error) {
const msg = this.nim.sendFile({
scene: 'p2p',
to: this.targetId,
file: file
})
console.log('正在发送p2p image消息, id=' + msg.idClient)
this.pushMsg(msg)
this.msgs = this.msgs.filter(e => e.time !== tempMsg.time)
} else {
const msg = this.nim.sendText({
scene: 'p2p',
to: this.targetId,
text: '文件上传失败!',
isLocal: true,
custom: { type: 'file', status: 'fail' }
})
this.pushMsg(msg)
}
}
})
},
fileSelect(action) {
this.sendType = action.type
const dom = this.$refs['fileElem']
setTimeout(() => {
dom && dom.click()
}, 100)
},
// 图片查看
viewImage(file) {
const currt = file.url
const tempList = this.msgs.filter(e => ['file', 'image'].includes(e.type)).map(e => e.file)
const imageList = []
tempList.forEach(e => {
const array = ['JPG', 'PNG', 'JEPG', 'GIF']
if (array.includes(e.type) || array.includes(e.ext.toLocaleUpperCase())) {
imageList.push(e.url)
}
})
console.log('imageList', imageList)
const index = imageList.indexOf(currt) || 0
showImagePreview({
images: imageList,
startPosition: index,
closeable: true
})
},
clearMsg() {
this.nim.deleteLocalMsgsBySession({
scene: 'p2p',
to: this.targetId,
delLastMsg: true,
done: (error) => {
if (error) {
showFailToast('删除失败')
} else {
showSuccessToast('删除成功')
}
}
});
},
clearAllMsg() {
this.nim.deleteAllLocalMsgs({
done: (error) => {
if (error) {
showFailToast('删除失败')
} else {
showSuccessToast('删除成功')
}
}
})
},
onBack() {
this.$router.replace({ name: 'residentWX-nim' })
}
},
beforeUnmount() {
if (this.nim) {
this.nim.destroy({
done: function (err) {
console.log('nim 断开连接')
}
})
}
}
}
</script>
<style lang="less" scoped>
.head {
position: relative;
border-bottom: 1px solid #3C3C435C;
.back-bt {
position: absolute;
left: .16rem;
top: 50%;
transform: translateY(-50%);
}
}
@import url('../utils/common.less');
.footer {
border-top: 1px solid #3C3C431C;
background-color: #eeeeee66;
......
......@@ -130,17 +130,17 @@ const routes = [
name: 'residentWX-guide-list',
component: () => import(/* webpackChunkName: "residentWX-guide" */ '@/residentWX/guide/List.vue')
},
]
},
{
path: '/nim',
name: 'nim',
component: () => import(/* webpackChunkName: "nim" */ '@/nim/Session.vue'),
path: 'nim',
name: 'residentWX-nim',
component: () => import(/* webpackChunkName: "nim" */ '@/residentWX/nim/Index.vue'),
},
{
path: '/nimTest',
name: 'nimTest',
component: () => import(/* webpackChunkName: "nim" */ '@/nim/Test.vue'),
path: 'nim/:id',
name: 'residentWX-nim-session',
component: () => import(/* webpackChunkName: "nim" */ '@/residentWX/nim/Session.vue'),
}
]
}
]
......
......@@ -39,7 +39,7 @@ module.exports = defineConfig({
}
},
'/chronic-resident': {
target: 'http://192.168.1.174:8903',
target: 'http://192.168.1.145:8903',
// target: 'https://beta-tumour.zmnyjk.com',
changOrigin: true,
pathRewrite: {
......
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