feat: confirm report delivery after sharing

This commit is contained in:
malei 2026-08-02 00:15:00 +08:00
parent e11ce1aaaa
commit d9dfb8b7f1
3 changed files with 92 additions and 2 deletions

View File

@ -100,6 +100,10 @@ export const cancelAppointment = (id) => put(`/appointment/status?id=${id}&statu
// 提交报告
export const createReport = (data) => post('/report/create', data)
/** 员工在真实发送完成后显式确认;复制链接、二维码和预览均不得自动调用。 */
export const confirmReportSent = (reportId, channel) =>
post('/report/confirm-sent', { reportId, channel })
/** 触发服务回顾短片生成异步。composeMode: preset | interleave身份由后端从上下文派生 */
export const postReportHighlightStart = (data) => post('/report/highlight/start', data)

View File

@ -6,6 +6,10 @@
<text class="rsm-close" @click="emitClose"></text>
</view>
<text class="rsm-tip">复制下方链接或扫码打开也可点选话术后一键复制整段发给微信好友</text>
<view class="rsm-send-note" :class="{ 'rsm-send-note--done': sent }">
<text v-if="sent">已确认发送门店工作台不会再提醒</text>
<text v-else>复制扫码或预览都不代表已经发送请在宠主实际收到后再确认</text>
</view>
<view class="rsm-block">
<text class="rsm-label">报告链接</text>
@ -50,6 +54,14 @@
<button class="rsm-btn rsm-btn--ghost" @click="emitClose">稍后</button>
<button class="rsm-btn rsm-btn--primary" @click="previewReport">预览报告</button>
</view>
<button
class="rsm-btn rsm-confirm"
:class="{ 'rsm-confirm--done': sent }"
:disabled="sent || confirming"
@click="confirmAlreadySent"
>
{{ sent ? '已确认发送' : (confirming ? '确认中…' : '确认已发送给宠主') }}
</button>
</view>
</view>
</template>
@ -62,6 +74,9 @@ import { buildReportH5FullUrl } from '../../utils/reportPublicUrl.js'
const props = defineProps({
show: { type: Boolean, default: false },
reportToken: { type: String, default: '' },
reportId: { type: [Number, String], default: '' },
sent: { type: Boolean, default: false },
confirming: { type: Boolean, default: false },
petName: { type: String, default: '' },
/** 门店名(话术变量) */
storeName: { type: String, default: '' },
@ -69,7 +84,7 @@ const props = defineProps({
serviceType: { type: String, default: '' }
})
const emit = defineEmits(['close', 'preview'])
const emit = defineEmits(['close', 'preview', 'confirm-sent'])
const copiedUrl = ref(false)
const copiedTplIdx = ref(-1)
@ -185,6 +200,19 @@ function onMask() {
function previewReport() {
emit('preview')
}
function confirmAlreadySent() {
if (props.sent || props.confirming || !props.reportId) return
const channels = ['wechat', 'qr', 'other']
uni.showActionSheet({
title: '选择实际发送方式',
itemList: ['微信消息', '当面扫码', '其他方式'],
success: ({ tapIndex }) => {
const channel = channels[Number(tapIndex)]
if (channel) emit('confirm-sent', channel)
}
})
}
</script>
<style scoped>
@ -234,6 +262,21 @@ function previewReport() {
line-height: 1.5;
margin-bottom: 14px;
}
.rsm-send-note {
font-size: 12px;
color: #92400e;
line-height: 1.5;
padding: 10px 12px;
margin: 0 0 14px;
border: 1px solid #fde68a;
border-radius: 10px;
background: #fffbeb;
}
.rsm-send-note--done {
color: #166534;
border-color: #bbf7d0;
background: #f0fdf4;
}
.rsm-block {
margin-bottom: 16px;
}
@ -338,6 +381,16 @@ function previewReport() {
background: var(--c-slate-100);
color: var(--c-slate-600);
}
.rsm-confirm {
width: 100%;
margin-top: 10px;
background: var(--c-brand-heading);
color: #fff;
}
.rsm-confirm--done {
background: #dcfce7;
color: #166534;
}
.rsm-btn[disabled] {
opacity: 0.45;
}

View File

@ -184,12 +184,16 @@
<ReportShareModal
:show="shareModalOpen"
:report-id="shareReportId"
:report-token="shareToken"
:sent="shareSent"
:confirming="confirmingSend"
:pet-name="sharePetName"
:store-name="shareStoreName"
:service-type="shareServiceType"
@close="onShareModalClose"
@preview="onSharePreview"
@confirm-sent="onConfirmSent"
/>
</view>
</template>
@ -197,7 +201,7 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { onPageScroll } from '@dcloudio/uni-app'
import { createReport, getServiceTypeList, imgUrl, BASE_URL } from '../../api/index.js'
import { createReport, confirmReportSent, getServiceTypeList, imgUrl, BASE_URL } from '../../api/index.js'
import AppIcon from '../../components/AppIcon.vue'
import ReportShareModal from '../../components/report/ReportShareModal.vue'
import { getUserSession, getStoreSession } from '../../utils/session.js'
@ -239,7 +243,10 @@ const serviceTypes = ref([])
const currentAppointmentId = ref(null)
const submitting = ref(false)
const shareModalOpen = ref(false)
const shareReportId = ref(null)
const shareToken = ref('')
const shareSent = ref(false)
const confirmingSend = ref(false)
const sharePetName = ref('')
const shareStoreName = ref('')
const shareServiceType = ref('')
@ -711,7 +718,9 @@ const submitReport = async () => {
submitting.value = false
if (res.code === 200) {
const token = res.data.reportToken
shareReportId.value = res.data.reportId
shareToken.value = token
shareSent.value = false
sharePetName.value = report.value.petName || ''
shareStoreName.value = storeInfo?.name || ''
shareServiceType.value = report.value.serviceType || ''
@ -727,7 +736,10 @@ const submitReport = async () => {
const onShareModalClose = () => {
shareModalOpen.value = false
shareReportId.value = null
shareToken.value = ''
shareSent.value = false
confirmingSend.value = false
sharePetName.value = ''
shareStoreName.value = ''
shareServiceType.value = ''
@ -736,7 +748,10 @@ const onShareModalClose = () => {
const onSharePreview = () => {
const t = shareToken.value
shareModalOpen.value = false
shareReportId.value = null
shareToken.value = ''
shareSent.value = false
confirmingSend.value = false
sharePetName.value = ''
shareStoreName.value = ''
shareServiceType.value = ''
@ -745,6 +760,24 @@ const onSharePreview = () => {
}
}
const onConfirmSent = async (channel) => {
if (!shareReportId.value || confirmingSend.value || shareSent.value) return
confirmingSend.value = true
try {
const res = await confirmReportSent(shareReportId.value, channel)
if (res.code !== 200) {
uni.showToast({ title: res.message || '确认失败,请重试', icon: 'none' })
return
}
shareSent.value = true
uni.showToast({ title: res.data?.alreadyConfirmed ? '此前已确认发送' : '已记录发送', icon: 'success' })
} catch (e) {
uni.showToast({ title: '确认失败,请检查网络', icon: 'none' })
} finally {
confirmingSend.value = false
}
}
const goBack = () => {
const pages = getCurrentPages()
if (pages.length > 1) {