瀏覽代碼

fix:笼位和入室管理问题修复

liuzhenlin 2 月之前
父節點
當前提交
5187b1eb69

+ 1 - 2
components.d.ts

@@ -23,8 +23,8 @@ declare module 'vue' {
     VanCellGroup: typeof import('vant/es')['CellGroup']
     VanCheckbox: typeof import('vant/es')['Checkbox']
     VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
-    VanCol: typeof import('vant/es')['Col']
     VanDatePicker: typeof import('vant/es')['DatePicker']
+    VanDialog: typeof import('vant/es')['Dialog']
     VanEmpty: typeof import('vant/es')['Empty']
     VanField: typeof import('vant/es')['Field']
     VanFloatingBubble: typeof import('vant/es')['FloatingBubble']
@@ -51,7 +51,6 @@ declare module 'vue' {
     VanTabs: typeof import('vant/es')['Tabs']
     VanTag: typeof import('vant/es')['Tag']
     VanTextEllipsis: typeof import('vant/es')['TextEllipsis']
-    VanTimePicker: typeof import('vant/es')['TimePicker']
     VanUploader: typeof import('vant/es')['Uploader']
   }
 }

+ 87 - 32
src/components/FlowTable.vue

@@ -1,31 +1,31 @@
 <template>
-  <van-steps direction="vertical" :active="-1">
-    <van-step>
-      <template #active-icon></template>
-      <template #inactive-icon></template>
-      <div class="flex">
-        <h4>审核方式</h4>
-        <h4>审核人</h4>
-        <h4>审核结果</h4>
-        <h4>审核时间</h4>
+  <div class="flow-table-container">
+    <div class="header-row">
+      <span>审核方式</span>
+      <span>审核人</span>
+      <span>审核结果</span>
+      <span>审核时间</span>
+      <span>审核描述</span>
+    </div>
+    <van-steps direction="vertical" :active="active">
+      <van-step v-for="row in apprList" :key="row.id">
+        <div class="row">
+          <span>{{ getNodeModel(row.nodeType, row.nodeModel) }}</span>
+          <span>{{ row.userName || '-' }}</span>
+          <span>{{ formatResult(row.approvalResult) }}</span>
+          <span>{{ row.approvalDate ? formatDate(new Date(row.approvalDate), 'YYYY-mm-dd') : '-' }}</span>
+          <span class="desc" @click="handleViewDesc(row.approvalDesc)" v-if="row.approvalDesc">查看</span>
+          <span v-else>-</span>
+        </div>
+      </van-step>
+    </van-steps>
+
+    <van-dialog v-model:show="descDialog.show" title="审核描述" @confirm="descDialog.show = false">
+      <div class="desc-content">
+        {{ descDialog.content || '暂无描述' }}
       </div>
-    </van-step>
-  </van-steps>
-  <van-steps direction="vertical" :active="active">
-    <van-step v-for="row in apprList" :key="row.id">
-      <div class="flex">
-        <p>{{ getNodeModel(row.nodeType, row.nodeModel) }}</p>
-        <p>{{ row.userName }}</p>
-        <p>
-          <span v-if="row.approvalResult === 'pass'">通过</span>
-          <span v-else-if="row.approvalResult === 'rejection'">拒绝</span>
-          <span v-else-if="row.approvalResult === 'back'">退回</span>
-          <span v-else>审批中</span>
-        </p>
-        <p>{{ row.approvalDate ? formatDate(new Date(row.approvalDate), 'YYYY-mm-dd') : '' }}</p>
-      </div>
-    </van-step>
-  </van-steps>
+    </van-dialog>
+  </div>
 </template>
 <script lang="ts" setup>
   import { ref, computed, watch } from 'vue'
@@ -39,6 +39,10 @@
     defCode: { type: String, default: '' }
   })
   const apprList = ref<any[]>([])
+  const descDialog = ref<{ show: boolean; content: string }>({
+    show: false,
+    content: '',
+  })
   const active = computed(() => {
     return apprList.value.length - 1
   })
@@ -58,6 +62,19 @@
     const arr = res?.data?.nodes || []
     apprList.value = arr
   }
+  const formatResult = (result?: string) => {
+    if (result === 'pass') return '通过'
+    if (result === 'rejection') return '拒绝'
+    if (result === 'back') return '退回'
+    return '审批中'
+  }
+
+  const handleViewDesc = (desc?: string) => {
+    if (!desc) return
+    descDialog.value.content = desc
+    descDialog.value.show = true
+  }
+
   watch(
     () => props.id,
     (val) => {
@@ -72,12 +89,50 @@
   )
 </script>
 <style lang="scss" scoped>
-  :deep(.van-step) {
-    p,
-    h4 {
-      flex: 1;
-      text-align: center;
-      color: #333;
+  .flow-table-container {
+    .header-row {
+      display: grid;
+      grid-template-columns: 1.2fr 1fr 1fr 1fr 1fr;
+      padding: 8px 12px;
+      font-size: 13px;
+      color: #666;
+      background: #f7f8fa;
+      border-radius: 6px;
+      margin-bottom: 10px;
+      span {
+        font-weight: 600;
+        text-align: center;
+      }
     }
+
+    .row {
+      display: grid;
+      grid-template-columns: 1.2fr 1fr 1fr 1fr 1fr;
+      gap: 6px;
+      padding: 10px 14px;
+      background: #fff;
+      border-radius: 6px;
+      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
+      margin-bottom: 8px;
+
+      span {
+        font-size: 12px;
+        color: #555;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+
+      .desc {
+        color: #1989fa;
+        cursor: pointer;
+      }
+    }
+  }
+
+  .desc-content {
+    padding: 14px;
+    white-space: pre-wrap;
+    line-height: 1.6;
   }
 </style>

+ 69 - 57
src/layout/animal.vue

@@ -9,6 +9,9 @@
 <template>
   <router-view></router-view>
   <van-tabbar route :placeholder="true" v-if="route.path !== '/entry/add'">
+    <van-tabbar-item replace to="/home" icon="wap-home-o">
+      首页
+    </van-tabbar-item>
     <van-tabbar-item replace to="/my-cage" icon="send-gift-o">我的笼位</van-tabbar-item>
     <van-tabbar-item replace to="/animal-application" icon="send-gift-o">笼位申请</van-tabbar-item>
     <van-tabbar-item replace to="/animal-application-removal" icon="send-gift-o">申请带离</van-tabbar-item>
@@ -17,75 +20,84 @@
 </template>
 
 <script lang="ts" setup>
-  import to from 'await-to-js'
-  import { showDialog } from 'vant'
-  import { ref } from 'vue'
-  import { useRouter, useRoute } from 'vue-router'
-  import { useUserInfo } from '/@/stores/userInfo'
-  import { Local } from '/@/utils/storage'
+import to from 'await-to-js'
+import { showDialog } from 'vant'
+import { ref } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useUserInfo } from '/@/stores/userInfo'
+import { Local } from '/@/utils/storage'
 
-  const active = ref(0)
-  const router = useRouter()
-  const route = useRoute()
+const active = ref(0)
+const router = useRouter()
+const route = useRoute()
 
-  const onRouterPush = (val: string) => {
-    router.push(val)
-  }
-  const scan = async () => {
-    const res = await useUserInfo().scanCode()
-    if (res) {
-      window.location.href = res as string
-    }
+const onRouterPush = (val: string) => {
+  router.push(val)
+}
+const scan = async () => {
+  const res = await useUserInfo().scanCode()
+  if (res) {
+    window.location.href = res as string
   }
+}
 </script>
 
 <style lang="scss" scoped>
-  .footer-nav {
+.footer-nav {
+  height: 60px;
+  font-weight: bold;
+
+  ul {
     height: 60px;
-    font-weight: bold;
-    ul {
-      height: 60px;
+    display: flex;
+
+    li {
+      flex: 1;
       display: flex;
-      li {
-        flex: 1;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        font-size: 12px;
-        &.center-icon {
-          height: 60px;
-          flex: 0 0 60px;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+
+      &.center-icon {
+        height: 60px;
+        flex: 0 0 60px;
+        padding: 10px;
+        align-self: flex-end;
+
+        i {
           padding: 10px;
-          align-self: flex-end;
-          i {
-            padding: 10px;
-            background: #1d66dc;
-            border-radius: 50%;
-          }
-        }
-        img {
-          height: 24px;
-          width: 24px;
-        }
-        .img-container {
-          padding: 6px;
-          height: 24px;
-          width: 24px;
-          background-color: #419fe5;
+          background: #1d66dc;
           border-radius: 50%;
-          overflow: visible;
-        }
-        &.service .img-container {
-          background-color: #f59969;
-        }
-        &.todo .img-container {
-          background-color: #20b5ae;
-        }
-        &.user .img-container {
-          background-color: #595cac;
         }
       }
+
+      img {
+        height: 24px;
+        width: 24px;
+      }
+
+      .img-container {
+        padding: 6px;
+        height: 24px;
+        width: 24px;
+        background-color: #419fe5;
+        border-radius: 50%;
+        overflow: visible;
+      }
+
+      &.service .img-container {
+        background-color: #f59969;
+      }
+
+      &.todo .img-container {
+        background-color: #20b5ae;
+      }
+
+      &.user .img-container {
+        background-color: #595cac;
+      }
     }
   }
+}
 </style>

+ 69 - 57
src/layout/entry.vue

@@ -9,6 +9,9 @@
 <template>
   <router-view></router-view>
   <van-tabbar route :placeholder="true" v-if="route.path !== '/entry/add'">
+    <van-tabbar-item replace to="/home" icon="wap-home-o">
+      首页
+    </van-tabbar-item>
     <van-tabbar-item replace to="/entry" icon="send-gift-o">入室申请</van-tabbar-item>
     <van-tabbar-item replace to="/entry/manage" icon="send-gift-o">入室管理</van-tabbar-item>
     <van-tabbar-item replace to="/entry/mine" icon="coupon-o">我的入室</van-tabbar-item>
@@ -17,75 +20,84 @@
 </template>
 
 <script lang="ts" setup>
-  import to from 'await-to-js'
-  import { showDialog } from 'vant'
-  import { ref } from 'vue'
-  import { useRouter, useRoute } from 'vue-router'
-  import { useUserInfo } from '/@/stores/userInfo'
-  import { Local } from '/@/utils/storage'
+import to from 'await-to-js'
+import { showDialog } from 'vant'
+import { ref } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useUserInfo } from '/@/stores/userInfo'
+import { Local } from '/@/utils/storage'
 
-  const active = ref(0)
-  const router = useRouter()
-  const route = useRoute()
+const active = ref(0)
+const router = useRouter()
+const route = useRoute()
 
-  const onRouterPush = (val: string) => {
-    router.push(val)
-  }
-  const scan = async () => {
-    const res = await useUserInfo().scanCode()
-    if (res) {
-      window.location.href = res as string
-    }
+const onRouterPush = (val: string) => {
+  router.push(val)
+}
+const scan = async () => {
+  const res = await useUserInfo().scanCode()
+  if (res) {
+    window.location.href = res as string
   }
+}
 </script>
 
 <style lang="scss" scoped>
-  .footer-nav {
+.footer-nav {
+  height: 60px;
+  font-weight: bold;
+
+  ul {
     height: 60px;
-    font-weight: bold;
-    ul {
-      height: 60px;
+    display: flex;
+
+    li {
+      flex: 1;
       display: flex;
-      li {
-        flex: 1;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        font-size: 12px;
-        &.center-icon {
-          height: 60px;
-          flex: 0 0 60px;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+
+      &.center-icon {
+        height: 60px;
+        flex: 0 0 60px;
+        padding: 10px;
+        align-self: flex-end;
+
+        i {
           padding: 10px;
-          align-self: flex-end;
-          i {
-            padding: 10px;
-            background: #1d66dc;
-            border-radius: 50%;
-          }
-        }
-        img {
-          height: 24px;
-          width: 24px;
-        }
-        .img-container {
-          padding: 6px;
-          height: 24px;
-          width: 24px;
-          background-color: #419fe5;
+          background: #1d66dc;
           border-radius: 50%;
-          overflow: visible;
-        }
-        &.service .img-container {
-          background-color: #f59969;
-        }
-        &.todo .img-container {
-          background-color: #20b5ae;
-        }
-        &.user .img-container {
-          background-color: #595cac;
         }
       }
+
+      img {
+        height: 24px;
+        width: 24px;
+      }
+
+      .img-container {
+        padding: 6px;
+        height: 24px;
+        width: 24px;
+        background-color: #419fe5;
+        border-radius: 50%;
+        overflow: visible;
+      }
+
+      &.service .img-container {
+        background-color: #f59969;
+      }
+
+      &.todo .img-container {
+        background-color: #20b5ae;
+      }
+
+      &.user .img-container {
+        background-color: #595cac;
+      }
     }
   }
+}
 </style>

+ 3 - 0
src/layout/training.vue

@@ -9,6 +9,9 @@
 <template>
   <router-view></router-view>
   <van-tabbar route :placeholder="true">
+    <van-tabbar-item replace to="/home" icon="wap-home-o">
+      首页
+    </van-tabbar-item>
     <van-tabbar-item replace to="/training" icon="info-o">全部培训</van-tabbar-item>
     <van-tabbar-item replace to="/training/done" icon="passed">我的培训</van-tabbar-item>
     <van-tabbar-item replace to="/exam/mine" icon="question-o">我的考试</van-tabbar-item>

+ 24 - 1
src/utils/other.ts

@@ -51,6 +51,25 @@ export function getDictLabel(arr: RowDicDataType[], val:string) {
   return val
 }
 
+/**
+ * 防抖函数
+ * @param func 要执行的函数
+ * @param delay 延迟时间(毫秒)
+ * @returns 防抖后的函数
+ */
+export function debounce(func: Function, delay: number) {
+  let timer: number | null = null
+  return (...args: any[]) => {
+    if (timer) {
+      window.clearTimeout(timer)
+    }
+    timer = window.setTimeout(() => {
+      func(...args)
+      timer = null
+    }, delay)
+  }
+}
+
 /**
  * 统一批量导出
  * @method elSvg 导出全局注册 element plus svg 图标
@@ -62,6 +81,7 @@ export function getDictLabel(arr: RowDicDataType[], val:string) {
  * @method isMobile 判断是否是移动端
  * @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象
  * @method handleOpenLink 打开外部链接
+ * @method debounce 防抖函数
  */
 const other = {
 	deepClone: (obj: EmptyObjectType) => {
@@ -71,7 +91,10 @@ const other = {
 		return handleEmpty(list);
 	},
   getDictLabel: (arr: RowDicDataType[], val:string) => {
-    getDictLabel(arr, val)
+    return getDictLabel(arr, val)
+  },
+  debounce: (func: Function, delay: number) => {
+    return debounce(func, delay)
   }
 };
 

+ 252 - 572
src/view/animal/application/components/Detail.vue

@@ -1,576 +1,177 @@
 <template>
-  <div class="facilities-dialog-container">
-    <el-dialog
-      title="详情"
-      @close="closeDialog"
-      :close-on-click-modal="false"
-      v-model="props.showDialog"
-      width="100%"
-    >
-      <el-form
-        ref="expertDialogFormRef"
-        disabled
-        :model="state.form"
-        size="default"
-        label-width="140px"
-        label-position="top"
-      >
-        <el-row :gutter="35">
-          <el-col
-            :span="24"
-            class="mb20"
-          >
-            <el-form-item
-              label="课题名称"
-              prop="memberType"
-            >
-              <el-input
-                v-model="state.form.projectGroupName"
-                disabled
-                placeholder="请输入课题名称"
-              ></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col
-            :span="24"
-            class="mb20"
-          >
-            <el-form-item
-              label="姓名"
-              prop="memberName"
-            >
-              <el-input
-                v-model="state.form.userName"
-                disabled
-                placeholder="请输入姓名"
-              ></el-input>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row :gutter="20">
-          <el-col :span="24">
-            <el-form-item
-              label="部门"
-              prop="deptName"
-            >
-              <el-input
-                v-model="state.form.deptName"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="联系方式"
-              prop="phone"
-            >
-              <el-input
-                v-model="state.form.phone"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <h4 class="mb8 mt10">实验动物笼位预约信息</h4>
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="笼位数量"
-              prop="number"
-            >
-              <el-input-number
-                v-model="state.form.number"
-                style="width: 100%"
-                :min="1"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="开始使用时间"
-              prop="startDate"
-            >
-              <el-date-picker
-                v-model="state.form.startDate"
-                type="date"
-                placeholder="请开始使用时间"
-                clearable
-                style="width: 100%"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="品种品系"
-              prop="categoryId"
-            >
-              <el-select
-                v-model="state.form.categoryId"
-                placeholder="请选择"
-                disabled
-              >
-                <el-option
-                  v-for="item in animalTypeList"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.id"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="饲养区域"
-              prop="level"
-            >
-              <el-select
-                v-model="state.form.level"
-                placeholder="请选择"
-                disabled
-              >
-                <el-option
-                  v-for="item in LeavelList"
-                  :key="item.id"
-                  :label="item.name"
-                  :value="item.id"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          :gutter="20"
-          class="mt10"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="周龄"
-              prop="age"
-            >
-              <el-input-number
-                v-model="state.form.age"
-                style="width: 100%"
-                :min="0"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="体重"
-              prop="weight"
-            >
-              <el-input-number
-                v-model="state.form.weight"
-                style="width: 100%"
-                :min="0"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="雄性"
-              prop="maleNumber"
-            >
-              <el-input-number
-                style="width: 100%"
-                placeholder="雄性数量"
-                v-model="state.form.maleNumber"
-                :min="0"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="雌性"
-              prop="famaleNumber"
-            >
-              <el-input-number
-                style="width: 100%"
-                placeholder="雌性数量"
-                v-model="state.form.famaleNumber"
-                :min="0"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item
-              label="合计"
-              prop="totalNumber"
-            >
-              <el-input-number
-                style="width: 100%"
-                disabled
-                placeholder="合计"
-                v-model="state.form.totalNumber"
-                :min="0"
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="饲养总天数"
-              prop="feedingDay"
-            >
-              <el-input-number
-                v-model="state.form.feedingDay"
-                style="width: 100%"
-                :min="1"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <h4 class="mb8 mt20">采购渠道</h4>
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="采购渠道"
-              prop="buyFrom"
-            >
-              <el-radio-group
-                disabled
-                v-model="state.form.buyFrom"
-              >
-                <el-radio
-                  :label="ProcurementChannels.PURCHASED_BY_OTHERS"
-                  size="large"
-                >
-                  动物房代购
-                </el-radio>
-                <el-radio
-                  :label="ProcurementChannels.PURCHASED_BY_MYSELF"
-                  size="large"
-                >
-                  自行购买
-                </el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.buyFrom === ProcurementChannels.PURCHASED_BY_MYSELF"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="外购来源单位"
-              prop="comeFromUnit"
-            >
-              <el-input
-                v-model="state.form.comeFromUnit"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="动物到达时间"
-              prop="comeTime"
-            >
-              <el-date-picker
-                v-model="state.form.comeTime"
-                type="date"
-                placeholder="请选择到达时间"
-                clearable
-                style="width: 100%"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <h4 class="mb8 mt20">特殊要求和附件</h4>
-        <el-row
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col :span="24">
-            <el-form-item
-              label="是否有特殊饲养要求"
-              prop="hasFeedingSpecial"
-            >
-              <el-radio-group
-                disabled
-                v-model="state.form.hasFeedingSpecial"
-              >
-                <el-radio
-                  :label="FeedingSpecial.HAVE_FEEDING_SPECIAL"
-                  size="large"
-                >
-                  有
-                </el-radio>
-                <el-radio
-                  :label="FeedingSpecial.NO_FEEDING_SPECIAL"
-                  size="large"
-                >
-                  无
-                </el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row>
-          <el-col :span="24">
-            <el-form-item
-              label="特殊饲养要求"
-              prop="feedingSpecialDesc"
-            >
-              <el-input
-                v-model="state.form.feedingSpecialDesc"
-                disabled
-                placeholder="请输入特殊饲养要求"
-              ></el-input>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.licenseNumberFile && JSON.stringify(state.form.licenseNumberFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-          >
-            <el-form-item
-              label="生产许可证"
-              prop="licenseNumberFile"
-            >
-              <el-link
-                v-for="item in state.form.licenseNumberFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+  <div class="detail-drawer-container">
+    <van-popup v-model:show="props.showDialog" position="right"
+      :style="{ width: '100vw', maxWidth: '460px', height: '100vh' }" :closeable="true" @close="closeDialog"
+      :close-on-click-overlay="false">
+      <div class="drawer-wrapper">
+        <header class="drawer-header">
+          <h3>详情</h3>
+        </header>
+        <div class="drawer-body">
+          <van-cell-group inset>
+            <van-field v-model="state.form.projectGroupName" label="课题名称" readonly placeholder="请输入课题名称" />
+            <van-field v-model="state.form.userName" label="姓名" readonly placeholder="请输入姓名" />
+            <van-field v-model="state.form.deptName" label="部门" readonly />
+            <van-field v-model="state.form.phone" label="联系方式" readonly />
+          </van-cell-group>
+
+          <h4 class="section-title">实验动物笼位预约信息</h4>
+          <van-cell-group inset>
+            <van-field v-model="state.form.number" label="笼位数量" readonly type="digit" />
+            <van-field v-model="state.form.startDate" label="开始使用时间" readonly placeholder="请开始使用时间" />
+            <van-field v-model="state.form.categoryName" label="动物类别" readonly placeholder="请选择" />
+            <van-field v-model="state.form.variety" label="品种品系" readonly placeholder="请选择" />
+            <van-field v-model="levelName" label="饲养区域" readonly placeholder="请选择" />
+            <van-field v-model="state.form.age" label="周龄" readonly type="digit" />
+            <van-field v-model="state.form.weight" label="体重" readonly type="digit" />
+            <van-field v-model="state.form.maleNumber" label="雄性" readonly type="digit" />
+            <van-field v-model="state.form.famaleNumber" label="雌性" readonly type="digit" />
+            <van-field v-model="state.form.totalNumber" label="合计" readonly type="digit" />
+            <van-field v-model="state.form.feedingDay" label="饲养总天数" readonly type="digit" />
+          </van-cell-group>
+
+          <h4 class="section-title">采购渠道</h4>
+          <van-cell-group inset>
+            <van-cell title="采购渠道">
+              <template #value>
+                <van-radio-group v-model="state.form.buyFrom" direction="horizontal" disabled>
+                  <van-radio :name="ProcurementChannels.PURCHASED_BY_OTHERS">动物房代购</van-radio>
+                  <van-radio :name="ProcurementChannels.PURCHASED_BY_MYSELF">自行购买</van-radio>
+                </van-radio-group>
+              </template>
+            </van-cell>
+            <van-field v-if="state.form.buyFrom === ProcurementChannels.PURCHASED_BY_MYSELF"
+              v-model="state.form.comeFromUnit" label="外购来源单位" readonly />
+            <van-field v-model="state.form.comeTime" label="动物到达时间" readonly placeholder="请选择到达时间" />
+          </van-cell-group>
+
+          <h4 class="section-title">特殊要求和附件</h4>
+          <van-cell-group inset>
+            <van-cell title="是否有特殊饲养要求">
+              <template #value>
+                <van-radio-group v-model="state.form.hasFeedingSpecial" direction="horizontal" disabled>
+                  <van-radio :name="FeedingSpecial.HAVE_FEEDING_SPECIAL">有</van-radio>
+                  <van-radio :name="FeedingSpecial.NO_FEEDING_SPECIAL">无</van-radio>
+                </van-radio-group>
+              </template>
+            </van-cell>
+            <van-field v-model="state.form.feedingSpecialDesc" label="特殊饲养要求" readonly type="textarea" rows="3"
+              placeholder="请输入特殊饲养要求" />
+          </van-cell-group>
+
+          <!-- 附件列表 -->
+          <van-cell-group v-if="state.form.licenseNumberFile && JSON.stringify(state.form.licenseNumberFile) !== '[]'"
+            inset class="mt10">
+            <van-cell title="生产许可证">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.licenseNumberFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.animalTestDateFile && JSON.stringify(state.form.animalTestDateFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="近三个月动物质量检测证明"
-              prop="animalTestDateFile"
-            >
-              <el-link
-                v-for="item in state.form.animalTestDateFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group v-if="state.form.animalTestDateFile && JSON.stringify(state.form.animalTestDateFile) !== '[]'"
+            inset class="mt10">
+            <van-cell title="近三个月动物质量检测证明">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.animalTestDateFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.geneIdentificationFile && JSON.stringify(state.form.geneIdentificationFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="基因鉴定报告"
-              prop="geneIdentificationFile"
-            >
-              <el-link
-                v-for="item in state.form.geneIdentificationFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group
+            v-if="state.form.geneIdentificationFile && JSON.stringify(state.form.geneIdentificationFile) !== '[]'" inset
+            class="mt10">
+            <van-cell title="基因鉴定报告">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.geneIdentificationFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.envTestDateFile && JSON.stringify(state.form.envTestDateFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="近三个月饲养环境检测证明"
-              prop="envTestDateFile"
-            >
-              <el-link
-                v-for="item in state.form.envTestDateFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group v-if="state.form.envTestDateFile && JSON.stringify(state.form.envTestDateFile) !== '[]'" inset
+            class="mt10">
+            <van-cell title="近三个月饲养环境检测证明">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.envTestDateFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.cageAppointFile && JSON.stringify(state.form.cageAppointFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="笼位预约表"
-              prop="cageAppointFile"
-            >
-              <el-link
-                v-for="item in state.form.cageAppointFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group v-if="state.form.cageAppointFile && JSON.stringify(state.form.cageAppointFile) !== '[]'" inset
+            class="mt10">
+            <van-cell title="笼位预约表">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.cageAppointFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.ethicsCheckFile && JSON.stringify(state.form.ethicsCheckFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="实验动物福利伦理审查申请表"
-              prop="ethicsCheckFile"
-            >
-              <el-link
-                v-for="item in state.form.ethicsCheckFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group v-if="state.form.ethicsCheckFile && JSON.stringify(state.form.ethicsCheckFile) !== '[]'" inset
+            class="mt10">
+            <van-cell title="实验动物福利伦理审查申请表">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.ethicsCheckFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <el-row
-          v-if="state.form.ethicsAdviceFile && JSON.stringify(state.form.ethicsAdviceFile) !== '[]'"
-          class="mt10"
-          :gutter="20"
-        >
-          <el-col
-            :span="24"
-            class="mb20"
-            style="overflow: hidden"
-          >
-            <el-form-item
-              label="实验动物福利伦理审查意见表"
-              prop="ethicsAdviceFile"
-            >
-              <el-link
-                v-for="item in state.form.ethicsAdviceFile"
-                :key="item.url"
-                :href="item.url"
-                target="_blank"
-                type="primary"
-              >
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <van-cell-group v-if="state.form.ethicsAdviceFile && JSON.stringify(state.form.ethicsAdviceFile) !== '[]'"
+            inset class="mt10">
+            <van-cell title="实验动物福利伦理审查意见表">
+              <template #value>
+                <div class="file-links">
+                  <a v-for="item in state.form.ethicsAdviceFile" :key="item.url" :href="item.url" target="_blank"
+                    class="file-link">
                 {{ item.name }}
-              </el-link>
-            </el-form-item>
-          </el-col>
-        </el-row>
-
-        <div class="card">
-          <h4>审批记录</h4>
-          <FlowTable
-            :id="state.form.id"
-            :businessCode="`${state.form.id}`"
-            defCode="plat_cage_applications"
-          />
+                  </a>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+
+          <div class="card mt10">
+            <h4 class="section-title">审批记录</h4>
+            <FlowTable :id="state.form.id" :businessCode="`${state.form.id}`" defCode="plat_cage_applications" />
+          </div>
+        </div>
         </div>
-      </el-form>
-    </el-dialog>
+    </van-popup>
   </div>
 </template>
 
 <script setup lang="ts" name="systemProDialog">
   import to from 'await-to-js'
-  import { nextTick, reactive, ref, defineAsyncComponent, watch } from 'vue'
+import { nextTick, reactive, ref, defineAsyncComponent, watch, computed } from 'vue'
   import dayjs from 'dayjs'
 
   import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
@@ -591,6 +192,22 @@
   const expertDialogFormRef = ref()
   const animalTypeList = ref<any[]>([])
 
+const categoryName = computed(() => {
+  if (state.form.categoryId) {
+    const category = animalTypeList.value.find((item) => item.id === state.form.categoryId)
+    return category ? category.name : ''
+  }
+  return ''
+})
+
+const levelName = computed(() => {
+  if (state.form.level) {
+    const level = LeavelList.find((item) => item.id === state.form.level)
+    return level ? level.name : ''
+  }
+  return ''
+})
+
   const state = reactive({
     form: {
       id: 0,
@@ -598,6 +215,7 @@
       number: 0,
       approveStatus: '',
       categoryName: '',
+    variety: '',
       projectGroupName: '',
       createdTime: '',
       returnNumber: 0,
@@ -656,8 +274,10 @@
 
   const closeDialog = () => {
     emit('close')
+  if (expertDialogFormRef.value && expertDialogFormRef.value.resetFields) {
     expertDialogFormRef.value.resetFields()
   }
+  }
 
   // 暴露变量
   defineExpose({
@@ -673,23 +293,83 @@
   )
 </script>
 <style lang="scss" scoped>
-  :deep(.disUoloadSty .el-upload--picture-card) {
-    display: none; /* 上传按钮隐藏 */
+.detail-drawer-container {
+  .drawer-wrapper {
+    display: flex;
+    flex-direction: column;
+    height: 100vh;
   }
-  :deep(.el-descriptions__label.el-descriptions__cell.is-bordered-label) {
-    width: 120px;
+
+  .drawer-header {
+    padding: 16px;
+    border-bottom: 1px solid #f0f0f0;
+
+    h3 {
+      margin: 0;
+      font-size: 18px;
+      font-weight: 600;
+      text-align: center;
+    }
   }
-  .el-upload-list--picture-card .el-upload-list__item-thumbnail {
-    height: 120px;
+
+  .drawer-body {
+    flex: 1;
+    overflow-y: auto;
+    padding: 16px 12px 80px;
+    -webkit-overflow-scrolling: touch;
   }
-  .fileName {
+
+  .section-title {
+    font-size: 16px;
+    font-weight: 600;
+    margin: 20px 0 8px;
+    padding-left: 8px;
+    position: relative;
+
+    &::before {
+      content: '';
     position: absolute;
-    width: 100%;
     left: 0;
-    bottom: 0;
-    text-align: center;
-    font-size: 11px;
-    line-height: 1;
-    margin: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 3px;
+      height: 16px;
+      background-color: #1c9bfd;
+    }
+  }
+
+  .file-links {
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+  }
+
+  .file-link {
+    color: #1989fa;
+    text-decoration: none;
+    word-break: break-all;
+
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+
+  .card {
+    background-color: #fff;
+    border-radius: 8px;
+    margin-top: 16px;
+  }
+
+  .mt10 {
+    margin-top: 10px;
+  }
+}
+
+:deep(.van-field__label) {
+  width: 120px;
+}
+
+:deep(.van-cell__title) {
+  flex: 0 0 120px;
   }
 </style>

+ 77 - 18
src/view/animal/application/index.vue

@@ -24,16 +24,30 @@
             ></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item prop="serialNo">
-          <el-date-picker
-            v-model="dateTime"
-            type="daterange"
+        <el-form-item prop="dateRange">
+          <el-input
+            v-model="dateRangeText"
+            placeholder="请选择申请时间范围"
+            readonly
             style="width: 100%"
-            start-placeholder="开始时间"
-            end-placeholder="结束时间"
-            clearable
-            @change="search"
-          />
+            @click="showCalendar = true"
+          >
+            <template #suffix>
+              <el-icon
+                v-if="dateRangeText"
+                style="cursor: pointer; margin-right: 8px"
+                @click.stop="clearDateRange"
+              >
+                <Close />
+              </el-icon>
+              <el-icon
+                style="cursor: pointer"
+                @click.stop="showCalendar = true"
+              >
+                <Calendar />
+              </el-icon>
+            </template>
+          </el-input>
         </el-form-item>
         <!-- <el-form-item prop="serialNo">
           <el-select
@@ -214,14 +228,30 @@
       @click="handleApplication"
       axis="y"
     />
+
+    <!-- 日期范围选择日历 -->
+    <van-popup
+      v-model:show="showCalendar"
+      position="bottom"
+      :style="{ height: '80vh' }"
+      round
+    >
+      <van-calendar
+        v-model:show="showCalendar"
+        type="range"
+        :min-date="new Date(1900, 0, 1)"
+        @confirm="onDateRangeConfirm"
+      />
+    </van-popup>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { ref, reactive, onMounted, defineAsyncComponent } from 'vue'
+  import { ref, reactive, onMounted, defineAsyncComponent, computed } from 'vue'
   import to from 'await-to-js'
   import dayjs from 'dayjs'
   import { useRouter, useRoute } from 'vue-router'
+  import { Calendar, Close } from '@element-plus/icons-vue'
 
   import { formatDate } from '/@/utils/formatTime'
   import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
@@ -245,7 +275,18 @@
   const returnCageDialogRef = ref()
   const showDetailDialog = ref<boolean>(false)
   const dateTime = ref<any>([])
+  const showCalendar = ref(false)
+  const selectedDateRange = ref<[Date, Date] | null>(null)
   const offset = ref({ x: -80, y: 450 })
+
+  const dateRangeText = computed(() => {
+    if (selectedDateRange.value && selectedDateRange.value.length === 2) {
+      const start = dayjs(selectedDateRange.value[0]).format('YYYY-MM-DD')
+      const end = dayjs(selectedDateRange.value[1]).format('YYYY-MM-DD')
+      return `${start} 至 ${end}`
+    }
+    return ''
+  })
   const state = reactive({
     queryParams: {
       categoryId: null,
@@ -255,8 +296,8 @@
       pageNum: 1,
       pageSize: 20,
       userName: '',
-      startDate: '',
-      endDate: '',
+      beginTime: '',
+      endTime: '',
       approveStatus: '',
       isMyself: userInfos.value.userRoles === 'project_group_member' ? 1 : 0,
     },
@@ -275,8 +316,8 @@
       pageNum: 1,
       pageSize: 20,
       userName: '',
-      startDate: '',
-      endDate: '',
+      beginTime: '',
+      endTime: '',
       approveStatus: '',
       isMyself: userInfos.value.userRoles === 'project_group_member' ? 1 : 0,
     }
@@ -289,12 +330,12 @@
       pageSize: isExport ? 99999 : state.queryParams.pageSize,
     }
 
-    if (dateTime.value && dateTime.value[0]) {
-      payload.startDate = dayjs(dateTime.value[0]).format('YYYY-MM-DD')
+    if (dateTime.value && dateTime.value.length === 2 && dateTime.value[0]) {
+      payload.beginTime = dayjs(dateTime.value[0]).format('YYYY-MM-DD') +' 00:00:00'
     }
 
-    if (dateTime.value && dateTime.value[1]) {
-      payload.endDate = dayjs(dateTime.value[1]).format('YYYY-MM-DD')
+    if (dateTime.value && dateTime.value.length === 2 && dateTime.value[1]) {
+      payload.endTime = dayjs(dateTime.value[1]).format('YYYY-MM-DD') +' 23:59:59'
     }
 
     Object.entries(payload).forEach(([key, value]) => {
@@ -306,6 +347,24 @@
     return payload
   }
 
+  const onDateRangeConfirm = (values: Date[]) => {
+    if (values && values.length === 2) {
+      selectedDateRange.value = [values[0], values[1]]
+      dateTime.value = [
+        dayjs(values[0]).format('YYYY-MM-DD'),
+        dayjs(values[1]).format('YYYY-MM-DD')
+      ]
+      showCalendar.value = false
+      search()
+    }
+  }
+
+  const clearDateRange = () => {
+    selectedDateRange.value = null
+    dateTime.value = []
+    search()
+  }
+
   const formatToChineseDate = (dateStr: string) => {
     const date = new Date(dateStr)
     const year = date.getFullYear()

+ 20 - 3
src/view/animal/applicationRemoval/components/addEdit.vue

@@ -30,7 +30,7 @@
                 :rules="rules.takeawayReason" />
               <van-field v-model="state.form.takeawayTransport" label="运输方式" placeholder="请输入" :readonly="isReadOnly" required
                 :rules="rules.takeawayTransport" />
-              <van-field v-model="state.form.accessCardNumber" label="门禁卡序列号" placeholder="请输入" :readonly="isReadOnly" />
+              <!-- <van-field v-model="state.form.accessCardNumber" label="门禁卡序列号" placeholder="请输入" :readonly="isReadOnly" /> -->
               <van-field v-model="state.form.takeawayMaleNumber" label="雄性" placeholder="雄性数量" type="digit" :readonly="isReadOnly">
                 <template #button>
                   <van-stepper v-model="state.form.takeawayMaleNumber" :min="0" integer :disabled="isReadOnly" />
@@ -55,7 +55,15 @@
           </van-form>
         </div>
         <div class="dialog-footer">
-          <van-button v-if="state.dialog.type !== 'detail'" type="primary" @click="onSubmit" block native-type="submit">
+          <van-button
+            v-if="state.dialog.type !== 'detail'"
+            type="primary"
+            @click="onSubmit"
+            block
+            native-type="submit"
+            :loading="state.loading"
+            loading-text="提交中..."
+          >
             提交
           </van-button>
         </div>
@@ -161,11 +169,13 @@ const state = reactive<{
   form: CreateAnimalApplyLeavePayload & { categoryName?: string }
   safePromise: boolean
   safeRead: boolean
+  loading: boolean
   dialog: { isShowDialog: boolean; type: string; title: string; submitTxt: string }
 }>({
   form: defaultFormFields,
   safePromise: false,
   safeRead: false,
+  loading: false,
   dialog: {
     isShowDialog: false,
     type: '',
@@ -206,7 +216,11 @@ const openDialog = async (type: ActionType, sourceData?: TakeawayList) => {
     }
     // 设置日期选择器的值
     if (sourceData.takeawayDate) {
-      const dateParts = sourceData.takeawayDate.split('-')
+      // 统一格式化为年月日格式
+      const dateStr = formatDate(new Date(sourceData.takeawayDate), 'YYYY-mm-dd')
+      state.form.takeawayDate = dateStr
+      // 解析日期用于日期选择器
+      const dateParts = dateStr.split('-')
       selectedDate.value = new Date(parseInt(dateParts[0]), parseInt(dateParts[1]) - 1, parseInt(dateParts[2]))
     }
   }
@@ -252,6 +266,7 @@ const closeDialog = () => {
   }
   safePromiseStatus.value = false
   selectedDate.value = null
+  state.loading = false
   state.dialog.isShowDialog = false
 }
 
@@ -299,6 +314,7 @@ const onSubmit = async () => {
     return
   }
 
+  state.loading = true
   const post = platAnimalCageApplicationApi.createAnimalTakeawayApplications
   const [err]: ToResponse = await to(
     post(
@@ -312,6 +328,7 @@ const onSubmit = async () => {
     ),
   )
 
+  state.loading = false
   if (err) return
   showToast({
     type: 'success',

+ 374 - 235
src/view/animal/applicationRemoval/index.vue

@@ -1,32 +1,85 @@
 <template>
   <div class="entry-container">
-    <div class="search-wrap" ref="searchWrapRef">
-      <el-form :model="state.queryParams" ref="queryRef">
-        <el-form-item prop="applyDate">
-          <el-date-picker v-model="state.queryParams.applyDate" type="date" style="width: 100%" placeholder="申请时间"
-            clearable @change="search" />
-        </el-form-item>
+    <div
+      class="search-wrap"
+      ref="searchWrapRef"
+    >
+      <el-form
+        :model="state.queryParams"
+        ref="queryRef"
+      >
         <el-form-item prop="isMyself">
-          <el-select v-model="state.queryParams.isMyself" style="width: 100%" placeholder="申请人" clearable
-            @change="search">
-            <el-option label="我申请的" :value="1"></el-option>
-            <el-option label="全部" :value="0"></el-option>
+          <el-select
+            v-model="state.queryParams.isMyself"
+            style="width: 100%"
+            placeholder="申请人"
+            clearable
+            @change="search"
+          >
+            <el-option
+              label="我申请的"
+              :value="1"
+            ></el-option>
+            <el-option
+              label="全部"
+              :value="0"
+            ></el-option>
           </el-select>
         </el-form-item>
+        <el-form-item prop="dateRange">
+          <el-input
+            v-model="dateRangeText"
+            placeholder="请选择申请时间范围"
+            readonly
+            style="width: 100%"
+            @click="showCalendar = true"
+          >
+            <template #suffix>
+              <el-icon
+                v-if="dateRangeText"
+                style="cursor: pointer; margin-right: 8px"
+                @click.stop="clearDateRange"
+              >
+                <Close />
+              </el-icon>
+              <el-icon
+                style="cursor: pointer"
+                @click.stop="showCalendar = true"
+              >
+                <Calendar />
+              </el-icon>
+            </template>
+          </el-input>
+        </el-form-item>
       </el-form>
       <div style="text-align: right">
-        <el-button @click="handleExport" color="#2c78ff">
+        <el-button
+          @click="handleExport"
+          color="#2c78ff"
+        >
           导出
         </el-button>
-        <el-button color="#2c78ff" @click="openCageApplicationModal()">
+        <el-button
+          color="#2c78ff"
+          @click="openCageApplicationModal()"
+        >
           申请带离
         </el-button>
       </div>
     </div>
 
     <div class="list-container">
-      <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
-        <van-cell v-for="item in state.list" :key="item" @click="handleCheckDetail(item)">
+      <van-list
+        v-model:loading="state.loading"
+        :finished="state.finished"
+        finished-text="没有更多了"
+        @load="onLoad"
+      >
+        <van-cell
+          v-for="item in state.list"
+          :key="item"
+          @click="handleCheckDetail(item)"
+        >
           <template #default>
             <div class="list">
               <header class="flex justify-between">
@@ -63,7 +116,7 @@
               <p class="inst-title">
                 <span>动物类别</span>
                 <span class="title ml8">
-                  {{animalTypeList.find((type) => type.id === item.categoryId)?.name}}
+                  {{ animalTypeList.find((type) => type.id === item.categoryId)?.name }}
                 </span>
               </p>
               <p class="inst-title">
@@ -75,19 +128,34 @@
               <p class="inst-title">
                 <span>申请状态</span>
                 <span class="title ml8">
-                  <van-tag v-if="item.approveStatus == ApplyLeaveApproveStatus.SUBMIT" type="primary">
+                  <van-tag
+                    v-if="item.approveStatus == ApplyLeaveApproveStatus.SUBMIT"
+                    type="primary"
+                  >
                     提交
                   </van-tag>
-                  <van-tag v-else-if="item.approveStatus == ApplyLeaveApproveStatus.WAIT_APPROVE" type="primary">
+                  <van-tag
+                    v-else-if="item.approveStatus == ApplyLeaveApproveStatus.WAIT_APPROVE"
+                    type="primary"
+                  >
                     待审核
                   </van-tag>
-                  <van-tag v-else-if="item.approveStatus == ApplyLeaveApproveStatus.PASS" type="success">
+                  <van-tag
+                    v-else-if="item.approveStatus == ApplyLeaveApproveStatus.PASS"
+                    type="success"
+                  >
                     通过
                   </van-tag>
-                  <van-tag v-else-if="item.approveStatus == ApplyLeaveApproveStatus.REVOKE" type="success">
+                  <van-tag
+                    v-else-if="item.approveStatus == ApplyLeaveApproveStatus.REVOKE"
+                    type="success"
+                  >
                     撤回
                   </van-tag>
-                  <van-tag v-else-if="item.approveStatus == ApplyLeaveApproveStatus.REFUSE" type="danger">
+                  <van-tag
+                    v-else-if="item.approveStatus == ApplyLeaveApproveStatus.REFUSE"
+                    type="danger"
+                  >
                     审核不通过
                   </van-tag>
                 </span>
@@ -95,16 +163,25 @@
               <p class="inst-title">
                 <span>带离状态</span>
                 <span class="title ml8">
-                  <van-tag v-if="item.approveStatus == ApplyLeaveApproveStatus.PASS" type="success">
+                  <van-tag
+                    v-if="item.approveStatus == ApplyLeaveApproveStatus.PASS"
+                    type="success"
+                  >
                     已带离
                   </van-tag>
-                  <van-tag v-else-if="
-                    item.approveStatus == ApplyLeaveApproveStatus.WAIT_APPROVE ||
-                    item.approveStatus === ApplyLeaveApproveStatus.REFUSE
-                  " type="primary">
+                  <van-tag
+                    v-else-if="
+                      item.approveStatus == ApplyLeaveApproveStatus.WAIT_APPROVE ||
+                      item.approveStatus === ApplyLeaveApproveStatus.REFUSE
+                    "
+                    type="primary"
+                  >
                     待带离
                   </van-tag>
-                  <van-tag v-else-if="takeawayComplate(item)" type="success">
+                  <van-tag
+                    v-else-if="takeawayComplate(item)"
+                    type="success"
+                  >
                     已结束
                   </van-tag>
                 </span>
@@ -127,16 +204,27 @@
                   {{ item.returnFemaleNumber + item.returnMaleNumber }}
                 </span>
               </p>
-              <el-row class="mt16" :gutter="20">
+              <el-row
+                class="mt16"
+                :gutter="20"
+              >
                 <el-col :span="12">
-                  <el-button style="width: 100%" v-if="item.approveStatus === ApplyLeaveApproveStatus.PASS"
-                    type="primary" @click.stop="handleReturn(item)">
+                  <el-button
+                    style="width: 100%"
+                    v-if="item.approveStatus === ApplyLeaveApproveStatus.PASS"
+                    type="primary"
+                    @click.stop="handleReturn(item)"
+                  >
                     转回
                   </el-button>
                 </el-col>
                 <el-col :span="12">
-                  <el-button style="width: 100%" v-if="item.approveStatus === ApplyLeaveApproveStatus.PASS"
-                    type="primary" @click.stop="handleDie(item)">
+                  <el-button
+                    style="width: 100%"
+                    v-if="item.approveStatus === ApplyLeaveApproveStatus.PASS"
+                    type="primary"
+                    @click.stop="handleDie(item)"
+                  >
                     淘汰动物上报
                   </el-button>
                 </el-col>
@@ -147,240 +235,291 @@
       </van-list>
     </div>
   </div>
-  <AddEdit ref="addEditRef" @refresh="onLoad(true)" />
-  <TurnBackModal ref="turnBackModalRef" @refresh="onLoad(true)" />
-  <DieModal ref="dieModalRef" @refresh="onLoad(true)" />
+  <AddEdit
+    ref="addEditRef"
+    @refresh="onLoad(true)"
+  />
+  <TurnBackModal
+    ref="turnBackModalRef"
+    @refresh="onLoad(true)"
+  />
+  <DieModal
+    ref="dieModalRef"
+    @refresh="onLoad(true)"
+  />
+
+  <van-popup
+    v-model:show="showCalendar"
+    position="bottom"
+    :style="{ height: '80vh' }"
+    round
+  >
+    <van-calendar
+      v-model:show="showCalendar"
+      type="range"
+      :min-date="new Date(1900, 0, 1)"
+      @confirm="onDateRangeConfirm"
+    />
+  </van-popup>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, onMounted } from 'vue'
-import to from 'await-to-js'
-import dayjs from 'dayjs'
-
-import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
-import { ApplyLeaveApproveStatus, TakeawayList } from '/@/constants/pageConstants'
-import AddEdit from './components/addEdit.vue'
-import TurnBackModal from './components/turnBack.vue'
-import DieModal from './components/dieModal.vue'
-
-const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
-
-const addEditRef = ref<InstanceType<typeof AddEdit>>()
-const turnBackModalRef = ref<InstanceType<typeof TurnBackModal>>()
-const dieModalRef = ref<InstanceType<typeof DieModal>>()
-
-const animalTypeList = ref([])
-const dateTime = ref<any>([])
-
-const state = reactive({
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    isMyself: 0,
-    applyDate: '',
-    startDate: '',
-    endDate: '',
-  },
-  loading: true,
-  list: [] as any[],
-  finished: false,
-})
-
-const getDicts = () => {
-  Promise.all([platAnimalCageApplicationApi.getAnimalTypeList({})]).then(([animalType]) => {
-    animalTypeList.value = animalType.data
+  import { ref, reactive, onMounted, computed } from 'vue'
+  import to from 'await-to-js'
+  import dayjs from 'dayjs'
+  import { Calendar, Close } from '@element-plus/icons-vue'
+
+  import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
+  import { ApplyLeaveApproveStatus, TakeawayList } from '/@/constants/pageConstants'
+  import AddEdit from './components/addEdit.vue'
+  import TurnBackModal from './components/turnBack.vue'
+  import DieModal from './components/dieModal.vue'
+
+  const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
+
+  const addEditRef = ref<InstanceType<typeof AddEdit>>()
+  const turnBackModalRef = ref<InstanceType<typeof TurnBackModal>>()
+  const dieModalRef = ref<InstanceType<typeof DieModal>>()
+
+  const animalTypeList = ref([])
+  const dateTime = ref<any>([])
+  const showCalendar = ref(false)
+  const selectedDateRange = ref<[Date, Date] | null>(null)
+
+  const state = reactive({
+    queryParams: {
+      pageNum: 1,
+      pageSize: 10,
+      isMyself: 0,
+      takeawayDateStart: '',
+      takeawayDateEnd: '',
+    },
+    loading: true,
+    list: [] as any[],
+    finished: false,
   })
-}
 
-const setListPayload = (isExport?: boolean) => {
-  const payload = {
-    ...state.queryParams,
-    pageSize: isExport ? 99999 : state.queryParams.pageSize,
-  }
+  const dateRangeText = computed(() => {
+    if (selectedDateRange.value && selectedDateRange.value.length === 2) {
+      const start = dayjs(selectedDateRange.value[0]).format('YYYY-MM-DD')
+      const end = dayjs(selectedDateRange.value[1]).format('YYYY-MM-DD')
+      return `${start} 至 ${end}`
+    }
+    return ''
+  })
 
-  if (dateTime.value && dateTime.value[0]) {
-    payload.startDate = dayjs(dateTime.value[0]).format('YYYY-MM-DD')
+  const getDicts = () => {
+    Promise.all([platAnimalCageApplicationApi.getAnimalTypeList({})]).then(([animalType]) => {
+      animalTypeList.value = animalType.data
+    })
   }
 
-  if (dateTime.value && dateTime.value[1]) {
-    payload.endDate = dayjs(dateTime.value[1]).format('YYYY-MM-DD')
-  }
+  const setListPayload = (isExport?: boolean) => {
+    const payload = {
+      ...state.queryParams,
+      pageSize: isExport ? 99999 : state.queryParams.pageSize,
+    }
 
-  Object.entries(payload).forEach(([key, value]) => {
-    if (value === '' || value === null) {
-      delete payload[key as keyof typeof payload]
+    if (dateTime.value && dateTime.value[0]) {
+      payload.takeawayDateStart = dayjs(dateTime.value[0]).format('YYYY-MM-DD') + ' 00:00:00'
     }
-  })
 
-  return payload
-}
-
-const onLoad = async (isSearch?: boolean) => {
-  const [err, res]: ToResponse = await to(
-    platAnimalCageApplicationApi.getAnimalTakeawayApplicationsList({
-      ...setListPayload(),
-      pageNum: isSearch ? 1 : state.queryParams.pageNum,
-    }),
-  )
-  if (err) return
-
-  if (res && res.data && res.data.list || []) {
-    const list = res.data.list || []
-    state.loading = false
-
-    if (!isSearch) {
-      for (const item of list) {
-        state.list.push(item)
+    if (dateTime.value && dateTime.value[1]) {
+      payload.takeawayDateEnd = dayjs(dateTime.value[1]).format('YYYY-MM-DD') + ' 23:59:59'
+    }
+
+    Object.entries(payload).forEach(([key, value]) => {
+      if (value === '' || value === null) {
+        delete payload[key as keyof typeof payload]
       }
-      state.queryParams.pageNum++
-      if (list.length < state.queryParams.pageSize) {
-        state.finished = true
+    })
+
+    return payload
+  }
+
+  const onLoad = async (isSearch?: boolean) => {
+    const [err, res]: ToResponse = await to(
+      platAnimalCageApplicationApi.getAnimalTakeawayApplicationsList({
+        ...setListPayload(),
+        pageNum: isSearch ? 1 : state.queryParams.pageNum,
+      }),
+    )
+    if (err) return
+
+    if ((res && res.data && res.data.list) || []) {
+      const list = res.data.list || []
+      state.loading = false
+
+      if (!isSearch) {
+        for (const item of list) {
+          state.list.push(item)
+        }
+        state.queryParams.pageNum++
+        if (list.length < state.queryParams.pageSize) {
+          state.finished = true
+        }
+      } else {
+        state.list = list
       }
-    } else {
-      state.list = list
     }
   }
-}
-
-const search = () => {
-  onLoad(true)
-}
-
-const handleExport = async () => {
-  const [err, res]: ToResponse = await to(
-    platAnimalCageApplicationApi.getAnimalTakeawayApplicationsListExport({
-      ...setListPayload(true),
-      pageSize: 99999,
-      base64Enable: 1,
-    }),
-  )
-
-  if (err) return
-
-  if (res && res.data) {
-    const { base64, name } = res.data
-    const link = document.createElement('a')
-    link.href = `data:application/octet-stream;base64,${base64}`
-    link.download = name
-    link.style.display = 'none'
-    document.body.appendChild(link)
-    link.click()
-    document.body.removeChild(link)
+
+  const search = () => {
+    onLoad(true)
   }
-}
-
-const handleCheckDetail = (row: TakeawayList) => {
-  addEditRef.value?.openDialog('detail', row)
-}
-
-const formatToChineseDate = (dateStr: string) => {
-  const date = new Date(dateStr)
-  const year = date.getFullYear()
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const day = String(date.getDate()).padStart(2, '0')
-
-  return `${year}年${month}月${day}日`
-}
-
-const takeawayComplate = (data: TakeawayList) => {
-  const {
-    dieFemaleNumber,
-    dieMaleNumber,
-    returnFemaleNumber,
-    returnMaleNumber,
-    takeawayMaleNumber,
-    takewayFemaleNumber,
-  } = data
-
-  const total = takeawayMaleNumber + takewayFemaleNumber
-  const returnTotal = returnFemaleNumber + returnMaleNumber
-  const dieTotal = dieFemaleNumber + dieMaleNumber
-
-  return returnTotal >= total || dieTotal >= total || returnTotal + dieTotal >= total
-}
-
-const openCageApplicationModal = () => {
-  addEditRef.value.openDialog('add')
-}
-
-const handleReturn = (row: TakeawayList) => {
-  turnBackModalRef.value?.openDialog(row)
-}
-
-const handleDie = (row: TakeawayList) => {
-  dieModalRef.value?.openDialog(row)
-}
-
-onMounted(() => {
-  getDicts()
-  onLoad()
-})
-</script>
 
-<style lang="scss" scoped>
-.entry-container {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-
-  .search-wrap {
-    background: #fff;
-    margin-bottom: 10px;
-    padding: 15px;
+  const onDateRangeConfirm = (values: Date[]) => {
+    if (values && values.length === 2) {
+      selectedDateRange.value = [values[0], values[1]]
+      dateTime.value = [dayjs(values[0]).format('YYYY-MM-DD'), dayjs(values[1]).format('YYYY-MM-DD')]
+      showCalendar.value = false
+      search()
+    }
   }
 
-  .list-container {
-    overflow-y: auto;
-    padding: 10px;
-    border-radius: 4px;
-    flex: 1;
+  const clearDateRange = () => {
+    selectedDateRange.value = null
+    dateTime.value = []
+    state.queryParams.takeawayDateStart = ''
+    state.queryParams.takeawayDateEnd = ''
+    search()
   }
 
-  .van-list {
-    .van-cell {
-      background-color: #fff;
+  const handleExport = async () => {
+    const [err, res]: ToResponse = await to(
+      platAnimalCageApplicationApi.getAnimalTakeawayApplicationsListExport({
+        ...setListPayload(true),
+        pageSize: 99999,
+        base64Enable: 1,
+      }),
+    )
+
+    if (err) return
+
+    if (res && res.data) {
+      const { base64, name } = res.data
+      const link = document.createElement('a')
+      link.href = `data:application/octet-stream;base64,${base64}`
+      link.download = name
+      link.style.display = 'none'
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+    }
+  }
 
-      +.van-cell {
-        margin-top: 10px;
-      }
+  const handleCheckDetail = (row: TakeawayList) => {
+    addEditRef.value?.openDialog('detail', row)
+  }
 
-      header,
-      footer {
-        color: #333;
-      }
+  const formatToChineseDate = (dateStr: string) => {
+    const date = new Date(dateStr)
+    const year = date.getFullYear()
+    const month = String(date.getMonth() + 1).padStart(2, '0')
+    const day = String(date.getDate()).padStart(2, '0')
 
-      .title {
-        flex: 1;
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        text-align: left;
-      }
+    return `${year}年${month}月${day}日`
+  }
 
-      .inst-title {
-        color: #333;
-        text-align: left;
-        flex: 1;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-        margin-top: 4px;
-      }
+  const takeawayComplate = (data: TakeawayList) => {
+    const {
+      dieFemaleNumber,
+      dieMaleNumber,
+      returnFemaleNumber,
+      returnMaleNumber,
+      takeawayMaleNumber,
+      takewayFemaleNumber,
+    } = data
+
+    const total = takeawayMaleNumber + takewayFemaleNumber
+    const returnTotal = returnFemaleNumber + returnMaleNumber
+    const dieTotal = dieFemaleNumber + dieMaleNumber
+
+    return returnTotal >= total || dieTotal >= total || returnTotal + dieTotal >= total
+  }
+
+  const openCageApplicationModal = () => {
+    addEditRef.value.openDialog('add')
+  }
 
-      .time {
-        color: #f69a4d;
+  const handleReturn = (row: TakeawayList) => {
+    turnBackModalRef.value?.openDialog(row)
+  }
+
+  const handleDie = (row: TakeawayList) => {
+    dieModalRef.value?.openDialog(row)
+  }
+
+  onMounted(() => {
+    getDicts()
+    onLoad()
+  })
+</script>
+
+<style lang="scss" scoped>
+  .entry-container {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+
+    .search-wrap {
+      background: #fff;
+      margin-bottom: 10px;
+      padding: 15px;
+    }
+
+    .list-container {
+      overflow-y: auto;
+      padding: 10px;
+      border-radius: 4px;
+      flex: 1;
+    }
+
+    .van-list {
+      .van-cell {
+        background-color: #fff;
+
+        + .van-cell {
+          margin-top: 10px;
+        }
+
+        header,
+        footer {
+          color: #333;
+        }
+
+        .title {
+          flex: 1;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          text-align: left;
+        }
+
+        .inst-title {
+          color: #333;
+          text-align: left;
+          flex: 1;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          margin-top: 4px;
+        }
+
+        .time {
+          color: #f69a4d;
+        }
       }
     }
-  }
 
-  .inst-title {
-    :first-child {
-      color: rgb(120, 120, 120);
+    .inst-title {
+      :first-child {
+        color: rgb(120, 120, 120);
+      }
     }
   }
-}
 
-:deep(.van-tag) {
-  color: #fff !important;
-}
+  :deep(.van-tag) {
+    color: #fff !important;
+  }
 </style>

+ 154 - 140
src/view/animal/myCage/index.vue

@@ -59,30 +59,30 @@
                 </span>
               </p>
               <p class="inst-title">
-                <span>动物类</span>
+                <span>动物类</span>
                 <span class="title ml8">
-                  {{ item.categoryName }}
+                  {{animalTypeList.find((type) => type.id === item.categoryId)?.name}}
+                </span>
+              </p>
+              <p class="inst-title">
+                <span>品种品系</span>
+                <span class="title ml8">
+                  {{ item.variety }}
                 </span>
               </p>
               <p class="inst-title">
                 <span>级别</span>
                 <span class="title ml8">
-                  {{ LeavelList.find((leaveItem) => leaveItem.id === item.level)?.name || '' }}
+                  {{LeavelList.find((leaveItem) => leaveItem.id === item.level)?.name || ''}}
                 </span>
               </p>
               <footer class="flex justify-between mt16">
                 <span class="title">
-                  <el-button
-                    v-if="
-                      item.approveStatus === ApproveStatus.PASS.toString() &&
-                      item.returnStatus !== ReturnStatus.COMPLETE.toString() &&
-                      userInfos.id === item.userId
-                    "
-                    style="height: 25px"
-                    type="primary"
-                    @click.stop="handleRefundable(item)"
-                    >退还</el-button
-                  >
+                  <el-button v-if="
+                    item.approveStatus === ApproveStatus.PASS.toString() &&
+                    item.returnStatus !== ReturnStatus.COMPLETE.toString() &&
+                    userInfos.id === item.userId
+                  " style="height: 25px" type="primary" @click.stop="handleRefundable(item)">退还</el-button>
                 </span>
                 <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
               </footer>
@@ -92,165 +92,179 @@
       </van-list>
     </div>
 
-    <DetailModal :showDialog="showDetailDialog" :isReturnCageList="false" ref="detailModalRef" @close="() => (showDetailDialog = false)" />
-    <ReturnCageDialog ref="returnCageDialogRef" :currentRefundableItemNumber="currentRefundableItemNumber" :getTableData="handleRefresh" />
+    <DetailModal :showDialog="showDetailDialog" :isReturnCageList="false" ref="detailModalRef"
+      @close="() => (showDetailDialog = false)" />
+    <ReturnCageDialog ref="returnCageDialogRef" :currentRefundableItemNumber="currentRefundableItemNumber"
+      :getTableData="handleRefresh" />
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { reactive, ref, onMounted, defineAsyncComponent, ComponentPublicInstance } from 'vue'
-  import to from 'await-to-js'
+import { reactive, ref, onMounted, defineAsyncComponent, ComponentPublicInstance } from 'vue'
+import to from 'await-to-js'
 
-  import { formatDate, formatToChineseDate } from '/@/utils/formatTime'
-  import { ApproveStatus, ReturnStatus, LeavelList, ApproveStatusList, MyCageType } from '/@/constants/pageConstants'
-  import { useUserInfos } from '/@/hooks/useUserInfos'
-  import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
+import { formatDate, formatToChineseDate } from '/@/utils/formatTime'
+import { ApproveStatus, ReturnStatus, LeavelList, ApproveStatusList, MyCageType } from '/@/constants/pageConstants'
+import { useUserInfos } from '/@/hooks/useUserInfos'
+import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
 
-  interface ReturnCageDialogInstance extends ComponentPublicInstance {
-    handleOpenRefundableDialog: (id: number) => void
-  }
+interface ReturnCageDialogInstance extends ComponentPublicInstance {
+  handleOpenRefundableDialog: (id: number) => void
+}
+
+interface DetailModalInstance extends ComponentPublicInstance {
+  initForm: (id: number) => void
+}
+
+const DetailModal = defineAsyncComponent(() => import('/@/view/animal/application/components/Detail.vue'))
+const ReturnCageDialog = defineAsyncComponent(() => import('/@/view/animal/application/components/ReturnCageDialog.vue'))
+
+const { userInfos } = useUserInfos()
+const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
+
+const returnCageDialogRef = ref<ReturnCageDialogInstance>()
+const detailModalRef = ref<DetailModalInstance>()
+
+const showDetailDialog = ref<boolean>(false)
+const activeStatus = ref<MyCageType>(MyCageType.MINE_CAGE)
+const currentRefundableItemNumber = ref<number>(0)
+const animalTypeList = ref([])
 
-  interface DetailModalInstance extends ComponentPublicInstance {
-    initForm: (id: number) => void
+const state = reactive({
+  pageList: [],
+  loading: false,
+  finished: false,
+  queryParams: {
+    pageNum: 1,
+    pageSize: 25,
+    isMyself: 1
   }
+})
 
-  const DetailModal = defineAsyncComponent(() => import('/@/view/animal/application/components/Detail.vue'))
-  const ReturnCageDialog = defineAsyncComponent(() => import('/@/view/animal/application/components/ReturnCageDialog.vue'))
+const getDicts = () => {
+  Promise.all([platAnimalCageApplicationApi.getAnimalTypeList({})]).then(([animalType]) => {
+    animalTypeList.value = animalType.data
+  })
+}
+const changeType = () => {
+  state.queryParams.pageNum = 1
+  state.pageList = []
+  onLoad()
+}
 
-  const { userInfos } = useUserInfos()
-  const platAnimalCageApplicationApi = usePlatAnimalCageApplicationApi()
+const formatApproveStatus = (status: number) => {
+  return ApproveStatusList.find((item) => item.id === status)?.name || ''
+}
 
-  const returnCageDialogRef = ref<ReturnCageDialogInstance>()
-  const detailModalRef = ref<DetailModalInstance>()
+const handleRefundable = (row: any) => {
+  currentRefundableItemNumber.value = row.number
+  returnCageDialogRef.value.handleOpenRefundableDialog(row.id)
+}
 
-  const showDetailDialog = ref<boolean>(false)
-  const activeStatus = ref<MyCageType>(MyCageType.MINE_CAGE)
-  const currentRefundableItemNumber = ref<number>(0)
+const handleRefresh = () => {
+  resetQueryParams()
+  onLoad()
+}
 
-  const state = reactive({
-    pageList: [],
-    loading: false,
-    finished: false,
-    queryParams: {
+const resetQueryParams = () => {
+  ; (state.pageList = []),
+    (state.loading = false),
+    (state.finished = false),
+    (state.queryParams = {
       pageNum: 1,
       pageSize: 25,
       isMyself: 1
-    }
-  })
+    })
+}
 
-  const changeType = () => {
-    state.queryParams.pageNum = 1
-    state.pageList = []
-    onLoad()
-  }
+const onLoad = async (isSearch?: boolean) => {
+  state.loading = true
+  const apiRequest =
+    activeStatus.value === MyCageType.MINE_CAGE
+      ? platAnimalCageApplicationApi.getList(state.queryParams)
+      : platAnimalCageApplicationApi.getMyCageHistoryList(state.queryParams)
 
-  const formatApproveStatus = (status: number) => {
-    return ApproveStatusList.find((item) => item.id === status)?.name || ''
-  }
+  const [err, res]: ToResponse = await to(apiRequest)
+  if (err) return
+  const list = res?.data?.list || []
+  state.loading = false
 
-  const handleRefundable = (row: any) => {
-    currentRefundableItemNumber.value = row.number
-    returnCageDialogRef.value.handleOpenRefundableDialog(row.id)
-  }
-
-  const handleRefresh = () => {
-    resetQueryParams()
-    onLoad()
+  if (!isSearch) {
+    for (const item of list) {
+      state.pageList.push(item)
+    }
+    state.queryParams.pageNum++
+    if (list.length < state.queryParams.pageSize) {
+      state.finished = true
+    }
+  } else {
+    state.pageList = list
   }
+}
 
-  const resetQueryParams = () => {
-    ;(state.pageList = []),
-      (state.loading = false),
-      (state.finished = false),
-      (state.queryParams = {
-        pageNum: 1,
-        pageSize: 25,
-        isMyself: 1
-      })
-  }
+const handleCheckDetail = (row: any) => {
+  detailModalRef.value.initForm(row.id)
+  showDetailDialog.value = true
+}
 
-  const onLoad = async (isSearch?: boolean) => {
-    state.loading = true
-    const apiRequest =
-      activeStatus.value === MyCageType.MINE_CAGE
-        ? platAnimalCageApplicationApi.getList(state.queryParams)
-        : platAnimalCageApplicationApi.getMyCageHistoryList(state.queryParams)
+onMounted(() => {
+  onLoad()
+  getDicts()
+})
+</script>
 
-    const [err, res]: ToResponse = await to(apiRequest)
+<style lang="scss" scoped>
+.cage-container {
+  position: relative;
+  display: flex;
+  flex-direction: column;
 
-    state.loading = false
+  .list-container {
+    overflow-y: auto;
+    padding: 10px;
+    border-radius: 4px;
+    flex: 1;
+  }
 
-    if (err) return
-    
-    const list = res?.data?.list || []
+  .van-list {
+    .van-cell {
+      background-color: #fff;
 
-    if (!isSearch) {
-      for (const item of list) {
-        state.pageList.push(item)
+      +.van-cell {
+        margin-top: 10px;
       }
-      state.queryParams.pageNum++
-      if (list.length < state.queryParams.pageSize) {
-        state.finished = true
+
+      header,
+      footer {
+        color: #333;
       }
-    } else {
-      state.pageList = list
-    }
-  }
 
-  const handleCheckDetail = (row: any) => {
-    detailModalRef.value.initForm(row.id)
-    showDetailDialog.value = true
-  }
+      .title {
+        flex: 1;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        text-align: left;
+      }
 
-  onMounted(() => {
-    onLoad()
-  })
-</script>
+      .inst-title {
+        color: #333;
+        text-align: left;
+        flex: 1;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-top: 4px;
 
-<style lang="scss" scoped>
-  .cage-container {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    .list-container {
-      overflow-y: auto;
-      padding: 10px;
-      border-radius: 4px;
-      flex: 1;
-    }
-    .van-list {
-      .van-cell {
-        background-color: #fff;
-        + .van-cell {
-          margin-top: 10px;
-        }
-        header,
-        footer {
-          color: #333;
-        }
-        .title {
-          flex: 1;
-          white-space: nowrap;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          text-align: left;
-        }
-        .inst-title {
-          color: #333;
-          text-align: left;
-          flex: 1;
-          overflow: hidden;
-          white-space: nowrap;
-          text-overflow: ellipsis;
-          margin-top: 4px;
-          span:first-child {
-            color: rgb(120, 120, 120);
-          }
-        }
-        .time {
-          color: #f69a4d;
+        span:first-child {
+          color: rgb(120, 120, 120);
         }
       }
+
+      .time {
+        color: #f69a4d;
+      }
     }
   }
+}
 </style>

+ 74 - 16
src/view/animal/return/index.vue

@@ -7,16 +7,30 @@
             <el-option v-for="item in ApproveStatusList" :key="item.id" :label="item.name" :value="item.id"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item prop="serialNo">
-          <el-date-picker
-            v-model="dateTime"
-            type="daterange"
+        <el-form-item prop="dateRange">
+          <el-input
+            v-model="dateRangeText"
+            placeholder="请选择申请时间范围"
+            readonly
             style="width: 100%"
-            start-placeholder="开始时间"
-            end-placeholder="结束时间"
-            clearable
-            @change="search"
-          />
+            @click="showCalendar = true"
+          >
+            <template #suffix>
+              <el-icon
+                v-if="dateRangeText"
+                style="cursor: pointer; margin-right: 8px"
+                @click.stop="clearDateRange"
+              >
+                <Close />
+              </el-icon>
+              <el-icon
+                style="cursor: pointer"
+                @click.stop="showCalendar = true"
+              >
+                <Calendar />
+              </el-icon>
+            </template>
+          </el-input>
         </el-form-item>
         <el-form-item prop="serialNo">
           <el-input v-model="state.queryParams.userName" style="width: 100%" placeholder="申请人" clearable @change="search" />
@@ -89,13 +103,28 @@
       </van-list>
     </div>
   </div>
+
+  <van-popup
+    v-model:show="showCalendar"
+    position="bottom"
+    :style="{ height: '80vh' }"
+    round
+  >
+    <van-calendar
+      v-model:show="showCalendar"
+      type="range"
+      :min-date="new Date(1900, 0, 1)"
+      @confirm="onDateRangeConfirm"
+    />
+  </van-popup>
 </template>
 
 <script setup lang="ts">
-  import { ref, reactive, onMounted } from 'vue'
+  import { ref, reactive, onMounted, computed } from 'vue'
   import to from 'await-to-js'
   import dayjs from 'dayjs'
   import { useRoute } from 'vue-router'
+  import { Calendar, Close } from '@element-plus/icons-vue'
 
   import { formatDate } from '/@/utils/formatTime'
   import { usePlatAnimalCageApplicationApi } from '/@/api/platform/animal'
@@ -110,6 +139,17 @@
   const returnCageDialogRef = ref()
   const showDetailDialog = ref<boolean>(false)
   const dateTime = ref<any>([])
+  const showCalendar = ref(false)
+  const selectedDateRange = ref<[Date, Date] | null>(null)
+  const dateRangeText = computed(() => {
+    if (selectedDateRange.value && selectedDateRange.value.length === 2) {
+      const start = dayjs(selectedDateRange.value[0]).format('YYYY-MM-DD')
+      const end = dayjs(selectedDateRange.value[1]).format('YYYY-MM-DD')
+      return `${start} 至 ${end}`
+    }
+    return ''
+  })
+
   const state = reactive({
     queryParams: {
       categoryId: null,
@@ -119,8 +159,8 @@
       pageNum: 1,
       pageSize: 20,
       userName: '',
-      startDate: '',
-      endDate: '',
+      beginTime: '',
+      endTime: '',
       approveStatus: ''
     },
     finished: false,
@@ -138,8 +178,8 @@
       pageNum: 1,
       pageSize: 20,
       userName: '',
-      startDate: '',
-      endDate: '',
+      beginTime: '',
+      endTime: '',
       approveStatus: ''
     }
     ;(state.finished = false), (state.loading = true), (state.list = [] as any[])
@@ -152,11 +192,11 @@
     }
 
     if (dateTime.value && dateTime.value[0]) {
-      payload.startDate = dayjs(dateTime.value[0]).format('YYYY-MM-DD')
+      payload.beginTime = dayjs(dateTime.value[0]).format('YYYY-MM-DD') + ' 00:00:00'
     }
 
     if (dateTime.value && dateTime.value[1]) {
-      payload.endDate = dayjs(dateTime.value[1]).format('YYYY-MM-DD')
+      payload.endTime = dayjs(dateTime.value[1]).format('YYYY-MM-DD') + ' 23:59:59'
     }
 
     Object.entries(payload).forEach(([key, value]) => {
@@ -181,6 +221,24 @@
     return ApproveStatusList.find((item) => item.id === status)?.name || ''
   }
 
+  const onDateRangeConfirm = (values: Date[]) => {
+    if (values && values.length === 2) {
+      selectedDateRange.value = [values[0], values[1]]
+      dateTime.value = [
+        dayjs(values[0]).format('YYYY-MM-DD'),
+        dayjs(values[1]).format('YYYY-MM-DD')
+      ]
+      showCalendar.value = false
+      search()
+    }
+  }
+
+  const clearDateRange = () => {
+    selectedDateRange.value = null
+    dateTime.value = []
+    search()
+  }
+
   const onLoad = async (isSearch?: boolean) => {
     const [err, res]: ToResponse = await to(
       platAnimalCageApplicationApi.getCageReleaseList({

+ 0 - 1
src/view/entry/detail.vue

@@ -508,7 +508,6 @@
     .card {
       border-radius: 4px;
       background-color: #fff;
-      padding: 10px;
       box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
       & + .card {
         margin-top: 10px;

+ 87 - 30
src/view/entry/manage.vue

@@ -26,25 +26,31 @@
               ></el-option>
             </el-select>
           </el-form-item>
-          <el-form-item prop="serialNo">
-            <div class="flex justify-between">
-              <el-date-picker
-                v-model="startTime"
-                style="width: 48%"
-                type="date"
-                placeholder="开始时间"
-                clearable
-                @change="search"
-              />
-              <el-date-picker
-                v-model="endTime"
-                style="width: 48%"
-                type="date"
-                placeholder="结束时间"
-                clearable
-                @change="search"
-              />
-            </div>
+
+          <el-form-item prop="dateRange">
+            <el-input
+              v-model="dateRangeText"
+              placeholder="请选择时间范围"
+              readonly
+              style="width: 100%"
+              @click="showCalendar = true"
+            >
+              <template #suffix>
+                <el-icon
+                  v-if="dateRangeText"
+                  style="cursor: pointer; margin-right: 8px"
+                  @click.stop="clearDateRange"
+                >
+                  <Close />
+                </el-icon>
+                <el-icon
+                  style="cursor: pointer"
+                  @click.stop="showCalendar = true"
+                >
+                  <Calendar />
+                </el-icon>
+              </template>
+            </el-input>
           </el-form-item>
 
           <el-form-item
@@ -53,9 +59,10 @@
           >
             <el-input
               v-model="state.queryParams.memberName"
-              placeholder="入室申请名称"
+              placeholder="入室申请名称"
               clearable
               @keyup.enter="search"
+              @input="handleMemberNameInput"
             />
           </el-form-item>
         </el-form>
@@ -213,7 +220,7 @@
                 <span class="title ml8">
                   {{
                     item.appointEndDate
-                      ? `${formatDate(new Date(item.appointEndDate), 'YYYY-mm-dd')}~${formatDate(
+                      ? `${formatDate(new Date(item.appointStartDate), 'YYYY-mm-dd')}~${formatDate(
                           new Date(item.appointEndDate),
                           'YYYY-mm-dd',
                         )}`
@@ -293,19 +300,37 @@
       ref="confirmRef"
       @refresh="search"
     />
+
+    <!-- 日期范围选择日历 -->
+    <van-popup
+      v-model:show="showCalendar"
+      position="bottom"
+      :style="{ height: '80vh' }"
+      round
+    >
+      <van-calendar
+        v-model:show="showCalendar"
+        type="range"
+        :min-date="new Date(1900, 0, 1)"
+        @confirm="onDateRangeConfirm"
+      />
+    </van-popup>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { ref, reactive, onMounted, defineAsyncComponent } from 'vue'
+  import { ref, reactive, onMounted, defineAsyncComponent, computed } from 'vue'
   import { useRouter } from 'vue-router'
   import to from 'await-to-js'
   import { storeToRefs } from 'pinia'
+  import { Calendar, Close } from '@element-plus/icons-vue'
+  import dayjs from 'dayjs'
 
   import { usePlatformAppointApi } from '/@/api/platform/appoint'
   import { useCellAssignApi } from '/@/api/platform/home/assign'
   import { formatDate } from '/@/utils/formatTime'
   import { useUserInfo } from '/@/stores/userInfo'
+  import { debounce } from '/@/utils/other'
   import exportFileUtil from '/@/utils/base64ToFile'
 
   const UploadDialog = defineAsyncComponent(() => import('/@/view/entry/components/upload.vue'))
@@ -344,8 +369,18 @@
   const detailsDialog = ref()
   const allocateRef = ref()
   const confirmRef = ref()
-  const startTime = ref('')
-  const endTime = ref('')
+  const dateRange = ref<string[]>([])
+  const showCalendar = ref(false)
+  const selectedDateRange = ref<[Date, Date] | null>(null)
+
+  const dateRangeText = computed(() => {
+    if (selectedDateRange.value && selectedDateRange.value.length === 2) {
+      const start = dayjs(selectedDateRange.value[0]).format('YYYY-MM-DD')
+      const end = dayjs(selectedDateRange.value[1]).format('YYYY-MM-DD')
+      return `${start} 至 ${end}`
+    }
+    return ''
+  })
   const state = reactive({
     queryParams: {
       pageNum: 1,
@@ -424,15 +459,37 @@
     state.loading = true
   }
 
+  const onDateRangeConfirm = (values: Date[]) => {
+    if (values && values.length === 2) {
+      selectedDateRange.value = [values[0], values[1]]
+      dateRange.value = [dayjs(values[0]).format('YYYY-MM-DD'), dayjs(values[1]).format('YYYY-MM-DD')]
+      showCalendar.value = false
+      search()
+    }
+  }
+
+  const clearDateRange = () => {
+    selectedDateRange.value = null
+    dateRange.value = []
+    search()
+  }
+
+  // 防抖后的搜索函数
+  const debouncedSearch = debounce(() => {
+    search()
+  }, 500)
+
+  // 处理入室申请名称输入
+  const handleMemberNameInput = () => {
+    debouncedSearch()
+  }
+
   const search = () => {
-    if (startTime.value) {
-      state.queryParams.dateTime[0] = startTime.value
+    if (dateRange.value && dateRange.value.length === 2) {
+      state.queryParams.dateTime[0] = dateRange.value[0] + ' 00:00:00'
+      state.queryParams.dateTime[1] = dateRange.value[1] + ' 23:59:59'
     } else {
       state.queryParams.dateTime[0] = ''
-    }
-    if (endTime.value) {
-      state.queryParams.dateTime[1] = endTime.value
-    } else {
       state.queryParams.dateTime[1] = ''
     }
     resetQuery()

+ 83 - 28
src/view/entry/mine.vue

@@ -26,25 +26,30 @@
               ></el-option>
             </el-select>
           </el-form-item>
-          <el-form-item prop="serialNo">
-            <div class="flex justify-between">
-              <el-date-picker
-                v-model="startTime"
-                style="width: 48%"
-                type="date"
-                placeholder="开始时间"
-                clearable
-                @change="search"
-              />
-              <el-date-picker
-                v-model="endTime"
-                style="width: 48%"
-                type="date"
-                placeholder="结束时间"
-                clearable
-                @change="search"
-              />
-            </div>
+          <el-form-item prop="dateRange">
+            <el-input
+              v-model="dateRangeText"
+              placeholder="请选择入室时间范围"
+              readonly
+              style="width: 100%"
+              @click="showCalendar = true"
+            >
+              <template #suffix>
+                <el-icon
+                  v-if="dateRangeText"
+                  style="cursor: pointer; margin-right: 8px"
+                  @click.stop="clearDateRange"
+                >
+                  <Close />
+                </el-icon>
+                <el-icon
+                  style="cursor: pointer"
+                  @click.stop="showCalendar = true"
+                >
+                  <Calendar />
+                </el-icon>
+              </template>
+            </el-input>
           </el-form-item>
           <el-form-item prop="serialNo">
             <el-input
@@ -53,6 +58,7 @@
               placeholder="资源名称"
               clearable
               @keyup.enter="search"
+              @input="handleResNameInput"
             />
           </el-form-item>
         </el-form>
@@ -232,15 +238,31 @@
     </el-dialog>
     <Appoint ref="appointRef" />
     <RenewalEdit ref="renewalEditRef" />
+
+    <!-- 日期范围选择日历 -->
+    <van-popup
+      v-model:show="showCalendar"
+      position="bottom"
+      :style="{ height: '80vh' }"
+      round
+    >
+      <van-calendar
+        v-model:show="showCalendar"
+        type="range"
+        :min-date="new Date(1900, 0, 1)"
+        @confirm="onDateRangeConfirm"
+      />
+    </van-popup>
   </div>
 </template>
 
 <script name="entryMine" lang="ts" setup>
-  import { reactive, onMounted, ref } from 'vue'
+  import { reactive, onMounted, ref, computed } from 'vue'
   import { useRouter } from 'vue-router'
   import to from 'await-to-js'
   import { storeToRefs } from 'pinia'
   import { ElMessage } from 'element-plus'
+  import { Calendar, Close } from '@element-plus/icons-vue'
   import dayjs from 'dayjs'
 
   import { usePlatformAppointApi } from '/@/api/platform/appoint'
@@ -248,6 +270,7 @@
   import { usePlatformApi } from '/@/api/platform/home'
   import { formatDate } from '/@/utils/formatTime'
   import { useUserInfo } from '/@/stores/userInfo'
+  import { debounce } from '/@/utils/other'
   import Appoint from './components/appoint.vue'
   import RenewalEdit from './components/renewalEdit.vue'
 
@@ -271,10 +294,20 @@
   const renewalEditRef = ref()
   const appointRef = ref()
   const showOutDialog = ref<boolean>(false)
-  const startTime = ref('')
-  const endTime = ref('')
+  const dateRange = ref<string[]>([])
+  const showCalendar = ref(false)
+  const selectedDateRange = ref<[Date, Date] | null>(null)
   const platformList = ref<any[]>([])
 
+  const dateRangeText = computed(() => {
+    if (selectedDateRange.value && selectedDateRange.value.length === 2) {
+      const start = dayjs(selectedDateRange.value[0]).format('YYYY-MM-DD')
+      const end = dayjs(selectedDateRange.value[1]).format('YYYY-MM-DD')
+      return `${start} 至 ${end}`
+    }
+    return ''
+  })
+
   const state = reactive({
     queryParams: {
       platformId: null,
@@ -360,15 +393,37 @@
     state.loading = true
   }
 
+  const onDateRangeConfirm = (values: Date[]) => {
+    if (values && values.length === 2) {
+      selectedDateRange.value = [values[0], values[1]]
+      dateRange.value = [dayjs(values[0]).format('YYYY-MM-DD'), dayjs(values[1]).format('YYYY-MM-DD')]
+      showCalendar.value = false
+      search()
+    }
+  }
+
+  const clearDateRange = () => {
+    selectedDateRange.value = null
+    dateRange.value = []
+    search()
+  }
+
+  // 防抖后的搜索函数
+  const debouncedSearch = debounce(() => {
+    search()
+  }, 500)
+
+  // 处理资源名称输入
+  const handleResNameInput = () => {
+    debouncedSearch()
+  }
+
   const search = () => {
-    if (startTime.value) {
-      state.queryParams.dateTime[0] = startTime.value
+    if (dateRange.value && dateRange.value.length === 2) {
+      state.queryParams.dateTime[0] = dateRange.value[0] + ' 00:00:00'
+      state.queryParams.dateTime[1] = dateRange.value[1] + ' 23:59:59'
     } else {
       state.queryParams.dateTime[0] = ''
-    }
-    if (endTime.value) {
-      state.queryParams.dateTime[1] = endTime.value
-    } else {
       state.queryParams.dateTime[1] = ''
     }
     resetQuery()

+ 3 - 0
src/view/instr/list.vue

@@ -44,6 +44,9 @@
       </div>
     </div>
     <van-tabbar route :placeholder="true">
+      <van-tabbar-item replace to="/home" icon="wap-home-o">
+        首页
+      </van-tabbar-item>
       <van-tabbar-item replace to="/instr-follow" icon="star">
         收藏仪器
       </van-tabbar-item>

+ 190 - 184
src/view/instr/repairReport/index.vue

@@ -28,8 +28,8 @@
               <p class="inst-title">
                 <span>设备负责人</span>
                 <span class="title ml8">
-                  {{ item.equipmentManager.split('-')[0] }} <span class="ml10">(</span> {{ item.equipmentManager.split('-')[1] }} )</span
-                >
+                  {{ item.equipmentManager.split('-')[0] }} <span class="ml10">(</span> {{
+                    item.equipmentManager.split('-')[1] }} )</span>
               </p>
               <p class="inst-title">
                 <span>报修人</span>
@@ -46,14 +46,8 @@
               <div class="pic-col">
                 <span>故障图片</span>
                 <div>
-                  <van-image
-                    class="mr10 mb10"
-                    v-for="(v, i) in JSON.parse(item.faultPhoto)"
-                    width="100"
-                    height="100"
-                    :src="v.url"
-                    @click="showPreviewImg(item.faultPhoto, i)"
-                  />
+                  <van-image class="mr10 mb10" v-for="(v, i) in JSON.parse(item.faultPhoto)" width="100" height="100"
+                    :src="v.url" @click="showPreviewImg(item.faultPhoto, i)" />
                 </div>
               </div>
               <footer class="flex justify-between mt4">
@@ -61,23 +55,12 @@
                 <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
               </footer>
               <div class="flex mt20 btns">
-                <van-button
-                  style="width: 70px; height: 30px; margin: 0; font-size: 14px"
-                  class="mr20"
-                  type="danger"
-                  size="small"
-                  v-if="item.status == 10"
-                  @click.native.stop="onClose(item)"
-                >
+                <van-button style="width: 70px; height: 30px; margin: 0; font-size: 14px" class="mr20" type="danger"
+                  size="small" v-if="item.status == 10" @click.native.stop="onClose(item)">
                   关闭
                 </van-button>
-                <van-button
-                  style="width: 70px; height: 30px; margin: 0; font-size: 14px"
-                  type="success"
-                  size="small"
-                  v-if="item.status == 10"
-                  @click.native.stop="onConfirm(item.id)"
-                >
+                <van-button style="width: 70px; height: 30px; margin: 0; font-size: 14px" type="success" size="small"
+                  v-if="item.status == 10" @click.native.stop="onConfirm(item.id)">
                   确认
                 </van-button>
               </div>
@@ -86,192 +69,215 @@
         </van-cell>
       </van-list>
     </div>
-    <van-floating-bubble v-model:offset="offset" icon="plus" @click="onAdd" axis="y" />
+    <!-- <van-floating-bubble v-model:offset="offset" icon="plus" @click="onAdd" axis="y" /> -->
     <van-floating-bubble v-model:offset="offsetScan" icon="scan" @click="scan" axis="y" />
+    <van-action-bar placeholder>
+      <van-action-bar-icon icon="wap-home-o" text="首页" @click="router.push('/home')" />
+      <!-- <van-action-bar-icon icon="revoke" text="返回" @click="router.push('/entry')" /> -->
+      <!-- <van-action-bar-icon
+          :icon="state.instDetail.following ? 'star' : 'star-o'"
+          :class="{ follow: state.instDetail.following }"
+          :text="state.instDetail.following ? '取消收藏' : '收藏'"
+          @click="handleFollowInst"
+        /> -->
+      <van-action-bar-button type="primary" text="创建报修" @click="onAdd" />
+    </van-action-bar>
   </div>
 </template>
 
 <script name="repairReportHome" lang="ts" setup>
-  import to from 'await-to-js'
-  import { formatDate } from '/@/utils/formatTime'
-  import { onMounted, reactive, ref } from 'vue'
-  import { useRouter, useRoute } from 'vue-router'
-  import { useRepairReportApi } from '/@/api/instr/repairReport'
-  import { showImagePreview, showConfirmDialog, showNotify } from 'vant'
-  import 'vant/es/image-preview/style'
-  import { useUserInfo } from '/@/stores/userInfo'
-  import { useInstrApi } from '/@/api/instr/index'
+import to from 'await-to-js'
+import { formatDate } from '/@/utils/formatTime'
+import { onMounted, reactive, ref } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useRepairReportApi } from '/@/api/instr/repairReport'
+import { showImagePreview, showConfirmDialog, showNotify } from 'vant'
+import 'vant/es/image-preview/style'
+import { useUserInfo } from '/@/stores/userInfo'
+import { useInstrApi } from '/@/api/instr/index'
 
-  const repairReportApi = useRepairReportApi()
-  const instApi = useInstrApi()
+const repairReportApi = useRepairReportApi()
+const instApi = useInstrApi()
 
-  const router = useRouter()
-  const route = useRoute()
-  const offset = ref({ x: -80, y: 450 })
-  const offsetScan = ref({ x: -80, y: 540 })
-  const state = reactive({
-    queryParams: {
-      equipmentName: '',
-      status: '10',
-      pageNum: 1,
-      pageSize: 10
-    },
-    finished: false,
-    loading: true,
-    list: [] as any[]
-  })
+const router = useRouter()
+const route = useRoute()
+const offset = ref({ x: -80, y: 450 })
+const offsetScan = ref({ x: -80, y: 540 })
+const state = reactive({
+  queryParams: {
+    equipmentName: '',
+    status: '10',
+    pageNum: 1,
+    pageSize: 10
+  },
+  finished: false,
+  loading: true,
+  list: [] as any[]
+})
+
+const changeType = () => {
+  state.queryParams.pageNum = 1
+  state.list = []
+  onLoad()
+}
 
-  const changeType = () => {
-    state.queryParams.pageNum = 1
-    state.list = []
-    onLoad()
+const onLoad = async () => {
+  const [err, res]: ToResponse = await to(repairReportApi.getList(state.queryParams))
+  if (err) return
+  const list = res?.data?.list || []
+  for (const item of list) {
+    state.list.push(item)
+  }
+  state.loading = false
+  state.queryParams.pageNum++
+  if (list.length < state.queryParams.pageSize) {
+    state.finished = true
   }
+}
 
-  const onLoad = async () => {
-    const [err, res]: ToResponse = await to(repairReportApi.getList(state.queryParams))
-    if (err) return
-    const list = res?.data?.list || []
-    for (const item of list) {
-      state.list.push(item)
-    }
-    state.loading = false
-    state.queryParams.pageNum++
-    if (list.length < state.queryParams.pageSize) {
-      state.finished = true
-    }
+onMounted(() => {
+  const type = route.query.type
+  if (type) {
+    state.queryParams.status = type as string
   }
+  onLoad()
+})
 
-  onMounted(() => {
-    const type = route.query.type
-    if (type) {
-      state.queryParams.status = type as string
-    }
-    onLoad()
+const onClose = async (item: any) => {
+  showConfirmDialog({
+    title: '提示',
+    message: '确认关闭当前报修记录?'
+  }).then(async () => {
+    const [err, res]: ToResponse = await to(repairReportApi.close({ id: item.id }))
+    if (err) return
+    showNotify({ type: 'success', message: '关闭成功' })
+    changeType() // 刷新列表
   })
+}
 
-  const onClose = async (item: any) => {
-    showConfirmDialog({
-      title: '提示',
-      message: '确认关闭当前报修记录?'
-    }).then(async () => {
-      const [err, res]: ToResponse = await to(repairReportApi.close({ id: item.id }))
-      if (err) return
-      showNotify({ type: 'success', message: '关闭成功' })
-      changeType() // 刷新列表
-    })
+const showPreviewImg = (fullPhoto: string, index: number) => {
+  console.log('fullPhoto', fullPhoto)
+  const images = JSON.parse(fullPhoto).map((item: any) => item.url)
+  showImagePreview({
+    images,
+    startPosition: index
+  })
+}
+
+const onAdd = () => {
+  router.push('/inst/repairReport/add')
+}
+
+const onConfirm = (id: number) => {
+  router.push('/inst/repairReport/confirm?id=' + id)
+}
+
+const scan = async () => {
+  // decodeInstInfo('8f1117b76dc380334e00b6d980bde73d04a5a0f82c1f06b70d0e82108e8df201d9bcb81674cdf004c9b9046948e12b1b93467a058aec66a0f8b3f42e')
+  // return
+  const res = await useUserInfo().scanCode()
+  if (res) {
+    decodeInstInfo(res as string)
+  } else {
+    showNotify({ type: 'danger', message: '扫码失败,请重试' })
   }
+}
 
-  const showPreviewImg = (fullPhoto: string, index: number) => {
-    console.log('fullPhoto', fullPhoto)
-    const images = JSON.parse(fullPhoto).map((item: any) => item.url)
-    showImagePreview({
-      images,
-      startPosition: index
+const decodeInstInfo = async (content: string) => {
+  const [err, res]: ToResponse = await to(instApi.decode({ content }))
+  if (err) return
+  const instInfo = JSON.parse(res?.data.content || '{}')
+  if (instInfo) {
+    router.push({
+      path: '/instr-detail',
+      query: {
+        id: Number(instInfo.id)
+      }
     })
+  } else {
+    showNotify({ type: 'danger', message: '无法识别的设备信息' })
   }
+}
+</script>
 
-  const onAdd = () => {
-    router.push('/inst/repairReport/add')
-  }
+<style lang="scss" scoped>
+.entry-container {
+  position: relative;
+  display: flex;
+  flex-direction: column;
 
-  const onConfirm = (id: number) => {
-    router.push('/inst/repairReport/confirm?id=' + id)
+  .list-container {
+    overflow-y: auto;
+    padding: 10px;
+    border-radius: 4px;
+    flex: 1;
   }
 
-  const scan = async () => {
-    // decodeInstInfo('8f1117b76dc380334e00b6d980bde73d04a5a0f82c1f06b70d0e82108e8df201d9bcb81674cdf004c9b9046948e12b1b93467a058aec66a0f8b3f42e')
-    // return
-    const res = await useUserInfo().scanCode()
-    if (res) {
-      decodeInstInfo(res as string)
-    } else {
-      showNotify({ type: 'danger', message: '扫码失败,请重试' })
-    }
-  }
+  .van-list {
+    .van-cell {
+      background-color: #fff;
 
-  const decodeInstInfo = async (content: string) => {
-    const [err, res]: ToResponse = await to(instApi.decode({ content }))
-    if (err) return
-    const instInfo = JSON.parse(res?.data.content || '{}')
-    if (instInfo) {
-      router.push({
-        path: '/instr-detail',
-        query: {
-          id: Number(instInfo.id)
-        }
-      })
-    } else {
-      showNotify({ type: 'danger', message: '无法识别的设备信息' })
-    }
-  }
-</script>
+      +.van-cell {
+        margin-top: 10px;
+      }
 
-<style lang="scss" scoped>
-  .entry-container {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    .list-container {
-      overflow-y: auto;
-      padding: 10px;
-      border-radius: 4px;
-      flex: 1;
-    }
-    .van-list {
-      .van-cell {
-        background-color: #fff;
-        + .van-cell {
-          margin-top: 10px;
-        }
-        header,
-        footer {
-          color: #333;
-        }
-        .title {
-          flex: 1;
-          white-space: nowrap;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          text-align: left;
-        }
-        .inst-title {
-          color: #333;
-          text-align: left;
-          flex: 1;
-          overflow: hidden;
-          white-space: nowrap;
-          text-overflow: ellipsis;
-          margin-top: 4px;
-          span:first-child {
-            color: rgb(120, 120, 120);
-          }
-        }
-        .time {
-          color: #f69a4d;
+      header,
+      footer {
+        color: #333;
+      }
+
+      .title {
+        flex: 1;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        text-align: left;
+      }
+
+      .inst-title {
+        color: #333;
+        text-align: left;
+        flex: 1;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-top: 4px;
+
+        span:first-child {
+          color: rgb(120, 120, 120);
         }
       }
+
+      .time {
+        color: #f69a4d;
+      }
     }
   }
-  .pic-col {
-    color: #333;
-    text-align: left;
-    flex: 1;
-    margin-top: 4px;
-    display: flex;
-    span:first-child {
-      width: 100px;
-      color: rgb(120, 120, 120);
-    }
-  }
-  .btns {
-    justify-content: flex-end;
-  }
-  .search-wrap {
-    display: flex;
-    align-items: center;
-    justify-content: space-around;
-    padding: 0 15px;
-    margin: 10px 0 0;
-    background: #fff;
+}
+
+.pic-col {
+  color: #333;
+  text-align: left;
+  flex: 1;
+  margin-top: 4px;
+  display: flex;
+
+  span:first-child {
+    width: 100px;
+    color: rgb(120, 120, 120);
   }
+}
+
+.btns {
+  justify-content: flex-end;
+}
+
+.search-wrap {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  padding: 0 15px;
+  margin: 10px 0 0;
+  background: #fff;
+}
 </style>

+ 171 - 202
src/view/laboratory/inspection/index.vue

@@ -9,68 +9,30 @@
 <template>
   <div class="entry-container">
     <div class="search-wrap">
-      <van-search
-        placeholder="请输入任务名称"
-        v-model="state.queryParams.taskTitle"
-        :clearabled="true"
-        style="padding: 0; flex: 1"
-        class="mr10"
-      ></van-search>
+      <van-search placeholder="请输入任务名称" v-model="state.queryParams.taskTitle" :clearabled="true"
+        style="padding: 0; flex: 1" class="mr10"></van-search>
       <div>
-        <van-button
-          type="primary"
-          style="width: 60px"
-          shape="circle"
-          size="small"
-          @click="onLoad"
-        >
+        <van-button type="primary" style="width: 60px" shape="circle" size="small" @click="onLoad">
           搜索
         </van-button>
       </div>
     </div>
-    <van-tabs
-      v-model:active="state.queryParams.frcnStatus"
-      @change="changeType"
-    >
-      <van-tab
-        title="待整改"
-        name="10"
-      ></van-tab>
-      <van-tab
-        title="整改完成"
-        name="40"
-      ></van-tab>
-      <van-tab
-        title="全部"
-        name=""
-      ></van-tab>
+    <van-tabs v-model:active="state.queryParams.frcnStatus" @change="changeType">
+      <van-tab title="待整改" name="10"></van-tab>
+      <van-tab title="整改完成" name="40"></van-tab>
+      <van-tab title="全部" name=""></van-tab>
     </van-tabs>
     <div class="list-container">
-      <van-list
-        v-model:loading="state.loading"
-        :finished="state.finished"
-        finished-text="没有更多了"
-        @load="onLoad"
-      >
-        <van-cell
-          v-for="item in state.list"
-          :key="item"
-          @click.stop="toDetails(item.id)"
-        >
+      <van-list v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
+        <van-cell v-for="item in state.list" :key="item" @click.stop="toDetails(item.id)">
           <template #default>
             <div class="list">
               <header class="flex justify-between">
                 <strong class="title">{{ `${item.taskTitle}` }}</strong>
-                <van-tag
-                  v-if="item.frcnStatus == 10"
-                  type="primary"
-                >
+                <van-tag v-if="item.frcnStatus == 10" type="primary">
                   待整改
                 </van-tag>
-                <van-tag
-                  v-else-if="item.frcnStatus == 40"
-                  type="success"
-                >
+                <van-tag v-else-if="item.frcnStatus == 40" type="success">
                   整改完成
                 </van-tag>
               </header>
@@ -84,18 +46,12 @@
               </p>
               <p class="inst-title">
                 <span>限定整改时间</span>
-                <span
-                  v-if="item.frcnStartDate && item.frcnEndDate"
-                  class="title ml8"
-                >
+                <span v-if="item.frcnStartDate && item.frcnEndDate" class="title ml8">
                   {{ formatDate(new Date(item.frcnStartDate), 'YYYY-mm-dd') }}~{{
                     formatDate(new Date(item.frcnEndDate), 'YYYY-mm-dd')
                   }}
                 </span>
-                <span
-                  v-else
-                  class="title ml8"
-                >
+                <span v-else class="title ml8">
                   -
                 </span>
               </p>
@@ -110,13 +66,8 @@
                 <span class="time">{{ formatDate(new Date(item.createdTime), 'YYYY-mm-dd') }}</span>
               </footer> -->
               <div class="flex mt20 btns">
-                <van-button
-                  style="width: 70px; height: 30px; margin: 0; font-size: 14px"
-                  type="success"
-                  size="small"
-                  v-if="item.frcnStatus == 10"
-                  @click.native.stop="onFeedback(item.id)"
-                >
+                <van-button style="width: 70px; height: 30px; margin: 0; font-size: 14px" type="success" size="small"
+                  v-if="item.frcnStatus == 10" @click.native.stop="onFeedback(item.id)">
                   反馈
                 </van-button>
               </div>
@@ -125,165 +76,183 @@
         </van-cell>
       </van-list>
     </div>
-    <van-floating-bubble
-      v-model:offset="offset"
-      icon="plus"
-      @click="onAdd"
-      axis="y"
-    />
+    <van-action-bar placeholder>
+      <van-action-bar-icon icon="wap-home-o" text="首页" @click="router.push('/home')" />
+      <!-- <van-action-bar-icon icon="revoke" text="返回" @click="router.push('/entry')" /> -->
+      <!-- <van-action-bar-icon
+          :icon="state.instDetail.following ? 'star' : 'star-o'"
+          :class="{ follow: state.instDetail.following }"
+          :text="state.instDetail.following ? '取消收藏' : '收藏'"
+          @click="handleFollowInst"
+        /> -->
+      <van-action-bar-button type="primary" text="创建巡检" @click="onAdd" />
+    </van-action-bar>
+    <!-- <van-floating-bubble v-model:offset="offset" icon="plus" @click="onAdd" axis="y" /> -->
   </div>
 </template>
 
 <script name="repairReportHome" lang="ts" setup>
-  import to from 'await-to-js'
-  import { formatDate } from '/@/utils/formatTime'
-  import { onMounted, reactive, ref } from 'vue'
-  import { useRouter, useRoute } from 'vue-router'
-  import { useRectificationNewApi } from '/@/api/laboratory/inspection'
-  import { showImagePreview, showConfirmDialog, showNotify } from 'vant'
-  import 'vant/es/image-preview/style'
+import to from 'await-to-js'
+import { formatDate } from '/@/utils/formatTime'
+import { onMounted, reactive, ref } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { useRectificationNewApi } from '/@/api/laboratory/inspection'
+import { showImagePreview, showConfirmDialog, showNotify } from 'vant'
+import 'vant/es/image-preview/style'
 
-  const inspectionNewApi = useRectificationNewApi()
+const inspectionNewApi = useRectificationNewApi()
 
-  const router = useRouter()
-  const route = useRoute()
-  const offset = ref({ x: -80, y: 450 })
-  const offsetScan = ref({ x: -80, y: 540 })
-  const state = reactive({
-    queryParams: {
-      taskTitle: '',
-      frcnStatus: '10',
-      pageNum: 1,
-      pageSize: 10,
-    },
-    finished: false,
-    loading: true,
-    list: [] as any[],
-  })
+const router = useRouter()
+const route = useRoute()
+const offset = ref({ x: -80, y: 450 })
+const offsetScan = ref({ x: -80, y: 540 })
+const state = reactive({
+  queryParams: {
+    taskTitle: '',
+    frcnStatus: '10',
+    pageNum: 1,
+    pageSize: 10,
+  },
+  finished: false,
+  loading: true,
+  list: [] as any[],
+})
 
-  const changeType = () => {
-    state.queryParams.pageNum = 1
-    state.list = []
-    onLoad()
-  }
+const changeType = () => {
+  state.queryParams.pageNum = 1
+  state.list = []
+  onLoad()
+}
 
-  const onLoad = async () => {
-    const [err, res]: ToResponse = await to(inspectionNewApi.getList(state.queryParams))
-    if (err) return
-    const list = res?.data?.list || []
-    for (const item of list) {
-      state.list.push(item)
-    }
-    state.loading = false
-    state.queryParams.pageNum++
-    if (list.length < state.queryParams.pageSize) {
-      state.finished = true
-    }
+const onLoad = async () => {
+  const [err, res]: ToResponse = await to(inspectionNewApi.getList(state.queryParams))
+  if (err) return
+  const list = res?.data?.list || []
+  for (const item of list) {
+    state.list.push(item)
+  }
+  state.loading = false
+  state.queryParams.pageNum++
+  if (list.length < state.queryParams.pageSize) {
+    state.finished = true
   }
+}
 
-  onMounted(() => {
-    onLoad()
-  })
+onMounted(() => {
+  onLoad()
+})
 
-  const onClose = async (item: any) => {
-    // showConfirmDialog({
-    //   title: '提示',
-    //   message: '确认关闭当前报修记录?'
-    // }).then(async () => {
-    //   const [err, res]: ToResponse = await to(repairReportApi.close({ id: item.id }))
-    //   if (err) return
-    //   showNotify({ type: 'success', message: '关闭成功' })
-    //   changeType() // 刷新列表
-    // })
-  }
+const onClose = async (item: any) => {
+  // showConfirmDialog({
+  //   title: '提示',
+  //   message: '确认关闭当前报修记录?'
+  // }).then(async () => {
+  //   const [err, res]: ToResponse = await to(repairReportApi.close({ id: item.id }))
+  //   if (err) return
+  //   showNotify({ type: 'success', message: '关闭成功' })
+  //   changeType() // 刷新列表
+  // })
+}
 
-  const showPreviewImg = (fullPhoto: string, index: number) => {
-    console.log('fullPhoto', fullPhoto)
-    const images = JSON.parse(fullPhoto).map((item: any) => item.url)
-    showImagePreview({
-      images,
-      startPosition: index,
-    })
-  }
+const showPreviewImg = (fullPhoto: string, index: number) => {
+  console.log('fullPhoto', fullPhoto)
+  const images = JSON.parse(fullPhoto).map((item: any) => item.url)
+  showImagePreview({
+    images,
+    startPosition: index,
+  })
+}
 
-  const onAdd = () => {
-    router.push('/lab/inspection/addInspTask')
-  }
+const onAdd = () => {
+  router.push('/lab/inspection/addInspTask')
+}
 
-  const toDetails = async (id: number) => {
-    router.push('/lab/inspection/details?id=' + id)
-  }
+const toDetails = async (id: number) => {
+  router.push('/lab/inspection/details?id=' + id)
+}
 
-  const onFeedback = (id: number) => {
-    router.push('/lab/inspection/feedback?id=' + id)
-  }
+const onFeedback = (id: number) => {
+  router.push('/lab/inspection/feedback?id=' + id)
+}
 </script>
 
 <style lang="scss" scoped>
-  .entry-container {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    .list-container {
-      overflow-y: auto;
-      padding: 10px;
-      border-radius: 4px;
-      flex: 1;
-    }
-    .van-list {
-      .van-cell {
-        background-color: #fff;
-        + .van-cell {
-          margin-top: 10px;
-        }
-        header,
-        footer {
-          color: #333;
-        }
-        .title {
-          flex: 1;
-          white-space: nowrap;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          text-align: left;
-        }
-        .inst-title {
-          color: #333;
-          text-align: left;
-          flex: 1;
-          overflow: hidden;
-          white-space: nowrap;
-          text-overflow: ellipsis;
-          margin-top: 4px;
-          span:first-child {
-            color: rgb(120, 120, 120);
-          }
-        }
-        .time {
-          color: #f69a4d;
+.entry-container {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+
+  .list-container {
+    overflow-y: auto;
+    padding: 10px;
+    border-radius: 4px;
+    flex: 1;
+  }
+
+  .van-list {
+    .van-cell {
+      background-color: #fff;
+
+      +.van-cell {
+        margin-top: 10px;
+      }
+
+      header,
+      footer {
+        color: #333;
+      }
+
+      .title {
+        flex: 1;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        text-align: left;
+      }
+
+      .inst-title {
+        color: #333;
+        text-align: left;
+        flex: 1;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-top: 4px;
+
+        span:first-child {
+          color: rgb(120, 120, 120);
         }
       }
+
+      .time {
+        color: #f69a4d;
+      }
     }
   }
-  .pic-col {
-    color: #333;
-    text-align: left;
-    flex: 1;
-    margin-top: 4px;
-    display: flex;
-    span:first-child {
-      width: 100px;
-      color: rgb(120, 120, 120);
-    }
-  }
-  .btns {
-    justify-content: flex-end;
-  }
-  .search-wrap {
-    display: flex;
-    align-items: center;
-    justify-content: space-around;
-    padding: 15px 15px 0;
-    background: #fff;
+}
+
+.pic-col {
+  color: #333;
+  text-align: left;
+  flex: 1;
+  margin-top: 4px;
+  display: flex;
+
+  span:first-child {
+    width: 100px;
+    color: rgb(120, 120, 120);
   }
+}
+
+.btns {
+  justify-content: flex-end;
+}
+
+.search-wrap {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  padding: 15px 15px 0;
+  background: #fff;
+}
 </style>

+ 11 - 4
src/view/login/index.vue

@@ -14,7 +14,13 @@
         <h4>临床医学公共实验中心</h4>
       </div>
       <van-field v-model="state.form.userName" placeholder="请输入用户名" />
-      <van-field v-model="state.form.password" type="password" placeholder="请输入密码" class="mt10" />
+      <van-field v-model="state.form.password" :type="showPassword ? 'text' : 'password'" placeholder="请输入密码"
+        class="mt10">
+        <template #right-icon>
+          <van-icon :name="showPassword ? 'eye-o' : 'closed-eye'" @click="showPassword = !showPassword"
+            style="cursor: pointer;" />
+        </template>
+      </van-field>
 
       <div class="mt10 input-box code">
         <van-field v-model="state.form.idValueC" placeholder="验证码" />
@@ -27,7 +33,7 @@
         <van-button size="mini" round class="mt10 w50">找回密码</van-button>
         <van-button size="mini" round class="mt10 w50">找回密码</van-button>
       </van-row> -->
-      <van-button type="primary" round class="mt10" @click="onSignIn">登录</van-button>
+      <van-button type="primary" round class="mt10" @click="onSignIn" :loading="state.loading.signIn">登录</van-button>
       <van-button round class="mt10" @click="onRegister">注册</van-button>
       <!-- <van-button round class="mt10" @click="blueTooth">蓝牙</van-button> -->
     </header>
@@ -38,7 +44,7 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, reactive } from 'vue'
+import { onMounted, reactive, ref } from 'vue'
 import to from 'await-to-js'
 import { useLoginApi } from '/@/api/login/index'
 import { Local } from '/@/utils/storage'
@@ -55,6 +61,7 @@ const sm3 = crypto.sm3
 const loginApi = useLoginApi()
 const storesUseUserInfo = useUserInfo()
 const { userInfos, openId, unionId } = storeToRefs(storesUseUserInfo)
+const showPassword = ref(false)
 const state = reactive({
   captchaImage: '',
   loading: {
@@ -87,8 +94,8 @@ const onSignIn = async () => {
   // sm3
   const post = params.openId ? loginApi.weChatLogin : loginApi.signIn
   const [err, res]: ToResponse = await to(post(params))
+  state.loading.signIn = false
   if (err) {
-    state.loading.signIn = false
     await getCaptchaImage()
     state.form.idValueC = ''
     return