Files
anxinyan/user-app/src/stores/appraisal.ts

296 lines
8.5 KiB
TypeScript

import { defineStore } from "pinia";
import type { PreviewData, SubmitResult, UploadFileAsset, UploadItem } from "../api/appraisal";
type ProductState = {
categoryId: number;
categoryName: string;
brandId: number;
brandName: string;
};
type ExtraState = {
purchaseChannel: string;
purchasePrice: string;
purchaseDate: string;
usageStatus: string;
conditionDesc: string;
accessories: string[];
remark: string;
};
type ReturnAddressState = {
id: number;
consignee: string;
mobile: string;
province: string;
city: string;
district: string;
detailAddress: string;
fullAddress: string;
isDefault: boolean;
};
const storageKey = "anxinyan_appraisal_flow";
const legacyDemoExtra: ExtraState = {
purchaseChannel: "专柜",
purchasePrice: "8500",
purchaseDate: "",
usageStatus: "light_use",
conditionDesc: "轻微使用痕迹",
accessories: ["防尘袋", "包装盒"],
remark: "",
};
function initialProduct(): ProductState {
return {
categoryId: 0,
categoryName: "",
brandId: 0,
brandName: "",
};
}
function initialExtra(): ExtraState {
return {
purchaseChannel: "",
purchasePrice: "",
purchaseDate: "",
usageStatus: "",
conditionDesc: "",
accessories: [],
remark: "",
};
}
function isLegacyDemoExtra(extra: Partial<ExtraState> | undefined) {
if (!extra) return false;
const accessories = Array.isArray(extra.accessories) ? extra.accessories : [];
return (
extra.purchaseChannel === legacyDemoExtra.purchaseChannel &&
extra.purchasePrice === legacyDemoExtra.purchasePrice &&
(extra.purchaseDate || "") === legacyDemoExtra.purchaseDate &&
extra.usageStatus === legacyDemoExtra.usageStatus &&
extra.conditionDesc === legacyDemoExtra.conditionDesc &&
(extra.remark || "") === legacyDemoExtra.remark &&
accessories.length === legacyDemoExtra.accessories.length &&
accessories.every((item, index) => item === legacyDemoExtra.accessories[index])
);
}
function initialReturnAddress(): ReturnAddressState {
return {
id: 0,
consignee: "",
mobile: "",
province: "",
city: "",
district: "",
detailAddress: "",
fullAddress: "",
isDefault: false,
};
}
export const useAppraisalStore = defineStore("appraisal", {
state: () => ({
draftId: 0,
serviceProvider: "anxinyan",
serviceMode: "physical",
pricePackageId: 0,
pricePackageName: "",
pricePackageCode: "",
pricePackagePrice: 0,
currentStep: 1,
product: initialProduct(),
extra: initialExtra(),
returnAddress: initialReturnAddress(),
uploadTemplateId: 0,
requiredItems: [] as UploadItem[],
optionalItems: [] as UploadItem[],
preview: null as PreviewData | null,
submitResult: null as SubmitResult | null,
}),
actions: {
hydrate() {
const raw = uni.getStorageSync(storageKey);
if (!raw) return;
try {
const parsed = JSON.parse(raw);
Object.assign(this, parsed);
if (isLegacyDemoExtra(this.extra)) {
this.extra = initialExtra();
this.persist();
}
} catch (error) {
console.warn("hydrate appraisal store failed", error);
}
},
persist() {
uni.setStorageSync(
storageKey,
JSON.stringify({
draftId: this.draftId,
serviceProvider: this.serviceProvider,
serviceMode: this.serviceMode,
pricePackageId: this.pricePackageId,
pricePackageName: this.pricePackageName,
pricePackageCode: this.pricePackageCode,
pricePackagePrice: this.pricePackagePrice,
currentStep: this.currentStep,
product: this.product,
extra: this.extra,
returnAddress: this.returnAddress,
uploadTemplateId: this.uploadTemplateId,
requiredItems: this.requiredItems,
optionalItems: this.optionalItems,
preview: this.preview,
submitResult: this.submitResult,
}),
);
},
setServiceProvider(serviceProvider: string) {
this.serviceProvider = serviceProvider;
this.persist();
},
setPricePackage(payload: {
id: number;
packageName: string;
packageCode: string;
price: number;
}) {
this.pricePackageId = payload.id;
this.pricePackageName = payload.packageName;
this.pricePackageCode = payload.packageCode;
this.pricePackagePrice = payload.price;
this.persist();
},
setDraft(id: number) {
this.draftId = id;
this.persist();
},
setCurrentStep(step: number) {
this.currentStep = step;
this.persist();
},
setProduct(payload: Partial<ProductState>) {
this.product = {
...this.product,
...payload,
};
this.persist();
},
setExtra(payload: Partial<ExtraState>) {
this.extra = {
...this.extra,
...payload,
};
this.persist();
},
resetExtra() {
this.extra = initialExtra();
this.persist();
},
clearLegacyExtraDefaults() {
if (!isLegacyDemoExtra(this.extra)) {
return false;
}
this.extra = initialExtra();
this.persist();
return true;
},
setReturnAddress(payload: Partial<ReturnAddressState>) {
this.returnAddress = {
...this.returnAddress,
...payload,
};
this.persist();
},
clearReturnAddress() {
this.returnAddress = initialReturnAddress();
this.persist();
},
setUploadTemplate(templateId: number, requiredItems: UploadItem[], optionalItems: UploadItem[]) {
const requiredMap = new Map(this.requiredItems.map((item) => [item.item_code, item.files || []]));
const optionalMap = new Map(this.optionalItems.map((item) => [item.item_code, item.files || []]));
this.uploadTemplateId = templateId;
this.requiredItems = requiredItems.map((item) => ({
...item,
files: item.files || requiredMap.get(item.item_code) || [],
}));
this.optionalItems = optionalItems.map((item) => ({
...item,
files: item.files || optionalMap.get(item.item_code) || [],
}));
this.persist();
},
hydrateUploadItems(items: UploadItem[]) {
const required = items.filter((item) => item.is_required);
const optional = items.filter((item) => !item.is_required);
this.requiredItems = required.map((item) => ({
...item,
files: item.files || [],
}));
this.optionalItems = optional.map((item) => ({
...item,
files: item.files || [],
}));
this.persist();
},
upsertUploadFile(itemCode: string, file: UploadFileAsset, isRequired: boolean) {
const target = isRequired ? this.requiredItems : this.optionalItems;
const index = target.findIndex((item) => item.item_code === itemCode);
if (index >= 0) {
const currentFiles = target[index].files || [];
const nextFiles = [...currentFiles, file];
target[index] = {
...target[index],
files: nextFiles,
quality_status: nextFiles.length ? "uploaded" : "pending",
quality_message: "",
};
this.persist();
}
},
removeUploadFile(itemCode: string, fileId: string, isRequired: boolean) {
const target = isRequired ? this.requiredItems : this.optionalItems;
const index = target.findIndex((item) => item.item_code === itemCode);
if (index >= 0) {
const nextFiles = (target[index].files || []).filter((item) => item.file_id !== fileId);
target[index] = {
...target[index],
files: nextFiles,
quality_status: nextFiles.length ? "uploaded" : (target[index].is_required ? "pending" : "optional"),
};
this.persist();
}
},
setPreview(preview: PreviewData | null) {
this.preview = preview;
this.persist();
},
setSubmitResult(result: SubmitResult | null) {
this.submitResult = result;
this.persist();
},
resetForNewFlow() {
this.serviceProvider = "anxinyan";
this.serviceMode = "physical";
this.pricePackageId = 0;
this.pricePackageName = "";
this.pricePackageCode = "";
this.pricePackagePrice = 0;
this.draftId = 0;
this.currentStep = 1;
this.product = initialProduct();
this.extra = initialExtra();
this.returnAddress = initialReturnAddress();
this.uploadTemplateId = 0;
this.requiredItems = [];
this.optionalItems = [];
this.preview = null;
this.submitResult = null;
this.persist();
},
},
});