Compare commits

..
20 Commits
Author SHA1 Message Date
zoujiandong bafde6e744 Merge branch 'dev' 4.8 2026-04-08 09:04:32 +08:00
zoujiandong c7dbbfda16 4.7提交 2026-04-07 14:32:00 +08:00
zoujiandong fd78b504a6 4.3提交 2026-04-03 17:32:25 +08:00
zoujiandong a26fc1a4a7 4.2 2026-04-02 14:49:21 +08:00
zoujiandong ab812a2bc6 解决滋滋声 2026-04-02 11:37:44 +08:00
zoujiandong 5d31c3658b 4.2提交 2026-04-02 11:30:39 +08:00
zoujiandong 3140db227c 3.31更新兑换积分 2026-03-31 15:05:38 +08:00
zoujiandong 5f9a346960 3.31画像提交 2026-03-31 13:07:16 +08:00
zoujiandong 7180e176eb 3.30解决bug 2026-03-30 16:12:38 +08:00
zoujiandong e3a852cb43 3.30提交 2026-03-30 09:45:55 +08:00
zoujiandong 0a51901807 3.26 2026-03-26 17:34:15 +08:00
zoujiandong 8a879df4b4 3.25提交 2026-03-25 17:33:54 +08:00
zoujiandong 18c7f7386d 3.18提交 2026-03-18 17:19:05 +08:00
zoujiandong b5cc11ddd3 3.17 2026-03-17 18:59:46 +08:00
haomingming 4a22470f38 Merge branch 'master' of https://gitea.igandanyiyuan.com/gdxz/uniapp-app
# Conflicts:
#	pages_app/patientMsg/patientMsg.vue
2026-03-17 14:53:44 +08:00
haomingming 6cdd8b90db patientList 2026-03-17 14:49:50 +08:00
zoujiandong cbc427e41c 3.17上午提交 2026-03-17 14:02:39 +08:00
zoujiandong 45a9478790 3.16 2026-03-16 14:35:45 +08:00
haomingming c6ce198c31 Merge branch 'master' of https://gitea.igandanyiyuan.com/gdxz/uniapp-app 2026-03-16 14:34:53 +08:00
haomingming f4fe516224 webview 2026-03-16 14:34:46 +08:00
68 changed files with 8969 additions and 1684 deletions
+8
View File
@@ -1,4 +1,5 @@
<script> <script>
import version from '@/utils/version.js'
export default { export default {
globalData: { globalData: {
plAd: true, plAd: true,
@@ -20,6 +21,13 @@ export default {
options.referrerInfo.extraData.token options.referrerInfo.extraData.token
); );
uni.setStorageSync("userInfo", options.referrerInfo.extraData.userInfo); uni.setStorageSync("userInfo", options.referrerInfo.extraData.userInfo);
if(process.env.UNI_PLATFORM =="mp-weixin"){
uni.setStorageSync("app_version",version);
}else{
let app_version=options.referrerInfo.extraData.app_version?options.referrerInfo.extraData.app_version:version;
uni.setStorageSync("app_version",app_version);
};
//getApp({allowDefault: true}).globalData.apiHost = options.referrerInfo.extraData.apiHost; //getApp({allowDefault: true}).globalData.apiHost = options.referrerInfo.extraData.apiHost;
} }
if ( if (
+3
View File
@@ -336,6 +336,9 @@ const api = {
applyList(data){ applyList(data){
return request('/expertAPI/applyList', data, 'post', false); return request('/expertAPI/applyList', data, 'post', false);
}, },
patientListByGBKPage(data){
return request('/expertAPI/patientListByGBKPage', data, 'post', false);
},
patientListByGBK(data){ patientListByGBK(data){
return request('/expertAPI/patientListByGBK', data, 'post', false); return request('/expertAPI/patientListByGBK', data, 'post', false);
}, },
+2 -2
View File
@@ -2,8 +2,8 @@
"name" : "肝胆相照专家版", "name" : "肝胆相照专家版",
"appid" : "__UNI__89F511F", "appid" : "__UNI__89F511F",
"description" : "", "description" : "",
"versionName" : "4.2.5", "versionName" : "4.2.7",
"versionCode" : 425, "versionCode" : 427,
"transformPx" : false, "transformPx" : false,
"app-plus" : { "app-plus" : {
/* 5+App特有相关 */ /* 5+App特有相关 */
+42 -11
View File
@@ -752,6 +752,36 @@
} }
} }
}, },
{
"path": "patientMsg/patientList",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "patientMsg/patientPlan",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "patientMsg/patientListPerf",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{ {
"path": "patientGroup/patientGroup", "path": "patientGroup/patientGroup",
"style": { "style": {
@@ -792,6 +822,16 @@
} }
} }
}, },
{
"path": "searchPatient/searchPatient",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{ {
"path": "visitPlan/visitPlan", "path": "visitPlan/visitPlan",
"style": { "style": {
@@ -1127,16 +1167,7 @@
// } // }
// }, // },
// {
// "path": "searchPatient/searchPatient",
// "style": {
// "navigationStyle": "custom",
// "navigationBarTitleText": "uni-app分页",
// "app": {
// "bounce": "none"
// }
// }
// },
// { // {
// "path": "qikan/qikan", // "path": "qikan/qikan",
// "style": { // "style": {
@@ -1709,7 +1740,7 @@
"list": [ "list": [
{ {
"name": "", "name": "",
"path": "pages_app/patientMsg/patientMsg", "path": "pages/patientClass/patientClass",
"query": "" "query": ""
} }
] ]
+20 -8
View File
@@ -305,6 +305,15 @@ onUnload (() => {
from.value=options.from; from.value=options.from;
} }
// #ifdef APP-PLUS // #ifdef APP-PLUS
// uni.sendNativeEvent("portraitReport",{
// msg:{
// event_nickname: "list_page",
// page_type: "会议直播",
// },
// },
// (ret) => {
// console.log(ret);
// });
permissionListener.value=uni.createRequestPermissionListener(); permissionListener.value=uni.createRequestPermissionListener();
console.log('放在onLoad执行'); console.log('放在onLoad执行');
console.log(permissionListener.value); console.log(permissionListener.value);
@@ -379,6 +388,7 @@ const generateMonthList = () => {
console.log("生成的月份列表:", monthList.value); console.log("生成的月份列表:", monthList.value);
}; };
const goDetail = (item) => { const goDetail = (item) => {
console.log(item);
// uni.sendNativeEvent('noticeBack', { // uni.sendNativeEvent('noticeBack', {
// msg: 'go' // msg: 'go'
// }, ret => { // }, ret => {
@@ -388,7 +398,7 @@ const goDetail = (item) => {
const encoded = encodeURIComponent(item.path); const encoded = encodeURIComponent(item.path);
let imgPath=encodeURIComponent('https://doc.igandan.com/app/html/img/2016/20160714132557.png'); let imgPath=encodeURIComponent('https://doc.igandan.com/app/html/img/2016/20160714132557.png');
uni.navigateTo({ uni.navigateTo({
url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live_yugao&imgPath=${imgPath}&share=1&title=会议详情`, url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live_yugao&imgPath=${imgPath}&share=1&title=会议详情&tags=${item.tags}`,
}); });
}; };
const goBack = () => { const goBack = () => {
@@ -631,6 +641,7 @@ const meetingListBySearch = async (isRefresh = false) => {
poster: item.liveimg, // 使用 liveimg 作为海报 poster: item.liveimg, // 使用 liveimg 作为海报
time: timeStr, time: timeStr,
location: item.location || "线上", location: item.location || "线上",
tags: item.tags || '',
status: getStatusText(item.status), status: getStatusText(item.status),
// 保留原始数据用于其他功能 // 保留原始数据用于其他功能
originalData: item, originalData: item,
@@ -744,6 +755,7 @@ const getMeetingList = async (isRefresh = false) => {
liveurl: item.liveurl, liveurl: item.liveurl,
spareurl: item.spareurl, spareurl: item.spareurl,
path: item.path, path: item.path,
tags: item.tags || '',
status_code: item.status, status_code: item.status,
}; };
}); });
@@ -937,12 +949,12 @@ const selectProvince = (province) => {
// 播放视频 // 播放视频
const playVideo = (item) => { const playVideo = (item) => {
uni.sendNativeEvent('noticeBack', { // uni.sendNativeEvent('noticeBack', {
msg: 'go' // msg: 'go'
}, ret => { // }, ret => {
console.log('ret数据'); // console.log('ret数据');
console.log(ret); // console.log(ret);
}) // })
if(item.status_code==1 || item.status_code==2 || item.status_code==4){ if(item.status_code==1 || item.status_code==2 || item.status_code==4){
let zhiboUrl=OTHER_HOST+'/hcp/setInfo'; let zhiboUrl=OTHER_HOST+'/hcp/setInfo';
let userInfo=uni.getStorageSync('userInfo'); let userInfo=uni.getStorageSync('userInfo');
@@ -953,7 +965,7 @@ const playVideo = (item) => {
const encoded = encodeURIComponent(tempUrl); const encoded = encodeURIComponent(tempUrl);
let imgPath=encodeURIComponent(docUrl+item.liveimg); let imgPath=encodeURIComponent(docUrl+item.liveimg);
uni.navigateTo({ uni.navigateTo({
url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live&imgPath=${imgPath}&share=1&title=会议直播`, url: `/pages_app/webview/webview?url=${encoded}&sharetitle=${item.title}&bg=1&type=live&imgPath=${imgPath}&share=1&title=会议直播&tags=${item.tags}`,
}); });
}else{ }else{
goDetail(item); goDetail(item);
+13 -4
View File
@@ -122,7 +122,7 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { onShow } from "@dcloudio/uni-app"; import {onShow,onLoad,onBackPress } from "@dcloudio/uni-app";
import CustomTabbar from '@/components/tabBar/tabBar.vue'; import CustomTabbar from '@/components/tabBar/tabBar.vue';
import upImg from "@/static/cb_up.png" import upImg from "@/static/cb_up.png"
import downImg from "@/static/cb_up.png" import downImg from "@/static/cb_up.png"
@@ -131,7 +131,6 @@
import api from "@/api/api.js" import api from "@/api/api.js"
import docUrl from '@/utils/docUrl.js'; import docUrl from '@/utils/docUrl.js';
import navTo from '@/utils/navTo.js'; import navTo from '@/utils/navTo.js';
import {onBackPress} from '@dcloudio/uni-app';
import navBar from "@/components/navBar/navBar.vue" import navBar from "@/components/navBar/navBar.vue"
import formatNumber from '@/utils/formatNumber.js'; import formatNumber from '@/utils/formatNumber.js';
const type=ref(1) const type=ref(1)
@@ -139,7 +138,17 @@
plus.runtime.quit(); plus.runtime.quit();
return true; return true;
}); });
onLoad(() => {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "患教文库",
},
},
(ret) => {
console.log(ret);
});
})
// 分页相关状态 // 分页相关状态
const currentPage = ref(1); const currentPage = ref(1);
const pageSize = ref(10); const pageSize = ref(10);
@@ -288,7 +297,7 @@
// 跳转到详情页 // 跳转到详情页
if (article.path) { if (article.path) {
uni.navigateTo({ uni.navigateTo({
url: `/pages_app/webview/webviewClass?url=${encodeURIComponent(docUrl+article.path+'?fromtype=doctor')}&title=患教详情&uuid=${article.uuid}&sharetitle=${article.title}&summary=${article.summary}&type=huanjiao&imgPath=${encodeURIComponent(article.image)}` url: `/pages_app/webview/webviewClass?url=${encodeURIComponent(docUrl+article.path+'?fromtype=doctor')}&title=患教详情&uuid=${article.uuid}&sharetitle=${article.title}&summary=${article.summary}&type=huanjiao&imgPath=${encodeURIComponent(article.image)}&tags=${article.tags}`
}); });
} }
}; };
+17 -1
View File
@@ -104,6 +104,7 @@ import unidialog from '@/components/dialog/dialog.vue';
const freeVisible = ref(false) const freeVisible = ref(false)
const freeContent = ref('') const freeContent = ref('')
const goods_order_id = ref('') const goods_order_id = ref('')
const goodsName = ref('');
import navTo from '@/utils/navTo'; import navTo from '@/utils/navTo';
const freeClose = () => { const freeClose = () => {
freeVisible.value = false freeVisible.value = false
@@ -210,7 +211,8 @@ onLoad((opts) => {
console.log(22); console.log(22);
console.log(opts); console.log(opts);
goodsUuid.value = opts.goodsUuid goodsUuid.value = opts.goodsUuid
goodsNum.value = opts.goodsNum goodsNum.value = opts.goodsNum;
goodsName.value = opts.goodsName;
getAddress() getAddress()
}) })
@@ -240,6 +242,20 @@ const submit = () => {
if (!/^1\d{10}$/.test(mobile.value)) return uni.showToast({ title: '请输入正确手机号', icon: 'none' }) if (!/^1\d{10}$/.test(mobile.value)) return uni.showToast({ title: '请输入正确手机号', icon: 'none' })
if (!regionText.value) return uni.showToast({ title: '请选择地址', icon: 'none' }) if (!regionText.value) return uni.showToast({ title: '请选择地址', icon: 'none' })
if (!detail.value) return uni.showToast({ title: '请输入详细地址', icon: 'none' }) if (!detail.value) return uni.showToast({ title: '请输入详细地址', icon: 'none' })
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "boint_jifengood",
page_type: "积分商城",
resource_name:goodsName.value,
event_val:0,
event_unit:'积分',
number:goodsNum.value,
belong:'U盘',
},
},
(ret) => {
console.log(ret);
});
saveAddress(); saveAddress();
api.createGoodsOrder({ api.createGoodsOrder({
goodsUuid: goodsUuid.value, goodsUuid: goodsUuid.value,
+1 -1
View File
@@ -105,7 +105,7 @@
// TODO: 调用兑换接口 // TODO: 调用兑换接口
navTo({ navTo({
url:'/pages_app/buyUpan/address?goodsUuid='+goodsUuid.value+'&goodsNum='+quantity.value url:'/pages_app/buyUpan/address?goodsUuid='+goodsUuid.value+'&goodsNum='+quantity.value+'&goodsName='+goodsInfo.value.name
}) })
}; };
+1 -1
View File
@@ -83,7 +83,7 @@ const loadAllGroups = async () => {
try { try {
const res = await api.groupList(); const res = await api.groupList();
if (res.code === 200) { if (res.code === 200) {
const hiddenGroupNames = new Set(['待分组患者', '肝系医生非患教组', '肝系医生患教组']); const hiddenGroupNames = new Set(['待分组患者', '肝系一生非患教组', '肝系一生患教组']);
const arr = (res.data || []).filter((item) => !hiddenGroupNames.has(String(item?.name || '').trim())); const arr = (res.data || []).filter((item) => !hiddenGroupNames.has(String(item?.name || '').trim()));
allGroups.value = arr; allGroups.value = arr;
} }
+26
View File
@@ -384,6 +384,20 @@ const downLoad = (item) => {
}); });
return; return;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "magazine_donwload",
page_type: "诊疗指南",
resource_name:item.title,
belong:title.value,
number:1,
resource_option:item.tags
},
},
(ret) => {
console.log(ret);
});
addDownloadTask({ addDownloadTask({
url: docUrl + item.path, url: docUrl + item.path,
title: item.title, title: item.title,
@@ -406,6 +420,18 @@ const viewGuideline = (cell) => {
console.log(downloadTasks.value); console.log(downloadTasks.value);
downloadTasks.value.forEach(item => { downloadTasks.value.forEach(item => {
if(item.id == cell.uuid) { if(item.id == cell.uuid) {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "detail_page",
page_type: "诊疗指南",
resource_name:cell.title,
resource_option:cell.tags
},
},
(ret) => {
console.log(ret);
});
// if(isAndroid){ // if(isAndroid){
plus.runtime.openFile(item.localPath, function(e) { plus.runtime.openFile(item.localPath, function(e) {
console.log('打开成功'); console.log('打开成功');
+13 -2
View File
@@ -24,7 +24,7 @@
<uni-swipe-action-item <uni-swipe-action-item
v-for="(card, index) in bankCards" v-for="(card, index) in bankCards"
:key="card.uuid" :key="card.uuid"
style="margin-bottom: 20rpx;"
@change="onSwipeChange($event, card)" @change="onSwipeChange($event, card)"
> >
<template v-slot:right> <template v-slot:right>
@@ -82,6 +82,12 @@ onLoad((options) => {
if (options.returnTo) { if (options.returnTo) {
returnTo.value = decodeURIComponent(options.returnTo); returnTo.value = decodeURIComponent(options.returnTo);
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "常用银行卡",
},
});
}); });
onShow(() => { onShow(() => {
getBankCardList(); getBankCardList();
@@ -140,7 +146,9 @@ const bankLogoMap = {
}; };
const getBankLogoImg = (bankName) => { const getBankLogoImg = (bankName) => {
return bankLogoMap[bankName] || ''; let name1=bankName.split(',')[0];
let name2=bankName.split(',')[1];
return bankLogoMap[name1] || bankLogoMap[name2];
}; };
const handleDeleteBankCard = (card) => { const handleDeleteBankCard = (card) => {
@@ -232,6 +240,9 @@ const getBankLogo = (bankName) => {
:deep(.uni-nav-bar-right-text) { :deep(.uni-nav-bar-right-text) {
font-size: 32rpx; font-size: 32rpx;
} }
:deep(.uni-swipe){
margin-bottom: 20rpx!important;
}
} }
/* 银行卡列表样式 */ /* 银行卡列表样式 */
+4 -2
View File
@@ -301,7 +301,7 @@ const onNextStep = async () => {
} }
} catch (error) { } catch (error) {
console.error('身份验证失败:', error); console.error('身份验证失败:', error);
uni.showToast({ title: res.message, icon: 'none' }); uni.showToast({ title: error.data.message || error.message, icon: 'none' });
isLoading.value = false; isLoading.value = false;
} }
} else if (currentStep.value === 2) { } else if (currentStep.value === 2) {
@@ -351,7 +351,9 @@ const onGetSmsCode = async () => {
} }
}, 1000); }, 1000);
} else { } else {
uni.showToast({ title: res.msg || '短信验证码发送失败', icon: 'none' }); uni.showToast({
title: res.msg || res.data.message, icon: 'none'
});
} }
}; };
+23 -1
View File
@@ -178,6 +178,14 @@
if(res.code==200){ if(res.code==200){
msgList.value.forEach(item => { msgList.value.forEach(item => {
if(item.id==id){ if(item.id==id){
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "read_message",
},
},
(ret) => {
console.log(ret);
});
item.is_read=1; item.is_read=1;
} }
}); });
@@ -192,7 +200,17 @@
if (res.confirm) { if (res.confirm) {
api.appMesageRead().then(res => { api.appMesageRead().then(res => {
console.log(res); console.log(res);
getUnReadList(); getUnReadList();
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "clear_message",
},
},
(ret) => {
console.log(ret);
});
}); });
} }
@@ -226,8 +244,12 @@
navTo({ navTo({
url: url url: url
}) })
}else if(msg.extra.type==18){
let url=`/pages_app/myFlower/myFlower?from=msg`
navTo({
url: url
})
} }
}else{ }else{
let url=""; let url="";
if(msg.extra.module==1 && msg.extra.type==36){ if(msg.extra.module==1 && msg.extra.type==36){
+1 -1
View File
@@ -26,7 +26,7 @@
<text class="value amount" :class="amountClass">{{ detail.accountStr || '-' }}元</text> <text class="value amount" :class="amountClass">{{ detail.accountStr || '-' }}元</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="label">患者:</text> <text class="label">用户:</text>
<text class="value">{{ detail.userName || '-' }}</text> <text class="value">{{ detail.userName || '-' }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
+4 -4
View File
@@ -292,7 +292,7 @@ onMounted(() => {
&.user { &.user {
flex: 1; flex: 1;
text-align: center; text-align: center;
color: #ff0000; color: #8B2316;
} }
&.date-amount { &.date-amount {
@@ -304,7 +304,7 @@ onMounted(() => {
.date { .date {
font-size: 24rpx; font-size: 24rpx;
color: #666; color: #999;
margin-bottom: 4rpx; margin-bottom: 4rpx;
} }
@@ -312,11 +312,11 @@ onMounted(() => {
font-size: 24rpx; font-size: 24rpx;
&.positive { &.positive {
color: #ff0000; color: #000;
} }
&.negative { &.negative {
color: #666; color: #000;
} }
} }
} }
+6
View File
@@ -81,6 +81,12 @@ onLoad((options) => {
if(options.from){ if(options.from){
from.value = options.from; from.value = options.from;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的账户",
},
});
}); });
onBackPress(() => { onBackPress(() => {
if(!from.value){ if(!from.value){
+2 -1
View File
@@ -399,7 +399,8 @@ const onGetSmsCode = async () => {
} catch (error) { } catch (error) {
console.error('发送短信验证码失败:', error) console.error('发送短信验证码失败:', error)
uni.showToast({ uni.showToast({
title:error.message || error.msg, title:error.data.message || error.message || error.data.msg || error.msg || '频繁发送短信,请稍后再试'
,
icon: 'none' icon: 'none'
}) })
} finally { } finally {
+6
View File
@@ -178,6 +178,12 @@ const goBack = () => {
}; };
onLoad((options) => { onLoad((options) => {
from.value = options.from; from.value = options.from;
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的二维码",
},
});
}) })
// 页面显示时拉取用户信息,并尽量把图片转成 base64,避免 html2canvas 跨域污染 // 页面显示时拉取用户信息,并尽量把图片转成 base64,避免 html2canvas 跨域污染
+23 -8
View File
@@ -66,21 +66,21 @@
<view class="courseware-item" v-for="(item, index) in coursewareList" :key="index" @click="onItemClick(item)"> <view class="courseware-item" v-for="(item, index) in coursewareList" :key="index" @click="onItemClick(item)">
<view class="item-content"> <view class="item-content">
<view class="courseware-provider" v-if="activeTab === 'share'"> <view class="courseware-provider" v-if="activeTab === 'share'">
<text class="label">下载医生:</text> <text class="label">下载医生</text>
<text class="value">{{ item.providername }}</text> <text class="value">:{{ item.providername }}</text>
</view> </view>
<view class="courseware-name"> <view class="courseware-name">
<text class="label">课件名称:</text> <text class="label">课件名称</text>
<text class="value">{{ item.name }}</text> <text class="value">:{{ item.name }}</text>
</view> </view>
<view class="courseware-time"> <view class="courseware-time">
<text class="label">时间:</text> <text class="label">时间</text>
<text class="value">{{ item.time }}</text> <text class="value">:{{ item.time }}</text>
</view> </view>
<view class="courseware-status" v-if="activeTab === 'download'"> <view class="courseware-status" v-if="activeTab === 'download'">
<text class="label">状态:</text> <text class="label">状态</text>
<text class="value status-paid">{{item.status}}</text> <text class="value status-paid">:{{item.status}}</text>
</view> </view>
</view> </view>
</view> </view>
@@ -128,6 +128,12 @@ onLoad((options) => {
if(options.from){ if(options.from){
from.value = options.from; from.value = options.from;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的课件",
},
});
}); });
onMounted(() => { onMounted(() => {
onRefresh(); onRefresh();
@@ -400,6 +406,15 @@ const onLoadMore = () => {
width: 120rpx; width: 120rpx;
color: #8B2316; color: #8B2316;
font-size: 28rpx; font-size: 28rpx;
display: inline-block;
text-align: justify;
text-align-last: justify;
text-justify: inter-ideograph;
}
.label::after {
content: '';
display: inline-block;
width: 100%;
} }
.value { .value {
+6
View File
@@ -91,6 +91,12 @@ onLoad((options) => {
let userInfo= uni.getStorageSync('userInfo') let userInfo= uni.getStorageSync('userInfo')
user_uuid.value = userInfo.uuid; user_uuid.value = userInfo.uuid;
loadFirstTwoPages(); loadFirstTwoPages();
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的鲜花",
},
});
}); });
onBackPress(() => { onBackPress(() => {
if(!from.value){ if(!from.value){
+6 -6
View File
@@ -23,8 +23,8 @@
<view class="request-text">{{ item.content }}</view> <view class="request-text">{{ item.content }}</view>
<view class="action-buttons"> <view class="action-buttons">
<button class="reject-btn" @click="applyListOperate(item.uuid,3)">拒绝</button> <button class="reject-btn" @click="applyListOperate(item.uuid,3,item.content,item.patientUuid)">拒绝</button>
<button class="agree-btn" @click="applyListOperate(item.uuid,2,item.content)">同意</button> <button class="agree-btn" @click="applyListOperate(item.uuid,2,item.content,item.patientUuid)">同意</button>
</view> </view>
</view> </view>
</view> </view>
@@ -42,7 +42,7 @@
</view> </view>
<view class="history-content"> <view class="history-content">
<view class="history-time">{{ formatDate2(item.create_date) }}</view> <view class="history-time">{{ formatDate2(item.create_date) }}</view>
<view class="nickname">昵称:{{ item.nickname || item.patient_name }}</view> <view class="nickname">昵称:{{ item.patient_name || item.nickname }}</view>
<view class="history-text">{{ item.content}}</view> <view class="history-text">{{ item.content}}</view>
<view class="status-info"> <view class="status-info">
<up-image :src="goImg" width="30rpx" height="30rpx" v-if="item.status==2"></up-image> <up-image :src="goImg" width="30rpx" height="30rpx" v-if="item.status==2"></up-image>
@@ -172,7 +172,7 @@ const getApplyList = async () => { // 申请列表
}); });
} }
}; };
const applyListOperate = async (uuid,status,content) => { const applyListOperate = async (uuid,status,content,patientUuid) => {
let data = { let data = {
uuid: uuid, uuid: uuid,
status: status status: status
@@ -190,7 +190,7 @@ const getApplyList = async () => { // 申请列表
getRelationRecordLately(); getRelationRecordLately();
if(status == 2){ if(status == 2){
navTo({ navTo({
url: `/pages_app/patientRemark/patientRemark?uuid=${uuid}&&nickname=${nickname}` url: `/pages_app/patientRemark/patientRemark?uuid=${patientUuid}&&nickname=${nickname}`
}) })
} }
} }
@@ -205,7 +205,7 @@ const getApplyList = async () => { // 申请列表
if(status == 2){ if(status == 2){
navTo({ navTo({
url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}` url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}&from=myPatient`
}) })
} }
} }
+13 -1
View File
@@ -208,6 +208,12 @@ onLoad((options) => {
if(options.from){ if(options.from){
from.value = options.from; from.value = options.from;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的积分",
},
});
}); });
import navTo from "@/utils/navTo.js"; import navTo from "@/utils/navTo.js";
import nosigninImg from "@/static/sign_day_false.png" import nosigninImg from "@/static/sign_day_false.png"
@@ -234,7 +240,8 @@ const showSign = ref(false)
const signInfo=reactive({ const signInfo=reactive({
news:{ news:{
summary:'', summary:'',
title:'' title:'',
tags:''
} }
}) })
// 分页相关数据 - 收益 // 分页相关数据 - 收益
@@ -368,6 +375,11 @@ try {
// 签到成功后刷新数据 // 签到成功后刷新数据
getMyBonusPoints(); getMyBonusPoints();
getBonusPointsList(); getBonusPointsList();
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "signin"
},
});
// uni.showToast({ // uni.showToast({
// title: '签到成功,获得'+res.bonuspoints+'积分', // title: '签到成功,获得'+res.bonuspoints+'积分',
// icon: 'none' // icon: 'none'
+6
View File
@@ -250,6 +250,12 @@
if(options.from){ if(options.from){
from.value = options.from; from.value = options.from;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "我的福利",
},
});
getWelfarePage(); getWelfarePage();
}); });
onHide(() => { onHide(() => {
@@ -139,6 +139,12 @@
}); });
onLoad((options) => { onLoad((options) => {
from.value = options.from; from.value = options.from;
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "福利卡兑换",
},
});
}) })
const goWelfare = () => { const goWelfare = () => {
uni.setStorageSync('lookWelfare', 'useWelfare'); uni.setStorageSync('lookWelfare', 'useWelfare');
+20 -1
View File
@@ -108,9 +108,12 @@
<view class="item-title">{{ news.title }}</view> <view class="item-title">{{ news.title }}</view>
<view class="item-meta"> <view class="item-meta">
<text class="item-date">{{ formatDate(news.createDate) }}</text> <text class="item-date">{{ formatDate(news.createDate) }}</text>
<view class="item-stats"> <view class="item-stats middel" >
<uni-icons type="eye" size="14" color="#999"></uni-icons> <uni-icons type="eye" size="14" color="#999"></uni-icons>
<text>{{ news.readnum || 0 }}</text> <text>{{ news.readnum || 0 }}</text>
</view>
<view class="item-stats">
<uni-icons type="hand-up" size="14" color="#999" style="margin-left: 20rpx;"></uni-icons> <uni-icons type="hand-up" size="14" color="#999" style="margin-left: 20rpx;"></uni-icons>
<text>{{ news.agreenum || 0 }}</text> <text>{{ news.agreenum || 0 }}</text>
</view> </view>
@@ -150,6 +153,15 @@
if(options.from){ if(options.from){
from.value = options.from; from.value = options.from;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "肝胆新闻",
},
},
(ret) => {
console.log(ret);
});
}); });
onBackPress(() => { onBackPress(() => {
if(!from.value){ if(!from.value){
@@ -736,17 +748,24 @@
color: #999; color: #999;
.item-date { .item-date {
width:125rpx;
white-space: nowrap;
flex-shrink: 0; flex-shrink: 0;
} }
.item-stats { .item-stats {
display: flex; display: flex;
align-items: center; align-items: center;
text { text {
margin-left: 8rpx; margin-left: 8rpx;
} }
} }
.middel {
width:95rpx;
text-align: left;
}
} }
} }
} }
+20 -3
View File
@@ -123,7 +123,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { onShow,onLoad } from '@dcloudio/uni-app' import { onShow,onLoad,onBackPress} from '@dcloudio/uni-app'
import api from '@/api/api.js' import api from '@/api/api.js'
import docUrl from '@/utils/docUrl.js' import docUrl from '@/utils/docUrl.js'
import navTo from '@/utils/navTo.js' import navTo from '@/utils/navTo.js'
@@ -141,6 +141,13 @@
const patient_uuid = ref(''); const patient_uuid = ref('');
const note = ref(''); const note = ref('');
const avatarLoadError = ref(false); const avatarLoadError = ref(false);
const from = ref('');
onBackPress(() => {
if(!from.value){
plus.runtime.quit();
return true;
}
});
const confirmDialog = ()=>{ const confirmDialog = ()=>{
showDialog.value = false; showDialog.value = false;
uni.setClipboardData({ uni.setClipboardData({
@@ -179,9 +186,19 @@
// 计算属性:患者病史文本 // 计算属性:患者病史文本
onLoad((options) => { onLoad((options) => {
console.log(options);
patient_uuid.value = options.uuid; patient_uuid.value = options.uuid;
if(options.from){
from.value=options.from
}
}); });
const goBack = ()=> uni.navigateBack() const goBack = ()=> {
if(!from.value){
plus.runtime.quit();
}else{
uni.navigateBack();
}
}
const openCase = (uuid)=>{ const openCase = (uuid)=>{
navTo({ navTo({
url:'/pages_app/checkRecord/checkRecord?uuid='+uuid url:'/pages_app/checkRecord/checkRecord?uuid='+uuid
@@ -278,7 +295,7 @@
}); });
const goMakePlan = ()=> { const goMakePlan = ()=> {
navTo({ navTo({
url:'/pages_app/visitPlan/visitPlan' url:'/pages_app/visitPlan/visitPlan?uuid='+patient_uuid.value+'&patient_name='+patientInfo.value.realName+'&type=patientDetail'
}) })
} }
const recordIllness = ()=> { const recordIllness = ()=> {
+825
View File
@@ -0,0 +1,825 @@
<template>
<view class="content">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="患者列表"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right">
<uni-icons type="search" size="24" color="#8B2316" @click="searchPatients"></uni-icons>
<uni-icons type="plusempty" size="24" color="#8B2316" @click="goCode" style="margin-left: 30rpx;"></uni-icons>
</view>
</template>
</uni-nav-bar>
</view>
<view class="patient-list">
<view class="listbox alertContent" v-if="showAlert">
<image :src="delImg" class="del-img" mode="widthFix" @click="hideAlert"></image>
<image :src="alertImg" class="alert-img" mode="widthFix"></image>
</view>
<view class="listbox patient-building" v-if="patientDataLoading && !loadFinish">
<text class="patient-building-text">正在整理患者数据,请稍候...</text>
</view>
<view class="listbox" v-show="patientList.length > 0">
<scroll-view
class="groups-scroll"
scroll-y="true"
:scroll-into-view="scrollIntoViewId"
:scroll-with-animation="true"
@scroll="onGroupsScroll"
@scrolltolower="onGroupsScrollToLower"
:lower-threshold="120"
v-show="loadFinish"
>
<view class="special-actions">
<view class="action-item" @click="addNewPatient">
<view class="action-icon new-patient">
<up-image :src="newPatientImg" width="60rpx" height="60rpx"></up-image>
</view>
<text class="action-text">新的患者<text class="new-patient-count" v-if="applyList.length > 0">(待审核{{ applyList.length }}人)</text></text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
<view class="action-item" @click="managePatientGroups">
<view class="action-icon group-icon">
<view class="grid-icon">
<view class="grid-item"></view>
<view class="grid-item"></view>
<view class="grid-item"></view>
<view class="grid-item"></view>
</view>
</view>
<text class="action-text">患者分组 <text class="new-patient-count" v-if="patientList.length > 0">(随访{{ patientListTotal }}人)</text></text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="group-section" v-for="group in patientGroups" :key="group.letter" :id="`anchor-${group.letter}`">
<view class="group-header">{{ group.letter }}</view>
<view class="patient-item" v-for="item in group.items" :key="item.uuid || item.id" @longpress="onPatientItemLongPress(item)" @click="goPatientDetail(item.uuid)">
<image
v-if="hasPatientAvatar(item)"
class="patient-avatar"
:src="getPatientAvatar(item)"
mode="aspectFill"
lazy-load="true"
@error="handlePatientAvatarError(item)"
@click.stop="goPatientDetail(item.uuid)"
></image>
<view v-else class="patient-avatar avatar-placeholder"></view>
<view class="patient-info">
<text class="patient-name">{{ item.nickname || item.realName }}</text>
</view>
<view class="patient-status">
<view class="edit-icon" @click.stop="editPatient(item.uuid)">
<view class="edit-pen"></view>
</view>
<text class="follow-date">随访于{{ item.joinDateYMD }}</text>
</view>
</view>
</view>
<view class="load-more" v-if="patientListLoadingMore">
<text class="load-more-text">加载中...</text>
</view>
<view class="load-more" v-else-if="!patientListHasMore && patientList.length > 0">
<text class="load-more-text">没有更多了</text>
</view>
</scroll-view>
<view class="letter-index" v-if="loadFinish && indexList.length > 0">
<view class="letter-item" v-for="letter in indexList" :key="letter" :class="{ active: activeLetter === letter }" @click="onLetterTap(letter)">
{{ letter }}
</view>
</view>
</view>
<view class="listbox" v-show="patientList.length === 0 && !patientDataLoading">
<view class="special-actions">
<view class="action-item" @click="addNewPatient">
<view class="action-icon new-patient">
<up-image :src="newPatientImg" width="60rpx" height="60rpx"></up-image>
</view>
<text class="action-text">新的患者<text class="new-patient-count" v-if="applyList.length > 0">(待审核{{ applyList.length }}人)</text></text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
<view class="action-item" @click="managePatientGroups">
<view class="action-icon group-icon">
<view class="grid-icon">
<view class="grid-item"></view>
<view class="grid-item"></view>
<view class="grid-item"></view>
<view class="grid-item"></view>
</view>
</view>
<text class="action-text">患者分组</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
<empty></empty>
</view>
</view>
<view class="tab-bar">
<view class="tab-item" @click="goMessageTab">
<text class="tab-text">患者消息</text>
</view>
<view class="tab-item active">
<text class="tab-text">患者列表</text>
<view class="tab-dot" v-if="hasNewPatient"><uni-badge class="uni-badge-left-margin" :text="applyList.length" :offset="[-3, -3]" size="small"></uni-badge></view>
</view>
<view class="tab-item" @click="goPlanTab">
<text class="tab-text">随访计划</text>
</view>
</view>
</view>
<unidialog :visible="visible" :content="message" @close="visible=false" @confirm="deleteFollowUpFromList"></unidialog>
</template>
<script setup>
import { computed, nextTick, ref, shallowRef } from 'vue';
import { onBackPress, onLoad, onShow } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import pinyin from 'pinyin';
import api from '@/api/api.js';
import navTo from '@/utils/navTo.js';
import docUrl from '@/utils/docUrl.js';
import unidialog from '@/components/dialog/dialog.vue';
import newPatientImg from '@/static/new_patient.png';
import delImg from '@/static/iv_delete.png';
import alertImg from '@/static/patientgif.png';
const patientList = ref([]);
const patientDataLoaded = ref(false);
const patientDataLoading = ref(false);
const loadFinish = ref(false);
const showAlert = ref(false);
const applyList = ref([]);
const visible = ref(false);
const message = ref('');
const selectedPatientUuid = ref('');
const selectedPatientName = ref('');
const firstLetterCache = new Map();
const LETTER_ORDER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const GROUP_BUILD_CHUNK_SIZE = 180;
const PERF_TRACE = true;
const SCROLL_LOG_MIN_INTERVAL = 180;
const activeLetter = ref('');
const scrollIntoViewId = ref('');
const groupScrollRanges = ref([]);
const indexList = ref([]);
const avatarErrorMap = ref({});
const patientGroups = shallowRef([]);
const allPatientGroups = shallowRef([]);
const lastScrollTop = ref(0);
const lastScrollLogAt = ref(0);
const buildStartedAt = ref(0);
const fetchStartedAt = ref(0);
const patientListPage = ref(1);
const patientListPageSize = ref(300);
const patientListHasMore = ref(true);
const patientListLoadingMore = ref(false);
const canTriggerLowerLoad = ref(true);
const lastLowerTriggerTop = ref(0);
const LOWER_RESET_DISTANCE = 120;
const patientListTotal = ref(0);
const hasNewPatient = computed(() => applyList.value.length > 0);
const perfLog = (...args) => {
if (!PERF_TRACE) return;
console.log('[patientList/perf]', ...args);
};
const sortLetter = (a, b) => {
if (a === '#') return 1;
if (b === '#') return -1;
const ai = LETTER_ORDER.indexOf(a);
const bi = LETTER_ORDER.indexOf(b);
if (ai === -1 && bi === -1) return String(a).localeCompare(String(b));
if (ai === -1) return 1;
if (bi === -1) return -1;
return ai - bi;
};
const formatYMD = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('YYYY-MM-DD') : '';
};
const getFirstLetter = (name = '') => {
const firstChar = String(name).trim().charAt(0);
if (!firstChar) return '#';
if (/^[A-Za-z]$/.test(firstChar)) return firstChar.toUpperCase();
if (firstLetterCache.has(firstChar)) return firstLetterCache.get(firstChar);
try {
const py = pinyin(firstChar, { style: pinyin.STYLE_NORMAL })?.[0]?.[0] || '';
const letter = py ? py.charAt(0).toUpperCase() : '#';
firstLetterCache.set(firstChar, letter);
return letter;
} catch (error) {
return '#';
}
};
const getAvatarKey = (item = {}) => String(item.uuid || item.id || item.realName || item.nickname || '');
const normalizeAvatarUrl = (photo) => {
const raw = String(photo || '').trim();
if (!raw) return '';
if (/^https?:\/\//i.test(raw)) return raw;
return `${docUrl}${raw}`;
};
const hasPatientAvatar = (item = {}) => {
const key = getAvatarKey(item);
if (!item.avatarUrl) return false;
if (key && avatarErrorMap.value[key]) return false;
return true;
};
const getPatientAvatar = (item = {}) => {
return item.avatarUrl || '';
};
const handlePatientAvatarError = (item) => {
if (!item) return;
const key = getAvatarKey(item);
if (!key) return;
avatarErrorMap.value = { ...avatarErrorMap.value, [key]: true };
};
const rebuildIndexList = () => {
indexList.value = allPatientGroups.value
.map(g => String(g?.letter || '').toUpperCase())
.filter((letter, index, arr) => (letter === '#' || /^[A-Z]$/.test(letter)) && arr.indexOf(letter) === index)
.sort(sortLetter);
activeLetter.value = indexList.value[0] || '';
};
const rpxToPx = (rpx) => {
const { screenWidth } = uni.getSystemInfoSync();
return Math.round((screenWidth / 750) * rpx);
};
const buildGroupScrollRanges = () => {
const actionHeight = rpxToPx(120);
const headerHeight = rpxToPx(80);
const itemHeight = rpxToPx(150);
let cursor = actionHeight * 2;
groupScrollRanges.value = patientGroups.value.map(group => {
const groupHeight = headerHeight + (group.items?.length || 0) * itemHeight;
const range = { letter: group.letter, start: cursor, end: cursor + groupHeight };
cursor += groupHeight;
return range;
});
};
const onGroupsScroll = (e) => {
const top = Number(e?.detail?.scrollTop || 0);
if (!canTriggerLowerLoad.value && top < lastLowerTriggerTop.value - LOWER_RESET_DISTANCE) {
canTriggerLowerLoad.value = true;
}
if (!groupScrollRanges.value.length) return;
let current = groupScrollRanges.value[0].letter;
for (let i = 0; i < groupScrollRanges.value.length; i++) {
const range = groupScrollRanges.value[i];
if (top >= range.start) {
current = range.letter;
} else {
break;
}
}
activeLetter.value = current;
const now = Date.now();
if (now - lastScrollLogAt.value >= SCROLL_LOG_MIN_INTERVAL) {
perfLog('scroll', {
top,
deltaTop: top - lastScrollTop.value,
activeLetter: current,
groupCount: patientGroups.value.length,
patientCount: patientList.value.length
});
lastScrollLogAt.value = now;
lastScrollTop.value = top;
}
};
const onLetterTap = (letter) => {
if (!letter) return;
activeLetter.value = letter;
const targetId = `anchor-${letter}`;
if (scrollIntoViewId.value === targetId) {
scrollIntoViewId.value = '';
nextTick(() => {
scrollIntoViewId.value = targetId;
});
return;
}
scrollIntoViewId.value = targetId;
};
const waitForNextFrame = () => new Promise(resolve => setTimeout(resolve, 0));
const buildGroupsFromPatients = async () => {
buildStartedAt.value = Date.now();
perfLog('build:start', { count: patientList.value.length });
avatarErrorMap.value = {};
const map = new Map();
const source = patientList.value || [];
for (let i = 0; i < source.length; i++) {
const p = source[i] || {};
const normalized = { ...p, avatarUrl: normalizeAvatarUrl(p.photo), joinDateYMD: formatYMD(p.join_date) };
const first = getFirstLetter(normalized.nickname || normalized.realName);
const letter = /^[A-Z]$/.test(first) ? first : '#';
if (!map.has(letter)) map.set(letter, []);
map.get(letter).push(normalized);
if (i > 0 && i % GROUP_BUILD_CHUNK_SIZE === 0) {
await waitForNextFrame();
}
}
const letters = Array.from(map.keys()).sort(sortLetter);
allPatientGroups.value = letters.map(l => ({ letter: l, items: map.get(l) }));
patientGroups.value = allPatientGroups.value;
rebuildIndexList();
buildGroupScrollRanges();
perfLog('build:done', {
costMs: Date.now() - buildStartedAt.value,
groupCount: patientGroups.value.length,
patientCount: patientList.value.length
});
};
const getApplyList = async () => {
try {
const res = await api.applyList();
if (res && res.code === 200) applyList.value = res.data || [];
} catch (error) {}
};
const isApiSuccess = (res) => Number(res?.code) === 1 || Number(res?.code) === 200;
const parsePagedPatients = (res) => {
const data = res?.data;
if (Array.isArray(data)) {
return { list: data, total: undefined, pageNum: undefined, pages: undefined, isLastPage: undefined };
}
const list =
(Array.isArray(data?.list) && data.list) ||
(Array.isArray(data?.records) && data.records) ||
(Array.isArray(data?.rows) && data.rows) ||
(Array.isArray(data?.data) && data.data) ||
[];
const total = Number(
data?.total ??
data?.count ??
data?.totalCount ??
undefined
);
const pageNum = Number(data?.pageNum ?? data?.page ?? data?.current ?? undefined);
const pages = Number(data?.pages ?? data?.pageCount ?? undefined);
const isLastPage =
typeof data?.isLastPage === 'boolean'
? data.isLastPage
: undefined;
return {
list,
total: Number.isFinite(total) ? total : undefined
,
pageNum: Number.isFinite(pageNum) ? pageNum : undefined,
pages: Number.isFinite(pages) ? pages : undefined,
isLastPage
};
};
const mergePatientListUnique = (base = [], incoming = []) => {
if (!incoming.length) return base;
const seen = new Set();
const merged = [];
const pushUnique = (item) => {
const key = String(item?.uuid || item?.id || item?.mobile || '');
if (!key) {
merged.push(item);
return;
}
if (seen.has(key)) return;
seen.add(key);
merged.push(item);
};
for (let i = 0; i < base.length; i++) pushUnique(base[i]);
for (let i = 0; i < incoming.length; i++) pushUnique(incoming[i]);
return merged;
};
const applyPatientListData = async (list, total) => {
patientList.value = list || [];
patientListTotal.value = total || 0;
await buildGroupsFromPatients();
loadFinish.value = true;
patientDataLoaded.value = true;
if (patientListTotal.value > 1000) {
const hasAlert = uni.getStorageSync('hasAlert');
showAlert.value = !hasAlert;
}
};
const patientListByGBKPage = async ({ showLoading = true, reset = false } = {}) => {
if (patientDataLoading.value || patientListLoadingMore.value) return;
if (!reset && !patientListHasMore.value) return;
if (reset) {
patientDataLoading.value = true;
loadFinish.value = false;
patientListPage.value = 1;
patientListHasMore.value = true;
canTriggerLowerLoad.value = true;
lastLowerTriggerTop.value = 0;
} else {
patientListLoadingMore.value = true;
}
fetchStartedAt.value = Date.now();
perfLog('api:start', { showLoading, reset, page: patientListPage.value, pageSize: patientListPageSize.value });
if (showLoading) uni.showLoading({ title: '加载中...', mask: true });
try {
const requestPage = patientListPage.value;
const res = await api.patientListByGBKPage({
page: requestPage,
pageSize: patientListPageSize.value,
});
if (isApiSuccess(res)) {
const { list, total, pageNum, pages, isLastPage } = parsePagedPatients(res);
const merged = reset ? list : mergePatientListUnique(patientList.value, list);
await applyPatientListData(merged, total);
if (typeof isLastPage === 'boolean') {
patientListHasMore.value = !isLastPage;
} else if (typeof pages === 'number' && typeof pageNum === 'number') {
patientListHasMore.value = pageNum < pages;
} else if (typeof total === 'number') {
patientListHasMore.value = merged.length < total;
} else {
patientListHasMore.value = list.length >= patientListPageSize.value;
}
if (patientListHasMore.value) {
if (typeof pageNum === 'number') {
patientListPage.value = pageNum + 1;
} else {
patientListPage.value = requestPage + 1;
}
}
perfLog('api:done', {
totalMs: Date.now() - fetchStartedAt.value,
requestPage,
page: typeof pageNum === 'number' ? pageNum : (reset ? 1 : patientListPage.value - 1),
pageDataCount: list.length,
mergedCount: merged.length,
hasMore: patientListHasMore.value
});
} else {
perfLog('api:bad-code', { code: res.code });
}
} catch (error) {
perfLog('api:error', error?.message || error);
throw error;
} finally {
patientDataLoading.value = false;
patientListLoadingMore.value = false;
if (showLoading) uni.hideLoading();
}
};
const onGroupsScrollToLower = () => {
void patientListByGBKPage({ showLoading: false, reset: false });
};
const goPatientDetail = (uuid) => {
navTo({ url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}` });
};
const editPatient = (uuid) => {
navTo({ url: `/pages_app/patientSetting/patientSetting?uuid=${uuid}` });
};
const searchPatients = () => {
navTo({ url: '/pages_app/searchPatient/searchPatient' });
};
const addNewPatient = () => {
navTo({ url: '/pages_app/myPatient/myPatient?from=patientMsg' });
};
const managePatientGroups = () => {
navTo({ url: '/pages_app/patientGroup/patientGroup?from=patientMsg' });
};
const goCode = () => {
navTo({ url: '/pages_app/myCode/myCode' });
};
const hideAlert = () => {
showAlert.value = false;
uni.setStorageSync('hasAlert', true);
};
const onPatientItemLongPress = (item) => {
if (!item?.uuid) return;
selectedPatientUuid.value = item.uuid;
selectedPatientName.value = item.nickname || item.realName;
message.value = `确认解除[${selectedPatientName.value || '该患者'}]的随访关系?`;
visible.value = true;
};
const deleteFollowUpFromList = async () => {
if (!selectedPatientUuid.value) {
visible.value = false;
return;
}
const res = await api.cancelRes({ patientUuid: selectedPatientUuid.value });
if (isApiSuccess(res)) {
uni.showToast({ title: '解除成功', icon: 'none' });
await patientListByGBKPage({ showLoading: true, reset: true });
}
visible.value = false;
};
const goBack = () => {
plus.runtime.quit();
};
const goMessageTab = () => {
navTo({ url: '/pages_app/patientMsg/patientMsg' });
};
const goPlanTab = () => {
navTo({ url: '/pages_app/patientMsg/patientPlan' });
};
onBackPress(() => {
plus.runtime.quit();
return true;
});
onLoad(async () => {
perfLog('lifecycle:onLoad');
getApplyList();
void patientListByGBKPage({ showLoading: true, reset: true });
});
onShow(async () => {
perfLog('lifecycle:onShow', { patientDataLoaded: patientDataLoaded.value });
getApplyList();
if (!patientDataLoaded.value) {
void patientListByGBKPage({ showLoading: true, reset: true });
}
});
</script>
<style lang="scss" scoped>
.content {
background-color: #f5f5f5;
height: 100vh;
overflow-y: hidden;
}
.nav-right {
display: flex;
align-items: center;
}
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #f8f8f8;
display: flex;
align-items: center;
border-top: 1rpx solid #e0e0e0;
z-index: 999;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
height: 100%;
}
.tab-item:nth-child(2) {
border: 2rpx solid #ccc;
border-top: none;
border-bottom: none;
}
.tab-item .tab-text {
font-size: 32rpx;
color: #999;
}
.tab-item.active .tab-text {
color: #8B2316;
font-weight: 500;
}
.tab-dot {
position: absolute;
top: 18rpx;
right: 28rpx;
}
.patient-list {
width: 100%;
margin-top: calc(var(--status-bar-height) + 44px);
height: calc(100vh - var(--status-bar-height) - 44px - 103rpx);
overflow: hidden;
background-color: #fff;
display: flex;
flex-direction: column;
}
.listbox {
flex: 1;
min-height: 0;
}
.patient-building {
display: flex;
align-items: center;
justify-content: center;
}
.patient-building-text {
font-size: 28rpx;
color: #999;
}
.groups-scroll {
height: 100%;
}
.load-more {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
}
.load-more-text {
font-size: 24rpx;
color: #999;
}
.special-actions {
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.action-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.action-icon {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.new-patient,
.group-icon {
background-color: #8B2316;
}
.grid-icon {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 2rpx;
width: 32rpx;
height: 32rpx;
}
.grid-item {
background-color: #fff;
border-radius: 2rpx;
}
.action-text {
flex: 1;
font-size: 32rpx;
color: #333;
}
.new-patient-count {
color: red;
font-size: 32rpx;
margin-left: 10rpx;
}
.group-header {
padding: 20rpx 30rpx;
background-color: #e4e4e4;
font-size: 36rpx;
color: #666;
}
.patient-item {
display: flex;
align-items: center;
padding: 30rpx 80rpx 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
}
.patient-avatar {
width: 90rpx;
height: 90rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.avatar-placeholder {
position: relative;
background: #e0e0e0;
border-radius: 50%;
}
.avatar-placeholder::before {
content: '';
position: absolute;
width: 28rpx;
height: 28rpx;
border-radius: 50%;
background: #ffffff;
top: 16rpx;
left: 31rpx;
}
.avatar-placeholder::after {
content: '';
position: absolute;
width: 56rpx;
height: 30rpx;
border-radius: 28rpx 28rpx 12rpx 12rpx;
background: #ffffff;
left: 17rpx;
bottom: 14rpx;
}
.patient-info {
flex: 1;
}
.patient-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
.patient-status {
display: flex;
align-items: flex-end;
flex-direction: column;
}
.edit-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
}
.edit-pen {
position: relative;
width: 24rpx;
height: 6rpx;
background: #8B2316;
border-radius: 3rpx;
transform: rotate(-35deg);
}
.edit-pen::before {
content: '';
position: absolute;
left: -8rpx;
top: 0;
width: 0;
height: 0;
border-top: 3rpx solid transparent;
border-bottom: 3rpx solid transparent;
border-right: 8rpx solid #8B2316;
}
.edit-pen::after {
content: '';
position: absolute;
right: -5rpx;
top: 0;
width: 5rpx;
height: 6rpx;
background: #d9d9d9;
border-radius: 0 3rpx 3rpx 0;
}
.follow-date {
margin-top: 8rpx;
font-size: 24rpx;
color: #666;
}
.letter-index {
position: fixed;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
background-color: rgba(255, 255, 255, 0.9);
border-radius: 20rpx;
padding: 10rpx 5rpx;
z-index: 999;
}
.letter-item {
padding: 8rpx 12rpx;
font-size: 24rpx;
color: #666;
text-align: center;
border-radius: 10rpx;
}
.letter-item.active {
color: #8B2316;
font-weight: 600;
}
.alertContent {
z-index: 99;
position: fixed;
bottom: 100rpx;
width: 100%;
top: calc(var(--status-bar-height) + 44px);
pointer-events: none;
}
.alert-img {
width: 100%;
}
.del-img {
position: absolute;
top: 280rpx;
z-index: 999;
right: 100rpx;
width: 48rpx;
height: 48rpx;
pointer-events: auto;
}
</style>
+430
View File
@@ -0,0 +1,430 @@
<template>
<view class="content">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="患者列表压测"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right">
<uni-icons type="search" size="24" color="#8B2316"></uni-icons>
</view>
</template>
</uni-nav-bar>
</view>
<view class="patient-list">
<view class="perf-toolbar">
<view class="perf-btn primary" @click="loadRealData">加载真实数据</view>
<view class="perf-btn" @click="loadRealData(true)">强制刷新</view>
<view class="perf-btn" @click="clearData">清空</view>
</view>
<view class="perf-tip">当前数据量: {{ patientList.length }},分组: {{ patientGroups.length }},当前字母: {{ activeLetter || '-' }}</view>
<view class="listbox patient-building" v-if="patientDataLoading && !loadFinish">
<text class="patient-building-text">正在生成并整理模拟数据...</text>
</view>
<view class="listbox" v-show="patientList.length > 0">
<scroll-view
class="groups-scroll"
scroll-y="true"
:scroll-into-view="scrollIntoViewId"
:scroll-with-animation="true"
@scroll="onGroupsScroll"
v-show="loadFinish"
>
<view class="special-actions">
<view class="action-item">
<view class="action-icon new-patient"></view>
<text class="action-text">新的患者(模拟)</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="group-section" v-for="group in patientGroups" :key="group.letter" :id="`anchor-${group.letter}`">
<view class="group-header">{{ group.letter }}</view>
<view class="patient-item" v-for="item in group.items" :key="item.uuid || item.id">
<view class="patient-avatar avatar-placeholder"></view>
<view class="patient-info">
<text class="patient-name">{{ item.nickname || item.realName }}</text>
</view>
<view class="patient-status">
<text class="follow-date">随访于{{ item.joinDateYMD }}</text>
</view>
</view>
</view>
</scroll-view>
<view class="letter-index" v-if="loadFinish && indexList.length > 0">
<view class="letter-item" v-for="letter in indexList" :key="letter" :class="{ active: activeLetter === letter }" @click="onLetterTap(letter)">
{{ letter }}
</view>
</view>
</view>
<view class="listbox" v-show="patientList.length === 0 && !patientDataLoading">
<empty></empty>
</view>
</view>
</view>
</template>
<script setup>
import { nextTick, ref, shallowRef } from 'vue';
import { onBackPress } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import pinyin from 'pinyin';
import api from '@/api/api.js';
const patientList = ref([]);
const patientDataLoading = ref(false);
const loadFinish = ref(false);
const firstLetterCache = new Map();
const LETTER_ORDER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const GROUP_BUILD_CHUNK_SIZE = 180;
const indexList = ref([]);
const patientGroups = shallowRef([]);
const allPatientGroups = shallowRef([]);
const activeLetter = ref('');
const scrollIntoViewId = ref('');
const groupScrollRanges = ref([]);
const sortLetter = (a, b) => {
if (a === '#') return 1;
if (b === '#') return -1;
const ai = LETTER_ORDER.indexOf(a);
const bi = LETTER_ORDER.indexOf(b);
if (ai === -1 && bi === -1) return String(a).localeCompare(String(b));
if (ai === -1) return 1;
if (bi === -1) return -1;
return ai - bi;
};
const formatYMD = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('YYYY-MM-DD') : '';
};
const getFirstLetter = (name = '') => {
const firstChar = String(name).trim().charAt(0);
if (!firstChar) return '#';
if (/^[A-Za-z]$/.test(firstChar)) return firstChar.toUpperCase();
if (firstLetterCache.has(firstChar)) return firstLetterCache.get(firstChar);
try {
const py = pinyin(firstChar, { style: pinyin.STYLE_NORMAL })?.[0]?.[0] || '';
const letter = py ? py.charAt(0).toUpperCase() : '#';
firstLetterCache.set(firstChar, letter);
return letter;
} catch (error) {
return '#';
}
};
const rebuildIndexList = () => {
indexList.value = allPatientGroups.value
.map(g => String(g?.letter || '').toUpperCase())
.filter((letter, index, arr) => (letter === '#' || /^[A-Z]$/.test(letter)) && arr.indexOf(letter) === index)
.sort(sortLetter);
activeLetter.value = indexList.value[0] || '';
};
const onLetterTap = (letter) => {
if (!letter) return;
activeLetter.value = letter;
const targetId = `anchor-${letter}`;
if (scrollIntoViewId.value === targetId) {
scrollIntoViewId.value = '';
nextTick(() => {
scrollIntoViewId.value = targetId;
});
return;
}
scrollIntoViewId.value = targetId;
};
const rpxToPx = (rpx) => {
const { screenWidth } = uni.getSystemInfoSync();
return Math.round((screenWidth / 750) * rpx);
};
const buildGroupScrollRanges = () => {
const actionHeight = rpxToPx(120); // action-item 高度近似
const headerHeight = rpxToPx(80); // group-header 高度近似
const itemHeight = rpxToPx(150); // patient-item 高度近似
let cursor = actionHeight;
groupScrollRanges.value = patientGroups.value.map(group => {
const groupHeight = headerHeight + (group.items?.length || 0) * itemHeight;
const range = {
letter: group.letter,
start: cursor,
end: cursor + groupHeight
};
cursor += groupHeight;
return range;
});
};
const onGroupsScroll = (e) => {
const top = Number(e?.detail?.scrollTop || 0);
if (!groupScrollRanges.value.length) return;
let current = groupScrollRanges.value[0].letter;
for (let i = 0; i < groupScrollRanges.value.length; i++) {
const range = groupScrollRanges.value[i];
if (top >= range.start) {
current = range.letter;
} else {
break;
}
}
activeLetter.value = current;
};
const waitForNextFrame = () => new Promise(resolve => setTimeout(resolve, 0));
const buildGroupsFromPatients = async () => {
const map = new Map();
const source = patientList.value || [];
for (let i = 0; i < source.length; i++) {
const p = source[i] || {};
const normalized = {
...p,
joinDateYMD: formatYMD(p.join_date)
};
const first = getFirstLetter(normalized.nickname || normalized.realName);
const letter = /^[A-Z]$/.test(first) ? first : '#';
if (!map.has(letter)) map.set(letter, []);
map.get(letter).push(normalized);
if (i > 0 && i % GROUP_BUILD_CHUNK_SIZE === 0) {
await waitForNextFrame();
}
}
const letters = Array.from(map.keys()).sort(sortLetter);
allPatientGroups.value = letters.map(l => ({ letter: l, items: map.get(l) }));
patientGroups.value = allPatientGroups.value;
rebuildIndexList();
buildGroupScrollRanges();
};
const loadRealData = async (force = false) => {
if (patientDataLoading.value) return;
if (!force && patientList.value.length > 0) return;
patientDataLoading.value = true;
loadFinish.value = false;
try {
const t1 = Date.now();
const res = await api.patientListByGBK();
if (res?.code !== 1) {
uni.showToast({ title: '获取数据失败', icon: 'none' });
return;
}
patientList.value = res.data || [];
await buildGroupsFromPatients();
loadFinish.value = true;
const t2 = Date.now();
uni.showToast({
title: `加载完成 ${patientList.value.length}条/${t2 - t1}ms`,
icon: 'none',
duration: 1500
});
} finally {
patientDataLoading.value = false;
}
};
const clearData = () => {
patientList.value = [];
patientGroups.value = [];
allPatientGroups.value = [];
indexList.value = [];
groupScrollRanges.value = [];
activeLetter.value = '';
scrollIntoViewId.value = '';
loadFinish.value = false;
};
const goBack = () => {
plus.runtime.quit();
};
onBackPress(() => {
plus.runtime.quit();
return true;
});
</script>
<style lang="scss" scoped>
.content {
background-color: #f5f5f5;
height: 100vh;
overflow-y: hidden;
}
.nav-right {
display: flex;
align-items: center;
}
.patient-list {
width: 100%;
margin-top: calc(var(--status-bar-height) + 44px);
height: calc(100vh - var(--status-bar-height) - 44px);
overflow: hidden;
background-color: #fff;
display: flex;
flex-direction: column;
}
.perf-toolbar {
display: flex;
align-items: center;
padding: 16rpx 20rpx;
border-bottom: 1rpx solid #f0f0f0;
background: #fff;
position: sticky;
top: 0;
z-index: 5;
}
.perf-btn {
padding: 10rpx 16rpx;
background: #f2f2f2;
border-radius: 8rpx;
font-size: 24rpx;
color: #555;
margin-right: 12rpx;
}
.perf-btn.primary {
background: #8B2316;
color: #fff;
}
.perf-tip {
font-size: 24rpx;
color: #999;
padding: 8rpx 20rpx 14rpx;
}
.listbox {
flex: 1;
min-height: 0;
}
.patient-building {
display: flex;
align-items: center;
justify-content: center;
}
.patient-building-text {
font-size: 28rpx;
color: #999;
margin-top: 60rpx;
}
.groups-scroll {
height: 100%;
}
.special-actions {
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.action-item {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.action-icon {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.new-patient {
background-color: #8B2316;
}
.action-text {
flex: 1;
font-size: 32rpx;
color: #333;
}
.group-header {
padding: 20rpx 30rpx;
background-color: #e4e4e4;
font-size: 36rpx;
color: #666;
}
.patient-item {
display: flex;
align-items: center;
padding: 30rpx 80rpx 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
background-color: #fff;
}
.patient-avatar {
width: 90rpx;
height: 90rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.avatar-placeholder {
position: relative;
background: #e0e0e0;
border-radius: 50%;
}
.avatar-placeholder::before {
content: '';
position: absolute;
width: 28rpx;
height: 28rpx;
border-radius: 50%;
background: #ffffff;
top: 16rpx;
left: 31rpx;
}
.avatar-placeholder::after {
content: '';
position: absolute;
width: 56rpx;
height: 30rpx;
border-radius: 28rpx 28rpx 12rpx 12rpx;
background: #ffffff;
left: 17rpx;
bottom: 14rpx;
}
.patient-info {
flex: 1;
}
.patient-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
.patient-status {
display: flex;
align-items: flex-end;
flex-direction: column;
}
.follow-date {
margin-top: 8rpx;
font-size: 24rpx;
color: #666;
}
.letter-index {
position: fixed;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
background-color: rgba(255, 255, 255, 0.9);
border-radius: 20rpx;
padding: 10rpx 5rpx;
z-index: 999;
}
.letter-item {
padding: 8rpx 12rpx;
font-size: 24rpx;
color: #666;
text-align: center;
border-radius: 10rpx;
}
.letter-item.active {
color: #8B2316;
font-weight: 600;
}
</style>
+1 -1
View File
@@ -625,7 +625,7 @@
// 搜索患者 // 搜索患者
const searchPatients = () => { const searchPatients = () => {
navTo({ navTo({
url: `/pages_app/searchPatient/searchPatient` url: `/pages_app/searchPatient/searchPatient?from=patientMsg`
}) })
}; };
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,481 @@
<template>
<view class="content">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="患者消息"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right">
<uni-icons type="search" size="24" color="#8B2316" @click="searchPatients"></uni-icons>
<uni-icons type="staff" size="24" color="#8B2316" @click="managePatients" style="margin-left: 30rpx;"></uni-icons>
</view>
</template>
</uni-nav-bar>
</view>
<scroll-view
class="message-list"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
>
<empty></empty>
</scroll-view>
<view class="tab-bar">
<view class="tab-item active">
<text class="tab-text">患者消息</text>
</view>
<view class="tab-item" @click="goListTab">
<text class="tab-text">患者列表</text>
<view class="tab-dot" v-if="hasNewPatient">
<uni-badge class="uni-badge-left-margin" :text="applyList.length" :offset="[-3, -3]" size="small"></uni-badge>
</view>
</view>
<view class="tab-item" @click="goPlanTab">
<text class="tab-text">随访计划</text>
</view>
</view>
</view>
</template>
<script setup>
<<<<<<< HEAD
import { computed, ref } from 'vue';
import { onBackPress, onLoad, onShow } from '@dcloudio/uni-app';
import api from '@/api/api.js';
import navTo from '@/utils/navTo.js';
=======
import { ref, getCurrentInstance, computed, nextTick, shallowRef } from 'vue';
import { onShow,onLoad,onBackPress,onHide} from "@dcloudio/uni-app";
import dayImg from "@/static/visit_data11.png"
import planImg from "@/static/visitplan.png"
import api from '@/api/api.js';
import navTo from '@/utils/navTo.js';
import docUrl from '@/utils/docUrl.js';
import unidialog from '@/components/dialog/dialog.vue'
import defaultImg from "@/static/default.png"
import newPatientImg from "@/static/new_patient.png"
import delImg from "@/static/iv_delete.png"
import editImg from "@/static/edit_patitent.png"
const patientList = ref([]);
const visible = ref(false)
const message = ref('')
const selectedPatientUuid = ref('')
const selectedPatientName = ref('')
const patientDataLoaded = ref(false);
const patientDataLoading = ref(false);
const firstLetterCache = new Map();
const showAlert = ref(false);
const navHeight = ref(40);
onBackPress(() => {
plus.runtime.quit();
return true;
});
onHide(() => {
patientDataLoaded.value = false;
});
import alertImg from "@/static/patientgif.png"
const getNavHeight = () => {
const systemInfo = uni.getSystemInfoSync();
console.log(2223);
console.log(systemInfo);
navHeight.value = systemInfo.statusBarHeight + 44;
console.log(navHeight.value);
}
import pinyin from 'pinyin';
import dayjs from 'dayjs'
import lineImg from "@/static/item_visitplan_fg.png"
const loadFinish = ref(false);
//import ConversationList from './conversation-list/index.vue'
const title = ref('患者消息');
const goPatientDetail = (uuid) => {
navTo({
url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}&from=patientMsg`
})
}
const editPatient = (uuid) => {
console.log(uuid)
navTo({
url: `/pages_app/patientSetting/patientSetting?uuid=${uuid}?from=patientMsg`
})
}
const onPatientItemLongPress = (item) => {
if (!item || !item.uuid) return
selectedPatientUuid.value = item.uuid
selectedPatientName.value = item.nickname || item.realName
message.value = `确认解除[${selectedPatientName.value || '该患者'}]的随访关系?`
visible.value = true
}
const deleteFollowUpFromList = async () => {
if (!selectedPatientUuid.value) {
visible.value = false
return
}
const res = await api.cancelRes({
patientUuid: selectedPatientUuid.value
})
if (res.code == 1) {
uni.showToast({
title: '解除成功',
icon: 'none'
})
patientListByGBK()
}
visible.value = false
}
const avatarErrorMap = ref({});
const getAvatarKey = (item = {}) => String(item.uuid || item.id || item.realName || item.nickname || '');
const normalizeAvatarUrl = (photo) => {
const raw = String(photo || '').trim();
if (!raw) return defaultImg;
if (/^https?:\/\//i.test(raw)) return raw;
return `${docUrl}${raw}`;
};
const getPatientAvatar = (item = {}) => {
const key = getAvatarKey(item);
if (key && avatarErrorMap.value[key]) return defaultImg;
return item.avatarUrl || defaultImg;
};
const handlePatientAvatarError = (item) => {
if (!item) return;
const key = getAvatarKey(item);
if (!key) return;
avatarErrorMap.value = {
...avatarErrorMap.value,
[key]: true
};
}
const goCode = () => {
navTo({
url: `/pages_app/myCode/myCode?from=patientMsg`
})
}
// 仅保留年月日
const formatYMD = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('YYYY-MM-DD') : '';
}
const formatYearMonth = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('YYYY年MM月') : '';
}
// 格式化显示日(如:03)
const formatDay = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('DD日') : '';
}
// 申请列表数据
const messageList = ref([
{
id: 1,
patientName: '测试',
messagePreview: '[图片]',
time: '2025-08-11',
avatar: ''
}
]);
// 当前激活的标签
const activeTab = ref('message');
const activeLetter = ref('A');
const scrollIntoViewId = ref('');
// 控制添加菜单弹窗显示
const showAddMenuFlag = ref(false);
// 组件实例(用于 selectorQuery 作用域)
const { proxy } = getCurrentInstance();
const LETTER_ORDER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const sortLetter = (a, b) => {
if (a === '#') return 1;
if (b === '#') return -1;
const ai = LETTER_ORDER.indexOf(a);
const bi = LETTER_ORDER.indexOf(b);
if (ai === -1 && bi === -1) return String(a).localeCompare(String(b));
if (ai === -1) return 1;
if (bi === -1) return -1;
return ai - bi;
};
// up-index-list 索引使用实际分组字母,避免索引与锚点数量不一致
const indexList = ref([]);
// 列表高度(像素):窗口高度 - 顶部导航(rpx转px) - 底部tab(约100rpx->px) - 操作区(约200rpx->px)
const groupsListHeight = ref(0);
const rpxToPx = (rpx) => {
const { screenWidth } = uni.getSystemInfoSync();
return Math.round((screenWidth / 750) * rpx);
};
const computeListHeight = () => {
const { windowHeight } = uni.getSystemInfoSync();
const reserved = rpxToPx(240); // 140rpx(nav) + 100rpx(tab) 近似
groupsListHeight.value = Math.max(0, windowHeight - reserved);
};
>>>>>>> cbc427e41ce2bf41f4bd01d9206215c015e257ae
const applyList = ref([]);
const isRefreshing = ref(false);
const hasNewPatient = computed(() => applyList.value.length > 0);
const getApplyList = async () => {
try {
const res = await api.applyList();
if (res && res.code === 200) {
applyList.value = res.data || [];
}
} catch (error) {}
};
const onRefresh = async () => {
isRefreshing.value = true;
try {
await getApplyList();
uni.showToast({
title: '刷新成功',
icon: 'none',
duration: 1200
});
<<<<<<< HEAD
} finally {
isRefreshing.value = false;
=======
};
// 随访计划上拉加载
const onFollowUpLoadMore = async () => {
if (!followUpHasMore.value || followUpLoading.value) return;
await getFollowUpList(false);
};
// 搜索患者
const searchPatients = () => {
navTo({
url: `/pages_app/searchPatient/searchPatient?from=patientMsg`
})
};
// 管理患者
const managePatients = () => {
uni.showToast({
title: '患者管理',
icon: 'none'
});
};
// 打开消息
const openMessage = (item) => {
uni.showToast({
title: `打开患者 ${item?.patientName || '未知'} 的消息`,
icon: 'none'
});
// 这里可以跳转到消息详情页面
// uni.navigateTo({
// url: `/pages_app/messageDetail/messageDetail?id=${item?.id}`
// });
};
// 切换标签
const switchTab = (tab) => {
activeTab.value = tab;
switch(tab) {
case 'message':
// 患者消息页面逻辑 - 刷新申请列表
uni.sendNativeEvent('goPatientMsg', {
msg: 'goPatientMsg'
},ret => {
console.log(ret);
})
// getApplyList();
// title.value = '患者消息';
break;
case 'list':
title.value = '患者列表';
if (patientDataLoaded.value) {
loadFinish.value = true;
break;
}
uni.showLoading({
title: '加载中...',
mask: true
});
patientListByGBK();
// 显示患者列表
break;
case 'plan':
title.value = '随访计划';
// 随访计划页面 - 加载随访计划数据
if (followUpList.value.length === 0) {
getFollowUpList(true);
}
break;
}
};
// 添加新患者
const addNewPatient = () => {
navTo({
url: '/pages_app/myPatient/myPatient?from=patientMsg'
})
};
// 管理患者分组
const managePatientGroups = () => {
navTo({
url: '/pages_app/patientGroup/patientGroup?from=patientMsg'
})
};
// 打开患者详情
const openPatient = (patientName) => {
uni.showToast({
title: `打开患者: ${patientName}`,
icon: 'none'
});
};
// 添加随访计划
const addFollowUpPlan = () => {
showAddMenuFlag.value = false;
uni.navigateTo({
url: '/pages_app/visit/visit?from=patientMsg'
});
};
const goBack = () => {
plus.runtime.quit();
>>>>>>> cbc427e41ce2bf41f4bd01d9206215c015e257ae
}
};
const searchPatients = () => {
navTo({
url: '/pages_app/searchPatient/searchPatient'
});
};
const managePatients = () => {
uni.showToast({
title: '患者管理',
icon: 'none'
});
};
const goListTab = () => {
navTo({
url: '/pages_app/patientMsg/patientList'
});
};
const goPlanTab = () => {
navTo({
url: '/pages_app/patientMsg/patientPlan'
});
};
const goBack = () => {
plus.runtime.quit();
};
const notifyNativePatientMsg = () => {
uni.sendNativeEvent('goPatientMsg', { msg: 'goPatientMsg' }, () => {});
};
onBackPress(() => {
plus.runtime.quit();
return true;
});
onLoad((options) => {
if (options.type === 'list') {
goListTab();
return;
}
if (options.type === 'plan') {
goPlanTab();
return;
}
getApplyList();
notifyNativePatientMsg();
});
onShow(() => {
getApplyList();
notifyNativePatientMsg();
});
</script>
<style lang="scss" scoped>
.content {
background-color: #f5f5f5;
height: 100vh;
overflow-y: hidden;
}
.nav-right {
display: flex;
align-items: center;
}
.message-list {
margin-top: calc(var(--status-bar-height) + 44px);
height: calc(100vh - var(--status-bar-height) - 44px - 103rpx);
background: #fff;
}
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #f8f8f8;
display: flex;
align-items: center;
border-top: 1rpx solid #e0e0e0;
z-index: 999;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
height: 100%;
}
.tab-item:nth-child(2) {
border: 2rpx solid #ccc;
border-top: none;
border-bottom: none;
}
.tab-text {
font-size: 32rpx;
color: #999;
}
.tab-item.active .tab-text {
color: #8B2316;
font-weight: 500;
}
.tab-dot {
position: absolute;
top: 18rpx;
right: 28rpx;
}
</style>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,201 @@
<template>
<view class="content">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="患者消息"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right">
<uni-icons type="search" size="24" color="#8B2316" @click="searchPatients"></uni-icons>
<uni-icons type="staff" size="24" color="#8B2316" @click="managePatients" style="margin-left: 30rpx;"></uni-icons>
</view>
</template>
</uni-nav-bar>
</view>
<scroll-view
class="message-list"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
>
<empty></empty>
</scroll-view>
<view class="tab-bar">
<view class="tab-item active">
<text class="tab-text">患者消息</text>
</view>
<view class="tab-item" @click="goListTab">
<text class="tab-text">患者列表</text>
<view class="tab-dot" v-if="hasNewPatient">
<uni-badge class="uni-badge-left-margin" :text="applyList.length" :offset="[-3, -3]" size="small"></uni-badge>
</view>
</view>
<view class="tab-item" @click="goPlanTab">
<text class="tab-text">随访计划</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue';
import { onBackPress, onLoad, onShow } from '@dcloudio/uni-app';
import api from '@/api/api.js';
import navTo from '@/utils/navTo.js';
const applyList = ref([]);
const isRefreshing = ref(false);
const hasNewPatient = computed(() => applyList.value.length > 0);
const getApplyList = async () => {
try {
const res = await api.applyList();
if (res && res.code === 200) {
applyList.value = res.data || [];
}
} catch (error) {}
};
const onRefresh = async () => {
isRefreshing.value = true;
try {
await getApplyList();
uni.showToast({
title: '刷新成功',
icon: 'none',
duration: 1200
});
} finally {
isRefreshing.value = false;
}
};
const searchPatients = () => {
navTo({
url: '/pages_app/searchPatient/searchPatient'
});
};
const managePatients = () => {
uni.showToast({
title: '患者管理',
icon: 'none'
});
};
const goListTab = () => {
navTo({
url: '/pages_app/patientMsg/patientList'
});
};
const goPlanTab = () => {
navTo({
url: '/pages_app/patientMsg/patientPlan'
});
};
const goBack = () => {
plus.runtime.quit();
};
const notifyNativePatientMsg = () => {
uni.sendNativeEvent('goPatientMsg', { msg: 'goPatientMsg' }, () => {});
};
onBackPress(() => {
plus.runtime.quit();
return true;
});
onLoad((options) => {
if (options.type === 'list') {
goListTab();
return;
}
if (options.type === 'plan') {
goPlanTab();
return;
}
getApplyList();
notifyNativePatientMsg();
});
onShow(() => {
getApplyList();
notifyNativePatientMsg();
});
</script>
<style lang="scss" scoped>
.content {
background-color: #f5f5f5;
height: 100vh;
overflow-y: hidden;
}
.nav-right {
display: flex;
align-items: center;
}
.message-list {
margin-top: calc(var(--status-bar-height) + 44px);
height: calc(100vh - var(--status-bar-height) - 44px - 103rpx);
background: #fff;
}
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #f8f8f8;
display: flex;
align-items: center;
border-top: 1rpx solid #e0e0e0;
z-index: 999;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
height: 100%;
}
.tab-item:nth-child(2) {
border: 2rpx solid #ccc;
border-top: none;
border-bottom: none;
}
.tab-text {
font-size: 32rpx;
color: #999;
}
.tab-item.active .tab-text {
color: #8B2316;
font-weight: 500;
}
.tab-dot {
position: absolute;
top: 18rpx;
right: 28rpx;
}
</style>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+378
View File
@@ -0,0 +1,378 @@
<template>
<view class="content">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="随访计划"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right" @click="showAddMenu">
<view class="save-btn">添加</view>
</view>
</template>
</uni-nav-bar>
</view>
<view class="plan">
<scroll-view
class="plan-scroll"
scroll-y="true"
refresher-enabled="true"
:refresher-triggered="followUpRefreshing"
@refresherrefresh="onFollowUpRefresh"
@scrolltolower="onFollowUpLoadMore"
:lower-threshold="100"
>
<view class="plan-list" v-if="groupedFollowUpList.length > 0">
<view class="plan-group" v-for="group in groupedFollowUpList" :key="group.yearMonth">
<view class="group-header">{{ group.yearMonth }}</view>
<view class="plan-card" v-for="item in group.items" :key="item.uuid" @click="goFollowDetail(item)">
<view class="left-rail">
<text class="day">{{ formatDay(item.datetime) }}</text>
</view>
<view class="linebox">
<up-image :src="lineImg" width="14rpx" height="180rpx"></up-image>
</view>
<view class="right-content">
<view class="leftcontent">
<view class="note">{{ item.note }}</view>
<view class="name">{{ item.patientname }}</view>
</view>
<uni-icons type="forward" size="20" color="#999"></uni-icons>
</view>
</view>
</view>
<view class="load-more" v-if="followUpLoading">
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
<text class="load-text">加载中...</text>
</view>
<view class="no-more" v-if="!followUpHasMore && followUpList.length > 0">
<text class="no-more-text">没有更多数据了</text>
</view>
</view>
<empty v-else></empty>
</scroll-view>
<view class="add-menu-popup" v-if="showAddMenuFlag" @click="hideAddMenu">
<view class="menu-content" @click.stop>
<view class="menu-item" @click="addSchedule">
<up-image :src="dayImg" width="34rpx" height="34rpx"></up-image>
<text class="menu-text">添加日程</text>
</view>
<view class="menu-divider"></view>
<view class="menu-item" @click="addFollowUpPlan">
<up-image :src="planImg" width="34rpx" height="34rpx"></up-image>
<text class="menu-text">添加随访计划</text>
</view>
</view>
</view>
</view>
<view class="tab-bar">
<view class="tab-item" @click="goMessageTab">
<text class="tab-text">患者消息</text>
</view>
<view class="tab-item" @click="goListTab">
<text class="tab-text">患者列表</text>
<view class="tab-dot" v-if="hasNewPatient"><uni-badge class="uni-badge-left-margin" :text="applyList.length" :offset="[-3, -3]" size="small" /></view>
</view>
<view class="tab-item active">
<text class="tab-text">随访计划</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue';
import { onBackPress, onLoad, onShow } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import api from '@/api/api.js';
import navTo from '@/utils/navTo.js';
import dayImg from '@/static/visit_data11.png';
import planImg from '@/static/visitplan.png';
import lineImg from '@/static/item_visitplan_fg.png';
const applyList = ref([]);
const page = ref(1);
const followUpList = ref([]);
const followUpLoading = ref(false);
const followUpRefreshing = ref(false);
const followUpHasMore = ref(true);
const followUpPageSize = ref(10);
const showAddMenuFlag = ref(false);
const hasNewPatient = computed(() => applyList.value.length > 0);
const formatYearMonth = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('YYYY年MM月') : '';
};
const formatDay = (input) => {
if (!input) return '';
const d = dayjs(input);
return d.isValid() ? d.format('DD日') : '';
};
const groupedFollowUpList = computed(() => {
if (!followUpList.value.length) return [];
const groups = new Map();
followUpList.value.forEach(item => {
const yearMonth = formatYearMonth(item.datetime);
if (!groups.has(yearMonth)) groups.set(yearMonth, []);
groups.get(yearMonth).push(item);
});
return Array.from(groups.entries())
.map(([yearMonth, items]) => ({
yearMonth,
items: items.sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
}))
.sort((a, b) => new Date(a.items[0].datetime) - new Date(b.items[0].datetime));
});
const getApplyList = async () => {
try {
const res = await api.applyList();
if (res && res.code === 200) {
applyList.value = res.data || [];
}
} catch (error) {}
};
const getFollowUpList = async (isRefresh = false) => {
if (followUpLoading.value) return;
followUpLoading.value = true;
try {
const currentPage = isRefresh ? 1 : page.value;
const res = await api.followUpList({
page: currentPage,
pageSize: followUpPageSize.value
});
if (res.code === 200) {
const newData = res.data.list || [];
if (isRefresh) {
followUpList.value = newData;
page.value = 1;
} else {
followUpList.value = [...followUpList.value, ...newData];
}
followUpHasMore.value = newData.length >= followUpPageSize.value;
if (!isRefresh) page.value++;
}
} finally {
followUpLoading.value = false;
followUpRefreshing.value = false;
}
};
const onFollowUpRefresh = async () => {
followUpRefreshing.value = true;
await getFollowUpList(true);
uni.showToast({ title: '刷新成功', icon: 'none', duration: 1200 });
};
const onFollowUpLoadMore = async () => {
if (!followUpHasMore.value || followUpLoading.value) return;
await getFollowUpList(false);
};
const addFollowUpPlan = () => {
showAddMenuFlag.value = false;
navTo({ url: '/pages_app/visit/visit?from=patientMsg' });
};
const addSchedule = () => {
showAddMenuFlag.value = false;
navTo({ url: '/pages_app/schedule/schedule' });
};
const showAddMenu = () => {
showAddMenuFlag.value = true;
};
const hideAddMenu = () => {
showAddMenuFlag.value = false;
};
const goFollowDetail = (raw) => {
if (!raw) return;
navTo({
url: `/pages_app/followDetail/followDetail?followUpUuid=${encodeURIComponent(raw.uuid || '')}&patient_name=${raw.patientname}`
});
};
const goBack = () => {
plus.runtime.quit();
};
const goMessageTab = () => {
navTo({ url: '/pages_app/patientMsg/patientMsg' });
};
const goListTab = () => {
navTo({ url: '/pages_app/patientMsg/patientList' });
};
onBackPress(() => {
plus.runtime.quit();
return true;
});
onLoad(() => {
getApplyList();
getFollowUpList(true);
});
onShow(() => {
getApplyList();
});
</script>
<style lang="scss" scoped>
.content {
background-color: #f5f5f5;
height: 100vh;
overflow-y: hidden;
}
.save-btn {
font-size: 32rpx;
color: #8b2316;
font-weight: 500;
}
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #f8f8f8;
display: flex;
align-items: center;
border-top: 1rpx solid #e0e0e0;
z-index: 999;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
height: 100%;
}
.tab-item:nth-child(2) {
border: 2rpx solid #ccc;
border-top: none;
border-bottom: none;
}
.tab-item .tab-text {
font-size: 32rpx;
color: #999;
}
.tab-item.active .tab-text {
color: #8B2316;
font-weight: 500;
}
.tab-dot {
position: absolute;
top: 18rpx;
right: 28rpx;
}
.plan {
position: fixed;
top: calc(var(--status-bar-height) + 44px);
left: 0;
right: 0;
bottom: 110rpx;
}
.plan-scroll {
height: 100%;
}
.plan-list {
padding-bottom: 20rpx;
}
.plan-group {
background: #f5f5f5;
}
.group-header {
text-align: left;
background: #eee;
color: #333;
font-size: 30rpx;
padding: 20rpx 30rpx;
}
.plan-card {
display: flex;
align-items: center;
background: #fff;
padding: 26rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.linebox {
margin: 0 20rpx;
}
.left-rail .day {
font-size: 36rpx;
color: #8B2316;
}
.right-content {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.note {
font-size: 30rpx;
color: #333;
}
.name {
margin-top: 30rpx;
font-size: 28rpx;
color: #8B2316;
}
.load-more,
.no-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
}
.load-text,
.no-more-text {
font-size: 28rpx;
color: #999;
}
.add-menu-popup {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.menu-content {
background-color: #fff;
border-radius: 20rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.2);
width: 80%;
max-width: 400rpx;
overflow: hidden;
position: fixed;
top: calc(var(--status-bar-height) + 48px);
right: 20rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 30rpx 40rpx;
}
.menu-text {
font-size: 32rpx;
color: #333;
margin-left: 20rpx;
}
.menu-divider {
height: 1rpx;
background-color: #f0f0f0;
margin: 0 40rpx;
}
</style>
+10 -6
View File
@@ -16,7 +16,7 @@
<view class="contentbox"> <view class="contentbox">
<view class="form-block"> <view class="form-block">
<view class="label">备注</view> <view class="label">备注</view>
<input class="input" v-model.trim="remark" :placeholder="groupInfo.nickname || '给患者添加备注名'" placeholder-class="ph" maxlength="20"/> <input class="input" v-model.trim="remark" :placeholder="groupInfo.nickname || '给患者添加备注名'" placeholder-class="ph" maxlength="20" />
</view> </view>
<view class="row" v-if="nickname"> <view class="row" v-if="nickname">
<view class="left">申请消息为:我是{{ nickname }}</view> <view class="left">申请消息为:我是{{ nickname }}</view>
@@ -25,10 +25,10 @@
</view> </view>
</view> </view>
<view class="form-block" @click="goGroup"> <view class="form-block" @click="goGroup">
<view class="label">分组</view> <view class="label" @click.stop="goGroup">分组</view>
<view class="iptbox readonly"> <view class="iptbox readonly">
<input class="input readonly-input" v-model.trim="groupName" :placeholder="groupInfo.name || '通过分组给患者分类'" placeholder-class="ph" maxlength="20" readonly disabled/> <view class="input readonly-input" :class="{'ph':!groupInfo.name}" @click.stop="goGroup">{{groupInfo.name || '通过分组给患者分类'}}</view>
<uni-icons type="right" size="20" color="#666"></uni-icons> <uni-icons type="right" size="20" color="#666" @click.stop="goGroup"></uni-icons>
</view> </view>
</view> </view>
<view class="form-block"> <view class="form-block">
@@ -60,6 +60,7 @@
const noteLength = computed(() => (note.value || '').length) const noteLength = computed(() => (note.value || '').length)
const isRemarkEmpty = computed(() => !(remark.value || '').trim()) const isRemarkEmpty = computed(() => !(remark.value || '').trim())
const nickname = ref('') const nickname = ref('')
const tempName=ref('')
const goBack = () => { const goBack = () => {
uni.navigateBack() uni.navigateBack()
@@ -88,7 +89,7 @@
patientCardData.value = res patientCardData.value = res
groupInfo.value = res.group || {} groupInfo.value = res.group || {}
// 初始化表单数据 // 初始化表单数据
remark.value = res.group?.nickname || '' remark.value = res.group?.nickname || remark.value
note.value = res.group?.note || '' note.value = res.group?.note || ''
groupName.value = res.group?.name || '' groupName.value = res.group?.name || ''
} }
@@ -179,7 +180,10 @@
color: #333; color: #333;
&.readonly-input { &.readonly-input {
background: transparent; background: transparent;
color: #999; color: #333;
}
&.ph{
color: #808080;
} }
} }
.textarea{ .textarea{
+16 -2
View File
@@ -66,7 +66,7 @@
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import navTo from '@/utils/navTo.js' import navTo from '@/utils/navTo.js'
import { onLoad, onShow } from '@dcloudio/uni-app' import { onLoad, onShow,onBackPress} from '@dcloudio/uni-app'
import navBar from '@/components/navBar/navBar.vue' import navBar from '@/components/navBar/navBar.vue'
import api from '@/api/api.js' import api from '@/api/api.js'
import unidialog from '@/components/dialog/dialog.vue' import unidialog from '@/components/dialog/dialog.vue'
@@ -76,8 +76,19 @@
const groupInfo = ref({}) const groupInfo = ref({})
const visible = ref(false) const visible = ref(false)
const message = ref('') const message = ref('')
const from = ref('');
onBackPress(() => {
if(!from.value){
plus.runtime.quit();
return true;
}
});
const goBack = () => { const goBack = () => {
uni.navigateBack(); if(!from.value){
plus.runtime.quit();
}else{
uni.navigateBack();
}
} }
const goRemark = () => { const goRemark = () => {
navTo({ url: '/pages_app/patientRemark/patientRemark?uuid=' + patientUuid.value }) navTo({ url: '/pages_app/patientRemark/patientRemark?uuid=' + patientUuid.value })
@@ -88,6 +99,9 @@
} }
onLoad((options) => { onLoad((options) => {
patientUuid.value = options.uuid; patientUuid.value = options.uuid;
if(options.from){
from.value = options.from;
}
console.log(patientUuid.value) console.log(patientUuid.value)
}) })
onShow(() => { onShow(() => {
+1 -1
View File
@@ -399,7 +399,7 @@
const videoId = video.id || video.uuid; const videoId = video.id || video.uuid;
navTo({ navTo({
url: `/pages_app/videoDetail/videoDetail?id=${videoId}` url: `/pages_app/videoDetail/videoDetail?id=${videoId}&type=patientVideo`
}); });
}; };
+9
View File
@@ -225,6 +225,15 @@
checkUser(); checkUser();
loadData(false); loadData(false);
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "肝胆课件",
},
},
(ret) => {
console.log(ret);
});
}) })
const goSearch = () => { const goSearch = () => {
uni.sendNativeEvent( uni.sendNativeEvent(
+82 -6
View File
@@ -153,6 +153,7 @@ import isAndroid from "@/utils/platform.js";
const isFirstDownload = ref(false); const isFirstDownload = ref(false);
const path=ref(''); const path=ref('');
const from=ref(''); const from=ref('');
const tags = ref('');
const pptVisible = ref(false); const pptVisible = ref(false);
const nativeDownloadMaskView = ref(null); const nativeDownloadMaskView = ref(null);
const nativeDownloadPopupView = ref(null); const nativeDownloadPopupView = ref(null);
@@ -640,7 +641,7 @@ onShow(() => {
console.log('canDownload'); console.log('canDownload');
console.log(canDownload.value); console.log(canDownload.value);
if(!canDownload.value){ if(!canDownload.value){
ganDanFileDetials(); ganDanFileDetials(true);
}else{ }else{
if(!hasDownload.value){ if(!hasDownload.value){
downloadGanDanFile('auto'); downloadGanDanFile('auto');
@@ -923,8 +924,6 @@ function createNativePopup() {
nativeMaskView.value.addEventListener('touchstart', handleMaskClick, false); nativeMaskView.value.addEventListener('touchstart', handleMaskClick, false);
nativePopupView.value.addEventListener('click', handlePanelClick, false); nativePopupView.value.addEventListener('click', handlePanelClick, false);
nativePopupView.value.addEventListener('touchstart', handlePanelClick, false);
nativePopupView.value.addEventListener('touchend', handlePanelClick, false);
// #endif // #endif
} }
@@ -991,6 +990,17 @@ const collection = () => {
title: "收藏成功", title: "收藏成功",
icon: "none", icon: "none",
}); });
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "collect",
page_type: "肝胆课件",
resource_name:shareTitle.value,
resource_option: tags.value,
},
},
(ret) => {
console.log(ret);
});
ganDanFileDetials(); ganDanFileDetials();
} }
}); });
@@ -1154,19 +1164,45 @@ const downLoadByType=()=>{
const downloadGanDanFile = (type) => { const downloadGanDanFile = (type) => {
showPptDownloadTip(); showPptDownloadTip();
let order_id=''; let order_id='';
let event_val=0;
let event_unit='分';
if(type=='free'){ if(type=='free'){
order_id='FREE'; order_id='FREE';
event_val=0;
} else if(type=='welfare'){ } else if(type=='welfare'){
order_id='USEWELFARENUM'; order_id='USEWELFARENUM';
event_val=0;
}else if(type=='freeRecord'){ }else if(type=='freeRecord'){
order_id='FREERECORD'; order_id='FREERECORD';
event_val=0;
}else if(type=="auto"){ }else if(type=="auto"){
order_id= orderId.value; order_id= orderId.value;
event_val=orderInfo.value.price;
}else if(type=="reDownload"){ }else if(type=="reDownload"){
order_id=orderInfo.value.order.order_id+"&R"; order_id=orderInfo.value.order.order_id+"&R";
event_val=0;
} }
console.log('1218'); console.log('1218');
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "order_gdfile_download",
page_type: "肝胆课件",
resource_name:shareTitle.value,
resource_option: tags.value,
event_val:event_val,
event_unit:'分',
number:1,
},
},
(ret) => {
console.log(ret);
});
api.downloadGanDanFileV2({ api.downloadGanDanFileV2({
file_uuid: uuid.value, file_uuid: uuid.value,
order_id: order_id, order_id: order_id,
@@ -1310,11 +1346,13 @@ const base64ToFile=(base64Str, fileName, callback)=>{
const ganDanFileDetials = async () => { const ganDanFileDetials = async (flag = false) => {
const res = await api.ganDanFileDetials({ const res = await api.ganDanFileDetials({
file_uuid: uuid.value, file_uuid: uuid.value,
}); });
if (res.code == 200) {res.data if (res.code == 200) {
tags.value = res.data.tags?res.data.tags:'';
orderInfo.value = res.data; orderInfo.value = res.data;
// 设置分享文案 // 设置分享文案
shareTitle.value = res.data.name; shareTitle.value = res.data.name;
@@ -1326,7 +1364,23 @@ const ganDanFileDetials = async () => {
order.value = res.data.order; order.value = res.data.order;
let money = res.data.price/ 100; let money = res.data.price/ 100;
price.value = money > 1 ? money.toFixed(1) : money.toFixed(2); price.value = money > 1 ? money.toFixed(1) : money.toFixed(2);
if(flag){
try {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "detail_page",
page_type: "肝胆课件",
resource_name:res.data.name,
resource_option: tags.value,
},
},
(ret) => {
console.log(ret);
});
} catch (error) {
}
}
downloadTasks.value.forEach(item => { downloadTasks.value.forEach(item => {
if (item.id == uuid.value) { if (item.id == uuid.value) {
uni.getSavedFileInfo({ uni.getSavedFileInfo({
@@ -1478,6 +1532,17 @@ const resumeDownloadingTasks = () => {
// ===== 分享到微信 ===== // ===== 分享到微信 =====
const shareToWechat = () => { const shareToWechat = () => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: "肝胆课件",
resource_name:shareTitle.value,
resource_option: tags.value,
},
},
(ret) => {
console.log(ret);
});
uni.downloadFile({ uni.downloadFile({
url: 'https://doc.igandan.com/app/html/img/2016/20160714132557.png', url: 'https://doc.igandan.com/app/html/img/2016/20160714132557.png',
success: function (res) { success: function (res) {
@@ -1560,6 +1625,17 @@ const shareToWechat = () => {
// ===== 分享到朋友圈 ===== // ===== 分享到朋友圈 =====
const shareToMoments = () => { const shareToMoments = () => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: "肝胆课件",
resource_name:shareTitle.value,
resource_option: tags.value,
},
},
(ret) => {
console.log(ret);
});
uni.downloadFile({ uni.downloadFile({
url: 'https://doc.igandan.com/app/html/img/2016/20160714132557.png', url: 'https://doc.igandan.com/app/html/img/2016/20160714132557.png',
success: function (res) { success: function (res) {
+4 -1
View File
@@ -76,15 +76,18 @@ const addCommentV2=async()=>{
comment:replyText.value+'||'+name.value+':'+comment_partent.value comment:replyText.value+'||'+name.value+':'+comment_partent.value
}) })
if(res.code==200){ if(res.code==200){
uni.$emit('replySuccess',true);
console.log('replySuccess')
uni.showToast({ title: '回复成功', icon: 'none' }) uni.showToast({ title: '回复成功', icon: 'none' })
replyText.value = ''; replyText.value = '';
uni.navigateBack(); uni.navigateBack();
} }
} }
onUnmounted(() => { onUnmounted(() => {
// 清理工作 // 清理工作
console.log('回复页面已卸载') //console.log('回复页面已卸载')
}) })
// 方法 // 方法
+22 -3
View File
@@ -30,7 +30,7 @@
</view> </view>
<view class="section-content"> <view class="section-content">
<text class="content-text">{{ selectedPatient || '请选择患者' }}</text> <text class="content-text">{{ selectedPatient || '请选择患者' }}</text>
<uni-icons type="right" class ="icon" size="20" color="#999"></uni-icons> <uni-icons type="right" class ="icon" size="20" color="#999" v-if="type != 'patientDetail'"></uni-icons>
</view> </view>
</view> </view>
@@ -105,7 +105,7 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { onShow } from "@dcloudio/uni-app"; import { onShow,onLoad } from "@dcloudio/uni-app";
import navTo from '@/utils/navTo.js'; import navTo from '@/utils/navTo.js';
import api from '@/api/api.js'; import api from '@/api/api.js';
// 表单数据 // 表单数据
@@ -124,6 +124,20 @@
const goBack = () => { const goBack = () => {
uni.navigateBack(); uni.navigateBack();
}; };
const patientName = ref('')
const type = ref('')
onLoad((options) => {
if(options.uuid){
patientUuid.value = options.uuid
}
if(options.patient_name){
selectedPatient.value = options.patient_name
}
if(options.type){
type.value = options.type
}
})
const addFollowUps=()=>{ const addFollowUps=()=>{
api.addFollowUps({ api.addFollowUps({
patient_uuid: patientUuid.value, patient_uuid: patientUuid.value,
@@ -160,7 +174,10 @@
// 选择患者 // 选择患者
const selectPatient = () => { const selectPatient = () => {
navTo({ if(type.value == 'patientDetail'){
return
}else{
navTo({
url:'/pages_app/selectPatientSingle/selectPatientSingle', url:'/pages_app/selectPatientSingle/selectPatientSingle',
events: { events: {
onPatientsSelected: (data) => { onPatientsSelected: (data) => {
@@ -169,6 +186,8 @@
} }
} }
}) })
}
}; };
// 选择日期 // 选择日期
+54 -13
View File
@@ -1,7 +1,21 @@
<template> <template>
<view class="select-page"> <view class="select-page">
<navBar title="搜索患者" /> <view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="搜索患者"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
</uni-nav-bar>
</view>
<!-- 搜索框 --> <!-- 搜索框 -->
<view class="search-bar"> <view class="search-bar">
@@ -19,6 +33,7 @@
<image class="avatar" :src="docUrl + (p.photo || '')" mode="aspectFill" /> <image class="avatar" :src="docUrl + (p.photo || '')" mode="aspectFill" />
<view class="name">{{ p.nickname || p.realName }}</view> <view class="name">{{ p.nickname || p.realName }}</view>
</view> </view>
<empty v-if="availablePatientList.length == 0" :emptyDesc="'暂无搜索结果'"></empty>
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
@@ -26,29 +41,38 @@
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import docUrl from '@/utils/docUrl.js' import docUrl from '@/utils/docUrl.js'
import { onShow,onLoad} from "@dcloudio/uni-app"; import { onShow,onLoad,onBackPress} from "@dcloudio/uni-app";
import empty from "@/components/empty/empty.vue"
import api from '@/api/api.js' import api from '@/api/api.js'
import navTo from '@/utils/navTo.js' import navTo from '@/utils/navTo.js'
import navBar from '@/components/navBar/navBar.vue' import navBar from '@/components/navBar/navBar.vue'
const from = ref(''); const from = ref('');
const keyword = ref('') const keyword = ref('')
const patientList = ref([]) const patientList = ref([])
const availablePatientList = ref([])
// 计算属性:显示所有患者,但标记已选中的状态 // 计算属性:显示所有患者,但标记已选中的状态
const availablePatientList = computed(() => { // const availablePatientList = computed(() => {
return patientList.value // return patientList.value
}) // })
onBackPress(() => {
if(!from.value){
plus.runtime.quit();
return true;
}
});
const patientListByGBK = async () => { const patientListByGBK = async () => {
const res = await api.patientListByGBK(); const res = await api.patientListByGBK();
if(res.code == 1){ if(res.code == 1){
patientList.value = res.data; patientList.value = res.data;
availablePatientList.value = res.data;
} }
}; };
onLoad((options) => { onLoad((options) => {
if(options.from == 'chatMsg'){ if(options.from){
from.value = 'chatMsg'; from.value = options.from;
} }
}) })
onShow(() => { onShow(() => {
patientListByGBK(); patientListByGBK();
@@ -58,13 +82,19 @@
const goDetail = (uuid) => { const goDetail = (uuid) => {
navTo({ url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}` }) navTo({ url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}&from=searchPatient`})
} }
const onSearch = () => { const onSearch = () => {
patientList.value = patientList.value.filter(p => p.realName.indexOf(keyword.value) !== -1 || (p.nickname && p.nickname.indexOf(keyword.value) !== -1) || p.mobile.indexOf(keyword.value) !== -1) availablePatientList.value = patientList.value.filter(p => p.realName.indexOf(keyword.value) !== -1 || (p.nickname && p.nickname.indexOf(keyword.value) !== -1) || p.mobile.indexOf(keyword.value) !== -1)
} }
const goBack = () => uni.navigateBack() const goBack = () => {
if(!from.value){
plus.runtime.quit();
}else{
uni.navigateBack();
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -75,8 +105,12 @@ const goDetail = (uuid) => {
.confirm-btn{ background:#7f7f7f; padding: 10rpx 18rpx; border-radius: 26rpx; } .confirm-btn{ background:#7f7f7f; padding: 10rpx 18rpx; border-radius: 26rpx; }
.confirm-btn.active{ background:#8B2316; } .confirm-btn.active{ background:#8B2316; }
.search-bar{ .search-bar{
position: fixed;
left:30rpx;
right:30rpx;
top:calc(var(--status-bar-height) + 44px);
border: 2rpx solid #eee; border: 2rpx solid #eee;
margin: 20rpx 30rpx; display:flex; align-items:center; gap: 16rpx; margin: 20rpx 0rpx; display:flex; align-items:center; gap: 16rpx;
.input-wrap{ flex:1; background:#fff; border-radius: 12rpx; padding: 16rpx 20rpx; } .input-wrap{ flex:1; background:#fff; border-radius: 12rpx; padding: 16rpx 20rpx; }
.search-input{ font-size: 28rpx; color:#333; } .search-input{ font-size: 28rpx; color:#333; }
.ph{ color:#bfbfbf; } .ph{ color:#bfbfbf; }
@@ -87,7 +121,14 @@ const goDetail = (uuid) => {
width: 88rpx; height: 72rpx; background:#fff; width: 88rpx; height: 72rpx; background:#fff;
} }
} }
.list{ border-radius: 12rpx; } .list{
border-radius: 12rpx;
position: fixed;
left: 0;
right: 0;
top: calc(var(--status-bar-height) + 44px + 115rpx);
bottom: 0rpx;
}
.item{background:#fff; display:flex; align-items:center;padding: 24rpx 30rpx; border-bottom: 2rpx solid #eee; } .item{background:#fff; display:flex; align-items:center;padding: 24rpx 30rpx; border-bottom: 2rpx solid #eee; }
.avatar{ width: 96rpx; height:96rpx; border-radius: 16rpx; background:#ffe; } .avatar{ width: 96rpx; height:96rpx; border-radius: 16rpx; background:#ffe; }
.name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; } .name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; }
@@ -0,0 +1,352 @@
<template>
<view class="select-page">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="选择患者"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="confirm-btn" :class="{ active: selectedIds.length > 0 }" @click="confirmSelect">
<text class="confirm-text">确定({{ selectedIds.length }})</text>
</view>
</template>
</uni-nav-bar>
</view>
<!-- 搜索框 -->
<view class="search-bar">
<view class="input-wrap">
<input class="search-input" v-model.trim="keyword" placeholder="搜索患者的备注名、昵称或手机号" placeholder-class="ph" @input="$u.debounce(onSearch, 500)" />
</view>
<view class="search-btn" @click="onSearch">
<uni-icons type="search" size="50rpx" color="#999" />
</view>
</view>
<!-- 列表 -->
<scroll-view class="list" scroll-y @scrolltolower="onScrollToLower" :lower-threshold="100">
<view class="item" @click="toggle(p.uuid)" v-for="p in availablePatientList" :key="p.uuid">
<image class="avatar" :src="getAvatarSrc(p)" mode="aspectFill" @error="handleAvatarError(p)" />
<view class="name">{{ p.realName || p.nickname}}</view>
<view class="check" >
<view class="circle" :class="{ active: selectedIds.includes(p.uuid) }"></view>
</view>
</view>
</scroll-view>
<view class="bottom-actions">
<view class="select-all-btn" @click="toggleSelectAll">
{{ isAllSelected ? '全不选' : '全选' }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed,nextTick} from 'vue'
import docUrl from '@/utils/docUrl.js'
import { onShow,onLoad} from "@dcloudio/uni-app";
import api from '@/api/api.js'
import navTo from '@/utils/navTo.js'
import defaultImg from "@/static/default.png"
const from = ref('');
const keyword = ref('')
const selectedIds = ref([])
const patientList = ref([])
const selectedDetail = ref([])
const availablePatientList = ref([])
const patientListPage = ref(1)
const patientListPageSize = ref(200)
const patientListHasMore = ref(true)
const patientListLoading = ref(false)
const patientListLoadingMore = ref(false)
const isAllSelected = computed(() => {
const total = patientList.value.length
return total > 0 && selectedIds.value.length === total
})
// 计算属性:显示所有患者,但标记已选中的状态
// const availablePatientList = computed(() => {
// return patientList.value
// })
const isApiSuccess = (res) => Number(res?.code) === 1 || Number(res?.code) === 200
const parsePagedPatients = (res) => {
const data = res?.data
if (Array.isArray(data)) {
return { list: data, total: undefined, pageNum: undefined, pages: undefined, isLastPage: undefined }
}
const list =
(Array.isArray(data?.list) && data.list) ||
(Array.isArray(data?.records) && data.records) ||
(Array.isArray(data?.rows) && data.rows) ||
(Array.isArray(data?.data) && data.data) ||
[]
const total = Number(data?.total ?? data?.count ?? data?.totalCount ?? undefined)
const pageNum = Number(data?.pageNum ?? data?.page ?? data?.current ?? undefined)
const pages = Number(data?.pages ?? data?.pageCount ?? undefined)
const isLastPage = typeof data?.isLastPage === 'boolean' ? data.isLastPage : undefined
return {
list,
total: Number.isFinite(total) ? total : undefined,
pageNum: Number.isFinite(pageNum) ? pageNum : undefined,
pages: Number.isFinite(pages) ? pages : undefined,
isLastPage
}
}
const mergePatientListUnique = (base = [], incoming = []) => {
if (!incoming.length) return base
const seen = new Set()
const merged = []
const pushUnique = (item) => {
const key = String(item?.uuid || item?.id || item?.mobile || '')
if (!key) {
merged.push(item)
return
}
if (seen.has(key)) return
seen.add(key)
merged.push(item)
}
base.forEach(pushUnique)
incoming.forEach(pushUnique)
return merged
}
const applyPatientList = (list = []) => {
patientList.value = list
onSearch()
}
const patientListByGBKPage = async ({ reset = false, showLoading = false } = {}) => {
if (patientListLoading.value || patientListLoadingMore.value) return
if (!reset && !patientListHasMore.value) return
if (reset) {
patientListLoading.value = true
patientListPage.value = 1
patientListHasMore.value = true
} else {
patientListLoadingMore.value = true
}
if (showLoading) {
uni.showLoading({ title: '加载中...', mask: true })
}
try {
const requestPage = patientListPage.value
const res = await api.patientListByGBKPage({
page: requestPage,
//pageNum: requestPage,
//current: requestPage,
pageSize: patientListPageSize.value,
//size: patientListPageSize.value
})
if (!isApiSuccess(res)) return
const { list, total, pageNum, pages, isLastPage } = parsePagedPatients(res)
const merged = reset ? list : mergePatientListUnique(patientList.value, list)
applyPatientList(merged)
if (typeof isLastPage === 'boolean') {
patientListHasMore.value = !isLastPage
} else if (typeof pages === 'number' && typeof pageNum === 'number') {
patientListHasMore.value = pageNum < pages
} else if (typeof total === 'number') {
patientListHasMore.value = merged.length < total
} else {
patientListHasMore.value = list.length >= patientListPageSize.value
}
if (patientListHasMore.value) {
patientListPage.value = typeof pageNum === 'number' ? pageNum + 1 : requestPage + 1
}
} finally {
patientListLoading.value = false
patientListLoadingMore.value = false
if (showLoading) {
nextTick(() => {
uni.hideLoading()
})
}
}
}
const onScrollToLower = () => {
void patientListByGBKPage({ reset: false, showLoading: false })
}
onLoad((options) => {
uni.showLoading({
title: '加载中...',
mask: true
})
if(options.from ){
from.value =options.from;
}
// 读取已选中的成员ID
try {
const preSelected = uni.getStorageSync('preSelectedIds')
if (Array.isArray(preSelected)) {
selectedIds.value = [...preSelected]
// 清理缓存
uni.removeStorageSync('preSelectedIds')
}
} catch (e) {}
void patientListByGBKPage({ reset: true, showLoading: true });
})
onShow(() => {
// 根据已选中的ID更新selectedDetail
updateSelectedDetail();
});
// 根据已选中的ID更新selectedDetail
const updateSelectedDetail = () => {
selectedDetail.value = selectedIds.value.map(id => {
const p = patientList.value.find(x => x.uuid === id)
return { uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickName: p?.nickName || '' }
})
}
const toggle = (id) => {
const i = selectedIds.value.indexOf(id)
if (i > -1) {
// 如果已选中,则取消选中
selectedIds.value.splice(i, 1)
const di = selectedDetail.value.findIndex(it => it.uuid === id)
if (di > -1) selectedDetail.value.splice(di, 1)
} else {
// 如果未选中,则选中
selectedIds.value.push(id)
const p = patientList.value.find(x => x.uuid === id)
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickName: p?.nickName || '' })
}
}
const getAvatarSrc = (patient) => {
if (patient?._avatarLoadError) return defaultImg
return patient?.photo ? (docUrl + patient.photo) : defaultImg
}
const handleAvatarError = (patient) => {
if (!patient) return
patient._avatarLoadError = true
}
const toggleSelectAll = () => {
if (!patientList.value.length) return
if (isAllSelected.value) {
selectedIds.value = []
selectedDetail.value = []
return
}
selectedIds.value = patientList.value.map(item => item.uuid)
selectedDetail.value = patientList.value.map(item => ({
uuid: item.uuid,
realName: item?.realName || '',
photo: item?.photo || '',
nickName: item?.nickName || ''
}))
}
const onSearch = () => {
if(keyword.value.replace(/\s/g, "")){
availablePatientList.value = patientList.value.filter(p => p.realName.indexOf(keyword.value) !== -1 || (p.nickname && p.nickname.indexOf(keyword.value) !== -1) || p.mobile.indexOf(keyword.value) !== -1)
}else{
availablePatientList.value = patientList.value
}
}
const goBack = () => uni.navigateBack()
const confirmSelect = () => {
if(from.value == 'chatMsg' || from.value == 'videoDetail'){
if(selectedIds.value.length >200){
uni.showToast({
title: '选择人数不能超过200',
icon: 'none'
})
return;
}
}
const payload = { ids: selectedIds.value, list: selectedDetail.value }
// 通过事件通道回传
try {
const pages = getCurrentPages()
const curr = pages[pages.length - 1]
const ec = curr?.getOpenerEventChannel?.()
ec?.emit && ec.emit('onPatientsSelected', payload);
console.log(selectedDetail.value)
uni.$emit('selectedChatPatientsSingle', {patients: selectedDetail.value });
} catch (e) {}
// 兜底:使用本地存储
try {
uni.setStorageSync('patientsSelectedPayload', payload) } catch (e) {}
uni.setStorageSync('selectedChatPatientsSingle', {patients: selectedDetail.value })
if(from.value == 'chatMsg'){
navTo({
url: '/pages_chat/groupSend/groupSend?from=chatMsg'
})
}else{
uni.navigateBack()
}
}
</script>
<style lang="scss" scoped>
.select-page{
min-height: 100vh; background:#fefefe;overflow: hidden;
}
.confirm-text{ color:#fff; font-size: 28rpx;white-space: nowrap; }
.confirm-btn{ background:#7f7f7f; padding: 10rpx 18rpx; border-radius: 26rpx; }
.confirm-btn.active{ background:#8B2316; }
.search-bar{
border: 2rpx solid #eee;
margin: 0rpx 30rpx; display:flex;
margin-top: calc(var(--status-bar-height) + 64px);
align-items:center; gap: 16rpx;
.input-wrap{ flex:1; background:#fff; border-radius: 12rpx; padding: 16rpx 20rpx; }
.search-input{ font-size: 28rpx; color:#333; }
.ph{ color:#bfbfbf; }
.search-btn{
display: flex;
align-items: center;
justify-content: center;
width: 88rpx; height: 72rpx; background:#fff;
}
}
.list{
border-radius: 12rpx;
position: fixed;
left: 0;
right: 0;
top: calc(var(--status-bar-height) + 64px + 94rpx);
bottom: 130rpx;
}
.item{background:#fff; display:flex; align-items:center;padding: 24rpx 30rpx; border-bottom: 2rpx solid #eee; }
.avatar{ width: 96rpx; height:96rpx; border-radius: 16rpx; background:#ffe; }
.name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; }
.check{ padding-left: 12rpx; }
.circle{ width: 40rpx; height: 40rpx; border-radius: 50%; border: 2rpx solid #cfcfcf; }
.circle.active{ background:#8B2316; border-color:#8B2316; position: relative; }
.circle.active::after{ content:''; position:absolute; left: 14rpx; top: 6rpx; width: 10rpx; height: 18rpx; border: 4rpx solid #fff; border-top: 0; border-left: 0; transform: rotate(45deg); }
.bottom-actions{
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-top: 1rpx solid #f0f0f0;
padding: 20rpx 30rpx 20rpx;
}
.select-all-btn{
height: 88rpx;
border-radius: 12rpx;
color: #8B2316;
background: #fff;
border: 2rpx solid #8B2316;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
</style>
+106 -23
View File
@@ -29,7 +29,7 @@
</view> </view>
<!-- 列表 --> <!-- 列表 -->
<scroll-view class="list" scroll-y> <scroll-view class="list" scroll-y @scrolltolower="onScrollToLower" :lower-threshold="100">
<view class="item" @click="toggle(p.uuid)" v-for="p in availablePatientList" :key="p.uuid"> <view class="item" @click="toggle(p.uuid)" v-for="p in availablePatientList" :key="p.uuid">
<image class="avatar" :src="getAvatarSrc(p)" mode="aspectFill" @error="handleAvatarError(p)" /> <image class="avatar" :src="getAvatarSrc(p)" mode="aspectFill" @error="handleAvatarError(p)" />
<view class="name">{{ p.realName || p.nickname}}</view> <view class="name">{{ p.realName || p.nickname}}</view>
@@ -60,6 +60,11 @@
const patientList = ref([]) const patientList = ref([])
const selectedDetail = ref([]) const selectedDetail = ref([])
const availablePatientList = ref([]) const availablePatientList = ref([])
const patientListPage = ref(1)
const patientListPageSize = ref(200)
const patientListHasMore = ref(true)
const patientListLoading = ref(false)
const patientListLoadingMore = ref(false)
const isAllSelected = computed(() => { const isAllSelected = computed(() => {
const total = patientList.value.length const total = patientList.value.length
return total > 0 && selectedIds.value.length === total return total > 0 && selectedIds.value.length === total
@@ -68,32 +73,110 @@
// const availablePatientList = computed(() => { // const availablePatientList = computed(() => {
// return patientList.value // return patientList.value
// }) // })
const patientListByGBK = async () => { const isApiSuccess = (res) => Number(res?.code) === 1 || Number(res?.code) === 200
const parsePagedPatients = (res) => {
const res = await api.patientListByGBK(); const data = res?.data
if(res.code == 1){ if (Array.isArray(data)) {
patientList.value = res.data; return { list: data, total: undefined, pageNum: undefined, pages: undefined, isLastPage: undefined }
availablePatientList.value = res.data;
nextTick(() => {
uni.hideLoading();
});
// if(res.data.length > 1000){
// setTimeout(() => {
// uni.hideLoading();
// }, 5000);
// }else{
// uni.hideLoading();
// }
} }
const list =
}; (Array.isArray(data?.list) && data.list) ||
(Array.isArray(data?.records) && data.records) ||
(Array.isArray(data?.rows) && data.rows) ||
(Array.isArray(data?.data) && data.data) ||
[]
const total = Number(data?.total ?? data?.count ?? data?.totalCount ?? undefined)
const pageNum = Number(data?.pageNum ?? data?.page ?? data?.current ?? undefined)
const pages = Number(data?.pages ?? data?.pageCount ?? undefined)
const isLastPage = typeof data?.isLastPage === 'boolean' ? data.isLastPage : undefined
return {
list,
total: Number.isFinite(total) ? total : undefined,
pageNum: Number.isFinite(pageNum) ? pageNum : undefined,
pages: Number.isFinite(pages) ? pages : undefined,
isLastPage
}
}
const mergePatientListUnique = (base = [], incoming = []) => {
if (!incoming.length) return base
const seen = new Set()
const merged = []
const pushUnique = (item) => {
const key = String(item?.uuid || item?.id || item?.mobile || '')
if (!key) {
merged.push(item)
return
}
if (seen.has(key)) return
seen.add(key)
merged.push(item)
}
base.forEach(pushUnique)
incoming.forEach(pushUnique)
return merged
}
const applyPatientList = (list = []) => {
patientList.value = list
onSearch()
}
const patientListByGBKPage = async ({ reset = false, showLoading = false } = {}) => {
if (patientListLoading.value || patientListLoadingMore.value) return
if (!reset && !patientListHasMore.value) return
if (reset) {
patientListLoading.value = true
patientListPage.value = 1
patientListHasMore.value = true
} else {
patientListLoadingMore.value = true
}
if (showLoading) {
uni.showLoading({ title: '加载中...', mask: true })
}
try {
const requestPage = patientListPage.value
const res = await api.patientListByGBKPage({
page: requestPage,
//pageNum: requestPage,
//current: requestPage,
pageSize: patientListPageSize.value,
//size: patientListPageSize.value
})
if (!isApiSuccess(res)) return
const { list, total, pageNum, pages, isLastPage } = parsePagedPatients(res)
const merged = reset ? list : mergePatientListUnique(patientList.value, list)
applyPatientList(merged)
if (typeof isLastPage === 'boolean') {
patientListHasMore.value = !isLastPage
} else if (typeof pages === 'number' && typeof pageNum === 'number') {
patientListHasMore.value = pageNum < pages
} else if (typeof total === 'number') {
patientListHasMore.value = merged.length < total
} else {
patientListHasMore.value = list.length >= patientListPageSize.value
}
if (patientListHasMore.value) {
patientListPage.value = typeof pageNum === 'number' ? pageNum + 1 : requestPage + 1
}
} finally {
patientListLoading.value = false
patientListLoadingMore.value = false
if (showLoading) {
nextTick(() => {
uni.hideLoading()
})
}
}
}
const onScrollToLower = () => {
void patientListByGBKPage({ reset: false, showLoading: false })
}
onLoad((options) => { onLoad((options) => {
uni.showLoading({ uni.showLoading({
title: '加载中...', title: '加载中...',
mask: true mask: true
}) })
if(options.from == 'chatMsg'){ if(options.from ){
from.value = 'chatMsg'; from.value =options.from;
} }
// 读取已选中的成员ID // 读取已选中的成员ID
try { try {
@@ -104,7 +187,7 @@
uni.removeStorageSync('preSelectedIds') uni.removeStorageSync('preSelectedIds')
} }
} catch (e) {} } catch (e) {}
patientListByGBK(); void patientListByGBKPage({ reset: true, showLoading: true });
}) })
onShow(() => { onShow(() => {
@@ -174,7 +257,7 @@
} }
const goBack = () => uni.navigateBack() const goBack = () => uni.navigateBack()
const confirmSelect = () => { const confirmSelect = () => {
if(from.value == 'chatMsg'){ if(from.value == 'chatMsg' || from.value == 'videoDetail'){
if(selectedIds.value.length >200){ if(selectedIds.value.length >200){
uni.showToast({ uni.showToast({
title: '选择人数不能超过200', title: '选择人数不能超过200',
+13 -2
View File
@@ -3,7 +3,7 @@
<view class="status_bar"></view> <view class="status_bar"></view>
<uni-nav-bar <uni-nav-bar
left-icon="left" left-icon="left"
title="肝胆视频1" title="肝胆视频"
@clickLeft="goBack" @clickLeft="goBack"
color="#8B2316" color="#8B2316"
:border="false" :border="false"
@@ -435,6 +435,15 @@ onLoad((options) => {
setTimeout(() => { setTimeout(() => {
loadCategoryVideos("all"); loadCategoryVideos("all");
}, 500); }, 500);
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "肝胆视频",
},
},
(ret) => {
console.log(ret);
});
}); });
// 加载轮播数据 // 加载轮播数据
@@ -690,9 +699,11 @@ const switchTab = (tabIndex) => {
// 播放视频 // 播放视频
const playVideo = (video) => { const playVideo = (video) => {
const videoId = video.id || video.uuid; const videoId = video.id || video.uuid;
console.log(222);
console.log(video.tags);
navTo({ navTo({
url: `/pages_app/videoDetail/videoDetail?id=${videoId}`, url: `/pages_app/videoDetail/videoDetail?id=${videoId}&tags=${video.tags}`,
}); });
}; };
+347 -7
View File
@@ -38,6 +38,9 @@
id="myVideo" id="myVideo"
object-fit="contain" object-fit="contain"
:autoplay="false" :autoplay="false"
@play="onVideoPlay"
@pause="onVideoPause"
@ended="onVideoEnded"
@error="onVideoError" @error="onVideoError"
></video> ></video>
<!-- <sunny-video <!-- <sunny-video
@@ -204,6 +207,12 @@
<view class="share-popup"> <view class="share-popup">
<view class="share-title">分享到</view> <view class="share-title">分享到</view>
<view class="share-content"> <view class="share-content">
<!-- <view class="share-item" @click="shareToPatient" v-if="type == 'patientVideo'">
<view class="share-icon wechat-icon" style="background: none;">
<image class="share-img" :src="paitientImg" mode="aspectFill" />
</view>
<text class="share-text">患者</text>
</view> -->
<view class="share-item" @click="shareToWechat"> <view class="share-item" @click="shareToWechat">
<view class="share-icon wechat-icon"> <view class="share-icon wechat-icon">
<image class="share-img" :src="wxImg" mode="aspectFill" /> <image class="share-img" :src="wxImg" mode="aspectFill" />
@@ -228,12 +237,25 @@
</view> </view>
</view> </view>
</uni-popup> </uni-popup>
<!-- 选择弹窗(复制自 groupMessage) -->
<view v-if="showModal" class="mask" @click="closeModal"></view>
<view v-if="showModal" class="center-modal">
<view class="modal-title">温馨提示</view>
<view class="modal-divider"></view>
<view class="modal-item" @click="onSelect('single')">
<text class="text">单独选择</text>
</view>
<view class="modal-divider"></view>
<view class="modal-item" @click="onSelect('group')">
<text class="text">分组选择</text>
</view>
</view>
</template> </template>
<script setup> <script setup>
import { ref, nextTick } from "vue"; import { ref, nextTick } from "vue";
//import uniVideo from "@/components/uniVideo/uniVideo.vue"; //import uniVideo from "@/components/uniVideo/uniVideo.vue";
import { onLoad, onShow,onReady } from "@dcloudio/uni-app"; import { onLoad, onShow,onReady,onUnload,onHide } from "@dcloudio/uni-app";
import unidialog from "@/components/dialog/dialog.vue"; import unidialog from "@/components/dialog/dialog.vue";
import collectImg from "@/static/icon_book_collect_sel.png"; import collectImg from "@/static/icon_book_collect_sel.png";
import discollectImg from "@/static/icon_book_collect_nor.png"; import discollectImg from "@/static/icon_book_collect_nor.png";
@@ -245,13 +267,16 @@ import logoImg from "@/static/weiboShare.png";
import isAndroid from "@/utils/platform.js"; import isAndroid from "@/utils/platform.js";
import avastarImg from "@/static/avastar.png"; import avastarImg from "@/static/avastar.png";
import playImg from "@/static/play.png"; import playImg from "@/static/play.png";
import paitientImg from "@/static/info_icon_1.png";
import api from "@/api/api"; import api from "@/api/api";
import docUrl from "@/utils/docUrl"; import docUrl from "@/utils/docUrl";
import navTo from "@/utils/navTo"; import navTo from "@/utils/navTo";
const shareLink = ref(""); const shareLink = ref("");
import otherHost from "@/utils/otherHost"; import otherHost from "@/utils/otherHost";
const selectedPatients = ref([]);
const video_uuid = ref(""); const video_uuid = ref("");
const videoInfo = ref({}); const videoInfo = ref({});
const tags = ref("");
const networkVisible = ref(false); const networkVisible = ref(false);
const networkContent = ref(""); const networkContent = ref("");
const pointVisible = ref(false); const pointVisible = ref(false);
@@ -260,6 +285,9 @@ const notEnoughVisible = ref(false);
const notEnoughContent = ref(""); const notEnoughContent = ref("");
const welfareNum = ref(0); const welfareNum = ref(0);
const isPlaying = ref(false); const isPlaying = ref(false);
const playTime = ref(0);
const timer = ref(null);
const hasPlay = ref(false);
// 删除弹窗相关 // 删除弹窗相关
const showDeleteModal = ref(false); const showDeleteModal = ref(false);
const currentDeleteItem = ref(null); const currentDeleteItem = ref(null);
@@ -270,6 +298,7 @@ const downLoadStatus = ref('start');
let downList = uni.getStorageSync("downLoadVideo") || []; let downList = uni.getStorageSync("downLoadVideo") || [];
const downLoadList = ref(downList); const downLoadList = ref(downList);
const isFullScreen = ref(false); const isFullScreen = ref(false);
const showModal = ref(false);
//import DomVideoPlayer from 'uniapp-video-player' //import DomVideoPlayer from 'uniapp-video-player'
//import sunnyVideo from "@/uni_modules/sunny-video/components/sunny-video/sunny-video.vue"; //import sunnyVideo from "@/uni_modules/sunny-video/components/sunny-video/sunny-video.vue";
@@ -291,7 +320,29 @@ const onFullscreenChange = (e) => {
}; };
const onVideoPlay = () => { const onVideoPlay = () => {
isPlaying.value = true; isPlaying.value = true;
hasPlay.value = true;
timer.value = setInterval(() => {
playTime.value++;
}, 1000);
}; };
const onVideoPause = () => {
isPlaying.value = false;
if(timer.value) {
clearInterval(timer.value);
timer.value = null;
}
};
const onVideoEnded = () => {
isPlaying.value = false;
if(timer.value) {
clearInterval(timer.value);
timer.value = null;
}
};
onHide(() => {
});
// const playVideo = () => { // const playVideo = () => {
// videoContext.value.play(); // videoContext.value.play();
// }; // };
@@ -314,6 +365,7 @@ const startDownload = (index) => {
hasDownload.value = true; hasDownload.value = true;
isVideoDownloadRecord.value=true; isVideoDownloadRecord.value=true;
downLoadStatus.value = 'loading'; downLoadStatus.value = 'loading';
const task = uni.downloadFile({ const task = uni.downloadFile({
url: taskItem.url, url: taskItem.url,
success: (res1) => { success: (res1) => {
@@ -449,6 +501,20 @@ const useWelfareNum = async () => {
if (res.code == 1) { if (res.code == 1) {
uni.showToast({ title: "使用次数成功", icon: "none" }); uni.showToast({ title: "使用次数成功", icon: "none" });
welfareNum.value--; welfareNum.value--;
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "viorder_deo_download",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
event_unit:'分',
number:1,
event_val:0,
},
},
(ret) => {
console.log(ret);
});
addDownloadTask({ addDownloadTask({
url: videoSrc.value, url: videoSrc.value,
id: video_uuid.value, id: video_uuid.value,
@@ -465,12 +531,13 @@ const onVideoError = (e) => {
// }); // });
videoSrc.value= videoSrc.value.replace('.mp4', '.m3u8'); videoSrc.value= videoSrc.value.replace('.mp4', '.m3u8');
}; };
const videoDetail = async () => { const videoDetail = async (flag = false) => {
const res = await api.videoDetail({ video_uuid: video_uuid.value }); const res = await api.videoDetail({ video_uuid: video_uuid.value });
if (res.code == 200) { if (res.code == 200) {
const userInfo = uni.getStorageSync("userInfo"); const userInfo = uni.getStorageSync("userInfo");
videoInfo.value = res.video; videoInfo.value = res.video;
tags.value = res.video.tags;
//hasDownload.value = downLoadList.value.includes(video_uuid.value); //hasDownload.value = downLoadList.value.includes(video_uuid.value);
let vid =res.video.polyv_uuid || extractPolyvVid(res.video.content); let vid =res.video.polyv_uuid || extractPolyvVid(res.video.content);
console.log(222222); console.log(222222);
@@ -488,6 +555,24 @@ const videoDetail = async () => {
"/video/hcp_detial?share=1&uuid=" + "/video/hcp_detial?share=1&uuid=" +
video_uuid.value + video_uuid.value +
"&expertshare=videoshare&fromtype=doctor"; "&expertshare=videoshare&fromtype=doctor";
console.log(videoInfo.value.name)
if(flag) {
try {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "detail_page",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:''
},
},
(ret) => {
console.log(ret);
});
} catch (error) {
}
}
} }
}; };
const collection = async () => { const collection = async () => {
@@ -497,9 +582,30 @@ const collection = async () => {
}); });
if (res.code == 200) { if (res.code == 200) {
uni.showToast({ title: "收藏成功", icon: "none" }); uni.showToast({ title: "收藏成功", icon: "none" });
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "collect",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
},
},
(ret) => {
console.log(ret);
});
videoDetail(); videoDetail();
} }
}; };
const addpoint=()=>{
api.addBonusPointsN({
score_type:5
}).then(res=>{
if(res.code==200){
}
})
}
const discollection = async () => { const discollection = async () => {
const res = await api.discollection({ const res = await api.discollection({
other_uuid: video_uuid.value, other_uuid: video_uuid.value,
@@ -573,8 +679,76 @@ const resumeDownloadingTasks = () => {
}); });
}); });
}; };
onUnload(() => {
console.log('onUnload');
uni.$off('selectedChatPatientsSingle');
uni.$off('replySuccess', handleReplySuccess);
if(timer.value) {
clearInterval(timer.value);
timer.value = null;
};
if(hasPlay.value) {
let id=videoInfo.value.polyv_uuid || extractPolyvVid(videoInfo.value.content);
console.log(id);
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "watch_video",
page_type:'',
resource_name:id,
resource_option:tags.value?tags.value:'',
event_val:playTime.value,
event_unit:'秒',
},
});
}
});
const replySuccessLocked = ref(false);
const lastReplySuccessAt = ref(0);
const runReplySuccessOnce = (action) => {
const now = Date.now();
if (replySuccessLocked.value) return false;
if (now - Number(lastReplySuccessAt.value || 0) < 1000) return false;
replySuccessLocked.value = true;
lastReplySuccessAt.value = now;
try {
action && action();
return true;
} finally {
setTimeout(() => {
replySuccessLocked.value = false;
}, 300);
}
};
const handleReplySuccess = () => {
runReplySuccessOnce(() => {
console.log('reply-success');
try {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "comment",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
},
},
(ret) => {
console.log(ret);
});
} catch (error) {
}
});
};
onShow(() => { onShow(() => {
uni.$on('selectedChatPatientsSingle',(data)=>{
console.log(data)
selectedPatients.value = data.patients;
if(type.value == 'patientVideo') {
addGroupSendMsg4YunXin()
}
});
uni.$off('replySuccess', handleReplySuccess);
uni.$on('replySuccess', handleReplySuccess);
hasDownload.value = false; hasDownload.value = false;
downLoadStatus.value = 'start'; downLoadStatus.value = 'start';
console.log('onShow'); console.log('onShow');
@@ -614,18 +788,72 @@ onShow(() => {
if (activeTab.value == "comment") { if (activeTab.value == "comment") {
videoCommentListV2(); videoCommentListV2();
} }
if(from.value != 'download') { if (from.value != "download") {
videoDetail(); videoDetail(true);
} }
uni.hideLoading(); uni.hideLoading();
}); });
const shareRef = ref(); const shareRef = ref();
const shareActionLocked = ref(false);
const lastShareActionAt = ref(0);
const runShareActionOnce = (action) => {
const now = Date.now();
if (shareActionLocked.value) return false;
if (now - Number(lastShareActionAt.value || 0) < 800) return false;
shareActionLocked.value = true;
lastShareActionAt.value = now;
try {
action && action();
} finally {
setTimeout(() => {
shareActionLocked.value = false;
}, 500);
}
return true;
};
// 分享APP // 分享APP
const shareToggle = () => { const shareToggle = () => {
shareRef.value.open(); shareRef.value.open();
}; };
const shareToPatient = () => {
if (!runShareActionOnce(() => {})) return;
closeShare();
setTimeout(() => {
openModal();
}, 120);
};
const openModal = () => {
showModal.value = true;
};
const closeModal = () => {
showModal.value = false;
};
const addGroupSendMsg4YunXin=async()=>{
const res=await api.addGroupSendMsg4YunXin({
patient_user_uuid: selectedPatients.value.map(item => item.uuid).join(','),
msg_content: videoInfo.value.uuid,
msg_type:4,
});
if(res.code==200){
uni.showToast({ title: "推荐成功", icon: "none" });
}else{
uni.showToast({ title: res.msg, icon: "none" });
}
}
const onSelect = (type) => {
showModal.value = false;
if (type === "single") {
navTo({
url: "/pages_app/selectPatient/selectPatient?from=videoDetail",
});
} else if (type === "group") {
navTo({
url: "/pages_chat/patientGroup/patientGroup?from=videoDetail",
});
}
};
// 关闭分享弹窗 // 关闭分享弹窗
const closeShare = () => { const closeShare = () => {
@@ -634,6 +862,22 @@ const closeShare = () => {
// 分享到微信 // 分享到微信
const shareToWechat = () => { const shareToWechat = () => {
if (!runShareActionOnce(() => {})) return;
if(type.value == 'patientVideo') {
addpoint();
}
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
},
},
(ret) => {
console.log(ret);
});
// #ifdef APP-PLUS // #ifdef APP-PLUS
// 使用系统分享 // 使用系统分享
uni.downloadFile({ uni.downloadFile({
@@ -652,6 +896,7 @@ const shareToWechat = () => {
href: shareLink.value, href: shareLink.value,
imageUrl: res2.tempFilePath, imageUrl: res2.tempFilePath,
success: function (res) { success: function (res) {
console.log("success:" + JSON.stringify(res)); console.log("success:" + JSON.stringify(res));
}, },
fail: function (err) { fail: function (err) {
@@ -729,6 +974,21 @@ const shareToWechat = () => {
// 分享到朋友圈 // 分享到朋友圈
const shareToMoments = () => { const shareToMoments = () => {
if (!runShareActionOnce(() => {})) return;
if(type.value == 'patientVideo') {
addpoint();
}
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
},
},
(ret) => {
console.log(ret);
});
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.downloadFile({ uni.downloadFile({
url: isAndroid?docUrl + videoInfo.value.imgpath:'https://doc.igandan.com/app/html/img/2016/20160714132557.png', url: isAndroid?docUrl + videoInfo.value.imgpath:'https://doc.igandan.com/app/html/img/2016/20160714132557.png',
@@ -746,6 +1006,7 @@ const shareToMoments = () => {
href: shareLink.value, href: shareLink.value,
imageUrl: res2.tempFilePath, imageUrl: res2.tempFilePath,
success: function (res) { success: function (res) {
//console.log("success:" + JSON.stringify(res)); //console.log("success:" + JSON.stringify(res));
}, },
fail: function (err) { fail: function (err) {
@@ -783,6 +1044,7 @@ const shareToMoments = () => {
// 分享到新浪微博 // 分享到新浪微博
const shareToWeibo = () => { const shareToWeibo = () => {
if (!runShareActionOnce(() => {})) return;
// #ifdef APP-PLUS // #ifdef APP-PLUS
uni.share({ uni.share({
provider: "sinaweibo", provider: "sinaweibo",
@@ -831,14 +1093,23 @@ const shareToWeibo = () => {
closeShare(); closeShare();
}; };
const from = ref(''); const from = ref('');
const type = ref('');
// 使用uni-app的onLoad生命周期 // 使用uni-app的onLoad生命周期
onLoad((options) => { onLoad((options) => {
console.log(options);
if(options.type) {
type.value = options.type;
}
if(options.tags) {
tags.value = options.tags;
}
uni.getSystemInfo({ uni.getSystemInfo({
success: function (res) { success: function (res) {
videoWidth.value = res.windowWidth; // 窗口宽度 videoWidth.value = res.windowWidth; // 窗口宽度
}, },
}); });
console.log('enrtyVideoDetail'); console.log('enrtyVideoDetail');
//这个是处理关于滋滋声的问题,不能删掉
uni.sendNativeEvent('enrtyVideoDetail', { uni.sendNativeEvent('enrtyVideoDetail', {
msg: 'enrtyVideoDetail' msg: 'enrtyVideoDetail'
}, ret => { }, ret => {
@@ -897,6 +1168,20 @@ const payVideoDownload = async () => {
const res = await api.payVideoDownload({ video_uuid: video_uuid.value }); const res = await api.payVideoDownload({ video_uuid: video_uuid.value });
console.log(res); console.log(res);
if (res.code == 200) { if (res.code == 200) {
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "viorder_deo_download",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
event_unit:'积分',
number:1,
event_val:videoInfo.value.point,
},
},
(ret) => {
console.log(ret);
});
addDownloadTask({ addDownloadTask({
url: videoSrc.value, url: videoSrc.value,
id: video_uuid.value, id: video_uuid.value,
@@ -978,6 +1263,17 @@ const addCommentV2 = async () => {
if (res.code == 200) { if (res.code == 200) {
uni.showToast({ title: "评论成功", icon: "none" }); uni.showToast({ title: "评论成功", icon: "none" });
commentText.value = ""; commentText.value = "";
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "comment",
page_type: type.value == 'patientVideo' ? '患教视频' : "肝胆视频",
resource_name:videoInfo.value.name,
resource_option:tags.value?tags.value:'',
},
},
(ret) => {
console.log(ret);
});
videoCommentListV2(); videoCommentListV2();
} }
}; };
@@ -1503,6 +1799,50 @@ $theme-color: #8b2316;
background-color: #f5f5f5; background-color: #f5f5f5;
} }
/* 选择弹窗(与 groupMessage 保持一致) */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1100;
}
.center-modal {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin-top:100rpx;
width: 650rpx;
background: #ffffff;
border-radius: 16rpx;
z-index: 9999999;
overflow: hidden;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.15);
}
.modal-title {
text-align: center;
font-size: 34rpx;
color: #8b2316;
padding: 28rpx 20rpx;
}
.modal-item {
padding: 36rpx 28rpx;
font-size: 30rpx;
color: #333333;
background: #ffffff;
}
.modal-divider {
height: 2rpx;
background: #eeeeee;
}
// 删除确认弹窗 // 删除确认弹窗
.delete-modal-mask { .delete-modal-mask {
position: fixed; position: fixed;
+13 -6
View File
@@ -7,9 +7,7 @@
left-icon="left" left-icon="left"
title="添加随访计划" title="添加随访计划"
@clickLeft="goBack" @clickLeft="goBack"
color="#8B2316" color="#8B2316"
:border="false" :border="false"
backgroundColor="#eee" backgroundColor="#eee"
> >
@@ -106,10 +104,17 @@
const dealIndex=ref(0); const dealIndex=ref(0);
import api from '@/api/api.js' import api from '@/api/api.js'
const from = ref(''); const from = ref('');
const type = ref('');
onLoad((options) => { onLoad((options) => {
console.log(options)
patientUuid.value = options.uuid; patientUuid.value = options.uuid;
patientName.value = options.patient_name; patientName.value = options.patient_name;
from.value = options.from; if(options.from){
from.value = options.from;
}
if(options.type){
type.value = options.type;
}
}); });
const selectedPatient = ref(''); const selectedPatient = ref('');
const defaultContent = '请近日来医院复诊、复查'; const defaultContent = '请近日来医院复诊、复查';
@@ -191,11 +196,13 @@
const goBack=()=>uni.navigateBack(); const goBack=()=>uni.navigateBack();
const submitPlan=()=>{ const submitPlan=()=>{
addFollowUps(); addFollowUps();
uni.showToast({ title: '已提交', icon: 'none' }); //uni.showToast({ title: '已提交', icon: 'none' });
setTimeout(()=>uni.navigateBack(),1000); //setTimeout(()=>uni.navigateBack(),1000);
}; };
const selectPatient=()=>{ const selectPatient=()=>{
if(from.value=='patientMsg'){ if(type.value == 'patientDetail'){
return false
}else if(from.value=='patientMsg'){
navTo({ navTo({
url:'/pages_app/selectPatient/selectPatient', url:'/pages_app/selectPatient/selectPatient',
events: { events: {
+40 -4
View File
@@ -84,14 +84,27 @@
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app' import { onShow,onLoad } from '@dcloudio/uni-app'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import api from '@/api/api.js' import api from '@/api/api.js'
import navTo from '@/utils/navTo.js' import navTo from '@/utils/navTo.js'
import dayImg from '@/static/visit_data11.png' import dayImg from '@/static/visit_data11.png'
import planImg from '@/static/visitplan.png' import planImg from '@/static/visitplan.png'
import lineImg from '@/static/item_visitplan_fg.png' import lineImg from '@/static/item_visitplan_fg.png'
const patientUuid = ref('')
const patientName = ref('')
const type = ref('')
onLoad((options) => {
if(options.uuid){
patientUuid.value = options.uuid
}
if(options.patient_name){
patientName.value = options.patient_name
}
if(options.type){
type.value = options.type
}
})
const formatYearMonth = (input) => { const formatYearMonth = (input) => {
if (!input) return '' if (!input) return ''
const d = dayjs(input) const d = dayjs(input)
@@ -162,8 +175,31 @@ const goBack = () => uni.navigateBack()
const showAddMenuFlag = ref(false) const showAddMenuFlag = ref(false)
const showAddMenu = () => { showAddMenuFlag.value = true } const showAddMenu = () => { showAddMenuFlag.value = true }
const hideAddMenu = () => { showAddMenuFlag.value = false } const hideAddMenu = () => { showAddMenuFlag.value = false }
const addFollowUpPlan = () => { showAddMenuFlag.value = false; uni.navigateTo({ url: '/pages_app/visit/visit?from=visitPlan' }) } const addFollowUpPlan = () => {
const addSchedule = () => { showAddMenuFlag.value = false; uni.navigateTo({ url: '/pages_app/schedule/schedule' }) } showAddMenuFlag.value = false;
if(type.value == 'patientDetail'){
uni.navigateTo({
url: '/pages_app/visit/visit?uuid='+patientUuid.value+'&patient_name='+patientName.value+'&type=patientDetail'
})
}else{
uni.navigateTo({
url: '/pages_app/visit/visit?from=visitPlan'
})
}
}
const addSchedule = () => {
showAddMenuFlag.value = false;
if(type.value == 'patientDetail'){
uni.navigateTo({
url: '/pages_app/schedule/schedule?uuid='+patientUuid.value+'&patient_name='+patientName.value+'&type=patientDetail'
})
}else{
uni.navigateTo({
url: '/pages_app/schedule/schedule'
})
}
}
const goFollowDetail = (raw) => { if (!raw) return; navTo({ url: `/pages_app/followDetail/followDetail?followUpUuid=${encodeURIComponent(raw.uuid || '')}&patient_name=${raw.patientname}` }) } const goFollowDetail = (raw) => { if (!raw) return; navTo({ url: `/pages_app/followDetail/followDetail?followUpUuid=${encodeURIComponent(raw.uuid || '')}&patient_name=${raw.patientname}` }) }
onShow(() => { onShow(() => {
+76 -9
View File
@@ -74,6 +74,9 @@ const summary = ref("");
const shareLink = ref(""); const shareLink = ref("");
const shareImg = ref(""); const shareImg = ref("");
const shareTitle = ref(""); const shareTitle = ref("");
const type = ref("");
const tags = ref("");
const reportTitle = ref("");
const goBack = () => { const goBack = () => {
uni.navigateBack({ uni.navigateBack({
fail() { fail() {
@@ -97,9 +100,16 @@ onHide(() => {
// plus.screen.lockOrientation('landscape-primary'); // plus.screen.lockOrientation('landscape-primary');
}); });
onLoad((query) => { onLoad((query) => {
console.log(query);
if (query.title) { if (query.title) {
title.value = query.title; title.value = query.title;
} }
if (query.tags) {
tags.value = query.tags;
}
if (query.type) {
type.value = query.type;
}
if (query.share == 1) { if (query.share == 1) {
canShare.value = true; canShare.value = true;
} }
@@ -122,13 +132,39 @@ onLoad((query) => {
shareImg.value = query.imgPath?decodeURIComponent(query.imgPath):'https://doc.igandan.com/app/html/img/2016/20160714132557.png'; shareImg.value = query.imgPath?decodeURIComponent(query.imgPath):'https://doc.igandan.com/app/html/img/2016/20160714132557.png';
if (query.type == "live_yugao") { if (query.type == "live_yugao") {
shareTitle.value = query.sharetitle; shareTitle.value = query.sharetitle;
reportTitle.value = shareTitle.value;
summary.value = "分享一篇来自“肝胆相照”的会议预告:" + shareTitle.value; summary.value = "分享一篇来自“肝胆相照”的会议预告:" + shareTitle.value;
console.log(reportTitle.value);
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "detail_page",
page_type: "会议直播",
resource_name:reportTitle.value,
resource_option:tags.value,
},
},
(ret) => {
console.log(ret);
});
} else if (query.type == "live_old") { } else if (query.type == "live_old") {
shareTitle.value = query.sharetitle; shareTitle.value = query.sharetitle;
summary.value = "分享一篇来自“肝胆相照”的历史会议:" + shareTitle.value; summary.value = "分享一篇来自“肝胆相照”的历史会议:" + shareTitle.value;
} else if (query.type == "live") { } else if (query.type == "live") {
shareTitle.value = "肝胆相照直播:" + query.sharetitle; shareTitle.value = "肝胆相照直播:" + query.sharetitle;
reportTitle.value = query.sharetitle;
summary.value = shareTitle.value; summary.value = shareTitle.value;
console.log(reportTitle.value);
// uni.sendNativeEvent("portraitReport",{
// msg:{
// event_nickname: "detail_page",
// page_type: "会议直播",
// resource_name:reportTitle.value,
// resource_option:tags.value,
// },
// },
// (ret) => {
// console.log(ret);
// });
}else if(query.type=='news'){ }else if(query.type=='news'){
shareTitle.value = query.sharetitle; shareTitle.value = query.sharetitle;
summary.value =''; summary.value ='';
@@ -231,6 +267,15 @@ const closeShare = () => {
const nativeMaskView = ref(null); const nativeMaskView = ref(null);
const nativePopupView = ref(null); const nativePopupView = ref(null);
const popupShowing = ref(false); const popupShowing = ref(false);
/** 原生分享面板:防止同一次手势内重复触发(兜底) */
let nativePanelLastActionAt = 0;
const NATIVE_PANEL_ACTION_GAP_MS = 400;
function tryCommitNativePanelAction(fn) {
const t = Date.now();
if (t - nativePanelLastActionAt < NATIVE_PANEL_ACTION_GAP_MS) return;
nativePanelLastActionAt = t;
fn();
}
// 保存弹窗尺寸信息,供事件处理使用 // 保存弹窗尺寸信息,供事件处理使用
const popupInfo = ref({ const popupInfo = ref({
screenW: 0, screenW: 0,
@@ -577,7 +622,7 @@ function createNativePopup() {
); );
if (relativeY >= cancelTop) { if (relativeY >= cancelTop) {
console.log("点击了取消按钮"); console.log("点击了取消按钮");
closeNativePopup(); tryCommitNativePanelAction(() => closeNativePopup());
return; return;
} }
@@ -611,12 +656,16 @@ function createNativePopup() {
if (idx === 0) { if (idx === 0) {
console.log("执行微信分享"); console.log("执行微信分享");
shareToWechat(); tryCommitNativePanelAction(() => {
closeNativePopup(); shareToWechat();
closeNativePopup();
});
} else if (idx === 1) { } else if (idx === 1) {
console.log("执行朋友圈分享"); console.log("执行朋友圈分享");
shareToMoments(); tryCommitNativePanelAction(() => {
closeNativePopup(); shareToMoments();
closeNativePopup();
});
} else if (idx === 2) { } else if (idx === 2) {
console.log("执行微博分享"); console.log("执行微博分享");
// shareToWeibo(); // shareToWeibo();
@@ -634,19 +683,17 @@ function createNativePopup() {
} }
}; };
// 同时监听 click 和 touchstart 事件 // 仅监听 click:若同时绑定 touchstart/touchend,一次点击会触发多次,导致分享重复执行
nativeMaskView.value.addEventListener("click", handleMaskClick, false); nativeMaskView.value.addEventListener("click", handleMaskClick, false);
nativeMaskView.value.addEventListener("touchstart", handleMaskClick, false);
nativePopupView.value.addEventListener("click", handlePanelClick, false); nativePopupView.value.addEventListener("click", handlePanelClick, false);
nativePopupView.value.addEventListener("touchstart", handlePanelClick, false);
nativePopupView.value.addEventListener("touchend", handlePanelClick, false);
// #endif // #endif
} }
function showNativePopup() { function showNativePopup() {
// #ifdef APP-PLUS // #ifdef APP-PLUS
if (popupShowing.value) return; if (popupShowing.value) return;
nativePanelLastActionAt = 0;
if (!nativeMaskView.value || !nativePopupView.value) { if (!nativeMaskView.value || !nativePopupView.value) {
createNativePopup(); createNativePopup();
} }
@@ -668,6 +715,16 @@ function closeNativePopup() {
// 分享到微信 // 分享到微信
const shareToWechat = () => { const shareToWechat = () => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
if(type.value == "live" || type.value == "live_yugao"){
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: "会议直播",
resource_name:reportTitle.value,
resource_option:tags.value,
},
});
}
// 使用系统分享 // 使用系统分享
uni.downloadFile({ uni.downloadFile({
url: shareImg.value, url: shareImg.value,
@@ -770,6 +827,16 @@ onUnload(() => {
// 分享到朋友圈 // 分享到朋友圈
const shareToMoments = () => { const shareToMoments = () => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
if(type.value == "live" || type.value == "live_yugao"){
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "share",
page_type: "会议直播",
resource_name:reportTitle.value,
resource_option:tags.value,
},
});
}
uni.downloadFile({ uni.downloadFile({
url: shareImg.value, url: shareImg.value,
success: function (res) { success: function (res) {
File diff suppressed because it is too large Load Diff
+46 -34
View File
@@ -22,7 +22,7 @@
<view class="divider"></view> <view class="divider"></view>
<view class="filter-item" @click="showFilterPopup"> <view class="filter-item" @click="showFilterPopup">
<text :class="{active: keywords}">筛选</text> <text :class="{ active: keywords }">筛选</text>
<up-image <up-image
:src="keywords ? filterOn : filter" :src="keywords ? filterOn : filter"
width="36rpx" width="36rpx"
@@ -50,12 +50,20 @@
:errorIcon="lazyImg" :errorIcon="lazyImg"
:lazy-load="true" :lazy-load="true"
> >
<template #error> <template #error>
<image :src="lazyImg" class="category-icon" mode="widthFix"></image> <image
</template> :src="lazyImg"
<template v-slot:loading> class="category-icon"
<image :src="lazyImg" class="category-icon" mode="widthFix"></image> mode="widthFix"
</template> ></image>
</template>
<template v-slot:loading>
<image
:src="lazyImg"
class="category-icon"
mode="widthFix"
></image>
</template>
</up-image> </up-image>
</view> </view>
<view class="item-content"> <view class="item-content">
@@ -293,7 +301,7 @@
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { onShow, onBackPress,onLoad} from "@dcloudio/uni-app"; import { onShow, onBackPress, onLoad } from "@dcloudio/uni-app";
import upImg from "@/static/cb_up.png"; import upImg from "@/static/cb_up.png";
import downImg from "@/static/cb_up.png"; import downImg from "@/static/cb_up.png";
import filter from "@/static/cb_screen_no.png"; import filter from "@/static/cb_screen_no.png";
@@ -341,9 +349,7 @@ onBackPress(() => {
return true; return true;
}); });
// 页面加载 // 页面加载
onMounted(() => { onMounted(() => {});
});
const showFilter = ref(false); const showFilter = ref(false);
// 筛选弹窗相关方法 // 筛选弹窗相关方法
const showFilterPopup = () => { const showFilterPopup = () => {
@@ -395,32 +401,40 @@ const confirmFilter = throttle(() => {
isFilterActive.value = true; isFilterActive.value = true;
hideFilterPopup(); hideFilterPopup();
const selectedTags = filterTags.value.filter((tag) => tag.selected); const selectedTags = filterTags.value.filter((tag) => tag.selected);
let words=""; let words = "";
console.log("选中的筛选标签:", selectedTags); console.log("选中的筛选标签:", selectedTags);
for (let i = 0; i < selectedTags.length; i++) { for (let i = 0; i < selectedTags.length; i++) {
if (words) { if (words) {
words += "," + selectedTags[i].NAME; words += "," + selectedTags[i].NAME;
} else { } else {
words= selectedTags[i].NAME; words = selectedTags[i].NAME;
} }
} }
keywords.value=words; keywords.value = words;
if(!keywords.value) return; if (!keywords.value) return;
console.log(1111111); console.log(1111111);
console.log('看看执行了几次'); console.log("看看执行了几次");
navTo({ navTo({
url: "/pages_app/hot/hot?keywords="+encodeURIComponent(keywords.value) url: "/pages_app/hot/hot?keywords=" + encodeURIComponent(keywords.value),
}); });
// 根据选中的标签重新加载数据 // 根据选中的标签重新加载数据
},1000); }, 1000);
onLoad(() => { onLoad(() => {
loadGuideTypes(); loadGuideTypes();
loadSearchLibrary(); loadSearchLibrary();
loadGuideTags(); loadGuideTags();
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "诊疗指南",
},
},
(ret) => {
console.log(ret);
});
}); });
onShow(() => { onShow(() => {});
});
// 加载指南分类(guideType) // 加载指南分类(guideType)
const loadGuideTypes = async () => { const loadGuideTypes = async () => {
@@ -458,14 +472,13 @@ const loadGuideTags = async () => {
const res = await api.guideTag({ const res = await api.guideTag({
type: 3, type: 3,
}); });
if (res && res.code === 200 && res.data) { if (res && res.code === 200 && res.data) {
// 将API返回的标签数据转换为筛选标签格式 // 将API返回的标签数据转换为筛选标签格式
filterTags.value = res.data.map((tag) => ({ filterTags.value = res.data.map((tag) => ({
...tag, ...tag,
selected: false, selected: false,
})); }));
} }
} catch (e) { } catch (e) {
console.error("加载指南标签失败:", e); console.error("加载指南标签失败:", e);
@@ -670,7 +683,7 @@ $accent-color: #4a90e2;
gap: 5px; gap: 5px;
.search-text { .search-text {
font-size:36rpx; font-size: 36rpx;
color: $gray-medium; color: $gray-medium;
} }
} }
@@ -690,13 +703,12 @@ $accent-color: #4a90e2;
gap: 3px; gap: 3px;
font-size: 36rpx; font-size: 36rpx;
color: #999; color: #999;
text{ text {
margin-top:-1rpx; margin-top: -1rpx;
&.active { &.active {
color: #8b2316; color: #8b2316;
}
} }
}
} }
} }
@@ -1148,7 +1160,7 @@ $accent-color: #4a90e2;
.filter-tags { .filter-tags {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: 60rpx; margin-bottom: 60rpx;
max-height: 65vh; max-height: 65vh;
overflow-y: auto; overflow-y: auto;
@@ -1159,7 +1171,7 @@ $accent-color: #4a90e2;
padding: 8rpx 0; /* 去掉左右padding,仅保留上下 */ padding: 8rpx 0; /* 去掉左右padding,仅保留上下 */
border-radius: 30rpx; border-radius: 30rpx;
font-size: 26rpx; font-size: 26rpx;
margin:8rpx 5rpx; margin: 8rpx 5rpx;
border: 2rpx solid #efefef; border: 2rpx solid #efefef;
transition: all 0.3s ease; transition: all 0.3s ease;
width: 152rpx; /* 固定宽度 */ width: 152rpx; /* 固定宽度 */
+24
View File
@@ -460,6 +460,19 @@ const downLoad = (item) => {
}); });
return; return;
} }
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "magazine_donwload",
page_type: "诊疗指南",
resource_name:item.title,
belong:title.value,
number:1,
resource_option:item.tags
},
},
(ret) => {
console.log(ret);
});
addDownloadTask({ addDownloadTask({
url: docUrl + item.path, url: docUrl + item.path,
title: item.title, title: item.title,
@@ -485,6 +498,17 @@ const viewGuideline = (cell) => {
if(item.id == cell.uuid) { if(item.id == cell.uuid) {
console.log(2222222222); console.log(2222222222);
console.log(item.localPath); console.log(item.localPath);
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "detail_page",
page_type: "诊疗指南",
resource_name:cell.title,
resource_option:cell.tags
},
},
(ret) => {
console.log(ret);
});
// if(isAndroid){ // if(isAndroid){
plus.runtime.openFile(item.localPath, function(e) { plus.runtime.openFile(item.localPath, function(e) {
console.log('打开成功'); console.log('打开成功');
+1 -1
View File
@@ -126,7 +126,7 @@
<view class="bottom-bar" @click="openModal"> <view class="bottom-bar" @click="openModal">
<view class="btn-primary" >群发消息</view> <view class="btn-primary" >群发消息</view>
</view> </view>
<!-- 弹窗与遮罩 --> <!-- 选择弹窗 -->
<view v-if="showModal" class="mask" @click="closeModal"></view> <view v-if="showModal" class="mask" @click="closeModal"></view>
<view v-if="showModal" class="center-modal"> <view v-if="showModal" class="center-modal">
<view class="modal-title">温馨提示</view> <view class="modal-title">温馨提示</view>
+1
View File
@@ -243,6 +243,7 @@ const addOutPatient=async()=>{
} }
} }
const updateOutPatient=async()=>{ const updateOutPatient=async()=>{
const res = await api.updateOutPatient({ const res = await api.updateOutPatient({
uuid: uuid.value, uuid: uuid.value,
+51 -20
View File
@@ -15,7 +15,8 @@
> >
<template #right> <template #right>
<view class="nav-right"> <view class="nav-right">
<up-icon name="share" color="#8B2316" size="24" @click="shareToggle"></up-icon> <up-image :src="shareIcon" width="36rpx" height="36rpx" @click="shareToggle"></up-image>
<!-- <up-icon name="share" color="#8B2316" size="24" @click="shareToggle"></up-icon> -->
<up-image :src="siteImg" width="36rpx" height="36rpx" @click="goSite"></up-image> <up-image :src="siteImg" width="36rpx" height="36rpx" @click="goSite"></up-image>
</view> </view>
</template> </template>
@@ -199,6 +200,7 @@ import empty from '@/components/empty/empty.vue'
import sendImg from '@/static/send_feed.png' import sendImg from '@/static/send_feed.png'
import dateBg from '@/static/data_sign.png' import dateBg from '@/static/data_sign.png'
import editImg from '@/static/edit_icon.png' import editImg from '@/static/edit_icon.png'
import shareIcon from '@/static/icon_share.png'
import unidialog from '@/components/dialog/dialog.vue' import unidialog from '@/components/dialog/dialog.vue'
import deleteImg from '@/static/delete_icon.png' import deleteImg from '@/static/delete_icon.png'
import sinaImg from "@/static/share_sina.png" import sinaImg from "@/static/share_sina.png"
@@ -218,32 +220,45 @@ const addressList = ref([]);
const shareRef = ref() const shareRef = ref()
const shareLink = ref('') const shareLink = ref('')
const patient_user_uuid = ref(''); const patient_user_uuid = ref('');
const isLock=ref(true)
// 响应式数据 // 响应式数据
const from = ref(''); const from = ref('');
const currentTab = ref('suspension') const currentTab = ref('suspension')
const remarks = ref('') const remarks = ref('')
const note = ref('') const note = ref('')
const patientList = ref([]);
const outpatientSchedules = ref([]) const outpatientSchedules = ref([])
const outPatientList = ref([]); const outPatientList = ref([]);
const patientListByGBK=()=>{ const patientListByGBK=()=>{
api.patientListByGBK().then(res=>{ api.patientListByGBK().then(res=>{
if(res.code==1){ if(res.code==1){
isLock.value = false;
patientList.value = res.data;
console.log(res.data) console.log(res.data)
for(let i=0;i<res.data.length;i++){
if(patient_user_uuid.value){
patient_user_uuid.value += res.data[i].uuid+',';
}else{
patient_user_uuid.value += res.data[i].uuid;
}
}
} }
}) })
} }
const noticeConfirm = () => { const noticeConfirm = () => {
noticeVisible.value = false; noticeVisible.value = false;
publishOutPatient(); if(patientList.value.length >= 200 || isLock.value){
uni.sendNativeEvent(
"goGroupMessagePage",
{
msg: "goGroupMessagePage",
},
(ret) => {
console.log(ret);
}
);
// navTo({
// url:'/pages_chat/groupMessage/groupMessage?from=outPatient'
// })
}else{
publishOutPatient();
}
} }
const noticeCancel=()=>{ const noticeCancel=()=>{
noticeVisible.value = false; noticeVisible.value = false;
@@ -269,6 +284,13 @@ onLoad((options) => {
const expertUuid = userInfo.uuid; const expertUuid = userInfo.uuid;
shareLink.value = other_host+'/wxPatient/index.htm#/outPatient?link=share&expertUuid='+expertUuid+'&from=doctor' shareLink.value = other_host+'/wxPatient/index.htm#/outPatient?link=share&expertUuid='+expertUuid+'&from=doctor'
SHARE_TITLE.value = userInfo.realName+'医生门诊详情' SHARE_TITLE.value = userInfo.realName+'医生门诊详情'
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "list_page",
page_type: "出/停诊公告",
},
});
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -344,10 +366,18 @@ const goSite = () => {
}) })
} }
const publishOutPatient = async (uuid) => { const publishOutPatient = async (uuid) => {
let uuids='';
for(let i=0;i<patientList.value.length;i++){
if(uuids){
uuids+=','+patientList.value[i].uuid;
}else{
uuids += patientList.value[i].uuid;
}
}
const res = await api.publishOutPatient({ const res = await api.publishOutPatient({
msg_type:'5', msg_type:'5',
msg_content:'', msg_content:'',
patient_user_uuid:patient_user_uuid.value patient_user_uuid:uuids
}) })
if(!from.value){ if(!from.value){
plus.runtime.quit(); plus.runtime.quit();
@@ -1029,8 +1059,9 @@ bottom: 152rpx;
align-items: flex-start; align-items: flex-start;
} }
.schedule-left { .schedule-left {
height: 200rpx; min-height: 200rpx;
border-right: 2rpx solid #f0f0f0; padding:20rpx 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@@ -1050,8 +1081,10 @@ bottom: 152rpx;
} }
.schedule-right { .schedule-right {
border-left: 2rpx solid #f0f0f0;
flex: 1; flex: 1;
height: 200rpx; min-height: 200rpx;
padding:20rpx 0;
margin-left: 30prx; margin-left: 30prx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -1077,13 +1110,10 @@ bottom: 152rpx;
color: #333; color: #333;
} }
.location-info {
display: flex;
align-items: center;
gap: 15rpx;
}
.location { .location {
display: inline-block;
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
} }
@@ -1106,9 +1136,10 @@ bottom: 152rpx;
border: 2rpx solid #8B2316; border: 2rpx solid #8B2316;
border-radius: 20rpx; border-radius: 20rpx;
padding:2rpx 16rpx; padding:2rpx 16rpx;
white-space: nowrap;
font-size: 20rpx; font-size: 20rpx;
color: #8B2316; color: #8B2316;
display: flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
+12 -3
View File
@@ -171,13 +171,16 @@ const selectedPatientCount = computed(() => {
// 合计数量:只计算去重后的患者数量 // 合计数量:只计算去重后的患者数量
const totalSelectedCount = computed(() => selectedPatientCount.value); const totalSelectedCount = computed(() => selectedPatientCount.value);
const from = ref('');
onLoad(() => { onLoad((options) => {
uni.showLoading({ uni.showLoading({
title: '加载中...', title: '加载中...',
mask: true mask: true
}); });
fetchGroupList(); fetchGroupList();
if(options.from){
from.value = options.from;
}
}); });
const fetchGroupList = async () => { const fetchGroupList = async () => {
@@ -311,7 +314,11 @@ const confirmGroup = () => {
} }
try { try {
uni.navigateTo({ if(from.value){
uni.$emit('selectedChatPatientsSingle', { patients: dedupPatients });
uni.navigateBack();
}else{
uni.navigateTo({
url: '/pages_chat/groupSend/groupSend', url: '/pages_chat/groupSend/groupSend',
success: (res) => { success: (res) => {
try { try {
@@ -322,6 +329,8 @@ const confirmGroup = () => {
} }
} }
}); });
}
} catch (e) { } catch (e) {
console.error('跳转失败', e); console.error('跳转失败', e);
uni.showToast({ title: '跳转失败,请重试', icon: 'none' }); uni.showToast({ title: '跳转失败,请重试', icon: 'none' });
+27 -1
View File
@@ -111,6 +111,7 @@ const freeVisible = ref(false)
const freeContent = ref('') const freeContent = ref('')
const goods_order_id = ref('') const goods_order_id = ref('')
const points = ref(0) const points = ref(0)
const goodsPoints=ref(0);
const freeClose = () => { const freeClose = () => {
freeVisible.value = false freeVisible.value = false
} }
@@ -125,6 +126,28 @@ const freeConfirm = () => {
icon: 'none', icon: 'none',
duration: 1000 duration: 1000
}); });
let belong = '';
if(goodsName.value.indexOf('U盘') > -1 || goodsName.value.indexOf('u盘') > -1){
belong = 'U盘';
}else if(goodsName.value.indexOf('京东E卡') > -1){
belong = '京东E卡';
}else{
belong = '书籍';
}
uni.sendNativeEvent("portraitReport",{
msg:{
event_nickname: "boint_jifengood",
page_type: "积分商城",
resource_name:goodsName.value,
event_val:goodsPoints.value,
event_unit:'积分',
number:goodsNum.value,
belong:belong,
},
},
(ret) => {
console.log(ret);
});
setTimeout(() => { setTimeout(() => {
navTo({ navTo({
url: '/pages_goods/pointMall/pointMall?from=exchange' url: '/pages_goods/pointMall/pointMall?from=exchange'
@@ -141,7 +164,8 @@ const email = ref('')
const editingId = ref(null) const editingId = ref(null)
const goodsUuid = ref('') const goodsUuid = ref('')
const type = ref(1) const type = ref(1)
const goodsNum = ref(1) const goodsNum = ref(1);
const goodsName = ref('');
const goBack = () => uni.navigateBack() const goBack = () => uni.navigateBack()
// 省市区数据(适配树形数组:[{code,label,value,children:[...] }...]) // 省市区数据(适配树形数组:[{code,label,value,children:[...] }...])
@@ -213,6 +237,8 @@ onLoad((opts) => {
goodsUuid.value = opts.goodsUuid goodsUuid.value = opts.goodsUuid
goodsNum.value = opts.goodsNum; goodsNum.value = opts.goodsNum;
type.value = opts.type; type.value = opts.type;
goodsName.value = opts.goodsName;
goodsPoints.value = opts.goodsPoints;
getAddress() getAddress()
}) })
const saveAddress = () => { const saveAddress = () => {
+1 -1
View File
@@ -146,7 +146,7 @@ const goAddress = () => {
freeVisible.value = true freeVisible.value = true
}else{ }else{
uni.navigateTo({ uni.navigateTo({
url: '/pages_goods/exchange/address?goodsUuid='+id.value+'&type='+type.value+'&goodsNum='+qty.value url: '/pages_goods/exchange/address?goodsUuid='+id.value+'&type='+type.value+'&goodsNum='+qty.value+'&goodsName='+title.value+'&goodsPoints='+needPoints.value
}) })
} }
+9
View File
@@ -263,6 +263,15 @@ onBackPress(() => {
if(opts.name){ if(opts.name){
name.value = opts.name; name.value = opts.name;
} }
// uni.sendNativeEvent("portraitReport",{
// msg:{
// event_nickname: "list_page",
// page_type: "积分商城",
// },
// },
// (ret) => {
// console.log(ret);
// });
}); });
// 方法 // 方法
const goBack = () => { const goBack = () => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

+1 -1
View File
@@ -32,7 +32,7 @@
"**/*.vue", "**/*.vue",
"**/*.js", "**/*.js",
"**/*.jsx" "**/*.jsx"
], , "pages_app/patientMsg/patientMsg.nvue", "pages_app/patientMsg/patientMsg.nvue", "pages_app/patientMsg/patientMsg.nvue" ],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"dist", "dist",
+2 -2
View File
@@ -1,4 +1,4 @@
let BASE_URL='https://dev-app.igandan.com/app'; //let BASE_URL='https://dev-app.igandan.com/app';
//let BASE_URL='https://app.igandan.com/app' let BASE_URL='https://app.igandan.com/app'
export default BASE_URL export default BASE_URL
+2 -2
View File
@@ -1,5 +1,5 @@
let DOC_URL='https://dev-doc.igandan.com/app/'; //let DOC_URL='https://dev-doc.igandan.com/app/';
//let DOC_URL='https://doc.igandan.com/app/' let DOC_URL='https://doc.igandan.com/app/'
// if(app.globalData.apiHost.indexOf('dev')>-1){ // if(app.globalData.apiHost.indexOf('dev')>-1){
// DOC_URL='https://dev-doc.igandan.com/app/' // DOC_URL='https://dev-doc.igandan.com/app/'
// }else{ // }else{
+2 -2
View File
@@ -1,5 +1,5 @@
let OTHER_HOST='https://dev-wx.igandan.com' //let OTHER_HOST='https://dev-wx.igandan.com'
//let OTHER_HOST='https://wx.igandan.com' let OTHER_HOST='https://wx.igandan.com'
//const app = getApp({allowDefault: true}); //const app = getApp({allowDefault: true});
// if(app.globalData.apiHost && app.globalData.apiHost.indexOf('dev')>-1){ // if(app.globalData.apiHost && app.globalData.apiHost.indexOf('dev')>-1){
// OTHER_HOST='https://dev-wx.igandan.com' // OTHER_HOST='https://dev-wx.igandan.com'
+12 -7
View File
@@ -58,10 +58,12 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
header['Authorization']='Bearer ' + token; header['Authorization']='Bearer ' + token;
let userInfo= uni.getStorageSync('userInfo') let userInfo= uni.getStorageSync('userInfo')
defaultData = { defaultData = {
version: version, mini_version: version,
version: uni.getStorageSync('app_version'),
user_uuid:userInfo.uuid, user_uuid:userInfo.uuid,
client_type:client_type, client_type:client_type,
timestamp:new Date().getTime() timestamp:new Date().getTime(),
} }
postData={ postData={
...data, ...data,
@@ -75,7 +77,8 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
let userInfo= uni.getStorageSync('userInfo') let userInfo= uni.getStorageSync('userInfo')
console.log(userInfo) console.log(userInfo)
defaultData = { defaultData = {
version: version, mini_version: version,
version: uni.getStorageSync('app_version'),
user_uuid:userInfo.uuid, user_uuid:userInfo.uuid,
client_type:client_type, client_type:client_type,
timestamp:new Date().getTime()+"" timestamp:new Date().getTime()+""
@@ -120,9 +123,9 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
if(process.env.UNI_PLATFORM == "mp-weixin"){ if(process.env.UNI_PLATFORM == "mp-weixin"){
uni.redirectTo({url:'/pages_app/login/login'}) uni.redirectTo({url:'/pages_app/login/login'})
} }
// else{ // else{
// uni.navigateTo({url:'/pages_app/login/login'}) // uni.navigateTo({url:'/pages_app/login/login'})
// } // }
uni.sendNativeEvent('getNewToken', { uni.sendNativeEvent('getNewToken', {
msg: 'getNewToken' msg: 'getNewToken'
},ret => { },ret => {
@@ -184,7 +187,9 @@ export const request = (url, data = {}, method = 'post', loading = false, conten
}else if(res.data.code==106){ }else if(res.data.code==106){
e(res.data) e(res.data)
}else{ }else if(res.data.code==202){
e(res.data)
}else{
uni.showToast({ uni.showToast({
title: res.data.message, title: res.data.message,
icon: 'none' icon: 'none'
+1 -1
View File
@@ -1,2 +1,2 @@
const version="4.2.2" const version="4.2.7"
export default version export default version