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 createReport = (data) => post('/report/create', data)
|
||||||
|
|
||||||
|
/** 员工在真实发送完成后显式确认;复制链接、二维码和预览均不得自动调用。 */
|
||||||
|
export const confirmReportSent = (reportId, channel) =>
|
||||||
|
post('/report/confirm-sent', { reportId, channel })
|
||||||
|
|
||||||
/** 触发服务回顾短片生成(异步)。composeMode: preset | interleave;身份由后端从上下文派生 */
|
/** 触发服务回顾短片生成(异步)。composeMode: preset | interleave;身份由后端从上下文派生 */
|
||||||
export const postReportHighlightStart = (data) => post('/report/highlight/start', data)
|
export const postReportHighlightStart = (data) => post('/report/highlight/start', data)
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,10 @@
|
|||||||
<text class="rsm-close" @click="emitClose">✕</text>
|
<text class="rsm-close" @click="emitClose">✕</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="rsm-tip">复制下方链接或扫码打开;也可点选话术后一键复制整段发给微信好友。</text>
|
<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">
|
<view class="rsm-block">
|
||||||
<text class="rsm-label">报告链接</text>
|
<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--ghost" @click="emitClose">稍后</button>
|
||||||
<button class="rsm-btn rsm-btn--primary" @click="previewReport">预览报告</button>
|
<button class="rsm-btn rsm-btn--primary" @click="previewReport">预览报告</button>
|
||||||
</view>
|
</view>
|
||||||
|
<button
|
||||||
|
class="rsm-btn rsm-confirm"
|
||||||
|
:class="{ 'rsm-confirm--done': sent }"
|
||||||
|
:disabled="sent || confirming"
|
||||||
|
@click="confirmAlreadySent"
|
||||||
|
>
|
||||||
|
{{ sent ? '已确认发送' : (confirming ? '确认中…' : '确认已发送给宠主') }}
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -62,6 +74,9 @@ import { buildReportH5FullUrl } from '../../utils/reportPublicUrl.js'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: { type: Boolean, default: false },
|
show: { type: Boolean, default: false },
|
||||||
reportToken: { type: String, default: '' },
|
reportToken: { type: String, default: '' },
|
||||||
|
reportId: { type: [Number, String], default: '' },
|
||||||
|
sent: { type: Boolean, default: false },
|
||||||
|
confirming: { type: Boolean, default: false },
|
||||||
petName: { type: String, default: '' },
|
petName: { type: String, default: '' },
|
||||||
/** 门店名(话术变量) */
|
/** 门店名(话术变量) */
|
||||||
storeName: { type: String, default: '' },
|
storeName: { type: String, default: '' },
|
||||||
@ -69,7 +84,7 @@ const props = defineProps({
|
|||||||
serviceType: { type: String, default: '' }
|
serviceType: { type: String, default: '' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'preview'])
|
const emit = defineEmits(['close', 'preview', 'confirm-sent'])
|
||||||
|
|
||||||
const copiedUrl = ref(false)
|
const copiedUrl = ref(false)
|
||||||
const copiedTplIdx = ref(-1)
|
const copiedTplIdx = ref(-1)
|
||||||
@ -185,6 +200,19 @@ function onMask() {
|
|||||||
function previewReport() {
|
function previewReport() {
|
||||||
emit('preview')
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -234,6 +262,21 @@ function previewReport() {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
margin-bottom: 14px;
|
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 {
|
.rsm-block {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
@ -338,6 +381,16 @@ function previewReport() {
|
|||||||
background: var(--c-slate-100);
|
background: var(--c-slate-100);
|
||||||
color: var(--c-slate-600);
|
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] {
|
.rsm-btn[disabled] {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -184,12 +184,16 @@
|
|||||||
|
|
||||||
<ReportShareModal
|
<ReportShareModal
|
||||||
:show="shareModalOpen"
|
:show="shareModalOpen"
|
||||||
|
:report-id="shareReportId"
|
||||||
:report-token="shareToken"
|
:report-token="shareToken"
|
||||||
|
:sent="shareSent"
|
||||||
|
:confirming="confirmingSend"
|
||||||
:pet-name="sharePetName"
|
:pet-name="sharePetName"
|
||||||
:store-name="shareStoreName"
|
:store-name="shareStoreName"
|
||||||
:service-type="shareServiceType"
|
:service-type="shareServiceType"
|
||||||
@close="onShareModalClose"
|
@close="onShareModalClose"
|
||||||
@preview="onSharePreview"
|
@preview="onSharePreview"
|
||||||
|
@confirm-sent="onConfirmSent"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -197,7 +201,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
import { onPageScroll } from '@dcloudio/uni-app'
|
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 AppIcon from '../../components/AppIcon.vue'
|
||||||
import ReportShareModal from '../../components/report/ReportShareModal.vue'
|
import ReportShareModal from '../../components/report/ReportShareModal.vue'
|
||||||
import { getUserSession, getStoreSession } from '../../utils/session.js'
|
import { getUserSession, getStoreSession } from '../../utils/session.js'
|
||||||
@ -239,7 +243,10 @@ const serviceTypes = ref([])
|
|||||||
const currentAppointmentId = ref(null)
|
const currentAppointmentId = ref(null)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const shareModalOpen = ref(false)
|
const shareModalOpen = ref(false)
|
||||||
|
const shareReportId = ref(null)
|
||||||
const shareToken = ref('')
|
const shareToken = ref('')
|
||||||
|
const shareSent = ref(false)
|
||||||
|
const confirmingSend = ref(false)
|
||||||
const sharePetName = ref('')
|
const sharePetName = ref('')
|
||||||
const shareStoreName = ref('')
|
const shareStoreName = ref('')
|
||||||
const shareServiceType = ref('')
|
const shareServiceType = ref('')
|
||||||
@ -711,7 +718,9 @@ const submitReport = async () => {
|
|||||||
submitting.value = false
|
submitting.value = false
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const token = res.data.reportToken
|
const token = res.data.reportToken
|
||||||
|
shareReportId.value = res.data.reportId
|
||||||
shareToken.value = token
|
shareToken.value = token
|
||||||
|
shareSent.value = false
|
||||||
sharePetName.value = report.value.petName || ''
|
sharePetName.value = report.value.petName || ''
|
||||||
shareStoreName.value = storeInfo?.name || ''
|
shareStoreName.value = storeInfo?.name || ''
|
||||||
shareServiceType.value = report.value.serviceType || ''
|
shareServiceType.value = report.value.serviceType || ''
|
||||||
@ -727,7 +736,10 @@ const submitReport = async () => {
|
|||||||
|
|
||||||
const onShareModalClose = () => {
|
const onShareModalClose = () => {
|
||||||
shareModalOpen.value = false
|
shareModalOpen.value = false
|
||||||
|
shareReportId.value = null
|
||||||
shareToken.value = ''
|
shareToken.value = ''
|
||||||
|
shareSent.value = false
|
||||||
|
confirmingSend.value = false
|
||||||
sharePetName.value = ''
|
sharePetName.value = ''
|
||||||
shareStoreName.value = ''
|
shareStoreName.value = ''
|
||||||
shareServiceType.value = ''
|
shareServiceType.value = ''
|
||||||
@ -736,7 +748,10 @@ const onShareModalClose = () => {
|
|||||||
const onSharePreview = () => {
|
const onSharePreview = () => {
|
||||||
const t = shareToken.value
|
const t = shareToken.value
|
||||||
shareModalOpen.value = false
|
shareModalOpen.value = false
|
||||||
|
shareReportId.value = null
|
||||||
shareToken.value = ''
|
shareToken.value = ''
|
||||||
|
shareSent.value = false
|
||||||
|
confirmingSend.value = false
|
||||||
sharePetName.value = ''
|
sharePetName.value = ''
|
||||||
shareStoreName.value = ''
|
shareStoreName.value = ''
|
||||||
shareServiceType.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 goBack = () => {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
if (pages.length > 1) {
|
if (pages.length > 1) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user