feat: confirm report delivery after sharing
This commit is contained in:
parent
e11ce1aaaa
commit
d9dfb8b7f1
@ -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)
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user