Compare commits

..

13 Commits

Author SHA1 Message Date
wangdongbo
1040539640 no message 2026-09-04 09:23:27 +08:00
66ae382b55 权益增加总额统计 2026-09-03 17:58:02 +08:00
huxuejian
01ee09524b fix:问题修复 2026-09-03 17:35:08 +08:00
24ee4a48f5 bug修复 2026-09-02 14:50:21 +08:00
e50d222a02 bug修复last 2026-09-01 17:42:46 +08:00
8e42f147af 问题修复 2026-09-01 16:31:54 +08:00
0126068ae6 bug修复 2026-09-01 15:42:31 +08:00
huxuejian
3e387ad687 Merge branch 'dev-benefitUP' of http://175.27.226.205:3000/huxuejian/ykt-wxapp into dev-benefitUP 2026-09-01 15:33:32 +08:00
huxuejian
17b2a434cc fix:问题修复 2026-09-01 15:33:31 +08:00
c985379bc8 bug修复2 2026-09-01 15:21:46 +08:00
c33f9929d1 bug修复 2026-09-01 14:46:32 +08:00
1e0cd71432 权益生成待办功能完成 2026-08-27 14:47:13 +08:00
8daf6ce4e5 加入套餐改造并且加入权益详情 2026-08-27 14:10:42 +08:00
16 changed files with 1938 additions and 1528 deletions

View File

@ -20,7 +20,33 @@ export const ToDoEventType = {
followupAppointment: "复诊预约", followupAppointment: "复诊预约",
confirmArrival: "确认到院", confirmArrival: "确认到院",
prenatalFollowUp: "孕期回访", prenatalFollowUp: "孕期回访",
}; childHealthFollowUp: "儿童保健回访",
benefitExecution: "权益执行",
}
// {
// followUpNoShow: "未到院回访",
// followUpNoDeal: "未成交回访",
// followUp: "诊后回访",
// followUpPostSurgery: "术后回访",
// followUpPostTreatment: "治疗后回访",
// appointmentReminder: "就诊提醒",
// followUpReminder: "复诊提醒",
// medicationReminder: "用药提醒",
// serviceSummary: "咨询服务",
// eventNotification: "活动通知",
// ContentReminder: "宣教发送",
// questionnaire: "问卷调查",
// followUpComplaint: "投诉回访",
// followUpActivity: "活动回访",
// other: "其他",
// Feedback: "意见反馈",
// // 预约相关服务类型
// treatmentAppointment: "治疗预约",
// followupAppointment: "复诊预约",
// confirmArrival: "确认到院",
// prenatalFollowUp: "孕期回访",
// };
export const statusNames = { export const statusNames = {
notStart: "未开始", notStart: "未开始",

View File

@ -0,0 +1,48 @@
<template>
<text>{{ displayName }}</text>
</template>
<script setup>
import { computed, onMounted, reactive } from 'vue';
import api from '@/utils/api';
import useAccountStore from '@/store/account';
const props = defineProps({
openid: { type: String, default: '' },
fallback: { type: String, default: '' },
});
const SPECIAL_NAMES = { system: '系统', none: '咨询公共池', not: '无' };
const sharedUserNameMap = reactive({});
let sharedLoadingPromise = null;
const accountStore = useAccountStore();
const displayName = computed(() => {
const id = String(props.openid || '').trim();
if (!id) return props.fallback || '';
return SPECIAL_NAMES[id] || sharedUserNameMap[id] || props.fallback || id;
});
async function loadMembers() {
if (sharedLoadingPromise) return sharedLoadingPromise;
const team = uni.getStorageSync('ykt_case_current_team') || {};
const corpId = String(team.corpId || accountStore.doctorInfo?.corpId || accountStore.account?.corpId || '');
if (!corpId) return;
sharedLoadingPromise = api('getCorpMember', { corpId, page: 1, pageSize: 1000 }, false)
.then((res) => {
const list = res?.data?.data || res?.data?.list || res?.data || res?.list || [];
if (!Array.isArray(list)) return;
const map = {};
list.forEach((member) => {
const userid = String(member?.userid || member?.userId || member?.corpUserId || '').trim();
const name = String(member?.anotherName || member?.name || '').trim();
if (userid && name) map[userid] = name;
});
Object.assign(sharedUserNameMap, map);
})
.catch(() => {})
.finally(() => { sharedLoadingPromise = null; });
return sharedLoadingPromise;
}
onMounted(loadMembers);
</script>

View File

@ -118,6 +118,12 @@
:reachBottomTime="reachBottomTime" :reachBottomTime="reachBottomTime"
:floatingBottom="floatingBottom" :floatingBottom="floatingBottom"
/> />
<BenefitProjectTab
v-else-if="currentTab === 'benefitProject'"
:data="archive"
:archiveId="archiveId"
:floatingBottom="floatingBottom"
/>
<FollowUpManageTab <FollowUpManageTab
v-else v-else
:data="archive" :data="archive"
@ -232,6 +238,7 @@ import dayjs from 'dayjs';
import HealthProfileTab from '@/pages/case/components/archive-detail/health-profile-tab.vue'; import HealthProfileTab from '@/pages/case/components/archive-detail/health-profile-tab.vue';
import ServiceInfoTab from '@/pages/case/components/archive-detail/service-info-tab.vue'; import ServiceInfoTab from '@/pages/case/components/archive-detail/service-info-tab.vue';
import FollowUpManageTab from '@/pages/case/components/archive-detail/follow-up-manage-tab.vue'; import FollowUpManageTab from '@/pages/case/components/archive-detail/follow-up-manage-tab.vue';
import BenefitProjectTab from '@/pages/case/components/archive-detail/benefit-project-tab.vue';
import api from '@/utils/api'; import api from '@/utils/api';
import useAccountStore from '@/store/account'; import useAccountStore from '@/store/account';
import { hideLoading, loading, toast } from '@/utils/widget'; import { hideLoading, loading, toast } from '@/utils/widget';
@ -243,6 +250,7 @@ const NEED_RELOAD_STORAGE_KEY = 'ykt_case_need_reload';
const tabs = [ const tabs = [
{ key: 'visitRecord', title: '病历记录' }, { key: 'visitRecord', title: '病历记录' },
{ key: 'followUp', title: '回访任务' }, { key: 'followUp', title: '回访任务' },
{ key: 'benefitProject', title: '权益项目' },
{ key: 'serviceRecord', title: '服务记录' } { key: 'serviceRecord', title: '服务记录' }
]; ];
@ -562,7 +570,10 @@ onLoad((options) => {
fetchArchive(); fetchArchive();
}); });
onShow(() => { onShow(async () => {
//
await getDoctorInfo();
const cached = uni.getStorageSync(STORAGE_KEY); const cached = uni.getStorageSync(STORAGE_KEY);
const cachedId = cached && typeof cached === 'object' ? String(cached._id || cached.id || '') : ''; const cachedId = cached && typeof cached === 'object' ? String(cached._id || cached.id || '') : '';
if (cached && typeof cached === 'object' && (!cachedId || !archiveId.value || cachedId === archiveId.value)) { if (cached && typeof cached === 'object' && (!cachedId || !archiveId.value || cachedId === archiveId.value)) {

View File

@ -0,0 +1,452 @@
<template>
<view class="wrap">
<view class="switcher">
<view
v-for="item in recordTypes"
:key="item.value"
class="switch-item"
:class="{ active: currentType === item.value }"
@click="switchType(item.value)"
>
{{ item.label }}
</view>
</view>
<view v-if="list.length" class="list">
<view v-for="item in list" :key="item._id" class="record-card">
<view class="record-header">
<view class="record-title">{{ item.projectName || '未命名权益项目' }}</view>
<view v-if="currentType === 'treatment'" class="status" :class="item.statusClass">
{{ item.statusLabel }}
</view>
<view v-else class="status status-blue">已治疗</view>
</view>
<template v-if="currentType === 'treatment'">
<view class="summary-row">
<view class="summary-item">
<text class="summary-label">总数</text>
<text class="summary-value">{{ item.usageCount }}</text>
</view>
<view class="summary-item highlight">
<text class="summary-label">剩余数</text>
<text class="summary-value">{{ item.restUsageCount }}</text>
</view>
<view class="summary-item">
<text class="summary-label">有效期</text>
<text class="summary-value">{{ item.validTimeText }}</text>
</view>
</view>
<view class="detail-row">
<text class="detail-label">购买日期</text>
<text class="detail-value">{{ item.purchaseDate }}</text>
</view>
<view class="detail-row">
<text class="detail-label">治疗科室</text>
<text class="detail-value">{{ item.treatmentDeptName || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">录单人</text>
<view class="detail-value"><WwUser :openid="item.creator || item.billdCreator || item.billDoctorId" :fallback="item.creatorName || item.billdCreatorName || '--'" /></view>
</view>
</template>
<template v-else>
<view class="detail-row">
<text class="detail-label">治疗日期</text>
<text class="detail-value">{{ item.treatmentDate }}</text>
</view>
<view class="detail-row">
<text class="detail-label">治疗数</text>
<text class="detail-value strong">{{ item.deductUsageCount }}</text>
</view>
<view class="detail-row">
<text class="detail-label">治疗部位</text>
<text class="detail-value">{{ item.treatmentArea || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">治疗科室</text>
<text class="detail-value">{{ item.treatmentDeptName || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">治疗医生</text>
<view class="detail-value"><WwUser :openid="item.treatmentDoctorUserId || item.doctorUserId || item.doctorId || item.executorUserId" :fallback="item.doctorName || item.treatmentDoctorName || '--'" /></view>
</view>
<view v-if="item.treatmentRemark" class="remark">{{ item.treatmentRemark }}</view>
</template>
</view>
</view>
<view v-else-if="loading" class="state">加载中...</view>
<view v-else class="state">暂无{{ currentType === 'treatment' ? '权益记录' : '治疗明细' }}</view>
<uni-load-more
v-if="list.length"
:status="loadMoreStatus"
:contentText="loadMoreText"
@clickLoadMore="loadMore"
/>
<view
v-if="canBenefitEntry"
class="fab"
:style="{ bottom: `${floatingBottom}px` }"
@click="goToBenefitEntry"
>
<uni-icons type="plusempty" size="24" color="#fff" />
</view>
</view>
</template>
<script setup>
import { computed, onMounted, ref, watch } from 'vue';
import dayjs from 'dayjs';
import { storeToRefs } from 'pinia';
import api from '@/utils/api';
import useAccountStore from '@/store/account';
import { toast } from '@/utils/widget';
import WwUser from '@/components/ww-user/index.vue';
const props = defineProps({
data: { type: Object, default: () => ({}) },
archiveId: { type: String, default: '' },
floatingBottom: { type: Number, default: 16 },
});
const recordTypes = [
{ label: '权益记录', value: 'treatment' },
{ label: '治疗明细', value: 'deduct' },
];
const currentType = ref('treatment');
const list = ref([]);
const page = ref(1);
const pageSize = 10;
const total = ref(0);
const loading = ref(false);
const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore;
const canBenefitEntry = computed(() => doctorInfo.value?.benefitEntryEnabled === true);
const loadMoreStatus = computed(() => {
if (loading.value) return 'loading';
return list.value.length < total.value ? 'more' : 'no-more';
});
const loadMoreText = {
contentdown: '点击加载更多',
contentrefresh: '加载中...',
contentnomore: '没有更多了',
};
function getCorpId() {
const team = uni.getStorageSync('ykt_case_current_team') || {};
return String(team.corpId || doctorInfo.value?.corpId || account.value?.corpId || '');
}
async function ensureDoctor() {
if (doctorInfo.value || !account.value?.openid) return;
await getDoctorInfo();
}
function formatDate(value) {
if (!value) return '--';
const date = dayjs(value);
return date.isValid() ? date.format('YYYY-MM-DD') : '--';
}
function mapTreatment(row) {
const expired = row.validTime && dayjs(row.validTime).isBefore(dayjs(), 'day');
return {
...row,
_id: String(row._id || row.id || `${row.projectId}_${row.billTime}`),
usageCount: row.usageCount ?? 0,
restUsageCount: row.restUsageCount ?? 0,
validTimeText: !row.validTime ? '无限期' : `${formatDate(row.validTime)}${expired ? '(已过期)' : ''}`,
purchaseDate: formatDate(row.billTime || row.createTreatementTime),
statusLabel: expired ? '已过期' : row.restUsageCount > 0 ? '可使用' : '已用完',
statusClass: expired ? 'status-red' : row.restUsageCount > 0 ? 'status-green' : 'status-gray',
creatorName: row.creatorName || row.billdCreatorName || '',
};
}
function mapDeduct(row) {
return {
...row,
_id: String(row._id || row.id || `${row.projectId}_${row.treatmentTime}`),
treatmentDate: formatDate(row.treatmentTime || row.createTreatementTime),
deductUsageCount: row.deductUsageCount ?? 0,
doctorName: row.doctorName || row.treatmentDoctorName || '',
};
}
async function fetchList(reset = false) {
if (!props.archiveId || loading.value) return;
if (reset) {
page.value = 1;
total.value = 0;
list.value = [];
}
await ensureDoctor();
const corpId = getCorpId();
if (!corpId) return;
loading.value = true;
try {
const params = {
corpId,
customerId: String(props.archiveId),
page: page.value,
pageSize,
};
const res = currentType.value === 'treatment'
? await api('getTreatmentRecord', params, false)
: await api('getDeductRecord', { ...params, deductStatus: ['deducted'] }, false);
if (!res?.success) {
toast(res?.message || '获取权益数据失败');
return;
}
const payload = res?.data && typeof res.data === 'object' ? res.data : res;
const rows = Array.isArray(res?.list)
? res.list
: Array.isArray(payload?.list)
? payload.list
: Array.isArray(payload?.data)
? payload.data
: Array.isArray(payload)
? payload
: [];
total.value = Number(res?.total ?? payload?.total ?? rows.length);
const mapped = rows.map(currentType.value === 'treatment' ? mapTreatment : mapDeduct);
list.value = page.value === 1 ? mapped : [...list.value, ...mapped];
page.value += 1;
} catch (error) {
toast('获取权益数据失败');
} finally {
loading.value = false;
}
}
function switchType(type) {
if (currentType.value === type) return;
currentType.value = type;
fetchList(true);
}
function loadMore() {
if (list.value.length < total.value) fetchList();
}
function goToBenefitEntry() {
if (!canBenefitEntry.value || !props.archiveId) return;
const team = uni.getStorageSync('ykt_case_current_team') || {};
const rawGroupId = String(props.data?.chatGroupId || props.data?.groupId || '');
const groupId = rawGroupId.startsWith('GROUP') ? rawGroupId.slice(5) : rawGroupId;
const teamId = String(team.teamId || team._id || team.id || '');
const teamName = String(team.name || team.teamName || '');
const corpId = getCorpId();
const patientName = String(props.data?.name || '');
const query = [
`groupId=${encodeURIComponent(groupId)}`,
`patientId=${encodeURIComponent(props.archiveId)}`,
`patientName=${encodeURIComponent(patientName)}`,
`teamId=${encodeURIComponent(teamId)}`,
`teamName=${encodeURIComponent(teamName)}`,
`corpId=${encodeURIComponent(corpId)}`,
].join('&');
uni.navigateTo({ url: `/pages/message/benefit-entry?${query}` });
}
onMounted(() => fetchList(true));
watch(() => props.archiveId, (value, oldValue) => {
if (value && value !== oldValue) fetchList(true);
});
</script>
<style scoped lang="scss">
.wrap {
padding: 20rpx 28rpx 180rpx;
}
.switcher {
display: flex;
padding: 6rpx;
background: #eef4ff;
border-radius: 14rpx;
}
.switch-item {
flex: 1;
height: 68rpx;
line-height: 68rpx;
text-align: center;
color: #5d6878;
font-size: 26rpx;
border-radius: 10rpx;
}
.switch-item.active {
color: #0877f1;
background: #fff;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(8, 119, 241, 0.1);
}
.list {
margin-top: 20rpx;
}
.record-card {
margin-bottom: 20rpx;
padding: 24rpx;
background: #fff;
border-radius: 18rpx;
box-shadow: 0 10rpx 26rpx rgba(31, 41, 55, 0.06);
}
.record-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #f1f3f5;
}
.record-title {
flex: 1;
min-width: 0;
color: #1f2937;
font-size: 30rpx;
font-weight: 600;
line-height: 42rpx;
}
.status {
flex-shrink: 0;
padding: 8rpx 14rpx;
border-radius: 999rpx;
font-size: 22rpx;
}
.status-green {
color: #16834b;
background: #e8f8ef;
}
.status-red {
color: #d14343;
background: #fff0f0;
}
.status-gray {
color: #6b7280;
background: #f1f3f5;
}
.status-blue {
color: #0877f1;
background: #eaf3ff;
}
.summary-row {
display: flex;
margin: 20rpx 0;
padding: 18rpx 0;
background: #f8fafc;
border-radius: 12rpx;
}
.summary-item {
flex: 1;
min-width: 0;
text-align: center;
border-right: 2rpx solid #e7ebf0;
}
.summary-item:last-child {
border-right: 0;
}
.summary-label,
.summary-value {
display: block;
}
.summary-label {
color: #8a94a3;
font-size: 22rpx;
}
.summary-value {
margin-top: 8rpx;
color: #27364a;
font-size: 28rpx;
font-weight: 600;
}
.summary-item.highlight .summary-value {
color: #0877f1;
}
.detail-row {
display: flex;
padding-top: 16rpx;
font-size: 25rpx;
line-height: 36rpx;
}
.detail-label {
width: 136rpx;
flex-shrink: 0;
color: #8a94a3;
}
.detail-value {
flex: 1;
min-width: 0;
color: #3b4654;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.detail-value.strong {
color: #0877f1;
font-weight: 600;
}
.remark {
margin-top: 18rpx;
padding: 16rpx;
color: #687385;
background: #f8fafc;
border-radius: 10rpx;
font-size: 24rpx;
line-height: 34rpx;
word-break: break-all;
}
.state {
padding: 240rpx 0;
color: #9aa0a6;
text-align: center;
font-size: 26rpx;
}
.fab {
position: fixed;
right: 32rpx;
width: 104rpx;
height: 104rpx;
border-radius: 52rpx;
background: #0877f1;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20rpx 36rpx rgba(8, 119, 241, 0.3);
z-index: 9999;
}
</style>

View File

@ -1599,8 +1599,8 @@ watch(
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35); box-shadow: 0 10px 18px rgba(79, 110, 247, 0.35);
z-index: 20; z-index: 1000;
} }
.popup { .popup {

View File

@ -915,7 +915,7 @@ watch(
right: 32rpx; right: 32rpx;
width: 104rpx; width: 104rpx;
height: 104rpx; height: 104rpx;
z-index: 20; z-index: 1000;
} }
.fab { .fab {

View File

@ -885,7 +885,7 @@ watch(
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35); box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35);
z-index: 20; z-index: 1000;
} }
.popup { .popup {

View File

@ -47,13 +47,8 @@
<view class="section mt-20"> <view class="section mt-20">
<view class="section-title">回访结果</view> <view class="section-title">回访结果</view>
<view class="result-box"> <view class="result-box">
<textarea <textarea v-model="form.result" class="result-textarea" placeholder="请填写回访结果" maxlength="500"
v-model="form.result" :disabled="!(editable || canEditResult)" />
class="result-textarea"
placeholder="请填写回访结果"
maxlength="500"
:disabled="!(editable || canEditResult)"
/>
</view> </view>
</view> </view>
@ -191,8 +186,9 @@ const mobiles = computed(() => {
if (Array.isArray(cached.mobiles)) arr.push(...cached.mobiles.map(String)); if (Array.isArray(cached.mobiles)) arr.push(...cached.mobiles.map(String));
} }
const cleaned = arr.map((i) => String(i || '').trim()).filter(Boolean); const cleaned = arr.map((i) => String(i || '').trim()).filter(Boolean);
if (!cleaned.includes('13800000000')) cleaned.push('13800000000'); const { mobile, phone1, phone2, phone3 } = customer.value || {};
return Array.from(new Set(cleaned)); cleaned.push(mobile, phone1, phone2, phone3)
return Array.from(new Set(cleaned.filter(i => typeof i === 'string' && i.trim())));
}); });
function getUserId() { function getUserId() {
@ -319,7 +315,13 @@ async function getTodo() {
return; return;
} }
todo.value = res.data && typeof res.data === 'object' ? res.data : null; todo.value = res.data && typeof res.data === 'object' ? res.data : null;
if (todo.value.customerId && !customer.value) {
getCustomer(todo.value.customerId)
}
await loadUserNameMap(String(todo.value?.executeTeamId || '')); await loadUserNameMap(String(todo.value?.executeTeamId || ''));
if (todo.value && todo.value.creatorUserId && !userNameMap.value[todo.value.creatorUserId]) {
getCorpMember(todo.value.creatorUserId)
}
const parsed = parseTodoMethod(todo.value?.todoMethod); const parsed = parseTodoMethod(todo.value?.todoMethod);
if (parsed.todoMethod) { if (parsed.todoMethod) {
@ -335,6 +337,14 @@ async function getTodo() {
form.result = String(todo.value?.result || '') || ''; form.result = String(todo.value?.result || '') || '';
} }
async function getCustomer(_id) {
const res = await api('getMemberInfo', { params: { _id } })
const [data] = res && Array.isArray(res.data) ? res.data : [];
if (data) {
customer.value = data
}
}
function toggleMethod(v) { function toggleMethod(v) {
if (!editable.value) return; if (!editable.value) return;
todoMethod.value = v; todoMethod.value = v;
@ -368,6 +378,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);
@ -410,6 +430,14 @@ async function save() {
setTimeout(() => uni.navigateBack(), 300); setTimeout(() => uni.navigateBack(), 300);
} }
async function getCorpMember(userId) {
const res = await api('getCorpMemberByUserId', { userId });
const [data] = res && Array.isArray(res.data) ? res.data : [];
if (data && data.userid && data.anotherName) {
userNameMap.value[data.userid] = data.anotherName
}
}
function remove() { function remove() {
if (!canRemove.value) return; if (!canRemove.value) return;
uni.showModal({ uni.showModal({
@ -435,13 +463,16 @@ function remove() {
height: 100vh; height: 100vh;
background: #f6f7f8; background: #f6f7f8;
} }
.scroll { .scroll {
height: 100vh; height: 100vh;
} }
.section { .section {
background: #fff; background: #fff;
padding: 24rpx 30rpx; padding: 24rpx 30rpx;
} }
.border-top { .border-top {
border-top: 1px solid #e5e7eb; border-top: 1px solid #e5e7eb;
} }
@ -451,11 +482,13 @@ function remove() {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.head-title { .head-title {
font-size: 34rpx; font-size: 34rpx;
font-weight: 600; font-weight: 600;
color: #111827; color: #111827;
} }
.head-tag { .head-tag {
flex-shrink: 0; flex-shrink: 0;
font-size: 26rpx; font-size: 26rpx;
@ -464,23 +497,28 @@ function remove() {
background: #f3f4f6; background: #f3f4f6;
color: #6b7280; color: #6b7280;
} }
.tag-processing { .tag-processing {
background: #fee2e2; background: #fee2e2;
color: #ef4444; color: #ef4444;
} }
.tag-notStart { .tag-notStart {
background: #dbeafe; background: #dbeafe;
color: #2563eb; color: #2563eb;
} }
.tag-treated { .tag-treated {
background: #dcfce7; background: #dcfce7;
color: #16a34a; color: #16a34a;
} }
.tag-cancelled, .tag-cancelled,
.tag-expired { .tag-expired {
background: #f3f4f6; background: #f3f4f6;
color: #6b7280; color: #6b7280;
} }
.head-content { .head-content {
margin-top: 24rpx; margin-top: 24rpx;
font-size: 30rpx; font-size: 30rpx;
@ -492,21 +530,25 @@ function remove() {
font-size: 30rpx; font-size: 30rpx;
color: #6b7280; color: #6b7280;
} }
.sub-content { .sub-content {
margin-top: 24rpx; margin-top: 24rpx;
font-size: 30rpx; font-size: 30rpx;
color: #6b7280; color: #6b7280;
line-height: 44rpx; line-height: 44rpx;
} }
.file-line { .file-line {
margin-top: 12rpx; margin-top: 12rpx;
font-size: 30rpx; font-size: 30rpx;
line-height: 40rpx; line-height: 40rpx;
} }
.file-type { .file-type {
color: #111827; color: #111827;
margin-right: 12rpx; margin-right: 12rpx;
} }
.file-name { .file-name {
color: #2563eb; color: #2563eb;
} }
@ -516,21 +558,25 @@ function remove() {
font-weight: 600; font-weight: 600;
color: #111827; color: #111827;
} }
.method-row { .method-row {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 24rpx; margin-top: 24rpx;
} }
.radio { .radio {
width: 32rpx; width: 32rpx;
height: 32rpx; height: 32rpx;
margin-right: 20rpx; margin-right: 20rpx;
} }
.method-label { .method-label {
font-size: 30rpx; font-size: 30rpx;
color: #111827; color: #111827;
margin-right: 20rpx; margin-right: 20rpx;
} }
.method-select { .method-select {
margin-left: auto; margin-left: auto;
width: 400rpx; width: 400rpx;
@ -542,6 +588,7 @@ function remove() {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.method-value { .method-value {
font-size: 30rpx; font-size: 30rpx;
color: #111827; color: #111827;
@ -550,6 +597,7 @@ function remove() {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.method-value.muted { .method-value.muted {
color: #9ca3af; color: #9ca3af;
} }
@ -559,10 +607,12 @@ function remove() {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
} }
.kv-left { .kv-left {
font-size: 30rpx; font-size: 30rpx;
color: #6b7280; color: #6b7280;
} }
.kv-right { .kv-right {
font-size: 30rpx; font-size: 30rpx;
color: #111827; color: #111827;
@ -572,6 +622,7 @@ function remove() {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.kv-right.link { .kv-right.link {
color: #2563eb; color: #2563eb;
text-decoration: underline; text-decoration: underline;
@ -580,12 +631,14 @@ function remove() {
.mt-20 { .mt-20 {
margin-top: 20rpx; margin-top: 20rpx;
} }
.result-box { .result-box {
margin-top: 24rpx; margin-top: 24rpx;
padding: 20rpx; padding: 20rpx;
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
border-radius: 16rpx; border-radius: 16rpx;
} }
.result-textarea { .result-textarea {
width: 100%; width: 100%;
min-height: 160rpx; min-height: 160rpx;
@ -594,19 +647,24 @@ function remove() {
color: #111827; color: #111827;
box-sizing: border-box; box-sizing: border-box;
} }
.meta { .meta {
font-size: 30rpx; font-size: 30rpx;
} }
.meta-row { .meta-row {
margin-top: 24rpx; margin-top: 24rpx;
} }
.meta-row:first-child { .meta-row:first-child {
margin-top: 0; margin-top: 0;
} }
.meta-key { .meta-key {
color: #6b7280; color: #6b7280;
margin-right: 12rpx; margin-right: 12rpx;
} }
.meta-val { .meta-val {
color: #6b7280; color: #6b7280;
} }
@ -623,6 +681,7 @@ function remove() {
padding: 30rpx; padding: 30rpx;
box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06); box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
} }
.footer-btn { .footer-btn {
flex: 0 0 auto; flex: 0 0 auto;
min-width: 260rpx; min-width: 260rpx;
@ -631,18 +690,22 @@ function remove() {
border-radius: 16rpx; border-radius: 16rpx;
font-size: 30rpx; font-size: 30rpx;
} }
.footer-btn::after { .footer-btn::after {
border: none; border: none;
} }
.footer-btn.plain { .footer-btn.plain {
background: #fff; background: #fff;
color: #2563eb; color: #2563eb;
border: 1px solid #2563eb; border: 1px solid #2563eb;
} }
.footer-btn.plain.danger { .footer-btn.plain.danger {
color: #ef4444; color: #ef4444;
border-color: #ef4444; border-color: #ef4444;
} }
.footer-btn.primary { .footer-btn.primary {
background: #2563eb; background: #2563eb;
color: #fff; color: #fff;

View File

@ -124,7 +124,6 @@ const eventTypeLabel = computed(() => getTodoEventTypeLabel(form.eventType));
const mobiles = computed(() => { const mobiles = computed(() => {
const arr = []; const arr = [];
if (archiveMobile.value) arr.push(String(archiveMobile.value)); if (archiveMobile.value) arr.push(String(archiveMobile.value));
if (!arr.includes('13800000000')) arr.push('13800000000');
return arr; return arr;
}); });

View File

@ -158,9 +158,10 @@ const __NEW_FOLLOWUP_EVENT_TYPE_VALUES__ = [
'followUpNoShow', 'followUpNoShow',
'other', 'other',
]; ];
const eventTypeList = __NEW_FOLLOWUP_EVENT_TYPE_VALUES__ // const eventTypeList = __NEW_FOLLOWUP_EVENT_TYPE_VALUES__
.map((v) => __ALL_TODO_EVENT_TYPE_OPTIONS__.find((i) => i.value === v)) // .map((v) => __ALL_TODO_EVENT_TYPE_OPTIONS__.find((i) => i.value === v))
.filter(Boolean); // .filter(Boolean);
const eventTypeList = __ALL_TODO_EVENT_TYPE_OPTIONS__;
const accountStore = useAccountStore(); const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore); const { account, doctorInfo } = storeToRefs(accountStore);

File diff suppressed because it is too large Load Diff

View File

@ -90,6 +90,7 @@ const props = defineProps({
corpId: { type: String, default: "" }, corpId: { type: String, default: "" },
orderStatus: { type: String, default: "" }, orderStatus: { type: String, default: "" },
isGenerating: { type: Boolean, default: false }, isGenerating: { type: Boolean, default: false },
canBenefitEntry: { type: Boolean, default: false },
}); });
// Emits // Emits
@ -543,11 +544,6 @@ const morePanelButtons = computed(() => {
icon: "/static/icon/xuanjiaowenzhang.png", icon: "/static/icon/xuanjiaowenzhang.png",
action: goToArticleList, action: goToArticleList,
}, },
{
text: "权益录入",
icon: "/static/icon/quanyi.svg",
action: goToBenefitEntry,
},
{ {
text: "问卷", text: "问卷",
icon: "/static/icon/wenjuan.png", icon: "/static/icon/wenjuan.png",
@ -555,6 +551,14 @@ const morePanelButtons = computed(() => {
}, },
]; ];
if (props.canBenefitEntry) {
buttons.splice(4, 0, {
text: "权益录入",
icon: "/static/icon/quanyi.svg",
action: goToBenefitEntry,
});
}
// //
if (props.orderStatus === "finished") { if (props.orderStatus === "finished") {
// "" // ""

File diff suppressed because it is too large Load Diff

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 {

View File

@ -12,6 +12,7 @@ const urlsConfig = {
getTeamData: 'getTeamData', getTeamData: 'getTeamData',
getTeamById: 'getTeamById', getTeamById: 'getTeamById',
getTeamBymember: 'getTeamBymember', getTeamBymember: 'getTeamBymember',
getAllTeamByCorp: 'getAllTeamByCorp',
getCurrentTemplate: 'getCurrentTemplate', getCurrentTemplate: 'getCurrentTemplate',
getTemplateGroup: 'getTemplateGroup', getTemplateGroup: 'getTemplateGroup',
getTemplateListByTemptype: 'getTemplateListByTemptype', getTemplateListByTemptype: 'getTemplateListByTemptype',
@ -39,6 +40,7 @@ const urlsConfig = {
getHlwHospitalList: 'getHlwHospitalList', getHlwHospitalList: 'getHlwHospitalList',
updateAutoAcceptConsult: 'updateAutoAcceptConsult', updateAutoAcceptConsult: 'updateAutoAcceptConsult',
getAutoAcceptConsult: 'getAutoAcceptConsult', getAutoAcceptConsult: 'getAutoAcceptConsult',
getCorpMemberByUserId: 'getCorpMemberByUserId'
}, },
knowledgeBase: { knowledgeBase: {
@ -101,10 +103,16 @@ const urlsConfig = {
// 健康档案相关接口 // 健康档案相关接口
addMedicalRecord: 'addMedicalRecord', addMedicalRecord: 'addMedicalRecord',
addTreatmentRecord: "addTreatmentRecord", addTreatmentRecord: "addTreatmentRecord",
updateTreatmentRecord: "updateTreatmentRecord",
completeBenefitTodo: "completeBenefitTodo",
getProjectPackageList: "getProjectPackageList",
getTreatmentRecord: "getTreatmentRecord",
getDeductRecord: "getDeductRecord",
getMedicalRecordById: 'getMedicalRecordById', getMedicalRecordById: 'getMedicalRecordById',
updateMedicalRecord: 'updateMedicalRecord', updateMedicalRecord: 'updateMedicalRecord',
removeMedicalRecord: 'removeMedicalRecord', removeMedicalRecord: 'removeMedicalRecord',
getCustomerMedicalRecord: 'getCustomerMedicalRecord', getCustomerMedicalRecord: 'getCustomerMedicalRecord',
getMemberInfo: "getMemberInfo",
// 客户阶段 // 客户阶段
getCustomerType: 'getCustomerType', getCustomerType: 'getCustomerType',
}, },
@ -151,7 +159,8 @@ const urlsConfig = {
customerTransferRecord: 'customerTransferRecord', customerTransferRecord: 'customerTransferRecord',
// sendConsultRejectedMessage: "sendConsultRejectedMessage" // sendConsultRejectedMessage: "sendConsultRejectedMessage"
getTeamTodos: 'getTeamTodos', getTeamTodos: 'getTeamTodos',
updateTaskTodo: 'updateTaskTodo' updateTaskTodo: 'updateTaskTodo',
createTodo: 'createTodo'
} }
} }

View File

@ -1,20 +1,46 @@
export const TODO_EVENT_TYPE_LABELS = { export const TODO_EVENT_TYPE_LABELS = {
followUpNoShow: '未到院回访', followUpNoShow: "未到院回访",
followUpNoDeal: '未成交回访', followUpNoDeal: "未成交回访",
followUp: '诊后回访', followUp: "诊后回访",
followUpPostSurgery: '术后回访', followUpPostSurgery: "术后回访",
followUpPostTreatment: '治疗后回访', followUpPostTreatment: "治疗后回访",
appointmentReminder: '就诊提醒', appointmentReminder: "就诊提醒",
followUpReminder: '复诊提醒', followUpReminder: "复诊提醒",
medicationReminder: '用药提醒', medicationReminder: "用药提醒",
serviceSummary: '咨询服务', serviceSummary: "咨询服务",
eventNotification: '活动通知', eventNotification: "活动通知",
ContentReminder: '宣教发送', ContentReminder: "宣教发送",
questionnaire: '问卷调查', questionnaire: "问卷调查",
followUpComplaint: '投诉回访', followUpComplaint: "投诉回访",
followUpActivity: '活动回访', followUpActivity: "活动回访",
other: '其他', other: "其他",
}; Feedback: "意见反馈",
// 预约相关服务类型
treatmentAppointment: "治疗预约",
followupAppointment: "复诊预约",
confirmArrival: "确认到院",
prenatalFollowUp: "孕期回访",
childHealthFollowUp: "儿童保健回访",
benefitExecution: "权益执行",
}
// {
// benefitExecution: '权益执行',
// followUpNoShow: '未到院回访',
// followUpNoDeal: '未成交回访',
// followUp: '诊后回访',
// followUpPostSurgery: '术后回访',
// followUpPostTreatment: '治疗后回访',
// appointmentReminder: '就诊提醒',
// followUpReminder: '复诊提醒',
// medicationReminder: '用药提醒',
// serviceSummary: '咨询服务',
// eventNotification: '活动通知',
// ContentReminder: '宣教发送',
// questionnaire: '问卷调查',
// followUpComplaint: '投诉回访',
// followUpActivity: '活动回访',
// other: '其他',
// };
export function getTodoEventTypeLabel(value) { export function getTodoEventTypeLabel(value) {
const v = String(value || ''); const v = String(value || '');