217 lines
6.9 KiB
TypeScript
217 lines
6.9 KiB
TypeScript
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 : '提交失败,请稍后重试,已填写内容不会清空';
|
||
},
|
||
});
|