瀏覽代碼

建档页 表单字段 存储统一形式

cc12458 1 月之前
父節點
當前提交
86bcdfb576
共有 4 個文件被更改,包括 101 次插入73 次删除
  1. 79 66
      src/pages/register.page.vue
  2. 7 2
      src/request/api/account.api.ts
  3. 4 3
      src/request/model/register.model.ts
  4. 11 2
      src/stores/visitor.store.ts

+ 79 - 66
src/pages/register.page.vue

@@ -3,7 +3,7 @@ import NavHomeSelect from '@/assets/images/nav-home.select.png?url';
 import { Toast } from '@/platform';
 
 import { getCaptchaMethod, registerAccountMethod, registerFieldsMethod, dictionariesMethod, searchAccountMethod } from '@/request/api';
-import type { CascaderOption, Fields, Option, RegisterModel } from '@/request/model';
+import type { Field, Fields, Option, RegisterModel } from '@/request/model';
 import { useRouteQuery } from '@vueuse/router';
 import { useCaptcha, useRequest, useSerialRequest } from 'alova/client';
 
@@ -11,23 +11,26 @@ import type { FormInstance } from 'vant';
 import { RadioGroup as vanRadioGroup } from 'vant';
 import PickerDialog from '@/components/PickerDialog.vue';
 import CascaderDialog from '@/components/CascaderDialog.vue';
-import { useFlowStore } from '@/stores';
+import { useFlowStore, useVisitor } from '@/stores';
 
 const formRef = useTemplateRef<FormInstance>('register-form');
-const modelRef = ref<Partial<RegisterModel>>({ code: '' });
-const modelValueRef = ref<Partial<RegisterModel>>({});
-const model = computed(() => ({ ...modelRef.value, ...modelValueRef.value }));
+const modelLabel = ref<Partial<RegisterModel>>({ });
+const modelValue = ref<Partial<RegisterModel>>({ code: '' });
+const model = computed(() => ({ ...modelLabel.value, ...modelValue.value }));
 
 const { data: fields, loading } = useSerialRequest([dictionariesMethod, (dictionaries) => registerFieldsMethod(dictionaries)]).onSuccess(({ data }) => {
   const sex = data.find((field) => field.name === 'sex');
   if (sex) {
     const unknown = (<any>sex).component?.options?.find((option: any) => option.value === '2');
-    modelRef.value.sex = unknown?.value;
+    modelLabel.value.sex = unknown?.label;
+    modelValue.value.sex = unknown?.value;
   }
 });
 
 const flow = useFlowStore();
+const visitor = useVisitor();
 const { loading: submitting, send: submit } = useRequest(registerAccountMethod, { immediate: false }).onSuccess(({ data }) => {
+  visitor.updatePatient(modelLabel.value, data);
   flow.router.push();
 });
 
@@ -35,36 +38,13 @@ const forbiddenFields = shallowRef<Record<string, boolean>>({});
 const { loading: searching, send: search } = useRequest((data) => searchAccountMethod(data), {
   immediate: false,
 }).onSuccess(({ data }) => {
-  if (!fields.value.some(field => field.name === 'phone')) Reflect.deleteProperty(data, 'phone');
-  const modelLabel = {} as Record<string, any>;
-  const modelValue = {} as Record<string, any>;
-  const forbidden = {} as Record<string, boolean>;
-
-  for (const [key, value] of Object.entries(data)) {
-    const field = fields.value?.find((field) => field.name === key);
-    if (field) forbidden[key] = !!value && !['phone', 'cardno'].includes(key);
-    if (typeof value === 'string' && field?.component?.name === 'picker') {
-      const result = value.split(',').map((value) => {
-        const [v, l] = value.split(':');
-        return { value, label: l ?? (field.component as { options: Option[] })?.options?.find((option) => option.value === v)?.label ?? v };
-      });
-      modelValue[key] = result.map((t) => t.value).join(',');
-      modelLabel[key] = result.map((t) => t.label).join(',');
-    } else if (typeof value === 'object' && field?.component?.name === 'cascader') {
-      modelLabel[key] = value.map((option) => option.label).join(' / ');
-      modelValue[key] = value;
-    } else {
-      modelLabel[key] = value;
-    }
-  }
-  forbiddenFields.value = forbidden;
-  modelRef.value = { ...modelRef.value, ...modelLabel };
-  modelValueRef.value = { ...modelValueRef.value, ...modelValue };
+  if (!fields.value.some((field) => field.name === 'phone')) Reflect.deleteProperty(data, 'phone');
+  setValues(data);
 });
 
 let captchaLoaded = false;
 const { loading: captchaLoading, countdown, send: getCaptcha } = useCaptcha(
-  () => getCaptchaMethod(modelRef.value.phone!),
+  () => getCaptchaMethod(model.value.phone!),
   { initialCountdown: 60 },
 ).onSuccess(({ data }) => {
   captchaLoaded = true;
@@ -73,7 +53,7 @@ const { loading: captchaLoading, countdown, send: getCaptcha } = useCaptcha(
 const getCaptchaHandle = async () => {
   try {
     await formRef.value?.validate('phone');
-    if ( !modelRef.value.phone ) throw { message: `请输入手机号码` };
+    if ( !model.value.phone ) throw { message: `请输入手机号码` };
     await getCaptcha();
     const field = fields.value.find(field => field.name === 'code');
     if ( field?.keyboard ) { field.keyboard.show = true; }
@@ -87,7 +67,7 @@ const searchHandle = async (key: 'cardno' | 'code') => {
   try {
     await formRef.value?.validate(key);
     forbiddenFields.value = {};
-    const { cardno, phone, code } = modelRef.value;
+    const { cardno, phone, code } = model.value;
     await search({ cardno, phone, code })
       .then((data) => {
         forbiddenFields.value[forbidden] = !!(data as any)[forbidden];
@@ -101,8 +81,8 @@ const searchHandle = async (key: 'cardno' | 'code') => {
 
 function onKeyboardBlur(field: Fields[number]) {
   if ( field?.name === 'phone' && !captchaLoaded ) { getCaptchaHandle(); }
-  if ( field?.name === 'cardno' && modelRef.value.cardno ) { searchHandle('cardno'); }
-  if ( field?.name === 'code' && modelRef.value.phone ) { searchHandle('code'); }
+  if ( field?.name === 'cardno' && model.value.cardno ) { searchHandle('cardno'); }
+  if ( field?.name === 'code' && model.value.phone ) { searchHandle('code'); }
 }
 
 function onSubmitHandle() {
@@ -120,7 +100,7 @@ watch(scan, key => {
   if ( key ) {
     try {
       const { model } = JSON.parse(sessionStorage.getItem(`scan_${ key }`) ?? '');
-      modelRef.value = { ...modelRef.value, ...model };
+      setValues(model);
     } catch ( e: any ) {}
   }
 }, { immediate: true });
@@ -137,27 +117,60 @@ const keyboardProps = reactive({
   props: {},
   show: false,
 });
-
 const pickerProps = reactive({
   key: '',
   props: { options: [], selected: [] },
   show: false,
-  handle(options: Option[]) {
-    const key = (this ?? pickerProps).key;
-    (modelRef.value as Record<string, any>)[key] = options.map(option => option.label).join(',');
-    (modelValueRef.value as Record<string, any>)[key] = options.map(option => option.value).join(',');
-  }
 });
 const cascaderProps = reactive({
   key: '',
-  props: { options: [], loading: false, },
+  props: { options: [], loading: false },
   show: false,
-  handle(options: CascaderOption[]) {
-    const key = (this ?? cascaderProps).key;
-    (modelRef.value as Record<string, any>)[key] = options.map((option) => option.label).join(' / ');
-    (modelValueRef.value as Record<string, any>)[key] = options;
-  },
 });
+
+const handle = (value: any, field: Field | string) => {
+  field = ((key: Field | string): Field => (typeof key === 'string' ? fields.value.find((field) => field.name === key)! : key))(field);
+  const key = field.name!;
+  if (field.component?.name === 'radio') {
+    const option = field?.component?.options?.find((option) => option.value === value);
+    (modelLabel.value as any)[key] = option?.label;
+    (modelValue.value as any)[key] = option?.value;
+  } else if (field.component?.name === 'picker') {
+    if (typeof value === 'string') {
+      value = value.split(',').map((value) => {
+        const [v, l] = value.split(':');
+        return { value, label: l ?? (field.component as any).options?.find((option: Option) => option.value === v)?.label ?? v };
+      });
+    }
+    (modelLabel.value as any)[key] = value.map((option: Option) => option.label).join(',');
+    (modelValue.value as any)[key] = value.map((option: Option) => option.value).join(',');
+  } else if (field.component?.name === 'cascader') {
+    (modelLabel.value as any)[key] = value.map((option: Option) => option.label).join('/');
+    (modelValue.value as any)[key] = value;
+  } else if (field.component?.name === 'code') {
+    (modelValue.value as any)[key] = value;
+  } else {
+    (modelLabel.value as any)[key] = value;
+    (modelValue.value as any)[key] = value;
+  }
+};
+const setValues = (values: Record<string, any>) => {
+  forbiddenFields.value = {};
+  if (!values) {
+    modelLabel.value = {};
+    modelValue.value = {};
+  }
+  for (const [key, value] of Object.entries(values ?? {})) {
+    const field = fields.value?.find((field) => field.name === key);
+    if (field) {
+      forbiddenFields.value[key] = !!value && !['phone', 'cardno'].includes(key);
+      handle(value, field as any)
+    } else {
+      (modelValue as any).value[key] = value;
+    }
+  }
+}
+
 function onFieldFocus(field: any) {
   if (forbiddenFields.value[field.name]) return;
   if (field.keyboard) {
@@ -178,7 +191,7 @@ function onFieldFocus(field: any) {
       ...field.component.props,
       title: field.control.label,
       options: field.component.options,
-      selected: (modelValueRef.value as Record<string, string>)[field.name]?.split(','),
+      selected: (modelValue.value as Record<string, string>)[field.name]?.split(','),
     };
   } else if (field.component?.name === 'cascader') {
     cascaderProps.key = field.name;
@@ -197,7 +210,7 @@ function onFieldFocus(field: any) {
           title: field.control.label,
           options: field.component.options,
           loading: false,
-          selected: (modelValueRef.value as Record<string, { value: string }[]>)[field.name]?.map((option) => option.value),
+          selected: (modelValue.value as Record<string, { value: string }[]>)[field.name]?.map((option) => option.value),
         };
       })();
     } else {
@@ -212,6 +225,7 @@ function onFieldFocus(field: any) {
 function onFieldBlur(field: any) {
   keyboardProps.show = false;
   pickerProps.show = false;
+  cascaderProps.show = false;
 }
 </script>
 <template>
@@ -230,22 +244,21 @@ function onFieldBlur(field: any) {
       </div>
     </div>
     <div class="page-content p-6 overflow-auto">
-      <van-form class="register-form" ref="register-form" colon required="auto"
-                scroll-to-error scroll-to-error-position="center"
-                @submit="onSubmitHandle()"
-      >
+      <van-form class="register-form" ref="register-form" colon required="auto" scroll-to-error scroll-to-error-position="center" @submit="onSubmitHandle()">
         <van-cell-group :border="false">
           <template v-for="field in fields" :key="field.name">
             <template v-if="!field.control?.hide || (typeof field.control?.hide === 'function' && !field.control.hide(model))">
-              <van-field v-model="modelRef[field.name]" :name="field.name" :id="field.name"
-                         :rules="field.rules" v-bind="field.control"
-                         :class="{'no-border': field.control?.border === false}"
-                         @focus="onFieldFocus(field)" @blur="onFieldBlur(field)"
-                         :readonly="field.control?.readonly" @click="onFieldFocus(field)"
-                         :disabled="forbiddenFields[field.name]"
+              <van-field
+                :model-value="field.control?.readonly ? modelLabel[field.name] : model[field.name]" @update:model-value="handle($event, field)"
+                :name="field.name" :id="field.name"
+                :rules="field.rules" v-bind="field.control"
+                :class="{'no-border': field.control?.border === false}"
+                @focus="onFieldFocus(field)" @blur="onFieldBlur(field)"
+                :readonly="field.control?.readonly" @click="onFieldFocus(field)"
+                :disabled="forbiddenFields[field.name]"
               >
                 <template #input v-if="field.component?.name === 'radio'">
-                  <van-radio-group v-model="modelRef[field.name]" direction="horizontal" shape="dot" :disabled="forbiddenFields[field.name]">
+                  <van-radio-group direction="horizontal" shape="dot" :disabled="forbiddenFields[field.name]" v-model="modelValue[field.name]" @change="handle($event, field)">
                     <van-radio v-for="option in field.component?.options" :key="option.value" :name="option.value">
                       {{ option.label }}
                     </van-radio>
@@ -254,7 +267,7 @@ function onFieldBlur(field: any) {
                 <template #input v-else-if="field.component?.name === 'code'">
                   <van-password-input
                     style="width: 100%;"
-                    v-model:value="modelRef[field.name]" v-bind="(field.component as any)!.props"
+                    v-model:value="modelValue[field.name]" v-bind="(field.component as any)!.props"
                     :focused="field.keyboard?.show" @focus="field.keyboard && (field.keyboard.show = true);fix('code')"
                   />
                 </template>
@@ -279,9 +292,9 @@ function onFieldBlur(field: any) {
           <img v-else class="size-full" src="@/assets/images/next-step.svg" alt="提交" @click="formRef?.submit()">
         </div>
       </div>
-      <van-number-keyboard v-bind="keyboardProps.props" :show="keyboardProps.show" v-model="modelRef[keyboardProps.key]"></van-number-keyboard>
-      <PickerDialog v-bind="pickerProps.props" v-model:show="pickerProps.show" @selected="pickerProps.handle($event)"></PickerDialog>
-      <CascaderDialog v-bind="cascaderProps.props" v-model:show="cascaderProps.show" @selected="cascaderProps.handle($event)"></CascaderDialog>
+      <van-number-keyboard v-bind="keyboardProps.props" :show="keyboardProps.show" v-model="modelValue[keyboardProps.key]" @update:model-value="handle($event, keyboardProps.key)"></van-number-keyboard>
+      <PickerDialog v-bind="pickerProps.props" v-model:show="pickerProps.show" @selected="handle($event, pickerProps.key)"></PickerDialog>
+      <CascaderDialog v-bind="cascaderProps.props" v-model:show="cascaderProps.show" @selected="handle($event, cascaderProps.key)"></CascaderDialog>
     </div>
   </div>
 </template>

+ 7 - 2
src/request/api/account.api.ts

@@ -1,5 +1,5 @@
-import { cacheFor } from '@/request/api/index';
 import type { CascaderOption, Dictionaries, Fields, RegisterModel } from '@/request/model';
+import { cacheFor } from '@/request/api/index';
 import { fromRegisterFields } from '@/request/model';
 import { getRoutePath } from '@/router';
 import { useVisitor } from '@/stores';
@@ -60,8 +60,13 @@ export function searchAccountMethod(params: Partial<RegisterModel>) {
       for (const key of address) {
         const code = data?.[`${key}Code`];
         const name = data?.[`${key}Name`];
+        Reflect.deleteProperty(data, `${key}Code`);
+        Reflect.deleteProperty(data, `${key}Name`);
         if (name && code) data.address.push({ label: name, value: key === 'province' ? code.slice(0, 2) : code.padEnd(12, '0') });
-        else try { delete data.address; } catch {}
+        else {
+          Reflect.deleteProperty(data, 'address');
+          break;
+        }
       }
       return Object.fromEntries(Object.entries(data).filter(([item, value]) => !!value)) as Partial<RegisterModel>;
     },

+ 4 - 3
src/request/model/register.model.ts

@@ -29,6 +29,7 @@ export interface RegisterModel {
 }
 
 export interface Field {
+  name?: FieldKey;
   control: {
     label: string; placeholder?: string;
     type?: string; min?: number; max?: number; minlength?: number; maxlength?: number;
@@ -46,9 +47,9 @@ export interface Field {
 }
 
 export type FieldKey = keyof RegisterModel;
-export type Fields = ( Field & { name: FieldKey } )[];
+export type Fields = Field[];
 
-const Fields: Record<FieldKey, Field> = {
+export const Field_Config: Record<FieldKey, Field> = {
   height: {
     control: {
       label: '身高', placeholder: '请输入身高',
@@ -248,7 +249,7 @@ export function fromRegisterFields(options: string[], dictionaries?: Dictionarie
   return options.map(option => {
     const values = option.split(':');
     const name = toCamelCase(values[ 0 ]);
-    const field = Fields[ name as unknown as FieldKey ] ?? {
+    const field = Field_Config[ name as unknown as FieldKey ] ?? {
       control: { label: name, type: 'text' },
       rules: [],
     };

+ 11 - 2
src/stores/visitor.store.ts

@@ -1,6 +1,7 @@
 import { defineStore } from 'pinia';
-import type { PulseAnalysisModel } from '@/request/model';
+import type { PulseAnalysisModel, RegisterModel } from '@/request/model';
 
+type Patient = { [K in keyof RegisterModel]?: RegisterModel[K] | string };
 
 export const useVisitor = defineStore('visitor', () => {
   const patientId = ref<string>();
@@ -8,11 +9,14 @@ export const useVisitor = defineStore('visitor', () => {
   const reportId = ref<string>();
   const pulseReport = shallowRef<PulseAnalysisModel>();
 
+  const patient = shallowRef<Patient>();
+
   const $reset = () => {
     patientId.value = '';
     resultId.value = '';
     reportId.value = '';
     pulseReport.value = void 0;
+    patient.value = void 0;
   };
 
   function updatePulseReport(report: PulseAnalysisModel, patient?: string) {
@@ -22,5 +26,10 @@ export const useVisitor = defineStore('visitor', () => {
     }
   }
 
-  return { patientId, resultId, reportId, pulseReport, $reset, updatePulseReport };
+  function updatePatient(data: Patient, id?: string) {
+    if (!id || id === patientId.value) patient.value = data;
+    else patient.value = void 0;
+  }
+
+  return { patientId, resultId, reportId, pulseReport, $reset, updatePulseReport, patient, updatePatient };
 });