Prechádzať zdrojové kódy

管理员-依托单位,科学家

YY 2 rokov pred
rodič
commit
1c99855946

+ 173 - 0
src/components/c-search.vue

@@ -0,0 +1,173 @@
+<template>
+  <div id="c-search">
+    <el-row>
+      <el-col :span="24" class="title" v-if="is_title">
+        <el-col :span="24" class="title_1">
+          <span>{{ title || $route.meta.title }}</span>
+          <span>{{ tip }}</span>
+        </el-col>
+        <el-col :span="24" class="title_2">
+          <span>{{ remark }}</span>
+        </el-col>
+      </el-col>
+      <el-col :span="24" class="search" v-if="is_search">
+        <el-form ref="form" :model="form" label-width="auto">
+          <el-col :span="24" class="form">
+            <template v-for="(item, index) in fields">
+              <el-col :span="8" class="form_1" :key="'form-field-' + index" v-if="item.isSearch == true">
+                <el-form-item :label="getField('label', item)" :prop="item.model">
+                  <template v-if="!item.custom">
+                    <template v-if="item.type === 'select'">
+                      <el-select v-model="form[item.model]" v-bind="item.options" filterable clearable @change="dataChange(item.model)">
+                        <slot :name="item.model" v-bind="{ item }"></slot>
+                      </el-select>
+                    </template>
+                    <template v-else>
+                      <el-input
+                        v-model="form[item.model]"
+                        :type="getField('type', item)"
+                        :placeholder="getField('place', item)"
+                        clearable
+                        v-bind="item.options"
+                        @change="dataChange(item.model)"
+                      ></el-input>
+                    </template>
+                  </template>
+                  <template v-else>
+                    <slot :name="item.model" v-bind="{ item }"></slot>
+                    <!-- <el-input v-model="form[item.model]" clearable :placeholder="`输入${item.label}`"></el-input> -->
+                  </template>
+                </el-form-item>
+              </el-col>
+            </template>
+          </el-col>
+          <el-col :span="24" class="btn">
+            <el-button type="primary" @click="toSubmit()">查询</el-button>
+            <el-button type="danger" @click="toReset()">重置</el-button>
+          </el-col>
+        </el-form>
+      </el-col>
+      <el-col :span="24" class="back" v-if="is_back">
+        <el-button type="primary" @click="toBack()">返回</el-button>
+        <slot name="custombtn"></slot>
+      </el-col>
+      <el-col :span="24" class="slot">
+        <slot name="isslot"></slot>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, toRefs } from 'vue';
+import type { Ref } from 'vue';
+import _ from 'lodash';
+interface fieldsItem {
+  model: string;
+  type: string;
+  // readonly: string;
+  options: object;
+  custom: string;
+  // required: string;
+  // limit: number | undefined;
+  isSearch: boolean;
+}
+const props = defineProps({
+  is_title: { type: Boolean, default: true },
+  is_search: { type: Boolean, default: false },
+  is_back: { type: Boolean, default: false },
+  fields: { type: Array<fieldsItem> },
+  title: { type: String },
+  tip: { type: String },
+  remark: { type: String },
+});
+const { is_title } = toRefs(props);
+const { is_search } = toRefs(props);
+const { is_back } = toRefs(props);
+const { fields } = toRefs(props);
+const { title } = toRefs(props);
+const { tip } = toRefs(props);
+const { remark } = toRefs(props);
+
+let form: Ref<{}> = ref({});
+const emit = defineEmits(['search', 'toReset', 'toBack','dataChange']);
+const toSubmit = () => {
+  const obj = _.pickBy(form);
+  emit('search', { ...obj });
+};
+// 重置
+const toReset = () => {
+  form.value = {};
+  emit('search');
+  emit('toReset');
+};
+// 文字描述
+const getField = (item, data) => {
+  let res = _.get(data, item, null);
+  if (item === 'type') res = res === null ? `text` : res;
+  if (item === 'place') res = res === null ? `请输入${data.label}` : res;
+  if (item === 'required') res = res === null ? false : res;
+  if (item === `error`) res = res === null ? `${data.label}错误` : res;
+  return res;
+};
+// 获取输入值
+const dataChange = (model: string) => {
+  const value = form.value[model];
+  emit('dataChange', { model, value });
+};
+// 返回
+const toBack = () => {
+  emit('toBack');
+};
+</script>
+
+<style lang="scss" scoped>
+.title {
+  margin: 0 0 5px 0;
+  .title_1 {
+    margin: 0 0 5px 0;
+    span:first-child {
+      font-size: 20px;
+      font-weight: 700;
+      margin-right: 10px;
+    }
+    span:last-child {
+      font-size: 14px;
+      color: #979797;
+    }
+  }
+  .title_2 {
+    span {
+      color: #8baae7;
+      font-size: 14px;
+      margin-top: 10px;
+    }
+  }
+}
+.search {
+  margin: 0 0 10px 0;
+  .form {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    .form_1 {
+      padding: 0 0 0 10px;
+      .el-form-item {
+        float: left;
+        width: 100%;
+        margin: 0 0 10px 0;
+      }
+      .el-select {
+        width: 100%;
+      }
+    }
+  }
+
+  .btn {
+    text-align: right;
+  }
+}
+.back {
+  text-align: left;
+  margin: 0 0 10px 0;
+}
+</style>

+ 162 - 0
src/components/common/scientist/info.vue

@@ -0,0 +1,162 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <component :is="CForm" :fields="field" :rules="{}" :form="form" labelWidth="auto" :isSave="false">
+          <template #phone>
+            <el-input v-model="form.phone.phone" readonly></el-input>
+          </template>
+          <template #email>
+            <el-input v-model="form.email.email" readonly></el-input>
+          </template>
+          <template #education>
+            <el-option v-for="i in educationList" :key="i.dict_value" :label="i.dict_label" :value="i.dict_value"></el-option>
+          </template>
+          <template #degree>
+            <el-option v-for="i in degreeList" :key="i.dict_value" :label="i.dict_label" :value="i.dict_value"></el-option>
+          </template>
+          <template #card="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #team_phone>
+            <el-input v-model="form.team_phone.phone" readonly></el-input>
+          </template>
+          <template #zc_file="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #direction="{ item }">
+            <span v-for="(i, index) in form[item.model]" :key="index" class="direction">
+              <span>{{ index + 1 }}.</span>{{ i.name }}
+            </span>
+          </template>
+          <template #case="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #case_file="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #settle_file="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #job_zc_file="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+        </component>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script lang="ts" setup>
+import CForm from '@/components/c-form.vue';
+import { ref, toRefs, watch } from 'vue';
+import store from '@/stores/counter';
+import type { Ref } from 'vue';
+interface opItem {
+  dict_value: string;
+  dict_label: string;
+}
+interface operaItem {
+  label: string;
+  model: string;
+  method: string;
+  confirm?: boolean;
+  type?: string;
+  display?: any;
+}
+const props = defineProps({
+  info: { type: Object, default: () => {} },
+  type: { type: String },
+
+  // 行业领域
+  fieldList: { type: Array },
+  // 是否在职人员
+  isjobList: { type: Array },
+  zcList: { type: Array },
+
+  educationList: { type: Array<opItem> },
+  degreeList: { type: Array<opItem> },
+});
+const { info } = toRefs(props);
+const { type } = toRefs(props);
+const { fieldList } = toRefs(props);
+const { isjobList } = toRefs(props);
+const { zcList } = toRefs(props);
+const { educationList } = toRefs(props);
+const { degreeList } = toRefs(props);
+let field: Ref<any[]> = ref([]);
+let form: Ref<{ phone: { phone: string }; email: { email: string }; team_phone: { phone: string } }> = ref({
+  phone: { phone: '' },
+  email: { email: '' },
+  team_phone: { phone: '' },
+});
+
+// 查询
+const search = async (e) => {
+  let user = store.state.user;
+  if (user && user.role_type != '1') {
+    if (e.phone && e.phone.is_show == false) e.phone.phone = '暂未公开';
+    if (e.email && e.email.is_show == false) e.email.email = '暂未公开';
+    if (e.team_phone && e.team_phone.is_show == false) e.team_phone.phone = '暂未公开';
+  }
+  form.value = e;
+  // 整理依托单位显示内容
+  let fields: Ref<any[]> = ref([
+    { label: '姓名', model: 'name', options: { readonly: true } },
+    { label: '出生年月', model: 'brith', options: { readonly: true } },
+    { label: '居住地', model: 'live_place', options: { readonly: true } },
+    { label: '手机号', model: 'phone', custom: true },
+    { label: '电子邮箱', model: 'email', custom: true },
+    { label: '学历', model: 'education', type: 'select' },
+    { label: '学位', model: 'degree', type: 'select' },
+    { label: '身份证', model: 'card', custom: true },
+    { label: '团队联系人', model: 'team_name', options: { readonly: true } },
+    { label: '团队联系电话', model: 'team_phone', custom: true },
+    { label: '所在单位全称', model: 'company', options: { readonly: true } },
+    { label: '所在单位地址', model: 'address', options: { readonly: true } },
+    { label: '专业技术职称', model: 'zc', options: { readonly: true } },
+    { label: '职称证明', model: 'zc_file', custom: true },
+    { label: '社会兼职', model: 'part_job', options: { readonly: true } },
+    { label: '研究领域', model: 'fields', options: { readonly: true } },
+    { label: '研究方向', model: 'direction', custom: true },
+    { label: '服务企业代表性案例', model: 'case', custom: true },
+    { label: '案例证明材料', model: 'case_file', custom: true },
+    { label: '是否为在职人员', model: 'is_job' },
+  ]);
+  if (type.value == 'web') {
+    fields.value = fields.value.filter((i) => i.model != 'card' && i.model != 'zc_file' && i.model != 'settle_file' && i.model != 'job_zc_file');
+  }
+  field.value = fields.value;
+};
+watch(info, (newVal, oldVal) => {
+  if (newVal && newVal._id) {
+    search(newVal);
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.direction {
+  display: inline-block;
+  background-color: #409eff;
+  border-radius: 5px;
+  padding: 0 5px;
+  margin: 0 5px 5px 0;
+  color: #ffffff;
+  line-height: 2.5;
+  span {
+    padding: 0 5px 0 0;
+  }
+}
+</style>

+ 128 - 0
src/components/common/unit/info.vue

@@ -0,0 +1,128 @@
+<template>
+  <div id="infos">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <!-- <data-form :fields="fields" :form="form" :rules="{}" :isSave="false" :span="24"> -->
+        <component :is="CForm" :fields="field" :rules="{}" :form="form" labelWidth="auto" :isSave="false">
+          <template #card="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #unit_phone>
+            <el-input v-model="form.unit_phone.phone" readonly></el-input>
+          </template>
+          <template #unit_email>
+            <el-input v-model="form.unit_email.email" readonly></el-input>
+          </template>
+          <template #direction="{ item }">
+            <span v-for="(i, index) in form[item.model]" :key="index" class="direction">
+              <span>{{ index + 1 }}.</span>{{ i.name }}
+            </span>
+          </template>
+          <template #audit_report="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #special_report="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+          <template #prove_file="{ item }">
+            <el-link class="link" :href="i.url" :underline="false" v-for="(i, index) in form[item.model]" :key="index" target="_blank">
+              <i class="el-icon-view el-icon--right"></i><span>{{ index + 1 }}.{{ i.name }}</span>
+            </el-link>
+          </template>
+        </component>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script lang="ts" setup>
+import CForm from '@/components/c-form.vue';
+import { ref, toRefs, watch } from 'vue';
+import store from '@/stores/counter';
+import type { Ref } from 'vue';
+interface operaItem {
+  label: string;
+  model: string;
+  method: string;
+  confirm?: boolean;
+  type?: string;
+  display?: any;
+}
+const props = defineProps({
+  info: { type: Object, default: () => {} },
+  type: { type: String },
+});
+const { info } = toRefs(props);
+const { type } = toRefs(props);
+let field: Ref<any[]> = ref([]);
+let form: Ref<{ unit_phone: { phone: string }; unit_email: { email: string } }> = ref({ unit_phone: { phone: '' }, unit_email: { email: '' } });
+
+// 查询
+const search = async (e) => {
+  let user = store.state.user;
+  if (user && user.role_type != '1') {
+    if (e.unit_phone && e.unit_phone.is_show == false) e.unit_phone.phone = '暂未公开';
+    if (e.unit_email && e.unit_email.is_show == false) e.unit_email.email = '暂未公开';
+  }
+  form.value = e;
+  // 整理依托单位显示内容
+  let fields: Ref<any[]> = ref([
+    { label: '单位全称', model: 'company', options: { readonly: true } },
+    { label: '单位地址', model: 'address', options: { readonly: true } },
+    { label: '单位法人姓名', model: 'name', options: { readonly: true } },
+    { label: '单位法人手机号码', model: 'phone', options: { readonly: true } },
+    { label: '法人身份证', model: 'card', custom: true },
+    { label: '单位联系人', model: 'unit_contact', options: { readonly: true } },
+    { label: '单位联系电话', model: 'unit_phone', custom: true },
+    { label: '单位电子邮箱', model: 'unit_email', custom: true },
+    { label: '行业领域(2)', model: 'fields', options: { readonly: true } },
+    { label: '技术需求方向', model: 'direction', custom: true },
+    { label: '上一年度财务审计报告', model: 'audit_report', custom: true },
+    { label: '上一年度R&D投入专项', model: 'special_report', custom: true },
+    { label: '其他证明资料', model: 'prove_file', custom: true },
+  ]);
+  if (type.value == 'web') {
+    fields.value = fields.value.filter(
+      (i) => i.model != 'name' && i.model != 'phone' && i.model != 'card' && i.model != 'audit_report' && i.model != 'special_report' && i.model != 'prove_file'
+    );
+  }
+
+  field.value = fields.value;
+};
+watch(info, (newVal, oldVal) => {
+  if (newVal && newVal._id) {
+    search(newVal);
+  }
+});
+// watch: {
+//     info: {
+//       deep: true,
+//       immediate: true,
+//       handler(val) {
+//         if (val && val._id) {
+//           this.search(val);
+//         }
+//       },
+//     },
+//   },
+</script>
+
+<style lang="scss" scoped>
+.direction {
+  display: inline-block;
+  background-color: #409eff;
+  border-radius: 5px;
+  padding: 0 5px;
+  margin: 0 5px 5px 0;
+  color: #ffffff;
+  line-height: 2.5;
+  span {
+    padding: 0 5px 0 0;
+  }
+}
+</style>

+ 1 - 0
src/main.ts

@@ -16,6 +16,7 @@ app.use(ElementPlus, { locale });
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component);
 }
+
 app.config.globalProperties.$moment = moment;
 app.config.globalProperties.$limit = parseInt(import.meta.env.VITE_APP_PAGE_SIZE) || 10;
 app.mount('#app');

+ 13 - 0
src/router/index.ts

@@ -1,8 +1,18 @@
 import { createRouter, createWebHistory } from 'vue-router';
+// 公共
+import common from './module/common';
+// 管理员
+import admin from './module/admin';
+// 个人用户
+import users from './module/users';
+// 企业用户
+import unit from './module/unit';
 import store from '@/stores/counter';
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
+    // 公共
+    ...common,
     {
       path: '/',
       meta: { title: '基础研究动态管理平台', is_filter: true },
@@ -14,6 +24,9 @@ const router = createRouter({
           meta: { title: '系统首页' },
           component: () => import('@/views/home/index.vue'),
         },
+        ...admin,
+        ...users,
+        ...unit,
       ],
     },
   ],

+ 126 - 0
src/router/module/admin.js

@@ -0,0 +1,126 @@
+export default [
+  // 用户管理
+  {
+    path: '/center/users/company',
+    meta: { title: '依托单位', is_filter: true },
+    component: () => import('@/views/center/users/company/index.vue'),
+  },
+  {
+    path: '/center/users/company/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/users/company/info.vue'),
+  },
+  {
+    path: '/center/users/scientist',
+    meta: { title: '科学家', is_filter: true },
+    component: () => import('@/views/center/users/scientist/index.vue'),
+  },
+  {
+    path: '/center/users/scientist/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/users/scientist/info.vue'),
+  },
+  // 科学家工作室
+  {
+    path: '/center/studio/info',
+    meta: { title: '信息列表', is_filter: true },
+    component: () => import('@/views/center/studio/info/index.vue'),
+  },
+  {
+    path: '/center/studio/info/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/studio/info/info.vue'),
+  },
+  {
+    path: '/center/studio/info/export',
+    meta: { title: '导出PDF', is_filter: true },
+    component: () => import('@/views/center/studio/info/export.vue'),
+  },
+  {
+    path: '/center/studio/year',
+    meta: { title: '年度报告', is_filter: true },
+    component: () => import('@/views/center/studio/year/index.vue'),
+  },
+  {
+    path: '/center/studio/flair',
+    meta: { title: '保留资质', is_filter: true },
+    component: () => import('@/views/center/studio/flair/index.vue'),
+  },
+  // 技术供求
+  {
+    path: '/center/supplydemand/support',
+    meta: { title: '技术支持', is_filter: true },
+    component: () => import('@/views/center/supplydemand/support/index.vue'),
+  },
+  {
+    path: '/center/supplydemand/support/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/supplydemand/support/info.vue'),
+  },
+  {
+    path: '/center/supplydemand/demand',
+    meta: { title: '技术需求', is_filter: true },
+    component: () => import('@/views/center/supplydemand/demand/index.vue'),
+  },
+  {
+    path: '/center/supplydemand/demand/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/supplydemand/demand/info.vue'),
+  },
+  // 其他内容
+  {
+    path: '/center/other/achieve',
+    meta: { title: '成果展示', is_filter: true },
+    component: () => import('@/views/center/other/achieve/index.vue'),
+  },
+  {
+    path: '/center/other/achieve/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/center/other/achieve/info.vue'),
+  },
+  {
+    path: '/center/other/notice',
+    meta: { title: '通知公告', is_filter: true },
+    component: () => import('@/views/center/other/notice/index.vue'),
+  },
+  {
+    path: '/center/other/notice/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/center/other/notice/add.vue'),
+  },
+  {
+    path: '/center/other/download',
+    meta: { title: '相关下载', is_filter: true },
+    component: () => import('@/views/center/other/download/index.vue'),
+  },
+  {
+    path: '/center/other/download/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/center/other/download/add.vue'),
+  },
+  {
+    path: '/center/other/contact',
+    meta: { title: '联系处室', is_filter: true },
+    component: () => import('@/views/center/other/contact/index.vue'),
+  },
+  {
+    path: '/center/other/message',
+    meta: { title: '系统消息', is_filter: true },
+    component: () => import('@/views/center/other/message/index.vue'),
+  },
+  {
+    path: '/center/other/message',
+    meta: { title: '系统消息', is_filter: true },
+    component: () => import('@/views/center/other/message/index.vue'),
+  },
+  {
+    path: '/center/other/message/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/center/other/message/add.vue'),
+  },
+  {
+    path: '/userInfo/center',
+    meta: { title: '个人中心', is_filter: true },
+    component: () => import('@/views/userInfo/center/index.vue'),
+  },
+];

+ 1 - 0
src/router/module/common.js

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

+ 94 - 0
src/router/module/unit.js

@@ -0,0 +1,94 @@
+export default [
+  // 个人中心
+  {
+    path: '/unit/center',
+    meta: { title: '个人中心', is_filter: true },
+    component: () => import('@/views/unit/center/index.vue'),
+  },
+  {
+    path: '/unit/builddesire',
+    meta: { title: '发布工作室建设意愿', is_filter: true },
+    component: () => import('@/views/unit/builddesire/index.vue'),
+  },
+  {
+    path: '/unit/builddesire/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/builddesire/add.vue'),
+  },
+  // 科学家工作室申报
+  {
+    path: '/unit/studio',
+    meta: { title: '科学家工作室', is_filter: true },
+    component: () => import('@/views/unit/studio/index.vue'),
+  },
+  {
+    path: '/unit/studio/add',
+    meta: { title: '申报进度', is_filter: true },
+    component: () => import('@/views/unit/studio/add.vue'),
+  },
+  {
+    path: '/unit/scientist',
+    meta: { title: '入驻科学家', is_filter: true },
+    component: () => import('@/views/unit/scientist/index.vue'),
+  },
+  {
+    path: '/unit/scientist/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/scientist/add.vue'),
+  },
+  {
+    path: '/unit/achieve',
+    meta: { title: '发布合作成果', is_filter: true },
+    component: () => import('@/views/unit/achieve/index.vue'),
+  },
+  {
+    path: '/unit/achieve/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/achieve/add.vue'),
+  },
+  {
+    path: '/unit/achieve/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/unit/achieve/info.vue'),
+  },
+  {
+    path: '/unit/yearreport',
+    meta: { title: '年度报告', is_filter: true },
+    component: () => import('@/views/unit/yearreport/index.vue'),
+  },
+  {
+    path: '/unit/yearreport/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/yearreport/add.vue'),
+  },
+  {
+    path: '/unit/applyflair',
+    meta: { title: '保留资质', is_filter: true },
+    component: () => import('@/views/unit/applyflair/index.vue'),
+  },
+  {
+    path: '/unit/applyflair/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/applyflair/add.vue'),
+  },
+  {
+    path: '/unit/contactoffice',
+    meta: { title: '联系处室', is_filter: true },
+    component: () => import('@/views/unit/contactoffice/index.vue'),
+  },
+  {
+    path: '/unit/demand',
+    meta: { title: '发布技术需求', is_filter: true },
+    component: () => import('@/views/unit/demand/index.vue'),
+  },
+  {
+    path: '/unit/demand/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/unit/demand/add.vue'),
+  },
+  {
+    path: '/unit/demand/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/unit/demand/info.vue'),
+  },
+];

+ 33 - 0
src/router/module/users.js

@@ -0,0 +1,33 @@
+export default [
+  // 个人中心
+  {
+    path: '/user/center',
+    meta: { title: '个人中心', is_filter: true },
+    component: () => import('@/views/user/center/index.vue'),
+  },
+  {
+    path: '/user/scientist/studio',
+    meta: { title: '入驻科学家工作室', is_filter: true },
+    component: () => import('@/views/user/scientist/studio/index.vue'),
+  },
+  {
+    path: '/user/scientist/studio/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/user/scientist/studio/info.vue'),
+  },
+  {
+    path: '/user/support',
+    meta: { title: '发布技术支持', is_filter: true },
+    component: () => import('@/views/user/support/index.vue'),
+  },
+  {
+    path: '/user/support/add',
+    meta: { title: '信息管理', is_filter: true },
+    component: () => import('@/views/user/support/add.vue'),
+  },
+  {
+    path: '/user/support/info',
+    meta: { title: '详细信息', is_filter: true },
+    component: () => import('@/views/user/support/info.vue'),
+  },
+];

+ 200 - 0
src/views/center/studio/flair/index.vue

@@ -0,0 +1,200 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_search="true" :fields="fields" @search="btSearch">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #studio_id>
+              <el-option v-for="i in studioList" :key="i.id" :label="i.name" :value="i.id"></el-option>
+            </template>
+          </c-search>
+        </el-col>
+        <el-col :span="24" class="two">
+          <data-table :fields="fields" :opera="opera" @query="search" :data="list" :total="total" @exam="toExam">
+            <template #file="{ row, item }">
+              <el-link v-for="(i, index) in row[item.model]" :key="index" :href="i.url" target="_blank">{{ i.name }}</el-link>
+            </template>
+          </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+    <c-dialog :dialog="dialog" @toClose="toClose">
+      <template v-slot:info>
+        <el-col :span="24" class="dialog_one" v-if="dialog.type == '1'">
+          <el-form :model="form" :rules="rules" ref="form" label-width="auto">
+            <el-form-item label="审核状态" prop="status">
+              <el-select v-model="form.status" clearable filterable placeholder="请选择审核状态" style="width: 100%">
+                <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="审核意见" prop="remark">
+              <el-input v-model="form.remark" placeholder="请输入审核意见" type="textarea"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button size="small" type="primary" @click="toSubmit('form')">提交审核</el-button>
+            </el-form-item>
+          </el-form>
+        </el-col>
+      </template>
+    </c-dialog>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('applyflair');
+const { mapActions: studio } = createNamespacedHelpers('studio');
+const { mapActions: sysdictdata } = createNamespacedHelpers('sysdictdata');
+const { mapActions: message } = createNamespacedHelpers('message');
+const { mapActions: unitStudioApply } = createNamespacedHelpers('unitStudioApply');
+const moment = require('moment');
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      // 查询
+      searchInfo: {},
+      fields: [
+        { label: '序号', options: { type: 'index' } },
+        {
+          label: '工作室名称',
+          model: 'studio_id',
+          type: 'select',
+          format: (i) => {
+            let data = this.studioList.find((r) => r.id == i);
+            if (data) return data.name;
+          },
+          isSearch: true,
+        },
+        { label: '依托单位名称', model: 'company_name' },
+        { label: '申请时间', model: 'apply_time' },
+        { label: '评估文件', model: 'file', custom: true },
+        {
+          label: '审核状态',
+          model: 'status',
+          type: 'select',
+          format: (i) => {
+            let data = this.statusList.find((r) => r.dict_value == i);
+            if (data) return data.dict_label;
+          },
+          isSearch: true,
+        },
+      ],
+      opera: [{ label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' }],
+      list: [],
+      total: 0,
+      // 工作室列表
+      studioList: [],
+      // 审核状态
+      statusList: [],
+      dialog: { title: '信息审核', show: false, type: '1' },
+      form: {},
+      rules: {
+        status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+        remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+      },
+    };
+  },
+  created() {
+    this.searchOther();
+    this.search();
+  },
+  methods: {
+    ...mapActions(['query', 'update']),
+    ...studio({ sQuery: 'query' }),
+    ...sysdictdata({ dQuery: 'query' }),
+    ...message({ mCreate: 'create' }),
+    ...unitStudioApply({ CFetch: 'fetch' }),
+    async search({ skip = 0, limit = this.$limit, ...info } = {}) {
+      if (this.studio_id) info.studio_id = this.studio_id;
+      let res = await this.query({ skip, limit, ...info, ...this.searchInfo });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    btSearch(query) {
+      this.$set(this, `searchInfo`, query);
+      this.search();
+    },
+    // 信息审核
+    toExam({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = { title: '信息审核', show: true, type: '1' };
+    },
+    // 提交审核
+    toSubmit(formName) {
+      this.$refs[formName].validate(async (valid) => {
+        if (valid) {
+          let data = this.form;
+          let obj = { id: data.id, status: data.status };
+          let res = await this.update(obj);
+          if (this.$checkRes(res, `信息审核成功`, res.errmsg)) this.createMess(data);
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    // 发送系统消息
+    async createMess(data) {
+      let res = await this.CFetch(data.user_id);
+      if (this.$checkRes(res)) {
+        let obj = {
+          user_id: this.user._id,
+          title: '审核通知',
+          send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+          type: '3',
+          user: [{ id: data.user_id, company: data.company_name, phone: res.data.phone }],
+          content: '您好,您所申请《' + data.studio_name + '》的保留资质,' + `${data.status == '1' ? '已通过审核' : '未通过审核'}` + ',原因:' + data.remark,
+        };
+        let arr = await this.mCreate(obj);
+        if (this.$checkRes(arr, `系统信息发送成功`, arr.errmsg)) this.toClose();
+      }
+    },
+    // 关闭弹框
+    toClose() {
+      this.form = {};
+      this.dialog = { title: '信息审核', show: false, type: '1' };
+      this.search();
+    },
+    // 查詢其他信息
+    async searchOther() {
+      let res;
+      // 工作室
+      res = await this.sQuery({ status: '7' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `studioList`, res.data);
+      }
+      // 审核状态
+      res = await this.dQuery({ dict_type: 'studio_status' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `statusList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    studio_id() {
+      return this.$route.query.studio_id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 259 - 0
src/views/center/studio/info/export.vue

@@ -0,0 +1,259 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight" v-loading="loading">
+        <el-col :span="24" class="one">
+          <c-search :is_back="true" @toBack="toBack()">
+            <template slot="custombtn">
+              <el-button type="success" size="small" @click="toExport()">导出</el-button>
+              <!-- <el-button type="success" size="small" v-print="{ id: 'demo', popTitle: info.name }">打印</el-button> -->
+            </template>
+          </c-search>
+        </el-col>
+        <el-col :span="24" class="two" id="demo" ref="print" style="box-shadow: 0 0 10px #f1f1f1; margin: 0 0 10px 0; padding: 10px 200px">
+          <el-col :span="24" class="two_1" style="text-align: center; margin: 10px 0 15px 0; font-weight: none; font-size: 48px; font-family: 仿宋">
+            吉林省科学家工作室申报基本信息表
+          </el-col>
+          <el-col :span="24" class="two_2" style="padding: 0 0%">
+            <table border="1" style="border-spacing: 0; width: 100%">
+              <tr>
+                <td rowspan="2" class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">科学家工作室</td>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">名称</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.name }}</td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">专业领域</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.zy_fields_name }}</td>
+              </tr>
+              <tr>
+                <td rowspan="3" class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">依托单位</td>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">名称</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.company_name }}</td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">联系人</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.company_contact }}</td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">联系方式</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.company_contact_phone }}</td>
+              </tr>
+              <tr>
+                <td :rowspan="sci_num" class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">入驻科学家</td>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">姓名</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.scientist_name }}</td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">所在单位</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.scientistinfo_company }}</td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">联系方式</td>
+                <td style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.scientistinfo_phone }}</td>
+              </tr>
+              <tr>
+                <td :rowspan="info.team.length + 1" class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">团队成员</td>
+              </tr>
+              <tr v-for="(t, tindex) in info.team" :key="tindex">
+                <td class="team" style="font-size: 28px; font-family: 仿宋; padding: 8px">
+                  <p style="margin: 0 0 5px 0; font-size: 28px">{{ t.name }}</p>
+                  <p style="margin: 0 0 5px 0; font-size: 28px">职称:{{ t.zc }}</p>
+                  <p style="margin: 0 0 5px 0; font-size: 28px">工作单位:{{ t.company }}</p>
+                </td>
+              </tr>
+              <tr>
+                <td class="key" style="font-weight: none; font-size: 28px; font-family: 仿宋; padding: 8px">申报日期</td>
+                <td colspan="2" style="font-size: 28px; font-family: 仿宋; padding: 8px">{{ info.apply_time }}</td>
+              </tr>
+            </table>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('studio');
+const { mapActions: scientistsettle } = createNamespacedHelpers('scientistsettle');
+const { mapActions: sysdictdata } = createNamespacedHelpers('sysdictdata');
+const { mapActions: unitStudioApply } = createNamespacedHelpers('unitStudioApply');
+const { mapActions: userStudioApply } = createNamespacedHelpers('userStudioApply');
+import htmlToPdf from '@common/src/util/htmlToPdf.js';
+export default {
+  name: 'info',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      // 依托单位基本信息
+      unitInfo: {},
+      // 科学家信息
+      sciInfo: {},
+      info: { team: [], zy_fields: [] },
+      fieldList: [],
+      // 入驻科学家入驻条数
+      sci_num: 0,
+      loading: false,
+    };
+  },
+  async created() {
+    await this.searchOther();
+    await this.searchunitBasic();
+    await this.searchsciBasic();
+    await this.search();
+  },
+  methods: {
+    ...mapActions(['fetch']),
+    ...scientistsettle(['query']),
+    ...sysdictdata({ dQuery: 'query' }),
+    ...unitStudioApply({ uFetch: 'fetch' }),
+    ...userStudioApply({ sFetch: 'fetch' }),
+    // 查询企业基本信息
+    async searchunitBasic() {
+      if (this.company_id) {
+        let res = await this.uFetch(this.company_id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `unitInfo`, res.data);
+        }
+      }
+    },
+    // 查询科学家基本信息
+    async searchsciBasic() {
+      if (this.scientistinfo_id) {
+        let res = await this.sFetch(this.scientistinfo_id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `sciInfo`, res.data);
+        }
+      }
+    },
+    async search() {
+      if (this.id) {
+        let res = await this.fetch(this.id);
+        if (this.$checkRes(res)) {
+          let info = res.data;
+          info.team = await this.sarchTeam();
+          if (info.zy_fields && info.zy_fields.length > 0) info.zy_fields_name = this.searchDict(info.zy_fields, 'fieldList');
+          info.company_contact = this.unitInfo.unit_contact;
+          info.company_contact_phone = this.unitInfo.unit_phone.phone;
+          info.scientistinfo_company = this.sciInfo.company;
+          info.scientistinfo_phone = this.sciInfo.phone.phone;
+          this.$set(this, `sci_num`, 4 + info.team.length);
+          this.$set(this, `info`, info);
+        }
+      }
+    },
+    // 查询团队信息
+    async sarchTeam() {
+      let list = [];
+      let res = await this.query({ studio_id: this.id });
+      if (this.$checkRes(res)) {
+        if (res.total > 0) {
+          let data = res.data[0];
+          list = data.team;
+        }
+      }
+      return list;
+    },
+    searchDict(e, type) {
+      let data = [];
+      for (const val of e) {
+        let info = this[type].find((i) => i.dict_value == val);
+        if (info) data.push(info.dict_label);
+      }
+      return data.join(',');
+    },
+    // 导出
+    toExport() {
+      let that = this;
+      this.$confirm('您确认要导出此信息吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          that.loading = true;
+          let ele = document.querySelector('#demo');
+          let pdfName = this.info.name;
+          htmlToPdf.createPDF(ele, pdfName);
+          that.loading = false;
+        })
+        .catch(() => {});
+    },
+    // 返回
+    toBack() {
+      window.history.go('-1');
+    },
+    // 查询其他信息
+    async searchOther() {
+      let res;
+      // 专业领域
+      res = await this.dQuery({ dict_type: 'studio_field' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `fieldList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+    company_id() {
+      return this.$route.query.company_id;
+    },
+    scientistinfo_id() {
+      return this.$route.query.scientistinfo_id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+// .main {
+//   .two {
+//     box-shadow: 0 0 10px #f1f1f1;
+//     margin: 0 0 10px 0;
+//     padding: 10px 20px;
+//     .two_1 {
+//       text-align: center;
+//       margin: 0 0 1vw 0;
+//       font-weight: none;
+//       font-size: 30px;
+//       font-family: 仿宋;
+//     }
+//     .two_2 {
+//       padding: 0 14%;
+//       table {
+//         tr {
+//           td {
+//             font-size: 28px;
+//             font-family: 仿宋;
+//             padding: 8px;
+//           }
+//           .team {
+//             p {
+//               margin: 0 0 5px 0;
+//               font-size: 14px;
+//             }
+//           }
+//           .key {
+//             font-weight: none;
+//           }
+//         }
+//       }
+//     }
+//   }
+// }
+</style>

+ 190 - 0
src/views/center/studio/info/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_search="true" :fields="fields" @search="btSearch"> </c-search>
+        </el-col>
+        <el-col :span="24" class="two">
+          <data-table :fields="fields" :opera="opera" @query="search" :data="list" :total="total" @view="toView" @exam="toExam" @export="toExport">
+          </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+    <c-dialog :dialog="dialog" @toClose="toClose">
+      <template v-slot:info>
+        <el-col :span="24" class="dialog_one" v-if="dialog.type == '1'">
+          <el-form :model="form" :rules="rules" ref="form" label-width="auto">
+            <el-form-item label="审核状态" prop="status">
+              <el-select v-model="form.status" clearable filterable placeholder="请选择审核状态" style="width: 100%">
+                <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="审核意见" prop="remark">
+              <el-input v-model="form.remark" placeholder="请输入审核意见" type="textarea"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button size="small" type="primary" @click="toSubmit('form')">提交审核</el-button>
+            </el-form-item>
+          </el-form>
+        </el-col>
+      </template>
+    </c-dialog>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('studio');
+const { mapActions: unitStudioApply } = createNamespacedHelpers('unitStudioApply');
+const { mapActions: message } = createNamespacedHelpers('message');
+const { mapActions: sysdictdata } = createNamespacedHelpers('sysdictdata');
+const moment = require('moment');
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      searchInfo: {},
+      fields: [
+        { label: '序号', options: { type: 'index' } },
+        { label: '依托单位', model: 'company_name', isSearch: true },
+        { label: '工作室名称', model: 'name', isSearch: true },
+        { label: '入驻科学家', model: 'scientist_name', isSearch: true },
+        { label: '申报日期', model: 'apply_time' },
+        {
+          label: '申报状态',
+          model: 'status',
+          format: (i) => {
+            let data = this.statusList.find((r) => r.dict_value == i);
+            if (data) return data.dict_label;
+          },
+        },
+      ],
+      opera: [
+        { label: '详情', method: 'view' },
+        { label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' },
+        { label: '导出', method: 'export' },
+      ],
+      list: [],
+      total: 0,
+      // 领域
+      fieldList: [],
+      // 审核状态
+      statusList: [],
+      dialog: { title: '信息审核', show: false, type: '1' },
+      form: {},
+      rules: {
+        status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+        remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+      },
+    };
+  },
+  async created() {
+    await this.searchOther();
+    await this.search();
+  },
+  methods: {
+    ...mapActions(['query', 'update']),
+    ...unitStudioApply({ CFetch: 'fetch' }),
+    ...message({ mCreate: 'create' }),
+    ...sysdictdata({ sQuery: 'query' }),
+    async search({ skip = 0, limit = this.$limit, ...info } = {}) {
+      let res = await this.query({ skip, limit, ...info, ...this.searchInfo });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    btSearch(query) {
+      this.$set(this, `searchInfo`, query);
+      this.search();
+    },
+    // 详情
+    toView({ data }) {
+      this.$router.push({ path: '/center/studio/info/info', query: { id: data.id } });
+    },
+    // 导出
+    toExport({ data }) {
+      this.$router.push({
+        path: '/center/studio/info/export',
+        query: { id: data._id, company_id: data.company_id, scientistinfo_id: data.scientistinfo_id },
+      });
+    },
+    // 审核
+    toExam({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = { title: '信息审核', show: true, type: '1' };
+    },
+    // 提交审核
+    toSubmit(formName) {
+      this.$refs[formName].validate(async (valid) => {
+        if (valid) {
+          let data = this.form;
+          let obj = { id: data._id, status: data.status };
+          let res = await this.update(obj);
+          if (this.$checkRes(res, `信息审核成功`, res.errmsg)) this.createMess(data);
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    // 发送系统消息
+    async createMess(data) {
+      let res = await this.CFetch(data.user_id);
+      if (this.$checkRes(res)) {
+        let status = this.statusList.find((r) => r.dict_value == data.status);
+        if (status) {
+          let obj = {
+            user_id: this.user._id,
+            title: '审核通知',
+            send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+            type: '3',
+            user: [{ id: data.user_id, company: data.company, phone: res.data.phone }],
+            content: '您好,您所申报《' + data.name + '》的建设申请,' + status.dict_label + ',原因:' + data.remark,
+          };
+          let arr = await this.mCreate(obj);
+          if (this.$checkRes(arr, `系统信息发送成功`, arr.errmsg)) this.toClose();
+        }
+      }
+    },
+    // 关闭弹框
+    toClose() {
+      this.form = {};
+      this.dialog = { title: '信息审核', show: false, type: '1' };
+      this.search();
+    },
+    // 查询其他信息
+    async searchOther() {
+      let res;
+      // 申报状态
+      res = await this.sQuery({ dict_type: 'studio_studio_status' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `statusList`, res.data);
+      }
+      // 领域
+      res = await this.sQuery({ dict_type: 'studio_field' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `fieldList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 94 - 0
src/views/center/studio/info/info.vue

@@ -0,0 +1,94 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_back="true" @toBack="toBack()">
+            <!-- <template slot="custombtn">
+              <el-button type="warning" size="small" @click="toExport()">导出PDF</el-button>
+            </template> -->
+          </c-search>
+        </el-col>
+        <el-col :span="24" class="two">
+          <studio-info :info="info"></studio-info>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('studio');
+const { mapActions: scientistsettle } = createNamespacedHelpers('scientistsettle');
+
+export default {
+  name: 'info',
+  props: {},
+  components: {
+    studioInfo: () => import('@c/common/studio/info.vue'),
+  },
+  data: function () {
+    return {
+      info: { team: [] },
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...mapActions(['fetch']),
+    ...scientistsettle(['query']),
+    async search() {
+      if (this.id) {
+        let res = await this.fetch(this.id);
+        if (this.$checkRes(res)) {
+          res.data.team = await this.sarchTeam();
+          this.$set(this, `info`, res.data);
+        }
+      }
+    },
+    // 查询团队信息
+    async sarchTeam() {
+      let list = [];
+      let res = await this.query({ studio_id: this.id });
+      if (this.$checkRes(res)) {
+        if (res.total > 0) {
+          let data = res.data[0];
+          list = data.team;
+        }
+      }
+      return list;
+    },
+    // 返回
+    toBack() {
+      window.history.go('-1');
+    },
+    // 导出pdf
+    toExport() {
+      this.$router.push({
+        path: '/center/studio/info/export',
+        query: { id: this.id, company_id: this.info.company_id, scientistinfo_id: this.info.scientistinfo_id },
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 200 - 0
src/views/center/studio/year/index.vue

@@ -0,0 +1,200 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <c-search :is_search="true" :fields="fields" @search="btSearch">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+            </template>
+            <template #studio_id>
+              <el-option v-for="i in studioList" :key="i.id" :label="i.name" :value="i.id"></el-option>
+            </template>
+          </c-search>
+        </el-col>
+        <el-col :span="24" class="two">
+          <data-table :fields="fields" :opera="opera" @query="search" :data="list" :total="total" @exam="toExam">
+            <template #file="{ row, item }">
+              <el-link v-for="(i, index) in row[item.model]" :key="index" :href="i.url" target="_blank">{{ i.name }}</el-link>
+            </template>
+          </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+    <c-dialog :dialog="dialog" @toClose="toClose">
+      <template v-slot:info>
+        <el-col :span="24" class="dialog_one" v-if="dialog.type == '1'">
+          <el-form :model="form" :rules="rules" ref="form" label-width="auto">
+            <el-form-item label="审核状态" prop="status">
+              <el-select v-model="form.status" clearable filterable placeholder="请选择审核状态" style="width: 100%">
+                <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="审核意见" prop="remark">
+              <el-input v-model="form.remark" placeholder="请输入审核意见" type="textarea"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button size="small" type="primary" @click="toSubmit('form')">提交审核</el-button>
+            </el-form-item>
+          </el-form>
+        </el-col>
+      </template>
+    </c-dialog>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('yearreport');
+const { mapActions: studio } = createNamespacedHelpers('studio');
+const { mapActions: sysdictdata } = createNamespacedHelpers('sysdictdata');
+const { mapActions: message } = createNamespacedHelpers('message');
+const { mapActions: unitStudioApply } = createNamespacedHelpers('unitStudioApply');
+const moment = require('moment');
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      // 查询
+      searchInfo: {},
+      fields: [
+        { label: '序号', options: { type: 'index' } },
+        {
+          label: '工作室名称',
+          model: 'studio_id',
+          type: 'select',
+          format: (i) => {
+            let data = this.studioList.find((r) => r.id == i);
+            if (data) return data.name;
+          },
+          isSearch: true,
+        },
+        { label: '依托单位名称', model: 'company_name' },
+        { label: '申请时间', model: 'apply_time' },
+        { label: '报告文件', model: 'file', custom: true },
+        {
+          label: '审核状态',
+          model: 'status',
+          type: 'select',
+          format: (i) => {
+            let data = this.statusList.find((r) => r.dict_value == i);
+            if (data) return data.dict_label;
+          },
+          isSearch: true,
+        },
+      ],
+      opera: [{ label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' }],
+      list: [],
+      total: 0,
+      // 工作室列表
+      studioList: [],
+      // 审核状态
+      statusList: [],
+      dialog: { title: '信息审核', show: false, type: '1' },
+      form: {},
+      rules: {
+        status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+        remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+      },
+    };
+  },
+  created() {
+    this.searchOther();
+    this.search();
+  },
+  methods: {
+    ...mapActions(['query', 'update']),
+    ...studio({ sQuery: 'query' }),
+    ...sysdictdata({ dQuery: 'query' }),
+    ...message({ mCreate: 'create' }),
+    ...unitStudioApply({ CFetch: 'fetch' }),
+    async search({ skip = 0, limit = this.$limit, ...info } = {}) {
+      if (this.studio_id) info.studio_id = this.studio_id;
+      let res = await this.query({ skip, limit, ...info, ...this.searchInfo });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    btSearch(query) {
+      this.$set(this, `searchInfo`, query);
+      this.search();
+    },
+    // 信息审核
+    toExam({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = { title: '信息审核', show: true, type: '1' };
+    },
+    // 提交审核
+    toSubmit(formName) {
+      this.$refs[formName].validate(async (valid) => {
+        if (valid) {
+          let data = this.form;
+          let obj = { id: data.id, status: data.status };
+          let res = await this.update(obj);
+          if (this.$checkRes(res, `信息审核成功`, res.errmsg)) this.createMess(data);
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+    // 发送系统消息
+    async createMess(data) {
+      let res = await this.CFetch(data.user_id);
+      if (this.$checkRes(res)) {
+        let obj = {
+          user_id: this.user._id,
+          title: '审核通知',
+          send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+          type: '3',
+          user: [{ id: data.user_id, company: data.company_name, phone: res.data.phone }],
+          content: '您好,您所上传《' + data.studio_name + '》的年度报告,' + `${data.status == '1' ? '已通过审核' : '未通过审核'}` + ',原因:' + data.remark,
+        };
+        let arr = await this.mCreate(obj);
+        if (this.$checkRes(arr, `系统信息发送成功`, arr.errmsg)) this.toClose();
+      }
+    },
+    // 关闭弹框
+    toClose() {
+      this.form = {};
+      this.dialog = { title: '信息审核', show: false, type: '1' };
+      this.search();
+    },
+    // 查詢其他信息
+    async searchOther() {
+      let res;
+      // 工作室
+      res = await this.sQuery({ status: '7' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `studioList`, res.data);
+      }
+      // 审核状态
+      res = await this.dQuery({ dict_type: 'studio_status' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `statusList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    studio_id() {
+      return this.$route.query.studio_id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 235 - 0
src/views/center/users/company/index.vue

@@ -0,0 +1,235 @@
+<template>
+  <div id="index">
+    <el-col class="main animate__animated animate__backInRight">
+      <el-col :span="24" class="one">
+        <component :is="partsSearch" :is_search="true" :fields="fields" @search="partSearch">
+          <template #status>
+            <el-option v-for="i in statusList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+          </template>
+          <!-- <template #unit_phone>
+            <el-input v-model="searchForm.unit_phone" placeholder="请输入单位联系电话" clearable size="small"></el-input>
+          </template> -->
+        </component>
+      </el-col>
+      <el-col :span="24" class="two">
+        <component
+          :is="CTable"
+          :fields="fields"
+          :opera="opera"
+          :select="false"
+          :selected="selected"
+          @handleSelect="handleSelect"
+          @query="search"
+          :data="tableData"
+          :total="total"
+          @view="toView"
+          @exam="toExam"
+          @del="toDel"
+        >
+          <!-- <template #unit_phone="{ row, item }">
+            {{ row[item.model].phone }}
+          </template> -->
+        </component>
+      </el-col>
+    </el-col>
+  </div>
+  <el-dialog v-model="dialog.show" title="文件夹" :before-close="handleClose">
+    <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+      <template #status>
+        <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+      </template>
+    </component>
+  </el-dialog>
+</template>
+<script setup lang="ts">
+import store from '@/stores/counter';
+import moment from 'moment';
+// #region 组件
+import partsSearch from '@/components/c-search.vue';
+import CTable from '@/components/c-table.vue';
+import CForm from '@/components/c-form.vue';
+// #endregion
+import type { Ref } from 'vue';
+import { ref, onMounted, getCurrentInstance, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+// #region 接口
+import { UnitStudioApplyStore } from '@common/src/stores/studio/role/unitStudioApply'; // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import { MessageStore } from '@common/src/stores/studio/other/message'; // 系统消息
+import { UnitStore } from '@common/src/stores/users/unit';
+import { RoleStore } from '@common/src/stores/admin/role';
+import type { IQueryResult } from '@/util/types.util';
+const unitStudioApply = UnitStudioApplyStore();
+const dictData = DictDataStore();
+const message = MessageStore();
+const role = RoleStore();
+const unit = UnitStore();
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+interface operaItem {
+  label: string;
+  method: string;
+  confirm?: boolean;
+  type?: string;
+  display?: any;
+}
+// #endregion
+// 列表数据
+let tableData: Ref<any[]> = ref([]);
+// 列表
+let fields: Ref<any[]> = ref([
+  { label: '序号', options: { type: 'index' } },
+  { label: '单位名称', model: 'company', isSearch: true },
+  { label: '单位地址', model: 'address', isSearch: true },
+  { label: '单位联系人', model: 'unit_contact', isSearch: true },
+  { label: '单位联系电话', model: 'unit_phone.phone', isSearch: true },
+  {
+    label: '审核状态',
+    model: 'status',
+    type: 'select',
+    format: (i) => {
+      let data = statusList.value.find((r) => r.dict_value == i);
+      if (data) return data.dict_label;
+    },
+    isSearch: true,
+  },
+]);
+// 操作
+let opera: Ref<any[]> = ref([
+  { label: '详情', method: 'view' },
+  { label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' },
+  { label: '删除', method: 'del', type: 'danger', confirm: true },
+]);
+// 多选
+let selected: Ref<any[]> = ref([]);
+// 总数
+let total: Ref<number> = ref(0);
+let skip = 0;
+let limit: number = proxy.$limit;
+// 查询数据
+let searchForm: Ref<{}> = ref({});
+// 弹框
+const dialog: Ref<{ type: string; show: boolean; title: string }> = ref({ type: '1', show: false, title: '信息管理' });
+// 审核表单
+let form: Ref<{}> = ref({});
+// 必填项
+const rules = reactive<FormRules>({
+  status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+  remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '审核状态', model: 'status', type: 'select' },
+  { label: '审核意见', model: 'remark', type: 'textarea' },
+]);
+// 角色
+let roleInfo: Ref<{ _id: string }> = ref({ _id: '' });
+// 状态
+let statusList: Ref<any[]> = ref([]);
+
+onMounted(async () => {
+  await searchOther();
+  await search({ skip, limit });
+});
+// 查询
+const search = async (e: { skip: number; limit: number }) => {
+  const { skip, limit } = e;
+  let info = { limit: limit, skip: skip, ...searchForm.value };
+  const res: IQueryResult = await unitStudioApply.query(info);
+  tableData.value = res.data as any[];
+  total.value = res.total;
+};
+// 查询
+const partSearch = (form: { [x: string]: any }) => {
+  searchForm.value = form;
+  search({ skip, limit });
+};
+
+// 修改
+const toView = async (data: { _id: string }) => {
+  router.push({ path: '/center/users/company/info', query: { id: data._id } });
+};
+// 审核
+const toExam = (data: object) => {
+  console.log(data);
+
+  form.value = data;
+  dialog.value = { title: '信息管理', show: true, type: '1' };
+};
+// 审核保存
+const toSave = async (data: { _id: string; status: string }) => {
+  let obj = { _id: data._id, status: data.status };
+  let res: IQueryResult = await unitStudioApply.create(obj);
+  if (obj.status == '1') updateRole(data);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '维护信息成功' });
+    createMess(data);
+  } else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+// // 分配角色
+const updateRole = async (e) => {
+  let userInfo = await unit.fetch(e.unit_id);
+  if (userInfo.errcode == 0) {
+    let object = {
+      _id: userInfo.data._id,
+      role: [...userInfo.data.role, roleInfo.value._id],
+      account: userInfo.data.account,
+      unit_name: userInfo.data.unit_name,
+    };
+    let res = await unit.update(object);
+  }
+};
+// // 发送系统消息
+const createMess = async (data) => {
+  let user = store.state.user;
+  let res = await unit.fetch(data.unit_id);
+  if (res.errcode == 0) {
+    let obj = {
+      user_id: user._id,
+      title: '审核通知',
+      send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+      type: '3',
+      user: [{ id: data.unit_id, company: data.company, phone: data.phone }],
+      content: '您好,权限申请,' + `${data.status == '1' ? '已通过审核' : '未通过审核'}` + ',原因:' + data.remark,
+    };
+    let arr = await message.create(obj);
+    if (arr.errcode == 0) {
+      ElMessage({ type: 'success', message: '系统信息发送成功' });
+      handleClose();
+    }
+  }
+};
+// 删除
+const toDel = async (data: { _id: string }) => {
+  const res: IQueryResult = await unitStudioApply.del(data._id);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '删除成功' });
+    search({ skip, limit });
+  }
+};
+
+// 关闭弹窗
+const handleClose = () => {
+  form.value = {};
+  search({ skip, limit });
+  dialog.value = { title: '信息管理', show: false, type: '' };
+};
+// 选择
+const handleSelect = () => {};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await dictData.query({ dict_type: 'studio_status' });
+  statusList.value = p1.data as [];
+  // 角色
+  const p2 = await role.query({ code: 'studio-unit', account_type: '3', status: 'Y' });
+  roleInfo.value = p2.data[0];
+};
+</script>
+<style scoped>
+.main .one {
+  margin: 0 0 10px 0;
+}
+</style>

+ 64 - 0
src/views/center/users/company/info.vue

@@ -0,0 +1,64 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_back="true" @toBack="toBack()"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component :is="unitInfo" :info="info"></component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import unitInfo from '@/components/common/unit/info.vue';
+import { useRouter, useRoute } from 'vue-router';
+import type { Ref } from 'vue';
+import { ref, toRefs, onMounted } from 'vue';
+import { UnitStudioApplyStore } from '@common/src/stores/studio/role/unitStudioApply'; // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const unitStudioApply = UnitStudioApplyStore();
+const dictData = DictDataStore();
+let route = useRoute();
+let info: Ref<{}> = ref({});
+// 行业领域
+let fieldList: Ref<any[]> = ref([]);
+// route.query.id,
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+  if (route.query.id) {
+    let id = route.query.id;
+    const res: IQueryResult = await unitStudioApply.fetch(id);
+    let data: { fields: any } = res.data as { fields: any };
+    if (data.fields && data.fields.length > 0) data.fields = searchDict(data.fields, 'fieldList');
+    info.value = res.data;
+  }
+};
+const searchDict = (e, type) => {
+  let data = [];
+  for (const val of e) {
+    let value = [type].find((r) => r.dict_value == val);
+    if (value) data.push(value.dict_label);
+  }
+  if (data && data.length > 0) return data.toString();
+  else return '暂无';
+};
+const toBack = () => {
+  window.history.go(-1);
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---行业领域
+  const p1: IQueryResult = await dictData.query({ dict_type: 'studio_field' });
+  fieldList.value = p1.data as [];
+};
+</script>
+<style scoped></style>

+ 239 - 0
src/views/center/users/scientist/index.vue

@@ -0,0 +1,239 @@
+<template>
+  <div id="index">
+    <el-col class="main animate__animated animate__backInRight">
+      <el-col :span="24" class="one">
+        <component :is="partsSearch" :is_search="true" :fields="fields" @search="partSearch">
+          <template #status>
+            <el-option v-for="i in statusList" :key="i.model" :label="i.dict_label" :value="i.dict_value"></el-option>
+          </template>
+          <!-- <template #unit_phone>
+            <el-input v-model="searchForm.unit_phone" placeholder="请输入单位联系电话" clearable size="small"></el-input>
+          </template> -->
+        </component>
+      </el-col>
+      <el-col :span="24" class="two">
+        <component
+          :is="CTable"
+          :fields="fields"
+          :opera="opera"
+          :select="false"
+          :selected="selected"
+          @handleSelect="handleSelect"
+          @query="search"
+          :data="tableData"
+          :total="total"
+          @view="toView"
+          @exam="toExam"
+          @del="toDel"
+        >
+          <!-- <template #unit_phone="{ row, item }">
+            {{ row[item.model].phone }}
+          </template> -->
+        </component>
+      </el-col>
+    </el-col>
+  </div>
+  <el-dialog v-model="dialog.show" title="文件夹" :before-close="handleClose">
+    <component :is="CForm" :fields="infoFields" :rules="rules" :form="form" labelWidth="auto" @save="toSave">
+      <template #status>
+        <el-option v-for="(item, index) in statusList" :key="index" :label="item.dict_label" :value="item.dict_value"></el-option>
+      </template>
+    </component>
+  </el-dialog>
+</template>
+<script setup lang="ts">
+import store from '@/stores/counter';
+import moment from 'moment';
+// #region 组件
+import partsSearch from '@/components/c-search.vue';
+import CTable from '@/components/c-table.vue';
+import CForm from '@/components/c-form.vue';
+// #endregion
+import type { Ref } from 'vue';
+import { ref, onMounted, getCurrentInstance, reactive } from 'vue';
+import type { FormRules } from 'element-plus';
+import { ElMessage } from 'element-plus';
+import { useRouter } from 'vue-router';
+// #region 接口
+import { UserStudioApplyStore } from '@common/src/stores/studio/role/userStudioApply'; // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import { MessageStore } from '@common/src/stores/studio/other/message'; // 系统消息
+import { UsersStore } from '@common/src/stores/users/users';
+import { RoleStore } from '@common/src/stores/admin/role';
+import type { IQueryResult } from '@/util/types.util';
+const userStudioApply = UserStudioApplyStore();
+const dictData = DictDataStore();
+const message = MessageStore();
+const role = RoleStore();
+const users = UsersStore();
+const { proxy } = getCurrentInstance() as any;
+const router = useRouter();
+interface operaItem {
+  label: string;
+  method: string;
+  confirm?: boolean;
+  type?: string;
+  display?: any;
+}
+// #endregion
+// 列表数据
+let tableData: Ref<any[]> = ref([]);
+// 列表
+let fields: Ref<any[]> = ref([
+  { label: '序号', options: { type: 'index' } },
+  { label: '姓名', model: 'name', isSearch: true },
+  { label: '出生年月', model: 'brith' },
+  { label: '居住地', model: 'live_place' },
+  { label: '手机号', model: 'phone.phone' },
+  { label: '电子邮箱', model: 'email.email' },
+  { label: '团队联系人', model: 'team_name' },
+  { label: '团队联系电话', model: 'team_phone.phone' },
+  { label: '所在单位全称', model: 'company', isSearch: true },
+  {
+    label: '审核状态',
+    model: 'status',
+    type: 'select',
+    format: (i) => {
+      let data = statusList.value.find((r) => r.dict_value == i);
+      if (data) return data.dict_label;
+    },
+    isSearch: true,
+  },
+]);
+// 操作
+let opera: Ref<any[]> = ref([
+  { label: '详情', method: 'view' },
+  { label: '审核', method: 'exam', type: 'warning', display: (i) => i.status == '0' },
+  { label: '删除', method: 'del', type: 'danger', confirm: true },
+]);
+// 多选
+let selected: Ref<any[]> = ref([]);
+// 总数
+let total: Ref<number> = ref(0);
+let skip = 0;
+let limit: number = proxy.$limit;
+// 查询数据
+let searchForm: Ref<{}> = ref({});
+// 弹框
+const dialog: Ref<{ type: string; show: boolean; title: string }> = ref({ type: '1', show: false, title: '信息管理' });
+// 审核表单
+let form: Ref<{}> = ref({});
+// 必填项
+const rules = reactive<FormRules>({
+  status: [{ required: true, message: '请选择审核状态', trigger: 'change' }],
+  remark: [{ required: true, message: '请输入审核意见', trigger: 'blur' }],
+});
+// 表单
+let infoFields: Ref<any[]> = ref([
+  { label: '审核状态', model: 'status', type: 'select' },
+  { label: '审核意见', model: 'remark', type: 'textarea' },
+]);
+// 角色
+let roleInfo: Ref<{ _id: string }> = ref({ _id: '' });
+// 状态
+let statusList: Ref<any[]> = ref([]);
+
+onMounted(async () => {
+  await searchOther();
+  await search({ skip, limit });
+});
+// 查询
+const search = async (e: { skip: number; limit: number }) => {
+  const { skip, limit } = e;
+  let info = { limit: limit, skip: skip, ...searchForm.value };
+  const res: IQueryResult = await userStudioApply.query(info);
+  tableData.value = res.data as any[];
+  total.value = res.total;
+};
+// 查询
+const partSearch = (form: { [x: string]: any }) => {
+  searchForm.value = form;
+  search({ skip, limit });
+};
+
+// 修改
+const toView = async (data: { _id: string }) => {
+  router.push({ path: '/center/users/scientist/info', query: { id: data._id } });
+};
+// 审核
+const toExam = (data: object) => {
+  console.log(data);
+
+  form.value = data;
+  dialog.value = { title: '信息管理', show: true, type: '1' };
+};
+// 审核保存
+const toSave = async (data: { _id: string; status: string }) => {
+  let obj = { _id: data._id, status: data.status };
+  let res: IQueryResult = await userStudioApply.create(obj);
+  if (obj.status == '1') updateRole(data);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '维护信息成功' });
+    createMess(data);
+  } else ElMessage({ type: 'warning', message: `${res.errmsg}` });
+};
+// // 分配角色
+const updateRole = async (e) => {
+  let userInfo = await users.fetch(e.unit_id);
+  if (userInfo.errcode == 0) {
+    let object = {
+      _id: userInfo.data._id,
+      role: [...userInfo.data.role, roleInfo.value._id],
+      account: userInfo.data.account,
+      unit_name: userInfo.data.unit_name,
+    };
+    let res = await users.update(object);
+  }
+};
+// // 发送系统消息
+const createMess = async (data) => {
+  let user = store.state.user;
+  let res = await users.fetch(data.unit_id);
+  if (res.errcode == 0) {
+    let obj = {
+      user_id: user._id,
+      title: '审核通知',
+      send_time: moment().format('YYYY-MM-DD HH:mm:ss'),
+      type: '3',
+      user: [{ id: data.unit_id, company: data.company, phone: data.phone }],
+      content: '您好,权限申请,' + `${data.status == '1' ? '已通过审核' : '未通过审核'}` + ',原因:' + data.remark,
+    };
+    let arr = await message.create(obj);
+    if (arr.errcode == 0) {
+      ElMessage({ type: 'success', message: '系统信息发送成功' });
+      handleClose();
+    }
+  }
+};
+// 删除
+const toDel = async (data: { _id: string }) => {
+  const res: IQueryResult = await userStudioApply.del(data._id);
+  if (res.errcode == 0) {
+    ElMessage({ type: 'success', message: '删除成功' });
+    search({ skip, limit });
+  }
+};
+
+// 关闭弹窗
+const handleClose = () => {
+  form.value = {};
+  search({ skip, limit });
+  dialog.value = { title: '信息管理', show: false, type: '' };
+};
+// 选择
+const handleSelect = () => {};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---审核状态
+  const p1: IQueryResult = await dictData.query({ dict_type: 'studio_status' });
+  statusList.value = p1.data as [];
+  // 角色
+  const p2 = await role.query({ code: 'studio-users', account_type: '3', status: 'Y' });
+  roleInfo.value = p2.data[0];
+};
+</script>
+<style scoped>
+.main .one {
+  margin: 0 0 10px 0;
+}
+</style>

+ 102 - 0
src/views/center/users/scientist/info.vue

@@ -0,0 +1,102 @@
+<template>
+  <div id="info">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one">
+          <component :is="partsSearch" :is_back="true" @toBack="toBack()"></component>
+        </el-col>
+        <el-col :span="24" class="two">
+          <component
+            :is="scientistInfo"
+            :info="info"
+            :educationList="educationList"
+            :degreeList="degreeList"
+            :fieldList="fieldList"
+            :isjobList="isjobList"
+            :zcList="zcList"
+          ></component>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import partsSearch from '@/components/c-search.vue';
+import scientistInfo from '@/components/common/scientist/info.vue';
+import { useRouter, useRoute } from 'vue-router';
+import type { Ref } from 'vue';
+import { ref, toRefs, onMounted } from 'vue';
+import { UserStudioApplyStore } from '@common/src/stores/studio/role/userStudioApply'; // 列表
+import { DictDataStore } from '@common/src/stores/users/sysdictdata'; // 字典表
+import type { IQueryResult } from '@/util/types.util';
+const userStudioApply = UserStudioApplyStore();
+const dictData = DictDataStore();
+let route = useRoute();
+let info: Ref<{}> = ref({});
+// 行业领域
+let fieldList: Ref<any[]> = ref([]);
+// 是否在职人员
+let isjobList: Ref<any[]> = ref([]);
+
+let zcList: Ref<any[]> = ref([]);
+// 学历
+let educationList: Ref<any[]> = ref([]);
+// 学位
+let degreeList: Ref<any[]> = ref([]);
+
+onMounted(async () => {
+  await searchOther();
+  await search();
+});
+const search = async () => {
+  if (route.query.id) {
+    let id = route.query.id;
+    const res: IQueryResult = await userStudioApply.fetch(id);
+    let data: { is_job: any; zc: any; fields: any } = res.data as { is_job: any; zc: any; fields: any };
+    data.is_job = searchDicts(data.is_job, 'isjobList');
+    data.zc = searchDicts(data.zc, 'zcList');
+    if (data.fields && data.fields.length > 0) data.fields = searchDict(data.fields, 'fieldList');
+    info.value = res.data;
+  }
+};
+const searchDict = (e, type) => {
+  let data = [];
+  if (e && e.length > 0) {
+    for (const val of e) {
+      let value = [type].find((r) => r.dict_value == val);
+      if (value) data.push(value.dict_label);
+    }
+    if (data && data.length > 0) return data.toString();
+  }
+};
+
+const searchDicts = (e, type) => {
+  let data = [type].find((r) => r.dict_value == e);
+  if (data && data._id) return data.dict_label;
+};
+const toBack = () => {
+  window.history.go(-1);
+};
+// 查询其他信息
+const searchOther = async () => {
+  // 字典表---行业领域
+  const p1: IQueryResult = await dictData.query({ dict_type: 'studio_field' });
+  fieldList.value = p1.data as [];
+  // 字典表---是否为在职人员
+  const p2: IQueryResult = await dictData.query({ dict_type: 'studio_scientist_is_job' });
+  isjobList.value = p2.data as [];
+  // 字典表---专业技术职称
+  const p3: IQueryResult = await dictData.query({ dict_type: 's-builddesire-zc' });
+  let data: any = p3.data as [];
+  data.filter((i) => i.dict_value != '0');
+  zcList.value = data;
+  // 字典表---学历
+  const p4: IQueryResult = await dictData.query({ dict_type: 'education' });
+  educationList.value = p4.data as [];
+  // 字典表---学历
+  const p5: IQueryResult = await dictData.query({ dict_type: 'degree' });
+  degreeList.value = p5.data as [];
+};
+</script>
+<style scoped></style>

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

@@ -7,4 +7,5 @@
 </template>
 
 <script setup lang="ts"></script>
+
 <style scoped></style>

+ 11 - 1
vite.config.ts

@@ -12,7 +12,17 @@ export default defineConfig({
         target: 'http://basic.waityou24.cn',
       },
       '/jcyjdtglpt/v1/api': {
-        target: 'http://basic.waityou24.cn',
+        target: 'http://192.168.1.113:13010',
+        changeOrigin: true,
+        ws: false,
+      },
+      '/semail/api': {
+        target: 'http://192.168.1.113:16001',
+        changeOrigin: true,
+        ws: false,
+      },
+      '/studioadmin/api': {
+        target: 'http://192.168.1.113:16001',
         changeOrigin: true,
         ws: false,
       },