9.4上午提交

This commit is contained in:
zoujiandong 2025-09-04 10:22:51 +08:00
parent b068fb00cc
commit e86f8e77a7
17 changed files with 1364 additions and 155 deletions

View File

@ -340,6 +340,40 @@ const api = {
groupList(data){
return request('/expertAPI/groupListU', data, 'post', false);
},
patientListByGroup(data){
return request('/expertAPI/patientListByGroupU', data, 'post', false);
},
groupDelete(data){
return request('/expertAPI/deleteGroupU', data, 'post', false);
},
groupAdd(data){
return request('/expertAPI/addGroupU', data, 'post', false);
},
groupUpdate(data){
return request('/expertAPI/updateGroupU', data, 'post', false);
},
updateNicknameNote(data){
return request('/expertAPI/updateNicknameNote', data, 'post', false);
},
patientCard(data){
return request('/expertAPI/patientCard', data, 'post', false);
},
patientCardUpdateGroup(data){
return request('/expertAPI/patientCardUpdateGroup', data, 'post', false);
},
addFollowUps(data){
return request('/expertAPI/addFollowUps', data, 'post', false);
},
getFollowUp(data){
return request('/expertAPI/getFollowUp', data, 'post', false);
},
deleteFollowUp(data){
return request('/expertAPI/deleteFollowUp', data, 'post', false);
},
cancelRes(data){
return request('/expertAPI/cancelRes', data, 'post', false);
},
}
export default api

View File

@ -100,7 +100,7 @@
console.log('格式化后的内容数组:', lines);
return lines;
}
const emit = defineEmits(['close'])
const emit = defineEmits(['close','confirm'])
@ -109,7 +109,9 @@
}
const onconfirm=()=>{
emit('close')
emit('confirm')
emit('close');
}
</script>

View File

@ -212,6 +212,7 @@
"path": "login/login",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -222,6 +223,7 @@
"path": "video/video",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -232,6 +234,7 @@
"path": "myPatient/myPatient",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -242,6 +245,7 @@
"path": "patientSetting/patientSetting",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -252,6 +256,7 @@
"path": "patientRemark/patientRemark",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -262,6 +267,18 @@
"path": "groupEdit/groupEdit",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "groupManage/groupManage",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -272,17 +289,40 @@
"path": "selectPatient/selectPatient",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "visitNote/visitNote",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "followDetail/followDetail",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
}
}
},
{
"path": "videoDetail/videoDetail",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -293,6 +333,7 @@
"path": "myFlower/myFlower",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -543,6 +584,7 @@
{
"path": "setting/setting",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "uni-app分页",
"app": {
@ -564,6 +606,7 @@
"path": "visit/visit",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -574,6 +617,7 @@
"path": "patientDetail/patientDetail",
"style": {
"navigationStyle": "custom",
"navigationBarRightButton":{ "hide": true},
"navigationBarTitleText": "uni-app分页",
"app": {
"bounce": "none"
@ -817,6 +861,21 @@
"backgroundColor": "#F8F8F8",
"app-plus": {
"background": "#efeff4"
},
"mp-weixin": {
"navigationStyle": "custom"
},
"mp-alipay": {
"navigationStyle": "custom"
},
"mp-baidu": {
"navigationStyle": "custom"
},
"mp-toutiao": {
"navigationStyle": "custom"
},
"mp-qq": {
"navigationStyle": "custom"
}
},
"condition" : { //

View File

@ -216,12 +216,12 @@
<up-image :src="signImg" width="604rpx" height="964rpx" ></up-image>
</view>
<view class="signcontent">
<view class="day">今天是我们相识的第211</view>
<view class="signtotal">本周共签到2</view>
<view class="signcontinue">已经连续签到2</view>
<view class="day">今天是我们相识的第{{signInfo.gdxzday}}</view>
<view class="signtotal">本周共签到{{signInfo.totalDay}}</view>
<view class="signcontinue">已经连续签到{{signInfo.continuous_day}}</view>
<view class="tip">连续签到获取更多积分</view>
<view class="news" @click="showSign=false">
我是新闻内容
<view class="news" @click.stop="goNews">
{{signInfo.news.summary}}
</view>
</view>
</view>
@ -253,6 +253,7 @@
import sign from "@/static/home_qiandao_icon.png"
import signImg from "@/static/sign_in_bng_big.png"
import dayjs from 'dayjs'
import docUrl from "@/utils/docUrl.js"
const expertDetail=reactive({})
const showSign=ref(false)
// refs
@ -512,7 +513,23 @@
url: '/pages/guide/list'
});
};
const goNews=()=>{
let url=docUrl+signInfo.news.path;
// #ifdef H5
window.open(url, '_blank');
// #endif
// #ifdef APP-PLUS
plus.runtime.openURL(url);
// #endif
// #ifdef MP
const encoded = encodeURIComponent(url);
uni.navigateTo({
url: `/pages_app/webview/webview?url=${encoded}`
});
// #endif
}
//
const onBannerClick = (item,index) => {
if (!item) return;
@ -833,7 +850,7 @@
url: '/pages_course/course/course'
});
};
const signInfo=reactive({})
//
const onSignClick = async () => {
//
@ -848,13 +865,15 @@
score_type:1
});
if (res && res.code === 200) {
if (res && res.code ==1) {
//
isSignedIn.value = true;
uni.showToast({
title: '签到成功,获得10积分',
icon: 'success'
title: '签到成功,获得'+res.bonuspoints+'积分',
icon: 'none'
});
Object.assign(signInfo,res);
//
showSign.value = true;

View File

@ -0,0 +1,116 @@
<template>
<view class="visit-note-page">
<uni-nav-bar
left-icon="left"
title="随访详情"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eee"
>
</uni-nav-bar>
<!-- 患者行 -->
<view class="row" @click="noop">
<text class="row-label">患者</text>
<text class="row-value">{{ patientName }}</text>
</view>
<view class="row" @click="noop">
<text class="row-label">日期</text>
<text class="row-value">{{ datetime }}</text>
</view>
<!-- 标题栏 -->
<view class="section-title">随访内容</view>
<!-- 输入框区域 -->
<view class="note-box">
<textarea class="note-textarea" v-model.trim="note" placeholder="请填写随访内容" auto-height :maxlength="200" />
</view>
<!-- 底部删除按钮 -->
<view class="fixed-footer">
<view class="fixed-btn" @click="deleteFollowUp">删除该条记录</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad,onShow } from '@dcloudio/uni-app'
import api from '@/api/api.js'
const patientName = ref('')
const note = ref('请近日来医院复诊、复查')
const datetime = ref('')
const followUpUuid = ref('')
onLoad((options) => {
patientName.value = options?.patient_name
if (options?.followUpUuid) followUpUuid.value = options.followUpUuid
})
onShow(() => {
getFollowUp();
})
const deleteFollowUp = () => {
api.deleteFollowUp({
uuid: followUpUuid.value
}).then(res => {
console.log(res)
if(res.code == 200){
uni.showToast({
title: '删除成功',
icon: 'none'
})
setTimeout(() => goBack(), 600)
}
})
}
const getFollowUp = () => {
api.getFollowUp({
uuid: followUpUuid.value
}).then(res => {
console.log(res)
if(res.code == 200){
note.value = res.data.note
datetime.value = res.data.datetime
}
})
}
const goBack = () => uni.navigateBack()
const submit = () => {
try {
const pages = getCurrentPages()
const curr = pages[pages.length - 1]
const ec = curr?.getOpenerEventChannel?.()
ec?.emit && ec.emit('onVisitNoteSubmit', { note: note.value, idx: Number(idx.value) })
} catch (e) {}
uni.showToast({ title: '已添加', icon: 'success' })
setTimeout(() => goBack(), 600)
}
const noop = () => {}
</script>
<style lang="scss" scoped>
.visit-note-page{
min-height: 100vh; background:#f5f5f5; padding-bottom: 120rpx;
}
.nav-right{ display:flex; align-items:center; }
.submit-text{ color:#8B2316; font-size: 30rpx; }
.row{
margin-top: 20rpx; background:#fff; padding: 26rpx 30rpx; display:flex; align-items:center; justify-content:space-between; border-bottom: 1rpx solid #eee;
.row-label{ font-size: 32rpx; color:#8B2316; font-weight: 500; }
.row-value{ font-size: 32rpx; color:#333; }
}
.section-title{ background:#fff; padding: 22rpx 30rpx; color:#8B2316; font-size: 30rpx; border-top: 1rpx solid #eee; border-bottom: 1rpx solid #eee; }
.note-box{ background:#fff; padding: 20rpx 24rpx; }
.note-textarea{
box-sizing: border-box;
width: 100%; min-height: 260rpx; background:#e5e5e5; border-radius: 0; padding: 20rpx; font-size: 32rpx; color:#111;
}
.fixed-footer{ position: fixed; left: 0; right: 0; bottom: 0; padding: 20rpx; background: transparent; }
.fixed-btn{ background:#8B2316; border-radius: 12rpx; padding: 24rpx 30rpx; text-align:center; color:#fff; font-size:30rpx; margin: 0 20rpx; }
</style>

View File

@ -2,7 +2,7 @@
<view class="group-edit-page">
<uni-nav-bar
left-icon="left"
title="编辑分组"
:title="groupUuid ? '编辑分组' : '新建分组'"
@clickLeft="goBack"
fixed
color="#8B2316"
@ -11,7 +11,7 @@
backgroundColor="#eee"
>
<template #right>
<text class="save-text" @click="saveGroup">保存</text>
<view class="save-text" @click="saveGroup">保存</view>
</template>
</uni-nav-bar>
@ -26,30 +26,35 @@
<!-- 分组成员 -->
<view class="section-header">分组成员</view>
<view class="add-member" @click="addMember">
<view class="add-circle">
<up-icon name="plus" size="34" color="#bfbfbf" />
</view>
<text class="add-text">添加组患者</text>
</view>
<view class="member-list">
<view class="member-item" v-for="(m, idx) in members" :key="m.uuid || idx">
<image class="avatar" :src="docUrl + (m.photo || '')" mode="aspectFill" />
<text class="member-name">{{ m.realName || '未知' }}</text>
<view class="remove-btn" @click="removeMember(idx)">
<view class="remove-circle">
<up-icon name="minus" color="#fff" size="28rpx" bold></up-icon>
</view>
<!-- 已选中的成员 -->
<view class="selected-members" v-if="members.length > 0">
<view class="selected-item" v-for="(m, idx) in members" :key="m.uuid || idx">
<image class="selected-avatar" :src="docUrl + (m.photo || '')" mode="aspectFill" />
<text class="selected-name">{{ m.realName || '未知' }}</text>
<view class="remove-selected" @click="removeMember(idx)">
<text class="remove-text"></text>
<!-- <up-icon name="minus" size="43rpx" color="#fff" bold /> -->
</view>
</view>
</view>
<!-- 底部删除按钮 -->
<view class="bottom-danger">
<button class="danger-btn" @click="deleteGroup">删除分组</button>
<button class="danger-btn" @click="visible=true">删除分组</button>
</view>
<unidialog :visible="visible" :content="'删除分析组?'" @close="visible=false" @confirm="confirmDelete"></unidialog>
</view>
</template>
<script setup>
@ -58,19 +63,24 @@
import navTo from '@/utils/navTo.js'
import docUrl from '@/utils/docUrl.js'
import delImg from "@/static/iv_delete.png"
import api from '@/api/api.js'
import unidialog from '@/components/dialog/dialog.vue'
const groupUuid = ref('')
const groupName = ref('')
const members = ref([])
const visible = ref(false)
const confirmDelete = () => {
deleteGroup();
}
onLoad((query) => {
groupUuid.value = query?.uuid || ''
console.log(query)
groupUuid.value = query?.uuid || '';
if(groupUuid.value){
patientListByGroup()
}
// TODO: uuid
//
groupName.value = '看看'
members.value = [
{ uuid: 'u1', realName: '测试', photo: '' }
]
})
onShow(() => {
@ -89,9 +99,17 @@
const saveGroup = () => {
// TODO: : { uuid: groupUuid, name: groupName, members }
uni.showToast({ title: '已保存', icon: 'success' })
if(groupUuid.value){
groupUpdate()
}else{
groupAdd()
}
}
const addMember = () => {
// ID
const selectedIds = members.value.map(m => m.uuid)
uni.setStorageSync('preSelectedIds', selectedIds)
//
uni.navigateTo({
url: '/pages_app/selectPatient/selectPatient',
@ -106,15 +124,49 @@
members.value.splice(idx, 1)
}
const deleteGroup = () => {
uni.showModal({
title: '删除分组',
content: '确定要删除该分组吗?',
success: (res) => {
if (res.confirm) {
// TODO:
uni.showToast({ title: '已删除', icon: 'success' })
setTimeout(() => goBack(), 700)
}
api.groupDelete({
group_uuid: groupUuid.value
}).then(res => {
if(res.code == 200){
uni.showToast({ title: '删除成功', icon: 'none' })
setTimeout(() => goBack(), 700)
}
})
}
const groupAdd = () => {
if(!groupName.value){
uni.showToast({
title: '请输入分组名称',
icon: 'none'
})
return
}
api.groupAdd({
name: groupName.value,
patient_uuid: members.value.map(m => m.uuid).join(','),
}).then(res => {
if(res.code == 200){
uni.showToast({ title: '保存成功', icon: 'none' })
setTimeout(() => goBack(), 700)
}
})
}
const groupUpdate = () => {
if(!groupName.value){
uni.showToast({
title: '请输入分组名称',
icon: 'none'
})
return
}
api.groupUpdate({
uuid: groupUuid.value,
name: groupName.value,
patient_uuid: members.value.map(m => m.uuid).join(','),
}).then(res => {
if(res.code == 200){
uni.showToast({ title: '保存成功', icon: 'none' })
setTimeout(() => goBack(), 700)
}
})
}
@ -129,6 +181,16 @@
}
})
}
const patientListByGroup = async () => {
const res = await api.patientListByGroup({
group_uuid: groupUuid.value,
list_sort:0
})
if(res.code == 200){
members.value = res.data.patient_list
groupName.value = res.data.group.name
}
}
</script>
<style lang="scss" scoped>
@ -137,7 +199,16 @@
background: #f5f5f5;
padding-bottom: 160rpx;
}
.save-text{ color:#8B2316; font-size: 30rpx; }
.save-text{
background:#8B2316;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 15rpx 25rpx;
color:#fff;
border-radius: 12rpx;
}
.section-header{
background:#d9d9d9;
@ -160,6 +231,18 @@
.icon-btn{ padding-left: 20rpx; }
}
.selected-members{
background:#fff;
.selected-item{
display:flex; align-items:center; justify-content:space-between;
padding: 20rpx 30rpx; border-bottom: 1rpx solid #eee;
.selected-avatar{ width: 80rpx; height: 80rpx; border-radius: 12rpx; background:#ffe; }
.selected-name{ flex:1; margin-left: 20rpx; font-size: 30rpx; color:#333; }
.remove-selected{ width: 48rpx; height: 48rpx; border-radius: 50%; background:#8B2316; display:flex; align-items:center; justify-content:center; }
}
.remove-text{ font-size: 14rpx; color:#fff;font-weight: bold; }
}
.add-member{
background:#fff;
display:flex;
@ -176,18 +259,6 @@
.add-text{ font-size: 32rpx; color:#666; }
}
.member-list{
background:#fff;
.member-item{
display:flex; align-items:center; justify-content:space-between;
padding: 26rpx 30rpx; border-bottom: 1rpx solid #eee;
.avatar{ width: 100rpx; height: 100rpx; border-radius: 16rpx; background:#ffe; }
.member-name{ flex:1; margin-left: 20rpx; font-size: 32rpx; color:#333; }
.remove-btn{ padding-left: 20rpx; }
.remove-circle{ width: 48rpx; height:48rpx; background:#8B2316; border-radius:50%; display:flex; align-items:center; justify-content:center; }
}
}
.bottom-danger{
position: fixed; left:30rpx; right:30rpx; bottom: 30rpx;
background:#fff; border-top: 1rpx solid #eee;

View File

@ -0,0 +1,251 @@
<template>
<view class="group-manage-page">
<uni-nav-bar
left-icon="left"
title="分组管理"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="save-text" @click="saveGroups">保存</view>
</template>
</uni-nav-bar>
<!-- 已选中的分组 -->
<view class="selected-section">
<view class="tag-list">
<view
class="tag-item selected"
v-for="(group, index) in selectedGroups"
:key="group.uuid"
>
<text class="tag-text">{{ group.name }}</text>
</view>
</view>
</view>
<!-- 所有分组 -->
<view class="all-groups-section">
<view class="section-title">所有分组(选择分组)</view>
<view class="tag-list">
<view
class="tag-item"
:class="{ selected: isGroupSelected(group) }"
v-for="group in allGroups"
:key="group.uuid"
@click="selectGroup(group)"
>
<text class="tag-text">{{ group.name }}</text>
</view>
</view>
</view>
<!-- 底部添加按钮 -->
<view class="bottom-bar">
<button class="add-btn" @click="show=true">添加分组</button>
</view>
</view>
<up-modal :show="show" :title="'添加分组'" showCancelButton confirmColor="#8B2316" @confirm="addGroup" @cancel="show=false">
<view class="slot-content">
<input class="input" v-model="groupName" placeholder="请输入分组名称" />
</view>
</up-modal>
</template>
<script setup>
import { ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import api from "@/api/api.js";
const selectedGroups = ref([]);
const allGroups = ref([]);
const groupName = ref('');
const show = ref(false);
const patientUuid = ref('');
onLoad((options) => {
patientUuid.value = options.uuid
});
onShow(() => {
loadAllGroups();
});
const goBack = () => uni.navigateBack();
const loadAllGroups = async () => {
try {
const res = await api.groupList();
if (res.code === 200) {
allGroups.value = res.data || [];
}
} catch (e) {
console.error("加载分组列表失败:", e);
}
};
const selectGroup = (group) => {
//
const isSelected = selectedGroups.value.some((g) => g.uuid === group.uuid);
if (isSelected) {
//
const index = selectedGroups.value.findIndex((g) => g.uuid === group.uuid);
selectedGroups.value.splice(index, 1);
return;
}
//
if (selectedGroups.value.length >= 3) {
uni.showToast({ title: '最多选择三个分组', icon: 'none' });
return;
}
//
selectedGroups.value.push(group);
};
const removeSelectedGroup = (index) => {
selectedGroups.value.splice(index, 1);
};
const isGroupSelected = (group) => {
return selectedGroups.value.some((g) => g.uuid === group.uuid);
};
const addGroup = () => {
if(!groupName.value){
uni.showToast({ title: '请输入分组名称', icon: 'none' })
return;
}
let userInfo=uni.getStorageSync('userInfo')
api.groupAdd({
patient_uuid:patientUuid.value,
name: groupName.value,
expert_uuid:userInfo.uuid,
}).then(res => {
if(res.code == 200){
loadAllGroups();
show.value = false;
groupName.value = '';
}
})
};
const saveGroups = () => {
api.patientCardUpdateGroup({
name:selectedGroups.value.map(g => g.name).join(','),
group_uuid: selectedGroups.value.map(g => g.uuid).join(','),
patient_uuid: patientUuid.value
}).then(res => {
if(res.code == 200){
uni.showToast({ title: '保存成功', icon: 'none' })
setTimeout(() => goBack(), 700);
}
})
};
</script>
<style lang="scss" scoped>
.group-manage-page {
min-height: 100vh;
background: #fff;
padding-bottom: 160rpx;
}
.save-text {
color: #8b2316;
font-size: 30rpx;
}
.input{
width:450rpx;
background: #f8f8f8;
border-radius: 12rpx;
padding: 16rpx 24rpx;
font-size: 30rpx;
color: #333;
}
.selected-section {
min-height: 300rpx;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.all-groups-section {
padding: 30rpx;
}
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.tag-item {
display: flex;
align-items: center;
background: #fff;
border: 2rpx solid #999;
border-radius: 40rpx;
padding: 12rpx 20rpx;
position: relative;
&.selected {
border-color: #8b2316;
.tag-text {
color: #8b2316;
}
}
}
.tag-text {
font-size: 28rpx;
color: #999;
margin-right: 8rpx;
}
.tag-close {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
background: #8b2316;
display: flex;
align-items: center;
justify-content: center;
margin-left: 8rpx;
}
.close-icon {
font-size: 20rpx;
color: #fff;
font-weight: bold;
}
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-top: 1rpx solid #f0f0f0;
padding: 20rpx 30rpx env(safe-area-inset-bottom);
}
.add-btn {
width: 100%;
height: 88rpx;
background: #20b2aa;
color: #fff;
border: none;
border-radius: 12rpx;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@ -11,9 +11,11 @@
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
/>
>
<template #right>
<view class="save-text" @click="addGroup">新建</view>
</template>
</uni-nav-bar>
<!-- 筛选排序栏 -->
<view class="filter-sort-bar">
<view class="sort-section" @click="toggleGroupSort">
@ -65,7 +67,7 @@
<view class="groupcell" v-for="(group, gi) in groups" :key="group.uuid || gi">
<view class="section-title" @click="toggleGroup(gi)">
<view class="left">
<view class="imgbox">
<view class="imgbox" :class="{ 'active':openGroups[gi] }">
<up-image v-if="!openGroups[gi]" :src="groupRightImg" width="19rpx" height="32rpx" ></up-image>
<up-image v-else :src="groupDownImg" width="32rpx" height="19rpx" ></up-image>
</view>
@ -102,6 +104,7 @@ import groupDownImg from "@/static/groupup_big.png"
import api from '@/api/api.js';
import docUrl from '@/utils/docUrl'
import dayjs from 'dayjs'
import navTo from '@/utils/navTo.js'
const list_sort = ref(0);
const group_sort = ref(0);
@ -143,8 +146,9 @@ const toggleGroup = (idx) => {
};
const editGroup = (group) => {
// TODO:
uni.showToast({ title: `编辑:${group.name || '未命名分组'}`, icon: 'none' });
navTo({
url: `/pages_app/groupEdit/groupEdit?uuid=${group.uuid}`
})
};
//
@ -173,7 +177,11 @@ const chooseGroupSort = (type) => {
closeGroupSort();
fetchGroupList();
};
const addGroup = () => {
navTo({
url: `/pages_app/groupEdit/groupEdit`
})
}
const toggleInnerSort = () => {
showInnerSort.value = !showInnerSort.value;
if (showInnerSort.value) showGroupSort.value = false;
@ -250,6 +258,14 @@ const createNew = () => {
.popup-item.active .item-text {
color: #8B2316;
}
.save-text{
color:#8B2316;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.status-bar {
display: flex;
justify-content: space-between;
@ -454,7 +470,9 @@ const createNew = () => {
align-items:center;
}
.imgbox{
margin-top: 4rpx;
&.active{
margin-top: -20rpx;
}
margin-right: 8rpx;
}
.right-edit{

View File

@ -87,7 +87,7 @@
<view class="listbox">
<!-- 使用 up-index-list 索引组件数据动态渲染 -->
<up-index-list :index-list="indexList" custom-nav-height="140rpx">
<up-index-list :index-list="indexList" custom-nav-height="140rpx" v-if="patientList.length > 0">
<template v-for="group in patientGroups" :key="group.letter">
<up-index-item >
<up-index-anchor :text="group.letter" />
@ -117,18 +117,49 @@
</up-index-item>
</template>
</up-index-list>
<empty v-else></empty>
</view>
</view>
<view class="plan" v-if="activeTab === 'plan'">
<view class="apply-list" v-if="applyList.length > 0">
<view class="apply-item" v-for="item in applyList" :key="item.id">
<view class="apply-content">
<text class="apply-text">{{ item.messagePreview }}</text>
<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="140rpx" ></up-image>
</view>
<view class="right-content">
<view class="note">{{ item.note }}</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>
</view>
<empty v-else></empty>
<empty v-else></empty>
</scroll-view>
<!-- 悬浮添加按钮 -->
<view class="floating-add-btn" @click="showAddMenu">
@ -175,7 +206,7 @@
<script setup>
import { ref, getCurrentInstance } from 'vue';
import { ref, getCurrentInstance, computed } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import dayImg from "@/static/visit_data11.png"
import planImg from "@/static/visitplan.png"
@ -185,6 +216,8 @@
const patientList = ref([]);
import pinyin from 'pinyin';
import dayjs from 'dayjs'
import lineImg from "@/static/item_visitplan_fg.png"
const goPatientDetail = (uuid) => {
navTo({
url: `/pages_app/patientDetail/patientDetail?uuid=${uuid}`
@ -202,6 +235,18 @@
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([
{
@ -283,7 +328,51 @@
//
const isRefreshing = ref(false);
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++;
}
console.log('随访计划数据:', followUpList.value);
}
} catch (error) {
console.error('获取随访计划失败:', error);
uni.showToast({
title: '获取数据失败',
icon: 'error',
duration: 2000
});
} finally {
followUpLoading.value = false;
followUpRefreshing.value = false;
}
};
const getApplyList = async () => { //
try {
@ -321,17 +410,36 @@
};
const page=ref(1)
const followUpList = async () => {
let userInfo=uni.getStorageSync('userInfo')
const res = await api.followUpList({
page:page.value,
pageSize:10
});
if(res.code === '1'){
followUpList.value = res.data;
const followUpList = ref([]);
// 访
const followUpLoading = ref(false);
const followUpRefreshing = ref(false);
const followUpHasMore = ref(true);
const followUpPageSize = ref(10);
// 访
const groupedFollowUpList = computed(() => {
if (!followUpList.value || followUpList.value.length === 0) {
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 onRefresh = async () => {
@ -356,6 +464,23 @@
}
};
// 访
const onFollowUpRefresh = async () => {
followUpRefreshing.value = true;
await getFollowUpList(true);
uni.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
});
};
// 访
const onFollowUpLoadMore = async () => {
if (!followUpHasMore.value || followUpLoading.value) return;
await getFollowUpList(false);
};
//
const searchPatients = () => {
uni.showToast({
@ -397,11 +522,10 @@
//
break;
case 'plan':
// 访
uni.showToast({
title: '随访计划',
icon: 'none'
});
// 访 - 访
if (followUpList.value.length === 0) {
getFollowUpList(true);
}
break;
}
};
@ -432,12 +556,13 @@
const addFollowUpPlan = () => {
showAddMenuFlag.value = false;
uni.navigateTo({
url: '/pages_app/visit/visit'
url: '/pages_app/visit/visit?from=patientMsg'
});
};
//
const addSchedule = () => {
showAddMenuFlag.value = false;
uni.navigateTo({
url: '/pages_app/schedule/schedule'
});
@ -457,17 +582,25 @@
//
onShow(() => {
activeTab.value='message';
loadMessageList();
computeListHeight();
getApplyList();
patientListByGBK();
followUpList();
getFollowUpList();
});
//
const loadMessageList = () => {
// API
};
const goFollowDetail = (raw) => {
if(!raw) return;
navTo({
url: `/pages_app/followDetail/followDetail?followUpUuid=${encodeURIComponent(raw.uuid || '')}&patient_name=${encodeURIComponent(raw.patient_name || '')}`
});
};
</script>
<style lang="scss" scoped>
@ -476,7 +609,9 @@
min-height: 100vh;
}
.linebox{
margin:0 20rpx;
}
//
.nav-right {
display: flex;
@ -878,5 +1013,99 @@
background-color: #f0f0f0;
margin: 0 40rpx;
}
/* 随访计划样式 */
.plan{
position: fixed;
top: 140rpx;
left: 0;
right: 0;
bottom:110rpx;
}
.plan-scroll {
height: 100%;
}
.plan-list{
margin-top: 20rpx;
padding-bottom: 20rpx;
}
.plan-group{
background:#f5f5f5;
}
.group-header{
text-align: center;
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;
}
.left-rail{
display:flex;
align-items:center;
color:#8B2316;
}
.left-rail .day{
font-size: 36rpx;
margin-right: 12rpx;
}
.left-rail .rail-dot{
width: 4rpx;
height: 60rpx;
background:#8B2316;
border-radius: 2rpx;
}
.right-content{
flex:1;
display:flex;
align-items:center;
justify-content:space-between;
}
.right-content .note{
font-size: 30rpx;
color:#333;
}
/* 加载状态样式 */
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
color: #999;
.load-text {
margin-left: 10rpx;
font-size: 28rpx;
}
}
.no-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx;
.no-more-text {
font-size: 28rpx;
color: #999;
}
}
</style>

View File

@ -12,34 +12,79 @@
/>
<view class="form-block">
<view class="label">备注</view>
<input class="input" v-model.trim="remark" placeholder="给患者添加备注名" placeholder-class="ph" maxlength="20"/>
<input class="input" v-model.trim="remark" :placeholder="groupInfo.nickname || '给患者添加备注名'" placeholder-class="ph" maxlength="20"/>
</view>
<view class="form-block">
<view class="form-block" @click="goGroup">
<view class="label">分组</view>
<view class="iptbox">
<input class="input" v-model.trim="remark" placeholder="通过分组给患者分类" placeholder-class="ph" maxlength="20" readonly/>
<view class="iptbox readonly">
<input class="input readonly-input" v-model.trim="groupName" :placeholder="groupInfo.name || '通过分组给患者分类'" placeholder-class="ph" maxlength="20" readonly disabled/>
<uni-icons type="right" size="20" color="#666"></uni-icons>
</view>
</view>
<view class="form-block">
<view class="label">描述</view>
<textarea class="textarea" v-model.trim="note" placeholder="补充患者关键信息,方便随访患者" placeholder-class="ph" auto-height maxlength="140"/>
<textarea class="textarea" v-model.trim="note" :placeholder="groupInfo.note || '补充患者关键信息,方便随访患者'" placeholder-class="ph" auto-height maxlength="140"/>
</view>
<view class="bottom-bar">
<button class="save-btn" @click="saveRemark">保存</button>
<button class="save-btn" @click="updateNicknameNote">保存</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import api from '@/api/api.js'
import navTo from '@/utils/navTo.js'
import { onLoad, onShow } from '@dcloudio/uni-app'
const remark = ref('')
const note = ref('')
const groupName = ref('')
const patientUuid = ref('')
const patientCardData = ref({})
const groupInfo = ref({})
const goBack = () => {
uni.navigateBack()
}
const goGroup = () => {
navTo({
url: `/pages_app/groupManage/groupManage?uuid=${patientUuid.value}`
})
}
onLoad((options) => {
patientUuid.value = options.uuid || ''
})
onShow(() => {
patientCard()
})
const patientCard = () =>{
api.patientCard({
patient_uuid: patientUuid.value
}).then(res => {
if(res.code == 200){
patientCardData.value = res
groupInfo.value = res.group || {}
//
remark.value = res.group?.nickname || ''
note.value = res.group?.note || ''
groupName.value = res.group?.name || ''
}
})
}
const updateNicknameNote = async() => {
const res = await api.updateNicknameNote({
patient_uuid: patientUuid.value,
nickname: remark.value,
note: note.value,
})
if(res.code == 200){
uni.showToast({ title: '保存成功', icon: 'none' })
setTimeout(() => goBack(), 700)
}
}
const saveRemark = () => {
if (!remark.value) {
@ -63,6 +108,10 @@
background: #f8f8f8;
display: flex;
align-items: center;
&.readonly {
background: #f5f5f5;
opacity: 0.8;
}
}
background: #fff;
@ -79,6 +128,10 @@
padding: 24rpx;
font-size: 30rpx;
color: #333;
&.readonly-input {
background: transparent;
color: #999;
}
}
.textarea{
background: #f8f8f8;
@ -101,6 +154,9 @@
padding: 20rpx 24rpx env(safe-area-inset-bottom);
.save-btn{
width: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 92rpx;
background: #8B2316;
color: #fff;

View File

@ -17,15 +17,15 @@
<view class="cell" @click="goRemark">
<text class="cell-left">设置备注</text>
<view class="cell-right">
<text class="cell-desc">给患者添加备注名</text>
<text class="cell-desc">{{ groupInfo.nickname || '给患者添加备注名' }}</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
<view class="cell" @click="goRemark">
<view class="cell" @click="goGroup">
<text class="cell-left">设置分组</text>
<view class="cell-right">
<text class="cell-desc">通过分组给患者分类</text>
<text class="cell-desc">{{ groupInfo.name || '通过分组给患者分类' }}</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
@ -33,7 +33,7 @@
<view class="cell" @click="goRemark">
<text class="cell-left">患者描述</text>
<view class="cell-right">
<text class="cell-desc">补充患者关键信息方便随访患者</text>
<text class="cell-desc">{{ groupInfo.note || '补充患者关键信息,方便随访患者' }}</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
@ -41,7 +41,7 @@
<view class="cell" @click="setNextFollow">
<text class="cell-left">下次随访时间</text>
<view class="cell-right">
<text class="cell-desc">添加随访提醒</text>
<text class="cell-desc">{{patientCardData.FollowUpDate? patientCardData.FollowUpDate : '添加随访提醒' }}</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
</view>
</view>
@ -55,40 +55,80 @@
</view>
<view class="danger-block">
<text class="danger-text" @click="unbindPatient">解除随访</text>
<text class="danger-text" @click="visible=true">解除随访</text>
</view>
</view>
<unidialog :visible="visible" :content="message" @close="visible=false" @confirm="deleteFollowUp" ></unidialog>
</template>
<script setup>
import { ref } from 'vue'
import navTo from '@/utils/navTo.js'
import { onLoad } from '@dcloudio/uni-app'
import { onLoad, onShow } from '@dcloudio/uni-app'
import api from '@/api/api.js'
import unidialog from '@/components/dialog/dialog.vue'
const patientUuid = ref('')
const patientName = ref('')
const patientCardData = ref({})
const groupInfo = ref({})
const visible = ref(false)
const message = ref('')
const goBack = () => {
uni.navigateBack();
}
const goRemark = () => {
navTo({ url: '/pages_app/patientRemark/patientRemark' })
navTo({ url: '/pages_app/patientRemark/patientRemark?uuid=' + patientUuid.value })
}
const goGroup = () => {
navTo({ url: '/pages_app/groupManage/groupManage?uuid=' + patientUuid.value })
}
onLoad((options) => {
patientUuid.value = options.uuid;
console.log(patientUuid.value)
})
onShow(() => {
patientCard();
})
const setNextFollow = () => {
navTo({ url: '/pages_app/visit/visit' })
if(patientCardData.value.FollowUpUuid){
navTo({ url: '/pages_app/followDetail/followDetail?followUpUuid=' +patientCardData.value.FollowUpUuid+'&patient_name=' + patientName.value })
}else{
navTo({ url: '/pages_app/visit/visit?uuid=' + patientUuid.value+'&patient_name=' + patientName.value })
}
}
const goFeedback = () => {
navTo({ url: '/pages_app/feedback/feedback' })
}
const unbindPatient = () => {
uni.showModal({
title: '确认解除',
content: '确定要解除随访关系吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已提交解除', icon: 'success' })
// TODO: 访
}
const patientCard = () =>{
api.patientCard({
patient_uuid: patientUuid.value
}).then(res => {
if(res.code == 200){
patientName.value = res.group.nickname || res.patient.realName
message.value = `解除[${patientName.value}]的随访关系?`
patientCardData.value = res
groupInfo.value = res.group || {}
}
})
}
const deleteFollowUp = () => {
api.cancelRes({
patientUuid: patientUuid.value
}).then(res => {
if(res.code == 1){
uni.showToast({
title: '解除成功',
icon: 'none'
})
uni.redirectTo({
url: '/pages_app/patientMsg/patientMsg'
})
}
visible.value = false
})
}
</script>
<style lang="scss" scoped>

View File

@ -22,14 +22,14 @@
<!-- 排序和筛选栏 -->
<view class="filter-bar">
<view class="filter-item" @click="toggleSort">
<text class="filter-text">{{sort==0?'最新':'最热'}}</text>
<text class="filter-text active">{{sort==0?'最新':'最热'}}</text>
<view class="newbox">
<up-image :src="upImg" width="20rpx" height="26rpx" ></up-image>
</view>
</view>
<view class="divider"></view>
<view class="filter-item" @click="showFilterPopup">
<text class="filter-text">筛选</text>
<text class="filter-text" :class="{active:isFilterActive}">筛选</text>
<view class="filterbox">
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
</view>
@ -119,6 +119,7 @@
import downLoadImg from "@/static/wdxz.png"
import api from '@/api/api.js';
const isFilterActive=ref(false)
const keywords=ref('')
const sort = ref(0); //
//
const refreshing = ref(false);
@ -256,7 +257,7 @@
page: page.value,
sort: sort.value,
//
keywords: '',
keywords:keywords.value,
title: '',
// fileType: selectedFileType.value,
// sortBy: sortType.value
@ -355,18 +356,27 @@
const toggleTag = (index) => {
filterTags.value[index].selected = !filterTags.value[index].selected;
isFilterActive.value = filterTags.value.some(tag => tag.selected);
//isFilterActive.value = filterTags.value.some(tag => tag.selected);
};
const resetFilter = () => {
filterTags.value.forEach(tag => tag.selected = false);
isFilterActive.value = false;
//isFilterActive.value = false;
};
const confirmFilter = () => {
const selectedTags = filterTags.value.filter(tag => tag.selected);
console.log('选中的筛选标签:', selectedTags);
for (var i = 0; i < selectedTags.length; i++) {
if(keywords.value){
keywords.value+=","+selectedTags[i].DES
}else{
keywords.value=selectedTags[i].DES
}
}
isFilterActive.value =true;
hideFilterPopup();
loadData(true);
//
};
const fromatPrice=(price)=>{
@ -496,6 +506,12 @@
.filterbox{
margin-top: 5rpx;
}
.filter-text{
color:#666;
}
.filter-text.active{
color:#8B2316
}
}
}

View File

@ -84,8 +84,8 @@
<view class="calendartop">
<view class="title">时间日期</view>
<view class="datebox">
<view class="year">2025</view>
<view class="day">11月22日周六</view>
<view class="year">{{ headerYear }}</view>
<view class="day">{{ headerDay }}</view>
</view>
</view>
<uni-calendar class="uni-calendar--hook" @change="change" @monthSwitch="monthSwitch" />
@ -110,6 +110,8 @@
const followUpContent = ref('请于近日来院复诊、复查');
const remindMe = ref(false);
const remindPatient = ref(true);
const headerYear = ref('');
const headerDay = ref('');
//
const goBack = () => {
@ -170,7 +172,17 @@
});
const change=(e)=>{
console.log('change 返回:', e)
if(e && e.fulldate){
const d = new Date(e.fulldate);
const y = d.getFullYear();
const m = d.getMonth()+1;
const dd = d.getDate();
const weekdays = ['日','一','二','三','四','五','六'];
const w = weekdays[d.getDay()];
headerYear.value = `${y}`;
headerDay.value = `${m}${dd}日周${w}`;
selectedDate.value = `${y}${m}${dd}日(星期${w})`;
}
};
const monthSwitch=(e)=>{
@ -179,7 +191,6 @@
//
const initPage = () => {
//
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth() + 1;
@ -187,6 +198,8 @@
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
const weekday = weekdays[today.getDay()];
selectedDate.value = `${year}${month}${day}日(星期${weekday})`;
headerYear.value = `${year}`;
headerDay.value = `${month}${day}日周${weekday}`;
};
</script>

View File

@ -29,7 +29,7 @@
<!-- 列表 -->
<scroll-view class="list" scroll-y>
<view class="item" @click="toggle(p.uuid)" v-for="p in patientList" :key="p.uuid">
<view class="item" @click="toggle(p.uuid)" v-for="p in availablePatientList" :key="p.uuid">
<image class="avatar" :src="docUrl + (p.photo || '')" mode="aspectFill" />
<view class="name">{{ p.realName || '-' }}</view>
<view class="check" >
@ -50,6 +50,11 @@
const selectedIds = ref([])
const patientList = ref([])
const selectedDetail = ref([])
//
const availablePatientList = computed(() => {
return patientList.value
})
const patientListByGBK = async () => {
const res = await api.patientListByGBK();
@ -61,27 +66,47 @@
};
onLoad(() => {
// ID
try {
const preSelected = uni.getStorageSync('preSelectedIds')
if (Array.isArray(preSelected)) {
selectedIds.value = [...preSelected]
//
uni.removeStorageSync('preSelectedIds')
}
} catch (e) {}
})
onShow(() => {
patientListByGBK();
// IDselectedDetail
updateSelectedDetail();
});
// IDselectedDetail
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 || '' }
})
}
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 || '' })
}
}
const onSearch = () => {}
const goBack = () => uni.navigateBack()
const confirmSelect = () => {

View File

@ -22,20 +22,24 @@
<view class="form-section" @click="selectPatient">
<view class="section-label"><text class="label-text">患者</text></view>
<view class="section-content">
<text class="content-text">{{ selectedPatient || '请选择患者' }}</text>
<uni-icons type="right" size="20" color="#999"></uni-icons>
<text class="content-text">{{patientName}}</text>
<uni-icons type="right" size="20" color="#999" v-if="from"></uni-icons>
</view>
</view>
<!-- 随访内容列表 -->
<view class="title-bar">随访内容</view>
<scroll-view class="visit-list" scroll-y scroll-with-animation :scroll-into-view="scrollIntoViewId">
<view class="visitbox">
<view class="divider"></view>
<scroll-view class="visit-list" scroll-y scroll-with-animation :scroll-into-view="scrollIntoViewId">
<view class="visit-item" v-for="(item, idx) in visits" :key="item.id" :id="`visit-${item.id}`">
<view class="left">
<uni-icons type="close" size="20" :color="idx===0 ? '#cfcfcf' : '#c0392b'" @click="removeVisit(idx)"></uni-icons>
</view>
<view class="mid">
<text class="visit-text">{{ idx+1 }}.{{ defaultContent }}</text>
<view class="mid" @click="goVisitNote(idx,item.note)">
<text class="visit-text">{{ idx+1 }}.{{ item.note}}</text>
</view>
<view class="right" @click="pickDate(idx)">
<text class="date-text">{{ item.date }}</text>
@ -43,6 +47,7 @@
</view>
</view>
</scroll-view>
</view>
<!-- 添加随访日程按钮 -->
<view class="add-row" @click="addVisit">
@ -66,24 +71,85 @@
</view>
</view>
</view>
<up-overlay :show="show">
<view class="warp">
<view class="calendarbox">
<view class="calendartop">
<!-- <view class="title">时间日期</view> -->
<view class="datebox">
<view class="year">{{ headerYear }}</view>
<view class="day">{{ headerDay }}</view>
</view>
</view>
<uni-calendar class="uni-calendar--hook" :date="calendarDate" @change="change" @monthSwitch="monthSwitch" />
<view class="calendarbottom">
<view class="cancel" @click="show=false">取消</view>
<view class="ok" @click="confirmDate">确定</view>
</view>
</view>
</view>
</up-overlay>
</template>
<script setup>
import { ref,nextTick } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import { onShow,onLoad } from "@dcloudio/uni-app";
import navTo from '@/utils/navTo.js';
const patientUuid = ref('');
const patientName = ref('');
const show = ref(false);
const dealIndex=ref(0);
import api from '@/api/api.js'
const from = ref('');
onLoad((options) => {
patientUuid.value = options.uuid;
patientName.value = options.patient_name;
from.value = options.from;
});
const selectedPatient = ref('');
const defaultContent = '请近日来医院复诊、复查';
const visits = ref([
{ id: 1, date: formatDate(new Date()) },
{ id: 2, date: formatDate(addMonths(new Date(), 6)) },
{ id: 3, date: formatDate(addMonths(new Date(), 12)) },
{ id: 4, date: formatDate(addMonths(new Date(), 24)) }
{ id: 1, date: formatDate(addDays(new Date(), 1)),note:defaultContent},
{ id: 2, date: formatDate(addMonths(new Date(), 6)),note:defaultContent },
{ id: 3, date: formatDate(addMonths(new Date(), 9)),note:defaultContent },
{ id: 4, date: formatDate(addMonths(new Date(), 12)),note:defaultContent }
]);
const remindMe = ref(false);
const remindPatient = ref(true);
const scrollIntoViewId = ref('');
const tempDate=ref('');
const headerYear=ref('');
const headerDay=ref('');
const calendarDate=ref('');
const setHeaderByDate=(d)=>{
const y=d.getFullYear();
const m=d.getMonth()+1;
const dd=d.getDate();
const w=['日','一','二','三','四','五','六'][d.getDay()];
headerYear.value=`${y}`;
headerDay.value=`${m}${dd}日周${w}`;
}
const change=(e)=>{
tempDate.value = formatDate(new Date(e.fulldate));
setHeaderByDate(new Date(e.fulldate));
calendarDate.value = e.fulldate;
}
const confirmDate=()=>{
visits.value[dealIndex.value].date = tempDate.value;
show.value=false;
}
const goVisitNote=(idx,note)=>{
navTo({
url: '/pages_app/visitNote/visitNote?idx='+idx+'&note='+encodeURIComponent(note)+'&patient_name='+encodeURIComponent(patientName.value),
events: {
onVisitNoteSubmit: ({ note, idx }) => {
console.log('onVisitNoteSubmit', note, idx)
visits.value[Number(idx)].note = note
}
}
})
}
function formatDate(d){
const y=d.getFullYear();
const m=`${d.getMonth()+1}`.padStart(2,'0');
@ -96,22 +162,52 @@
nd.setMonth(nd.getMonth()+m);
return nd;
}
function addDays(d, days){
const nd = new Date(d);
nd.setDate(nd.getDate() + days);
return nd;
}
const addFollowUps=()=>{
api.addFollowUps({
patient_uuid: patientUuid.value,
note: visits.value.map(item=>item.note).join('☆'),
datetime: visits.value.map(item=>String(item.date).split('(')[0]).join('☆').replace(/\./g,'-'),
isremindpatient: remindPatient.value?1:0,
isremindme:remindMe.value?1:0,
type:2
}).then(res=>{
console.log(res)
if(res.code==200){
uni.showToast({ title: '已提交', icon: 'success' });
setTimeout(()=>uni.navigateBack(),700);
}
})
}
const goBack=()=>uni.navigateBack();
const submitPlan=()=>{
addFollowUps();
uni.showToast({ title: '已提交', icon: 'success' });
setTimeout(()=>uni.navigateBack(),1000);
};
const selectPatient=()=>{
uni.showActionSheet({
itemList:['张三','李四','王五','赵六'],
success:(res)=>{ selectedPatient.value=['张三','李四','王五','赵六'][res.tapIndex]; }
});
if(from.value=='patientMsg'){
navTo({url:'/pages_app/selectPatient/selectPatient'})
}
};
const addVisit=()=>{
const id= Date.now();
visits.value.push({ id, date: formatDate(new Date()) });
// 3
let baseDate = new Date();
const last = visits.value[visits.value.length - 1];
if (last && last.date) {
// last.date 2025.09.02()
const pure = String(last.date).split('(')[0].replace(/\./g, '-');
const parsed = new Date(pure);
if (!isNaN(parsed.getTime())) baseDate = parsed;
}
const nextDate = addMonths(baseDate, 3);
visits.value.push({ id, date: formatDate(nextDate) });
//
nextTick(()=>{ scrollIntoViewId.value = `visit-${id}`; });
};
@ -120,13 +216,16 @@
visits.value.splice(idx,1);
};
const pickDate=(idx)=>{
uni.showDatePicker({
mode:'date',
value:new Date().toISOString().split('T')[0],
success:(res)=>{
visits.value[idx].date = formatDate(new Date(res.value));
}
});
show.value=true;
dealIndex.value=idx;
//
const base=new Date();
tempDate.value=formatDate(base);
setHeaderByDate(base);
const y=base.getFullYear();
const m=`${base.getMonth()+1}`.padStart(2,'0');
const d=`${base.getDate()}`.padStart(2,'0');
calendarDate.value=`${y}-${m}-${d}`;
};
const toggleRemindMe=()=>{ remindMe.value=!remindMe.value; };
@ -136,6 +235,70 @@
</script>
<style lang="scss" scoped>
.warp {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.calendarbox{
margin-top: 140rpx;
display: flex;
background-color: #ffffff;
flex-direction: column;
align-items: center;
justify-content: center;
:deep(.uni-calendar-item--checked){
background:#ff3346!important;
border-radius: 50%;
color:#fff;
}
:deep(.uni-calendar-item--isDay){
border-radius: 50%;
background:#ff3346!important;
}
.calendartop {
width:100%;
.title{
font-size:34rpx;
padding:20rpx 30rpx 8rpx;
}
.datebox{
background:#ff3346;
display: flex;
width:100%;
color:#fff;
font-size: 46rpx;
padding:20rpx 30rpx;
box-sizing:border-box;
align-items: center;
justify-content: center;
}
}
.calendarbottom{
color:#ff3346;
display: flex;
box-sizing:border-box;
padding:30rpx 40rpx 40rpx;
width:100%;
justify-content: flex-end;
.ok{
font-size: 28rpx;
margin-left: 40rpx;
}
.cancel{
font-size: 28rpx;
}
}
}
}
.content {
background-color: #f5f5f5;
min-height: 100vh;
}
.content {
background-color: #f5f5f5;
min-height: 100vh;
@ -154,15 +317,28 @@
.content-text { font-size: 32rpx; color:#333; }
}
.title-bar { padding: 20rpx 30rpx; color:#8B2316; font-size: 32rpx; background:#ffffff; margin-top: 20rpx; border-top: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f0f0f0; }
.visitbox{
position: relative;
.divider{
position: absolute;
left: 50%;
transform: translateX(-50%);
height: 100%;
width: 2rpx;
background: #f0f0f0;
z-index:2;
}
}
.visit-list {
position: relative;
/* 让列表在可视区域内滚动 */
height: 400rpx;
background-color: #ffffff;
}
.visit-item { box-sizing:border-box;display:flex; align-items:center; padding: 26rpx 30rpx; border-bottom:1rpx solid #f0f0f0; }
.visit-item .left { width: 60rpx; display:flex; justify-content:center; }
.visit-item .mid { flex:1; }
.visit-item .mid { flex:1;width:354rpx; }
.visit-text { font-size: 30rpx; color:#333; display: inline-block;white-space: nowrap;width:80%;overflow: hidden;}
.visit-item .right { flex:1;display:flex; align-items:center; }
.date-text { display: inline-block;white-space: nowrap; font-size: 30rpx; color:#333; margin-right: 10rpx; }

View File

@ -0,0 +1,84 @@
<template>
<view class="visit-note-page">
<uni-nav-bar
left-icon="left"
title="添加随访计划"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="nav-right" @click="submit" v-if="!followUpUuid">
<text class="submit-text">添加</text>
</view>
</template>
</uni-nav-bar>
<!-- 患者行 -->
<view class="row" @click="noop">
<text class="row-label">患者</text>
<text class="row-value">{{ patientName }}</text>
</view>
<!-- 标题栏 -->
<view class="section-title">随访内容</view>
<!-- 输入框区域 -->
<view class="note-box">
<textarea class="note-textarea" v-model.trim="note" placeholder="请填写随访内容" auto-height :maxlength="200" />
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const patientName = ref('')
const note = ref('请近日来医院复诊、复查')
const idx = ref(0)
onLoad((options) => {
patientName.value =decodeURIComponent( options?.patient_name )
if (options?.note) note.value = decodeURIComponent(options.note)
if (options?.idx) idx.value = options.idx
})
const goBack = () => uni.navigateBack()
const submit = () => {
try {
const pages = getCurrentPages()
const curr = pages[pages.length - 1]
const ec = curr?.getOpenerEventChannel?.()
ec?.emit && ec.emit('onVisitNoteSubmit', { note: note.value, idx: Number(idx.value) })
} catch (e) {}
uni.showToast({ title: '已添加', icon: 'success' })
setTimeout(() => goBack(), 600)
}
const noop = () => {}
</script>
<style lang="scss" scoped>
.visit-note-page{
min-height: 100vh; background:#f5f5f5;
}
.nav-right{ display:flex; align-items:center; }
.submit-text{ color:#8B2316; font-size: 30rpx; }
.row{
margin-top: 20rpx; background:#fff; padding: 26rpx 30rpx; display:flex; align-items:center; justify-content:space-between; border-bottom: 1rpx solid #eee;
.row-label{ font-size: 32rpx; color:#8B2316; font-weight: 500; }
.row-value{ font-size: 32rpx; color:#333; }
}
.section-title{box-sizing: border-box; margin-top: 20rpx; background:#fff; padding: 22rpx 30rpx; color:#8B2316; font-size: 30rpx; border-top: 1rpx solid #eee; border-bottom: 1rpx solid #eee; }
.note-box{ background:#fff; padding: 20rpx 24rpx; }
.note-textarea{
width: 100%; min-height: 260rpx; background:#e5e5e5; border-radius: 0; padding: 20rpx; font-size: 32rpx; color:#111;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB