guhongwei 2 år sedan
förälder
incheckning
c9fef0fdc3

+ 6 - 0
.env.development

@@ -0,0 +1,6 @@
+VITE_BASE_URL = "/publicbasic"
+VITE_OUT_DIR = "publicbasic"
+VITE_REQUEST_BASE = ''
+VITE_APP_HOST="http://basic.waityou24.cn"
+VITE_APP_PAGE_SIZE=10
+VITE_APP_ROUTER="basic"

+ 6 - 0
.env.production

@@ -0,0 +1,6 @@
+VITE_BASE_URL = "/publicbasic"
+VITE_OUT_DIR = "publicbasic"
+VITE_REQUEST_BASE = ''
+VITE_APP_HOST="http://basic.waityou24.cn"
+VITE_APP_PAGE_SIZE=10
+VITE_APP_ROUTER="basic"

+ 11 - 0
components.d.ts

@@ -9,11 +9,22 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
+    CPage: typeof import('./src/components/common/c-page.vue')['default']
+    CPopup: typeof import('./src/components/common/c-popup.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     VanButton: typeof import('vant/es')['Button']
+    VanCellGroup: typeof import('vant/es')['CellGroup']
     VanCol: typeof import('vant/es')['Col']
+    VanField: typeof import('vant/es')['Field']
+    VanForm: typeof import('vant/es')['Form']
     VanIcon: typeof import('vant/es')['Icon']
+    VanImage: typeof import('vant/es')['Image']
+    VanPagination: typeof import('vant/es')['Pagination']
+    VanPopup: typeof import('vant/es')['Popup']
+    VanRadio: typeof import('vant/es')['Radio']
+    VanRadioGroup: typeof import('vant/es')['RadioGroup']
     VanRow: typeof import('vant/es')['Row']
+    VanSearch: typeof import('vant/es')['Search']
   }
 }

BIN
src/assets/logo.png


+ 27 - 0
src/components/common/c-page.vue

@@ -0,0 +1,27 @@
+<template>
+  <div id="c-page">
+    <van-pagination v-model="currentPage" :page-count="total" mode="simple" @change="toCPage" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { toRefs } from 'vue';
+import type { Ref } from 'vue';
+import { ref, getCurrentInstance } from 'vue';
+// 基本设置
+const { proxy } = getCurrentInstance() as any;
+
+const props = defineProps({
+  total: { type: Number, default: 0 }
+});
+const currentPage: Ref<any> = ref(1);
+let limit: number = proxy.$limit;
+
+const emit = defineEmits(['search']);
+const toCPage = (page) => {
+  emit('search', { skip: (page - 1) * limit, limit: limit });
+};
+
+const { total } = toRefs(props);
+</script>
+<style scoped lang="scss"></style>

+ 29 - 0
src/components/common/c-popup.vue

@@ -0,0 +1,29 @@
+<template>
+  <div id="c-popup">
+    <van-popup v-model:show="popupInfo.show" :position="popupInfo.position || 'right'" round :style="{ width: '80%', height: '80%' }">
+      <van-col span="24" class="popup">
+        <van-col span="24" class="title">{{ popupInfo.title || '遮罩层' }}</van-col>
+        <van-col span="24" class="info"><slot name="info"></slot></van-col>
+      </van-col>
+    </van-popup>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { toRefs } from 'vue';
+// import type { Ref } from 'vue';
+// import { ref } from 'vue';
+
+const props = defineProps({
+  popupInfo: { type: Object, default: () => {} }
+});
+const { popupInfo } = toRefs(props);
+</script>
+<style scoped lang="scss">
+.popup {
+  padding: 10px;
+  .title {
+    font-weight: bold;
+  }
+}
+</style>

+ 4 - 1
src/components/index.ts

@@ -7,9 +7,12 @@ import cTable from '@common/src/components/frame/c-table.vue';
 import cUpload from '@common/src/components/frame/c-upload.vue';
 import cEditor from '@common/src/components/frame/wang-editor.vue';
 import cFile from '@common/src/components/frame/c-file.vue';
+// 本项目
+import cPage from '@/components/common/c-page.vue';
+import cPopup from '@/components/common/c-popup.vue';
 
 const components: {
   [propName: string]: Component;
-} = { cButton, cDialog, cSearch, cForm, cTable, cUpload, cEditor, cFile };
+} = { cButton, cDialog, cSearch, cForm, cTable, cUpload, cEditor, cFile, cPage, cPopup };
 
 export default components;

+ 3 - 0
src/main.ts

@@ -9,6 +9,9 @@ import 'animate.css';
 // vant
 import 'vant/lib/index.css';
 
+// 图标
+import '@common/src/assets/icon/iconfont.css';
+
 // moment
 import moment from 'moment';
 // lodash

+ 40 - 10
src/router/index.ts

@@ -1,20 +1,50 @@
 import { createRouter, createWebHistory } from 'vue-router';
-
+import store from '@/stores/counter';
+import axios from 'axios';
+// 管理员
+import admin from './module/admin';
+import account from './module/account';
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
     {
       path: '/',
-      meta: { title: '基础动态管理平台' },
-      component: () => import('@/views/homeIndex.vue')
-      // component: () => import('@/views/loginIndex.vue')
-    }
+      meta: { title: '省重点实验室' },
+      component: () => import('@/views/home/index.vue')
+    },
+    ...admin,
+    ...account
   ]
 });
-router.beforeEach((to, from, next) => {
-  // 赋值标题
-  document.title = `${to.meta.title}`;
-  // 向下进行
-  next();
+router.beforeEach(async (to, from, next) => {
+  document.title = `${to.meta.title} `;
+  const token = localStorage.getItem('token');
+  if (token) {
+    const res = await axios.request({
+      method: 'get',
+      url: '/jcyjdtglpt/v1/api/token/tokenView',
+      responseType: 'json',
+      headers: {
+        token: token
+      }
+    });
+    if (res.data.errcode == '0') {
+      store.commit('setUser', res.data.data, { root: true });
+    }
+    const arr = await axios.request({
+      method: 'get',
+      url: '/freeLabel/v2/api/activitys/isInAct',
+      responseType: 'json',
+      headers: {
+        token: token
+      }
+    });
+    if (arr.data.errcode == '0') {
+      store.commit('setIsInAct', arr.data.data, { root: true });
+    }
+    next();
+  } else {
+    window.alert('无登录信息,无法打开');
+  }
 });
 export default router;

+ 1 - 0
src/router/module/account.ts

@@ -0,0 +1 @@
+export default [];

+ 8 - 0
src/router/module/admin.ts

@@ -0,0 +1,8 @@
+export default [
+  // 实验室管理
+  {
+    path: '/laboratory/info',
+    meta: { title: '实验室管理-信息列表' },
+    component: () => import('@/views/laboratory/info/index.vue')
+  }
+];

+ 20 - 11
src/stores/counter.ts

@@ -1,12 +1,21 @@
-import { ref, computed } from 'vue';
-import { defineStore } from 'pinia';
-
-export const useCounterStore = defineStore('counter', () => {
-  const count = ref(0);
-  const doubleCount = computed(() => count.value * 2);
-  function increment() {
-    count.value++;
-  }
-
-  return { count, doubleCount, increment };
+import * as ustate from '@common/src/stores/user/state';
+import * as umutations from '@common/src/stores/user/mutations';
+import { createStore } from 'vuex';
+const store = createStore({
+  state: { ...ustate, isInAct: true },
+  getters: {
+    lab_id(state) {
+      let lab_id = undefined;
+      if (state.user.role_type && state.user.role_type === '2') {
+        lab_id = state.user.lab_id;
+      }
+      return lab_id;
+    }
+  },
+  mutations: {
+    ...umutations
+  },
+  actions: {},
+  modules: {}
 });
+export default store;

+ 1 - 1
src/views/homeIndex.vue

@@ -1,5 +1,5 @@
 <template>
-  <div id="homeIndex">
+  <div id="index">
     <van-row>
       <van-col span="24" class="main">
         <van-col span="12"><van-icon name="cart-o" color="#1989fa" /></van-col>

+ 214 - 0
src/views/home/index.vue

@@ -0,0 +1,214 @@
+<template>
+  <div id="index">
+    <van-row>
+      <van-col span="24" class="main animate__animated animate__backInRight">
+        <van-col span="24" class="one">
+          <van-image :src="webInfos.logo_url" class="image" fit="fill"></van-image>
+          <van-col span="24" class="one_1">
+            {{ userInfo.name }}
+          </van-col>
+          <van-col span="24" class="one_2">
+            <van-col span="12" class="list" v-for="(i, index) in moduleList" :key="index">
+              <van-button size="small" :class="[active == i.active ? 'active' : '']" @click="modulePath(i)">{{ i.name }}</van-button>
+            </van-col>
+          </van-col>
+        </van-col>
+        <van-col span="24" class="two">
+          <van-col span="24" class="list" v-for="(i, index) in menuList" :key="index">
+            <template v-if="i.type == '0'">
+              <van-col span="24" class="title_two">
+                <span @click="toPath(i)"> <i :class="['iconfont', i.icon]"></i> {{ i.name }}</span>
+              </van-col>
+              <van-col span="24" class="list_two">
+                <van-col span="8" class="title_thr" v-for="(r, indexs) in i.children" :key="indexs">
+                  <span @click="toPath(r)"> <i :class="['iconfont', r.icon]"></i>{{ r.name }}</span>
+                </van-col>
+              </van-col>
+            </template>
+            <template v-else>
+              <van-col span="24" class="title_one">
+                <span @click="toPath(i)"><i :class="['iconfont', i.icon]"></i>{{ i.name }}</span>
+              </van-col>
+            </template>
+          </van-col>
+        </van-col>
+      </van-col>
+    </van-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+// 基础
+import { webInfo, project_module_menus } from '@common/src/layout/site';
+import type { Ref } from 'vue';
+import { onMounted, ref, watch } from 'vue';
+import store from '@/stores/counter';
+import { useRouter } from 'vue-router';
+import { showToast } from 'vant';
+
+// 接口
+import { ModuleStore } from '@common/src/stores/admin/module'; //模块
+import { RoleStore } from '@common/src/stores/admin/role'; //模块
+import type { IQueryResult } from '@/util/types.util'; //
+const module = ModuleStore();
+const role = RoleStore();
+
+// 路由
+const router = useRouter();
+
+const webInfos: Ref<any> = ref(webInfo);
+// 用户
+const userInfo: Ref<any> = ref({});
+// 模块
+const moduleList: Ref<any> = ref(project_module_menus);
+// 选中
+const active: Ref<any> = ref(1);
+// 菜单
+const menuList: Ref<any> = ref([]);
+// 请求
+onMounted(async () => {
+  // 查询用户信息
+  await searchUser();
+});
+const searchUser = () => {
+  let user = store.state.user;
+  user.name = user.name || user.nick_name;
+  userInfo.value = user;
+  // 查询菜单
+};
+// 模块点击
+const modulePath = (e: any) => {
+  window.location.href = `${e.path}`;
+};
+// 跳转
+const toPath = (e) => {
+  if (e && e.path) {
+    router.push({ path: e.path });
+  }
+};
+const getMenu = async () => {
+  let name = import.meta.env.VITE_APP_ROUTER;
+  const res: IQueryResult = await module.query({ name: name, is_use: 'Y' });
+  if (res && res.errcode == '0') {
+    if (res.total > 0) {
+      let moduleInfo = res.data[0];
+      let roleInfo = await role.um();
+      if (roleInfo.errcode == '0') {
+        if (roleInfo.data) {
+          let menus = roleInfo.data[moduleInfo._id];
+          if (menus && menus.length > 0) {
+            menuList.value = menus;
+          }
+        }
+      }
+    }
+  } else {
+    showToast({ message: `暂无用户信息,无法获取菜单信息`, type: 'error' });
+  }
+};
+watch(
+  userInfo,
+  (newVal) => {
+    if (newVal && newVal._id) {
+      getMenu();
+    } else showToast({ message: `暂无用户信息,无法获取菜单信息`, type: 'error' });
+  },
+  {
+    deep: true
+  }
+);
+</script>
+<style scoped lang="scss">
+.main {
+  overflow: hidden;
+  .one {
+    background-color: #409eff;
+    height: 33vh;
+    overflow: hidden;
+    text-align: center;
+    .image {
+      width: 66px;
+      height: 66px;
+      overflow: hidden;
+      border-radius: 90px;
+      border: 1px solid #f1f1f1;
+      margin: 20px 0 10px 0;
+    }
+    .one_1 {
+      text-align: center;
+      margin: 0 0 10px 0;
+      font-size: 16px;
+    }
+    .one_2 {
+      display: flex;
+      flex-wrap: wrap;
+      padding: 0 10px;
+      .list {
+        margin: 0 0 10px 0;
+        padding: 0 10px;
+      }
+      .van-button {
+        width: 100%;
+      }
+      .active {
+        color: #ffffff;
+        background-color: #07c4a8;
+      }
+    }
+  }
+  .two {
+    height: 67vh;
+    overflow-y: auto;
+    padding: 10px;
+    .list {
+      margin: 0 0 10px 0;
+      .title_one {
+        span {
+          display: inline-block;
+          padding: 8px;
+          background-color: #ff0000;
+          color: #fff;
+          border-radius: 5px;
+          font-size: 15px;
+          i {
+            margin: 0 5px 0 0;
+          }
+        }
+      }
+      .title_two {
+        margin: 0 0 10px 0;
+        span {
+          display: inline-block;
+          padding: 8px;
+          background-color: #0000ff;
+          color: #fff;
+          border-radius: 5px;
+          font-size: 14px;
+          i {
+            margin: 0 5px 0 0;
+          }
+        }
+      }
+      .list_two {
+        display: flex;
+        flex-wrap: wrap;
+        .title_thr {
+          margin: 0 0 10px 0;
+          text-align: center;
+          span {
+            display: inline-block;
+            padding: 8px;
+            background-color: #07c4a8;
+            color: #fff;
+            border-radius: 5px;
+            font-size: 13px;
+            i {
+              margin: 0 5px 0 0;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 214 - 0
src/views/laboratory/info/index.vue

@@ -0,0 +1,214 @@
+<template>
+  <div id="index">
+    <van-row>
+      <van-col span="24" class="main animate__animated animate__backInRight">
+        <van-col span="24" class="one">
+          <van-col span="20" class="one_1">
+            <van-field v-model="searchForm.name" placeholder="请输入实验室名称" @blur="toInput" />
+          </van-col>
+          <van-col span="4" class="one_2">
+            <van-button size="small" @click="toChange">筛选</van-button>
+          </van-col>
+        </van-col>
+        <van-col span="24" class="two">
+          <van-col span="24" class="list" v-for="i in list" :key="i._id" @click="toView(i)">
+            <van-col span="24" class="name">
+              {{ i.name }}
+            </van-col>
+            <van-col span="24" class="other">
+              <van-col span="24" class="other_1">
+                <span>实验室主任:</span>
+                <span>{{ i.chief_name || '暂无' }}</span>
+              </van-col>
+              <van-col span="24" class="other_1">
+                <span>实验室联系人:</span>
+                <span>{{ i.lab_person || '暂无' }}</span>
+              </van-col>
+              <van-col span="24" class="other_1">
+                <span>实验室联系电话:</span>
+                <span>{{ i.lab_phone || '暂无' }}</span>
+              </van-col>
+            </van-col>
+          </van-col>
+        </van-col>
+        <van-col span="24" class="thr">
+          <cPage :total="total" @search="search">
+            <template v-slot:info>
+              <van-col span="24">第一部分</van-col>
+            </template>
+          </cPage>
+        </van-col>
+      </van-col>
+    </van-row>
+    <cPopup :popupInfo="popupInfo">
+      <template v-slot:info>
+        <van-col span="24" class="popup_one" v-if="popupInfo.type == '1'">
+          <van-form @submit="btSearch" label-width="6em">
+            <van-cell-group>
+              <van-field v-model="searchForm.chief_name" name="chief_name" label="实验室主任" placeholder="实验室主任" />
+              <van-field v-model="searchForm.lab_person" name="lab_person" label="实验室联系人" placeholder="实验室联系人" />
+              <van-field name="status" label="状态">
+                <template #input>
+                  <van-radio-group v-model="searchForm.status" direction="horizontal">
+                    <van-radio v-for="i in statusList" :key="i.dict_value" :name="i.dict_value">{{ i.dict_label }}</van-radio>
+                  </van-radio-group>
+                </template>
+              </van-field>
+            </van-cell-group>
+            <div class="btn">
+              <van-button size="small" type="primary" native-type="submit"> 提交查询 </van-button>
+              <van-button size="small" type="danger" @click="toReset()"> 重置查询 </van-button>
+            </div>
+          </van-form>
+        </van-col>
+      </template>
+    </cPopup>
+  </div>
+</template>
+
+<script setup lang="ts">
+// 基础
+// 基础
+import _ from 'lodash';
+import type { Ref } from 'vue';
+import { ref, getCurrentInstance, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+
+// 接口
+import { BasicStore } from '@common/src/stores/basic/basic';
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const basic = BasicStore();
+const dictAxios = DictDataStore();
+
+// 基本设置
+const { proxy } = getCurrentInstance() as any;
+// 路由
+const router = useRouter();
+
+const list: Ref<any> = ref([]);
+const total: Ref<any> = ref(0);
+const skip = 0;
+const limit: number = proxy.$limit;
+const searchForm: Ref<any> = ref({}); // 查询
+
+// 字典表
+const statusList: Ref<any> = ref([]);
+
+// 弹出层
+const popupInfo: Ref<any> = ref({ show: false, type: '1', title: '筛选' });
+
+// 请求
+onMounted(async () => {
+  // 查询其他信息
+  await searchOther();
+  // 查询列表
+  await search({ skip, limit });
+});
+const search = async (e: { skip: number; limit: number }) => {
+  const condition = _.cloneDeep(searchForm.value);
+  const info = { skip: e.skip, limit: e.limit, ...condition };
+  const res: IQueryResult = await basic.query(info);
+  if (res.errcode == 0) {
+    list.value = res.data as [];
+    total.value = res.total;
+  }
+};
+
+// 名称查询
+const toInput = () => {
+  search({ skip, limit });
+};
+// 筛选
+const toChange = () => {
+  popupInfo.value = { show: true, type: '1', title: '筛选' };
+};
+// 查询
+const btSearch = (query) => {
+  searchForm.value = query;
+  toClose();
+  search({ skip, limit });
+};
+// 重置查询
+const toReset = () => {
+  searchForm.value = {};
+  toClose();
+  search({ skip, limit });
+};
+// 关闭弹框
+const toClose = () => {
+  popupInfo.value = { show: false, type: '1', title: '筛选' };
+};
+const toView = (e) => {
+  router.push({ path: '/laboratory/info/detail', query: { id: e._id } });
+};
+// 查询其他信息
+const searchOther = async () => {
+  let res: IQueryResult;
+  // 状态
+  res = await dictAxios.query({ dict_type: 'label_status' });
+  if (res.errcode == '0') statusList.value = res.data as [];
+};
+</script>
+<style scoped lang="scss">
+.main {
+  height: 100vh;
+  overflow: hidden;
+  .one {
+    display: flex;
+    padding: 10px;
+    height: 9vh;
+    .one_1 {
+      .van-cell {
+        padding: 5px;
+        border: 1px solid#ff0000;
+      }
+    }
+    .one_2 {
+      .van-button {
+        width: 100%;
+        height: 5.4vh;
+      }
+    }
+  }
+  .two {
+    height: 85vh;
+    overflow-y: auto;
+    padding: 0 10px;
+    .list {
+      border: 1px solid #f1f1f1;
+      margin: 0 0 10px 0;
+      padding: 10px;
+      border-radius: 5px;
+      .name {
+        font-size: 16px;
+        margin: 0 0 5px 0;
+        font-family: monospace;
+        font-weight: bold;
+      }
+      .other {
+        .other_1 {
+          font-size: 14px;
+          margin: 0 0 5px 0;
+        }
+      }
+    }
+  }
+  .thr {
+    height: 6vh;
+    overflow: hidden;
+  }
+}
+.popup_one {
+  .van-field {
+    padding: 5px 0;
+  }
+  .btn {
+    text-align: center;
+    margin: 10px 0;
+    .van-button {
+      margin: 0 10px;
+    }
+  }
+}
+</style>

+ 3 - 0
tsconfig.json

@@ -26,6 +26,9 @@
         "../common/*"
       ]
     },
+    "types": [
+      "vite/client"
+    ],
     "isolatedModules": false,
     "suppressImplicitAnyIndexErrors": true,
     "sourceMap": true,

+ 8 - 0
vite.config.ts

@@ -32,7 +32,15 @@ export default defineConfig(({ mode }) => {
           target: 'http://192.168.1.113:13010',
           changeOrigin: true,
           ws: false
+        },
+        '/freeLabel/v2/api': {
+          target: 'http://192.168.1.113:13002', // http://192.168.1.197:13002  //http://192.168.1.197:13202
+          changeOrigin: true,
+          ws: false
         }
+      },
+      fs: {
+        strict: false
       }
     },
     resolve: {