|
|
@@ -8,21 +8,11 @@
|
|
|
-->
|
|
|
<template>
|
|
|
<div class="instr-detail">
|
|
|
- <van-swipe
|
|
|
- v-if="noticeInfo.noticeTitle"
|
|
|
- class="my-swipe"
|
|
|
- :autoplay="5000"
|
|
|
- :show-indicators="false"
|
|
|
- vertical
|
|
|
- height="30"
|
|
|
- >
|
|
|
+ <van-swipe v-if="noticeInfo.noticeTitle" class="my-swipe" :autoplay="5000" :show-indicators="false" vertical
|
|
|
+ height="30">
|
|
|
<van-swipe-item @click="state.popupShow = true">
|
|
|
<div class="flex">
|
|
|
- <van-icon
|
|
|
- name="volume-o"
|
|
|
- class="mr4"
|
|
|
- :size="20"
|
|
|
- />
|
|
|
+ <van-icon name="volume-o" class="mr4" :size="20" />
|
|
|
{{ noticeInfo.noticeTitle }}
|
|
|
</div>
|
|
|
</van-swipe-item>
|
|
|
@@ -30,11 +20,7 @@
|
|
|
<header class="flex">
|
|
|
<div class="h100">
|
|
|
<!-- <img :showLoading="true" :src="state.instDetail.instPicture" width="80px" height="80px" /> -->
|
|
|
- <van-image
|
|
|
- width="80px"
|
|
|
- height="80px"
|
|
|
- :src="getImageUrl(state.instDetail.instPicture)"
|
|
|
- />
|
|
|
+ <van-image width="80px" height="80px" :src="getImageUrl(state.instDetail.instPicture)" />
|
|
|
</div>
|
|
|
<div class="i-right ml10">
|
|
|
<div class="h100 flex flex-top flex-column flex-between">
|
|
|
@@ -43,19 +29,11 @@
|
|
|
</div>
|
|
|
<footer>
|
|
|
<div class="flex flex-top mb4 mt-auto">
|
|
|
- <img
|
|
|
- class="i-r-icon"
|
|
|
- src="../../assets/img/user.png"
|
|
|
- v-if="state.instDetail.instHeadName"
|
|
|
- />
|
|
|
+ <img class="i-r-icon" src="../../assets/img/user.png" v-if="state.instDetail.instHeadName" />
|
|
|
<div class="detailTxt">{{ state.instDetail.instHeadName }}</div>
|
|
|
</div>
|
|
|
<div class="flex flex-top">
|
|
|
- <img
|
|
|
- class="i-r-icon"
|
|
|
- src="../../assets/img/address.png"
|
|
|
- v-if="state.instDetail.placeAddress"
|
|
|
- />
|
|
|
+ <img class="i-r-icon" src="../../assets/img/address.png" v-if="state.instDetail.placeAddress" />
|
|
|
<div class="detailTxt">
|
|
|
{{ state.instDetail.placeAddress + setLaboratoryName(state.instDetail.laboratoryName) }}
|
|
|
</div>
|
|
|
@@ -64,27 +42,12 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</header>
|
|
|
- <van-tabs
|
|
|
- v-model:active="active"
|
|
|
- @change="tabChange"
|
|
|
- >
|
|
|
- <van-tab
|
|
|
- title="仪器信息"
|
|
|
- name="info"
|
|
|
- ></van-tab>
|
|
|
- <van-tab
|
|
|
- title="待审核"
|
|
|
- name="approval"
|
|
|
- ></van-tab>
|
|
|
- <van-tab
|
|
|
- title="历史申请"
|
|
|
- name="history"
|
|
|
- ></van-tab>
|
|
|
+ <van-tabs v-model:active="active" @change="tabChange">
|
|
|
+ <van-tab title="仪器信息" name="info"></van-tab>
|
|
|
+ <van-tab title="待审核" name="approval"></van-tab>
|
|
|
+ <van-tab title="历史申请" name="history"></van-tab>
|
|
|
</van-tabs>
|
|
|
- <div
|
|
|
- v-if="active === 'info'"
|
|
|
- class="content"
|
|
|
- >
|
|
|
+ <div v-if="active === 'info'" class="content">
|
|
|
<div class="card">
|
|
|
<h4>仪器信息</h4>
|
|
|
<ul>
|
|
|
@@ -130,18 +93,10 @@
|
|
|
<h4>主要功能</h4>
|
|
|
<div class="text">{{ state.instDetail.instFunctFeat }}</div>
|
|
|
</div>
|
|
|
- <div
|
|
|
- class="card"
|
|
|
- v-if="isNeedGrant"
|
|
|
- @click="applicationAuth"
|
|
|
- >
|
|
|
+ <div class="card" v-if="isNeedGrant" @click="applicationAuth">
|
|
|
<h4>资质申请</h4>
|
|
|
</div>
|
|
|
- <div
|
|
|
- class="card"
|
|
|
- v-if="isNeedGrant"
|
|
|
- @click="applyTraining"
|
|
|
- >
|
|
|
+ <div class="card" v-if="isNeedGrant" @click="applyTraining">
|
|
|
<h4>培训申请</h4>
|
|
|
</div>
|
|
|
<!-- <div class="card">
|
|
|
@@ -151,81 +106,42 @@
|
|
|
<a href="javascript: void(0);" @click="realDown(item.docName, item.docUrl)">{{ item.docName }}</a>
|
|
|
</div>
|
|
|
</template>
|
|
|
- </div> -->
|
|
|
+</div> -->
|
|
|
</div>
|
|
|
- <van-list
|
|
|
- v-else
|
|
|
- v-model:loading="state.loading"
|
|
|
- :finished="state.finished"
|
|
|
- finished-text="没有更多了"
|
|
|
- @load="onLoad"
|
|
|
- >
|
|
|
- <van-cell
|
|
|
- v-for="item in state.list"
|
|
|
- :key="item.id"
|
|
|
- >
|
|
|
+ <van-list v-else v-model:loading="state.loading" :finished="state.finished" finished-text="没有更多了" @load="onLoad">
|
|
|
+ <van-cell v-for="item in state.list" :key="item.id">
|
|
|
<template #default>
|
|
|
<div class="list">
|
|
|
<header class="flex justify-between">
|
|
|
<strong class="title">{{ item.userName }}的预约</strong>
|
|
|
- <van-tag
|
|
|
- v-if="item.appointStatus == '10'"
|
|
|
- type="default"
|
|
|
- >
|
|
|
+ <van-tag v-if="item.appointStatus == '10'" type="default">
|
|
|
待审核
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '11'"
|
|
|
- type="warning"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '11'" type="warning">
|
|
|
已退回
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '20'"
|
|
|
- type="success"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '20'" type="success">
|
|
|
已通过
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '30'"
|
|
|
- type="danger"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '30'" type="danger">
|
|
|
已驳回
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '40'"
|
|
|
- type="warning"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '40'" type="warning">
|
|
|
已取消
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '50'"
|
|
|
- type="default"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '50'" type="default">
|
|
|
已上机
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '60'"
|
|
|
- type="primary"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '60'" type="primary">
|
|
|
已完成
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '70'"
|
|
|
- type="warning"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '70'" type="warning">
|
|
|
审核超时
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '80'"
|
|
|
- type="danger"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '80'" type="danger">
|
|
|
超时取消
|
|
|
</van-tag>
|
|
|
- <van-tag
|
|
|
- v-else-if="item.appointStatus == '90'"
|
|
|
- type="danger"
|
|
|
- >
|
|
|
+ <van-tag v-else-if="item.appointStatus == '90'" type="danger">
|
|
|
超时未上机
|
|
|
</van-tag>
|
|
|
</header>
|
|
|
@@ -265,73 +181,31 @@
|
|
|
</template>
|
|
|
</van-cell>
|
|
|
</van-list>
|
|
|
- <van-back-top
|
|
|
- target=".instr-detail"
|
|
|
- bottom="10vh"
|
|
|
- />
|
|
|
+ <van-back-top target=".instr-detail" bottom="10vh" />
|
|
|
</div>
|
|
|
<van-action-bar placeholder>
|
|
|
- <van-action-bar-icon
|
|
|
- icon="wap-home-o"
|
|
|
- text="首页"
|
|
|
- @click="onRouterPush('/home')"
|
|
|
- />
|
|
|
- <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-icon
|
|
|
- icon="revoke"
|
|
|
- text="返回"
|
|
|
- @click="onRouterPush('/instr-list')"
|
|
|
- />
|
|
|
- <van-action-bar-button
|
|
|
- v-if="state.instDetail.instStatus == '10' && state.instDetail.isAppointment == '10'"
|
|
|
- type="primary"
|
|
|
- text="使用预约"
|
|
|
- @click="onAppoint('use')"
|
|
|
- />
|
|
|
- <van-action-bar-button
|
|
|
- v-if="state.instDetail.instStatus == '10' && state.instDetail.isSampleDelivery == '10'"
|
|
|
- type="warning"
|
|
|
- text="送样预约"
|
|
|
- @click="onAppoint('sample')"
|
|
|
- />
|
|
|
+ <van-action-bar-icon icon="wap-home-o" text="首页" @click="onRouterPush('/home')" />
|
|
|
+ <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-icon icon="revoke" text="返回" @click="onRouterPush('/instr-list')" />
|
|
|
+ <van-action-bar-button v-if="state.instDetail.instStatus == '10' && state.instDetail.isAppointment == '10'"
|
|
|
+ v-auth="'instr_appoint_btn'" type="primary" text="使用预约" @click="onAppoint('use')" />
|
|
|
+ <van-action-bar-button v-if="state.instDetail.instStatus == '10' && state.instDetail.isSampleDelivery == '10'"
|
|
|
+ v-auth="'instr_sampleDelivery_btn'" type="warning" text="送样预约" @click="onAppoint('sample')" />
|
|
|
</van-action-bar>
|
|
|
<!-- 通知 -->
|
|
|
- <van-popup
|
|
|
- v-model:show="state.popupShow"
|
|
|
- round
|
|
|
- :closeable="true"
|
|
|
- position="top"
|
|
|
- :style="{ padding: '20px' }"
|
|
|
- >
|
|
|
+ <van-popup v-model:show="state.popupShow" round :closeable="true" position="top" :style="{ padding: '20px' }">
|
|
|
<h4>{{ noticeInfo.noticeTitle }}</h4>
|
|
|
- <div
|
|
|
- class="notice-container"
|
|
|
- v-html="noticeInfo.noticeContent"
|
|
|
- ></div>
|
|
|
+ <div class="notice-container" v-html="noticeInfo.noticeContent"></div>
|
|
|
</van-popup>
|
|
|
<!-- 申请须知 -->
|
|
|
- <van-popup
|
|
|
- v-model:show="state.needToKnowShow"
|
|
|
- round
|
|
|
- :closeable="true"
|
|
|
- position="bottom"
|
|
|
- :style="{ height: '90vh' }"
|
|
|
- >
|
|
|
+ <van-popup v-model:show="state.needToKnowShow" round :closeable="true" position="bottom" :style="{ height: '90vh' }">
|
|
|
<div class="need-to-know">
|
|
|
<h4 class="mt8 mb8">申请须知</h4>
|
|
|
<p>{{ state.instDetail.applicationNotes }}</p>
|
|
|
<footer>
|
|
|
- <van-button
|
|
|
- class="w100"
|
|
|
- type="primary"
|
|
|
- round
|
|
|
- @click="confirmAppoint"
|
|
|
- >
|
|
|
+ <van-button class="w100" type="primary" round @click="confirmAppoint">
|
|
|
我知道了
|
|
|
</van-button>
|
|
|
</footer>
|
|
|
@@ -341,427 +215,462 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
- import to from 'await-to-js'
|
|
|
- import { useRoute, useRouter } from 'vue-router'
|
|
|
- import { ElMessageBox, ElMessage } from 'element-plus'
|
|
|
- import { useInstrApi } from '/@/api/instr'
|
|
|
- import { useInstDocApi } from '/@/api/instr/document'
|
|
|
- import { onMounted, reactive, ref } from 'vue'
|
|
|
- import { formatDate } from '/@/utils/formatTime'
|
|
|
- import { showNotify } from 'vant'
|
|
|
- import download from 'downloadjs'
|
|
|
- import { useNoticeApi } from '/@/api/instr/notice'
|
|
|
- import { useUseAppointApi } from '/@/api/instr/useAppoint'
|
|
|
- import { useBlackApi } from '/@/api/blacklist'
|
|
|
- import AddAuthDialog from './addAuthorization/index.vue'
|
|
|
- import { useUserInfos } from '/@/hooks/useUserInfos'
|
|
|
- import { useTrainingApi } from '/@/api/instr/inst/training'
|
|
|
- import { getImageUrl } from '/@/utils/url'
|
|
|
-
|
|
|
- const route = useRoute()
|
|
|
- const router = useRouter()
|
|
|
- const instApi = useInstrApi()
|
|
|
- const instDocApi = useInstDocApi()
|
|
|
- const noticeApi = useNoticeApi()
|
|
|
- const useAppointApi = useUseAppointApi()
|
|
|
- const blacklistApi = useBlackApi()
|
|
|
- const trainingApi = useTrainingApi()
|
|
|
- const active = ref('info')
|
|
|
- const state = reactive({
|
|
|
- detailsLoading: false,
|
|
|
- instStatus: {
|
|
|
- 10: '正常',
|
|
|
- 20: '故障',
|
|
|
- 30: '报废',
|
|
|
- },
|
|
|
- instDetail: {} as any,
|
|
|
- instFiles: [] as any[],
|
|
|
- loading: false,
|
|
|
- finished: false,
|
|
|
- queryParams: {
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
- instId: 0,
|
|
|
- appointStatus: [],
|
|
|
- },
|
|
|
- list: [] as any[],
|
|
|
- popupShow: false,
|
|
|
- needToKnowShow: false,
|
|
|
- appointType: '',
|
|
|
- })
|
|
|
- const noticeInfo = ref({ noticeTitle: '', noticeContent: '' })
|
|
|
+import to from 'await-to-js'
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
+import { ElMessageBox, ElMessage } from 'element-plus'
|
|
|
+import { useInstrApi } from '/@/api/instr'
|
|
|
+import { useInstDocApi } from '/@/api/instr/document'
|
|
|
+import { onMounted, reactive, ref } from 'vue'
|
|
|
+import { formatDate } from '/@/utils/formatTime'
|
|
|
+import { showNotify } from 'vant'
|
|
|
+import download from 'downloadjs'
|
|
|
+import { useNoticeApi } from '/@/api/instr/notice'
|
|
|
+import { useUseAppointApi } from '/@/api/instr/useAppoint'
|
|
|
+import { useBlackApi } from '/@/api/blacklist'
|
|
|
+import AddAuthDialog from './addAuthorization/index.vue'
|
|
|
+import { useUserInfos } from '/@/hooks/useUserInfos'
|
|
|
+import { useTrainingApi } from '/@/api/instr/inst/training'
|
|
|
+import { getImageUrl } from '/@/utils/url'
|
|
|
|
|
|
- const isNeedGrant = ref(false)
|
|
|
+const route = useRoute()
|
|
|
+const router = useRouter()
|
|
|
+const instApi = useInstrApi()
|
|
|
+const instDocApi = useInstDocApi()
|
|
|
+const noticeApi = useNoticeApi()
|
|
|
+const useAppointApi = useUseAppointApi()
|
|
|
+const blacklistApi = useBlackApi()
|
|
|
+const trainingApi = useTrainingApi()
|
|
|
+const active = ref('info')
|
|
|
+const state = reactive({
|
|
|
+ detailsLoading: false,
|
|
|
+ instStatus: {
|
|
|
+ 10: '正常',
|
|
|
+ 20: '故障',
|
|
|
+ 30: '报废',
|
|
|
+ },
|
|
|
+ instDetail: {} as any,
|
|
|
+ instFiles: [] as any[],
|
|
|
+ loading: false,
|
|
|
+ finished: false,
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ instId: 0,
|
|
|
+ appointStatus: [],
|
|
|
+ },
|
|
|
+ list: [] as any[],
|
|
|
+ popupShow: false,
|
|
|
+ needToKnowShow: false,
|
|
|
+ appointType: '',
|
|
|
+})
|
|
|
+const noticeInfo = ref({ noticeTitle: '', noticeContent: '' })
|
|
|
|
|
|
- const addAuthDialogRef = ref()
|
|
|
+const isNeedGrant = ref(false)
|
|
|
|
|
|
- const { userInfos } = useUserInfos()
|
|
|
+const addAuthDialogRef = ref()
|
|
|
|
|
|
- const getNeedGrant = async (instId: number) => {
|
|
|
- const [err, res]: ToResponse = await to(useAppointApi.getNeedGrant({ instId }))
|
|
|
- if (err) return
|
|
|
- isNeedGrant.value = res?.data
|
|
|
- }
|
|
|
+const { userInfos } = useUserInfos()
|
|
|
|
|
|
- // 获取仪器详情
|
|
|
- const getDetail = async (id: number) => {
|
|
|
- state.detailsLoading = true
|
|
|
- const [err, res]: ToResponse = await to(instApi.getDetail({ id }))
|
|
|
- state.detailsLoading = false
|
|
|
- if (err) return
|
|
|
- if (res?.code === 200) {
|
|
|
- state.instDetail = res.data
|
|
|
- getDocs()
|
|
|
- getNotice()
|
|
|
- }
|
|
|
- }
|
|
|
- const getNotice = async () => {
|
|
|
- const param = {
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 1,
|
|
|
- instId: state.instDetail.instId,
|
|
|
- }
|
|
|
- const [err, res]: ToResponse = await to(noticeApi.list({ ...param }))
|
|
|
- if (err) return
|
|
|
- noticeInfo.value = res?.data?.list.length > 0 ? res?.data?.list[0] : {}
|
|
|
+const getNeedGrant = async (instId: number) => {
|
|
|
+ const [err, res]: ToResponse = await to(useAppointApi.getNeedGrant({ instId }))
|
|
|
+ if (err) return
|
|
|
+ isNeedGrant.value = res?.data
|
|
|
+}
|
|
|
+
|
|
|
+// 获取仪器详情
|
|
|
+const getDetail = async (id: number) => {
|
|
|
+ state.detailsLoading = true
|
|
|
+ const [err, res]: ToResponse = await to(instApi.getDetail({ id }))
|
|
|
+ state.detailsLoading = false
|
|
|
+ if (err) return
|
|
|
+ if (res?.code === 200) {
|
|
|
+ state.instDetail = res.data
|
|
|
+ getDocs()
|
|
|
+ getNotice()
|
|
|
}
|
|
|
- // 附件列表
|
|
|
- const getDocs = async () => {
|
|
|
- const [err, res]: ToResponse = await to(
|
|
|
- instDocApi.list({ noPage: true, instId: state.instDetail.instId, docType: '' }),
|
|
|
- )
|
|
|
- if (err) return
|
|
|
- state.instFiles = res?.data.list || []
|
|
|
+}
|
|
|
+const getNotice = async () => {
|
|
|
+ const param = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 1,
|
|
|
+ instId: state.instDetail.instId,
|
|
|
}
|
|
|
- const realDown = (filename: string, fileurl: string) => {
|
|
|
- let ua = navigator.userAgent.toLowerCase()
|
|
|
- if (ua.includes('mac')) {
|
|
|
- //iOS 将文件url转换为文件流 在下载
|
|
|
- downloadFun(fileurl + '?response-content-type=application/octet-stream', filename)
|
|
|
- } else {
|
|
|
- //android 直接用插件的方法下载即可
|
|
|
- download(fileurl, filename)
|
|
|
- }
|
|
|
+ const [err, res]: ToResponse = await to(noticeApi.list({ ...param }))
|
|
|
+ if (err) return
|
|
|
+ noticeInfo.value = res?.data?.list.length > 0 ? res?.data?.list[0] : {}
|
|
|
+}
|
|
|
+// 附件列表
|
|
|
+const getDocs = async () => {
|
|
|
+ const [err, res]: ToResponse = await to(
|
|
|
+ instDocApi.list({ noPage: true, instId: state.instDetail.instId, docType: '' }),
|
|
|
+ )
|
|
|
+ if (err) return
|
|
|
+ state.instFiles = res?.data.list || []
|
|
|
+}
|
|
|
+const realDown = (filename: string, fileurl: string) => {
|
|
|
+ let ua = navigator.userAgent.toLowerCase()
|
|
|
+ if (ua.includes('mac')) {
|
|
|
+ //iOS 将文件url转换为文件流 在下载
|
|
|
+ downloadFun(fileurl + '?response-content-type=application/octet-stream', filename)
|
|
|
+ } else {
|
|
|
+ //android 直接用插件的方法下载即可
|
|
|
+ download(fileurl, filename)
|
|
|
}
|
|
|
- // 创建a标签 实现下载
|
|
|
- const downloadFun = async (blobFile, fileName) => {
|
|
|
- let blob = new Blob([blobFile], {
|
|
|
- type: 'application/pdf;charset=UTF-8',
|
|
|
- })
|
|
|
+}
|
|
|
+// 创建a标签 实现下载
|
|
|
+const downloadFun = async (blobFile, fileName) => {
|
|
|
+ let blob = new Blob([blobFile], {
|
|
|
+ type: 'application/pdf;charset=UTF-8',
|
|
|
+ })
|
|
|
+ // @ts-ignore
|
|
|
+ if (window.navigator.msSaveOrOpenBlob) {
|
|
|
// @ts-ignore
|
|
|
- if (window.navigator.msSaveOrOpenBlob) {
|
|
|
- // @ts-ignore
|
|
|
- navigator.msSaveBlob(blob, fileName)
|
|
|
- } else {
|
|
|
- let link = document.createElement('a')
|
|
|
- link.href = window.URL.createObjectURL(blob)
|
|
|
- link.download = fileName
|
|
|
- link.click()
|
|
|
- window.URL.revokeObjectURL(link.href) //释放内存
|
|
|
- }
|
|
|
- }
|
|
|
- const setLaboratoryName = (name) => {
|
|
|
- return name ? `(${name})` : ''
|
|
|
- }
|
|
|
- const tabChange = (name: string) => {
|
|
|
- if (name === 'history' || name === 'approval') {
|
|
|
- state.finished = false
|
|
|
- state.list = []
|
|
|
- state.queryParams = {
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
- instId: state.instDetail.id,
|
|
|
- appointStatus: name === 'approval' ? ['10'] : [],
|
|
|
- }
|
|
|
- onLoad()
|
|
|
- }
|
|
|
+ navigator.msSaveBlob(blob, fileName)
|
|
|
+ } else {
|
|
|
+ let link = document.createElement('a')
|
|
|
+ link.href = window.URL.createObjectURL(blob)
|
|
|
+ link.download = fileName
|
|
|
+ link.click()
|
|
|
+ window.URL.revokeObjectURL(link.href) //释放内存
|
|
|
}
|
|
|
- const onLoad = async () => {
|
|
|
- state.loading = true
|
|
|
- const [err, res]: ToResponse = await to(useAppointApi.getListByPermission(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 setLaboratoryName = (name) => {
|
|
|
+ return name ? `(${name})` : ''
|
|
|
+}
|
|
|
+const tabChange = (name: string) => {
|
|
|
+ if (name === 'history' || name === 'approval') {
|
|
|
+ state.finished = false
|
|
|
+ state.list = []
|
|
|
+ state.queryParams = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ instId: state.instDetail.id,
|
|
|
+ appointStatus: name === 'approval' ? ['10'] : [],
|
|
|
}
|
|
|
+ onLoad()
|
|
|
}
|
|
|
- const getBreachTypes = (row: any) => {
|
|
|
- let breachTypes = <string[]>[]
|
|
|
- if (row.isLate) breachTypes.push('迟到')
|
|
|
- if (row.isOvertime) breachTypes.push('超时')
|
|
|
- if (row.isLeaveEarly) breachTypes.push('早退')
|
|
|
- if (row.isAbsence) breachTypes.push('爽约')
|
|
|
- return breachTypes.join('、') || '-'
|
|
|
- }
|
|
|
- const getAppointTime = (row: any) => {
|
|
|
- const startDate = new Date(row.startTime)
|
|
|
- const endDate = new Date(row.endTime)
|
|
|
- // 计算两个日期之间的时间差(以毫秒为单位)
|
|
|
- const timeDifference = endDate.getTime() - startDate.getTime()
|
|
|
- // 计算天数
|
|
|
- const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24))
|
|
|
- // 计算剩余的毫秒数
|
|
|
- const remainingMilliseconds = timeDifference % (1000 * 60 * 60 * 24)
|
|
|
- // 计算小时数
|
|
|
- const hours = Math.floor(remainingMilliseconds / (1000 * 60 * 60))
|
|
|
- // 计算剩余的毫秒数
|
|
|
- const remainingMillisecondsAfterHours = remainingMilliseconds % (1000 * 60 * 60)
|
|
|
- // 计算分钟数
|
|
|
- const minutes = Math.floor(remainingMillisecondsAfterHours / (1000 * 60))
|
|
|
- return `${days}天${hours}小时${minutes}分`
|
|
|
+}
|
|
|
+const onLoad = async () => {
|
|
|
+ state.loading = true
|
|
|
+ const [err, res]: ToResponse = await to(useAppointApi.getListByPermission(state.queryParams))
|
|
|
+ if (err) return
|
|
|
+ const list = res?.data?.list || []
|
|
|
+ for (const item of list) {
|
|
|
+ state.list.push(item)
|
|
|
}
|
|
|
- // 关注/取关
|
|
|
- const handleFollowInst = async () => {
|
|
|
- const [err] = state.instDetail.following
|
|
|
- ? await to(instApi.unfollow({ ids: [state.instDetail.id] }))
|
|
|
- : await to(instApi.follow({ ids: [state.instDetail.id] }))
|
|
|
- if (err) return
|
|
|
- showNotify({ type: 'success', message: !state.instDetail.following ? '收藏成功' : '已取消收藏' })
|
|
|
- getDetail(state.instDetail.id)
|
|
|
+ state.loading = false
|
|
|
+ state.queryParams.pageNum++
|
|
|
+ if (list.length < state.queryParams.pageSize) {
|
|
|
+ state.finished = true
|
|
|
}
|
|
|
- const onAppoint = async (type: string) => {
|
|
|
- state.appointType = type
|
|
|
- state.needToKnowShow = true
|
|
|
- }
|
|
|
- const confirmAppoint = async () => {
|
|
|
- const [err, res]: ToResponse = await to(blacklistApi.checkInBlacklist())
|
|
|
- if (err) return
|
|
|
- if (res.data) {
|
|
|
- showNotify({ type: 'danger', message: '您已被拉入黑名单,无法预约,请联系管理员' })
|
|
|
- return
|
|
|
- }
|
|
|
- if (state.appointType === 'sample') {
|
|
|
- onRouterPush('/sample-appoint', { id: state.instDetail.id })
|
|
|
- } else {
|
|
|
- onRouterPush('/instr-appoint', { id: state.instDetail.id })
|
|
|
- }
|
|
|
+}
|
|
|
+const getBreachTypes = (row: any) => {
|
|
|
+ let breachTypes = <string[]>[]
|
|
|
+ if (row.isLate) breachTypes.push('迟到')
|
|
|
+ if (row.isOvertime) breachTypes.push('超时')
|
|
|
+ if (row.isLeaveEarly) breachTypes.push('早退')
|
|
|
+ if (row.isAbsence) breachTypes.push('爽约')
|
|
|
+ return breachTypes.join('、') || '-'
|
|
|
+}
|
|
|
+const getAppointTime = (row: any) => {
|
|
|
+ const startDate = new Date(row.startTime)
|
|
|
+ const endDate = new Date(row.endTime)
|
|
|
+ // 计算两个日期之间的时间差(以毫秒为单位)
|
|
|
+ const timeDifference = endDate.getTime() - startDate.getTime()
|
|
|
+ // 计算天数
|
|
|
+ const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24))
|
|
|
+ // 计算剩余的毫秒数
|
|
|
+ const remainingMilliseconds = timeDifference % (1000 * 60 * 60 * 24)
|
|
|
+ // 计算小时数
|
|
|
+ const hours = Math.floor(remainingMilliseconds / (1000 * 60 * 60))
|
|
|
+ // 计算剩余的毫秒数
|
|
|
+ const remainingMillisecondsAfterHours = remainingMilliseconds % (1000 * 60 * 60)
|
|
|
+ // 计算分钟数
|
|
|
+ const minutes = Math.floor(remainingMillisecondsAfterHours / (1000 * 60))
|
|
|
+ return `${days}天${hours}小时${minutes}分`
|
|
|
+}
|
|
|
+// 关注/取关
|
|
|
+const handleFollowInst = async () => {
|
|
|
+ const [err] = state.instDetail.following
|
|
|
+ ? await to(instApi.unfollow({ ids: [state.instDetail.id] }))
|
|
|
+ : await to(instApi.follow({ ids: [state.instDetail.id] }))
|
|
|
+ if (err) return
|
|
|
+ showNotify({ type: 'success', message: !state.instDetail.following ? '收藏成功' : '已取消收藏' })
|
|
|
+ getDetail(state.instDetail.id)
|
|
|
+}
|
|
|
+const onAppoint = async (type: string) => {
|
|
|
+ state.appointType = type
|
|
|
+ state.needToKnowShow = true
|
|
|
+}
|
|
|
+const confirmAppoint = async () => {
|
|
|
+ const [err, res]: ToResponse = await to(blacklistApi.checkInBlacklist())
|
|
|
+ if (err) return
|
|
|
+ if (res.data) {
|
|
|
+ showNotify({ type: 'danger', message: '您已被拉入黑名单,无法预约,请联系管理员' })
|
|
|
+ return
|
|
|
}
|
|
|
- const onRouterPush = (val: string, params?: any) => {
|
|
|
- router.push({
|
|
|
- path: val,
|
|
|
- query: { ...params },
|
|
|
- })
|
|
|
+ if (state.appointType === 'sample') {
|
|
|
+ onRouterPush('/sample-appoint', { id: state.instDetail.id })
|
|
|
+ } else {
|
|
|
+ onRouterPush('/instr-appoint', { id: state.instDetail.id })
|
|
|
}
|
|
|
+}
|
|
|
+const onRouterPush = (val: string, params?: any) => {
|
|
|
+ router.push({
|
|
|
+ path: val,
|
|
|
+ query: { ...params },
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- const applicationAuth = async () => {
|
|
|
- console.log('applicationAuth')
|
|
|
- addAuthDialogRef.value.openDialog('personal', state.instDetail)
|
|
|
- }
|
|
|
+const applicationAuth = async () => {
|
|
|
+ console.log('applicationAuth')
|
|
|
+ addAuthDialogRef.value.openDialog('personal', state.instDetail)
|
|
|
+}
|
|
|
|
|
|
- const applyTraining = () => {
|
|
|
- console.log('applyTraining')
|
|
|
- ElMessageBox.confirm('确认发起培训申请?', '提示', {
|
|
|
- confirmButtonText: '确认',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning',
|
|
|
+const applyTraining = () => {
|
|
|
+ console.log('applyTraining')
|
|
|
+ ElMessageBox.confirm('确认发起培训申请?', '提示', {
|
|
|
+ confirmButtonText: '确认',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ })
|
|
|
+ .then(async () => {
|
|
|
+ const params = {
|
|
|
+ instCode: state.instDetail.instCode,
|
|
|
+ instId: state.instDetail.id,
|
|
|
+ instName: state.instDetail.instName,
|
|
|
+ userId: userInfos.value.id,
|
|
|
+ userName: userInfos.value.nickName,
|
|
|
+ }
|
|
|
+ const [err]: ToResponse = await to(trainingApi.add({ ...params }))
|
|
|
+ if (err) return
|
|
|
+ ElMessage.success('培训申请提交成功')
|
|
|
})
|
|
|
- .then(async () => {
|
|
|
- const params = {
|
|
|
- instCode: state.instDetail.instCode,
|
|
|
- instId: state.instDetail.id,
|
|
|
- instName: state.instDetail.instName,
|
|
|
- userId: userInfos.value.id,
|
|
|
- userName: userInfos.value.nickName,
|
|
|
- }
|
|
|
- const [err]: ToResponse = await to(trainingApi.add({ ...params }))
|
|
|
- if (err) return
|
|
|
- ElMessage.success('培训申请提交成功')
|
|
|
- })
|
|
|
- .catch(() => {})
|
|
|
- }
|
|
|
+ .catch(() => { })
|
|
|
+}
|
|
|
|
|
|
- onMounted(() => {
|
|
|
- const id = route.query.id ? +route.query.id : 0
|
|
|
- getDetail(id)
|
|
|
- getNeedGrant(id)
|
|
|
- })
|
|
|
+onMounted(() => {
|
|
|
+ const id = route.query.id ? +route.query.id : 0
|
|
|
+ getDetail(id)
|
|
|
+ getNeedGrant(id)
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
- .instr-detail {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
- background-color: #f7f8fa;
|
|
|
- .my-swipe {
|
|
|
- background-color: #fff;
|
|
|
- height: 30px !important;
|
|
|
- line-height: 30px !important;
|
|
|
- :deep(.flex) {
|
|
|
+.instr-detail {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ background-color: #f7f8fa;
|
|
|
+
|
|
|
+ .my-swipe {
|
|
|
+ background-color: #fff;
|
|
|
+ height: 30px !important;
|
|
|
+ line-height: 30px !important;
|
|
|
+
|
|
|
+ :deep(.flex) {
|
|
|
+ height: 30px;
|
|
|
+ overflow: hidden;
|
|
|
+ padding: 0 12px;
|
|
|
+
|
|
|
+ span {
|
|
|
+ display: inline-block;
|
|
|
height: 30px;
|
|
|
+ line-height: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ span:first-child {
|
|
|
+ flex: 1;
|
|
|
+ white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
- padding: 0 12px;
|
|
|
- span {
|
|
|
- display: inline-block;
|
|
|
- height: 30px;
|
|
|
- line-height: 30px;
|
|
|
- }
|
|
|
- span:first-child {
|
|
|
- flex: 1;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- }
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
}
|
|
|
- > header {
|
|
|
- height: auto;
|
|
|
- min-height: 80px;
|
|
|
- background-color: #fff;
|
|
|
- padding: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ >header {
|
|
|
+ height: auto;
|
|
|
+ min-height: 80px;
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .inst-info {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .i-right {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 14px;
|
|
|
+ height: auto;
|
|
|
+ min-height: 80px;
|
|
|
+
|
|
|
+ .i-r-icon {
|
|
|
+ width: 15px;
|
|
|
+ height: 15px;
|
|
|
+ margin-right: 10px;
|
|
|
}
|
|
|
- .inst-info {
|
|
|
- display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detailTxt {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #333333;
|
|
|
+ white-space: normal;
|
|
|
+ overflow: visible;
|
|
|
+ text-overflow: unset;
|
|
|
+ word-break: break-all;
|
|
|
+
|
|
|
+ &.name {
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .card {
|
|
|
+ border-radius: 4px;
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 10px;
|
|
|
+ box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
|
|
+
|
|
|
+ &+.card {
|
|
|
+ margin-top: 10px;
|
|
|
}
|
|
|
- .i-right {
|
|
|
- flex: 1;
|
|
|
- font-size: 14px;
|
|
|
- height: auto;
|
|
|
- min-height: 80px;
|
|
|
- .i-r-icon {
|
|
|
- width: 15px;
|
|
|
- height: 15px;
|
|
|
- margin-right: 10px;
|
|
|
+
|
|
|
+ h4 {
|
|
|
+ height: 18px;
|
|
|
+ line-height: 18px;
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-weight: normal;
|
|
|
+ margin-left: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ display: inline-block;
|
|
|
+ content: '';
|
|
|
+ width: 3px;
|
|
|
+ height: 18px;
|
|
|
+ background-color: #1c9bfd;
|
|
|
+ margin-right: 4px;
|
|
|
+ vertical-align: middle;
|
|
|
}
|
|
|
}
|
|
|
- .detailTxt {
|
|
|
- font-size: 12px;
|
|
|
- color: #333333;
|
|
|
- white-space: normal;
|
|
|
- overflow: visible;
|
|
|
- text-overflow: unset;
|
|
|
- word-break: break-all;
|
|
|
- &.name {
|
|
|
- font-weight: bold;
|
|
|
- font-size: 16px;
|
|
|
+
|
|
|
+ >ul {
|
|
|
+ li {
|
|
|
+ display: flex;
|
|
|
+ padding: 6px 0;
|
|
|
+
|
|
|
+ label {
|
|
|
+ width: 80px;
|
|
|
+ min-width: 80px;
|
|
|
+ color: #969799;
|
|
|
+ }
|
|
|
+
|
|
|
+ span {
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- .content {
|
|
|
- padding: 10px;
|
|
|
+
|
|
|
+ .text {
|
|
|
+ white-space: pre-wrap;
|
|
|
}
|
|
|
- .card {
|
|
|
- border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .van-list {
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .van-cell {
|
|
|
background-color: #fff;
|
|
|
- padding: 10px;
|
|
|
- box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
|
|
- & + .card {
|
|
|
+
|
|
|
+ +.van-cell {
|
|
|
margin-top: 10px;
|
|
|
}
|
|
|
- h4 {
|
|
|
- height: 18px;
|
|
|
- line-height: 18px;
|
|
|
- display: flex;
|
|
|
- margin-bottom: 10px;
|
|
|
- span {
|
|
|
- font-weight: normal;
|
|
|
- margin-left: auto;
|
|
|
- }
|
|
|
- &::before {
|
|
|
- display: inline-block;
|
|
|
- content: '';
|
|
|
- width: 3px;
|
|
|
- height: 18px;
|
|
|
- background-color: #1c9bfd;
|
|
|
- margin-right: 4px;
|
|
|
- vertical-align: middle;
|
|
|
- }
|
|
|
- }
|
|
|
- > ul {
|
|
|
- li {
|
|
|
- display: flex;
|
|
|
- padding: 6px 0;
|
|
|
- label {
|
|
|
- width: 80px;
|
|
|
- min-width: 80px;
|
|
|
- color: #969799;
|
|
|
- }
|
|
|
- span {
|
|
|
- word-break: break-all;
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ header,
|
|
|
+ footer {
|
|
|
+ color: #333;
|
|
|
}
|
|
|
- .text {
|
|
|
- white-space: pre-wrap;
|
|
|
+
|
|
|
+ .title {
|
|
|
+ flex: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ text-align: left;
|
|
|
}
|
|
|
- }
|
|
|
- .van-list {
|
|
|
- padding: 10px;
|
|
|
- border-radius: 4px;
|
|
|
- flex: 1;
|
|
|
- .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 {
|
|
|
- display: inline-block;
|
|
|
- width: 80px;
|
|
|
- min-width: 80px;
|
|
|
- color: rgb(120, 120, 120);
|
|
|
- }
|
|
|
- }
|
|
|
- .time {
|
|
|
- color: #f69a4d;
|
|
|
+
|
|
|
+ .inst-title {
|
|
|
+ color: #333;
|
|
|
+ text-align: left;
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ margin-top: 4px;
|
|
|
+
|
|
|
+ span:first-child {
|
|
|
+ display: inline-block;
|
|
|
+ width: 80px;
|
|
|
+ min-width: 80px;
|
|
|
+ color: rgb(120, 120, 120);
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- .btns {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- li {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 0 8px;
|
|
|
- font-size: 12px;
|
|
|
- i {
|
|
|
- margin-bottom: 4px;
|
|
|
+
|
|
|
+ .time {
|
|
|
+ color: #f69a4d;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- :deep(.follow .van-icon) {
|
|
|
- color: #fdc33e;
|
|
|
- }
|
|
|
- .need-to-know {
|
|
|
- height: calc(100% - 20px);
|
|
|
- overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.btns {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ li {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- padding: 10px 20px;
|
|
|
- white-space: pre-wrap;
|
|
|
- p {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
- }
|
|
|
- footer {
|
|
|
- flex: 0 0 45px;
|
|
|
- margin-top: 4px;
|
|
|
- border-top: 1px solid #f7f8fa;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 0 8px;
|
|
|
+ font-size: 12px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-bottom: 4px;
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.follow .van-icon) {
|
|
|
+ color: #fdc33e;
|
|
|
+}
|
|
|
+
|
|
|
+.need-to-know {
|
|
|
+ height: calc(100% - 20px);
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ padding: 10px 20px;
|
|
|
+ white-space: pre-wrap;
|
|
|
+
|
|
|
+ p {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ footer {
|
|
|
+ flex: 0 0 45px;
|
|
|
+ margin-top: 4px;
|
|
|
+ border-top: 1px solid #f7f8fa;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|