2026-03-13 16:18:33 +08:00

298 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="group-edit-page">
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
:title="groupUuid ? '编辑分组' : '新建分组'"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eee"
>
<template #right>
<view class="save-text" @click="saveGroup">保存</view>
</template>
</uni-nav-bar>
</view>
<view class="contentbox">
<!-- 分组名称 -->
<view class="section-header">分组名称</view>
<view class="name-row">
<input class="name-input" v-model.trim="groupName" placeholder="请输入分组名称" maxlength="20" />
<view class="icon-btn" v-if="groupName" @click="clearName">
<up-image :src="delImg" width="48rpx" height="48rpx" />
</view>
</view>
<!-- 分组成员 -->
<view class="section-header">分组成员</view>
<view class="add-member" @click="addMember">
<up-image :src="addImg" width="120rpx" height="120rpx" />
<text class="add-text">添加组患者</text>
</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="getMemberAvatar(m)" mode="aspectFill" @error="handleMemberAvatarError(m)" />
<text class="selected-name">{{ m.nickname || 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>
<!-- 底部删除按钮 -->
<view class="bottom-danger">
<button class="danger-btn" @click="visible=true">删除分组</button>
</view>
<unidialog :visible="visible" :content="'删除分析组?'" @close="visible=false" @confirm="confirmDelete"></unidialog>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import navTo from '@/utils/navTo.js'
import docUrl from '@/utils/docUrl.js'
import delImg from "@/static/iv_delete.png"
import addImg from "@/static/addpatient.png"
import api from '@/api/api.js'
import defaultImg from "@/static/default.png"
import unidialog from '@/components/dialog/dialog.vue'
const groupUuid = ref('')
const groupName = ref('')
const members = ref([])
const visible = ref(false)
const avatarErrorMap = ref({})
const getAvatarKey = (member = {}) => String(member.uuid || member.id || member.realName || '')
const normalizeAvatarUrl = (photo) => {
const raw = String(photo || '').trim()
if (!raw) return defaultImg
if (/^https?:\/\//i.test(raw)) return raw
return `${docUrl}${raw}`
}
const getMemberAvatar = (member = {}) => {
const key = getAvatarKey(member)
if (key && avatarErrorMap.value[key]) return defaultImg
return normalizeAvatarUrl(member.photo)
}
const handleMemberAvatarError = (member) => {
const key = getAvatarKey(member)
if (!key) return
avatarErrorMap.value = {
...avatarErrorMap.value,
[key]: true
}
}
const confirmDelete = () => {
deleteGroup();
}
onLoad((query) => {
console.log(query)
groupUuid.value = query?.uuid || '';
if(groupUuid.value){
patientListByGroup()
}
// TODO: 根据 uuid 拉取分组详情
// 预置示例
})
onShow(() => {
// 兜底读取从选择页写入的缓存
try {
const cached = uni.getStorageSync('patientsSelectedPayload')
if (cached && Array.isArray(cached.list) && cached.list.length) {
mergeSelected(cached.list)
uni.removeStorageSync('patientsSelectedPayload')
}
} catch (e) {}
})
const goBack = () => uni.navigateBack()
const clearName = () => { groupName.value = '' }
const saveGroup = () => {
// TODO: 调用保存接口: { uuid: groupUuid, name: groupName, members }
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',
events: {
onPatientsSelected: ({ ids, list }) => {
if (Array.isArray(list)) mergeSelected(list)
}
}
})
}
const removeMember = (idx) => {
members.value.splice(idx, 1)
}
const deleteGroup = () => {
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({
group_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)
}
})
}
// 将选择结果合并到成员列表,按 uuid 去重
const mergeSelected = (selectedList) => {
const existIds = new Set(members.value.map(m => m.uuid))
selectedList.forEach(s => {
if (!existIds.has(s.uuid)) {
existIds.add(s.uuid)
members.value.push({ uuid: s.uuid, realName: s.realName, photo: s.photo || '' })
}
})
}
const patientListByGroup = async () => {
avatarErrorMap.value = {}
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>
.contentbox{
margin-top: calc(var(--status-bar-height) + 44px);
}
.group-edit-page{
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 160rpx;
}
.save-text{
color:#8B2316;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 15rpx 25rpx;
border-radius: 12rpx;
}
.section-header{
background:#d9d9d9;
color:#333;
padding: 22rpx 30rpx;
font-size: 30rpx;
}
.name-row{
background:#fff;
display:flex;
align-items:center;
justify-content:space-between;
padding: 24rpx 30rpx;
border-bottom: 1rpx solid #eee;
.name-input{
flex:1;
font-size: 32rpx;
color:#333;
}
.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;
align-items:center;
gap:20rpx;
padding: 26rpx 30rpx;
border-bottom: 1rpx solid #eee;
.add-circle{
width: 96rpx; height: 96rpx; border-radius: 50%;
border: 4rpx solid #e5e5e5;
display:flex; align-items:center; justify-content:center;
background:#fff;
}
.add-text{ font-size: 32rpx; color:#666; }
}
.bottom-danger{
position: fixed; left:30rpx; right:30rpx; bottom: 30rpx;
background:#fff; border-top: 1rpx solid #eee;
.danger-btn{ width:100%; height: 96rpx; background:#8B2316; color:#fff; border:none; border-radius: 12rpx; font-size: 32rpx;display: flex; align-items: center; justify-content: center; }
}
</style>