Files
lehuo/apps/miniprogram/miniprogram/pages/application-form/application-form.ts
T
2026-08-12 14:35:12 +08:00

217 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {
ApplicationApplyMode,
CreateServiceApplicationRequest,
ServiceCategory,
ServiceCategoryOption,
} from '@lehuo/contracts';
import {
APPLICATION_APPLY_MODE_LABELS,
APPLICATION_APPLY_MODES,
SERVICE_CATEGORIES,
SERVICE_CATEGORY_LABELS,
} from '@lehuo/contracts';
import { MiniProgramApiError } from '../../services/api-client';
import {
createServiceApplication,
fetchServiceCategories,
} from '../../services/service-applications';
import { createClientRequestId } from '../../utils/request-id';
const TOTAL_STEPS = 5;
function isServiceCategory(value: string): value is ServiceCategory {
return (SERVICE_CATEGORIES as readonly string[]).includes(value);
}
function isApplyMode(value: string): value is ApplicationApplyMode {
return (APPLICATION_APPLY_MODES as readonly string[]).includes(value);
}
Page({
data: {
step: 1,
totalSteps: TOTAL_STEPS,
loading: true,
submitting: false,
errorMessage: '',
categories: [] as ServiceCategoryOption[],
modeOptions: APPLICATION_APPLY_MODES.map((code) => ({
code,
label: APPLICATION_APPLY_MODE_LABELS[code],
})),
clientRequestId: createClientRequestId(),
serviceCategory: '',
applyMode: '',
beneficiaryName: '',
contactMobile: '',
regionCode: '',
description: '',
preferredContactTime: '',
needsHomeVisit: false,
mobilityDifficulty: false,
privacyConsent: false,
serviceCategoryLabel: '',
applyModeLabel: '',
contactMobileMasked: '',
},
onLoad(options: Record<string, string | undefined>) {
if (options.mode && APPLICATION_APPLY_MODES.includes(options.mode as ApplicationApplyMode)) {
const mode = options.mode as ApplicationApplyMode;
this.setData({ applyMode: mode, applyModeLabel: APPLICATION_APPLY_MODE_LABELS[mode] });
}
void this.loadCategories();
},
async loadCategories() {
this.setData({ loading: true, errorMessage: '' });
try {
const result = await fetchServiceCategories();
this.setData({ categories: result.items, loading: false });
} catch (error) {
this.setData({ loading: false, errorMessage: this.errorText(error) });
}
},
retryLoad() {
void this.loadCategories();
},
selectCategory(event: WechatMiniprogram.TouchEvent) {
const code = event.currentTarget.dataset.code as ServiceCategory;
this.setData({
serviceCategory: code,
serviceCategoryLabel: SERVICE_CATEGORY_LABELS[code],
clientRequestId: createClientRequestId(),
errorMessage: '',
});
},
selectMode(event: WechatMiniprogram.TouchEvent) {
const code = event.currentTarget.dataset.code as ApplicationApplyMode;
this.setData({
applyMode: code,
applyModeLabel: APPLICATION_APPLY_MODE_LABELS[code],
clientRequestId: createClientRequestId(),
errorMessage: '',
});
},
updateBeneficiaryName(event: WechatMiniprogram.Input) {
this.updateField({ beneficiaryName: event.detail.value });
},
updateContactMobile(event: WechatMiniprogram.Input) {
this.updateField({ contactMobile: event.detail.value });
},
updateRegionCode(event: WechatMiniprogram.Input) {
this.updateField({ regionCode: event.detail.value });
},
updateDescription(event: WechatMiniprogram.Input) {
this.updateField({ description: event.detail.value });
},
updatePreferredContactTime(event: WechatMiniprogram.Input) {
this.updateField({ preferredContactTime: event.detail.value });
},
updateNeedsHomeVisit(event: WechatMiniprogram.SwitchChange) {
this.updateField({ needsHomeVisit: event.detail.value });
},
updateMobilityDifficulty(event: WechatMiniprogram.SwitchChange) {
this.updateField({ mobilityDifficulty: event.detail.value });
},
updatePrivacyConsent(event: WechatMiniprogram.CheckboxGroupChange) {
this.setData({ privacyConsent: event.detail.value.includes('accepted'), errorMessage: '' });
},
nextStep() {
const message = this.validateStep(this.data.step);
if (message) {
this.setData({ errorMessage: message });
return;
}
const next = Math.min(TOTAL_STEPS, this.data.step + 1);
this.setData({
step: next,
errorMessage: '',
contactMobileMasked: this.data.contactMobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2'),
});
},
previousStep() {
this.setData({ step: Math.max(1, this.data.step - 1), errorMessage: '' });
},
async submitApplication() {
if (!this.data.privacyConsent) {
this.setData({ errorMessage: '请阅读并同意隐私授权后再提交' });
return;
}
if (
!isServiceCategory(this.data.serviceCategory) ||
!isApplyMode(this.data.applyMode) ||
this.data.submitting
)
return;
this.setData({ submitting: true, errorMessage: '' });
const input: CreateServiceApplicationRequest = {
clientRequestId: this.data.clientRequestId,
serviceCategory: this.data.serviceCategory,
applyMode: this.data.applyMode,
beneficiaryName: this.data.beneficiaryName.trim(),
contactMobile: this.data.contactMobile.trim(),
regionCode: this.data.regionCode.trim(),
description: this.data.description.trim(),
...(this.data.preferredContactTime.trim()
? { preferredContactTime: this.data.preferredContactTime.trim() }
: {}),
needsHomeVisit: this.data.needsHomeVisit,
mobilityDifficulty: this.data.mobilityDifficulty,
privacyConsent: true,
};
try {
const result = await createServiceApplication(input);
void wx.redirectTo({
url: `/pages/application-success/application-success?publicId=${encodeURIComponent(result.application.publicId)}`,
});
} catch (error) {
this.setData({ submitting: false, errorMessage: this.errorText(error) });
}
},
updateField(values: Record<string, string | boolean>) {
this.setData({
...values,
clientRequestId: createClientRequestId(),
errorMessage: '',
});
},
validateStep(step: number): string {
if (step === 1 && !this.data.serviceCategory) return '请选择需要的服务类型';
if (step === 2 && !this.data.applyMode) return '请选择您与服务对象的关系';
if (step === 3) {
if (!this.data.beneficiaryName.trim()) return '请填写服务对象姓名或称呼';
if (!/^1[3-9]\d{9}$/.test(this.data.contactMobile.trim())) return '请填写正确的手机号码';
if (this.data.regionCode.trim().length < 2) return '请填写所在市、区或街道';
}
if (step === 4 && this.data.description.trim().length < 10) {
return '请至少用 10 个字描述目前的困难或希望得到的帮助';
}
return '';
},
errorText(error: unknown): string {
if (error instanceof MiniProgramApiError) {
return `${error.message}${error.requestId ? `(编号:${error.requestId})` : ''}`;
}
return error instanceof Error ? error.message : '提交失败,请稍后重试,已填写内容不会清空';
},
});