Browse Source

Merge remote-tracking branch 'origin/master'

wwj 4 months ago
parent
commit
6cb9754a4d

BIN
public/favicon.ico


BIN
public/logo.gif


+ 4 - 1
src/api/museums/specimeninfo/index.ts

@@ -40,7 +40,10 @@ export const SpecimenInfoApi = {
   getSpecimenInfoPage: async (params: any) => {
     return await request.get({ url: `/museums/specimen-info/page`, params })
   },
-
+//查询标本记录
+  getStustusInfo: async (specimenId: any) => {
+    return await request.get({ url: `/museums/specimen-info/logs?specimenId=` + specimenId + '&moduleType=MUSEUMS 标本'})
+  },
   // 查询标本管理详情
   getSpecimenInfo: async (id: number) => {
     return await request.get({ url: `/museums/specimen-info/get?id=` + id })

+ 7 - 6
src/api/museums/specimenoutbound/index.ts

@@ -5,7 +5,7 @@ export interface SpecimenOutboundVO {
   id: number // 主键
   infoId: string // 关联到总表中的标本ID
   chineseName: string // 中文名称
-  specimenNumber: string // 申请出库的标本编号
+  number: string // 申请出库的标本编号
   applicantName: string // 申请人或申请单位
   applicationDate: Date // 申请日期
   applicationUsage: string // 申请出库的用途
@@ -21,14 +21,15 @@ export interface SpecimenOutboundVO {
   specimenCondition: string // 标本情况
   sampleStatus: number // 标本状态(已出库、已回库、出库审批中)
   estimatedReturnTime:Date//预计退还日期
-  approvalTime:Date//一审时间
+  approvalTime?:Date//一审时间
   twoApprovalTime:Date//二审时间
   expectedCollectionTime:Date//预计领取时间
-  rejectionReasons: string//二审驳回原因
-  approveUsers:string//一审批者
-  twoApproveUsers:  string//二审批者
+  rejectionReasons?: string//二审驳回原因
+  approveUsers?:string//一审批者
+  twoApproveUsers?:  string//二审批者
   startTime: Date//项目开始时间
 
+
 }
 
 // 标本出库回库信息 API
@@ -66,7 +67,7 @@ export const SpecimenOutboundApi = {
   // ApprovalPassSpecimenOutbound: async (data: SpecimenOutboundVO) => {
   //   return await request.post({ url: `/museums/specimen-outbound/approve`, data })
   // },
-  // 审批驳回标本出库回库
+  //审批驳回标本出库回库
  ApprovalSpecimenOutbound: async (data: SpecimenOutboundVO) => {
     return await request.post({ url: `/museums/specimen-outbound/approve`, data })
   },

BIN
src/assets/svgs/login.1.jpg


BIN
src/assets/svgs/login2.jpg


+ 1 - 0
src/layout/components/Footer/src/Footer.vue

@@ -16,6 +16,7 @@ const title = computed(() => appStore.getTitle)
 
 <template>
   <div
+    style="line-height: 12px"
     :class="prefixCls"
     class="h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)] overflow-hidden"
   >

+ 1 - 1
src/locales/zh-CN.ts

@@ -114,7 +114,7 @@ export default {
     small: '小'
   },
   login: {
-    welcome: '欢迎使用本系统',
+    welcome: '欢迎使用地质博物馆标管理系统',
     message: '开箱即用的中后台管理系统',
     tenantname: '租户名称',
     username: '用户名',

+ 6 - 6
src/views/Home/Index.vue

@@ -136,23 +136,23 @@
       <el-row>
         <el-col :xl="9" :lg="9" :md="12" :sm="24" :xs="24">
 
-          <el-card shadow="hover" class="mr-5px mt-5px" style="height: 410px;">
+          <el-card shadow="hover" class="mr-5px" style="height: 390px;">
             <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>历年标本数量统计</span></div>
 
             <el-skeleton :loading="loading" animated>
-              <Echart :options="yearOptionsData" :height="340" />
+              <Echart :options="yearOptionsData" :height="320" />
             </el-skeleton>
           </el-card>
           <!-- 历年标本来源增减统计 -->
 
         </el-col>
         <el-col :xl="9" :lg="9" :md="12" :sm="24" :xs="24">
-          <el-card shadow="hover" class="mr-5px mt-5px" style="height: 410px;">
+          <el-card shadow="hover" class="mr-5px " style="height: 390px;">
 
             <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>历年标本来源增减统计</span></div>
 
             <el-skeleton :loading="loading" animated>
-              <Echart :options="originOptionsData" :height="340" />
+              <Echart :options="originOptionsData" :height="320" />
             </el-skeleton>
           </el-card>
           <!-- 历年标本来源增减统计 -->
@@ -161,9 +161,9 @@
         <!-- 本年标本回库 -->
         <el-col :xl="6" :lg="6" :md="24" :sm="24" :xs="24">
 
-          <el-card shadow="hover" class="mr-5px mt-5px" style="height: 410px;">
+          <el-card shadow="hover" class="mr-5px " style="height: 390px;">
             <div class="title"><img src="/@/assets/imgs/tj3.png" alt="" /><span>按标本类型统计</span></div>
-            <Echart :options="pieOptionsData" :height="340" />
+            <Echart :options="pieOptionsData" :height="320" />
 
           </el-card>
         </el-col>

+ 18 - 15
src/views/Login/Login.vue

@@ -10,17 +10,20 @@
 
         <!-- 左边的背景图 + 欢迎语 -->
         <div class="h-[calc(100%-60px)] flex items-center justify-center">
-          <TransitionGroup
-            appear
-            enter-active-class="animate__animated animate__bounceInLeft"
-            tag="div"
-          >
-            <img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
+<!--          <TransitionGroup-->
+<!--            appear-->
+<!--            enter-active-class="animate__animated animate__bounceInLeft"-->
+<!--            tag="div"-->
+<!--          >-->
+            <!-- 背景图 -->
+            <img key="1" alt="" class="w-350px" style="width:800px" src="../../assets/svgs/login.1.jpg" />
+            <!-- 欢迎语 -->
             <div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div>
-            <div key="3" class="mt-5 text-14px font-normal text-white">
-              {{ t('login.message') }}
-            </div>
-          </TransitionGroup>
+            <!-- 消息 -->
+<!--            <div key="3" class="mt-5 text-14px font-normal text-white">-->
+<!--              {{ t('login.message') }}-->
+<!--            </div>-->
+<!--          </TransitionGroup>-->
         </div>
       </div>
       <div
@@ -48,13 +51,13 @@
             <!-- 账号登录 -->
             <LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 手机登录 -->
-            <MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 二维码登录 -->
-            <QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
+<!--            <MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
+<!--            &lt;!&ndash; 二维码登录 &ndash;&gt;-->
+<!--            <QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
             <!-- 注册 -->
             <RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 三方登录 -->
-            <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
+<!--            <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
           </div>
         </Transition>
       </div>
@@ -69,7 +72,7 @@ import { useAppStore } from '@/store/modules/app'
 import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
 import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
 
-import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue } from './components'
+import { LoginForm,  RegisterForm,  } from './components'
 
 defineOptions({ name: 'Login' })
 

+ 52 - 52
src/views/Login/components/LoginForm.vue

@@ -16,15 +16,15 @@
         </el-form-item>
       </el-col>
       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
-          <el-input
-            v-model="loginData.loginForm.tenantName"
-            :placeholder="t('login.tenantNamePlaceholder')"
-            :prefix-icon="iconHouse"
-            link
-            type="primary"
-          />
-        </el-form-item>
+<!--        <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">-->
+<!--          <el-input-->
+<!--            v-model="loginData.loginForm.tenantName"-->
+<!--            :placeholder="t('login.tenantNamePlaceholder')"-->
+<!--            :prefix-icon="iconHouse"-->
+<!--            link-->
+<!--            type="primary"-->
+<!--          />-->
+<!--        </el-form-item>-->
       </el-col>
       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
         <el-form-item prop="username">
@@ -82,49 +82,49 @@
         mode="pop"
         @success="handleLogin"
       />
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <el-row :gutter="5" justify="space-between" style="width: 100%">
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnMobile')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.MOBILE)"
-              />
-            </el-col>
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnQRCode')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.QR_CODE)"
-              />
-            </el-col>
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnRegister')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.REGISTER)"
-              />
-            </el-col>
-          </el-row>
-        </el-form-item>
-      </el-col>
-      <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <div class="w-[100%] flex justify-between">
-            <Icon
-              v-for="(item, key) in socialList"
-              :key="key"
-              :icon="item.icon"
-              :size="30"
-              class="anticon cursor-pointer"
-              color="#999"
-              @click="doSocialLogin(item.type)"
-            />
-          </div>
-        </el-form-item>
-      </el-col>
+<!--      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">-->
+<!--        <el-form-item>-->
+<!--          <el-row :gutter="5" justify="space-between" style="width: 100%">-->
+<!--            <el-col :span="8">-->
+<!--              <XButton-->
+<!--                :title="t('login.btnMobile')"-->
+<!--                class="w-[100%]"-->
+<!--                @click="setLoginState(LoginStateEnum.MOBILE)"-->
+<!--              />-->
+<!--            </el-col>-->
+<!--            <el-col :span="8">-->
+<!--              <XButton-->
+<!--                :title="t('login.btnQRCode')"-->
+<!--                class="w-[100%]"-->
+<!--                @click="setLoginState(LoginStateEnum.QR_CODE)"-->
+<!--              />-->
+<!--            </el-col>-->
+<!--            <el-col :span="8">-->
+<!--              <XButton-->
+<!--                :title="t('login.btnRegister')"-->
+<!--                class="w-[100%]"-->
+<!--                @click="setLoginState(LoginStateEnum.REGISTER)"-->
+<!--              />-->
+<!--            </el-col>-->
+<!--          </el-row>-->
+<!--        </el-form-item>-->
+<!--      </el-col>-->
+<!--      <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>-->
+<!--      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">-->
+<!--        <el-form-item>-->
+<!--          <div class="w-[100%] flex justify-between">-->
+<!--            <Icon-->
+<!--              v-for="(item, key) in socialList"-->
+<!--              :key="key"-->
+<!--              :icon="item.icon"-->
+<!--              :size="30"-->
+<!--              class="anticon cursor-pointer"-->
+<!--              color="#999"-->
+<!--              @click="doSocialLogin(item.type)"-->
+<!--            />-->
+<!--          </div>-->
+<!--        </el-form-item>-->
+<!--      </el-col>-->
 <!--      <el-divider content-position="center">萌新必读</el-divider>-->
 <!--      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">-->
 <!--        <el-form-item>-->

+ 1 - 2
src/views/museums/museumsdocument/index.vue

@@ -305,8 +305,7 @@ const handleExport = async () => {
       console.log("photo",photo)
       const response = await fetch(photo.url);
       const blob = await response.blob();
-      const extension = photo.url.split('.').pop() // 获取文件扩展名
-      zip.file(`${photo.id}.${extension}`, blob)
+      zip.file(`${photo.name}`, blob)
     });
 
     await Promise.all(promises);

+ 1 - 0
src/views/museums/photogroup/PhotosIndex.vue

@@ -238,6 +238,7 @@ const handleExport = async () => {
 
     const zip = new JSZip();
     const promises = selectedPhotos.value.map(async (photo) => {
+      console.log("photo",photo)
       const response = await fetch(photo.photoUrl);
       const blob = await response.blob();
       const extension = photo.photoUrl.split('.').pop() // 获取文件扩展名

+ 1 - 1
src/views/museums/specimeninfo/SpecimenImportForm.vue

@@ -81,7 +81,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
 const formLoading = ref(false) // 表单的加载中
 const uploadRef = ref()
 const uploadimageRef = ref()
-// const uploadimageRef = ref()
+
 const message = useMessage() // 消息弹窗
 import axios from 'axios';
 const updateSupport = ref(0) // 是否更新已经存在的用户数据

+ 286 - 4
src/views/museums/specimeninfo/index.vue

@@ -323,7 +323,7 @@
           @click="handleImport"
           v-hasPermi="['museums:specimen-info:import']"
         >
-          <Icon icon="ep:upload" /> 批量导入
+          <Icon icon="ep:download" /> 批量导入
         </el-button>
         <el-button
           type="success"
@@ -331,7 +331,15 @@
           @click="handleExport"
           :loading="exportLoading"
         >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
+          <Icon icon="ep:upload" class="mr-5px" /> 导出excel
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          @click="labelExport"
+          :loading="exportLoading"
+        >
+          <Icon icon="ep:upload" class="mr-5px" /> 制作标签
         </el-button>
       </el-form-item>
     </el-form>
@@ -341,9 +349,10 @@
   <ContentWrap>
     <el-segmented  block  v-model="queryParams.specimenType" :options="options" size="large"  @update:modelValue="handleSegmentedChange"/>
 
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" />
 <!--      <el-table-column label="序号" align="center" prop="id" />-->
+
       <el-table-column label="标本编号" align="center" prop="specimenNumber" />
       <el-table-column label="中文名称" align="center" prop="chineseName" />
 
@@ -456,6 +465,54 @@
     />
   </ContentWrap>
 
+<!--  制作标签弹窗-->
+  <el-dialog v-model="dialogTableVisible" title="标签制作" width="800">
+    <div>
+      <el-form-item label="尺寸">
+        <el-radio-group v-model="formData.size">
+          <el-radio :value="1">90:54</el-radio>
+          <el-radio :value="2">90:50</el-radio>
+          <el-radio :value="3">5:5</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form :model="formData" label-width="auto" style="max-width: 750px">
+        <el-form-item label="标题">
+          <el-select v-model="formData.content" placeholder="请选择字段">
+            <el-option
+              v-for="item in optionsLabel"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+
+        <div v-for="(item, index) in formData.contentItems" :key="index" style="margin-top: 10px">
+          <el-form-item>
+            <div style="float: left; margin-right: 5px">
+              <el-button type="primary" :icon="Delete" size="small" @click="removeContentItem(index)" />
+            </div>
+            <div style="float: left">内容{{ index + 1 }}:</div>
+            <el-select v-model="item.value" placeholder="请选择字段" style="float: left; width: 40%">
+              <el-option
+                v-for="opt in optionsLabel"
+                :key="opt.value"
+                :label="opt.label"
+                :value="opt.value"
+              />
+            </el-select>
+          </el-form-item>
+
+        </div>
+
+        <el-form-item>
+          <el-button type="success" @click="addContentItem">增加内容</el-button>
+          <el-button type="primary" @click="onSubmit">导出图片</el-button>
+          <el-button @click="dialogTableVisible = false">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </el-dialog>
   <!-- 表单弹窗:添加/修改 -->
   <SpecimenInfoForm ref="formRef" @success="getList" />
   <SpecimenImportForm ref="importFormRef" @success="getList" />
@@ -464,12 +521,15 @@
 
 <script setup lang="ts">
 import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
+import { Plus,Delete } from '@element-plus/icons-vue'
 import {dateFormatter, formatDate} from '@/utils/formatTime'
 import download from '@/utils/download'
 import { SpecimenInfoApi, SpecimenInfoVO } from '@/api/museums/specimeninfo'
 import SpecimenInfoForm from './SpecimenInfoForm.vue'
 import SpecimenImportForm from './SpecimenImportForm.vue'
 import  ImageImportForm from './imageImportForm.vue'
+import JSZip from 'jszip';
+import { saveAs } from 'file-saver';
 /** 标本管理 列表 */
 defineOptions({ name: 'SpecimenInfo' })
 
@@ -520,6 +580,18 @@ const queryParams = reactive({
   operator: undefined,
   entryDate: []
 })
+//标签打印的表单
+const formData = ref({
+  content: undefined,
+  selectedData: [] as SpecimenInfoVO[],
+  contentItems: [] as { label: string; value: string }[],
+  size:1,
+  sizeMap: {
+    1: { width: 900, height: 540 },
+    2: { width: 900, height: 500 },
+    3: { width: 500, height: 500 }
+  }
+})
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 
@@ -613,6 +685,134 @@ const handleExport = async () => {
     exportLoading.value = false
   }
 }
+//字段类型
+const optionsLabel = [
+
+  {
+    value: 'chineseName',
+    label: '中文名称',
+  },
+  {
+    value: 'specimenNumber',
+    label: '标本编号',
+  },
+  {
+    value: 'specimenType',
+    label: '标本类型',
+  },
+  {
+    value: 'storageLocation',
+    label: '存放位置',
+  },
+  {
+    value: 'englishName',
+    label: '英文名称',
+  },
+
+  {
+    value: 'composition',
+    label: '成分',
+  },
+  {
+    value: 'origin',
+    label: '产地',
+  },
+  {
+    value: 'era',
+    label: '时代',
+  },
+  {
+    value: 'preservedLayer',
+    label: '保存地层',
+  },
+  {
+    value: 'meteoriteType',
+    label: '陨石类型',
+  },
+
+  {
+    value: 'internationalName',
+    label: '国际命名',
+  },
+  {
+    value: 'preservationType',
+    label: '保存类型',
+  },
+  {
+    value: 'size',
+    label: '尺寸',
+  },
+  {
+    value: 'preservationType',
+    label: '保存类型',
+  },
+  {
+    value: 'weight',
+    label: '重量',
+  },
+  {
+    value: 'source',
+    label: '来源',
+  },
+  {
+    value: 'provider',
+    label: '标本提供者',
+  },
+
+  {
+    value: 'purpose',
+    label: '用途',
+  },
+
+  {
+    value: 'acquisitionTime',
+    label: '入藏时间',
+  },
+
+]
+
+
+
+const selectedlabel = ref<any[]>([]) // 选中的标本
+const dialogTableVisible = ref(false) //弹窗开关
+//标签内容新增
+const addContentItem = () => {
+  if (formData.value.contentItems.length >= 4) {
+    message.warning('最多添加4个标签内容')
+    return
+  }
+  formData.value.contentItems.push({ label: '', value: '' })
+}
+//标签内容删除
+const removeContentItem = (index: number) => {
+  formData.value.contentItems.splice(index, 1)
+}
+/** 处理选中变化 */
+const handleSelectionChange = (selection: any[]) => {
+  selectedlabel.value = selection
+}
+/** 导出标签操作 */
+const labelExport = async () => {
+  try {
+    // 导出的二次确认
+    await message.exportConfirm()
+    // 发起导出
+    exportLoading.value = true
+    if (selectedlabel.value.length === 0) {
+      message.warning('请勾选要制作标签的标本')
+      return
+    }
+    dialogTableVisible.value = true
+    // 将选中的数据传递给对话框
+    formData.value.selectedData = selectedlabel.value
+
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+
 
 /** 初始化 **/
 onMounted(() => {
@@ -623,6 +823,7 @@ onMounted(() => {
 /** 查看标本详情页面 **/
 import { useRouter } from 'vue-router';
 import UserImportForm from "@/views/system/user/UserImportForm.vue";
+import {ref} from "vue";
 const router = useRouter();
 const viewDetails=(dataId: number) => {
   router.push({
@@ -637,5 +838,86 @@ const handleSegmentedChange = (value: string) => {
   queryParams.specimenType = value
   handleQuery()
 }
+
+//标签导出
+const onSubmit = async () => {
+  try {
+    const selectedData = formData.value.selectedData
+    const labels = selectedData.map(item => {
+      const content = formData.value.contentItems
+        .map(contentItem => {
+          const label = optionsLabel.find(opt => opt.value === contentItem.value)?.label
+          const value = item[contentItem.value]
+          if (value !== null && value !== undefined && value !== '') {
+            return `${label}:${value}`
+          }
+          return ''
+        })
+        .filter(Boolean) // 过滤掉空字符串
+        .join('\n') // 每个内容项换行
+
+      return {
+        title: item[formData.value.content],
+        content: content,
+        specimenNumber: item.specimenNumber
+      }
+    })
+    const zip = new JSZip();
+    // 生成标签图片的逻辑
+    for (const label of labels) {
+      console.log('label',label)
+      const canvas = document.createElement('canvas')
+      const ctx = canvas.getContext('2d')
+      if (ctx) {
+        const size = formData.value.sizeMap[formData.value.size]
+        canvas.width = size.width
+        canvas.height = size.height
+        ctx.fillStyle = 'white'
+        ctx.fillRect(0, 0, canvas.width, canvas.height)
+
+        // 设置文本对齐方式为居中
+        ctx.textAlign = 'center'
+        // 设置字体样式
+        ctx.font = `${Math.min(size.width, size.height) * 0.2}px Arial`
+        // 设置文本颜色
+        ctx.fillStyle = 'black'
+        // 绘制标题
+        ctx.fillText(label.title, canvas.width / 2, size.height * 0.3)
+
+        // 设置文本对齐方式为左对齐
+        ctx.textAlign = 'left'
+        // 设置字体样式
+        ctx.font = `${Math.min(size.width, size.height) * 0.08}px Arial`
+        // 绘制内容
+        const lines = label.content.split('\n')
+        let y = size.height * 0.45
+        for (const line of lines) {
+          ctx.fillText(line, size.width * 0.15, y)
+          y += Math.min(size.width, size.height) * 0.15 // 每行间隔
+        }
+
+        // 将画布转换为数据URL
+        const dataUrl = canvas.toDataURL('image/png')
+        // 将数据URL转换为Blob
+        const response = await fetch(dataUrl)
+        const blob = await response.blob()
+
+        // 将Blob添加到压缩文件中
+        zip.file(`${label.specimenNumber}.png`, blob)
+      }
+    }
+    // 生成压缩文件
+    const content = await zip.generateAsync({ type: 'blob' });
+    // 下载压缩文件
+    saveAs(content, 'labels.zip');
+    message.success('标签图片已导出')
+  } catch (error) {
+    message.error('导出失败')
+  } finally {
+    dialogTableVisible.value = false
+  }
+}
 </script>
 
+
+

+ 29 - 56
src/views/museums/specimeninfo/specimenDetails.vue

@@ -156,22 +156,22 @@
           </div>
         </template>
         <el-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24" class="mb-8px">
-          <div class="demo-image__preview" v-if="specimenData ">
+          <div v-if="specimenData.imagePath && specimenData.imagePath.length">
+          <div class="demo-image__preview">
             <el-image
-
               style=" align-items: center"
               :src="specimenData.imagePath[0]"
               :zoom-rate="1.2"
               :max-scale="7"
               :min-scale="0.2"
-              :preview-src-list="specimenData.imagePaths"
+              :preview-src-list="specimenData.imagePath"
               :initial-index="index"
               fit="cover"
             />
           </div>
 
           <div>
-            <el-space style="margin-top: 10px"  v-if="specimenData ">
+            <el-space style="margin-top: 10px" >
               <div class="demo-image__preview" v-for="(url, index) in specimenData.imagePath"
                    :key="index">
                 <el-image
@@ -185,8 +185,10 @@
                   fit="cover"
                 />
               </div>
-      </el-space>
+            </el-space>
+          </div>
           </div>
+            <el-empty v-else  description="暂无图片" />
         </el-col>
 
 
@@ -195,40 +197,23 @@
         <template #header>
           <div class="card-header" >
             <h1 class="text1" style="margin-bottom: 10px">馆藏状态</h1>
-            <span v-if="specimenRecord">{{collectionStatusText}}</span>
           </div>
         </template>
         <el-scrollbar height="600px">
           <el-timeline style="max-width: 600px;margin-left: 15px" >
             <el-timeline-item
-              v-for="(item, index) in specimenData"
+              v-for="(item, index) in stustuss"
               :key="index"
-              :timestamp="item.outgoingTime"
-              placement="top">
+              type="primary"
+              :hollow = "true"
+            >
+              <h4>{{formatDate(item.createTime) }}</h4>
               <el-card>
-                <p>{{  }}</p>
-                <p>状态:{{  }}</p>
-                <p>申请人或单位:{{item.applicantName }}</p>
-                <p>用途:{{item.applicationUsage}}</p>
-                <p>审批员:{{item.approveUsers}}</p>
-                <p>审批时间:{{}}</p>
-                <p>出库员:{{ item.operator }}</p>
+                <h4>操作状态:{{item.subType}}</h4>
+                <div style="line-height: 30px">操作人:{{ item.userName}}</div>
+                <div style="line-height: 20px">操作内容:{{item.action}}</div>
               </el-card>
             </el-timeline-item>
-            <el-timeline-item
-              v-for="(item, index) in specimenData"
-              :key="index"
-              :timestamp="item.acquisitionTime"
-              placement="top">
-              <el-card>
-                <p>回库</p>
-                <p>退还人:{{ item.operator }}</p>
-                <p>点收人:{{item.applicationUsage}}</p>
-                <p>退还日期:{{item.approveUsers}}</p>
-                <p>标本情况:{{}}</p>
-              </el-card>
-            </el-timeline-item>
-
           </el-timeline>
         </el-scrollbar>
       </el-card>
@@ -253,8 +238,10 @@ const router = useRouter() // 路由
 const {query} = useRoute() // 查询参数
 import type {ComponentSize} from 'element-plus'
 import {formatDate} from "@/utils/formatTime";
-
-
+const stustuss = ref()
+const specimenData = ref({
+  imagePath:[]
+}); // 初始化响应式数据
 const size = ref<ComponentSize>('large')
 
 
@@ -265,30 +252,26 @@ const srcList = ref([])
 
 
 
-let specimenData = ref(null); // 初始化响应式数据
+
 const fetchData = async () => {
+  loading.value = true
   try {
     console.log(query.dataId)
     let res: any = await SpecimenInfoApi.getSpecimenInfo(query.dataId)
+    const stutus = await SpecimenInfoApi.getStustusInfo(query.dataId)
+    stustuss.value = stutus
+    console.log('stustuss',stustuss.value)
     console.log(res)
     res.discoveryTime = formatDate(res.discoveryTime)
     res.fallTime = formatDate(res.fallTime)
     res.acquisitionTime = formatDate(res.acquisitionTime)
-
-
     specimenData.value = res
     console.log(specimenData.value)
   }  finally {
+    loading.value = false
   }
 }
 
-// // 在组件加载时获取数据
-onMounted(async () => {
-
-  await fetchData()
-
-
-})
 
 
 /**标本类型**/
@@ -325,20 +308,6 @@ const preservationTypeText = computed(() => {
       return '';
   }
 });
-/**来源**/
-const collectionStatusText = computed(() => {
-  if (specimenData.value === null) {
-    return '';
-  }
-  switch (specimenData.value.collectionStatus) {
-    case 0:
-      return '在馆';
-    case 1:
-      return '借出';
-    default:
-      return '';
-  }
-});
 /**馆藏状态(在馆、借出)**/
 const sourceText = computed(() => {
   if (specimenData.value === null) {
@@ -356,6 +325,10 @@ const sourceText = computed(() => {
   }
 });
 
+// // 在组件加载时获取数据
+onMounted(async () => {
+  await fetchData()
+})
 
 </script>
 

+ 1 - 1
src/views/museums/specimenoutbound/ApproalProcess.vue

@@ -113,7 +113,7 @@
       <el-table-column
         label="申请日期"
         align="center"
-        prop="applicationDate"
+        prop="createTime"
         :formatter="dateFormatter"
         width="180px"
       />

+ 11 - 11
src/views/museums/specimenoutbound/OutboundApplication.vue

@@ -130,7 +130,7 @@
               target="_blank"
             >预览</el-link
             >
-            <el-link v-else type="primary" download :href="row.url" :underline="false" target="_blank"
+            <el-link v-else type="primary" download :href="row.attachments" :underline="false" target="_blank"
             >下载</el-link
             >
             </div>
@@ -139,7 +139,7 @@
         <el-table-column
           label="申请日期"
           align="center"
-          prop="applicationDate"
+          prop="createTime"
           :formatter="dateFormatter"
           width="180px"
         />
@@ -199,7 +199,7 @@
               v-if="scope.row.status !== 0 && scope.row.status !== 1 && scope.row.status !== 3 && scope.row.status !== 4 && scope.row.status !== 5 "
               link
               type="success"
-              @click="handleInitiative(scope.row.id)"
+              @click="openForm('update', scope.row.id)"
 
             >
              修改申请
@@ -225,9 +225,9 @@
     </ContentWrap>
 
     <!-- 表单弹窗:编辑标本出库申请 -->
-
+<!--    <InitiativeProcessInstance ref="initiativeFormRef" @success="getList" />-->
     <SpecimenOutboundForm ref="formRef" @success="getList"/>
-    <InitiativeProcessInstance ref="initiativeFormRef" @success="getList" />
+<!--    <InitiativeProcessInstance ref="initiativeFormRef" @success="getList" />-->
   </el-skeleton>
 </template>
 
@@ -237,9 +237,9 @@ import {dateFormatter} from "@/utils/formatTime";
 import download from '@/utils/download'
 import {SpecimenOutboundApi, SpecimenOutboundVO} from '@/api/museums/specimenoutbound'
 import SpecimenOutboundForm from "@/views/museums/specimenoutbound/SpecimenOutboundForm.vue";
-import InitiativeProcessInstance from '@/views/museums/specimenoutbound/OutboundForm/InitiativeProcessInstance.vue'
+// import InitiativeProcessInstance from '@/views/museums/specimenoutbound/OutboundForm/InitiativeProcessInstance.vue'
 import {ref,} from "vue";
-import type { Action } from 'element-plus'
+// import type { Action } from 'element-plus'
 defineOptions({name: 'OutboundApplication'})
 import {getIntDictOptions, DICT_TYPE} from '@/utils/dict'
 
@@ -310,10 +310,10 @@ const openForm = (type: string, id?: number) => {
 
 /** 查看并修改出库申请*/
 
- const  initiativeFormRef = ref()
-const handleInitiative = (type: string, id?: number) => {
-  initiativeFormRef.value.open(type, id)
-}
+//  const  initiativeFormRef = ref()
+// const handleInitiative = (type: string, id?: number) => {
+//   initiativeFormRef.value.open(type, id)
+// }
 
 
 // const handleInitiative = async (id) => {

+ 28 - 33
src/views/museums/specimenoutbound/OutboundForm/ApproalDetails.vue

@@ -36,7 +36,7 @@
            <el-descriptions-item label="出库备注信息:" label-align="right" label-class-name="my-label">{{ outboundData.outboundRemarks}}</el-descriptions-item>
            </el-descriptions>
          </el-col>
-         <el-col :xl="3" :lg="3" :md="24" :sm="24" :xs="24" >
+         <el-col :xl="2" :lg="2" :md="24" :sm="24" :xs="24" >
            <el-descriptions
              v-if="outboundData"
              direction="vertical"
@@ -50,17 +50,11 @@
                align="center"
                label-class-name="my-label"
                class-name="my-content"
-             ><div class="demo-image__preview" v-if="outboundData" >
-               <el-image
-                 style="width: 40px; height:40px;margin-right: 5px"
-                 class="h-200px w-150px"
-                 lazy
-                 :src="outboundData.attachments"
-                 :preview-src-list="outboundData.attachments"
-                 preview-teleported
-                 fit="cover"
-               />
-             </div>
+             >
+               <div class="demo-image__preview" v-if="outboundData" >
+
+
+               </div>
              </el-descriptions-item>
            </el-descriptions>
 <!--           <div class="demo-image__preview" v-if="outboundData ">-->
@@ -137,18 +131,20 @@
            </el-table-column>
          </el-table>
          <!-- 分页 -->
-         <Pagination
-           :total="total"
-           v-model:page="queryParams.pageNo"
-           v-model:limit="queryParams.pageSize"
-           @pagination="getList"
-         />
+<!--         <Pagination-->
+<!--           :total="total"-->
+<!--           v-model:page="queryParams.pageNo"-->
+<!--           v-model:limit="queryParams.pageSize"-->
+<!--           @pagination="getList"-->
+<!--         />-->
+         <div v-if="outboundData.status === 0 || outboundData.status === 1 ">
          <el-button
            @click="handleApproval"
            type="primary"
            style="float: right; margin-left: 20px;margin-top: 20px;margin-bottom: 20px"
          > 审批
          </el-button>
+         </div>
        </el-card>
      </el-col>
 
@@ -174,23 +170,23 @@ const {query} = useRoute() // 查询参数
 const loading = ref(true) // 列表的加载中
 const list = ref<SpecimenOutboundVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  infoId: undefined,
-  chineseName: undefined,
-  specimenNumber: undefined,
-  attachments: undefined,
-  processInstanceId: undefined,
-  specimenCondition: undefined,
-  imagePath:undefined
-})
+// const queryParams = reactive({
+//   pageNo: 1,
+//   pageSize: 10,
+//   infoId: undefined,
+//   chineseName: undefined,
+//   specimenNumber: undefined,
+//   attachments: undefined,
+//   processInstanceId: undefined,
+//   specimenCondition: undefined,
+//   imagePath:undefined
+// })
 
 /** 查询列表 */
 const getList = async () => {
   loading.value = true
   try {
-    const data = await SpecimenOutboundApi.getSpecimenOutboundDetailsPage(queryParams)
+    const data = await SpecimenOutboundApi.getSpecimenOutboundDetailsPage(query.dataId)
     list.value = data.list
     total.value = data.total
   } finally {
@@ -218,9 +214,8 @@ onMounted(async () => {
 
 // 检查用户权限的方法
 const ApprovalRef = ref()
-const handleApproval = ( id?: number) => {
-  ApprovalRef.value.open( id)
-
+const handleApproval = () => {
+  ApprovalRef.value.open(query.dataId)
 }
 </script>
 

+ 15 - 13
src/views/museums/specimenoutbound/OutboundForm/InitiativeProcessInstance.vue

@@ -30,15 +30,17 @@
     <el-button type="primary"
                style="float: right"
                @click="addSpecimenNumber">+</el-button>
-
+    <el-form-item label="标本编号"  prop="number" label-width="150px">
+      <el-input v-model="outboundData.number[0]" placeholder="请输入标本编号" label-width="150px"/>
+    </el-form-item>
     <el-form-item
-      v-for="(id, index) in outboundData.specimenNumber"
+      v-for="(item, index) in outboundData.number.slice(1)"
       :key="index"
-      :label="`标本编号 ${index + 1}`"
-      prop="specimenNumber"
+      :label="`标本编号 ${index + 2}`"
+      prop="number"
       label-width="150px"
     >
-      <el-input v-model="outboundData.specimenNumber[index]" placeholder="请输入标本编号" />
+      <el-input v-model="outboundData.number[index + 1]" placeholder="请输入标本编号" />
     </el-form-item>
     <el-form-item label="申请人或申请单位"  prop="applicantName" label-width="150px">
       <el-input v-model="outboundData.applicantName" placeholder="请输入申请人或申请单位" label-width="150px"/>
@@ -90,13 +92,13 @@
 import {ref, reactive, onMounted} from "vue";
 
 import {SpecimenOutboundApi, SpecimenOutboundVO} from '@/api/museums/specimenoutbound'
-import {useRoute} from 'vue-router';
+// import {useRoute} from 'vue-router';
 import { useI18n } from 'vue-i18n';
 defineOptions({name: 'InitiativeProcessInstance'})
 const formLoading = ref(false) // 表单的加载中
 const dialogVisible = ref(false) // 弹窗的是否展示
 const formType = ref('')
- const {query} = useRoute() // 查询参数
+ // const {query} = useRoute() // 查询参数
 const someType = ref('')
 // 国际化和消息提示
 const { t } = useI18n();
@@ -110,7 +112,7 @@ const outboundData = ref({
   id: undefined,
   infoId: undefined,
   chineseName: [],
-  specimenNumber: [''],
+  number: [''],
   applicantName: undefined,
   applicationDate: undefined,
   applicationUsage: undefined,
@@ -131,7 +133,7 @@ const outboundData = ref({
 const formRules = reactive({
   infoId: [{ required: true, message: '关联到总表中的标本ID不能为空', trigger: 'blur' }],
   chineseName: [{ required: true, message: '中文名称不能为空', trigger: 'blur' }],
-  specimenNumber: [{ required: true, message: '申请出库的标本编号不能为空', trigger: 'blur' }],
+  number: [{ required: true, message: '申请出库的标本编号不能为空', trigger: 'blur' }],
   applicantName: [{ required: true, message: '申请人或申请单位不能为空', trigger: 'blur' }],
   applicationUsage: [{ required: true, message: '申请出库的用途不能为空', trigger: 'blur' }],
   returnDate: [{ required: true, message: '退还日期不能为空', trigger: 'blur' }],
@@ -141,11 +143,11 @@ const formRules = reactive({
 
 /**增减标本编号**/
 const addSpecimenNumber = () => {
-  outboundData.value.specimenNumber.push(''); // 向数组中添加一个新的空字符串
+  outboundData.value.number.push(''); // 向数组中添加一个新的空字符串
 };
 const removeSpecimenNumber = () => {
-  if (outboundData.value.specimenNumber.length > 1) { // 确保至少保留一个输入框
-    outboundData.value.specimenNumber.pop(); // 从数组中移除最后一个元素
+  if (outboundData.value.number.length > 0) { // 确保至少保留一个输入框
+    outboundData.value.number.pop(); // 从数组中移除最后一个元素
   }
 };
 
@@ -205,7 +207,7 @@ const resetForm = () => {
     id: undefined,
     infoId: undefined,
     chineseName: undefined,
-    specimenNumber: undefined,
+    number: undefined,
     applicantName: undefined,
     applicationDate: undefined,
     applicationUsage: undefined,

+ 1 - 3
src/views/museums/specimenoutbound/OutboundForm/specimenReturnForm.vue

@@ -7,9 +7,7 @@
       :rules="formRules"
       label-width="100px"
     >
-<!--      <el-form-item label="标本ID" prop="infoId">-->
-<!--        <el-input v-model="formData.infoId" placeholder="请输入标本ID"/>-->
-<!--      </el-form-item>-->
+
       <el-form-item label="退还人" prop="returner">
         <el-input v-model="formData.returner" placeholder="请输入退还人"/>
       </el-form-item>

+ 88 - 143
src/views/museums/specimenoutbound/SpecimenOutboundForm.vue

@@ -1,29 +1,33 @@
 <template>
-  <Dialog title="编辑标本出库申请" v-model="dialogVisible">
+  <Dialog title="编辑标本出库申请" v-model="dialogVisible" style="width: 850px">
     <el-form
       ref="formRef"
       :model="formData"
       :rules="formRules"
       label-width="140px"
       v-loading="formLoading"
+
     >
-<!--      <div>-->
-<!--        <div style="margin-bottom: 20px;color: red;font-size: 20px">-->
-<!--          申请驳回原因:{{formData.processInstanceId}}-->
-<!--        </div>-->
-<!--            <div style="margin-bottom: 20px;margin-left: 70px;font-size: 18px; font-weight: bold">请重新申请</div>-->
-<!--            </div>-->
+            <div v-if="formData.status === 6 &&  formData.status === 2">
+              <div style="margin-bottom: 20px;color: red;font-size: 20px">
+                申请驳回原因:{{formData.processInstanceId}}
+              </div>
+                  <div style="margin-bottom: 20px;margin-left: 70px;font-size: 18px; font-weight: bold">请重新申请</div>
+                  </div>
 
-      <!--        点击添加的时候扩展一栏,即formData.specimenNumber的长度+1-->
+<!--              点击添加的时候扩展一栏,即formData.specimenNumber的长度+1-->
+      <div>
       <el-button type="primary"
                  style="float: right ;margin-left: 5px; padding: 5px 10px; font-size: 12px;"
                  @click="removeSpecimenNumber">-</el-button>
       <el-button type="primary"
                  style="float: right; padding: 5px 10px; font-size: 12px;"
                  @click="addSpecimenNumber">+</el-button>
+         </div>
       <el-form-item label="标本编号"  prop="number" label-width="150px">
         <el-input v-model="formData.number[0]" placeholder="请输入标本编号" label-width="150px"/>
       </el-form-item>
+      <template v-if="formData.number && formData.number.length > 1">
         <el-form-item
           v-for="(item, index) in formData.number.slice(1)"
           :key="index + 1"
@@ -33,62 +37,47 @@
         >
           <el-input v-model="formData.number[index + 1]" placeholder="请输入标本编号" />
         </el-form-item>
+      </template>
+
       <el-form-item label="申请人姓名"  prop="applicantName" label-width="150px">
         <el-input v-model="formData.applicantName" placeholder="请输入申请人姓名" label-width="150px"/>
       </el-form-item>
-      <el-form-item label="申请单位"  prop="applicantName" label-width="150px">
-        <el-input v-model="formData.applicationUsage" placeholder="请输入申请人姓名" label-width="150px"/>
-      </el-form-item>
-      <el-form-item label="申请日期" prop="applicationDate" label-width="150px">
-        <el-date-picker
-          v-model="formData.applicationDate"
-          type="date"
-          value-format="x"
-          placeholder="选择申请日期"
-        />
+      <el-form-item label="申请单位" prop="applicantName" label-width="150px">
+        <el-input v-model="formData.applicationUsage" placeholder="请输入申请单位"
+                  label-width="150px"/>
       </el-form-item>
+
       <el-form-item label="研究项目名称" prop="projectName" label-width="150px">
-        <el-input v-model="formData.projectName" placeholder="请输入申请出库的用途" label-width="150px"/>
+        <el-input v-model="formData.projectName" placeholder="请输入研究项目名称"
+                  label-width="150px"/>
       </el-form-item>
       <el-form-item label="电话号码" prop="phoneNumber" label-width="150px">
         <el-input v-model="formData.phoneNumber" placeholder="电话号码" label-width="150px"/>
       </el-form-item>
       <el-form-item label="附件上传" prop="attachments" label-width="150px">
-        <el-upload
-          ref="uploadRef"
-          v-model:file-list="fileList"
-          :action="importUrl + '?another=' + 1"
-          :auto-upload="false"
-          :data="data"
-          :disabled="formLoading"
+        <UploadFile
+          v-model="formData.attachments"
           :limit="1"
-          :on-change="handleFileChange"
-          :on-error="submitFormError"
-          :on-exceed="handleExceed"
-          :on-success="submitFormSuccess"
-          drag
-          :headers="uploadHeaders"
-        >
-        <i class="el-icon-upload"></i>
-        <div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em></div>
-        </el-upload>
+          class="min-w-80px"
+        />
+
       </el-form-item>
 
-      <el-form-item label="预计领取日期" prop="outgoingTime" label-width="150px">
+      <el-form-item label="项目开始时间" prop="outgoingTime" label-width="150px">
         <el-date-picker
-          v-model="formData.expectedCollectionTime"
+          v-model="formData.startTime"
           type="date"
           value-format="x"
-          placeholder="选择领取日期"
+          placeholder="选择项目开始时间"
         />
       </el-form-item>
 
-      <el-form-item label="预计退还日期" prop="returnDate" label-width="150px">
+      <el-form-item label="项目结束时间" prop="returnDate" label-width="150px">
         <el-date-picker
-          v-model="formData.estimatedReturnTime"
+          v-model="formData.endTime"
           type="date"
           value-format="x"
-          placeholder="选择退还日期"
+          placeholder="选择项目结束时间"
         />
       </el-form-item>
 
@@ -101,26 +90,27 @@
 
 </template>
 <script setup lang="ts">
-import { SpecimenOutboundApi, SpecimenOutboundVO } from '@/api/museums/specimenoutbound'
+
+import {SpecimenOutboundApi, SpecimenOutboundVO} from '@/api/museums/specimenoutbound'
 //import { UploadFilled } from '@element-plus/icons-vue'
 import {ref, reactive, defineEmits} from "vue";
+
 /** 标本出库回库信息 表单 */
-defineOptions({ name: 'SpecimenOutboundForm' })
-const importUrl =
-  import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + 'museums:specimen-outbound:create'
-const { t } = useI18n() // 国际化
+defineOptions({name: 'SpecimenOutboundForm'})
+
+const {t} = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
-const fileList = ref([]) // 文件列表
-const data = ref({ path: '' })
-const uploadRef = ref()
+// const fileList = ref([]) // 文件列表
+// const data = ref({path: ''})
+// const uploadRef = ref()
 const dialogVisible = ref(false) // 弹窗的是否展示
 const dialogTitle = ref('') // 弹窗的标题
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
 const formData = ref({
   id: undefined,
-  infoId: undefined,
-  chineseName: [],// 确保初始值为数组
+  infoId: [],
+  chineseName: undefined,// 确保初始值为数组
   number: [''],// 确保初始值为数组
   applicantName: undefined,
   applicationDate: undefined,
@@ -136,21 +126,21 @@ const formData = ref({
   returnDate: undefined,
   specimenCondition: undefined,
   sampleStatus: undefined,
-  estimatedReturnTime: undefined,
-  expectedCollectionTime:undefined
+  startTime: undefined,
+  endTime: undefined,
+  projectName:undefined,
+  phoneNumber:undefined
 })
 
 
 const formRules = reactive({
-  chineseName: [{ required: true, message: '中文名称不能为空', trigger: 'blur' }],
-  number: [{ required: true, message: '申请出库的标本编号不能为空', trigger: 'blur' }],
-  applicantName: [{ required: true, message: '申请人或申请单位不能为空', trigger: 'blur' }],
-  applicationUsage: [{ required: true, message: '申请出库的用途不能为空', trigger: 'blur' }],
-  operator: [{ required: true, message: '出库员不能为空', trigger: 'blur' }],
-  returner: [{ required: true, message: '退还人不能为空', trigger: 'blur' }],
-  receiver: [{ required: true, message: '点收人不能为空', trigger: 'blur' }],
-  estimatedReturnTime: [{ required: true, message: '预计退还日期不能为空', trigger: 'blur' }],
-  expectedCollectionTime: [{ required: true, message: '预计领取日期不能为空', trigger: 'blur' }]
+
+  number: [{required: true, message: '标本编号不能为空', trigger: 'blur'}],
+  applicantName: [{required: true, message: '申请人姓名不能为空', trigger: 'blur'}],
+  applicationUsage: [{required: true, message: '申请单位不能为空', trigger: 'blur'}],
+  projectName: [{required: true, message: '研究项目名称不能为空', trigger: 'blur'}],
+  startTime: [{required: true, message: '项目开始时间不能为空', trigger: 'blur'}],
+  endTime: [{required: true, message: '项目结束时间不能为空', trigger: 'blur'}]
 })
 const formRef = ref() // 表单 Ref
 /**增减标本编号**/
@@ -163,6 +153,7 @@ const removeSpecimenNumber = () => {
   }
 };
 /** 打开弹窗 */
+
 const open = async (type: string, id?: number) => {
   dialogVisible.value = true
   dialogTitle.value = t('action.' + type)
@@ -173,96 +164,33 @@ const open = async (type: string, id?: number) => {
     formLoading.value = true
     try {
       formData.value = await SpecimenOutboundApi.getSpecimenOutbound(id)
+      //
+      // if (!formData.value.number || !Array.isArray(formData.value.number) || formData.value.number.length === 0) {
+      //   formData.value.number = ['']; // 如果不是数组或为空,则初始化为包含一个空字符串的数组
+      // }
+      if (!Array.isArray(formData.value.number)) {
+        formData.value.number = [];// 如果不是数组,则转换为包含该数据的数组
+      } else if (formData.value.number.length === 0) {
+        formData.value.number = ['']; // 如果数组为空,则初始化为包含一个空字符串的数组
+      }
+
     } finally {
       formLoading.value = false
     }
   }
 }
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+defineExpose({open}) // 提供 open 方法,用于打开弹窗
 
 
 
-// /** 标本搜索选择 */
-// const props = reactive({
-//   value: 'value',
-//   label: 'label',
-//   children: 'children',
-//   multiple: true
-// });
-//
-// const options = ref([]);
-//
-// const fetchOptions = async () => {
-//   try {
-//     const response = await SpecimenOutboundApi.createSpecimenOutbound();
-//     options.value = processOptions(response.data);
-//   } catch (error) {
-//     console.error('获取选项数据失败:', error);
-//   }
-// };
-//
-// const processOptions = (data) => {
-//   // 将后端数据格式化为 el-cascader 需要的格式
-//   return data.map(item => ({
-//     value: item.id, // 假设后端返回的ID字段是id
-//     label: item.name, // 假设后端返回的名称字段是name
-//     children: item.children ? processOptions(item.children) : undefined
-//   }));
-// };
-//
-//
-// const handleCascaderChange = (value, formData) => {
-//   formData.value.chineseName = value;
-// };
-
-
-
-
-/** 上传错误提示 */
-const submitFormError = (): void => {
-  message.error('上传失败,请您重新上传!')
-  formLoading.value = false
-}
-/** 处理上传的文件发生变化 */
-const handleFileChange = (file) => {
-  data.value.path = file.name
-}
-const uploadHeaders = ref() // 上传 Header 头
-
-/** 文件数超出提示 */
-const handleExceed = (): void => {
-  message.error('最多只能上传一个文件!')
-}
 
 /** 提交表单 */
 const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-// const submitForm = async () => {
-//   // 校验表单
-//   await formRef.value.validate()
-//   // 提交请求
-//   formLoading.value = true
-//   try {
-//     const data = formData.value as unknown as SpecimenOutboundVO
-//     if (formType.value === 'create') {
-//       await SpecimenOutboundApi.createSpecimenOutbound(data)
-//       message.success(t('新增标本出库申请成功'))
-//     } else {
-//       await SpecimenOutboundApi.alterSpecimenOutbound(data)
-//       message.success(t('标本出库申请修改成功'))
-//     }
-//     dialogVisible.value = false
-//     // 发送操作成功的事件
-//     emit('success')
-//   } finally {
-//     formLoading.value = false
-//   }
-// }
-//
+
+
 
 const submitForm = async () => {
-  // 校验表单
 
-  await formRef.value.validate();
 
   // // 将数组字段转换为字符串
   // if (formData.value.chineseName.length) {
@@ -271,6 +199,8 @@ const submitForm = async () => {
   // if (formData.value.specimenNumber.length) {
   //   formData.value.specimenNumber = formData.value.specimenNumber.join(',');
   // }
+  // 校验表单
+  await formRef.value.validate()
 
   // 提交请求
   formLoading.value = true;
@@ -294,8 +224,8 @@ const submitForm = async () => {
 const resetForm = () => {
   formData.value = {
     id: undefined,
-    infoId: undefined,
-    chineseName: [],// 确保初始值为数组
+    infoId: [],
+    chineseName: undefined,// 确保初始值为数组
     number: [],// 确保初始值为数组
     applicantName: undefined,
     applicationDate: undefined,
@@ -310,10 +240,25 @@ const resetForm = () => {
     receiver: undefined,
     returnDate: undefined,
     specimenCondition: undefined,
-    sampleStatus: undefined
+    sampleStatus: undefined,
+    startTime: undefined,
+    endTime:undefined,
+    projectName:undefined,
+    phoneNumber:undefined
   }
   formRef.value?.resetFields()
 }
 
-
+// // 在组件加载时获取数据
+// onMounted(async () => {
+//   const formData = await SpecimenOutboundApi.getSpecimenOutbound(data);
+//   if (formData && typeof formData.number === 'string') {
+//     try {
+//       formData.value.number = JSON.parse(formData.number);
+//     } catch (error) {
+//       console.error('解析numberStr出错:', error);
+//       formData.value.number = [];
+//     }
+//   }
+// })
 </script>

+ 14 - 9
src/views/museums/specimenoutbound/approval.vue

@@ -24,7 +24,7 @@
         </el-form-item>
       <el-form-item  v-if="approvalStatus === 2">
         <el-input
-          v-model="formData.chkRemarks"
+          v-model="formData.rejectionReasons"
           style="width:800px ;height: 200px"
           placeholder="请输入驳回原因"
           type="textarea"
@@ -47,7 +47,7 @@ import { ref, defineEmits, nextTick  } from 'vue'
 import {SpecimenOutboundApi} from "@/api/museums/specimenoutbound";
 const {t} = useI18n() // 国际化
 const approvalStatus = ref(0);
-const uploadRef = ref()
+// const uploadRef = ref()
 const formLoading = ref(false) // 表单的加载中
 const dialogVisible = ref(false) // 弹窗的是否展示
 
@@ -56,8 +56,13 @@ const message = useMessage() // 消息弹窗
 
 const formData = ref({
   id: undefined,
-  processInstanceId:undefined,
-  chkRemarks : undefined
+  status : 0,
+  processInstanceId: undefined, // 审批建议
+  rejectionReasons: undefined, // 驳回原因
+  approveUsers: undefined, // 一审批员
+  twoApproveUsers: undefined, // 二审批员
+  approvalTime: null, // 一审时间
+  twoApprovalTime: null, // 二审时间
 })
 /** 打开弹窗 */
 const open = async (id?: number) => {
@@ -77,7 +82,7 @@ defineExpose({ open })// 提供 open 方法,用于打开弹窗
 const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 const submitApproval = async () => {
   // 检查是否有驳回原因
-  if ((approvalStatus.value === 2 || approvalStatus.value === 6)  && !formData.value.chkRemarks.trim()) {
+  if (approvalStatus.value === 2   && !formData.value.rejectionReasons.trim()) {
     message.error(t('请输入驳回原因'))
     return// 阻止表单提交
   }
@@ -86,10 +91,10 @@ const submitApproval = async () => {
     const data = {
       ...formData.value,
      status: approvalStatus.value, // 确保传入审批状态
-      approvalRemarks:  (approvalStatus.value === 2 || approvalStatus.value === 6)? chkRemarks.value : '' // 如果是驳回,则传入驳回原因
+      processInstanceId:  approvalStatus.value === 2 ? formData.value.rejectionReasons : formData.value.processInstanceId // 如果是驳回,则传入驳回原因
     };
-
-      await SpecimenOutboundApi.ApprovalSpecimenOutbound(data)
+    console.log(data,"wozaizheli")
+    await SpecimenOutboundApi.ApprovalSpecimenOutbound(data)
     message.success(t('common.updateSuccess'));
     dialogVisible.value = false
     // 发送操作成功的事件
@@ -104,6 +109,6 @@ const resetForm = async (): Promise<void> => {
   // 重置上传状态和文件
   formLoading.value = false
   await nextTick()
-  uploadRef.value?.clearFiles()
+  // uploadRef.value?.clearFiles()
 }
 </script>