瀏覽代碼

first commit

DESKTOP-34DQRNR\TJK 2 年之前
當前提交
e187d87bde
共有 55 個文件被更改,包括 14301 次插入0 次删除
  1. 1 0
      .env
  2. 1 0
      .env.development
  3. 1 0
      .env.production
  4. 1 0
      .env.staging
  5. 2 0
      .eslintignore
  6. 31 0
      .eslintrc.json
  7. 23 0
      .gitignore
  8. 1 0
      .husky/note.md
  9. 1 0
      .husky/pre-commit
  10. 12 0
      Dockerfile
  11. 35 0
      README.md
  12. 5 0
      babel.config.js
  13. 19 0
      jsconfig.json
  14. 51 0
      nginx.conf
  15. 8684 0
      package-lock.json
  16. 47 0
      package.json
  17. 7 0
      public/domain.js
  18. 二進制
      public/favicon.ico
  19. 16 0
      public/index.html
  20. 9 0
      src/App.vue
  21. 66 0
      src/api/board.js
  22. 二進制
      src/assets/battery1.png
  23. 二進制
      src/assets/battery2.png
  24. 二進制
      src/assets/battery3.png
  25. 二進制
      src/assets/battery4.png
  26. 二進制
      src/assets/battery5.png
  27. 二進制
      src/assets/infusion_alarm.png
  28. 二進制
      src/assets/infusion_normal.png
  29. 二進制
      src/assets/infusion_pause.png
  30. 二進制
      src/assets/logo.png
  31. 27 0
      src/components/Demo.vue
  32. 22 0
      src/main.js
  33. 6 0
      src/mock/index.js
  34. 11 0
      src/mock/user.js
  35. 21 0
      src/router/index.js
  36. 5 0
      src/store/getters.js
  37. 17 0
      src/store/index.js
  38. 41 0
      src/store/modules/user.js
  39. 95 0
      src/styles/iconfont.css
  40. 二進制
      src/styles/iconfont.eot
  41. 1 0
      src/styles/iconfont.js
  42. 149 0
      src/styles/iconfont.json
  43. 32 0
      src/styles/iconfont.svg
  44. 二進制
      src/styles/iconfont.ttf
  45. 二進制
      src/styles/iconfont.woff
  46. 二進制
      src/styles/iconfont.woff2
  47. 1 0
      src/styles/index.less
  48. 3978 0
      src/styles/main.css
  49. 1 0
      src/styles/variable.less
  50. 29 0
      src/utils/auth.js
  51. 117 0
      src/utils/index.js
  52. 98 0
      src/utils/request.js
  53. 20 0
      src/utils/validate.js
  54. 572 0
      src/views/Index.vue
  55. 45 0
      vue.config.js

+ 1 - 0
.env

@@ -0,0 +1 @@
+VUE_APP_TITLE=DEMO

+ 1 - 0
.env.development

@@ -0,0 +1 @@
+VUE_APP_FLAG=dev

+ 1 - 0
.env.production

@@ -0,0 +1 @@
+VUE_APP_FLAG=prod

+ 1 - 0
.env.staging

@@ -0,0 +1 @@
+VUE_APP_FLAG=staging

+ 2 - 0
.eslintignore

@@ -0,0 +1,2 @@
+assets
+mock

+ 31 - 0
.eslintrc.json

@@ -0,0 +1,31 @@
+{
+    "env": {
+        "browser": true,
+        "es2021": true
+    },
+    "extends": [
+        "plugin:vue/essential",
+        "airbnb-base"
+    ],
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module"
+    },
+    "plugins": [
+        "vue"
+    ],
+    "rules": {
+        "semi": [2, "never"],
+        "comma-dangle": 0,
+        "no-console": 0,
+        "quotes": [2, "single"],
+        "no-undef": 0,
+        "no-debugger": 0,
+        "import/no-unresolved": 0,
+        "import/extensions": 0,
+        "no-param-reassign": 0,
+        "vue/multi-word-component-names": 0,
+        "vue/no-multiple-template-root": 0,
+        "import/no-extraneous-dependencies": 0
+    }
+}

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 1 - 0
.husky/note.md

@@ -0,0 +1 @@
+try to run `chmod +x ./.husky/pre-commit` if pre-commit doesn't work.

+ 1 - 0
.husky/pre-commit

@@ -0,0 +1 @@
+npx eslint ./src --ext .js,.vue

+ 12 - 0
Dockerfile

@@ -0,0 +1,12 @@
+FROM nginx:alpine
+MAINTAINER wuyunfeng
+
+RUN mkdir -p /app/
+COPY ./dist /app/
+COPY ./nginx.conf /etc/nginx/nginx.conf
+
+EXPOSE 443 80
+ENV serverUrl=http://172.28.100.100:8005 deviceUrl=http://172.28.100.100:8006 language=zh showDate=true
+
+
+CMD sh -c "sed -i -e \"s~^.*serverUrl.*$~serverUrl:'$serverUrl',~;s~^.*language.*$~language:'$language',~;s~^.*deviceUrl.*$~deviceUrl:'$deviceUrl',~;s~^.*showDate.*$~showDate:$showDate~\" /app/domain.js; exec nginx -g \"daemon off;\""

+ 35 - 0
README.md

@@ -0,0 +1,35 @@
+# view-ui-project-vuecli
+This project build for Vue3, Vue-router, Vuex, ViewUIPlus and Vue-cli.
+## Install
+```shell
+npm install
+```
+## Run
+```shell
+npm run serve
+```
+## Build
+### Build for Production
+```shell
+npm run build
+```
+### Build for Staging
+```shell
+npm run build:staging
+```
+## Run Eslint
+### Run without Fixing
+
+- This operation will also run before git-commit.
+```shell
+npm run lint
+```
+### Run with Fixing
+```shell
+npm run lint:fix
+```
+## .env Description
+
+- This project exposes environment variables on `process.env` object.
+- Different modes (development/staging/production) correspond to different environment files (.env.*).
+- .env file is always included, duplicate variables are overwritten by the specific mode file (.env.*).

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

+ 19 - 0
jsconfig.json

@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "esnext",
+    "baseUrl": "./",
+    "moduleResolution": "node",
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  }
+}

+ 51 - 0
nginx.conf

@@ -0,0 +1,51 @@
+#这个文件给docker用的
+#user  nobody;
+worker_processes  1;
+
+#error_log  logs/error.log;
+#error_log  logs/error.log  notice;
+#error_log  logs/error.log  info;
+
+#pid        logs/nginx.pid;
+
+
+events {
+    worker_connections  1024;
+}
+
+
+http {
+    include       mime.types;
+    default_type  application/octet-stream;
+
+    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+    #                  '$status $body_bytes_sent "$http_referer" '
+    #                  '"$http_user_agent" "$http_x_forwarded_for"';
+
+    #access_log  logs/access.log  main;
+
+    sendfile        on;
+    #tcp_nopush     on;
+
+    #keepalive_timeout  0;
+    keepalive_timeout  65;
+    client_max_body_size 10m;
+    gzip on;
+    gzip_min_length  5k;
+    gzip_buffers     4 16k;
+    gzip_http_version 1.0;
+    gzip_comp_level 4;
+    gzip_types       text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
+    gzip_vary on;
+
+    server {
+        listen       80;
+        server_name  localhost;
+
+        location / {
+            root /app;
+            try_files $uri $uri/ /index.html $uri/ =404;
+            index  index.html index.htm;
+        }
+    }
+}

文件差異過大導致無法顯示
+ 8684 - 0
package-lock.json


+ 47 - 0
package.json

@@ -0,0 +1,47 @@
+{
+  "name": "view-ui-project-vuecli",
+  "version": "1.0.0",
+  "description": "a template project for vue3, vue-router, vuex, ViewUIPlus and @vue/cli.",
+  "scripts": {
+    "dev": "npm run serve",
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "build:staging": "vue-cli-service build --mode=staging",
+    "lint": "eslint ./src --ext .js,.vue",
+    "lint:fix": "eslint ./src --ext .js,.vue --fix",
+    "prepare": "husky install"
+  },
+  "dependencies": {
+    "core-js": "^3.8.3",
+    "view-ui-plus": "^1.1.0",
+    "vue": "^3.2.13",
+    "vue-router": "^4.0.3",
+    "vuex": "^4.0.0",
+    "moment": "^2.29.1",
+    "js-cookie": "2.2.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~5.0.0",
+    "@vue/cli-plugin-router": "~5.0.0",
+    "@vue/cli-plugin-vuex": "~5.0.0",
+    "@vue/cli-service": "~5.0.0",
+    "eslint": "^8.14.0",
+    "eslint-config-airbnb-base": "^15.0.0",
+    "eslint-plugin-vue": "^8.7.1",
+    "husky": "^7.0.4",
+    "less": "^4.0.0",
+    "less-loader": "^10.2.0",
+    "mockjs": "^1.1.0",
+    "style-resources-loader": "^1.4.1",
+    "vue-cli-plugin-style-resources-loader": "~0.1.5",
+    "axios": "0.18.1"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ],
+  "engines": {
+    "node": ">= 12.0.0",
+    "npm": ">= 6.9.0"
+  }
+}

+ 7 - 0
public/domain.js

@@ -0,0 +1,7 @@
+const domain = {
+  serverUrl: 'http://192.168.1.198:8005',
+  language:'zh',
+  deviceUrl: 'http://192.168.1.198:8006',
+  showDate:true
+}
+

二進制
public/favicon.ico


+ 16 - 0
public/index.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, target-densitydpi=device-dpi">
+<!--    <meta content='device-width, initial-scale=1, maximum-scale=1, user-scalable=yes, target-densitydpi=device-dpi' name='viewport'>-->
+    <link rel="shortcut icon" href="https://file.iviewui.com/file/iview-design-favicon.ico" />
+    <title><%= htmlWebpackPlugin.options.APP_TITLE %></title>
+    <script src="/domain.js" ></script>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 9 - 0
src/App.vue

@@ -0,0 +1,9 @@
+<template>
+  <router-view/>
+</template>
+
+<script>
+export default {
+  name: 'App'
+}
+</script>

+ 66 - 0
src/api/board.js

@@ -0,0 +1,66 @@
+import request from '@/utils/request'
+
+export function getBoardTitles(partid) {
+  return request({
+    url: `/ncs/boardtitle/partboardtitle/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getBasicNursecfg(partid) {
+  return request({
+    url: `/boardinfo/getbasicnursecfg/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+
+export function getpartinfosummary(partid) {
+  return request({
+    url: `/boardinfo/getpartinfosummary/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getCallingList(partid) {
+  return request({
+    url: `/boardinfo/calling/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getNurseInfoArray(partid) {
+  return request({
+    url: `/boardinfo/nursemanagerbeds/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getBoardInfo(partid) {
+  return request({
+    url: `/boardinfo/customboardscreen/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getBedInfo(partid) {
+  return request({
+    url: `/boardinfo/infusion/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+export function getPartSetting(partid) {
+  return request({
+    url: `/deviceBed/getPartSetting/${partid}`,
+    method: 'get',
+    loading: false
+  })
+}

二進制
src/assets/battery1.png


二進制
src/assets/battery2.png


二進制
src/assets/battery3.png


二進制
src/assets/battery4.png


二進制
src/assets/battery5.png


二進制
src/assets/infusion_alarm.png


二進制
src/assets/infusion_normal.png


二進制
src/assets/infusion_pause.png


二進制
src/assets/logo.png


+ 27 - 0
src/components/Demo.vue

@@ -0,0 +1,27 @@
+<template>
+  <div class="demo">
+    <Button type="primary" @click="handleClick">Welcome!</Button>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Demo',
+  setup() {
+  },
+  methods: {
+    handleClick() {
+      this.$Message.info('Welcome to View UI Plus Demo!')
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.demo{
+  color: lightcoral;
+  font-size: @font-size;
+  text-align: center;
+  margin: 300px 0 0 0;
+}
+</style>

+ 22 - 0
src/main.js

@@ -0,0 +1,22 @@
+import { createApp } from 'vue'
+import ViewUIPlus from 'view-ui-plus'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import './styles/index.less'
+import '@/styles/main.css'
+import '@/styles/iconfont.css'
+const language = domain.language
+if (language === 'zh') {
+    require('moment/locale/zh-cn')
+} else {
+    require('moment/locale/en-gb')
+}
+// import './mock'
+
+const app = createApp(App)
+
+app.use(store)
+  .use(router)
+  .use(ViewUIPlus)
+  .mount('#app')

+ 6 - 0
src/mock/index.js

@@ -0,0 +1,6 @@
+import Mock from 'mockjs'
+import './user'
+
+Mock.setup({ timeout: 300 })
+
+export default Mock

+ 11 - 0
src/mock/user.js

@@ -0,0 +1,11 @@
+import Mock from 'mockjs'
+
+Mock.mock(/\/userList/, 'get', {
+  code: 200,
+  data: [
+    {
+      id: '@id',
+      name: '@name'
+    }
+  ]
+})

+ 21 - 0
src/router/index.js

@@ -0,0 +1,21 @@
+import { createRouter, createWebHistory } from 'vue-router'
+
+import Index from "@/views/Index";
+
+const routes = [
+  {
+    path: '/index',
+    name: 'home',
+    component: Index
+  }
+]
+
+const router = createRouter({
+  routes,
+  history: createWebHistory(process.env.BASE_URL),
+  scrollBehavior() {
+    return { top: 0 }
+  }
+})
+
+export default router

+ 5 - 0
src/store/getters.js

@@ -0,0 +1,5 @@
+const getters = {
+    token: state => state.user.token,
+    partid: state => state.user.partId
+}
+export default getters

+ 17 - 0
src/store/index.js

@@ -0,0 +1,17 @@
+import { createStore } from 'vuex'
+import user from './modules/user'
+import getters from './getters'
+export default createStore({
+  state: {
+  },
+
+    getters,
+
+  mutations: {
+  },
+  actions: {
+  },
+  modules: {
+    user
+  }
+})

+ 41 - 0
src/store/modules/user.js

@@ -0,0 +1,41 @@
+import * as Auth from '@/utils/auth'
+
+const state = {
+  token: Auth.getToken(),
+  partId: Auth.getPartId()
+}
+
+const mutations = {
+  SET_TOKEN: (state, token) => {
+    state.token = token
+  },
+  SET_PARTID: (state, partId) => {
+    state.partId = partId
+    Auth.setPartId(partId)
+  }
+}
+
+const actions = {
+  // set device Mac
+  setDeviceMAC({ commit }, mac) {
+    return new Promise((resolve, reject) => {
+      commit('SET_TOKEN', mac)
+      Auth.setToken(mac)
+    })
+  },
+  // set partId
+  setPartId({ commit }, partid) {
+    return new Promise((resolve, reject) => {
+      commit('SET_PARTID', partid)
+      Auth.setPartId(partid)
+    })
+  }
+}
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions
+}
+

+ 95 - 0
src/styles/iconfont.css

@@ -0,0 +1,95 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 1441977 */
+  src: url('iconfont.woff2?t=1647832527773') format('woff2'),
+       url('iconfont.woff?t=1647832527773') format('woff'),
+       url('iconfont.ttf?t=1647832527773') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-chuangwei:before {
+  content: "\e617";
+}
+
+.icon-chuangwei1:before {
+  content: "\e661";
+}
+
+.icon-chuyuan:before {
+  content: "\e995";
+}
+
+.icon-zhuyuan:before {
+  content: "\ea3d";
+}
+
+.icon-huli:before {
+  content: "\e603";
+}
+
+.icon-yihu:before {
+  content: "\e656";
+}
+
+.icon-guidangfanweitongji:before {
+  content: "\e645";
+}
+
+.icon-shijian:before {
+  content: "\e793";
+}
+
+.icon-16gf-phoneOutgoing:before {
+  content: "\e8f7";
+}
+
+.icon-20gl-missedOutgoing:before {
+  content: "\e91f";
+}
+
+.icon-20gf-phoneIncoming:before {
+  content: "\e934";
+}
+
+.icon-jiaohu--:before {
+  content: "\e606";
+}
+
+.icon-hujiao:before {
+  content: "\e747";
+}
+
+.icon-weibiaoti1:before {
+  content: "\e60c";
+}
+
+.icon-yonghu:before {
+  content: "\e613";
+}
+
+.icon-EmergencySOS:before {
+  content: "\e98e";
+}
+
+.icon-chuangweiguanli:before {
+  content: "\e644";
+}
+
+.icon-jian:before {
+  content: "\e612";
+}
+
+.icon-shouhou:before {
+  content: "\e713";
+}
+
+.icon-tongjitubiao:before {
+  content: "\e67a";
+}
+

二進制
src/styles/iconfont.eot


文件差異過大導致無法顯示
+ 1 - 0
src/styles/iconfont.js


+ 149 - 0
src/styles/iconfont.json

@@ -0,0 +1,149 @@
+{
+  "id": "1441977",
+  "name": "wdkl",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "5116846",
+      "name": "床位",
+      "font_class": "chuangwei",
+      "unicode": "e617",
+      "unicode_decimal": 58903
+    },
+    {
+      "icon_id": "12882329",
+      "name": "床位",
+      "font_class": "chuangwei1",
+      "unicode": "e661",
+      "unicode_decimal": 58977
+    },
+    {
+      "icon_id": "16200110",
+      "name": "出院",
+      "font_class": "chuyuan",
+      "unicode": "e995",
+      "unicode_decimal": 59797
+    },
+    {
+      "icon_id": "16204182",
+      "name": "住院",
+      "font_class": "zhuyuan",
+      "unicode": "ea3d",
+      "unicode_decimal": 59965
+    },
+    {
+      "icon_id": "1220256",
+      "name": "护理",
+      "font_class": "huli",
+      "unicode": "e603",
+      "unicode_decimal": 58883
+    },
+    {
+      "icon_id": "6894496",
+      "name": "医护",
+      "font_class": "yihu",
+      "unicode": "e656",
+      "unicode_decimal": 58966
+    },
+    {
+      "icon_id": "11661866",
+      "name": "归档范围统计",
+      "font_class": "guidangfanweitongji",
+      "unicode": "e645",
+      "unicode_decimal": 58949
+    },
+    {
+      "icon_id": "15175202",
+      "name": "事件",
+      "font_class": "shijian",
+      "unicode": "e793",
+      "unicode_decimal": 59283
+    },
+    {
+      "icon_id": "7565353",
+      "name": "16gf-phoneOutgoing",
+      "font_class": "16gf-phoneOutgoing",
+      "unicode": "e8f7",
+      "unicode_decimal": 59639
+    },
+    {
+      "icon_id": "7566332",
+      "name": "20gl-missedOutgoing",
+      "font_class": "20gl-missedOutgoing",
+      "unicode": "e91f",
+      "unicode_decimal": 59679
+    },
+    {
+      "icon_id": "7567066",
+      "name": "20gf-phoneIncoming",
+      "font_class": "20gf-phoneIncoming",
+      "unicode": "e934",
+      "unicode_decimal": 59700
+    },
+    {
+      "icon_id": "12676318",
+      "name": "交互-01-01",
+      "font_class": "jiaohu--",
+      "unicode": "e606",
+      "unicode_decimal": 58886
+    },
+    {
+      "icon_id": "21833832",
+      "name": "呼叫",
+      "font_class": "hujiao",
+      "unicode": "e747",
+      "unicode_decimal": 59207
+    },
+    {
+      "icon_id": "97353",
+      "name": "电话图标",
+      "font_class": "weibiaoti1",
+      "unicode": "e60c",
+      "unicode_decimal": 58892
+    },
+    {
+      "icon_id": "3187284",
+      "name": "用户",
+      "font_class": "yonghu",
+      "unicode": "e613",
+      "unicode_decimal": 58899
+    },
+    {
+      "icon_id": "12088832",
+      "name": "Emergency SOS",
+      "font_class": "EmergencySOS",
+      "unicode": "e98e",
+      "unicode_decimal": 59790
+    },
+    {
+      "icon_id": "14852925",
+      "name": "床位管理",
+      "font_class": "chuangweiguanli",
+      "unicode": "e644",
+      "unicode_decimal": 58948
+    },
+    {
+      "icon_id": "2966202",
+      "name": "减",
+      "font_class": "jian",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "7090062",
+      "name": "售后",
+      "font_class": "shouhou",
+      "unicode": "e713",
+      "unicode_decimal": 59155
+    },
+    {
+      "icon_id": "925649",
+      "name": "统计图表",
+      "font_class": "tongjitubiao",
+      "unicode": "e67a",
+      "unicode_decimal": 59002
+    }
+  ]
+}

文件差異過大導致無法顯示
+ 32 - 0
src/styles/iconfont.svg


二進制
src/styles/iconfont.ttf


二進制
src/styles/iconfont.woff


二進制
src/styles/iconfont.woff2


+ 1 - 0
src/styles/index.less

@@ -0,0 +1 @@
+@import '~view-ui-plus/src/styles/index.less';

文件差異過大導致無法顯示
+ 3978 - 0
src/styles/main.css


+ 1 - 0
src/styles/variable.less

@@ -0,0 +1 @@
+@font-size: 16px;

+ 29 - 0
src/utils/auth.js

@@ -0,0 +1,29 @@
+import Cookies from 'js-cookie'
+
+const TokenKey = 'device_mac'
+
+export function getToken() {
+  return Cookies.get(TokenKey)
+}
+
+export function setToken(token) {
+  return Cookies.set(TokenKey, token)
+}
+
+export function removeToken() {
+  return Cookies.remove(TokenKey)
+}
+
+/**  科室 Cookie **/
+
+export function getPartId() {
+  return Cookies.get('partId')
+}
+
+export function setPartId(partid) {
+  return Cookies.set('partId', partid)
+}
+
+export function removePartId() {
+  return Cookies.remove('partId')
+}

+ 117 - 0
src/utils/index.js

@@ -0,0 +1,117 @@
+/**
+ * Created by PanJiaChen on 16/11/18.
+ */
+
+/**
+ * Parse the time to string
+ * @param {(Object|string|number)} time
+ * @param {string} cFormat
+ * @returns {string | null}
+ */
+export function parseTime(time, cFormat) {
+  if (arguments.length === 0 || !time) {
+    return null
+  }
+  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if ((typeof time === 'string')) {
+      if ((/^[0-9]+$/.test(time))) {
+        // support "1548221490638"
+        time = parseInt(time)
+      } else {
+        // support safari
+        // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
+        time = time.replace(new RegExp(/-/gm), '/')
+      }
+    }
+
+    if ((typeof time === 'number') && (time.toString().length === 10)) {
+      time = time * 1000
+    }
+    date = new Date(time)
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    const value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
+    return value.toString().padStart(2, '0')
+  })
+  return time_str
+}
+
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+export function formatTime(time, option) {
+  if (('' + time).length === 10) {
+    time = parseInt(time) * 1000
+  } else {
+    time = +time
+  }
+  const d = new Date(time)
+  const now = Date.now()
+
+  const diff = (now - d) / 1000
+
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) {
+    // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  if (option) {
+    return parseTime(time, option)
+  } else {
+    return (
+      d.getMonth() +
+      1 +
+      '月' +
+      d.getDate() +
+      '日' +
+      d.getHours() +
+      '时' +
+      d.getMinutes() +
+      '分'
+    )
+  }
+}
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function param2Obj(url) {
+  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
+  if (!search) {
+    return {}
+  }
+  const obj = {}
+  const searchArr = search.split('&')
+  searchArr.forEach(v => {
+    const index = v.indexOf('=')
+    if (index !== -1) {
+      const name = v.substring(0, index)
+      const val = v.substring(index + 1, v.length)
+      obj[name] = val
+    }
+  })
+  return obj
+}

+ 98 - 0
src/utils/request.js

@@ -0,0 +1,98 @@
+import axios from 'axios'
+import store from '@/store'
+//import { getToken } from '@/utils/auth'
+// import { deviceUrl } from '@/utils/domain'
+const qs = require('qs')
+// create an axios instance
+const deviceUrl = domain.deviceUrl
+const service = axios.create({
+  baseURL: deviceUrl, // url = base url + request url
+  // withCredentials: true, // send cookies when cross-domain requests
+  timeout: 5000, // request timeout
+  paramsSerializer: params => qs.stringify(params, { arryFormat: 'repeat' })
+})
+
+// request interceptor
+service.interceptors.request.use(
+  config => {
+    // do something before request is sent
+    config.headers['Content-type'] = 'application/x-www-form-urlencoded;charset=utf-8'
+    // 如果是put/post请求,用qs.stringify序列化参数
+    const is_put_post = config.method === 'put' || config.method === 'post' || config.method === 'delete'
+    const is_json = config.headers['Content-Type'] === 'application/json'
+    if (is_put_post && is_json) {
+      config.data = JSON.stringify(config.data)
+    }
+    if (is_put_post && !is_json) {
+      config.data = qs.stringify(config.data, { arrayFormat: 'repeat' })
+    }
+
+    if (store.getters.token) {
+      // let each request carry token
+      // ['X-Token'] is a custom headers key
+      // please modify it according to the actual situation
+      // config.headers['X-Token'] = getToken()
+    }
+    return config
+  },
+  error => {
+    // do something with request error
+    console.log(error) // for debug
+    return Promise.reject(error)
+  }
+)
+
+// response interceptor
+service.interceptors.response.use(
+  /**
+   * If you want to get http information such as headers or status
+   * Please return  response => response
+  */
+
+  /**
+   * Determine the request status by custom code
+   * Here is just an example
+   * You can also judge the status by HTTP Status Code
+   */
+  response => {
+    return response.data
+    // if the custom code is not 20000, it is judged as an error.
+    // if (res.code !== 20000) {
+    //   Message({
+    //     message: res.message || 'Error',
+    //     type: 'error',
+    //     duration: 5 * 1000
+    //   })
+    //
+    //   // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
+    //   if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
+    //     // to re-login
+    //     MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
+    //       confirmButtonText: 'Re-Login',
+    //       cancelButtonText: 'Cancel',
+    //       type: 'warning'
+    //     }).then(() => {
+    //       store.dispatch('user/resetToken').then(() => {
+    //         location.reload()
+    //       })
+    //     })
+    //   }
+    //   return Promise.reject(new Error(res.message || 'Error'))
+    // } else {
+    //   return res
+    // }
+  },
+  error => {
+    const error_response = error.response || {}
+    const error_data = error_response.data || {}
+      this.$Message.info(error_data.message)
+    // Message({
+    //   message: error_data.message,
+    //   type: 'error',
+    //   duration: 5 * 1000
+    // })
+    // return Promise.reject(error_data)
+  }
+)
+
+export default service

+ 20 - 0
src/utils/validate.js

@@ -0,0 +1,20 @@
+/**
+ * Created by PanJiaChen on 16/11/18.
+ */
+
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export function isExternal(path) {
+  return /^(https?:|mailto:|tel:)/.test(path)
+}
+
+/**
+ * @param {string} str
+ * @returns {Boolean}
+ */
+export function validUsername(str) {
+  const valid_map = ['admin', 'editor']
+  return valid_map.indexOf(str.trim()) >= 0
+}

+ 572 - 0
src/views/Index.vue

@@ -0,0 +1,572 @@
+<template>
+
+    <Layout v-if="hasRegister">
+        <Header class="layout-header text-l" style="padding: 0 10px">
+            <Row>
+                <Col span="8">
+                    <div class="title text-shadow text-white text-left ">{{ serverTitle }}</div>
+                </Col>
+                <Col span="8">
+                    <div class="content_title text-center" data-aos="zoom-in-up">输液监控</div>
+                </Col>
+                <Col span="8">
+                    <div v-if="showDate" class="time text-right">{{ currentTime }}</div>
+                </Col>
+            </Row>
+
+        </Header>
+        <Content :style="{'height':mainAreaHeight+'px'}" style="overflow: hidden">
+            <Grid :col="8" padding="4px" :border="false">
+                <GridItem v-for="(item,index1) in bedInfoArray[screen]" :key="index1">
+                    <Card :padding="8" :class="item.alarm_status?'arrow_box':''">
+                        <Row>
+                            <Col :span="14">
+                                <div class=" bed-num text-s" :class="item.id!==null?'line-black':'line-gray'"><span
+                                        class="iconfont padding-right-xxs">&#xe644;</span>{{ item.frame_bed_full_name }}
+                                </div>
+                            </Col>
+                            <Col :span="10">
+                                <template v-for="(op,idx) in item.list" :key="idx">
+                                    <div v-if="op.bool_basic" class="line-white nurse_option_item text-xs text-center"
+                                         :style="{ backgroundColor:'#'+op.nurse_color_rbg }">
+                                        {{ op.nurse_option_name }}
+                                    </div>
+                                </template>
+                            </Col>
+                        </Row>
+
+                        <div class="patient-info">
+                            <Row class="text-s margin-top-xs name-line">
+                                <Col :span="12" :class="item.sex===1?'line-blue':'line-pink'">
+                                    {{ item.named }}
+                                </Col>
+                                <Col :span="6" :class="item.sex===1?'line-blue':'line-pink'">
+                                    {{ item.age }}{{ ageUnitFilter(item.age_unit)}}
+                                </Col>
+                                <Col :span="6" :style="{textIndent:item.id==null?'2000px':'0px'}"
+                                     class="line-grey text-right">
+                                    {{ item.sex===1?'男':'女' }}
+                                </Col>
+                            </Row>
+                            <Row class=" line-grey ">
+                                <Col :span="24" class="text-left"><span>住院号:</span>{{ item.card_no }}</Col>
+                            </Row>
+                            <Row class=" line-grey">
+                                <Col :span="24" class="text-left"><span>入院日期:</span>{{ unixToDate(item.in_date) }}</Col>
+                            </Row>
+                            <!--                        <Row class="margin-top-xs line-grey">-->
+                            <!--                            <Col :span="10">医生:</Col>-->
+                            <!--                            <Col :span="14">{{ item.doctor_name }}</Col>-->
+                            <!--                        </Row>-->
+
+                        </div>
+                        <Divider plain style="margin: 0;"></Divider>
+                        <Row>
+                            <Col :span="15">
+                                <Row class=" line-grey">
+                                    <Col :span="24"><span>状态:</span><span :class="'infusion-state'+item.infusion_status">{{infusionStatusFormat(item.infusion_status)}}</span>
+                                    </Col>
+                                </Row>
+                                <Row class=" line-grey">
+                                    <Col :span="24"><span>响铃:</span><span :class="item.ring_state?'ring-on':'ring-off'">{{item.ring_state===undefined?'':item.ring_state?'打开':'静音'}}</span>
+                                    </Col>
+                                </Row>
+                                <Row class=" line-grey">
+                                    <Col :span="24"><span>阈锁:</span><span :class="item.lock_status?'lock-off':'lock-on'">{{item.lock_status===undefined?'':item.lock_status?'关闭':'打开'}}</span>
+                                    </Col>
+                                </Row>
+                                <Row class=" line-grey">
+                                    <Col :span="24" class="flex"><span>电量:</span>
+                                        <div class="battery " :class="'battery'+item.battery"></div>
+                                    </Col>
+
+                                </Row>
+                            </Col>
+                            <Col :span="9">
+                                <div class="infustion "
+                                     :class="item.alarm_status===undefined?'':item.alarm_status?'infustion-alarm':item.infusion_status===1?'infustion-pause':'infustion-normal'">
+
+                                </div>
+                            </Col>
+                        </Row>
+                    </Card>
+
+                </GridItem>
+            </Grid>
+        </Content>
+
+                <Footer class="layout-footer text-sm"> <Row>
+                    <Col span="8">
+                        <div class=" "><span class="margin-right-lg">科室床位使用:{{ partInfoSummary.part_bed_used }}/{{ partInfoSummary.part_bed_sum }}(总计)</span>
+
+                        </div>
+
+                    </Col>
+                    <Col span="8">
+                        <div class="text-center">
+                            <span>输液监控床位:{{beds.length}}/{{ partInfoSummary.part_bed_used }}(入住)</span>
+                        </div>
+
+                    </Col>
+                    <Col span="8">
+                        <div  class=" text-right">当前显示:第{{screen*24+1}}-{{screen*24+  (bedInfoArray[screen]!==undefined?bedInfoArray[screen].length:0 )}}/{{beds.length}}床</div>
+                    </Col>
+                </Row></Footer>
+    </Layout>
+    <Layout v-else style="height: 100vh">
+        <Content style="height: 100%">
+            <div class="bullshit text-center ">
+                <div class="bullshit__oops text-lg text-cyan margin-top-lg">这是一台新的输液看板设备,请在呼叫中心管理后台设置该设备的所属科室。</div>
+                <div class="bullshit__info text-lg text-cyan margin-top-lg">
+                    该设备MAC地址为:<span class="text-red">{{ deviceMAC }}</span> ,ID号为:<span
+                        class="text-red">{{ deviceId }}</span>
+                </div>
+                <div class="bullshit__headline"/>
+                <div class="bullshit__info text-lg text-cyan margin-top-lg">如有疑问,请联系管理员,谢谢!</div>
+            </div>
+        </Content>
+    </Layout>
+</template>
+
+<script>
+    import {reactive,watch} from 'vue'
+    import moment from 'moment'
+    import * as API_Board from '@/api/board'
+
+    const deviceUrl = domain.deviceUrl
+    const showDate = domain.showDate
+    export default {
+        name: "Index",
+        data() {
+            return {
+                serverTitle: '',
+                currentTime: '',
+                showDate: true,
+                beds: reactive([]),
+                bedInfoArray: reactive([]),
+                baseNurseConfig: [],
+                partSetting: {},
+                partInfoSummary:{},
+                screen:0,
+                countTimes:0,
+                staySeconds:5,
+                websock: null,
+                boardData: {},
+                deviceMAC: this.$route.query.mac,
+                hasRegister: false,
+                deviceId: '',
+                itemCountPerScreen: 20
+            }
+        },
+        computed: {
+            mainAreaHeight() {
+                return window.devicePixelRatio > 1 ? Number(document.documentElement.clientHeight)-114 : Number(document.documentElement.clientHeight) - 114
+            }
+        },
+        async beforeMount() {
+            if (this.$route.query.mac !== undefined && this.$route.query.mac !== '') {
+                // 保存mac地址
+                this.$store.dispatch('user/setDeviceMAC', this.$route.query.mac)
+                // 初始化websocket
+                await this.initWebSocket()
+            }
+        },
+        mounted() {
+            this.$Notice.config({
+                top: 64,
+                duration: 0
+            })
+            moment.locale()
+            this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss dddd')
+            const _this = this
+            // 每分钟检查一次websocket状态,如果掉线,则重连
+            setInterval(function () {
+                if (_this.websock === null || _this.websock.readyState !== 1) {
+                    _this.initWebSocket()
+                }
+            }, 60000)
+
+            setInterval(function () {
+                _this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss dddd')
+                _this.swiperChange()
+            }, 1000)
+            console.log('widown', window.devicePixelRatio)
+            watch(()=>[...this.beds],(n,o)=>{
+                // if(n.length!==o.length){
+                const screens = Math.ceil(n.length / this.itemCountPerScreen)
+                this.bedInfoArray = []
+                for (var i = 1; i <= screens; i++) {
+                    this.bedInfoArray.push(n.slice((i - 1) * this.itemCountPerScreen, i * this.itemCountPerScreen >= n.length
+                        ? n.length : i * this.itemCountPerScreen))
+                }
+                // }
+            })
+        },
+        methods: {
+            getBedInfo() {
+                API_Board.getBedInfo(this.$store.getters.partid).then(res => {
+                    console.log('res', res)
+                    // this.beds = [...res.items].slice(0, 24)
+                    this.beds = [...res['items']]
+
+
+                    this.serverTitle = res.part_name
+                })
+            },
+
+            swiperChange() {
+
+
+                    if (this.countTimes > this.staySeconds) {
+                        // console.log('bbb', this.bedInfoArray.length)
+                        this.countTimes = 0
+                        this.screen++
+                        if(this.screen===this.bedInfoArray.length||this.bedInfoArray===0){
+                            this.screen=0
+                        }
+                    }
+                // console.log('aaa',this.screen)
+                    this.countTimes = this.countTimes + 1
+                // console.log('count',this.countTimes)
+
+            },
+
+            getPartInfoSummary() {
+                API_Board.getpartinfosummary(this.$store.getters.partid).then(res => {
+                    this.partInfoSummary = { ...res }
+                })
+            },
+
+            ageUnitFilter(val) {
+
+                if (val === 'Y' || val === '岁') {
+                    return '岁'
+                } else if (val === 'M') {
+                    return '月'
+                } else if (val === 'D') {
+                    return '天'
+                } else {
+                    return val
+                }
+            },
+            unixToDate(unix) {
+                // console.log('unix', unix)
+                if (!unix) return unix
+                let _format = 'yy-MM-dd'
+                const d = new Date(unix * 1000)
+                const o = {
+                    'M+': d.getMonth() + 1,
+                    'd+': d.getDate(),
+                    'h+': d.getHours(),
+                    'm+': d.getMinutes(),
+                    's+': d.getSeconds(),
+                    'q+': Math.floor((d.getMonth() + 3) / 3),
+                    S: d.getMilliseconds()
+                }
+                if (/(y+)/.test(_format)) _format = _format.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length))
+                for (const k in o) if (new RegExp('(' + k + ')').test(_format)) _format = _format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+                return _format
+            },
+            // 获取基础护理分类
+            getBasicNursecfg() {
+                API_Board.getBasicNursecfg(this.$store.getters.partid).then(res => {//
+                    this.baseNurseConfig = [...res]
+                    console.log('数据加载完成1!')
+                })
+            },
+            // 获取科室设置
+            getPartSetting() {
+                API_Board.getPartSetting(this.$store.getters.partid).then(res => {
+                    this.partSetting = {...res}
+
+                })
+            },
+            initWebSocket: function () {
+                var stockbase = deviceUrl.replace('http', 'ws')
+                // console.log('store', this.$store)
+                this.websock = new WebSocket(stockbase + '/boardinfo/1/' + this.$store.getters.token)
+                this.websock.onopen = this.websocketonopen
+                this.websock.onerror = this.websocketonerror
+                this.websock.onmessage = this.websocketonmessage
+                this.websock.onclose = this.websocketclose
+            },
+            websocketonopen: function () {
+                console.log('WebSocket连接成功')
+            },
+            websocketonerror: function (e) {
+                console.log('WebSocket连接发生错误')
+            },
+            websocketonmessage: function (e) {
+                const boardObj = JSON.parse(e.data)
+                if (boardObj.partId !== undefined && boardObj.partId > 0) { // 返回了partid,说明设备已注册
+                    if (boardObj.code !== undefined) {
+                        // 把科室Id设置到store
+                        this.hasRegister = true
+                        this.$store.dispatch('user/setPartId', boardObj.partId)
+                        this.deviceId = boardObj.id
+                        this.getPartSetting()
+                        this.getBedInfo()
+                        this.getPartInfoSummary()
+                        if (boardObj.config !== null && boardObj.config !== '') {
+                            const boardconfig = JSON.parse(boardObj.config)
+                            this.staySeconds = boardconfig.staySeconds[0] // 轮换时间间隔
+                            if ('itemCountPerScreen' in boardconfig) {
+                                this.itemCountPerScreen = boardconfig.itemCountPerScreen
+                            }
+                            // 是否固定屏幕
+                            // this.fixedScreen = boardconfig.stayIndex !== 0
+                            // if (this.fixedScreen) {
+                            //     this.stayIndex = boardconfig.stayIndex
+                            // } else {
+                            //     // 不是固定屏幕定时任务中计算 stayIndex 的值,这里判断 this.stayIndex === 0 说明还没有初始化。把this.stayIndex 设置为第一屏
+                            //     if (this.stayIndex === 0) {
+                            //         this.stayIndex = 1
+                            //     }
+                            // }
+                            if (Object.keys(this.boardData).length === 0) {
+                                const _this = this
+                                setTimeout(function () {
+                                    _this.$forceUpdate()
+                                }, 2000)
+                            }
+                            // console.log('stayIndex', this.stayIndex)
+                        } else {
+                            this.autoplay = true
+                        }
+                    }
+                } else if ('hidden_call' in boardObj) {
+                    this.showCalling = false
+                } else if ('action' in boardObj) {
+
+                    const {alarm_status, battery, device_mac, duration, infusion_status, lock_status, ring_state} = boardObj
+                    if (boardObj.action === 'refresh') { //刷新报警器状态
+                        this.beds.forEach((item, index) => {
+                            if (item.device_mac === device_mac) {
+                                item = {...item, alarm_status, battery, duration, infusion_status, lock_status, ring_state}
+                                this.beds[index] = item
+
+                                if (alarm_status) { //报警提示
+                                    this.$Notice.close(device_mac)
+                                    this.$Notice.warning({
+                                        title:'告警提示',
+                                        name: device_mac,
+                                        desc: item.frame_bed_full_name + '输液完成',
+                                        render:h=>{
+                                            return h('span',{class:'warming'},(item.frame_bed_full_name?item.frame_bed_full_name:device_mac) + '输液完成')
+                                        },
+                                        duration: 0
+                                    })
+                                } else if(infusion_status===1){ //输液暂停
+                                    this.$Notice.close(device_mac)
+                                    this.$Notice.warning({
+                                        title:'告警提示',
+                                        name: device_mac,
+                                        desc: item.frame_bed_full_name + '暂停输液',
+                                        render:h=>{
+                                            return h('span',{class:'warming'},(item.frame_bed_full_name?item.frame_bed_full_name:device_mac) + '暂停输液')
+                                        },
+                                        duration: 0
+                                    })
+                                } else{
+                                    this.$Notice.close(device_mac)
+                                }
+                            }
+                        })
+
+                    } else if(boardObj.action === 'up') { //上屏
+
+                            console.log('up',boardObj)
+                            this.beds=[...this.beds,boardObj]
+
+
+                    } else if (boardObj.action === 'down') { //下屏
+                        this.beds.forEach((item, index) => {
+                            if (item.device_mac === device_mac) {
+                                this.beds.splice(index,1)
+                            }
+                        })
+
+                    }
+
+                    // console.log('beds', this.beds)
+
+
+                } else {
+                    this.deviceId = boardObj.id
+                    this.hasRegister = false
+                }
+            },
+            websocketclose: function (e) {
+                console.log('connection closed (' + e.code + ')')
+            },
+            infusionStatusFormat(state) {
+                if (state === undefined) {
+                    return ''
+                } else if (state === 0) {
+                    return '监测'
+                } else if (state === 1) {
+                    return '暂停'
+                } else if (state === 2) {
+                    return '完成'
+                } else if (state === 3) {
+                    return '结束'
+                }
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .layout-header,
+    .layout-footer {
+        background: #7CBCE9;
+        color: white;
+    }
+
+    .battery {
+        width: 45%;
+        display: inline-block;
+        height: 20px;
+        background-position: left center;
+        background-repeat: no-repeat;
+        background-size: 40px;
+    }
+
+    .vertical-center-modal {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+
+    .vertical-center-modal .ivu-modal {
+        top: 0;
+    }
+
+    .battery1, .battery0 {
+        background-image: url(~@/assets/battery1.png);
+    }
+
+    .battery2 {
+        background-image: url(~@/assets/battery2.png);
+    }
+
+    .battery3 {
+        background-image: url(~@/assets/battery3.png);
+    }
+
+    .battery4 {
+        background-image: url(~@/assets/battery4.png);
+    }
+
+    .battery5, .battery6 {
+        background-image: url(~@/assets/battery5.png);
+    }
+
+    .infustion {
+        width: 100%;
+        height: 100%;
+        background-position: right center;
+        background-repeat: no-repeat;
+        background-size: 60px;
+    }
+
+    .infustion-normal {
+        background-image: url(~@/assets/infusion_normal.png);
+    }
+
+    .infustion-alarm {
+        background-image: url(~@/assets/infusion_alarm.png);
+    }
+
+    .infustion-pause {
+        background-image: url(~@/assets/infusion_pause.png);
+    }
+
+    .infusion-state0, .ring-on, .lock-on {
+        color: green;
+    }
+
+    .infusion-state1 {
+        color: #e98c1e;
+    }
+
+    .infusion-state2, .infusion-state3, .ring-off, .lock-off {
+        color: red;
+    }
+
+    .nurse_option_item, .bed-num {
+        height: 20px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+    }
+
+    .arrow_box {
+        animation: glow 1000ms ease-out infinite alternate;
+    }
+    .ivu-divider{
+        background-color: #bbb;
+    }
+
+    .ivu-card-bordered{
+        border-color: #bbb;
+    }
+    @keyframes glow {
+        0% {
+            border-color: #e8eaec;
+            /*/box-shadow: 0 0 5px rgba(0,255,0,.2), inset 0 0 5px rgba(0,255,0,.1), 0 1px 0 #393;*/
+        }
+        50% {
+            border-color: #ed1c24;
+            /*/box-shadow: 0 0 5px rgba(0,255,0,.2), inset 0 0 5px rgba(0,255,0,.1), 0 1px 0 #393;*/
+        }
+        100% {
+            border-color: #f00;
+            /*/box-shadow: 0 0 20px rgba(0,255,0,.6), inset 0 0 10px rgba(0,255,0,.4), 0 1px 0 #6f6;*/
+        }
+    }
+    .ivu-layout-footer{
+        padding: 0 10px;
+        height: 50px;
+        line-height: 50px;
+    }
+
+    /*@media only screen and (-webkit-min-device-pixel-ratio: 1.2) {*/
+    /*    .ivu-layout-header {*/
+    /*        height: 32px;*/
+    /*        line-height: 32px;*/
+    /*    }*/
+    /*    .ivu-card{*/
+    /*        font-size: 11px;*/
+    /*    }*/
+    /*    .infustion {*/
+    /*       background-size: 45px;*/
+    /*    }*/
+    /*    .battery {*/
+    /*       height: 15px;*/
+    /*        background-size: 20px;*/
+    /*    }*/
+    /*    .nurse_option_item{*/
+    /*        height: 15px;*/
+    /*        line-height: 15px;*/
+    /*    }*/
+    /*}*/
+
+
+</style>
+<style lang="less">
+    .vertical-center-modal {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+
+    .vertical-center-modal .ivu-modal {
+        top: 0;
+    }
+    .warming{
+        font-size: 16px;
+        color: #ed1c24;
+    }
+
+</style>

+ 45 - 0
vue.config.js

@@ -0,0 +1,45 @@
+const { defineConfig } = require('@vue/cli-service')
+const path = require('path')
+
+const { NODE_ENV, VUE_APP_TITLE = '' } = process.env
+
+const config = {
+  transpileDependencies: false,
+  productionSourceMap: NODE_ENV === 'production',
+  chainWebpack: (setting) => {
+    setting.plugin('html').tap((args) => {
+      args[0].APP_TITLE = VUE_APP_TITLE
+      return args
+    })
+  },
+  css: {
+    loaderOptions: {
+      less: {
+        lessOptions: {
+          javascriptEnabled: true,
+        }
+      }
+    }
+  },
+  pluginOptions: {
+    'style-resources-loader': {
+      preProcessor: 'less',
+      patterns: [
+        path.resolve(__dirname, 'src/styles/variable.less')
+      ]
+    }
+  },
+  devServer: {
+    open: true,
+    port: 8080,
+    host: '192.168.1.198',
+    proxy: {
+      '/api': {
+        target: 'https://localhost:8080',
+        changeOrigin: true
+      }
+    }
+  }
+}
+
+module.exports = defineConfig(config)