快乐的梦鱼 дней назад: 3
Родитель
Сommit
985a86c8ed

+ 10 - 2
src/api/Utils.ts

@@ -1,3 +1,5 @@
+const bannedKeys = [ 'debug', 'where', 'select', 'insert', 'update', 'delete', 'count', 'sum', 'avg', 'max', 'min' ];
+
 export function transformSomeToArray(source: any) {
   if (typeof source === 'string') 
     return source.split(','); 
@@ -6,8 +8,14 @@ export function transformSomeToArray(source: any) {
       return source; 
     else {
       const arr = [];
-      for (const key in source)
-        arr.push(source[key]);
+      for (const key in source) {
+        const sourceValue = source[key];
+        if (bannedKeys.includes(key))
+          continue;
+        if (!sourceValue.id)
+          continue;
+        arr.push(sourceValue);
+      }
       return arr;
     }
   }

+ 60 - 3
src/api/collect/AssessmentContent.ts

@@ -178,6 +178,10 @@ export class SelfAssessmentCheckItemAnswer extends DataModel<SelfAssessmentCheck
       points: { clientSide: 'number', serverSide: 'number' },
       count: { clientSide: 'number', serverSide: 'number' },
     };
+    this._afterSolveServer = (data) => {
+      if (data.itemId)
+        data.id = data.itemId;
+    }
   }
 
   /**
@@ -210,7 +214,12 @@ export class SelfAssessmentDetail extends DataModel<SelfAssessmentDetail> {
       province: { clientSide: 'number', serverSide: 'number' },
       provincePoints: { clientSide: 'number', serverSide: 'number' },
       level: { clientSide: 'number', serverSide: 'string' },
-      awardTime: { clientSide: 'date', serverSide: 'string' },
+      awardTime: {
+        clientSide: 'date', 
+        clientSideDateFormat: 'YYYY-MM-DD',
+        serverSide: 'string',
+        serverSideDateFormat: 'YYYY-MM-DD',
+      },
       checkItems: {
         customToClientFn: (value) => {
           return transformArrayDataModel(SelfAssessmentCheckItemAnswer, transformSomeToArray(value), 'data');
@@ -366,6 +375,35 @@ export interface SaveCheckAnnexPayload {
   fileSize?: number;
 }
 
+/** 证明材料列表项(getAnnexList) */
+export class CheckAnnexListItem extends DataModel<CheckAnnexListItem> {
+  constructor() {
+    super(CheckAnnexListItem, '证明材料列表项');
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      id: { clientSide: 'number', serverSide: 'number' },
+      formId: { clientSide: 'number', serverSide: 'number' },
+      type: { clientSide: 'number', serverSide: 'number' },
+      attachId: { clientSide: 'number', serverSide: 'number' },
+      fileSize: { clientSide: 'number', serverSide: 'number' },
+    };
+  }
+
+  id = 0 as number;
+  name = '' as string;
+  formId = 0 as number;
+  url = '' as string;
+  /** 1=图片,2=视频,3=音频,4=文档,5=其他,6=外部链接 */
+  type = 0 as number;
+  desc = '' as string|null;
+  mimetype = '' as string|null;
+  attachId = null as number|null;
+  /** 文件大小 KB */
+  fileSize = null as number|null;
+  createtime = '' as string|null;
+  updatetime = '' as string|null;
+}
+
 export type IchCheckPaginated<T> = {
   total: number;
   perPage: number;
@@ -397,11 +435,17 @@ export class AssessmentContentApi extends AppServerRequestModule<DataModel> {
   async getCheckItems(level: number) {
     const res = await this.post('/ich/check/getCheckItems', '自查计分项目', { level });
     const list = transformSomeToArray(res.data) as KeyValue[];
-    const items = transformArrayDataModel<CheckItemInfo>(CheckItemInfo, list, 'data');
+    const items = transformArrayDataModel<CheckItemInfo>(CheckItemInfo, list, 'data') as CheckItemInfo[];
+    const map = new Map<number, CheckItemInfo>();
+    for (const item of items)
+      map.set(item.id, item);
     const top = items.filter((item) => item.pid === 0);
     for (const item of items) 
       item.children = items.filter((i) => i.pid === item.id);
-    return top;
+    return {
+      top,
+      map,
+    };
   }
 
   /**
@@ -471,6 +515,8 @@ export class AssessmentContentApi extends AppServerRequestModule<DataModel> {
       uni.downloadFile({
         url: `${this.config.baseUrl}/pdf/create?id=${id}`,
         success: (res) => {
+          if (res.statusCode !== 200)
+            throw new Error('下载失败,状态码:' + res.statusCode);
           resolve(res.tempFilePath);
         },
         fail: (err) => {
@@ -499,6 +545,17 @@ export class AssessmentContentApi extends AppServerRequestModule<DataModel> {
   }
 
   /**
+   * 证明材料列表(按自查表)
+   * POST `/ich/check/getAnnexList`
+   */
+  async getAnnexList(formId: number) {
+    const res = await this.post<KeyValue>('/ich/check/getAnnexList', '证明材料列表', {
+      form_id: formId,
+    });
+    return normalizePaginated<CheckAnnexListItem>(CheckAnnexListItem, res.requireData());
+  }
+
+  /**
    * 传承人基础信息(默认当前用户;管理员可传 userId)
    */
   async getInheritorBasic(userId?: number) {

+ 1 - 1
src/common/components/upload/AliOssUploadCo.ts

@@ -82,7 +82,7 @@ export function useAliOssUploadCo(
 ) {
   return (action: UploaderAction) => {
     const name = StringUtils.path.getFileName(action.item.filePath);
-    const uploadPath = `${subPath}/${name.split('.')[0]}-${RandomUtils.genNonDuplicateID(26)}.${StringUtils.path.getFileExt(name)}`;  
+    const uploadPath = `${subPath}/${name.split('.')[0]}-${RandomUtils.genNonDuplicateID(6)}.${StringUtils.path.getFileExt(name)}`;  
     const cancelHandler: { cancel: () => void } = {
       cancel: () => {},
     };    

+ 11 - 2
src/components/form/CalendarField.vue

@@ -19,7 +19,6 @@
       v-if="popupShow"
       v-bind="props"
       v-model="tempValue"
-      @selectTextChange="onSelectTextChange"
     />
     <slot name="footer">
       <Height :size="20" />
@@ -132,7 +131,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -144,6 +142,17 @@ const {
   emit as any,
   [],
   props.shouldUpdateValueImmediately,
+  (v) => {
+    if (!v)
+      return '';
+    if (typeof v === 'string')
+      return v;
+    if (v.length === 0)
+      return '';
+    if (v.length === 1)
+      return v[0];
+    return v.join(',');
+  },
   props.beforeConfirm,
   popupShow,
 );

+ 7 - 3
src/components/form/CascadePickerField.vue

@@ -14,8 +14,7 @@
     />
     <CascadePicker 
       v-bind="props"
-      v-model:value="tempValue" 
-      @selectTextChange="onSelectTextChange"
+      v-model:value="tempValue"
     />
   </Popup>
   <Text
@@ -105,7 +104,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -117,6 +115,12 @@ const {
   emit as any,
   [],
   props.shouldUpdateValueImmediately,
+  (v) => {
+    if (!v || v.length === 0)
+      return '';
+    //
+    return '';
+  },
   undefined,
   popupShow,
 );

+ 5 - 13
src/components/form/CascaderField.vue

@@ -16,7 +16,6 @@
       v-bind="props"
       :modelValue="tempValue"
       @update:modelValue="(v:any) => tempValue = v"
-      @selectTextChange="onSelectTextChange"
       @pickEnd="onPickEnd"
     />
     <slot name="footer">
@@ -124,7 +123,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -136,6 +134,11 @@ const {
   emit as any,
   [],
   props.shouldUpdateValueImmediately,
+  (v) => {
+    if (!v || v.length === 0)
+      return '';
+    return getCascaderText(v, props.valueKey, props.textKey, props.childrenKey, props.data);
+  },
   props.beforeConfirm,
   popupShow,
 );
@@ -145,17 +148,6 @@ function onPickEnd() {
     onConfirm();
 }
 
-watch(tempValue, (v) => {
-  if (!popupShow.value)
-    onSelectTextChange(getCascaderText(
-      v, 
-      props.valueKey, 
-      props.textKey, 
-      props.childrenKey, 
-      props.data
-    ), true);
-}, { immediate: true })
-
 defineExpose({
   confirm: onConfirm,
   cancel: onCancel,

+ 7 - 3
src/components/form/DatePicker.vue

@@ -10,9 +10,10 @@
 
 <script setup lang="ts">
 import { computed, onMounted } from 'vue';
-import type { PickerItem, PickerProps } from './Picker.vue';
+import type { PickerProps } from './Picker.vue';
 import Picker from './Picker.vue';
 import { DateUtils } from '@imengyu/imengyu-utils';
+import type { PickerItem } from './Picker';
 
 // 更新 DatePickerProps 接口
 export interface DatePickerProps extends Omit<PickerProps, 'columns'|'value'> {
@@ -50,6 +51,8 @@ const props = withDefaults(defineProps<DatePickerProps>(), {
   dayText: '日',
 });
 
+let forceUpdate = true;
+
 // 计算当前选中的值
 const value = computed(() => {
   const value : number[] = [];
@@ -129,10 +132,11 @@ function updateValue(v: number[]) {
       }
       emit('update:modelValue', date);
     }
-    emit('selectTextChange', DateUtils.formatDate(date, 'yyyy-MM-dd'));
+    emit('selectTextChange', DateUtils.formatDate(date, 'yyyy-MM-dd'), forceUpdate);
   } else {
-    emit('selectTextChange', '');
+    emit('selectTextChange', '', forceUpdate);
   }
+  forceUpdate = false;
 }
 onMounted(() => updateValue([]));
 

+ 4 - 3
src/components/form/DatePickerField.vue

@@ -15,7 +15,6 @@
     <DatePicker 
       v-bind="props"
       v-model="tempValue"
-      @selectTextChange="onSelectTextChange"
     />
   </Popup>
   <Text
@@ -29,7 +28,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, toRef } from 'vue';
+import { onMounted, ref, toRef } from 'vue';
 import { useFieldChildValueInjector } from './FormContext';
 import type { DatePickerProps } from './DatePicker.vue';
 import Popup from '../dialog/Popup.vue';
@@ -38,6 +37,7 @@ import DatePicker from './DatePicker.vue';
 import { usePickerFieldTempStorageData } from './PickerUtils';
 import Text, { type TextProps } from '../basic/Text.vue';
 import { usePickerFieldInstance, type PickerFieldInstance } from './Picker';
+import { DateUtils } from '@imengyu/imengyu-utils';
 
 export interface DatePickerFieldProps extends Omit<DatePickerProps, 'modelValue'> {
   modelValue?: Date;
@@ -105,7 +105,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -117,10 +116,12 @@ const {
   emit as any,
   new Date(),
   props.shouldUpdateValueImmediately,
+  (v) => DateUtils.formatDate(v, 'yyyy-MM-dd'),
   undefined,
   popupShow,
 );
 
+
 defineExpose<PickerFieldInstance>(usePickerFieldInstance(popupShow));
 defineOptions({
   options: {

+ 2 - 1
src/components/form/DateTimePicker.vue

@@ -10,9 +10,10 @@
 
 <script setup lang="ts">
 import { computed, onMounted } from 'vue';
-import type { PickerItem, PickerProps } from './Picker.vue';
+import type { PickerProps } from './Picker.vue';
 import Picker from './Picker.vue';
 import { DateUtils } from '@imengyu/imengyu-utils';
+import type { PickerItem } from './Picker';
 
 // 更新 DateTimePickerProps 接口,添加日期相关属性
 export interface DateTimePickerProps extends Omit<PickerProps, 'columns'|'value'> {

+ 2 - 2
src/components/form/DateTimePickerField.vue

@@ -15,7 +15,6 @@
     <DateTimePicker 
       v-bind="props"
       v-model="tempValue"
-      @selectTextChange="onSelectTextChange"
     />
   </Popup>
   <Text
@@ -38,6 +37,7 @@ import DateTimePicker from './DateTimePicker.vue';
 import { usePickerFieldTempStorageData } from './PickerUtils';
 import Text, { type TextProps } from '../basic/Text.vue';
 import { usePickerFieldInstance, type PickerFieldInstance } from './Picker';
+import { DateUtils } from '@imengyu/imengyu-utils';
 
 export interface DateTimePickerFieldProps extends Omit<DateTimePickerProps, 'modelValue'> {
   modelValue?: Date;
@@ -108,7 +108,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -120,6 +119,7 @@ const {
   emit as any,
   new Date(),
   props.shouldUpdateValueImmediately,
+  (v) => DateUtils.formatDate(v, 'yyyy-MM-dd HH:mm:ss'),
   undefined,
   popupShow,
 );

+ 13 - 1
src/components/form/FormContext.ts

@@ -158,7 +158,19 @@ export function useFieldChildValueInjector<T>(
   const cellContext = useCellContext();
   const context = useInjectFormItemContext();
   const formContext = useInjectFormContext();
-  const shadowRefValue = ref(propsModelValue.value ?? context.getFormModelValue() ?? initialValue) as Ref<T>;
+  const shadowRefValue = ref(getInitialValue()) as Ref<T>;
+
+  function getInitialValue() {
+    if (propsModelValue.value) {
+      console.log('getInitialValue1 ', propsModelValue.value);
+      return propsModelValue.value;
+    }
+    if (context?.getFormModelValue()) {
+      console.log('getInitialValue2 context.getFormModelValue()', context.getFormModelValue());
+      return context.getFormModelValue();
+    };
+    return initialValue;
+  }
 
   const value = computed(() => {
     if (secondParentContext)

+ 11 - 0
src/components/form/Picker.vue

@@ -118,6 +118,17 @@ onMounted(() => {
 const themeStyles = themeContext.useThemeStyles({
 });
 
+defineExpose({
+  refresh: loadValues,
+  getSelectedText: () => {
+    return pickerSelectIndex.value.map((p, i) => {
+      const cols = props.columns[i];
+      if (!cols || cols.length === 0) 
+        return null;
+      return cols[p]?.text ?? cols[0]?.text ?? null;
+    }).join(' ');
+  },
+});
 defineOptions({
   options: {
     styleIsolation: "shared",

+ 7 - 2
src/components/form/PickerField.vue

@@ -13,9 +13,9 @@
       @confirm="onConfirm"
     />
     <Picker 
+      ref="pickerRef"
       v-bind="props"
       v-model:value="tempValue"
-      @selectTextChange="onSelectTextChange"
     />
   </Popup>
   <Text
@@ -93,6 +93,7 @@ const props = withDefaults(defineProps<PickerFieldProps>(), {
 });
 
 const popupShow = ref(false);
+const pickerRef = ref();
 
 const {
   value,
@@ -108,7 +109,6 @@ const {
 );
 
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -120,6 +120,11 @@ const {
   emit as any,
   [],
   props.shouldUpdateValueImmediately,
+  (v) => {
+    if (!v || v.length === 0)
+      return '';
+    return pickerRef.value?.getSelectedText() ?? '';
+  },
   undefined,
   popupShow,
 );

+ 28 - 36
src/components/form/PickerUtils.ts

@@ -1,18 +1,18 @@
-import { nextTick, ref, watch, type Ref } from "vue";
+import { nextTick, onMounted, ref, watch, type Ref } from "vue";
 
 /**
  * 选择器字段临时存储数据的组合式函数
  * 用于管理选择器组件的临时值、选择文本和交互逻辑
  * 
  * @template T - 值的类型
- * @param 当前值的响应式引用
- * @param 更新值的回调函数
- * @param 关闭弹窗的回调函数
- * @param 事件发射器函数
- * @param 默认的新值
- * @param 是否立即更新值
- * @param 确认前的回调函数,返回true时取消确认
- * @param 弹窗显示状态的响应式引用
+ * @param value - 当前值的响应式引用
+ * @param updateValue - 更新值的回调函数
+ * @param closePopup - 关闭弹窗的回调函数
+ * @param emit - 事件发射器函数
+ * @param defaultNewValue - 默认的新值
+ * @param shouldUpdateValueImmediately - 是否立即更新值至绑定值
+ * @param beforeConfirm - 确认前的回调函数,返回true时取消确认
+ * @param popupShow - 弹窗显示状态的响应式引用
  */
 export function usePickerFieldTempStorageData<T>(
   value: Ref<T>, 
@@ -21,36 +21,25 @@ export function usePickerFieldTempStorageData<T>(
   emit: (name: string, d?: any) => void,
   defaultNewValue: T,
   shouldUpdateValueImmediately: boolean,
+  requireFormat: (value: T) => string,
   beforeConfirm?: ((value: T) => Promise<boolean>) | undefined,
   popupShow?: Ref<boolean>,
 ) {
 
-  let tempSelectText = '';
-  let tempLastSelectText = '';
   // 临时值的响应式引用,初始值为当前值或默认新值
   const tempValue = ref(value.value ?? defaultNewValue) as Ref<T>;
+  const beforeConfirmValue = ref(value.value ?? defaultNewValue) as Ref<T>;
   // 显示的文本的响应式引用
   const selectText = ref('');
 
   /**
-   * 当选择文本变化时的处理函数
-   * @param 新的选择文本
-   * @param 是否强制更新显示的选择文本
-   */
-  function onSelectTextChange(t: string, forceUpdate = false) {
-    tempSelectText = t;
-    emit('selectTextChange', t);
-    if (forceUpdate)
-      selectText.value = t;
-  }
-
-  /**
-   * 取消选择的处理函数
+   * 取消选择。恢复临时值为当前值或默认新值
    */
   function onCancel() {
     closePopup();
+    tempValue.value = beforeConfirmValue.value;
+    updateValue(tempValue.value);
     emit('cancel');
-    selectText.value = tempLastSelectText;
   }
 
   /**
@@ -61,13 +50,13 @@ export function usePickerFieldTempStorageData<T>(
     // 如果有确认前回调且返回true,则取消确认
     if (beforeConfirm && await beforeConfirm(tempValue.value))
       return;
-    selectText.value = tempSelectText;
     updateValue(tempValue.value);
     emit('confirm', value.value);
   }
 
   watch(value, (v) => {
     tempValue.value = v;
+    selectText.value = requireFormat(v);
   });
   watch(tempValue, (v) => {
     emit('tempValueChange', tempValue.value);
@@ -79,19 +68,22 @@ export function usePickerFieldTempStorageData<T>(
   if (popupShow) {
     watch(popupShow, (v) => {
       if (v) {
-        // 弹窗显示时,记录当前的选择文本作为上一次的选择文本
-        nextTick(() => {
-          tempLastSelectText = tempSelectText;
-        });
+        beforeConfirmValue.value = tempValue.value;
       }
-    })
+    });
   }
 
+  onMounted(() => {
+    if (value.value)
+      selectText.value = requireFormat(value.value);
+    else
+      selectText.value = '';
+  });
+
   return {
-    onSelectTextChange, // 选择文本变化处理函数
-    onCancel, // 取消处理函数
-    onConfirm, // 确认处理函数
-    selectText, // 显示的选择文本
-    tempValue, // 临时值
+    onCancel,
+    onConfirm,
+    selectText,
+    tempValue,
   }
 }

+ 18 - 3
src/components/form/TimePickerField.vue

@@ -15,7 +15,6 @@
     <TimePicker 
       v-bind="props"
       v-model="tempValue"
-      @selectTextChange="onSelectTextChange"
     />
   </Popup>
   <Text
@@ -29,7 +28,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, toRef } from 'vue';
+import { computed, ref, toRef } from 'vue';
 import { useFieldChildValueInjector } from './FormContext';
 import type { TimePickerProps } from './TimePicker.vue';
 import Popup from '../dialog/Popup.vue';
@@ -38,6 +37,7 @@ import TimePicker from './TimePicker.vue';
 import { usePickerFieldTempStorageData } from './PickerUtils';
 import Text, { type TextProps } from '../basic/Text.vue';
 import { usePickerFieldInstance, type PickerFieldInstance } from './Picker';
+import { DateUtils } from '@imengyu/imengyu-utils';
 
 export interface TimePickerFieldProps extends Omit<TimePickerProps, 'modelValue'> {
   modelValue?: Date;
@@ -100,8 +100,18 @@ const {
   props.initalValue,
 );
 
+const format = computed(() => {
+  const formats = []
+  if (props.showHours)
+    formats.push('HH');
+  if (props.showMinute)
+    formats.push('mm');
+  if (props.showSecond)
+    formats.push('ss');
+  return formats.join(':');
+});
+
 const {
-  onSelectTextChange,
   onCancel,
   onConfirm,
   selectText,
@@ -113,6 +123,11 @@ const {
   emit as any,
   new Date(),
   props.shouldUpdateValueImmediately,
+  (v) => {
+    if (!v)
+      return '';
+    return DateUtils.formatDate(v, format.value);
+  },
   undefined,
   popupShow,
 );

+ 5 - 5
src/components/form/Uploader.vue

@@ -70,19 +70,19 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, watch } from 'vue';
+import { reactive, ref } from 'vue';
 import { propGetThemeVar, useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
+import { Debounce, LogUtils } from '@imengyu/imengyu-utils';
+import { actionSheet } from '../dialog/CommonRoot';
 import type { ToastInstance } from '../feedback/Toast.vue';
+import type { UploaderAction, UploaderItem } from './Uploader';
 import Toast from '../feedback/Toast.vue';
+import Text from '../basic/Text.vue';
 import DialogRoot, { type DialogAlertRoot } from '../dialog/DialogRoot.vue';
 import UploaderListAddItem from './UploaderListAddItem.vue';
 import UploaderListItem from './UploaderListItem.vue';
 import FlexView from '../layout/FlexView.vue';
 import FlexCol from '../layout/FlexCol.vue';
-import type { UploaderAction, UploaderItem } from './Uploader';
-import { Debounce, LogUtils } from '@imengyu/imengyu-utils';
-import Text from '../basic/Text.vue';
-import { actionSheet } from '../dialog/CommonRoot';
 
 const themeContext = useTheme();
 const TAG = 'Uploader';

+ 26 - 5
src/pages/collect/assessment/evaluation-form.vue

@@ -27,6 +27,7 @@
             <Result v-if="!currentForm?.id" status="info" title="请先保存评估表后再上传佐证资料" />
             <Uploader
               v-else
+              ref="uploaderRef"
               :upload="assessmentAnnexUpload"
               :max-upload-count="9"
               :max-file-size="20 * 1024 * 1024"
@@ -74,9 +75,10 @@ import type { FieldProps } from '@/components/form/Field.vue';
 import type { SignatureFieldProps } from '@/components/form/SignatureField.vue';
 import { useImageSimpleUploadCo } from '@/common/components/upload/ImageUploadCo';
 import EvaluationFormBlock from './components/EvaluationFormBlock.vue';
-import Uploader from '@/components/form/Uploader.vue';
+import Uploader, { type UploaderInstance } from '@/components/form/Uploader.vue';
 import { getMimeType } from '@/common/components/upload/mimes';
 import Divider from '@/components/display/Divider.vue';
+import { stringUrlToUploaderItem } from '@/components/form/Uploader';
 
 let loaded = false;
 
@@ -108,6 +110,7 @@ const assessmentAnnexUpload = useAliOssUploadCo('assessment/annex', async (res,
   });
 });
 
+const uploaderRef = ref<UploaderInstance | null>(null);
 const formRef = ref<IDynamicFormRef | null>(null);
 const formOptions : IDynamicFormOptions = {
   formAdditionaProps: {
@@ -177,7 +180,10 @@ const formOptions : IDynamicFormOptions = {
           label: '获评时间',
           name: 'awardTime',
           type: 'date',
-          additionalProps: { placeholder: '请选择获评时间' },
+          additionalProps: { 
+            placeholder: '请选择获评时间',
+            shouldUpdateValueImmediately: true,
+          },
           formProps: {
             showRightArrow: true,
           },
@@ -274,11 +280,16 @@ const formOptions : IDynamicFormOptions = {
 };
 
 const checkItemList = ref<CheckItemInfo[]>([]);
+let checkItemMap = new Map<number, CheckItemInfo>();
 
 const totalPoints = computed(() => {
   if (!currentForm.value) 
     return 0;
   return currentFormCheckItems.value
+    .filter((item) => {
+      const checkItem = checkItemMap.get(item.id);
+      return checkItem && !checkItem.isTitle;
+    })
     .reduce((acc, item) => acc + (item.count * item.points), 0)
     - (currentForm.value.deductPoints ?? 0);
 });
@@ -297,10 +308,19 @@ async function loadBasicInfo() {
 }
 async function loadCheckItems() {
   assertNotNull(currentForm.value, 'currentForm is null');
-  checkItemList.value = await AssessmentContentApi.getCheckItems(0);
-  checkItemList.value.concat(await AssessmentContentApi.getCheckItems(Number(currentForm.value.level)));
+  const { top, map } = await AssessmentContentApi.getCheckItems(Number(currentForm.value.level));
+  checkItemList.value = top;
+  checkItemMap = map;
   currentFormCheckItems.value = currentForm.value.checkItems.concat();
 }
+async function loadAnnexList() {
+  assertNotNull(currentForm.value, 'currentForm is null');
+  console.log('awardTime', currentForm.value.awardTime);
+  const annexList = await AssessmentContentApi.getAnnexList(currentForm.value.id);
+  setTimeout(() => {
+    uploaderRef.value!.setList(annexList.data.map((item) => stringUrlToUploaderItem(item.url)));
+  }, 1000);
+}
 
 const submitLoading = ref(false);
 
@@ -367,6 +387,7 @@ const loader = useSimpleDataLoader(async () => {
     const detail = await AssessmentContentApi.getSelfAssessmentDetail(querys.value.id);
     currentForm.value = detail;
     await loadCheckItems();
+    await loadAnnexList();
     return;
   }
   const list = await AssessmentContentApi.getSelfAssessmentList({
@@ -376,7 +397,7 @@ const loader = useSimpleDataLoader(async () => {
   if (list.data.length > 0) {
     const detail = await AssessmentContentApi.getSelfAssessmentDetail(list.data[0].id);
     currentForm.value = detail;
-    console.log('load', currentForm.value);
+    await loadAnnexList();
     await loadCheckItems();
   } else {
     currentForm.value = null;