ykt-wxapp/pages/work/todo/todo-detail.vue
2026-02-02 17:49:17 +08:00

313 lines
11 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 v-if="todo" pageClass="bg-white">
<view class="px-15 py-12 border-b">
<view class="flex items-center">
<view class="mr-5 text-lg text-dark font-semibold">{{ todo.eventTypeLabel }}</view>
<view class="bg-light-text-color px-10 py-3 leading-normal text-base rounded overflow-hidden"
:class="statusClassNames[todo.eventStatus] || 'text-gray'">
{{ todo.eventStatusLabel }}
</view>
</view>
<view v-if="!canEdit" class="mt-5 text-base text-gray break-all">
{{ todo.taskContent || "暂无任务内容" }}
</view>
</view>
<view class="px-15 py-12 border-b">
<view class="text-lg text-dark font-semibold">发送内容</view>
<view class="mt-5 text-base text-gray break-all">
{{ todo.sendContent }}
</view>
<view v-for="(file, idx) in todo.fileList" :key="idx" class="mt-5 text-base text-gray break-all">
附件{{ file.file.name }}
</view>
</view>
<template v-if="canEdit">
<picker mode="date" :value="formData.planDate" @change="changePlanDate($event)">
<view class="flex items-center py-10 px-15 border-b">
<view class="text-base text-gray">计划执行时间</view>
<view class="flex-grow"></view>
<view class="text-base text-dark">{{ formData.planDate }}</view>
<view class="flex-shrink-0">
<uni-icons type="right" size="16" color="#999999" />
</view>
</view>
</picker>
<picker mode="selector" :range="memberList" range-key="name" :value="formData.executorUserId"
@change="changeExecutorUserId($event)">
<view class="flex items-center py-10 px-15 border-b">
<view class="text-base text-gray">执行人</view>
<view class="flex-grow"></view>
<view class="text-base text-dark">{{ memberMap[formData.executorUserId] || '' }}</view>
<view class="flex-shrink-0">
<uni-icons type="right" size="16" color="#999999" />
</view>
</view>
</picker>
<picker mode="selector" :range="eventTypeList" range-key="label" :value="formData.eventType"
@change="changeEventType($event)">
<view class="flex items-center py-10 px-15 border-b">
<view class="text-base text-gray">回访类型</view>
<view class="flex-grow"></view>
<view class="text-base text-dark">{{ ToDoEventType[formData.eventType] || '' }}</view>
<view class="flex-shrink-0">
<uni-icons type="right" size="16" color="#999999" />
</view>
</view>
</picker>
<view class="px-15 py-12 border-b">
<view class="text-lg text-dark font-semibold">任务内容</view>
<view class="mt-5 p-10 border rounded">
<textarea :value="formData.taskContent" class="w-full h-120 text-base text-dark" @input="changeTaskContent($event)" />
</view>
</view>
</template>
<!-- v-else -->
<template v-else>
<view class="flex items-center justify-between py-10 px-15 border-b">
<view class="text-base text-gray">计划执行时间</view>
<view class="text-base text-dark">{{ todo.planDate }}</view>
</view>
<view class="flex items-center justify-between py-10 px-15 border-b">
<view class="text-base text-gray">执行人</view>
<view class="text-base text-dark">{{ memberMap[todo.executorUserId] || '' }}</view>
</view>
<view class="px-15 py-12 border-b">
<view class="text-lg text-dark font-semibold">回访结果</view>
<view class="mt-5 p-10 border rounded">
<textarea :value="formData.result" class="w-full h-120 text-base text-dark" @input="changeResult($event)" />
</view>
</view>
</template>
<view class="px-15 py-12 border-b">
<view class="text-base text-gray">创建人{{ todo.creatorUserId }}</view>
<view class="text-base text-gray">创建时间{{ todo.createTime }}</view>
<view v-if="todo.endTime" class="text-base text-gray">执行时间{{ todo.endTime }}</view>
</view>
<template #footer>
<button-footer v-if="canEdit" confirmText="保存" :showCancel="false" @confirm="save()" />
<button-footer v-else-if="editable" cancelText="取消任务" confirmText="设为完成" @cancel="cancelTask()"
@confirm="completeTask()" />
<button-footer v-else-if="canWriteResult" confirmText="保存" :showCancel="false" @confirm="saveResult()" />
</template>
</full-page>
</template>
<script setup>
import { computed, ref } from "vue";
import { storeToRefs } from "pinia";
import dayjs from "dayjs";
import { statusNames, ToDoEventType, statusClassNames } from '@/baseData';
import useGuard from "@/hooks/useGuard.js";
import useAccountStore from "@/store/account.js";
import api from "@/utils/api.js";
import { confirm, toast } from "@/utils/widget";
import buttonFooter from '@/components/button-footer.vue';
import fullPage from '@/components/full-page.vue';
const { useLoad } = useGuard();
const { doctorInfo, account } = storeToRefs(useAccountStore());
const todo = ref(null);
const team = ref(null);
const form = ref({});
const isEditTodo = ref(false);
const formData = computed(() => ({ ...(todo.value || {}), ...form.value }));
const teamManager = computed(() => {
const memberLeaderList = team.value && Array.isArray(team.value.memberLeaderList) ? team.value.memberLeaderList : [];
return doctorInfo.value && memberLeaderList.includes(doctorInfo.value.userid);
})
const hasPermission = computed(() => {
const userid = doctorInfo.value && doctorInfo.value.userid;
return userid && todo.value && (userid === todo.value.executorUserId || teamManager.value);
})
const memberList = computed(() => {
const memberList = team.value && Array.isArray(team.value.memberList) ? team.value.memberList : [];
return memberList.map(i => ({ name: i.anotherName, value: i.userid }))
})
const memberMap = computed(() => memberList.value.reduce((m, item) => {
m[item.value] = item.name;
return m
}, {}))
const canEdit = computed(() => isEditTodo.value && ['notStart', 'processing'].includes(todo.value?.eventStatus))
const canWriteResult = computed(() => {
return hasPermission.value && 'treated' === todo.value?.eventStatus;
})
const editable = computed(() => {
const statusRight = ['notStart', 'processing'].includes(todo.value?.eventStatus);
return hasPermission.value && statusRight;
})
const eventTypeList = computed(() => Object.keys(ToDoEventType).map((key) => ({ label: ToDoEventType[key], value: key })));
function getVisitPlanStatus(row) {
if (row.eventStatus === "untreated" && dayjs().isBefore(dayjs(row.plannedExecutionTime))) {
return "notStart";
} else if (row.eventStatus === "untreated" && dayjs().isAfter(dayjs(row.plannedExecutionTime)) && dayjs().isBefore(dayjs(row.expireTime))) {
return "processing";
} else if (row.eventStatus === "treated") {
return "treated";
} else if (row.eventStatus === "closed") {
return "cancelled";
} else if (row.eventStatus === "expired" || dayjs().isAfter(row.expireTime)) {
return "expired";
}
return "";
}
function changePlanDate(e) {
form.value.planDate = e.detail.value;
}
function changeExecutorUserId(e) {
const idx = e.detail.value;
const user = memberList[idx];
form.value.executorUserId = user.value;
}
function changeEventType(e) {
const idx = e.detail.value;
const type = eventTypeList[idx];
form.value.eventType = type.value;
}
function changeTaskContent(e) {
form.value.taskContent = e.detail.value;
}
function changeResult(e) {
form.value.result = e.detail.value;
}
async function save() {
if (!formData.value.eventType) {
return toast('请选择回访类型');
}
if (!formData.value.planDate) {
return toast("请选择执行时间");
}
if (!formData.value.executorUserId) {
return toast("请选择处理人");
}
const taskParams = {
eventType: formData.value.eventType,
planExecutionTime: dayjs(formData.value.planDate).valueOf(),
executorUserId: formData.value.executorUserId,
executorTeamId: formData.value.executorTeamId || undefined,
executeTeamName: formData.value.executeTeamName || undefined,
taskContent: formData.value.taskContent || undefined,
}
const params = {
id: todo.value._id,
task: taskParams,
corpId: account.value.corpId
}
const res = await api('updateTaskTodo', params);
if (res && res.success) {
await toast('保存成功');
uni.navigateBack();
} else {
toast(res?.message || '保存失败')
}
}
async function cancelTask() {
await confirm('确定取消该回访任务吗');
const res = await api('setTodoStatus', {
id: todo.value._id,
eventStatus: 'closed',
result: formData.value.result || '已取消',
userId: doctorInfo.value.userid,
})
if (res && res.success) {
await toast('取消成功');
uni.navigateBack();
} else {
toast(res?.message || '取消失败')
}
}
async function completeTask() {
await confirm('确定完成该回访任务吗');
const res = await api('setTodoStatus', {
id: todo.value._id,
eventStatus: 'treated',
result: formData.value.result || '已完成',
userId: doctorInfo.value.userid,
})
if (res && res.success) {
await toast('操作成功');
uni.navigateBack();
} else {
toast(res?.message || '操作失败')
}
}
async function saveResult() {
if (typeof formData.value.result !== 'string' || formData.value.result.trim() === '') {
return toast('请填写回访结果')
}
const res = await api('updateTaskTodoResult', {
id: todo.value._id,
result: formData.value.result.trim(),
})
if (res && res.success) {
await toast('操作成功');
uni.navigateBack();
} else {
toast(res?.message || '操作失败')
}
}
async function getDetail(id) {
const res = await api('getTodoById', { id, corpId: account.value.corpId });
if (res && res.success) {
const eventStatus = getVisitPlanStatus(res.data);
todo.value = {
...res.data,
eventTypeLabel: ToDoEventType[res.data.eventType],
planDate: res.data.plannedExecutionTime && dayjs(res.data.plannedExecutionTime).isValid() ? dayjs(res.data.plannedExecutionTime).format("YYYY-MM-DD") : "",
endTime: res.data.endTime && dayjs(res.data.endTime).isValid() ? dayjs(res.data.endTime).format("YYYY-MM-DD HH:mm") : "",
createTime: res.data.createTime && dayjs(res.data.createTime).isValid() ? dayjs(res.data.createTime).format("YYYY-MM-DD HH:mm") : "",
eventStatus,
eventStatusLabel: statusNames[eventStatus],
fileList: Array.isArray(res.data.fileList) ? res.data.fileList.filter(i => i && i.file && i.file.name) : []
};
if (todo.value.executeTeamId) {
getTeamDetail(todo.value.executeTeamId)
}
console.log(ToDoEventType, res.data.eventType, ToDoEventType[res.data.eventType])
} else {
toast(res?.message || '获取数据失败')
}
}
async function getTeamDetail(teamId) {
const res = await api('getTeamData', { teamId, corpId: account.value.corpId });
if (res && res.success) {
team.value = res.data;
} else {
await toast(res.message || '获取团队信息失败')
uni.navigateBack();
}
}
useLoad(opts => {
console.clear()
isEditTodo.value = opts.editMode === 'YES';
getDetail(opts.id);
})
</script>
<style>
.mt-5 {
margin-top: 10rpx;
}
.h-120 {
height: 240rpx;
}
</style>