ykt-wxapp/pages/case/new-followup-record.vue

345 lines
9.2 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>
<!-- Mobile 来源: ykt-management-mobile/src/pages/customer/new-followup-record/new-followup-record.vue简化移植去除 pinia/接口 -->
<view class="page">
<view class="card">
<picker mode="date" :value="form.plannedExecutionTime" @change="changeDate">
<view class="row clickable">
<view class="label">回访日期</view>
<view class="right">
<view class="value" :class="{ muted: !form.plannedExecutionTime }">{{ form.plannedExecutionTime || '请选择回访日期' }}</view>
<uni-icons type="arrowright" size="16" color="#999" />
</view>
</view>
</picker>
<view class="block">
<view class="block-title">回访方式</view>
<view class="method-row">
<view class="method" @click="selectMethod('phone')">
<image class="radio" :src="`/static/circle${form.todoMethod === 'phone' ? 'd' : ''}.svg`" />
<view class="m-label">电话</view>
<view v-if="form.todoMethod === 'phone'" class="m-value" :class="{ muted: !form.phoneNumber }">{{ form.phoneNumber || '请选择电话号码' }}</view>
<uni-icons v-if="form.todoMethod === 'phone'" type="arrowright" size="16" color="#999" />
</view>
<view class="method" @click="selectMethod('wechat')">
<image class="radio" :src="`/static/circle${form.todoMethod === 'wechat' ? 'd' : ''}.svg`" />
<view class="m-label">微信</view>
</view>
</view>
</view>
<view class="row clickable" @click="selectType">
<view class="label">回访类型</view>
<view class="right">
<view class="value" :class="{ muted: !form.eventType }">{{ eventTypeLabel || '请选择回访类型' }}</view>
<uni-icons type="arrowright" size="16" color="#999" />
</view>
</view>
<view class="row clickable" @click="selectTeam">
<view class="label">所在团队</view>
<view class="right">
<view class="value" :class="{ muted: !form.teamId }">{{ form.teamName || '请选择团队' }}</view>
<uni-icons type="arrowright" size="16" color="#999" />
</view>
</view>
<view class="block">
<view class="block-title">回访结果</view>
<view class="textarea-box">
<textarea v-model="form.result" class="textarea tall" placeholder="请输入回访结果" maxlength="500" />
<view class="counter">{{ (form.result || '').length }}/500</view>
</view>
</view>
</view>
<view class="footer">
<button class="btn plain" @click="cancel">取消</button>
<button class="btn primary" @click="save">保存</button>
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import { ensureSeed, upsertFollowup } from '@/components/archive-detail/mock';
const archiveId = ref('');
const archiveName = ref('');
const archiveMobile = ref('');
const form = reactive({
plannedExecutionTime: '',
todoMethod: '',
phoneNumber: '',
eventType: '',
teamId: '',
teamName: '',
result: '',
});
const eventTypeList = [
{ label: '回访', value: 'followup' },
{ label: '复诊提醒', value: 'revisit' },
{ label: '问卷', value: 'questionnaire' },
{ label: '其他', value: 'other' },
];
const eventTypeLabel = computed(() => eventTypeList.find((i) => i.value === form.eventType)?.label || '');
const teamOptions = [
{ label: '口腔一科(示例)', value: 'team_1' },
{ label: '正畸团队(示例)', value: 'team_2' },
];
const mobiles = computed(() => {
const arr = [];
if (archiveMobile.value) arr.push(String(archiveMobile.value));
if (!arr.includes('13800000000')) arr.push('13800000000');
return arr;
});
onLoad((options) => {
archiveId.value = options?.archiveId ? String(options.archiveId) : '';
const c = uni.getStorageSync('new-followup-record-customer');
if (c && typeof c === 'object') {
archiveId.value = archiveId.value || String(c._id || '');
archiveName.value = String(c.name || '');
}
const cached = uni.getStorageSync('ykt_case_archive_detail');
if (cached && typeof cached === 'object') archiveMobile.value = String(cached.mobile || '');
if (!archiveId.value) {
uni.showToast({ title: '缺少 archiveId', icon: 'none' });
setTimeout(() => uni.navigateBack(), 300);
return;
}
ensureSeed(archiveId.value, { name: archiveName.value });
form.plannedExecutionTime = dayjs().format('YYYY-MM-DD');
});
function changeDate(e) {
form.plannedExecutionTime = e.detail.value || '';
}
function selectMethod(method) {
if (method === 'phone') {
if (mobiles.value.length === 0) {
uni.showToast({ title: '暂无可用电话号码', icon: 'none' });
return;
}
uni.showActionSheet({
itemList: mobiles.value,
success: ({ tapIndex }) => {
form.todoMethod = 'phone';
form.phoneNumber = mobiles.value[tapIndex] || '';
},
});
} else {
form.todoMethod = 'wechat';
form.phoneNumber = '';
}
}
function selectType() {
uni.showActionSheet({
itemList: eventTypeList.map((i) => i.label),
success: ({ tapIndex }) => {
form.eventType = eventTypeList[tapIndex]?.value || '';
},
});
}
function selectTeam() {
uni.showActionSheet({
itemList: teamOptions.map((i) => i.label),
success: ({ tapIndex }) => {
const t = teamOptions[tapIndex];
form.teamId = t.value;
form.teamName = t.label;
},
});
}
function cancel() {
uni.showModal({
title: '确认取消',
content: '确定要取消新建回访记录吗?',
success: (res) => res.confirm && uni.navigateBack(),
});
}
function save() {
if (!form.plannedExecutionTime) return uni.showToast({ title: '请选择回访日期', icon: 'none' });
if (!form.todoMethod) return uni.showToast({ title: '请选择回访方式', icon: 'none' });
if (!form.eventType) return uni.showToast({ title: '请选择回访类型', icon: 'none' });
if (!form.teamId) return uni.showToast({ title: '请选择所在团队', icon: 'none' });
if (!String(form.result || '').trim()) return uni.showToast({ title: '请输入回访结果', icon: 'none' });
const phoneValue = form.phoneNumber ? `phone:${form.phoneNumber}` : 'phone';
const plannedExecutionTime = dayjs(form.plannedExecutionTime).valueOf();
upsertFollowup({
archiveId: archiveId.value,
followup: {
plannedExecutionTime,
endTime: plannedExecutionTime,
status: 'treated',
eventStatusLabel: '已完成',
eventType: form.eventType,
eventTypeLabel: eventTypeLabel.value || '回访',
executeTeamId: form.teamId,
executeTeamName: form.teamName,
executorName: '我',
creatorName: '我',
taskContent: '',
result: form.result,
todoMethod: form.todoMethod === 'phone' ? phoneValue : form.todoMethod,
createTime: Date.now(),
},
});
uni.$emit('archive-detail:followup-changed');
uni.showToast({ title: '保存成功', icon: 'success' });
setTimeout(() => uni.navigateBack(), 300);
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f6f8;
padding-bottom: calc(76px + env(safe-area-inset-bottom));
}
.card {
background: #fff;
margin: 10px 14px 0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
}
.row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 14px;
border-bottom: 1px solid #f2f2f2;
}
.row.clickable:active {
background: #fafafa;
}
.label {
font-size: 14px;
color: #666;
}
.right {
display: flex;
align-items: center;
gap: 10px;
}
.value {
font-size: 14px;
color: #333;
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.value.muted {
color: #999;
}
.block {
padding: 14px 14px;
border-bottom: 1px solid #f2f2f2;
}
.block:last-child {
border-bottom: none;
}
.block-title {
font-size: 14px;
color: #666;
margin-bottom: 10px;
}
.method-row {
display: flex;
align-items: center;
gap: 18px;
}
.method {
display: flex;
align-items: center;
gap: 10px;
}
.radio {
width: 16px;
height: 16px;
}
.m-label {
font-size: 14px;
color: #333;
}
.m-value {
font-size: 14px;
color: #333;
max-width: 140px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.m-value.muted {
color: #999;
}
.textarea-box {
border: 1px solid #e6e6e6;
border-radius: 8px;
padding: 10px;
}
.textarea {
width: 100%;
min-height: 140px;
font-size: 14px;
box-sizing: border-box;
}
.textarea.tall {
min-height: 160px;
}
.counter {
margin-top: 6px;
text-align: right;
font-size: 12px;
color: #999;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
display: flex;
gap: 12px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
}
.btn {
flex: 1;
height: 44px;
line-height: 44px;
border-radius: 6px;
font-size: 15px;
}
.btn::after {
border: none;
}
.btn.plain {
background: #fff;
color: #4f6ef7;
border: 1px solid #4f6ef7;
}
.btn.primary {
background: #4f6ef7;
color: #fff;
}
</style>