353 lines
11 KiB
Vue
353 lines
11 KiB
Vue
<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>
|