ykt-wxapp/pages/home/work-home.vue
2026-02-11 16:24:47 +08:00

417 lines
13 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>
<full-page :showSafeArea="false" :customScroll="true">
<template #header>
<view class="user-header bg-white px-15 py-15">
<view class="flex items-center justify-between">
<view class="flex items-center flex-grow">
<view class="relative user-avatar mr-10" @click="editProfile()">
<image v-if="doctorInfo && doctorInfo.avatar" class="avatar-img rounded-full overflow-hidden"
:src="doctorInfo.avatar" mode="aspectFill" />
<image v-else class="avatar-img rounded-full overflow-hidden" src="/static/home/avatar.svg"
mode="aspectFill" />
<view v-if="doctorInfo" class="edit-sub flex items-center justify-center rounded-full bg-primary">
<image class="edit-icon" src="/static/work/pen.svg" mode="aspectFill" />
</view>
</view>
<view class="flex-col">
<text v-if="doctorInfo && doctorInfo.anotherName" class="user-name text-dark text-lg font-semibold">
{{ doctorInfo.anotherName }}
</text>
<text v-else class="user-name text-black text-lg font-semibold" @click="editProfile()">请完善信息</text>
<view class="flex items-center mt-5">
<view v-if="!doctorInfo || !doctorInfo.anotherName"
class="mr-10 flex items-center bg-warning text-white text-center px-10 rounded-full"
@click="editProfile()">
<view class="mr-5 pb-2 text-sm leading-normal text-white">信息待完善</view>
<view class="translate-y--1">
<uni-icons type="right" size="12" color="#fff" />
</view>
</view>
<view v-if="certStatus" class="flex items-center px-10 rounded-full" :class="certStatus.classnames"
@click.stop="handleCert()">
<view class="text-sm leading-normal">{{ certStatus.text }}</view>
<view v-if="certStatus.text === '未认证'" class="translate-y--1">
<uni-icons type="right" size="12" color="#999" />
</view>
<view v-else-if="certStatus.text === '认证失败'" class="translate-y--1">
<uni-icons type="right" size="12" color="#fff" />
</view>
</view>
</view>
</view>
</view>
<!-- 右侧操作按钮 -->
<view class="flex items-center">
<view class="action-btn flex-col items-center mr-10" @click="invitePatient()">
<image class="mb-5 qrcode-icon" src="/static/work/qrcode.svg" />
<text class="action-text text-dark text-sm">邀请患者</text>
</view>
<view class="action-btn flex-col items-center" @click="handleMore">
<image class="mb-5 qrcode-icon" src="/static/work/more.svg" />
<text class="action-text text-dark text-sm">更多</text>
</view>
</view>
</view>
</view>
<view class="mt-15 px-15 py-12 flex items-center justify-between bg-white">
<view class="text-dark text-lg font-semibold">待办列表</view>
<view class="flex text-base rounded-full bg-gray">
<view class="py-5 px-15 rounded-full" :class="followUpType === 'person' ? 'bg-primary text-white' : ''"
@click="changeFollowType('person')">
个人
</view>
<view class="py-5 px-15 rounded-full" :class="followUpType === 'team' ? 'bg-primary text-white' : ''"
@click="changeFollowType('team')">
团队
</view>
</view>
</view>
<view class="py-10 px-15 flex items-center">
<view class="flex-shrink-0 text-sm mr-10">
<text class="text-dark"></text>
<text class="text-danger">{{ total }}</text>
<text class="text-dark"></text>
</view>
<view class="flex">
<view v-for="i in statusList" :key="i.value" class="mr-5 py-5 px-10 bg-white text-sm rounded-sm"
:class="filterData.eventStatus == i.value ? 'text-primary' : 'text-dark'" @click="changeStatus(i.value)">
{{ i.label }}
</view>
</view>
<view class="flex-shrink-0 flex-grow flex justify-end" @click="filterVisible = !filterVisible">
<image class="icon-filter" :src="`/static/work/icon-filter${filtered ? 'ed' : ''}.svg`" />
</view>
</view>
</template>
<scroll-view v-if="list.length" scroll-y="true" class="h-full">
<view v-for="i in list" :key="i._id" class="mb-10 shadow-lg bg-white" @click="toTodoDetail(i)">
<view class="flex items-center justify-between px-15 py-10 border-b">
<view class="text-base text-dark">计划执行: {{ i.planDate }}</view>
<view class="flex items-center">
<view class="mr-5 text-base text-dark">患者: {{ i.customerName }}</view>
<image class="icon-edit" src="/static/work/edit-pen.svg" />
</view>
</view>
<view class="py-10 px-15 flex items-center">
<view class="mr-5 text-lg font-semibold">{{ i.eventTypeLabel }}</view>
<view class="bg-light-text-color px-10 py-3 leading-normal text-base rounded overflow-hidden"
:class="statusClassNames[i.eventStatus] || 'text-gray'">
{{ i.eventStatusLabel }}
</view>
</view>
<view v-if="i.sendContent" class="px-15 text-base leading-normal text-gray">{{ i.sendContent }}</view>
<view v-if="i.enableSend && i.fileList.length" class="mt-10 px-15 flex items-center">
<view class="mr-5 w-0 flex-grow">
<view v-for="(file, idx) in i.fileList" :key="idx" class="truncate text-base leading-normal text-dark">
发送内容{{ file.file.name }}
</view>
</view>
<!-- <view v-if="doctorInfo && doctorInfo.userid && i.executorUserId && doctorInfo.userid === i.executorUserId"
class="bg-primary px-10 py-3 text-base text-white rounded-sm">发送</view> -->
</view>
<view class="mt-10 px-15 text-base leading-normal text-gray truncate">
{{ i.executorUserName }}{{ i.executeTeamName }}
</view>
<view class="px-15 pb-10 text-base leading-normal text-gray truncate">
创建{{ i.createTime }} {{ i.creatorUserName }}
</view>
</view>
</scroll-view>
<view v-else class="flex flex-col items-center justify-center h-full bg-white">
<empty-data text="暂无记录" />
</view>
<template #footer>
<view class="border-b"></view>
</template>
</full-page>
<cert-popup :visible="visible" @close="visible = false" />
<filter-popup :data="filterData" :visible="filterVisible" @close="filterVisible = false"
@confirm="changeFilterData($event)" />
</template>
<script setup>
import { computed, ref } from 'vue';
import { storeToRefs } from "pinia";
import { statusNames, ToDoEventType, statusClassNames } from '@/baseData';
import useGuard from "@/hooks/useGuard.js";
import useInfoCheck from '@/hooks/useInfoCheck';
import usePageList from '@/hooks/usePageList';
import useAccountStore from "@/store/account.js";
import useTeamStore from "@/store/team.js";
import api from '@/utils/api';
import { toast } from '@/utils/widget';
import certPopup from "./components/cert-popup.vue";
import filterPopup from './components/filter-popup.vue';
import EmptyData from "@/components/empty-data.vue";
import fullPage from '@/components/full-page.vue';
import dayjs from 'dayjs';
const certConfig = {
failed: { text: '认证失败', classnames: 'bg-danger text-white' },
verified: { text: '已认证', classnames: 'bg-success text-white' },
verifying: { text: '认证中', classnames: 'bg-warning text-white' },
unverified: { text: '未认证', classnames: 'bg-gray text-dark' },
};
const statusList = [{ label: '全部', value: 'all' }, { label: '待处理', value: 'processing' }, { label: '未开始', value: 'notStart' }]
const { useLoad, useShow } = useGuard();
const { getDoctorInfo } = useAccountStore();
const { account, doctorInfo } = storeToRefs(useAccountStore());
const { chargeTeams } = storeToRefs(useTeamStore());
const { withInfo } = useInfoCheck();
const visible = ref(false);
const filtered = ref(false);
const filterVisible = ref(false);
const filterData = ref({ eventStatus: 'processing' });
const followUpType = ref('person') // person team
const { total, list, page, pages, pageSize, changePage } = usePageList(getList)
const certStatus = computed(() => doctorInfo.value?.verifyStatus ? certConfig[doctorInfo.value.verifyStatus] : null)
// 邀请患者
const invitePatient = withInfo(() => uni.navigateTo({ url: '/pages/work/team/invite/invite-patient' }));
const handleMore = withInfo(() => {
uni.showActionSheet({
itemList: ["我的团队", "联系客服"], //, "关于"
success: (res) => {
const url = res.tapIndex === 0 ? '/pages/work/team/list/team-list' : '/pages/work/service/contact-service';
uni.navigateTo({ url });
},
});
})
function changeFilterData(data) {
filterData.value = data;
const case1 = Object.keys(data).filter(i => i != 'eventStatus').length > 0;
const case2 = statusList.some(i => i.value === data.eventStatus);
filtered.value = case1 || !case2;
changePage(1);
}
function changeFollowType(type) {
if (followUpType.value === type) return;
followUpType.value = type;
changePage(1);
}
function changeStatus(val) {
if (filterData.value.eventStatus === val) return;
filterData.value.eventStatus = val;
changePage(1);
}
function editProfile() {
uni.navigateTo({
url: "/pages/work/profile",
});
}
function handleCert() {
if (doctorInfo.value.verifyStatus === 'verifying') {
toast('信息认证中,请耐心等待!')
} else {
visible.value = true
}
}
function toTodoDetail(todo) {
uni.navigateTo({
url: `/pages/case/followup-detail?archiveId=${encodeURIComponent(todo.customerId || '')}&mode=edit&id=${encodeURIComponent(todo._id)}`,
});
// uni.navigateTo({
// url: `/pages/work/todo/todo-detail?id=${id}&editMode=${editMode ? 'YES' : ''}`
// })
}
async function getList() {
if (!doctorInfo.value || !doctorInfo.value.userid) {
list.value = []
return
}
const data = {
corpId: account.value.corpId,
startDate: filterData.value.startDate,
endDate: filterData.value.endDate,
page: page.value,
pageSize: pageSize.value
}
if (followUpType.value === 'person') {
data.executorUserId = doctorInfo.value.userid;
} else {
data.teamIds = chargeTeams.value.map(i => i.teamId);
}
if (filterData.value.eventStatus !== 'all') {
data.statusList = [filterData.value.eventStatus]
}
if (filterData.value.eventType) {
data.eventType = filterData.value.eventType;
}
const res = await api('getTeamTodos', data);
const arr = res && Array.isArray(res.data) ? res.data.map(i => ({
...i,
eventTypeLabel: ToDoEventType[i.eventType],
planDate: i.plannedExecutionTime && dayjs(i.plannedExecutionTime).isValid() ? dayjs(i.plannedExecutionTime).format("YYYY-MM-DD") : "",
endTime: i.endTime && dayjs(i.endTime).isValid() ? dayjs(i.endTime).format("YYYY-MM-DD HH:mm") : "",
createTime: i.createTime && dayjs(i.createTime).isValid() ? dayjs(i.createTime).format("YYYY-MM-DD HH:mm") : "",
eventStatusLabel: statusNames[i.eventStatus],
fileList: Array.isArray(i.fileList) ? i.fileList.filter(i => i && i.file && i.file.name) : [],
ownTeam: followUpType.value === 'team'
})) : [];
list.value = page.value === 1 ? arr : [...list.value, ...arr];
total.value = res && res.total > 0 ? res.total : 0;
pages.value = res && res.pages > 0 ? res.pages : 0;
if (!res && !res.success) {
toast(res?.message || '查询待办失败')
}
}
useShow(async () => {
await getDoctorInfo();
changePage(1)
})
</script>
<style lang="scss" scoped>
.edit-sub {
width: 36rpx;
height: 36rpx;
position: absolute;
right: 0;
bottom: 0;
}
.edit-icon {
width: 24rpx;
height: 24rpx;
}
.user-header {
border-bottom: 1px solid #eee;
}
.user-avatar {
width: 80rpx;
height: 80rpx;
.avatar-img {
width: 100%;
height: 100%;
}
}
.user-name {
line-height: 1.5;
}
.py-3 {
padding-top: 6rpx;
padding-bottom: 6rpx;
}
.status-tag {
padding: 6rpx 16rpx;
border-radius: 20rpx;
display: inline-flex;
align-items: center;
&.tag-orange {
background: #ff6b35;
border: 1px solid #fff;
}
&.tag-gray {
background: #f5f5f5;
border: 1px solid #ddd;
}
.tag-text {
font-size: 22rpx;
line-height: 1;
}
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.qrcode-icon {
width: 40rpx;
height: 40rpx;
}
.action-text {
line-height: 1.2;
}
}
.more-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
.more-icon {
font-size: 48rpx;
color: #333;
line-height: 1;
font-weight: bold;
}
}
.todo-section {
.section-title {
padding-top: 20rpx;
}
}
.icon-filter {
width: 42rpx;
height: 42rpx;
}
.bg-opacity {
position: relative;
}
.bg-opacity::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.2;
background: currentColor;
}
.py-3 {
padding-top: 6rpx;
padding-bottom: 6rpx;
}
.pb-2 {
padding-bottom: 4rpx;
}
.translate-y--1 {
transform: translateY(-2rpx);
}
.icon-edit {
width: 24rpx;
height: 24rpx;
}
</style>