2026-01-22 17:39:23 +08:00
|
|
|
|
<template>
|
2026-01-26 15:39:14 +08:00
|
|
|
|
<!-- Mobile 来源: ykt-management-mobile/src/pages/manage-plan/plan-list/plan-list.vue -->
|
2026-01-22 17:39:23 +08:00
|
|
|
|
<view class="page">
|
2026-01-26 15:39:14 +08:00
|
|
|
|
<view v-if="loading" class="loading">加载中...</view>
|
|
|
|
|
|
<view v-else-if="list.length === 0" class="empty">
|
2026-01-22 17:39:23 +08:00
|
|
|
|
<image class="empty-img" src="/static/empty.svg" mode="aspectFit" />
|
|
|
|
|
|
<view class="empty-text">暂无回访计划</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<scroll-view v-else scroll-y class="scroll">
|
|
|
|
|
|
<view v-for="(p, idx) in list" :key="p.id" class="item">
|
|
|
|
|
|
<view class="left">
|
|
|
|
|
|
<view class="name-row">
|
|
|
|
|
|
<view class="name">{{ p.planName }}</view>
|
|
|
|
|
|
<view v-if="p.planType === 'corp'" class="tag corp">机构</view>
|
|
|
|
|
|
<view class="tag outline" @click.stop="preview(p)">详情</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="desc">应用范围:{{ p.planDetail }}</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="btn" @click="select(p)">选择</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view style="height: 24px;"></view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref } from 'vue';
|
|
|
|
|
|
import { onLoad } from '@dcloudio/uni-app';
|
2026-01-26 15:39:14 +08:00
|
|
|
|
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';
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
const archiveId = ref('');
|
2026-01-26 15:39:14 +08:00
|
|
|
|
const loading = ref(false);
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
2026-01-26 15:39:14 +08:00
|
|
|
|
const list = ref([]);
|
|
|
|
|
|
|
|
|
|
|
|
const accountStore = useAccountStore();
|
|
|
|
|
|
const { account, doctorInfo } = storeToRefs(accountStore);
|
|
|
|
|
|
const { getDoctorInfo } = accountStore;
|
2026-01-22 17:39:23 +08:00
|
|
|
|
|
|
|
|
|
|
onLoad((options) => {
|
|
|
|
|
|
archiveId.value = options?.archiveId ? String(options.archiveId) : '';
|
2026-01-26 15:39:14 +08:00
|
|
|
|
loadList();
|
2026-01-22 17:39:23 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-26 15:39:14 +08:00
|
|
|
|
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 getTeamId() {
|
|
|
|
|
|
const t = uni.getStorageSync('ykt_case_current_team') || {};
|
|
|
|
|
|
return String(t.teamId || '') || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function normalizePlan(raw) {
|
|
|
|
|
|
if (!raw || typeof raw !== 'object') return null;
|
|
|
|
|
|
const id = String(raw.planId || raw._id || '');
|
|
|
|
|
|
if (!id) return null;
|
|
|
|
|
|
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,
|
|
|
|
|
|
planName: String(raw.planName || ''),
|
|
|
|
|
|
planType: String(raw.planType || ''),
|
|
|
|
|
|
planDetail: String(raw.planDetail || ''),
|
|
|
|
|
|
firstEventType: eventType,
|
|
|
|
|
|
firstEventTypeLabel: eventType ? getTodoEventTypeLabel(eventType) : '',
|
|
|
|
|
|
taskContent,
|
|
|
|
|
|
taskCount: taskList.length,
|
|
|
|
|
|
taskList,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadList() {
|
|
|
|
|
|
if (loading.value) return;
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await ensureDoctor();
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
const userId = getUserId();
|
|
|
|
|
|
const teamId = getTeamId();
|
|
|
|
|
|
if (!corpId || !userId || !teamId) {
|
|
|
|
|
|
list.value = [];
|
|
|
|
|
|
toast('缺少用户/团队信息');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const res = await api('getManagementPlan', {
|
|
|
|
|
|
corpId,
|
|
|
|
|
|
userId,
|
|
|
|
|
|
planType: 'team',
|
|
|
|
|
|
teamId,
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: 999,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
list.value = [];
|
|
|
|
|
|
toast(res?.message || '获取回访计划失败');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const arr = Array.isArray(res.data) ? res.data : [];
|
|
|
|
|
|
list.value = arr.filter((p) => p && p.planStatus === true).map(normalizePlan).filter(Boolean);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 17:39:23 +08:00
|
|
|
|
function select(plan) {
|
|
|
|
|
|
uni.setStorageSync('select-mamagement-plan', plan);
|
2026-01-26 15:39:14 +08:00
|
|
|
|
uni.navigateTo({ url: `/pages/case/plan-execute?archiveId=${encodeURIComponent(archiveId.value)}` });
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function preview(plan) {
|
2026-01-26 15:39:14 +08:00
|
|
|
|
uni.setStorageSync('preview-mamagement-plan', plan);
|
|
|
|
|
|
uni.navigateTo({ url: `/pages/case/plan-preview?archiveId=${encodeURIComponent(archiveId.value)}` });
|
2026-01-22 17:39:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.page {
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.scroll {
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
}
|
2026-01-26 15:39:14 +08:00
|
|
|
|
.loading {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #9aa0a6;
|
|
|
|
|
|
}
|
2026-01-22 17:39:23 +08:00
|
|
|
|
.item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
border-bottom: 1px solid #f2f2f2;
|
|
|
|
|
|
}
|
|
|
|
|
|
.left {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.name-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.name {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
max-width: 220px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag.corp {
|
|
|
|
|
|
background: #4f6ef7;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.tag.outline {
|
|
|
|
|
|
border: 1px solid #4f6ef7;
|
|
|
|
|
|
color: #4f6ef7;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.desc {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
border: 1px solid #4f6ef7;
|
|
|
|
|
|
color: #4f6ef7;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty {
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-img {
|
|
|
|
|
|
width: 160px;
|
|
|
|
|
|
height: 160px;
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
}
|
|
|
|
|
|
.empty-text {
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #9aa0a6;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|