Procházet zdrojové kódy

📦 对接盖章版存档扫描图片上传

快乐的梦鱼 před 3 týdny
rodič
revize
a4372cce76

+ 2 - 0
src/api/collect/AssessmentContent.ts

@@ -508,6 +508,7 @@ export class SelfAssessmentDetail extends DataModel<SelfAssessmentDetail> {
   awardTime = dayjs();
   deductContent = '' as string|null;
   deductPoints = 0 as number;
+  scan = '' as string|null;
   points = 0 as number;
   /** 进度:
 0=草稿,
@@ -623,6 +624,7 @@ export class AgreementDetail extends DataModel<AgreementDetail> {
   idCard = '' as string|null;
   health = '' as string|null;
   ich = '' as string|null;
+  scan = '' as string|null;
   partyASign = '' as string|null;
   partyBSign = '' as string|null;
   unitSign = '' as string|null;

+ 3 - 0
src/assets/scss/fix.scss

@@ -80,6 +80,9 @@
   overflow-y: scroll!important;
   font-size: 1rem;
 }
+.ant-upload-select-picture-card {
+  overflow: hidden;
+}
 
 @media screen and (max-width: 768px) {
   .dynamic-form-group {

+ 9 - 0
src/assets/scss/main.scss

@@ -285,6 +285,15 @@ $small-banner-height: 445px;
   margin: 0 auto;
 }
 
+.small-preview {
+  width: 300px!important;
+  height: 200px!important;
+  border: 1px solid #eeeeee;
+  border-radius: 5px;
+  overflow: hidden;
+  object-fit: cover;
+}
+
 //Card box
 
 @media (max-width: 1280px) {

+ 2 - 2
src/pages/collect/assessment/argeement-sign-review.vue

@@ -81,8 +81,8 @@
                         v-model:value="rejectReason"
                         allow-clear
                         placeholder="请输入回退原因"
-                        rows="3"
-                        maxlength="500"
+                        :rows="3"
+                        :maxlength="500"
                         show-count
                       />
                     </a-form-item>

+ 7 - 1
src/pages/collect/assessment/argeement-sign.vue

@@ -22,10 +22,11 @@
             </a-result>
             <div v-else>
               <a-alert type="info" message="请仔细阅读传承协议,确保您已理解内容,并签署传承协议。" class="mb-4" show-icon />
-              
+            
               <AgreementFormDisplay
                 ref="agreementFormRef"
                 :currentAgreement="(currentAgreement as AgreementDetail)"
+                :isReviewer="isReviewer"
               />
 
               <a-alert
@@ -85,6 +86,7 @@ import { isInMiniProgram } from '@/composeables/MiniProgramIng.ts';
 import { getFormErrorFieldsMessage } from '@/common/Form.ts';
 import AgreementFormDisplay from './components/AgreementFormDisplay.vue';
 import AssessmentContentApi, { AgreementDetail } from '@/api/collect/AssessmentContent';
+import { useReview } from './composeables/Review.ts';
 
 function formatErr(e: unknown): string {
   if (e instanceof RequestApiError)
@@ -104,6 +106,10 @@ const queryUserId = computed(() => Number(route.query.userId) || 0);
 const currentAgreement = ref<AgreementDetail | null>(null);
 const agreementFormRef = ref<InstanceType<typeof AgreementFormDisplay> | null>(null);
 
+const currentProgress = computed(() => currentAgreement.value?.progress ?? 0);
+const { currentUserGroup, isDistrict, isEndLevel, isProtectUnit } = useReview(currentProgress);
+const isReviewer = computed(() => isDistrict.value || isEndLevel.value || isProtectUnit.value);
+
 async function loadBasicInfo() {
   const uid = authStore.userInfo?.id ?? authStore.userId;
   const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);

+ 30 - 2
src/pages/collect/assessment/components/AgreementFormDisplay.vue

@@ -60,6 +60,17 @@
           />
           <span v-else>待审核人员签名</span>
         </a-form-item>
+        <a-form-item v-if="isReviewer" label="盖章版存档扫描图片" name="scan">
+          <a-image v-if="currentAgreement.scan" :src="currentAgreement.scan" class="small-preview" alt="avatar" />
+          <a-upload
+            class="flex flex-row items-center gap-2 mt-2"
+            list-type="picture-card"
+            :show-upload-list="false"
+            :custom-request="(options: any) => handleScanUpload(options)"
+          >
+            <UploadOutlined /> 上传
+          </a-upload>
+        </a-form-item>
 
         <a-divider />
 
@@ -102,11 +113,14 @@ import AgreementBodyProvincial from './AgreementBodyProvincial.vue';
 import AgreementBodyMunicipal from './AgreementBodyMunicipal.vue';
 import AgreementBody from './AgreementBody.vue';
 import { Sign } from '@imengyu/vue-dynamic-form-rich';
-import { computed, ref } from 'vue';
-import type { FormInstance } from 'ant-design-vue';
+import { computed, handleError, ref } from 'vue';
+import { message, type FormInstance, type UploadProps } from 'ant-design-vue';
 import { useImageSimpleUploadCo } from '@/common/upload/ImageUploadCo.ts';
 import type { Rules } from 'async-validator';
 import { LEVEL_NATIONAL, LEVEL_PROVINCE, LEVEL_DISTRICT } from '@/api/collect/AssessmentConsts.ts';
+import CommonContent from '@/api/CommonContent.ts';
+import { StringUtils } from '@imengyu/imengyu-utils';
+import { UploadOutlined } from '@ant-design/icons-vue';
 
 const props = withDefaults(defineProps<{
   currentAgreement: AgreementDetail;
@@ -233,6 +247,20 @@ const formRules = computed<Rules>(() => {
   return rules;
 });
 
+async function handleScanUpload(options: Parameters<NonNullable<UploadProps['customRequest']>>[0]) {
+  const { file, onSuccess, onError } = options;
+  try {
+    const res = await CommonContent.uploadSmallFile(file as File, 'image', 'file');
+    onSuccess?.({
+      url: res.fullurl,
+      name: StringUtils.path.getFileName(res.fullurl),
+    });
+    props.currentAgreement!.scan = res.fullurl;
+  } catch (err) {
+    onError?.(err as Error);
+    message.error(err instanceof Error ? err.message : String(err));
+  }
+}
 async function validate() {
   await formRef.value?.validate()
 }

+ 1 - 1
src/pages/collect/assessment/composeables/Review.ts

@@ -26,7 +26,7 @@ export function useReview(sourceProgress?: ComputedRef<number>) {
   const isDistrict = computed(() => {
     return (
       currentUserGroup.value?.id === GROUP_DISTRICT_FI
-      || currentUserGroup.value?.id === GROUP_DISTRICT_FI
+      || currentUserGroup.value?.id === GROUP_DISTRICT_ST
       || currentUserGroup.value?.id === GROUP_DISTRICT_ND
       || currentUserGroup.value?.id === GROUP_DISTRICT_RD
       || currentUserGroup.value?.id === GROUP_DISTRICT_TH

+ 7 - 5
src/pages/collect/assessment/evaluation-form-list-block.vue

@@ -25,10 +25,12 @@
     :show-detail="handleReviewSelfAssessment"
   >
     <template #headLeft>
-      <div class="flex flex-row items-center gap-2">
-        <a-button type="primary" @click="openDownloadModal">打包下载</a-button>
-        <a-button @click="openExportExcelModal">导出Excel</a-button>
-        <a-button @click="openSummaryDownloadModal">审核结果汇总下载</a-button>
+      <div class="flex flex-col gap-2">
+        <div class="flex flex-row items-center gap-2">
+          <a-button type="primary" @click="openDownloadModal">打包下载</a-button>
+          <a-button @click="openExportExcelModal">导出Excel</a-button>
+          <a-button @click="openSummaryDownloadModal">审核结果汇总下载</a-button>
+        </div>
         <span>{{ progressHint }}</span>
       </div>
     </template>
@@ -332,7 +334,7 @@ async function handleSummaryDownload() {
   summaryDownloading.value = true;
   try {
     const level = summaryLevel.value > 0 ? String(summaryLevel.value) : undefined;
-    const region = summaryRegion.value?.trim() || undefined;
+    const region = summaryRegion.value || undefined;
     await AssessmentContentApi.downloadSummaryPdf({
       year: summaryYear.value,
       level,

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

@@ -36,9 +36,20 @@
               :current-form-check-items="(currentFormCheckItems as SelfAssessmentCheckItemAnswer[])"
               :readonly="false"
             />
-            <template v-if="isReviewer">
+            <template v-if="isReviewer && currentForm">
+              
+              <a-image v-if="currentForm.scan" :src="currentForm.scan" class="small-preview" alt="avatar" />
+              <a-upload
+                class="flex flex-row items-center gap-2 mt-2"
+                list-type="picture-card"
+                :show-upload-list="false"
+                :custom-request="(options: any) => handleScanUpload(options)"
+              >
+                <UploadOutlined /> 上传
+              </a-upload>
+
               <a-divider />
-              <a-button type="primary" block :loading="submitLoading" @click="reviewModalVisible = true">编辑审核意见</a-button>
+              <a-button type="primary" block :loading="submitLoading" @click="reviewModalVisible = true">过会后编辑分数和意见</a-button>
             </template>
             <a-divider />
             <a-space direction="vertical" class="w-full" size="middle">
@@ -62,7 +73,7 @@
       <iframe :src="previewUrl" class="w-full h-full" style="min-height: 60vh"></iframe>
     </a-modal>
     <a-modal
-      title="编辑审核意见"
+      title="过会后编辑分数和意见"
       v-model:visible="reviewModalVisible"
       :confirm-loading="reviewSubmitting"
       ok-text="提交审核"
@@ -103,8 +114,9 @@
 <script setup lang="ts">
 import { computed, h, onMounted, ref, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
-import { message, Modal } from 'ant-design-vue';
-import { waitTimeOut } from '@imengyu/imengyu-utils';
+import { message, Modal, type UploadProps } from 'ant-design-vue';
+import { UploadOutlined } from '@ant-design/icons-vue';
+import { StringUtils, waitTimeOut } from '@imengyu/imengyu-utils';
 import { RequestApiError } from '@imengyu/imengyu-utils';
 import { ArrowLeftOutlined } from '@ant-design/icons-vue';
 import { useAuthStore } from '@/stores/auth';
@@ -121,6 +133,7 @@ import { getFormErrorFieldsMessage } from '@/common/Form.ts';
 import { LEVEL_NATIONAL, LEVEL_PROVINCE, LEVEL_DISTRICT } from '@/api/collect/AssessmentConsts.ts';
 import { useReview } from './composeables/Review.ts';
 import { GROUP_LABELS, GROUP_TO_REVIEW_PROGRESS } from './composeables/GroupData.ts';
+import CommonContent from '@/api/CommonContent.ts';
 
 
 function formatErr(e: unknown): string {
@@ -382,6 +395,20 @@ const rejectTypeForGroup = computed(() => {
   return GROUP_TO_REVIEW_PROGRESS[gid]?.rejectTarget || 0;
 });
 
+async function handleScanUpload(options: Parameters<NonNullable<UploadProps['customRequest']>>[0]) {
+  const { file, onSuccess, onError } = options;
+  try {
+    const res = await CommonContent.uploadSmallFile(file as File, 'image', 'file');
+    onSuccess?.({
+      url: res.fullurl,
+      name: StringUtils.path.getFileName(res.fullurl),
+    });
+    currentForm.value!.scan = res.fullurl;
+  } catch (err) {
+    onError?.(err as Error);
+    message.error(err instanceof Error ? err.message : String(err));
+  }
+}
 async function handleReviewSubmit() {
   const cf = currentForm.value;
   if (!cf?.id) {