feat(eventOrder): add WhatsApp quote request functionality

Replaces the 'copy to clipboard' action with a direct 'Request a Quote' via WhatsApp. This streamlines the process for
users to submit their event order details.

- The 'Request a Quote' button now opens a pre-filled WhatsApp message to a designated contact number.
- A new `buildMessage` computed property is created in the `eventOrder` composable to generate a detailed quote request
message.
- Adds support for tracking referrers via a `?referer` URL query parameter, which is included in the final message.
This commit is contained in:
xiaomai
2025-10-16 21:52:33 +08:00
parent a5ac00baa1
commit 0d243daf42
2 changed files with 51 additions and 8 deletions

View File

@@ -58,19 +58,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
const { priceBreakdown, estimatedTotal, money } = useEventOrder(); const { priceBreakdown, estimatedTotal, buildMessage, money } = useEventOrder();
const items = computed(() => priceBreakdown.value); const items = computed(() => priceBreakdown.value);
const total = computed(() => estimatedTotal.value); const total = computed(() => estimatedTotal.value);
function onRequestQuote() { function onRequestQuote() {
// Placeholder interaction (no backend). Could open mailto or copy summary. const text = encodeURIComponent(buildMessage.value);
const lines = items.value.map( const url = `http://api.whatsapp.com/send?phone=+601157753558&text=${text}`;
(i) => window.open(url, "_blank");
`${i.label}${money.format(i.amount)}${i.note ? `${i.note}` : ""}`
);
const text = `预算合计:${money.format(total.value)}\n\n` + lines.join("\n");
if (navigator?.clipboard) navigator.clipboard.writeText(text).catch(() => {});
} }
</script> </script>

View File

@@ -4,6 +4,26 @@ import * as z from "zod";
export const _useEventOrder = () => { export const _useEventOrder = () => {
const sectionIndex = ref(0); const sectionIndex = ref(0);
// Referer
const REFERRER_MAP = {
anadesign: "A&A Design 广告先生",
dreamstudio: "Dream Studio",
infinity: "Infinity Visuals",
// 你可以自由扩充
} as const;
const route = useRoute();
const refererKey = computed(() => {
const raw = route.query.referer;
return Array.isArray(raw) ? raw[0] : raw || "";
});
const refererNote = computed(() => {
const key = refererKey.value as keyof typeof REFERRER_MAP;
return key in REFERRER_MAP ? `推薦單位:${REFERRER_MAP[key]}` : "";
});
function formatLocalDate(date: Date): string { function formatLocalDate(date: Date): string {
const y = date.getFullYear(); const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, "0"); const m = String(date.getMonth() + 1).padStart(2, "0");
@@ -253,6 +273,32 @@ export const _useEventOrder = () => {
maximumFractionDigits: 0, maximumFractionDigits: 0,
}); });
// 🚀 生成最终字符串
const buildMessage = computed(() => {
const lines = priceBreakdown.value
.map(
(i) =>
`- ${i.label}: ${money.format(i.amount)}${
i.note ? `${i.note}` : ""
}`
)
.join("\n");
return [
`[下单] 宴会大屏报价请求`,
`姓名:${orderState.contactName}`,
`电话:${orderState.contactNumber || "未填写"}`,
`活动:${orderState.eventName}`,
`日期:${orderState.eventDate}`,
`地点:${orderState.eventLocation}`,
``,
`服务明细:\n${lines || "无"}`,
``,
`总计:${money.format(estimatedTotal.value)}`,
`推荐单位:${refererNote.value || "无"}`,
].join("\n");
});
return { return {
sectionIndex, sectionIndex,
eventLocationItems, eventLocationItems,
@@ -260,6 +306,7 @@ export const _useEventOrder = () => {
orderState, orderState,
priceBreakdown, priceBreakdown,
estimatedTotal, estimatedTotal,
buildMessage,
money, money,
}; };
}; };