Explorar el Código

freature:(仪器管理)增加列表和详情页

liuzhenlin hace 11 meses
padre
commit
e5a62d9af3

+ 1 - 0
.gitignore

@@ -19,3 +19,4 @@ yarn-error.log*
 *.njsproj
 *.sln
 *.sw?
+.history/

+ 2 - 2
package-lock.json

@@ -1,6 +1,6 @@
 {
-  "name": "kejian",
-  "version": "0.1.0",
+  "name": "zunyiyikeda",
+  "version": "1.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 0
public/config.js

@@ -12,6 +12,7 @@ const $GlobalConfig = {
   // 登录验证微服务名称
   VUE_APP_AdminPath: 'dashoo.labsop.admin',
   VUE_APP_SETTING_PATH: 'dashoo.labsop.finance-zzh',
+  VUE_APP_INSTR_PATH: 'dashoo.labsop.apparatus',
   // 租户码
   VUE_APP_TENANT: 'default '
 }

+ 1 - 2
src/App.vue

@@ -4,8 +4,7 @@
       <el-header>
         <div class="flex flex-auto flex-center">
           <div class="logo mr12">
-            <img src="./assets/img/common-logo.png"
-                 alt />
+            <img src="./assets/img/common-logo.png" alt />
           </div>
           <el-menu :default-active="defaultActive"
                    class="el-menu-demo"

+ 57 - 0
src/api/instr/index.js

@@ -0,0 +1,57 @@
+/*
+ * @Author: wanglj
+ * @Date: 2022-04-25 10:38:19
+ * @LastEditors: wanglj
+ * @LastEditTime: 2025-01-09 15:19:14
+ * @Description: file content
+ * @FilePath: \labsop_website\src\api\introduce.js
+ */
+import request from "@/utils/micro_request";
+
+const basePath = $GlobalConfig.VUE_APP_INSTR_PATH;
+
+// 获取仪器列表
+export function getInstrList(data) {
+  return request.postRequestWithClientInfo(
+    basePath,
+    "JiangSuUniversity",
+    "GetList",
+    data
+  );
+}
+// 获取仪器详情
+export function getInstrDetails(data) {
+  return request.postRequestWithClientInfo(
+    basePath,
+    "JiangSuUniversity",
+    "GetEntityById",
+    data
+  );
+}
+// 获取附件列表 
+export function getFileTable(data) {
+  return request.postRequestWithClientInfo(
+    basePath,
+    "JiangSuUniversity",
+    "GetDocumentListByInst",
+    data
+  );
+}
+// 获取公告列表 
+export function getNoticeTable(data) {
+  return request.postRequestWithClientInfo(
+    basePath,
+    "JiangSuUniversity",
+    "GetNoticeListByInst",
+    data
+  );
+}
+// 获取型号数量
+export function getInstNameEnCount(data) {
+  return request.postRequestWithClientInfo(
+    basePath,
+    "JiangSuUniversity",
+    "GetInstNameEnCount",
+    data
+  );
+}

+ 1 - 1
src/api/login.js

@@ -21,4 +21,4 @@ export function signOut(data) {
  // 根据用户名获取用户信息
 export function getUserByUserName(data) {
   return request.postRequestWithClientInfo(basePath, "User", "GetUserByUserName", data);
-}
+}

BIN
src/assets/img/equipment-banner.png


+ 103 - 2
src/assets/styles/index.scss

@@ -1,15 +1,45 @@
-html, body {
+/* 初始化样式
+------------------------------- */
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  outline: none !important;
+}
+
+ul li {
+  list-style: none;
+
+}
+
+html,
+body,
+#app {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+  font-family: PingFang SC, Hiragino Sans GB, Arial, Microsoft YaHei, Verdana, Roboto, Noto, Helvetica Neue, sans-serif;
+  font-weight: 400;
+  -webkit-font-smoothing: antialiased;
+  -webkit-tap-highlight-color: transparent;
+  background-color: var(--next-bg-main-color);
   font-size: 14px;
+  position: relative;
 }
+
 .flex {
   display: flex;
 }
+
 .flex-auto {
   flex: 1;
 }
+
 .flex-center {
   align-items: center;
 }
+
 .w100 {
   width: 100% !important;
 }
@@ -65,12 +95,14 @@ html, body {
     padding-left: #{$i}px !important;
   }
 }
+
 .custom-submenu {
   .el-menu {
     padding: 0;
     border-radius: 4px;
     min-width: 120px;
   }
+
   .el-menu-item {
     font-weight: 400;
     font-size: 16px;
@@ -78,18 +110,22 @@ html, body {
     height: 24px;
     line-height: 24px;
     text-align: center;
+
     &:hover {
       background-color: #ebf3ff !important;
       color: #386afe !important;
     }
   }
 }
+
 .el-card {
   border-radius: 8px !important;
+
   .header {
     display: flex;
     align-items: center;
     justify-content: space-between;
+
     h4 {
       font-weight: bold;
       font-size: 18px;
@@ -97,14 +133,17 @@ html, body {
     }
   }
 }
+
 .common-container {
   padding: 20px 0 40px;
   background-color: #F8F9FD;
+
   .el-container {
     width: 1200px;
     margin: 0 auto;
   }
 }
+
 .left-tabs {
   width: 266px;
   border-radius: 8px;
@@ -115,6 +154,7 @@ html, body {
   min-height: 550px;
   display: flex;
   flex-direction: column;
+
   h4 {
     height: 72px;
     line-height: 72px;
@@ -124,8 +164,10 @@ html, body {
     text-align: center;
     background: linear-gradient(99deg, #1964fe 0%, #19abff 100%);
   }
+
   ul {
     flex: 1;
+
     li {
       height: 60px;
       font-weight: 400;
@@ -136,6 +178,7 @@ html, body {
       display: flex;
       align-items: center;
       justify-content: center;
+
       &:before {
         content: "";
         display: inline-block;
@@ -145,9 +188,11 @@ html, body {
         border-radius: 4px;
         margin-right: 8px;
       }
+
       &:hover,
       &.active {
         background-color: #e7f1ff;
+
         &:before {
           background: #2C78FF;
         }
@@ -155,25 +200,30 @@ html, body {
     }
   }
 }
+
 .right-content {
   flex: 1;
   border-radius: 8px;
   overflow: hidden;
   min-height: 550px;
   box-shadow: 0px 3px 6px 1px rgba(1, 64, 100, 0.16);
+
   ::v-deep .el-card__body {
     height: calc(100% - 100px);
   }
+
   .header {
     display: flex;
     align-items: center;
     justify-content: space-between;
+
     h4 {
       font-weight: bold;
       font-size: 18px;
       color: #2c405e;
     }
   }
+
   .article {
     h4 {
       text-align: center;
@@ -181,24 +231,29 @@ html, body {
       font-size: 24px;
       color: #386afe;
     }
+
     .snd-title {
       font-weight: 400;
       font-size: 14px;
       color: #565656;
       display: flex;
       justify-content: center;
+
       p {
         margin: 8px;
       }
     }
   }
+
   .link-list {
     display: flex;
     flex-direction: column;
     height: 100%;
     overflow-y: auto;
-    > ul {
+
+    >ul {
       flex: 1;
+
       li {
         display: flex;
         justify-content: space-between;
@@ -206,14 +261,17 @@ html, body {
         border-bottom: 1px dashed #ebf1f6;
         cursor: pointer;
         transition: all 0.3s;
+
         &:hover {
           background-color: #e7f1ff;
         }
+
         div {
           font-weight: 400;
           font-size: 16px;
           color: #585858;
         }
+
         span {
           font-weight: 400;
           font-size: 16px;
@@ -221,9 +279,52 @@ html, body {
         }
       }
     }
+
     .el-pagination {
       display: flex;
       justify-content: center;
     }
   }
+}
+
+.def-tabs-wrap {
+  .el-tabs__nav-scroll {
+    background: #f3f6fb;
+  }
+
+  .el-tabs__item {
+    font-weight: bold;
+    color: #585858;
+  }
+
+  .el-tabs__nav-wrap::after {
+    background: #DFE7EE;
+  }
+
+  .el-tabs--bottom .el-tabs__item.is-bottom:last-child,
+  .el-tabs--bottom .el-tabs__item.is-top:last-child,
+  .el-tabs--top .el-tabs__item.is-bottom:last-child,
+  .el-tabs--top .el-tabs__item.is-top:last-child {
+    padding-right: 12px
+  }
+
+  .el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),
+  .el-tabs--bottom .el-tabs__item.is-top:nth-child(2),
+  .el-tabs--top .el-tabs__item.is-bottom:nth-child(2),
+  .el-tabs--top .el-tabs__item.is-top:nth-child(2) {
+    padding-left: 12px;
+  }
+
+  .el-tabs__item.is-active {
+    border-top: 2px solid #2c78ff;
+    background: #ffffff;
+    color: #2c78ff;
+    font-weight: bold;
+    margin-top: 2px;
+    // border-radius: 4px 4px 0 0;
+  }
+
+  .el-tabs__active-bar {
+    background: none;
+  }
 }

+ 82 - 68
src/router.js

@@ -5,7 +5,7 @@ import store from './store'
 import { getUserByUserName } from '@/api/login'
 import awaitTo from 'await-to-js'
 
-Vue.use(Router)
+Vue.use(Router);
 
 let router = new Router({
   routes: [{
@@ -14,9 +14,9 @@ let router = new Router({
       component: () => import('./views/Home.vue')
     },
     {
-      path: '/introduce',
-      name: 'introduce',
-      component: () => import('./views/Introduce.vue'),
+      path: "/introduce",
+      name: "introduce",
+      component: () => import("./views/Introduce.vue"),
     },
     {
       path: '/news',
@@ -27,39 +27,52 @@ let router = new Router({
       component: () => import('./views/News.vue'),
     },
     {
-      path: '/newsdetails/:id',
-      name: 'newsdetails',
-      component: () => import('./views/NewsDetails.vue'),
+      path: "/newsdetails/:id",
+      name: "newsdetails",
+      component: () => import("./views/NewsDetails.vue"),
     },
+    // 仪器列表
     {
-      path: '/product',
-      name: 'product',
-      component: () => import('./views/Product.vue'),
+      path: "/appointment/equipment-details",
+      name: "equipmentDetails",
+      component: () => import("./views/equipment/details.vue"),
     },
     {
-      path: '/case',
-      name: 'case',
-      component: () => import('./views/Case.vue')
+      path: "/appointment",
+      name: "appointment",
+      component: () => import("./views/equipment"),
+      children: [
+        {
+          path: "/appointment/equipment",
+          name: "equipment",
+          component: () => import("./views/equipment/index.vue"),
+        },
+      ],
     },
     {
-      path: '/casedetails/:id',
-      name: 'casedetails',
-      component: () => import('./views/CaseDetails.vue')
+      path: "/product",
+      name: "product",
+      component: () => import("./views/Product.vue"),
     },
     {
-      path: '/goin',
-      name: 'goin',
-      component: () => import('./views/GoIn.vue')
+      path: "/case",
+      name: "case",
+      component: () => import("./views/Case.vue"),
     },
     {
-      path: '/download',
-      name: 'download',
-      component: () => import('./views/Download.vue')
+      path: "/casedetails/:id",
+      name: "casedetails",
+      component: () => import("./views/CaseDetails.vue"),
     },
     {
-      path: '/login',
-      name: 'login',
-      component: () => import('./views/Login.vue')
+      path: "/goin",
+      name: "goin",
+      component: () => import("./views/GoIn.vue"),
+    },
+    {
+      path: "/download",
+      name: "download",
+      component: () => import("./views/Download.vue"),
     },
     {
       path: '/register',
@@ -86,58 +99,59 @@ let router = new Router({
       path: '/admin',
       name: 'admin',
       meta: {
-        requireAuth: true
+        requireAuth: true,
       },
-      component: () => import('./views/Admin.vue'),
-      children: [{
-          path: '/admin/user',
-          name: 'user',
-          component: () => import('./views/Admin/User.vue')
+      component: () => import("./views/Admin.vue"),
+      children: [
+        {
+          path: "/admin/user",
+          name: "user",
+          component: () => import("./views/Admin/User.vue"),
         },
         {
-          path: '/admin/news',
-          name: 'new',
-          component: () => import('./views/Admin/News.vue')
+          path: "/admin/news",
+          name: "new",
+          component: () => import("./views/Admin/News.vue"),
         },
         {
-          path: '/admin/cases',
-          name: 'cases',
-          component: () => import('./views/Admin/Cases.vue')
+          path: "/admin/cases",
+          name: "cases",
+          component: () => import("./views/Admin/Cases.vue"),
         },
         {
-          path: '/admin/team',
-          name: 'team',
-          component: () => import('./views/Admin/Team.vue')
+          path: "/admin/team",
+          name: "team",
+          component: () => import("./views/Admin/Team.vue"),
         },
         {
-          path: '/admin/course',
-          name: 'course',
-          component: () => import('./views/Admin/Course.vue')
+          path: "/admin/course",
+          name: "course",
+          component: () => import("./views/Admin/Course.vue"),
         },
         {
-          path: '/admin/enterprise',
-          name: 'enterprise',
-          component: () => import('./views/Admin/Enterprise.vue')
+          path: "/admin/enterprise",
+          name: "enterprise",
+          component: () => import("./views/Admin/Enterprise.vue"),
         },
         {
-          path: '/admin/honor',
-          name: 'honor',
-          component: () => import('./views/Admin/Honor.vue')
+          path: "/admin/honor",
+          name: "honor",
+          component: () => import("./views/Admin/Honor.vue"),
         },
         {
-          path: '/admin/dictionary',
-          name: 'dictionary',
-          component: () => import('./views/Admin/Dictionary.vue')
+          path: "/admin/dictionary",
+          name: "dictionary",
+          component: () => import("./views/Admin/Dictionary.vue"),
         },
         {
-          path: '/admin/page',
-          name: 'page',
-          component: () => import('./views/Admin/Page.vue')
-        }
-      ]
-    }
-  ]
-})
+          path: "/admin/page",
+          name: "page",
+          component: () => import("./views/Admin/Page.vue"),
+        },
+      ],
+    },
+  ],
+});
 
 // 判断是否需要登录权限 以及是否登录
 router.beforeEach(async (to, from, next) => {
@@ -149,22 +163,22 @@ router.beforeEach(async (to, from, next) => {
     store.dispatch('setUserInfo', res.data.userInfo)
   }
   // 判断是否需要登录权限
-  if (to.matched.some(res => res.meta.requireAuth)) {
+  if (to.matched.some((res) => res.meta.requireAuth)) {
     // 判断是否登录
     if (token) {
       next()
     } else {
       // 没登录则跳转到登录界面
       next({
-        path: '/login',
+        path: "/login",
         query: {
-          redirect: to.fullPath
-        }
-      })
+          redirect: to.fullPath,
+        },
+      });
     }
   } else {
-    next()
+    next();
   }
-})
+});
 
-export default router
+export default router

+ 247 - 0
src/views/equipment/components/eqpt-details.vue

@@ -0,0 +1,247 @@
+<template>
+  <div class="details-wrap">
+    <div class="msg-item">
+      <div class="item-group">
+        <div class="item">
+          <h4 class="title">名称</h4>
+          <div class="text-item">{{ instrDetails.instName }}</div>
+        </div>
+      </div>
+      <div class="item-group">
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">仪器编号</h4>
+              <div class="text-item">{{ instrDetails.instCode }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">仪器型号</h4>
+              <div class="text-item">{{ instrDetails.instNameEn }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">仪器分类</h4>
+              <div class="text-item">{{ instrDetails.instClassDesc }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">规格</h4>
+              <div class="text-item">{{ instrDetails.instSpec }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">仪器价格</h4>
+              <div class="text-item">{{ instrDetails.instPrice }}元</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">制造国家</h4>
+              <div class="text-item">{{ instrDetails.instCountry }}</div>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">生产厂家</h4>
+              <div class="text-item">{{ instrDetails.instBrandDesc }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">出厂日期</h4>
+              <div class="text-item">{{ instrDetails.productDate }}</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">购置日期</h4>
+              <div class="text-item">{{ instrDetails.purchaseDate }}</div>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+      <!-- <div class="item-group">
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">名称</h4>
+              <div class="text-item">总有机碳分析仪</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">名称</h4>
+              <div class="text-item">总有机碳分析仪</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">名称</h4>
+              <div class="text-item">总有机碳分析仪</div>
+            </div>
+          </el-col>
+          <el-col :span="8">
+            <div class="item">
+              <h4 class="title">名称</h4>
+              <div class="text-item">总有机碳分析仪</div>
+            </div>
+          </el-col>
+        </el-row>
+      </div> -->
+    </div>
+    <div class="msg-tab">
+      <div class="left-menu">
+        <div
+          class="menu"
+          :class="{ active: menuActive == '1' }"
+          @click="menuActive = '1'"
+        >
+          主要规格及技术指标
+        </div>
+        <div
+          class="menu"
+          :class="{ active: menuActive == '2' }"
+          @click="menuActive = '2'"
+        >
+          主要功能及特色
+        </div>
+        <div
+          class="menu"
+          :class="{ active: menuActive == '3' }"
+          @click="menuActive = '3'"
+        >
+          主要附件及配置
+        </div>
+      </div>
+      <div class="right-content">
+        <div v-show="menuActive == '1'">
+          <div class="mod-title">主要规格及技术指标</div>
+          <div class="text-item">{{ instrDetails.instSpecParam }}</div>
+        </div>
+        <div v-show="menuActive == '2'">
+          <div class="mod-title">主要功能及特色</div>
+          <div class="text-item">{{ instrDetails.instFunctFeat }}</div>
+        </div>
+        <div v-show="menuActive == '3'">
+          <div class="mod-title">主要附件及配置</div>
+          <div class="text-item">{{ instrDetails.instAttConfig }}</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "equipmentDetails",
+  props: {
+    instrDetails: {
+      type: Object,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      menuActive: "1",
+      activeName: "first",
+    };
+  },
+  created() {},
+
+  mounted() {},
+
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.details-wrap {
+  .msg-item {
+    padding: 3px 30px;
+    background: #fafbfc;
+    border: 1px solid #e8ecf2;
+    border-radius: 2px;
+    font-size: 14px;
+    color: #666;
+    line-height: 24px;
+    .item-group:first-child {
+      border-top: 0;
+    }
+    .item-group {
+      border-top: 1px dashed #e1e4ee;
+      padding: 9px 0 10px;
+      .item {
+        padding: 5px 0;
+        .title {
+          width: 78px;
+          float: left;
+          color: #666;
+        }
+        .text-item {
+          padding-left: 78px;
+        }
+      }
+    }
+  }
+  .msg-tab {
+    margin: 24px 0 0;
+    min-height: 554px;
+    height: 554px;
+    background: #fff;
+    border: 1px solid #e8ecf2;
+    display: flex;
+    .left-menu {
+      width: 180px;
+      height: 100%;
+      padding: 10px 0;
+      background: #fafbfc;
+      .menu {
+        height: 60px;
+        line-height: 60px;
+        text-align: left;
+        padding-left: 18px;
+        cursor: pointer;
+        &.active {
+          background: #deecff;
+        }
+      }
+    }
+    .right-content {
+      flex: 1;
+      width: 0;
+      height: 100%;
+      padding: 10px 10px 10px 20px;
+      .mod-title {
+        margin-top: 15px;
+        padding-left: 17px;
+        font-size: 18px;
+        line-height: 24px;
+        padding-bottom: 16px;
+        position: relative;
+        &::before {
+          display: block;
+          content: "";
+          position: absolute;
+          left: 0;
+          top: 0;
+          width: 5px;
+          height: 24px;
+          background: #1677ff;
+          border-radius: 4px;
+        }
+      }
+      .text-item {
+        font-size: 16px;
+        line-height: 32px;
+      }
+    }
+  }
+}
+</style>

+ 87 - 0
src/views/equipment/components/instr-file.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="container">
+    <el-table :data="tableData" height="500px" style="width: 100%" border>
+      <!-- <el-table-column type="seq" width="60"></el-table-column> -->
+      <el-table-column prop="id" label="文件名称">
+        <template #default="{ row }">
+          <el-link :href="row.docUrl" type="primary" target="_blank">
+            {{ row.docName }}
+          </el-link>
+        </template>
+      </el-table-column>
+      <el-table-column prop="docType" label="附件类型"></el-table-column>
+      <el-table-column prop="createdName" label="操作人"></el-table-column>
+      <el-table-column prop="createdTime" label="更新时间"></el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="searchForm.pageNum"
+        :page-sizes="[10, 50, 100]"
+        :page-size="searchForm.pageSize"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+      ></el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import to from "await-to-js";
+import { getFileTable } from "@/api/instr/index";
+export default {
+  name: "equipment",
+  components: {},
+  data() {
+    return {
+      tableData: [],
+      searchForm: {
+        pageNum: 1,
+        pageSize: 10,
+        instId: 0,
+      },
+      total: 0,
+    };
+  },
+  created() {},
+
+  mounted() {
+    this.searchForm.instId = this.$route.query.id * 1;
+    this.getFileListData();
+  },
+
+  methods: {
+    async getFileListData() {
+      const [err, res] = await to(getFileTable(this.searchForm));
+      if (err) return;
+      if (res.code == 200) {
+        this.tableData = res.data.list;
+        this.total = res.data.total;
+      }
+    },
+    handleSizeChange(val) {
+      this.searchForm.pageSize = val;
+      this.getFileListData();
+    },
+    handleCurrentChange(val) {
+      this.searchForm.pageNum = val;
+      this.getFileListData();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  padding: 10px 0;
+}
+.pagination {
+  width: 100%;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 109 - 0
src/views/equipment/components/instr-notice.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="container">
+    <el-table :data="tableData" height="500px" style="width: 100%" border>
+      <el-table-column
+        prop="noticeTitle"
+        show-overflow
+        label="公告标题"
+      ></el-table-column>
+      <el-table-column prop="createdName" label="创建人"></el-table-column>
+      <el-table-column prop="noticeTime" label="公告时间"></el-table-column>
+      <el-table-column width="100">
+        <template #header>
+          <span>操作</span>
+        </template>
+        <template #default="{ row }">
+          <el-button type="text" @click="openDialog(row)">
+            查看公告
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="pagination">
+      <el-pagination
+        background
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="searchForm.pageNum"
+        :page-sizes="[10, 50, 100]"
+        :page-size="searchForm.pageSize"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+      ></el-pagination>
+    </div>
+    <!-- 公告详情 -->
+    <el-dialog
+      :title="noticeTitle"
+      :visible.sync="noticeTitleDialog"
+      width="800px"
+    >
+      <div v-html="noticeContent" class="content-scroll"></div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import to from "await-to-js";
+import { getNoticeTable } from "@/api/instr/index";
+export default {
+  name: "equipment",
+  components: {},
+  data() {
+    return {
+      tableData: [],
+      searchForm: {
+        pageNum: 1,
+        pageSize: 10,
+        instId: 0,
+      },
+      total: 0,
+      noticeTitle: "",
+      noticeTitleDialog: false,
+      noticeContent: "",
+    };
+  },
+  created() {},
+
+  mounted() {
+    this.searchForm.instId = this.$route.query.id * 1;
+    this.getNoticeListData();
+  },
+
+  methods: {
+    openDialog(row) {
+      this.noticeTitle = row.noticeTitle;
+      this.noticeTitleDialog = true;
+      this.noticeContent = row.noticeContent;
+    },
+    async getNoticeListData() {
+      const [err, res] = await to(getNoticeTable(this.searchForm));
+      if (err) return;
+      if (res.code == 200) {
+        this.tableData = res.data.list;
+        this.total = res.data.total;
+      }
+    },
+    handleSizeChange(val) {
+      this.searchForm.pageSize = val;
+      this.getNoticeListData();
+    },
+    handleCurrentChange(val) {
+      this.searchForm.pageNum = val;
+      this.getNoticeListData();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.container {
+  padding: 10px 0;
+}
+.pagination {
+  width: 100%;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 308 - 0
src/views/equipment/details.vue

@@ -0,0 +1,308 @@
+<template>
+  <section class="section-container">
+    <div class="detail-intro">
+      <div class="img-wrap">
+        <img :src="instrDetails.instPicture" alt="" style="width: 100%;" />
+      </div>
+      <div class="detail-info">
+        <h3 class="instr-name">{{ instrDetails.instName }}</h3>
+        <div class="info-wrap">
+          <ul>
+            <li>
+              <div class="msg">
+                <span class="num">
+                  {{ instrDetails.focusPeopleNum }}
+                </span>
+                /人
+              </div>
+              <em>关注者</em>
+            </li>
+            <li>
+              <div class="msg">
+                <span class="num">
+                  {{ instrDetails.usePeopleNum }}
+                </span>
+                /人
+              </div>
+              <em>使用者</em>
+            </li>
+            <li>
+              <div class="msg">
+                <span class="num">
+                  {{ instrDetails.useTimesNum }}
+                </span>
+                /次
+              </div>
+              <em>总次数</em>
+            </li>
+            <li>
+              <div class="msg">
+                <span class="num">
+                  {{ instrDetails.useDuration }}
+                </span>
+                /小时
+              </div>
+              <em>总时长</em>
+            </li>
+          </ul>
+        </div>
+        <div class="text-item">
+          <div class="info-item">
+            <div class="title">
+              收费标准
+            </div>
+            <div class="txt">
+              <div class="sf-box">
+                <div class="d1">机时</div>
+                <div class="d2">0元/小时</div>
+              </div>
+            </div>
+          </div>
+          <div class="info-item">
+            <div class="title">
+              设备型号
+            </div>
+            <div class="txt">
+              {{ instrDetails.instNameEn }}
+            </div>
+          </div>
+          <div class="info-item">
+            <div class="title">
+              当前状态
+            </div>
+            <div class="txt">
+              <span v-if="instrDetails.instStatus == '10'">
+                正常
+              </span>
+              <span v-if="instrDetails.instStatus == '20'">
+                故障
+              </span>
+              <span v-if="instrDetails.instStatus == '30'">
+                报废
+              </span>
+            </div>
+          </div>
+          <div class="info-item">
+            <div class="title">
+              负责人
+            </div>
+            <div class="txt">
+              {{ instrDetails.instHeadName }}
+            </div>
+          </div>
+          <div class="info-item">
+            <div class="title">
+              放置地点
+            </div>
+            <div class="txt">
+              <span :title="instrDetails.placeAddress">
+                {{ instrDetails.placeAddress || "-" }}
+                <span v-if="instrDetails.laboratoryName">
+                  ({{ instrDetails.laboratoryName }})
+                </span>
+              </span>
+            </div>
+          </div>
+        </div>
+        <div class="appoint-btn-wrap">
+          <el-button
+            type="primary"
+            size="default"
+            v-if="
+              instrDetails.instStatus == '10' &&
+                instrDetails.isAppointment == '10'
+            "
+            @click=""
+          >
+            使用预约
+          </el-button>
+          <el-button
+            type="success"
+            size="default"
+            v-if="
+              instrDetails.instStatus == '10' &&
+                instrDetails.isSampleDelivery == '10'
+            "
+            @click=""
+          >
+            送样预约
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <div class="tab-container def-tabs-wrap">
+      <el-tabs class="mt10 mb10" v-model="activeName" type="card" @tab-click="">
+        <el-tab-pane label="仪器信息" name="info">
+          <EqptDetails ref="eqptRef" :instrDetails="instrDetails"></EqptDetails>
+        </el-tab-pane>
+        <el-tab-pane label="预约信息" name="second"></el-tab-pane>
+        <el-tab-pane label="附件信息" name="file">
+          <FileTable v-if="activeName == 'file'"></FileTable>
+        </el-tab-pane>
+        <el-tab-pane label="仪器公告" name="notice">
+          <NoticeTable v-if="activeName == 'notice'"></NoticeTable>
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+  </section>
+</template>
+
+<script>
+import to from "await-to-js";
+import { getInstrDetails } from "@/api/instr/index";
+import EqptDetails from "./components/eqpt-details.vue";
+import FileTable from "./components/instr-file.vue";
+import NoticeTable from "./components/instr-notice.vue";
+export default {
+  name: "equipmentDetails",
+  components: { EqptDetails, FileTable, NoticeTable },
+  data() {
+    return {
+      activeName: "info",
+      instrDetails: {},
+    };
+  },
+  created() {},
+
+  mounted() {
+    this.getInstrDetails();
+  },
+
+  methods: {
+    async getInstrDetails() {
+      console.log(this.$route.query.id);
+      const id = this.$route.query.id * 1;
+      if (!id)
+        return this.$message({
+          message: "缺少必要查询参数",
+          type: "warning",
+        });
+      const [err, res] = await to(getInstrDetails({ id }));
+      if (err) return;
+      if (res.code == 200) {
+        this.instrDetails = res.data;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.section-container {
+  width: 1200px;
+  margin: 0 auto;
+  .detail-intro {
+    display: flex;
+    .img-wrap {
+      width: 420px;
+      height: 420px;
+      background: #f3f6fb;
+      border: 1px solid #e1e4ee;
+      -webkit-box-sizing: border-box;
+      box-sizing: border-box;
+      float: left;
+      position: relative;
+      overflow: hidden;
+    }
+    .detail-info {
+      margin-left: 28px;
+      .instr-name {
+        font-size: 20px;
+        font-weight: 700;
+        line-height: 26px;
+        padding: 10px 0 16px;
+      }
+      .info-wrap {
+        width: 483px;
+        height: 72px;
+        background: #f8f9fb;
+        border: 1px solid #e8ecf2;
+        border-radius: 4px;
+        margin-bottom: 16px;
+        color: #666;
+        li {
+          position: relative;
+          float: left;
+          width: 25%;
+          text-align: center;
+          font-size: 12px;
+          line-height: 16px;
+          position: relative;
+          .msg {
+            line-height: 18px;
+            font-weight: 700;
+            font-size: 14px;
+            padding: 16px 0 5px;
+            .num {
+              font-size: 20px;
+            }
+          }
+          &:nth-of-type(2)::before,
+          &:nth-of-type(3)::before,
+          &:nth-of-type(4)::before {
+            display: block;
+            content: "";
+            position: absolute;
+            top: 19px;
+            left: 0;
+            width: 1px;
+            height: 36px;
+            background: #e4e9f2;
+          }
+        }
+      }
+      .text-item {
+        min-height: 218px;
+        height: auto;
+        box-sizing: border-box;
+        padding-bottom: 47px;
+        .info-item {
+          width: 100%;
+          min-height: 28px;
+          font-size: 14px;
+          line-height: 28px;
+          margin-bottom: 6px;
+        }
+        .title {
+          width: 74px;
+          float: left;
+          font-weight: 700;
+          color: #666;
+        }
+        .txt {
+          margin-left: 74px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          max-width: 410px;
+          .sf-box {
+            display: inline-block;
+            border: 1px solid blue;
+            width: 150px;
+            .d1 {
+              display: inline-block;
+              background-color: dodgerblue;
+              padding: 0 8px 0 8px;
+              color: white;
+              border-right: 1px solid blue;
+            }
+            .d2 {
+              display: inline-block;
+              color: blue;
+              white-space: nowrap;
+              overflow: hidden;
+              text-overflow: ellipsis;
+              vertical-align: bottom;
+              width: 100px;
+              padding-left: 6px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+div {
+  color: #333;
+}
+</style>

+ 572 - 0
src/views/equipment/index.vue

@@ -0,0 +1,572 @@
+<template>
+  <div class="instr-container">
+    <div class="search-wrap">
+      <div class="search-item">
+        <div class="input-item">
+          <el-input
+            class="mr20"
+            v-model="searchForm.searchText"
+            placeholder="请输入仪器名称"
+            clearable
+            style="height: 48px;"
+          />
+          <el-button type="primary" style="width: 90px;" @click="globalSearch">
+            搜索
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <div class="common-container">
+      <section class="section-container">
+        <div class="filter-wrap">
+          <div class="filter-item dept-wrap">
+            <!-- 实验室管理的实验室 -->
+            <div class="left-tit">所属平台</div>
+            <div class="right-filter-content">
+              <div
+                class="more-item"
+                @click="handleExpandFilter('plat')"
+                v-if="laboratoryNameOptions.length > 8"
+              >
+                <span>{{ platFilterExpand ? "收起" : "更多" }}</span>
+                <i v-if="!platFilterExpand" class="el-icon-caret-top"></i>
+                <i v-else class="el-icon-caret-bottom"></i>
+              </div>
+              <div class="list">
+                <div
+                  @click="selectFilterItem(v.name, 'laboratoryName')"
+                  class="name"
+                  :class="{ avtive: searchForm.laboratoryName == v.name }"
+                  v-for="(v, i) in laboratoryNameOptions"
+                  v-show="!platFilterExpand ? i < 8 : i > -1"
+                  :key="i"
+                >
+                  {{ `${v.name}(${v.count}台)` }}
+                </div>
+                <!-- <div class="dragger-container" v-if="platFilterExpand">
+                  <div class="dragger-bar"></div>
+                </div> -->
+              </div>
+            </div>
+          </div>
+          <div class="filter-item address">
+            <!-- 仪器类别 -->
+            <div class="left-tit">仪器类别</div>
+            <div class="right-filter-content">
+              <div
+                class="more-item"
+                @click="handleExpandFilter('instrType')"
+                v-if="instClassDescOptions.length > 8"
+              >
+                <span>{{ instrTypeFilterExpand ? "收起" : "更多" }}</span>
+                <i v-if="!instrTypeFilterExpand" class="el-icon-caret-top"></i>
+                <i v-else class="el-icon-caret-bottom"></i>
+              </div>
+              <div class="list">
+                <div
+                  @click="selectFilterItem(v.name, 'instClassDesc')"
+                  class="name"
+                  :class="{ avtive: searchForm.instClassDesc == v.name }"
+                  v-for="(v, i) in instClassDescOptions"
+                  v-show="!instrTypeFilterExpand ? i < 8 : i > -1"
+                  :key="i"
+                >
+                  {{ `${v.name}(${v.count}台)` }}
+                </div>
+                <!-- <div class="dragger-container" v-if="instrTypeFilterExpand">
+                  <div class="dragger-bar"></div>
+                </div> -->
+              </div>
+            </div>
+          </div>
+          <div class="filter-item type">
+            <!-- 从前两个查询条件查询 -->
+            <div class="left-tit">仪器型号</div>
+            <div class="right-filter-content">
+              <div class="list">
+                <div
+                  @click="selectFilterItem(v.name, 'instNameEn')"
+                  class="name"
+                  :class="{ avtive: searchForm.instNameEn == v.name }"
+                  v-for="(v, i) in instNameEnOptions"
+                  v-show="!instrTypeFilterExpand ? i < 8 : i > -1"
+                  :key="i"
+                >
+                  {{ `${v.name}(${v.count}台)` }}
+                </div>
+                <!-- <div class="dragger-container" v-if="instrTypeFilterExpand">
+                  <div class="dragger-bar"></div>
+                </div> -->
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="instr-list">
+          <div class="filter-result">
+            <h4 class="title">已选条件</h4>
+            <div class="filter-wrap">
+              <el-tag
+                v-if="searchForm.laboratoryName"
+                type="primary"
+                size="normal"
+                closable
+                @close="closeTag('laboratoryName')"
+                class="mr20"
+              >
+                {{ searchForm.laboratoryName }}
+              </el-tag>
+              <el-tag
+                v-if="searchForm.instClassDesc"
+                type="success"
+                size="normal"
+                closable
+                @close="closeTag('instClassDesc')"
+                class="mr20"
+              >
+                {{ searchForm.instClassDesc }}
+              </el-tag>
+              <el-tag
+                v-if="searchForm.instNameEn"
+                type="warning"
+                size="normal"
+                closable
+                @close="closeTag('instNameEn')"
+              >
+                {{ searchForm.instNameEn }}
+              </el-tag>
+            </div>
+          </div>
+          <el-empty
+            v-if="instrDataList.length == 0"
+            description="很抱歉,没有找到相关的仪器~"
+          ></el-empty>
+          <div v-else>
+            <div class="instr-item" v-for="v in instrDataList" :key="v.id">
+              <div class="img-item">
+                <img :src="v.instPicture" style="width: 100%;" alt="" />
+              </div>
+              <div class="text-item">
+                <p class="name">{{ v.instName }}</p>
+                <div class="info">
+                  <div class="info-title">型号</div>
+                  <div class="info-txt">{{ v.instNameEn }}</div>
+                </div>
+                <div class="info">
+                  <div class="info-title">状态</div>
+                  <div class="info-txt">
+                    <span v-if="v.instStatus == '10'">
+                      正常
+                    </span>
+                    <span v-if="v.instStatus == '20'">
+                      故障
+                    </span>
+                    <span v-if="v.instStatus == '30'">
+                      报废
+                    </span>
+                  </div>
+                </div>
+                <div class="info">
+                  <div class="info-title">仪器负责人</div>
+                  <div class="info-txt">{{ v.instHeadName }}</div>
+                </div>
+                <div class="info">
+                  <div class="info-title">放置地点</div>
+                  <div class="info-txt">
+                    <span :title="v.placeAddress">
+                      {{ v.placeAddress || "-" }}
+                      <span v-if="v.laboratoryName">
+                        ({{ v.laboratoryName }})
+                      </span>
+                    </span>
+                  </div>
+                </div>
+              </div>
+              <div class="appoint-item">
+                <div class="mb20">
+                  <el-button
+                    type="primary"
+                    size="default"
+                    v-if="v.instStatus == '10' && v.isAppointment == '10'"
+                    @click=""
+                  >
+                    仪器预约
+                  </el-button>
+                </div>
+                <div>
+                  <el-button
+                    type="primary"
+                    plain
+                    size="default"
+                    @click="linkToDetails(v)"
+                  >
+                    查看详情
+                  </el-button>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="pagination">
+            <el-pagination
+              background
+              @size-change="handleSizeChange"
+              @current-change="handleCurrentChange"
+              :current-page="searchForm.pageNum"
+              :page-sizes="[10, 50, 100]"
+              :page-size="searchForm.pageSize"
+              layout="total, sizes, prev, pager, next, jumper"
+              :total="total"
+            ></el-pagination>
+          </div>
+        </div>
+      </section>
+    </div>
+  </div>
+</template>
+
+<script>
+import to from "await-to-js";
+import { Loading } from "element-ui";
+import { getInstrList, getInstNameEnCount } from "@/api/instr/index";
+export default {
+  name: "equipment",
+  components: {},
+  data() {
+    return {
+      platFilterExpand: false,
+      instrTypeFilterExpand: false,
+      searchForm: {
+        searchText: "",
+        laboratoryName: "",
+        instClassDesc: "",
+        instNameEn: "",
+        pageNum: 1,
+        pageSize: 10,
+      },
+      total: 0,
+      instrDataList: [],
+      laboratoryNameOptions: [],
+      instClassDescOptions: [],
+      instNameEnOptions: [],
+      options: {
+        text: "正在加载中,请稍后...",
+        background: "rgba(0, 0, 0, 0.6)",
+      },
+    };
+  },
+  created() {},
+
+  mounted() {
+    this.getInstrData();
+  },
+
+  methods: {
+    // 获取型号数量
+    async getInstNameEnCountData() {
+      const params = {
+        instClassDesc: this.searchForm.instClassDesc,
+        laboratoryName: this.searchForm.laboratoryName,
+        searchText: this.searchForm.searchText,
+      };
+      const [err, res] = await to(getInstNameEnCount(params));
+      if (err) return;
+      if (res.code == 200) {
+        this.instNameEnOptions = res.data;
+      }
+    },
+    // 上方搜索查询
+    async globalSearch() {
+      this.searchForm.pageNum = 1;
+      this.searchForm.instClassDesc = "";
+      this.searchForm.laboratoryName = "";
+      this.searchForm.instNameEn = "";
+      this.getInstrData();
+    },
+    async getInstrData() {
+      Loading.service(this.loadingOption);
+      const [err, res] = await to(getInstrList(this.searchForm));
+      if (err) return;
+      if (res.code == 200) {
+        this.instrDataList = res.data.list;
+        this.total = res.data.total;
+        this.laboratoryNameOptions = this.objTransforArr(
+          res.data.count.laboratoryName
+        );
+        this.instClassDescOptions = this.objTransforArr(
+          res.data.count.instClassDesc
+        );
+        this.getInstNameEnCountData();
+      }
+      setTimeout(() => {
+        Loading.service(this.loadingOption).close();
+      });
+    },
+    // 对象转数组
+    objTransforArr(obj) {
+      const arr = Object.entries(obj).map(([key, value]) => ({
+        name: key,
+        count: value,
+      }));
+      return arr;
+    },
+    // 删除查询条件
+    closeTag(type) {
+      switch (type) {
+        case "laboratoryName":
+          this.searchForm.laboratoryName = "";
+          break;
+        case "instClassDesc":
+          this.searchForm.instClassDesc = "";
+          break;
+        case "instNameEn":
+          this.searchForm.instNameEn = "";
+          break;
+      }
+      this.searchForm.pageNum = 1;
+      this.getInstrData();
+    },
+    // 展开 合并
+    handleExpandFilter(type = "plat" | "instrType") {
+      if (type === "plat") {
+        this.platFilterExpand = !this.platFilterExpand;
+      } else if (type === "instrType") {
+        this.instrTypeFilterExpand = !this.instrTypeFilterExpand;
+      }
+    },
+    // 选择查询条件
+    selectFilterItem(name, val) {
+      switch (val) {
+        case "laboratoryName":
+          if (this.searchForm.laboratoryName != name) {
+            this.searchForm.laboratoryName = name;
+            this.searchForm.pageNum = 1;
+            this.getInstrData();
+          }
+          break;
+        case "instClassDesc":
+          if (this.searchForm.instClassDesc != name) {
+            this.searchForm.instClassDesc = name;
+            this.searchForm.pageNum = 1;
+            this.getInstrData();
+          }
+          break;
+        case "instNameEn":
+          if (this.searchForm.instNameEn != name) {
+            this.searchForm.instNameEn = name;
+            this.searchForm.pageNum = 1;
+            this.getInstrData();
+          }
+          break;
+      }
+    },
+    handleSizeChange(val) {
+      this.searchForm.pageSize = val;
+      this.getInstrData();
+    },
+    handleCurrentChange(val) {
+      this.searchForm.pageNum = val;
+      this.getInstrData();
+    },
+    linkToDetails(v) {
+      this.$router.push("/appointment/equipment-details?id=" + v.id);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.search-wrap {
+  width: 100%;
+  height: 240px;
+  position: relative;
+  &::after {
+    display: block;
+    content: "";
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    background: url("../../assets/img/equipment-banner.png") center;
+  }
+  .search-item {
+    width: 800px;
+    height: 240px;
+    margin: 0 auto;
+    position: relative;
+    z-index: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .input-item {
+      width: 100%;
+      height: 40px;
+      display: flex;
+    }
+  }
+}
+.section-container {
+  width: 1200px;
+  margin: 0 auto;
+}
+.filter-wrap {
+  width: 100%;
+  .filter-item {
+    width: 100%;
+    min-height: 48px;
+    display: flex;
+    border-bottom: 2px dashed rgba(112, 112, 112, 0.18);
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+
+  .left-tit {
+    width: 120px;
+    background: #f1f1f1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: bold;
+  }
+  .right-filter-content {
+    flex: 1;
+    padding: 9px 24px 9px 36px;
+    display: flex;
+    flex-wrap: wrap;
+    position: relative;
+    .list {
+      max-height: 120px;
+      overflow: auto;
+      width: 100%;
+      display: flex;
+      flex-wrap: wrap;
+      // &::-webkit-scrollbar {
+      //   display: none;
+      // }
+      .dragger-container {
+        position: absolute;
+        width: 6px;
+        top: 36px;
+        right: 34px;
+        height: 84px;
+        background: #ccc;
+        border-radius: 20px;
+        .dragger-bar {
+          position: absolute;
+          width: 6px;
+          height: 60px;
+          top: 0px;
+          border-radius: 20px;
+          background: rgba(0, 0, 0, 0.75);
+        }
+      }
+    }
+    .name {
+      font-size: 14px;
+      margin: 5px 30px 5px 0;
+      cursor: pointer;
+      &:hover {
+        text-decoration: underline;
+        color: #1677ff;
+      }
+      &.avtive {
+        text-decoration: underline;
+        color: #1677ff;
+      }
+    }
+    .more-item {
+      position: absolute;
+      top: 12px;
+      right: 0;
+      padding-right: 16px;
+      font-size: 14px;
+      cursor: pointer;
+      z-index: 2;
+      i {
+        color: #b1b1b1;
+      }
+    }
+  }
+}
+.filter-result {
+  padding: 15px 18px 0 18px;
+  display: flex;
+  border: 1px solid rgba(112, 112, 112, 0.06);
+  position: relative;
+  .title {
+    width: 120px;
+    font-size: 16px;
+    color: #666;
+    font-weight: 700;
+    padding-right: 18px;
+    padding-top: 3px;
+    flex: 0 0 120px;
+  }
+  .filter-wrap {
+    min-height: 45px;
+    padding-right: 80px;
+  }
+}
+.instr-list {
+  background: #fff;
+  border-radius: 6px;
+  margin-top: 20px;
+  min-height: 200px;
+  .instr-item {
+    padding: 24px 30px;
+    margin: 0 30px;
+    display: flex;
+    .img-item {
+      width: 180px;
+      height: 180px;
+      border-radius: 6px;
+      overflow: hidden;
+      margin-right: 18px;
+      position: relative;
+      background: #f3f6fb;
+    }
+    .text-item {
+      flex: 1;
+      width: 0;
+      font-size: 14px;
+      padding-top: 6px;
+      .name {
+        font-weight: bold;
+        font-size: 20px;
+        margin-bottom: 12px;
+      }
+      .info {
+        color: #666;
+        display: flex;
+        font-size: 14px;
+        margin-bottom: 8px;
+        .info-title {
+          width: 80px;
+        }
+        .info-txt {
+          width: 0;
+          flex: 1;
+        }
+      }
+    }
+    .appoint-item {
+      width: 120px;
+      padding: 0 0 0 18px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+}
+.pagination {
+  width: 100%;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+div {
+  color: #333;
+}
+</style>