ykt-wxapp/pages/case/plan-list.vue

496 lines
12 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 customScroll pageStyle="background:#f4f5f7">
<template #header>
<view class="search-bar">
<input
v-model="keyword"
class="search-input"
placeholder="搜索"
confirm-type="search"
@input="onSearchInput"
@confirm="loadList"
/>
</view>
</template>
<view class="template-page">
<scroll-view scroll-y class="category-panel">
<view
v-for="item in categoryList"
:key="item.viewType"
class="category-item"
:class="{ active: activeViewType === item.viewType }"
@click="changeCategory(item.viewType)"
>
<text class="category-text">{{ item.label }}</text>
</view>
</scroll-view>
<view class="content-panel">
<view v-if="loading" class="state-text">加载中...</view>
<empty-data v-else-if="list.length === 0" fullCenter :text="emptyText" />
<scroll-view v-else scroll-y class="list-scroll">
<view v-for="p in list" :key="p.id" class="template-card">
<view class="card-header">
<view class="plan-name">{{ p.planName || '--' }}</view>
<view
v-if="canToggleFavorite(p)"
class="favorite-btn"
:class="{ collected: p.isFavorite }"
@click.stop="toggleFavorite(p)"
>
<uni-icons
class="favorite-icon"
:type="p.isFavorite ? 'star-filled' : 'star'"
size="18"
:color="p.isFavorite ? '#1f5cff' : '#667085'"
/>
</view>
</view>
<view class="creator" v-if="getCreatorText(p)">{{ getCreatorText(p) }}</view>
<view class="card-footer">
<view class="task-link" @click.stop="preview(p)">任务详情 </view>
<view class="select-btn" @click="select(p)">选择</view>
</view>
</view>
</scroll-view>
</view>
</view>
<template #footer>
<view class="footer">
<view class="service-entry" @click="toService">
<text class="service-muted">如没有符合的内容</text>
<text class="service-link">联系客服</text>
</view>
</view>
</template>
</full-page>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { storeToRefs } from 'pinia';
import api from '@/utils/api';
import useAccountStore from '@/store/account';
import { toast } from '@/utils/widget';
import { getTodoEventTypeLabel } from '@/utils/todo-const';
import EmptyData from '@/components/empty-data.vue';
import fullPage from '@/components/full-page.vue';
const archiveId = ref('');
const loading = ref(false);
const keyword = ref('');
const activeViewType = ref('my');
const emptyText = ref('暂无回访计划');
const list = ref([]);
const categoryList = ref([
{ label: '我的回访', viewType: 'my' },
{ label: '团队回访', viewType: 'team' },
{ label: '柚助手', viewType: 'doctorMiniapp' },
]);
let searchTimer = null;
const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore;
onLoad(async (options) => {
archiveId.value = options?.archiveId ? String(options.archiveId) : '';
await ensureDoctor();
buildCategoryList();
loadList();
});
async function ensureDoctor() {
if (doctorInfo.value) return;
if (!account.value?.openid) return;
try {
await getDoctorInfo();
} catch {
// ignore
}
}
function getUserId() {
const d = doctorInfo.value || {};
const a = account.value || {};
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
}
function getCorpId() {
const t = uni.getStorageSync('ykt_case_current_team') || {};
const a = account.value || {};
const d = doctorInfo.value || {};
return String(t.corpId || a.corpId || d.corpId || '') || '';
}
function buildCategoryList() {
const d = doctorInfo.value || {};
const categories = [
{ label: '我的回访', viewType: 'my' },
{ label: '团队回访', viewType: 'team' },
];
if (d.hospitalId || d.hospitalName) {
categories.push({
label: String(d.hospitalName || '第一执业点'),
viewType: 'firstHospital',
});
}
if (d.secondPracticeHospitalId || d.secondPracticeHospitalName) {
categories.push({
label: String(d.secondPracticeHospitalName || '第二执业点'),
viewType: 'secondHospital',
});
}
categories.push({ label: '柚助手', viewType: 'doctorMiniapp' });
categoryList.value = categories;
if (!categories.some((item) => item.viewType === activeViewType.value)) {
activeViewType.value = categories[0]?.viewType || 'my';
}
}
function normalizePlan(raw) {
if (!raw || typeof raw !== 'object') return null;
const id = String(raw._id || raw.planId || '');
if (!id) return null;
const planId = String(raw.planId || raw._id || '');
const taskList = Array.isArray(raw.taskList) ? raw.taskList : [];
const first = taskList[0] && typeof taskList[0] === 'object' ? taskList[0] : {};
const eventType = String(first.eventType || '');
const taskContent = String(first.taskContent || '');
return {
...raw,
id,
_id: String(raw._id || id),
planId,
sourceTemplateId: String(raw.sourceTemplateId || raw._id || id),
sourceTemplatePlanId: String(raw.sourceTemplatePlanId || planId),
planName: String(raw.planName || ''),
planType: String(raw.planType || ''),
planDetail: String(raw.planDetail || ''),
firstEventType: eventType,
firstEventTypeLabel: eventType ? getTodoEventTypeLabel(eventType) : '',
taskContent,
taskCount: taskList.length,
taskList,
isFavorite: Boolean(raw.isFavorite),
};
}
function unwrapLibraryData(res) {
const data = res?.data;
if (data && typeof data === 'object' && data.data && typeof data.data === 'object') {
return data.data;
}
return data && typeof data === 'object' ? data : {};
}
async function loadList() {
if (loading.value) return;
loading.value = true;
try {
await ensureDoctor();
buildCategoryList();
const corpId = getCorpId();
const userId = getUserId();
if (!corpId || !userId) {
list.value = [];
toast('缺少用户信息');
return;
}
const res = await api('getMyManagementPlanTemplateLibrary', {
corpId,
userId,
viewType: activeViewType.value,
keyword: keyword.value.trim(),
page: 1,
pageSize: 999,
});
if (!res?.success) {
list.value = [];
toast(res?.message || '获取回访计划失败');
return;
}
const payload = unwrapLibraryData(res);
const arr = Array.isArray(payload.list) ? payload.list : [];
list.value = arr.map(normalizePlan).filter(Boolean);
emptyText.value = payload.emptyState || '暂无回访计划';
} finally {
loading.value = false;
}
}
function changeCategory(viewType) {
if (activeViewType.value === viewType) return;
activeViewType.value = viewType;
list.value = [];
loadList();
}
function onSearchInput() {
if (searchTimer) clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
loadList();
}, 400);
}
function canToggleFavorite(plan) {
if (!plan || !plan._id) return false;
if (isCreatedByCurrentUser(plan)) return false;
if (activeViewType.value !== 'my') return true;
return Boolean(plan.isFavorite);
}
function isCreatedByCurrentUser(plan = {}) {
const userId = getUserId();
if (!userId) return false;
if (String(plan.createor || plan.creator || '') === userId) return true;
const signature = plan.creatorSignature && typeof plan.creatorSignature === 'object'
? plan.creatorSignature
: null;
return signature?.type === 'personal' && String(signature.person?.userId || '') === userId;
}
async function toggleFavorite(plan) {
const corpId = getCorpId();
const userId = getUserId();
const managementPlanId = String(plan?._id || '');
if (!corpId || !userId || !managementPlanId) return toast('缺少收藏信息');
const res = await api('toggleMyFollowupFavorite', {
corpId,
userId,
managementPlanId,
});
if (!res?.success) return toast(res?.message || '操作失败');
toast(res.message || '操作成功');
loadList();
}
function getCreatorText(plan = {}) {
const signature = plan.creatorSignature && typeof plan.creatorSignature === 'object'
? plan.creatorSignature
: null;
if (signature?.type === 'institution') {
return signature.institution?.name || '';
}
if (signature?.type === 'department') {
return signature.department?.name || '';
}
if (signature?.type === 'personal') {
return signature.person?.userName || '';
}
return String(plan.createorName || plan.creatorName || plan.teamName || plan.teanName || plan.createor || '');
}
function toService() {
uni.navigateTo({
url: '/pages/work/service/contact-service',
});
}
function select(plan) {
uni.setStorageSync('select-mamagement-plan', plan);
uni.navigateTo({ url: `/pages/case/plan-execute?archiveId=${encodeURIComponent(archiveId.value)}` });
}
function preview(plan) {
uni.setStorageSync('preview-mamagement-plan', plan);
uni.navigateTo({ url: `/pages/case/plan-preview?archiveId=${encodeURIComponent(archiveId.value)}` });
}
</script>
<style scoped>
.search-bar {
padding: 14rpx 18rpx 12rpx;
background: #fff;
border-bottom: 1rpx solid #edf0f2;
}
.search-input {
height: 68rpx;
padding: 0 24rpx;
border-radius: 12rpx;
background: #f3f5f8;
color: #111827;
font-size: 28rpx;
}
.template-page {
height: 100%;
display: flex;
min-height: 0;
}
.category-panel {
width: 210rpx;
height: 100%;
flex-shrink: 0;
background: #fbfcfe;
border-right: 1rpx solid #edf0f2;
}
.category-item {
position: relative;
min-height: 88rpx;
display: flex;
align-items: center;
margin: 8rpx 10rpx;
padding: 14rpx 12rpx 14rpx 16rpx;
border-radius: 12rpx;
color: #374151;
font-size: 30rpx;
line-height: 40rpx;
box-sizing: border-box;
}
.category-item.active {
color: #1667e8;
background: #eef5ff;
font-weight: 600;
}
.category-item.active::before {
content: "";
position: absolute;
left: 0;
top: 18rpx;
bottom: 18rpx;
width: 6rpx;
border-radius: 999rpx;
background: #1667e8;
}
.category-text {
width: 100%;
display: block;
overflow: visible;
word-break: break-all;
}
.content-panel {
position: relative;
flex: 1;
min-width: 0;
height: 100%;
background: #f5f6f8;
}
.list-scroll {
height: 100%;
}
.state-text {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #6b7280;
font-size: 30rpx;
}
.template-card {
position: relative;
margin: 16rpx 16rpx 0;
padding: 26rpx 24rpx 24rpx;
border: 1rpx solid #eef1f4;
border-radius: 14rpx;
background: #fff;
box-shadow: 0 4rpx 14rpx rgba(17, 24, 39, 0.04);
box-sizing: border-box;
}
.card-header {
display: flex;
align-items: flex-start;
gap: 16rpx;
}
.plan-name {
flex: 1;
min-width: 0;
color: #111827;
font-size: 34rpx;
font-weight: 700;
line-height: 46rpx;
word-break: break-word;
}
.favorite-btn {
flex-shrink: 0;
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: flex-end;
margin-left: auto;
}
.favorite-icon {
font-size: 32rpx;
line-height: 1;
}
.creator {
margin-top: 12rpx;
color: #6b7280;
font-size: 30rpx;
line-height: 40rpx;
word-break: break-all;
}
.task-link {
display: inline-flex;
color: #1667e8;
font-size: 30rpx;
line-height: 40rpx;
}
.card-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18rpx;
margin-top: 20rpx;
}
.select-btn {
flex-shrink: 0;
min-width: 76rpx;
height: 48rpx;
line-height: 46rpx;
padding: 0 18rpx;
border: 1rpx solid #1667e8;
border-radius: 999rpx;
color: #1667e8;
background: #fff;
font-size: 28rpx;
text-align: center;
box-sizing: border-box;
}
.footer {
position: relative;
background: #fff;
box-shadow: 0 -6rpx 18rpx rgba(15, 23, 42, 0.06);
}
.service-entry {
padding: 24rpx 30rpx;
text-align: center;
}
.service-muted {
color: #6b7280;
font-size: 30rpx;
}
.service-link {
color: #0877f1;
font-size: 30rpx;
}
</style>