ykt-wxapp/pages/home/work-home.vue
2026-02-06 17:10:48 +08:00

384 lines
12 KiB
Vue
Raw Permalink 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="status-tag tag-orange mr-10">
<text class="tag-text text-white">信息待完善</text>
</view>
<view v-if="certStatus" class="px-10 py-3 text-sm rounded-full" :class="certStatus.classnames"
@click.stop="handleCert()">
{{ certStatus.text }}
</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">
<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="text-base text-dark">患者: {{ i.customerName }}</view>
</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-opacity 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 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 { getTeams } = 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
}
}
async function getList() {
if (!doctorInfo.value || !doctorInfo.value.userid) {
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) : []
})) : [];
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 || '查询待办失败')
}
}
useLoad(() => {
console.log("工作台页面加载");
});
useShow(async () => {
console.log("工作台页面加!!!@@@载");
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;
}
</style>