权益生成待办功能完成

This commit is contained in:
zhanchao 2026-08-27 14:47:13 +08:00
parent 8daf6ce4e5
commit 1e0cd71432
5 changed files with 367 additions and 4 deletions

View File

@ -368,6 +368,16 @@ function markDone() {
const result = String(form.result || '').trim() || '已完成'; const result = String(form.result || '').trim() || '已完成';
const r = await api('setTodoStatus', { corpId, id: taskId.value, eventStatus: 'treated', result, userId, todoMethod: methodValue }); const r = await api('setTodoStatus', { corpId, id: taskId.value, eventStatus: 'treated', result, userId, todoMethod: methodValue });
if (!r?.success) return toast(r?.message || '操作失败'); if (!r?.success) return toast(r?.message || '操作失败');
if (todo.value?.benefitDeductOnComplete && todo.value?.benefitRecordId) {
const deductRes = await api('completeBenefitTodo', {
corpId,
todoId: taskId.value,
treatmentRecordId: todo.value.benefitRecordId,
executorUserId: userId,
completedAt: Date.now(),
});
if (!deductRes?.success) return toast(deductRes?.message || '待办已完成,但权益自动核销失败');
}
toast('操作成功'); toast('操作成功');
uni.$emit('archive-detail:followup-changed'); uni.$emit('archive-detail:followup-changed');
setTimeout(() => uni.navigateBack(), 300); setTimeout(() => uni.navigateBack(), 300);

View File

@ -83,6 +83,17 @@
<view class="field-label">有效期</view> <view class="field-label">有效期</view>
<view class="valid-time-picker" @tap="prepareValidTimePicker(projectEntry)"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeValidTime($event, projectEntry)"><view class="picker-value value-text">{{ projectEntry.validTime }}</view></picker></view> <view class="valid-time-picker" @tap="prepareValidTimePicker(projectEntry)"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeValidTime($event, projectEntry)"><view class="picker-value value-text">{{ projectEntry.validTime }}</view></picker></view>
</view> </view>
<view v-if="projectEntry.selected" class="todo-switch-row">
<text>生成待办</text>
<switch :checked="projectEntry.todoEnabled" color="#0877f1" @change="toggleTodo($event, projectEntry)" />
</view>
<view v-if="projectEntry.selected && projectEntry.todoEnabled" class="todo-config-card">
<input v-model="projectEntry.todoConfig.taskContent" class="todo-input" placeholder="任务内容" />
<picker :range="todoTypeOptions" range-key="label" @change="changeTodoType($event, projectEntry)"><view class="project-input"><text>类型{{ getTodoTypeLabel(projectEntry.todoConfig.eventType) }}</text><uni-icons type="right" size="16" color="#999" /></view></picker>
<view class="field-row"><view class="field-block"><view class="field-label">回访日期</view><view class="valid-time-picker" @tap="prepareTodoDatePicker(projectEntry, 'planDate')"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeTodoDate($event)"><view class="picker-value value-text">{{ projectEntry.todoConfig.planDate }}</view></picker></view></view><view class="field-block"><view class="field-label">截止日期</view><view class="valid-time-picker" @tap="prepareTodoDatePicker(projectEntry, 'expireDate')"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeTodoDate($event)"><view class="picker-value value-text">{{ projectEntry.todoConfig.expireDate }}</view></picker></view></view></view>
<view class="project-input" @tap="openTodoExecutorPicker(projectEntry)"><text :class="projectEntry.todoConfig.executorUserId ? 'value-text' : 'placeholder-text'">{{ projectEntry.todoConfig.executorName || '请选择团队和处理人' }}</text><uni-icons type="right" size="16" color="#999" /></view>
<view class="todo-deduct-row"><text>完成关联核销权益</text><switch :checked="projectEntry.todoConfig.deductBenefit" color="#0877f1" @change="projectEntry.todoConfig.deductBenefit = $event.detail.value" /></view>
</view>
</view> </view>
<view class="remove-entry" @click="removeEntry(index)">删除套餐</view> <view class="remove-entry" @click="removeEntry(index)">删除套餐</view>
</template> </template>
@ -185,6 +196,17 @@
</view> </view>
</picker> </picker>
</view> </view>
<view v-if="entry.project?._id" class="todo-switch-row">
<text>生成待办</text>
<switch :checked="entry.todoEnabled" color="#0877f1" @change="toggleTodo($event, entry)" />
</view>
<view v-if="entry.project?._id && entry.todoEnabled" class="todo-config-card">
<input v-model="entry.todoConfig.taskContent" class="todo-input" placeholder="任务内容" />
<picker :range="todoTypeOptions" range-key="label" @change="changeTodoType($event, entry)"><view class="project-input"><text>类型{{ getTodoTypeLabel(entry.todoConfig.eventType) }}</text><uni-icons type="right" size="16" color="#999" /></view></picker>
<view class="field-row"><view class="field-block"><view class="field-label">回访日期</view><view class="valid-time-picker" @tap="prepareTodoDatePicker(entry, 'planDate')"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeTodoDate($event)"><view class="picker-value value-text">{{ entry.todoConfig.planDate }}</view></picker></view></view><view class="field-block"><view class="field-label">截止日期</view><view class="valid-time-picker" @tap="prepareTodoDatePicker(entry, 'expireDate')"><picker mode="multiSelector" :range="validTimeRange" :value="validTimeIndex" @columnchange="onValidTimeColumnChange" @change="changeTodoDate($event)"><view class="picker-value value-text">{{ entry.todoConfig.expireDate }}</view></picker></view></view></view>
<view class="project-input" @tap="openTodoExecutorPicker(entry)"><text :class="entry.todoConfig.executorUserId ? 'value-text' : 'placeholder-text'">{{ entry.todoConfig.executorName || '请选择团队和处理人' }}</text><uni-icons type="right" size="16" color="#999" /></view>
<view class="todo-deduct-row"><text>完成关联核销权益</text><switch :checked="entry.todoConfig.deductBenefit" color="#0877f1" @change="entry.todoConfig.deductBenefit = $event.detail.value" /></view>
</view>
<view class="field-group"> <view class="field-group">
<view class="field-label">计划治疗医生</view> <view class="field-label">计划治疗医生</view>
@ -211,6 +233,22 @@
<text>添加权益项目</text> <text>添加权益项目</text>
</view> </view>
<view v-if="todoExecutorPickerVisible" class="picker-mask" @click="closeTodoExecutorPicker">
<view class="todo-executor-picker" @click.stop>
<view class="picker-header"><text class="picker-title">请选择团队和处理人</text><uni-icons type="closeempty" size="22" color="#666" @click="closeTodoExecutorPicker" /></view>
<scroll-view scroll-y class="todo-executor-list">
<view v-for="team in todoTeams" :key="team.teamId" class="todo-team-group">
<view class="todo-team-title">{{ team.name }}</view>
<view v-for="member in getTodoExecutorsForTeam(team)" :key="member.userid" class="todo-executor-item" @tap="selectTodoExecutor(member, team)">
<text>{{ member.name }}</text><text v-if="isSelectedTodoExecutor(member, team)" class="todo-selected">已选择</text>
</view>
<view v-if="!getTodoExecutorsForTeam(team).length" class="todo-empty">暂无团队成员</view>
</view>
<view v-if="!todoTeams.length" class="todo-empty">暂无可选团队</view>
</scroll-view>
</view>
</view>
<view v-if="projectPickerVisible" class="picker-mask" @click="closeProjectPicker"> <view v-if="projectPickerVisible" class="picker-mask" @click="closeProjectPicker">
<view class="project-picker" @click.stop> <view class="project-picker" @click.stop>
<view class="picker-header"> <view class="picker-header">
@ -254,11 +292,12 @@
</template> </template>
<script setup> <script setup>
import { nextTick, ref } from "vue"; import { computed, nextTick, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app"; import { onLoad } from "@dcloudio/uni-app";
import api from "@/utils/api.js"; import api from "@/utils/api.js";
import useAccountStore from "@/store/account.js"; import useAccountStore from "@/store/account.js";
import fullPage from "@/components/full-page.vue"; import fullPage from "@/components/full-page.vue";
import { TODO_EVENT_TYPE_LABELS } from "@/utils/todo-const.js";
const PENDING_FOLLOWUP_SEND_STORAGE_KEY = "ykt_followup_pending_send"; const PENDING_FOLLOWUP_SEND_STORAGE_KEY = "ykt_followup_pending_send";
@ -287,6 +326,12 @@ const projectPageSize = 30;
const VALID_YEAR_SPAN = 50; const VALID_YEAR_SPAN = 50;
const validTimeRange = ref([[], [], []]); const validTimeRange = ref([[], [], []]);
const validTimeIndex = ref([0, 0, 0]); const validTimeIndex = ref([0, 0, 0]);
const todoDateTarget = ref(null);
const teamMembers = ref([]);
const todoTeams = ref([]);
const todoExecutorPickerVisible = ref(false);
const todoExecutorTarget = ref(null);
const todoTypeOptions = Object.entries(TODO_EVENT_TYPE_LABELS).map(([value, label]) => ({ value, label }));
let searchTimer = null; let searchTimer = null;
let entryKey = 0; let entryKey = 0;
@ -397,6 +442,8 @@ function createEntry() {
dept: null, dept: null,
treatmentDoctorUserId: "", treatmentDoctorUserId: "",
treatmentDoctorName: "", treatmentDoctorName: "",
todoEnabled: false,
todoConfig: null,
}; };
} }
@ -419,6 +466,7 @@ onLoad((options = {}) => {
loadTeamContext().then(() => { loadTeamContext().then(() => {
loadCorpSettings(); loadCorpSettings();
loadStaffList(); loadStaffList();
loadTodoTeams();
}); });
}); });
@ -428,6 +476,9 @@ async function loadTeamContext() {
const res = await api("getTeamData", { teamId: teamId.value, corpId: corpId.value }, false); const res = await api("getTeamData", { teamId: teamId.value, corpId: corpId.value }, false);
const team = res?.data || {}; const team = res?.data || {};
if (team.corpId && team.corpId !== corpId.value) corpId.value = team.corpId; if (team.corpId && team.corpId !== corpId.value) corpId.value = team.corpId;
teamMembers.value = Array.isArray(team.memberList) ? team.memberList.map((member) => (
typeof member === "string" ? { userid: member, name: member } : member
)) : [];
} catch (error) { } catch (error) {
console.error("加载服务团队失败", error); console.error("加载服务团队失败", error);
} }
@ -441,12 +492,41 @@ async function loadStaffList() {
staffList.value = Array.isArray(list) staffList.value = Array.isArray(list)
? list.filter((staff) => staff?.userid).map((staff) => ({ ...staff, anotherName: staff.anotherName || staff.name || staff.userid })) ? list.filter((staff) => staff?.userid).map((staff) => ({ ...staff, anotherName: staff.anotherName || staff.name || staff.userid }))
: []; : [];
const staffMap = new Map(staffList.value.map((staff) => [staff.userid, staff]));
teamMembers.value = teamMembers.value.map((member) => ({ ...member, ...(staffMap.get(member.userid) || {}) }));
todoTeams.value = todoTeams.value.map((team) => ({
...team,
memberList: (team.memberList || []).map((member) => {
const userid = typeof member === "string" ? member : member?.userid;
return { ...(typeof member === "object" ? member : {}), ...(staffMap.get(userid) || {}), userid };
}),
}));
} catch (error) { } catch (error) {
staffList.value = []; staffList.value = [];
console.error("加载机构员工失败", error); console.error("加载机构员工失败", error);
} }
} }
async function loadTodoTeams() {
const userId = accountStore.doctorInfo?.userid || accountStore.account?.userid || "";
if (!corpId.value || !userId) return;
try {
const res = await api("getTeamBymember", { corpId: corpId.value, corpUserId: userId }, false);
const list = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
const baseTeams = list.length ? list : [{ teamId: teamId.value, name: teamName.value, memberList: teamMembers.value }];
const details = await Promise.all(baseTeams.map(async (team) => {
const currentTeamId = team.teamId || team._id || team.id;
if (!currentTeamId) return null;
const detailRes = await api("getTeamData", { corpId: corpId.value, teamId: currentTeamId }, false);
const detail = detailRes?.data || {};
return { teamId: currentTeamId, name: detail.name || team.name || team.teamName || "未命名团队", memberList: detail.memberList || team.memberList || [] };
}));
todoTeams.value = details.filter(Boolean);
} catch (error) {
todoTeams.value = teamId.value ? [{ teamId: teamId.value, name: teamName.value, memberList: teamMembers.value }] : [];
}
}
async function loadCorpSettings() { async function loadCorpSettings() {
if (!corpId.value) return; if (!corpId.value) return;
try { try {
@ -627,6 +707,7 @@ function createPackageProjectEntry(project = {}, packageItem = {}) {
project: normalizedProject, project: normalizedProject,
price: Number(normalizedProject.price ?? project.price ?? 0), price: Number(normalizedProject.price ?? project.price ?? 0),
usageCount: Number(project.number || 1), usageCount: Number(project.number || 1),
packageUnitUsageCount: Number(project.number || 1),
discount: packageItem.packageType === "free" ? 10 : Number(project.discount || 10), discount: packageItem.packageType === "free" ? 10 : Number(project.discount || 10),
totalPrice: Number(project.totalPrice || 0), totalPrice: Number(project.totalPrice || 0),
selected: packageItem.packageType === "fixed" || project.required === true, selected: packageItem.packageType === "fixed" || project.required === true,
@ -694,6 +775,7 @@ function togglePackageProject(entry, projectEntry) {
return; return;
} }
projectEntry.selected = !projectEntry.selected; projectEntry.selected = !projectEntry.selected;
if (projectEntry.selected) projectEntry.usageCount = Number(entry.usageCount || 1) * Number(projectEntry.packageUnitUsageCount || 1);
syncPackageSelection(entry); syncPackageSelection(entry);
syncPackageTotals(entry); syncPackageTotals(entry);
} }
@ -702,9 +784,103 @@ function calculatePackageTotal(entry) {
entry.usageCount = Math.max(1, Number(entry.usageCount || 1)); entry.usageCount = Math.max(1, Number(entry.usageCount || 1));
entry.discount = clampNumber(toNumber(entry.discount, 10), 0, 10); entry.discount = clampNumber(toNumber(entry.discount, 10), 0, 10);
entry.totalPrice = toFixedNumber(Number(entry.price || 0) * entry.usageCount * entry.discount / 10, 2); entry.totalPrice = toFixedNumber(Number(entry.price || 0) * entry.usageCount * entry.discount / 10, 2);
entry.projects.filter((project) => project.selected).forEach((project) => {
project.usageCount = entry.usageCount * Number(project.packageUnitUsageCount || 1);
});
syncPackageTotals(entry); syncPackageTotals(entry);
} }
function getDefaultTodoConfig(entry) {
const planDate = formatDate(new Date());
return {
taskContent: entry.project?.projectName || "",
eventType: "benefitExecution",
planDate,
expireDate: entry.validTime || formatDate(new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)),
executorUserId: "",
executorName: "",
executeTeamId: "",
executeTeamName: "",
deductBenefit: false,
};
}
function toggleTodo(event, entry) {
entry.todoEnabled = Boolean(event.detail.value);
if (entry.todoEnabled) entry.todoConfig = entry.todoConfig || getDefaultTodoConfig(entry);
}
function getTodoTypeLabel(value) {
return TODO_EVENT_TYPE_LABELS[value] || "请选择类型";
}
function changeTodoType(event, entry) {
entry.todoConfig.eventType = todoTypeOptions[Number(event.detail.value)]?.value || "benefitExecution";
}
function getTodoExecutors(entry) {
const team = todoTeams.value.find((item) => String(item.teamId) === String(entry.todoConfig?.executeTeamId));
return (team?.memberList || []).map((member) => {
const userid = typeof member === "string" ? member : member?.userid;
const staff = staffList.value.find((item) => item.userid === userid);
return { userid, name: staff?.anotherName || staff?.name || member?.anotherName || member?.name || userid };
}).filter((member) => member.userid);
}
function openTodoExecutorPicker(entry) {
todoExecutorTarget.value = entry;
todoExecutorPickerVisible.value = true;
}
function closeTodoExecutorPicker() {
todoExecutorPickerVisible.value = false;
todoExecutorTarget.value = null;
}
function getTodoExecutorsForTeam(team) {
return (team?.memberList || []).map((member) => {
const userid = typeof member === "string" ? member : member?.userid;
const staff = staffList.value.find((item) => item.userid === userid);
return { userid, name: staff?.anotherName || staff?.name || member?.anotherName || member?.name || userid };
}).filter((member) => member.userid);
}
function selectTodoExecutor(member, team) {
const entry = todoExecutorTarget.value;
if (!entry?.todoConfig) return;
entry.todoConfig.executeTeamId = team.teamId;
entry.todoConfig.executeTeamName = team.name;
entry.todoConfig.executorUserId = member.userid;
entry.todoConfig.executorName = member.name;
closeTodoExecutorPicker();
}
function isSelectedTodoExecutor(member, team) {
const config = todoExecutorTarget.value?.todoConfig;
return config?.executeTeamId === team.teamId && config?.executorUserId === member.userid;
}
function prepareTodoDatePicker(entry, field) {
todoDateTarget.value = { entry, field };
prepareValidTimePicker({ validTime: entry.todoConfig[field] });
}
function changeTodoDate(event) {
const target = todoDateTarget.value;
if (!target) return;
const [yearIndex, monthIndex, dayIndex] = event.detail.value || validTimeIndex.value;
const year = Number(validTimeRange.value[0][yearIndex].replace("年", ""));
const month = Number(validTimeRange.value[1][monthIndex].replace("月", ""));
const day = Number(validTimeRange.value[2][dayIndex].replace("日", ""));
const selectedDate = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
target.entry.todoConfig[target.field] = selectedDate;
validTimeIndex.value = [yearIndex, monthIndex, dayIndex];
if (target.field === "planDate" && !target.entry.validTime) {
target.entry.todoConfig.expireDate = formatDate(new Date(new Date(`${target.entry.todoConfig.planDate}T00:00:00`).getTime() + 7 * 24 * 60 * 60 * 1000));
}
todoDateTarget.value = null;
}
function syncPackageTotals(entry) { function syncPackageTotals(entry) {
const selected = entry.projects.filter((item) => item.selected); const selected = entry.projects.filter((item) => item.selected);
const baseTotal = selected.reduce((sum, item) => sum + Number(item.price || 0) * Number(item.usageCount || 1), 0); const baseTotal = selected.reduce((sum, item) => sum + Number(item.price || 0) * Number(item.usageCount || 1), 0);
@ -794,6 +970,8 @@ function validateEntries() {
for (const projectEntry of selectedProjects) { for (const projectEntry of selectedProjects) {
if (!projectEntry.dept?._id) return `${projectEntry.project.projectName}请选择治疗科室`; if (!projectEntry.dept?._id) return `${projectEntry.project.projectName}请选择治疗科室`;
if (projectEntry.hasValidDays && !projectEntry.validTime) return `${projectEntry.project.projectName}请选择有效日期`; if (projectEntry.hasValidDays && !projectEntry.validTime) return `${projectEntry.project.projectName}请选择有效日期`;
const todoError = validateTodo(projectEntry);
if (todoError) return todoError;
} }
continue; continue;
} }
@ -804,11 +982,82 @@ function validateEntries() {
if (!Number.isFinite(Number(entry.totalPrice)) || Number(entry.totalPrice) < 0) return `${label}总价不能小于0`; if (!Number.isFinite(Number(entry.totalPrice)) || Number(entry.totalPrice) < 0) return `${label}总价不能小于0`;
if (!Number(entry.usageCount) || Number(entry.usageCount) <= 0) return `${label}数量必须大于0`; if (!Number(entry.usageCount) || Number(entry.usageCount) <= 0) return `${label}数量必须大于0`;
if (!entry.dept?._id) return `${label}请选择治疗科室`; if (!entry.dept?._id) return `${label}请选择治疗科室`;
const todoError = validateTodo(entry);
if (todoError) return todoError;
} }
if (!patientId.value) return "当前会话缺少患者档案关联"; if (!patientId.value) return "当前会话缺少患者档案关联";
return ""; return "";
} }
function validateTodo(entry) {
if (!entry.todoEnabled) return "";
const config = entry.todoConfig || {};
if (!String(config.taskContent || "").trim()) return `${entry.project?.projectName || "权益项目"}请填写待办内容`;
if (!config.eventType) return `${entry.project?.projectName || "权益项目"}请选择待办类型`;
if (!config.planDate || !config.expireDate || config.expireDate < config.planDate) return `${entry.project?.projectName || "权益项目"}待办日期不正确`;
if (!config.executeTeamId) return `${entry.project?.projectName || "权益项目"}请选择处理团队`;
if (!config.executorUserId) return `${entry.project?.projectName || "权益项目"}请选择待办处理人`;
return "";
}
function getResponseId(data) {
if (typeof data === "string") return data;
if (!data || typeof data !== "object") return "";
return data._id || data.id || getResponseId(data.data);
}
async function linkBenefitTodos(entry, treatmentRecordId, userId) {
if (!entry.todoEnabled) return;
const config = entry.todoConfig;
const todoIds = [];
const quantity = Math.max(1, Number(entry.usageCount || 1));
for (let sequence = 1; sequence <= quantity; sequence += 1) {
const res = await api("createTodo", {
corpId: corpId.value,
params: {
corpId: corpId.value,
customerId: patientId.value,
customerName: patientName.value,
taskContent: String(config.taskContent).trim(),
eventType: config.eventType,
eventStatus: "untreated",
executeMethod: "todo",
plannedExecutionTime: new Date(`${config.planDate}T00:00:00`).getTime(),
expireTime: new Date(`${config.expireDate}T23:59:59`).getTime(),
executorUserId: config.executorUserId,
executeTeamId: config.executeTeamId,
executeTeamName: config.executeTeamName,
creatorUserId: userId,
benefitRecordId: treatmentRecordId,
benefitProjectId: entry.project._id,
benefitUsageCount: 1,
benefitTotalUsageCount: quantity,
taskQuantity: 1,
taskSequence: sequence,
benefitDeductOnComplete: Boolean(config.deductBenefit),
},
}, false);
if (!res?.success) throw new Error(res?.message || "待办生成失败");
const todoId = getResponseId(res?.data || res);
if (!todoId) throw new Error("待办生成成功但未返回待办ID");
todoIds.push(todoId);
}
const updateRes = await api("updateTreatmentRecord", {
corpId: corpId.value,
id: treatmentRecordId,
params: {
benefitTodoId: todoIds[0],
benefitTodoIds: todoIds,
benefitTodoConfig: {
benefitDeductOnComplete: Boolean(config.deductBenefit),
plannedExecutionTime: new Date(`${config.planDate}T00:00:00`).getTime(),
expireTime: new Date(`${config.expireDate}T23:59:59`).getTime(),
},
},
}, false);
if (!updateRes?.success) throw new Error(updateRes?.message || "权益待办关联失败");
}
function buildBenefitMessage(createdEntries) { function buildBenefitMessage(createdEntries) {
const items = createdEntries.map((entry) => ({ const items = createdEntries.map((entry) => ({
projectId: entry.project._id, projectId: entry.project._id,
@ -907,8 +1156,11 @@ async function submitEntries() {
teamId: teamId.value, teamId: teamId.value,
corpId: corpId.value, corpId: corpId.value,
}; };
const res = await api("addTreatmentRecord", { userId, operationSource: "wxApp", params: treatmentData }, false); const res = await api("addTreatmentRecord", { corpId: corpId.value, userId, operationSource: "wxApp", params: treatmentData }, false);
if (!res?.success) throw new Error(res?.message || "权益录入失败"); if (!res?.success) throw new Error(res?.message || "权益录入失败");
const treatmentRecordId = getResponseId(res?.data || res);
if (!treatmentRecordId) throw new Error("权益录入成功但未返回权益记录ID");
await linkBenefitTodos(entry, treatmentRecordId, userId);
createdEntries.push(entry); createdEntries.push(entry);
} }
queueBenefitMessage(createdEntries); queueBenefitMessage(createdEntries);
@ -1107,6 +1359,93 @@ async function submitEntries() {
font-size: 24rpx; font-size: 24rpx;
} }
.todo-switch-row,
.todo-deduct-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
color: #374151;
font-size: 26rpx;
font-weight: 600;
}
.todo-config-card {
margin-top: 16rpx;
padding: 20rpx;
border: 1rpx solid #dbe5f0;
border-radius: 14rpx;
background: #f7f9fc;
}
.todo-config-card .project-input {
margin-top: 16rpx;
}
.todo-input {
display: block;
width: 100%;
height: 76rpx;
box-sizing: border-box;
padding: 0 20rpx;
border: 1rpx solid #d1d5db;
border-radius: 12rpx;
background: #fff;
color: #111827;
font-size: 28rpx;
}
.todo-executor-picker {
width: 100%;
max-height: 72vh;
box-sizing: border-box;
padding: 24rpx;
border-radius: 24rpx 24rpx 0 0;
background: #fff;
}
.todo-executor-list {
max-height: 58vh;
}
.todo-team-group {
margin-top: 16rpx;
border: 1rpx solid #e5e7eb;
border-radius: 12rpx;
overflow: hidden;
}
.todo-team-title {
padding: 16rpx 20rpx;
background: #f3f6fa;
color: #374151;
font-size: 26rpx;
font-weight: 700;
}
.todo-executor-item {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 76rpx;
padding: 0 20rpx;
border-top: 1rpx solid #f0f2f5;
color: #1f2937;
font-size: 26rpx;
}
.todo-selected {
color: #0877f1;
font-size: 22rpx;
}
.todo-empty {
padding: 24rpx 20rpx;
color: #9ca3af;
font-size: 24rpx;
text-align: center;
}
.readonly-input { .readonly-input {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -239,6 +239,16 @@ async function completeTask() {
userId: doctorInfo.value.userid, userId: doctorInfo.value.userid,
}) })
if (res && res.success) { if (res && res.success) {
if (todo.value?.benefitDeductOnComplete && todo.value?.benefitRecordId) {
const deductRes = await api('completeBenefitTodo', {
corpId: account.value.corpId,
todoId: todo.value._id,
treatmentRecordId: todo.value.benefitRecordId,
executorUserId: doctorInfo.value.userid,
completedAt: Date.now(),
});
if (!deductRes?.success) return toast(deductRes?.message || '待办已完成,但权益自动核销失败');
}
await toast('操作成功'); await toast('操作成功');
uni.navigateBack(); uni.navigateBack();
} else { } else {
@ -310,4 +320,4 @@ useLoad(opts => {
.h-120 { .h-120 {
height: 240rpx; height: 240rpx;
} }
</style> </style>

View File

@ -94,6 +94,8 @@ const urlsConfig = {
// 健康档案相关接口 // 健康档案相关接口
addMedicalRecord: 'addMedicalRecord', addMedicalRecord: 'addMedicalRecord',
addTreatmentRecord: "addTreatmentRecord", addTreatmentRecord: "addTreatmentRecord",
updateTreatmentRecord: "updateTreatmentRecord",
completeBenefitTodo: "completeBenefitTodo",
getProjectPackageList: "getProjectPackageList", getProjectPackageList: "getProjectPackageList",
getTreatmentRecord: "getTreatmentRecord", getTreatmentRecord: "getTreatmentRecord",
getDeductRecord: "getDeductRecord", getDeductRecord: "getDeductRecord",
@ -145,7 +147,8 @@ const urlsConfig = {
customerTransferRecord: 'customerTransferRecord', customerTransferRecord: 'customerTransferRecord',
// sendConsultRejectedMessage: "sendConsultRejectedMessage" // sendConsultRejectedMessage: "sendConsultRejectedMessage"
getTeamTodos: 'getTeamTodos', getTeamTodos: 'getTeamTodos',
updateTaskTodo: 'updateTaskTodo' updateTaskTodo: 'updateTaskTodo',
createTodo: 'createTodo'
} }
} }

View File

@ -1,4 +1,5 @@
export const TODO_EVENT_TYPE_LABELS = { export const TODO_EVENT_TYPE_LABELS = {
benefitExecution: '权益执行',
followUpNoShow: '未到院回访', followUpNoShow: '未到院回访',
followUpNoDeal: '未成交回访', followUpNoDeal: '未成交回访',
followUp: '诊后回访', followUp: '诊后回访',