zhouwx
2026-06-12 5d0e4bcaf5d44a9ce2ff196e38c77676aa8ea880
中科大支线—添加菜单、角色
7 files added
18 files modified
30 files renamed
3134 ■■■■ changed files
package.json 7 ●●●● patch | view | raw | blame | history
src/api/login.js 2 ●●● patch | view | raw | blame | history
src/api/menu.js 4 ●●●● patch | view | raw | blame | history
src/layout/components/Sidebar/Logo.vue 22 ●●●● patch | view | raw | blame | history
src/layout/components/Sidebar/SidebarItem.vue 181 ●●●● patch | view | raw | blame | history
src/layout/components/Sidebar/index.vue 45 ●●●●● patch | view | raw | blame | history
src/permission.js 92 ●●●●● patch | view | raw | blame | history
src/router/index.js 643 ●●●●● patch | view | raw | blame | history
src/store/modules/dict.js 98 ●●●● patch | view | raw | blame | history
src/store/modules/permission.js 30 ●●●●● patch | view | raw | blame | history
src/store/modules/user.js 15 ●●●●● patch | view | raw | blame | history
src/utils/dict.js 2 ●●● patch | view | raw | blame | history
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/components/productTable.vue 2 ●●● patch | view | raw | blame | history
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/components/rawTable.vue 2 ●●● patch | view | raw | blame | history
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/avoidWarningMenu/avoidWarning/components/avoidDialog.vue patch | view | raw | blame | history
src/views/hazardousChemicals/avoidWarningMenu/avoidWarning/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/basicInfonMenu/basicInfon/components/basicDialog.vue patch | view | raw | blame | history
src/views/hazardousChemicals/basicInfonMenu/basicInfon/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/proDetail.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/proWarehouseRecord.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/productTable.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawDetail.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawTable.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawWarehouseRecord.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/viewQR.vue patch | view | raw | blame | history
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/finishedBasicInfoMenu/finishedBasicInfo/components/finishedBasicDialog.vue patch | view | raw | blame | history
src/views/hazardousChemicals/finishedBasicInfoMenu/finishedBasicInfo/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/homePageMenu/homePage/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/overdueWarningMenu/overdueWarning/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/approval/components/editDialog.vue 238 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/approval/index.vue 262 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/record/components/editDialog.vue 201 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/record/index.vue 266 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/subscribe/components/editDialog.vue 335 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/subscribeApplyManage/subscribe/index.vue 263 ●●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/systemManage/config/components/mapLocation.vue 24 ●●●● patch | view | raw | blame | history
src/views/hazardousChemicals/traceableQueryMenu/traceableQuery/index.vue 2 ●●● patch | view | raw | blame | history
src/views/hazardousChemicals/useCountMenu/useCount/index.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/addProDialog.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/addRawDialog.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/printCode.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/printCodeMore.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/proDetail.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/productTable.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/rawDetail.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/rawTable.vue patch | view | raw | blame | history
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/index.vue patch | view | raw | blame | history
src/views/homePage.vue 179 ●●●●● patch | view | raw | blame | history
src/views/index.vue 101 ●●●●● patch | view | raw | blame | history
src/views/system/menu/index.vue 2 ●●● patch | view | raw | blame | history
src/views/system/role/authUser.vue 60 ●●●● patch | view | raw | blame | history
src/views/system/role/index.vue 14 ●●●● patch | view | raw | blame | history
src/views/system/role/selectUser.vue 42 ●●●● patch | view | raw | blame | history
package.json
@@ -47,7 +47,12 @@
    "vue-router": "4.1.4",
    "vue3-count-to": "^1.1.2",
    "vue3-json-excel": "^1.0.10-alpha",
    "wangeditor5-for-vue3": "^0.1.0"
    "wangeditor5-for-vue3": "^0.1.0",
    "docx": "^9.5.1",
    "docx-preview": "^0.3.5",
    "docx-templates": "^4.14.1",
    "docxtemplater": "^3.65.1",
    "docxtemplater-image-module-free": "^1.1.1"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "3.1.0",
src/api/login.js
@@ -43,7 +43,7 @@
// 获取用户详细信息
export function getInfo() {
  return request({
    url: '/getInfo',
    url: '/system/account/getInfo',
    method: 'get'
  })
}
src/api/menu.js
@@ -3,7 +3,7 @@
// 获取路由
export const getRouters = () => {
  return request({
    url: '/getRouters',
    url: '/system/account/getRouters',
    method: 'get'
  })
}
}
src/layout/components/Sidebar/Logo.vue
@@ -1,11 +1,11 @@
<template>
  <div class="sidebar-logo-container" :class="{ 'collapse': collapse }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
    <transition name="sidebarLogoFade">
      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" :to="firstPage">
      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" :to="permissionStore.indexPage">
        <img v-if="logoImg" :src="logoImg" class="sidebar-logo" />
        <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
      </router-link>
      <router-link v-else key="expand" class="sidebar-logo-link" :to="firstPage">
      <router-link v-else key="expand" class="sidebar-logo-link" :to="permissionStore.indexPage">
        <img v-if="logoImg" :src="logoImg" class="sidebar-logo" />
        <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
      </router-link>
@@ -17,6 +17,7 @@
import variables from '@/assets/styles/variables.module.scss'
import logo from '@/assets/logo/logo2.png'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import {onMounted, ref} from "vue";
import Cookies from "js-cookie";
defineProps({
@@ -28,14 +29,14 @@
const logoImg = ref('')
const firstPage = ref("");
onMounted(() => {
    const routers = JSON.parse(Cookies.get('routers')) ;
    if(routers[0].children){
      firstPage.value = routers[0].path + '/' + routers[0].children[0].path;
    }else {
      firstPage.value = routers[0].path;
    }
  console.log(routers,'firstPage.value')
    // const routers = JSON.parse(Cookies.get('routers')) ;
    // if(routers[0].children){
    //   firstPage.value = routers[0].path + '/' + routers[0].children[0].path;
    // }else {
    //   firstPage.value = routers[0].path;
    // }
  //
  // console.log(routers,'firstPage.value')
    console.log(firstPage.value,'firstPage.value')
    if(Cookies.get('configInfo')){
      const config = JSON.parse(Cookies.get('configInfo'))
@@ -50,6 +51,7 @@
});
const permissionStore = usePermissionStore()
const title = import.meta.env.VITE_APP_TITLE;
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
src/layout/components/Sidebar/SidebarItem.vue
@@ -1,33 +1,147 @@
<template>
<!--  <div v-if="!item.hidden">-->
<!--    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">-->
<!--      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">-->
<!--        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">-->
<!--          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>-->
<!--          <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>-->
<!--        </el-menu-item>-->
<!--      </app-link>-->
<!--    </template>-->
<!--    <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>-->
<!--      <template v-if="item.meta" #title>-->
<!--        <svg-icon :icon-class="item.meta && item.meta.icon" />-->
<!--        <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>-->
<!--      </template>-->
<!--      <sidebar-item-->
<!--        v-for="child in item.children"-->
<!--        :key="child.path"-->
<!--        :is-nest="true"-->
<!--        :item="child"-->
<!--        :base-path="resolvePath(child.path)"-->
<!--        class="nest-menu"-->
<!--      />-->
<!--    </el-sub-menu>-->
<!--  </div>-->
  <div v-if="!item.hidden">
    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
          <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
    <!-- 优化条件:优先判定【仅有1个可见子菜单】,直接走一级菜单渲染 -->
    <template v-if="calcSingleChild(item) && item.path != '/system'">
      <app-link v-if="onlyOneChild?.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
        <el-menu-item
            :index="resolvePath(onlyOneChild.path)"
            :class="{ 'submenu-title-noDropdown': !isNest }"
        >
          <svg-icon :icon-class="item.meta?.icon || onlyOneChild.meta?.icon"/>
          <template #title>
            <span class="menu-title" :title="hasTitle(item.meta?.title || onlyOneChild.meta?.title)">
              {{ item.meta?.title || onlyOneChild.meta?.title }}
            </span>
          </template>
        </el-menu-item>
      </app-link>
    </template>
    <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
    <!-- 多个子菜单 / 强制目录:正常展开二级菜单 -->
    <el-sub-menu
        v-else
        ref="subMenu"
        :index="resolvePath(item.path)"
        popper-append-to-body
    >
      <template v-if="item.meta" #title>
        <svg-icon :icon-class="item.meta && item.meta.icon" />
        <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
      </template>
      <sidebar-item
        v-for="child in item.children"
        :key="child.path"
        :is-nest="true"
        :item="child"
        :base-path="resolvePath(child.path)"
        class="nest-menu"
          v-for="child in item.children"
          :key="child.path"
          :is-nest="true"
          :item="child"
          :base-path="resolvePath(child.path)"
          class="nest-menu"
      />
    </el-sub-menu>
  </div>
</template>
<!--<script setup>-->
<!--import { isExternal } from '@/utils/validate'-->
<!--import AppLink from './Link'-->
<!--import { getNormalPath } from '@/utils/ruoyi'-->
<!--const props = defineProps({-->
<!--  // route object-->
<!--  item: {-->
<!--    type: Object,-->
<!--    required: true-->
<!--  },-->
<!--  isNest: {-->
<!--    type: Boolean,-->
<!--    default: false-->
<!--  },-->
<!--  basePath: {-->
<!--    type: String,-->
<!--    default: ''-->
<!--  }-->
<!--})-->
<!--const onlyOneChild = ref({});-->
<!--function hasOneShowingChild(children = [], parent) {-->
<!--  if (!children) {-->
<!--    children = [];-->
<!--  }-->
<!--  const showingChildren = children.filter(item => {-->
<!--    if (item.hidden) {-->
<!--      return false-->
<!--    } else {-->
<!--      // Temp set(will be used if only has one showing child)-->
<!--      onlyOneChild.value = item-->
<!--      return true-->
<!--    }-->
<!--  })-->
<!--  // When there is only one child router, the child router is displayed by default-->
<!--  if (showingChildren.length === 1) {-->
<!--    return true-->
<!--  }-->
<!--  // Show parent if there are no child router to display-->
<!--  if (showingChildren.length === 0) {-->
<!--    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }-->
<!--    return true-->
<!--  }-->
<!--  return false-->
<!--};-->
<!--function resolvePath(routePath, routeQuery) {-->
<!--  if (isExternal(routePath)) {-->
<!--    return routePath-->
<!--  }-->
<!--  if (isExternal(props.basePath)) {-->
<!--    return props.basePath-->
<!--  }-->
<!--  if (routeQuery) {-->
<!--    let query = JSON.parse(routeQuery);-->
<!--    return { path: getNormalPath(props.basePath + '/' + routePath), query: query }-->
<!--  }-->
<!--  return getNormalPath(props.basePath + '/' + routePath)-->
<!--}-->
<!--function hasTitle(title){-->
<!--  if (title.length > 5) {-->
<!--    return title;-->
<!--  } else {-->
<!--    return "";-->
<!--  }-->
<!--}-->
<!--</script>-->
<script setup>
import { ref } from 'vue'
import { isExternal } from '@/utils/validate'
import AppLink from './Link'
import { getNormalPath } from '@/utils/ruoyi'
@@ -48,35 +162,44 @@
  }
})
const onlyOneChild = ref({});
// 修复:初始化为 null,而非空对象
const onlyOneChild = ref(null);
/**
 * 修复版:判断是否只有一个可见子菜单(核心改动)
 */
function hasOneShowingChild(children = [], parent) {
  if (!children) {
    children = [];
  }
  const showingChildren = children.filter(item => {
    if (item.hidden) {
      return false
    } else {
      // Temp set(will be used if only has one showing child)
      onlyOneChild.value = item
      return true
    }
  })
  if (!children) children = []
  // When there is only one child router, the child router is displayed by default
  // 1. 先统一过滤出【未隐藏】的子菜单
  const showingChildren = children.filter(item => !item.hidden)
  // 2. 仅有 1 个可见子菜单:赋值 + 标记无下级,适配原有判断
  if (showingChildren.length === 1) {
    const child = showingChildren[0]
    // 标记:当前子菜单没有可展示的下级(关键,适配原模板逻辑)
    onlyOneChild.value = { ...child, noShowingChildren: true }
    return true
  }
  // Show parent if there are no child router to display
  // 3. 没有可见子菜单
  if (showingChildren.length === 0) {
    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
    return true
  }
  // 4. 多个子菜单
  onlyOneChild.value = null
  return false
};
}
/**
 * 新增统一判断方法:简化模板条件,命中「单菜单合并逻辑」
 */
function calcSingleChild(item) {
// 存在子菜单 且 仅有一个可见子菜单
  return hasOneShowingChild(item.children, item)
}
function resolvePath(routePath, routeQuery) {
  if (isExternal(routePath)) {
@@ -93,7 +216,7 @@
}
function hasTitle(title){
  if (title.length > 5) {
  if (title && title.length > 5) {
    return title;
  } else {
    return "";
src/layout/components/Sidebar/index.vue
@@ -3,20 +3,20 @@
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
        :text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
        :unique-opened="true"
        :active-text-color="theme"
        :collapse-transition="false"
        mode="vertical"
          :default-active="activeMenu"
          :collapse="isCollapse"
          :background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
          :text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
          :unique-opened="true"
          :active-text-color="theme"
          :collapse-transition="false"
          mode="vertical"
      >
        <sidebar-item
          v-for="(route, index) in sidebarRouters"
          :key="route.path + index"
          :item="route"
          :base-path="route.path"
            v-for="(route, index) in sidebarRouters"
            :key="route.path + index"
            :item="route"
            :base-path="route.path"
        />
      </el-menu>
    </el-scrollbar>
@@ -30,30 +30,13 @@
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import {onMounted, ref} from "vue";
import Cookies from "js-cookie";
import menu from "./menu"
const route = useRoute();
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
// const sidebarRouters =  computed(() => permissionStore.sidebarRouters);
const sidebarRouters = ref([])
onMounted(() => {
    sidebarRouters.value = JSON.parse(Cookies.get('routers'))
});
// const getMenu = () => {
//     const userInfo = JSON.parse(Cookies.get('userInfo'))
//     if(userInfo.identity === 0) {
//
//         sidebarRouters.value =  menu.adminMenu
//         Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//     }
// }
const sidebarRouters =  computed(() => permissionStore.sidebarRouters);
const showLogo = computed(() => settingsStore.sidebarLogo);
const sideTheme = computed(() => settingsStore.sideTheme);
const theme = computed(() => settingsStore.theme);
src/permission.js
@@ -11,49 +11,81 @@
NProgress.configure({ showSpinner: false });
const whiteList = ['/homePage'];
const whiteList = ['/homePage','/fillForm','/checkProgress','/certPdf'];
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    to.meta.title && useSettingsStore().setTitle(to.meta.title)
    /* has token*/
    // if (to.path === '/homePage') {
    //   next({ path: '/' })
    //   NProgress.done()
    // } else {
    //   if (useUserStore().roles.length === 0) {
    //     isRelogin.show = true
    //     // 判断当前用户是否已拉取完user_info信息
    //     useUserStore().getInfo().then(() => {
    //       isRelogin.show = false
    //       usePermissionStore().generateRoutes().then(accessRoutes => {
    //         // 根据roles权限生成可访问的路由表
    //         accessRoutes.forEach(route => {
    //           if (!isHttp(route.path)) {
    //             router.addRoute(route) // 动态添加可访问路由表
    //           }
    //         })
    //         next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
    //       })
    //     }).catch(err => {
    //       useUserStore().logOut().then(() => {
    //         ElMessage.error(err)
    //         next({ path: '/' })
    //       })
    //     })
    //   } else {
    if (to.path === '/homePage') {
      next({ path: '/' })
      NProgress.done()
    } else if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      if (useUserStore().roles.length === 0) {
        isRelogin.show = true
        // 判断当前用户是否已拉取完user_info信息
        useUserStore().getInfo().then(() => {
          isRelogin.show = false
          usePermissionStore().generateRoutes().then(accessRoutes => {
            // 根据roles权限生成可访问的路由表
            accessRoutes.forEach(route => {
              if (!isHttp(route.path)) {
                router.addRoute(route) // 动态添加可访问路由表
              }
            })
            console.log(to.fullPath,"to.fullPath")
            if (to.fullPath == '/index') {
              // 当登录之后,直接通过ip地址和端口号访问时,跳转到第一个路由页面indexPage
              let pathIndex = ''
              if(accessRoutes.length == 0){
                ElMessage.warning('当前用户未配置菜单权限,请联系管理员!')
                next(`/index`)
                return
              }
              const firstRoute = accessRoutes[0]
              // 👇 新增判断:是否有子菜单
              if (firstRoute.children && firstRoute.children.length > 0) {
                // 有二级菜单,走原来的逻辑
                if (firstRoute.path == '/') {
                  pathIndex = firstRoute.path + firstRoute.children[0].path
                } else {
                  pathIndex = firstRoute.path + '/' + firstRoute.children[0].path
                }
              } else {
                // 没有二级菜单,直接用一级路由的path
                pathIndex = firstRoute?.path || '/404'
              }
              next({ path: pathIndex, replace: true }) // hack方法 确保addRoutes已完成
            } else {
              next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
            }
            // next({ ...to, replace: true })
            // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
          useUserStore().logOut().then(() => {
            ElMessage.error(err)
            next({ path: '/' })
          })
        })
      } else {
        next()
    //   }
    // }
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      // next(`/homePage?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      next('/homePage')
      next(`/homePage?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
src/router/index.js
@@ -1,4 +1,395 @@
import { createWebHistory, createRouter } from 'vue-router'
// import { createWebHistory, createRouter } from 'vue-router'
// /* Layout */
// import Layout from '@/layout'
//
// /**
//  * Note: 路由配置项
//  *
//  * hidden: true                     // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
//  * alwaysShow: true                 // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
//  *                                  // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
//  *                                  // 若你想不管路由下面的 children 声明的个数都显示你的根路由
//  *                                  // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
//  * redirect: noRedirect             // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
//  * name:'router-name'               // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
//  * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
//  * roles: ['admin', 'common']       // 访问路由的角色权限
//  * permissions: ['a:a:a', 'b:b:b']  // 访问路由的菜单权限
//  * meta : {
//     noCache: true                   // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
//     title: 'title'                  // 设置该路由在侧边栏和面包屑中展示的名字
//     icon: 'svg-name'                // 设置该路由的图标,对应路径src/assets/icons/svg
//     breadcrumb: false               // 如果设置为false,则不会在breadcrumb面包屑中显示
//     activeMenu: '/system/user'      // 当路由设置了该属性,则会高亮相对应的侧边栏。
//   }
//  */
//
// // 公共路由
// export const constantRoutes = [
//   {
//     path: '/redirect',
//     component: Layout,
//     hidden: true,
//     children: [
//       {
//         path: '/redirect/:path(.*)',
//         component: () => import('@/views/redirect/index.vue')
//       }
//     ]
//   },
//   {
//     path: '/homePage',
//     component: () => import('@/views/homePage'),
//     hidden: true
//   },
//   {
//     path: "/:pathMatch(.*)*",
//     component: () => import('@/views/homePage'),
//     hidden: true
//   },
//   {
//     path: '/401',
//     component: () => import('@/views/error/401'),
//     hidden: true
//   },
//   // {
//   //   path: '/newPage',
//   //   component: Layout,
//   //   redirect: '/newPage',
//   //   children: [
//   //     {
//   //       path: '/newPage',
//   //       component: () => import('@/views/onlineEducation/systemManage/banner/components/newPage.vue'),
//   //       name: 'NewPage',
//   //     }
//   //   ]
//   // },
//   {
//     path: '/warehouseManage',
//     component: Layout,
//     redirect: '/warehouseManage',
//     children: [
//       {
//         path: '/warehouseManage',
//         component: () => import('@/views/hazardousChemicals/warehouseManage/index.vue'),
//         name: 'warehouseManage',
//         meta: { title: '入库管理',icon: 'form',  affix: true }
//       }
//     ]
//   },
//
//   {
//     path: '/electronicWarehouse',
//     component: Layout,
//     redirect: '/electronicWarehouse',
//     children: [
//       {
//         path: '/electronicWarehouse',
//         component: () => import('@/views/hazardousChemicals/electronicWarehouse/index.vue'),
//         name: 'electronicWarehouse',
//         meta: { title: '电子仓库',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/rawDetail',
//     component: Layout,
//     redirect: '/rawDetail',
//     children: [
//       {
//         path: '/rawDetail',
//         component: () => import('@/views/hazardousChemicals/electronicWarehouse/components/rawDetail.vue'),
//         name: 'rawDetail',
//         meta: { title: '危化品详单',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/proDetail',
//     component: Layout,
//     redirect: '/proDetail',
//     children: [
//       {
//         path: '/proDetail',
//         component: () => import('@/views/hazardousChemicals/electronicWarehouse/components/proDetail.vue'),
//         name: 'proDetail',
//         meta: { title: '成品详单',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/whRawDetail',
//     component: Layout,
//     redirect: '/whRawDetail',
//     children: [
//       {
//         path: '/whRawDetail',
//         component: () => import('@/views/hazardousChemicals/warehouseManage/components/rawDetail.vue'),
//         name: 'whRawDetail',
//         meta: { title: '危化品详情',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/whProDetail',
//     component: Layout,
//     redirect: '/whProDetail',
//     children: [
//       {
//         path: '/whProDetail',
//         component: () => import('@/views/hazardousChemicals/warehouseManage/components/proDetail.vue'),
//         name: 'whProDetail',
//         meta: { title: '成品详情',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/accessRecords',
//     component: Layout,
//     redirect: '/accessRecords',
//     children: [
//       {
//         path: '/accessRecords',
//         component: () => import('@/views/hazardousChemicals/accessRecords/index.vue'),
//         name: 'accessRecords',
//         meta: { title: '取用记录',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/traceableQuery',
//     component: Layout,
//     redirect: '/traceableQuery',
//     children: [
//       {
//         path: '/traceableQuery',
//         component: () => import('@/views/hazardousChemicals/traceableQuery/index.vue'),
//         name: 'traceableQuery',
//         meta: { title: '溯源查询',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/overdueWarning',
//     component: Layout,
//     redirect: '/overdueWarning',
//     children: [
//       {
//         path: '/overdueWarning',
//         component: () => import('@/views/hazardousChemicals/overdueWarning/index.vue'),
//         name: 'overdueWarning',
//         meta: { title: '超期预警',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/avoidWarning',
//     component: Layout,
//     redirect: '/avoidWarning',
//     children: [
//       {
//         path: '/avoidWarning',
//         component: () => import('@/views/hazardousChemicals/avoidWarning/index.vue'),
//         name: 'avoidWarning',
//         meta: { title: '危化品相忌报警',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/useCount',
//     component: Layout,
//     redirect: '/useCount',
//     children: [
//       {
//         path: '/useCount',
//         component: () => import('@/views/hazardousChemicals/useCount/index.vue'),
//         name: 'useCount',
//         meta: { title: '用量统计',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/basicInfo',
//     component: Layout,
//     redirect: '/basicInfo',
//     children: [
//       {
//         path: '/basicInfo',
//         component: () => import('@/views/hazardousChemicals/basicInfon/index.vue'),
//         name: 'basicInfo',
//         meta: { title: '危化品基础信息',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/finishedBasicInfo',
//     component: Layout,
//     redirect: '/finishedBasicInfo',
//     children: [
//       {
//         path: '/finishedBasicInfo',
//         component: () => import('@/views/hazardousChemicals/finishedBasicInfo/index.vue'),
//         name: 'finishedBasicInfo',
//         meta: { title: '成品基础信息',icon: 'form',  affix: true }
//       }
//     ]
//   },
//   {
//     path: '/homePageIndex',
//     component: Layout,
//     redirect: '/homePageIndex',
//     meta: { title: '首页'},
//     children: [
//       {
//         path: 'homePageIndex',
//         component: () => import('@/views/hazardousChemicals/homePage/index.vue'),
//         name: 'homePageIndex',
//         meta: { title: '工作台',icon: 'form'}
//       },
//     ]
//   },
//   {
//     path: '/systemManage',
//     component: Layout,
//     redirect: '/systemManage/warehouse',
//     meta: { title: '系统管理'},
//     children: [
//       {
//         path: 'warehouse',
//         component: () => import('@/views/hazardousChemicals/systemManage/warehouse/index.vue'),
//         name: 'warehouse',
//         meta: { title: '仓库管理',icon: 'form'}
//       },
//       {
//         path: 'company',
//         component: () => import('@/views/hazardousChemicals/systemManage/company/index.vue'),
//         name: 'company',
//         meta: { title: '企业管理',icon: 'form'}
//       },
//       {
//         path: 'user',
//         component: () => import('@/views/hazardousChemicals/systemManage/user/index.vue'),
//         name: 'user',
//         meta: { title: '企业用户管理',icon: 'form'}
//       },
//       {
//         path: 'department',
//         component: () => import('@/views/hazardousChemicals/systemManage/department/index.vue'),
//         name: 'department',
//         meta: { title: '部门管理',icon: 'form'}
//       },
//       {
//         path: 'config',
//         component: () => import('@/views/hazardousChemicals/systemManage/config/index.vue'),
//         name: 'config',
//         meta: { title: '企业配置',icon: 'form'}
//       },
//       {
//         path: 'characteristic',
//         component: () => import('@/views/hazardousChemicals/systemManage/characteristic/index.vue'),
//         name: 'characteristic',
//         meta: { title: '危化品特性管理',icon: 'form'}
//       },
//       {
//         path: 'classifyInfoTable',
//         component: () => import('@/views/hazardousChemicals/systemManage/classifyInfoTable/index.vue'),
//         name: 'classifyInfoTable',
//         meta: { title: '危化品分类信息表',icon: 'form'}
//       },
//
//     ]
//   },
//
// ]
//
// // 动态路由,基于用户权限动态去加载
// export const dynamicRoutes = [
//   {
//     path: '/system/user-auth',
//     component: Layout,
//     hidden: true,
//     permissions: ['system:user:edit'],
//     children: [
//       {
//         path: 'role/:userId(\\d+)',
//         component: () => import('@/views/system/user/authRole'),
//         name: 'AuthRole',
//         meta: { title: '分配角色', activeMenu: '/system/user' }
//       }
//     ]
//   },
//   {
//     path: '/system/role-auth',
//     component: Layout,
//     hidden: true,
//     permissions: ['system:role:edit'],
//     children: [
//       {
//         path: 'user/:roleId(\\d+)',
//         component: () => import('@/views/system/role/authUser'),
//         name: 'AuthUser',
//         meta: { title: '分配用户', activeMenu: '/system/role' }
//       }
//     ]
//   },
//   {
//     path: '/system/dict-data',
//     component: Layout,
//     hidden: true,
//     permissions: ['system:dict:list'],
//     children: [
//       {
//         path: 'index/:dictId(\\d+)',
//         component: () => import('@/views/system/dict/data'),
//         name: 'Data',
//         meta: { title: '字典数据', activeMenu: '/system/dict' }
//       }
//     ]
//   },
//   {
//     path: '/monitor/job-log',
//     component: Layout,
//     hidden: true,
//     permissions: ['monitor:job:list'],
//     children: [
//       {
//         path: 'index/:jobId(\\d+)',
//         component: () => import('@/views/monitor/job/log'),
//         name: 'JobLog',
//         meta: { title: '调度日志', activeMenu: '/monitor/job' }
//       }
//     ]
//   },
//   {
//     path: '/tool/gen-edit',
//     component: Layout,
//     hidden: true,
//     permissions: ['tool:gen:edit'],
//     children: [
//       {
//         path: 'index/:tableId(\\d+)',
//         component: () => import('@/views/tool/gen/editTable'),
//         name: 'GenEdit',
//         meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
//       }
//     ]
//   }
// ]
//
// const router = createRouter({
//   history: createWebHistory(),
//   routes: constantRoutes,
//   scrollBehavior(to, from, savedPosition) {
//     if (savedPosition) {
//       return savedPosition
//     } else {
//       return { top: 0 }
//     }
//   },
// });
//
// export default router;
import { createWebHistory, createWebHashHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
@@ -44,7 +435,7 @@
  },
  {
    path: "/:pathMatch(.*)*",
    component: () => import('@/views/homePage'),
    component: () => import('@/views/error/404'),
    hidden: true
  },
  {
@@ -52,254 +443,35 @@
    component: () => import('@/views/error/401'),
    hidden: true
  },
  // {
  //   path: '/newPage',
  //   component: Layout,
  //   redirect: '/newPage',
  //   children: [
  //     {
  //       path: '/newPage',
  //       component: () => import('@/views/onlineEducation/systemManage/banner/components/newPage.vue'),
  //       name: 'NewPage',
  //     }
  //   ]
  // },
  {
    path: '/warehouseManage',
    component: Layout,
    redirect: '/warehouseManage',
    children: [
      {
        path: '/warehouseManage',
        component: () => import('@/views/hazardousChemicals/warehouseManage/index.vue'),
        name: 'warehouseManage',
        meta: { title: '入库管理',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/electronicWarehouse',
    path: '',
    component: Layout,
    redirect: '/electronicWarehouse',
    redirect: '/index',
    hidden: true,
    children: [
      {
        path: '/electronicWarehouse',
        component: () => import('@/views/hazardousChemicals/electronicWarehouse/index.vue'),
        name: 'electronicWarehouse',
        meta: { title: '电子仓库',icon: 'form',  affix: true }
        path: '/index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
      }
    ]
  },
  {
    path: '/rawDetail',
    path: '/user',
    component: Layout,
    redirect: '/rawDetail',
    hidden: true,
    redirect: 'noredirect',
    children: [
      {
        path: '/rawDetail',
        component: () => import('@/views/hazardousChemicals/electronicWarehouse/components/rawDetail.vue'),
        name: 'rawDetail',
        meta: { title: '危化品详单',icon: 'form',  affix: true }
        path: 'profile',
        component: () => import('@/views/system/user/profile/index'),
        name: 'Profile',
        meta: { title: '个人中心', icon: 'user' }
      }
    ]
  },
  {
    path: '/proDetail',
    component: Layout,
    redirect: '/proDetail',
    children: [
      {
        path: '/proDetail',
        component: () => import('@/views/hazardousChemicals/electronicWarehouse/components/proDetail.vue'),
        name: 'proDetail',
        meta: { title: '成品详单',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/whRawDetail',
    component: Layout,
    redirect: '/whRawDetail',
    children: [
      {
        path: '/whRawDetail',
        component: () => import('@/views/hazardousChemicals/warehouseManage/components/rawDetail.vue'),
        name: 'whRawDetail',
        meta: { title: '危化品详情',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/whProDetail',
    component: Layout,
    redirect: '/whProDetail',
    children: [
      {
        path: '/whProDetail',
        component: () => import('@/views/hazardousChemicals/warehouseManage/components/proDetail.vue'),
        name: 'whProDetail',
        meta: { title: '成品详情',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/accessRecords',
    component: Layout,
    redirect: '/accessRecords',
    children: [
      {
        path: '/accessRecords',
        component: () => import('@/views/hazardousChemicals/accessRecords/index.vue'),
        name: 'accessRecords',
        meta: { title: '取用记录',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/traceableQuery',
    component: Layout,
    redirect: '/traceableQuery',
    children: [
      {
        path: '/traceableQuery',
        component: () => import('@/views/hazardousChemicals/traceableQuery/index.vue'),
        name: 'traceableQuery',
        meta: { title: '溯源查询',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/overdueWarning',
    component: Layout,
    redirect: '/overdueWarning',
    children: [
      {
        path: '/overdueWarning',
        component: () => import('@/views/hazardousChemicals/overdueWarning/index.vue'),
        name: 'overdueWarning',
        meta: { title: '超期预警',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/avoidWarning',
    component: Layout,
    redirect: '/avoidWarning',
    children: [
      {
        path: '/avoidWarning',
        component: () => import('@/views/hazardousChemicals/avoidWarning/index.vue'),
        name: 'avoidWarning',
        meta: { title: '危化品相忌报警',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/useCount',
    component: Layout,
    redirect: '/useCount',
    children: [
      {
        path: '/useCount',
        component: () => import('@/views/hazardousChemicals/useCount/index.vue'),
        name: 'useCount',
        meta: { title: '用量统计',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/basicInfo',
    component: Layout,
    redirect: '/basicInfo',
    children: [
      {
        path: '/basicInfo',
        component: () => import('@/views/hazardousChemicals/basicInfon/index.vue'),
        name: 'basicInfo',
        meta: { title: '危化品基础信息',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/finishedBasicInfo',
    component: Layout,
    redirect: '/finishedBasicInfo',
    children: [
      {
        path: '/finishedBasicInfo',
        component: () => import('@/views/hazardousChemicals/finishedBasicInfo/index.vue'),
        name: 'finishedBasicInfo',
        meta: { title: '成品基础信息',icon: 'form',  affix: true }
      }
    ]
  },
  {
    path: '/homePageIndex',
    component: Layout,
    redirect: '/homePageIndex',
    meta: { title: '首页'},
    children: [
      {
        path: 'homePageIndex',
        component: () => import('@/views/hazardousChemicals/homePage/index.vue'),
        name: 'homePageIndex',
        meta: { title: '工作台',icon: 'form'}
      },
    ]
  },
  {
    path: '/systemManage',
    component: Layout,
    redirect: '/systemManage/warehouse',
    meta: { title: '系统管理'},
    children: [
      {
        path: 'warehouse',
        component: () => import('@/views/hazardousChemicals/systemManage/warehouse/index.vue'),
        name: 'warehouse',
        meta: { title: '仓库管理',icon: 'form'}
      },
      {
        path: 'company',
        component: () => import('@/views/hazardousChemicals/systemManage/company/index.vue'),
        name: 'company',
        meta: { title: '企业管理',icon: 'form'}
      },
      {
        path: 'user',
        component: () => import('@/views/hazardousChemicals/systemManage/user/index.vue'),
        name: 'user',
        meta: { title: '企业用户管理',icon: 'form'}
      },
      {
        path: 'department',
        component: () => import('@/views/hazardousChemicals/systemManage/department/index.vue'),
        name: 'department',
        meta: { title: '部门管理',icon: 'form'}
      },
      {
        path: 'config',
        component: () => import('@/views/hazardousChemicals/systemManage/config/index.vue'),
        name: 'config',
        meta: { title: '企业配置',icon: 'form'}
      },
      {
        path: 'characteristic',
        component: () => import('@/views/hazardousChemicals/systemManage/characteristic/index.vue'),
        name: 'characteristic',
        meta: { title: '危化品特性管理',icon: 'form'}
      },
      {
        path: 'classifyInfoTable',
        component: () => import('@/views/hazardousChemicals/systemManage/classifyInfoTable/index.vue'),
        name: 'classifyInfoTable',
        meta: { title: '危化品分类信息表',icon: 'form'}
      },
    ]
  },
]
// 动态路由,基于用户权限动态去加载
@@ -389,3 +561,4 @@
});
export default router;
src/store/modules/dict.js
@@ -1,57 +1,57 @@
const useDictStore = defineStore(
  'dict',
  {
    state: () => ({
      dict: new Array()
    }),
    actions: {
      // 获取字典
      getDict(_key) {
        if (_key == null && _key == "") {
          return null;
        }
        try {
          for (let i = 0; i < this.dict.length; i++) {
            if (this.dict[i].key == _key) {
              return this.dict[i].value;
            }
    'dict',
    {
      state: () => ({
        dict: new Array()
      }),
      actions: {
        // 获取字典
        getDict(_key) {
          if (_key == null && _key == "") {
            return null;
          }
        } catch (e) {
          return null;
        }
      },
      // 设置字典
      setDict(_key, value) {
        if (_key !== null && _key !== "") {
          this.dict.push({
            key: _key,
            value: value
          });
        }
      },
      // 删除字典
      removeDict(_key) {
        var bln = false;
        try {
          for (let i = 0; i < this.dict.length; i++) {
            if (this.dict[i].key == _key) {
              this.dict.splice(i, 1);
              return true;
          try {
            for (let i = 0; i < this.dict.length; i++) {
              if (this.dict[i].key == _key) {
                return this.dict[i].value;
              }
            }
          } catch (e) {
            return null;
          }
        } catch (e) {
          bln = false;
        },
        // 设置字典
        setDict(_key, value) {
          if (_key !== null && _key !== "") {
            this.dict.push({
              key: _key,
              value: value
            });
          }
        },
        // 删除字典
        removeDict(_key) {
          var bln = false;
          try {
            for (let i = 0; i < this.dict.length; i++) {
              if (this.dict[i].key == _key) {
                this.dict.splice(i, 1);
                return true;
              }
            }
          } catch (e) {
            bln = false;
          }
          return bln;
        },
        // 清空字典
        cleanDict() {
          this.dict = new Array();
        },
        // 初始字典
        initDict() {
        }
        return bln;
      },
      // 清空字典
      cleanDict() {
        this.dict = new Array();
      },
      // 初始字典
      initDict() {
      }
    }
  })
    })
export default useDictStore
src/store/modules/permission.js
@@ -32,6 +32,9 @@
      setSidebarRouters(routes) {
        this.sidebarRouters = routes
      },
      setIndexPages(routes) {
        this.indexPage = routes
      },
      generateRoutes(roles) {
        return new Promise(resolve => {
          // 向后端请求路由数据
@@ -39,15 +42,40 @@
            const sdata = JSON.parse(JSON.stringify(res.data))
            const rdata = JSON.parse(JSON.stringify(res.data))
            const defaultData = JSON.parse(JSON.stringify(res.data))
            let firstPage = ''
            //通过权限返回菜单去避免 如有首页权限 出现//index 情况
            // if (res.data[0].path == '/') {
            //   firstPage = res.data[0].path + res.data[0].children[0].path
            // } else{
            //   firstPage = res.data[0].path + '/' + res.data[0].children[0].path
            // }
            if (res.data && res.data.length > 0 && res.data[0]) {
              const firstMenu = res.data[0];
              // 2. 判断第一个菜单是否有子菜单
              if (firstMenu.children && firstMenu.children.length > 0) {
                // 有子菜单,按原来的逻辑拼接
                if (firstMenu.path === '/') {
                  firstPage = firstMenu.path + firstMenu.children[0].path;
                } else {
                  firstPage = firstMenu.path + '/' + firstMenu.children[0].path;
                }
              } else {
                firstPage = firstMenu.path;
              }
            } else {
              firstPage = '/index';
            }
            const sidebarRoutes = filterAsyncRouter(sdata)
            const rewriteRoutes = filterAsyncRouter(rdata, false, true)
            const defaultRoutes = filterAsyncRouter(defaultData)
            const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
            asyncRoutes.forEach(route => { router.addRoute(route) })
            this.setRoutes(rewriteRoutes)
            // this.setRoutes(rewriteRoutes)
            this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
            // this.setSidebarRouters(sidebarRoutes)
            this.setDefaultRoutes(sidebarRoutes)
            this.setTopbarRoutes(defaultRoutes)
            this.setIndexPages(firstPage)
            resolve(rewriteRoutes)
          })
        })
src/store/modules/user.js
@@ -23,9 +23,8 @@
        const password = userInfo.password
        const code = userInfo.code
        const uuid = userInfo.uuid
        const identity = userInfo.identity
        return new Promise( (resolve, reject) => {
          login(username, password, code, uuid, identity).then( async res => {
          login(username, password, code, uuid).then( async res => {
            setToken(res.data.token)
            if(res.data && res.data.id){
              const info = await getUserById(res.data.id);
@@ -42,6 +41,8 @@
                if(con.code === 200){
                  Cookies.set('configInfo',JSON.stringify(con.data))
                }
              }else {
                Cookies.set('configInfo',null)
              }
            }
            this.token = res.data.token
@@ -55,12 +56,14 @@
      getInfo() {
        return new Promise((resolve, reject) => {
          getInfo().then(res => {
            const user = res.user
            const user = res.data.user
            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
            if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
              this.roles = res.roles
              this.permissions = res.permissions
            if (res.data.roles && res.data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
              this.roles = res.data.roles
              this.permissions = res.data.permissions
            } else {
              this.roles = ['ROLE_DEFAULT']
            }
src/utils/dict.js
@@ -21,4 +21,4 @@
    })
    return toRefs(res.value);
  })()
}
}
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/components/productTable.vue
File was renamed from src/views/hazardousChemicals/accessRecords/components/productTable.vue
@@ -69,7 +69,7 @@
<script setup>
import {getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import flowDeail from '../../../components/flowDetail.vue'
import flowDeail from '../../../../components/flowDetail.vue'
import {delWarehouse, getWarehouse} from "@/api/hazardousChemicals/warehouse";
import {
  delProductRecord,
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/components/rawTable.vue
File was renamed from src/views/hazardousChemicals/accessRecords/components/rawTable.vue
@@ -70,7 +70,7 @@
<script setup>
import {getCurrentInstance, nextTick, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import flowDeail from '../../../components/flowDetail.vue'
import flowDeail from '../../../../components/flowDetail.vue'
const router = useRouter()
const route = useRoute()
import {
src/views/hazardousChemicals/accessRecordsMenu/accessRecords/index.vue
src/views/hazardousChemicals/avoidWarningMenu/avoidWarning/components/avoidDialog.vue
src/views/hazardousChemicals/avoidWarningMenu/avoidWarning/index.vue
src/views/hazardousChemicals/basicInfonMenu/basicInfon/components/basicDialog.vue
src/views/hazardousChemicals/basicInfonMenu/basicInfon/index.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/proDetail.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/proWarehouseRecord.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/productTable.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawDetail.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawTable.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/rawWarehouseRecord.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/components/viewQR.vue
src/views/hazardousChemicals/electronicWarehouseMenu/electronicWarehouse/index.vue
src/views/hazardousChemicals/finishedBasicInfoMenu/finishedBasicInfo/components/finishedBasicDialog.vue
src/views/hazardousChemicals/finishedBasicInfoMenu/finishedBasicInfo/index.vue
src/views/hazardousChemicals/homePageMenu/homePage/index.vue
src/views/hazardousChemicals/overdueWarningMenu/overdueWarning/index.vue
src/views/hazardousChemicals/subscribeApplyManage/approval/components/editDialog.vue
New file
@@ -0,0 +1,238 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="800px"
        :before-close="handleClose"
        :close-on-press-escape="false"
        :close-on-click-modal="false"
    >
      <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" >
        <el-form-item required label= "危化品列表" >
          <div style="display: flex;width: 100%;margin-top: 5px">
            <el-table :data="state.form.hazardousTargets" :border="true">
              <el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
              <el-table-column label="危化品名称" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.name'" >
                    <span>{{row.name}}</span>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="数量" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.num'">
                    <span>{{row.num}}</span>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="title !== '查看'" >
                <template #default="scope" >
                  <el-button link type="danger"  @click="handleDelete(scope.row)" >删除</el-button>
                </template>
              </el-table-column>
            </el-table>
            <el-button
                v-if="title !== '查看'"
                style="margin-left: 20px"
                type="primary"
                @click="addObject"
            >新增</el-button>
          </div>
        </el-form-item>
        <el-form-item label="申购文件:">
          <div>
            <el-link  type="primary" @click="openFile(state.form.filePath)">{{state.form.fileName}}</el-link>
          </div>
        </el-form-item>
        <el-form-item label="审批意见:" v-if="title === '审批' && state.form.state == 0" >
          <div style="display: flex;flex-direction: column;width: 70%">
            <el-radio-group v-model="state.form.regestState">
              <el-radio :label="0">通过</el-radio>
              <el-radio :label="1">驳回</el-radio>
            </el-radio-group>
            <el-input v-if="state.form.regestState == 1" v-model="state.form.regest" type="textarea" :rows="5"></el-input>
          </div>
        </el-form-item>
        <el-form-item label="审批状态:" v-if="title === '查看' && state.form.state != 0">
          <span>{{state.form.state == 1 ? '审批通过' : '审批驳回' }}</span>
        </el-form-item>
        <el-form-item label="审批意见:" v-if="title === '查看' && state.form.state == 2" style="width: 70%">
          <el-input v-model="state.form.regest" type="textarea" :rows="5"></el-input>
        </el-form-item>
      </el-form>
      <template #footer v-if="title !=='查看'">
        <span class="dialog-footer">
            <el-button @click="handleClose" size="default">取 消</el-button>
            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {reactive, ref, toRefs} from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import {addCompany, checkName, distributeCompany, editCompany} from "@/api/hazardousChemicals/company";
import {verifyPhone, verifyPwd} from "@/utils/validate";
import {getUser} from "@/api/hazardousChemicals/user";
import {getToken} from "@/utils/auth";
import {renderAsync} from "docx-preview";
const dialogVisible = ref(false);
const title = ref("");
const busRef = ref();
const length = ref()
const emit = defineEmits(["getList"]);
const state = reactive({
  form: {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: '',
    hazardousTargets:[],
  },
  formRules:{
    creditCode: [{ required: true, message: '请输入企业信用代码', trigger: 'blur' }],
    major:[{ required: true, message: '请输入负责人', trigger: 'blur' }],
    type: [{ required: true, message: '请选择企业类型', trigger: 'blur' }],
  },
  peopleList:[],
  fileList:[],
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
  header: {
    Authorization: getToken()
  },
  fileLimit: 1,
})
const openDialog = async (type, value) => {
  title.value = type === 'approval' ? '审批' : type ==='edit' ? '申购编辑' : '查看' ;
  state.form = JSON.parse(JSON.stringify(value));
  dialogVisible.value = true;
}
const onSubmit = async () => {
  const valid = await busRef.value.validate();
  if(valid){
    if(title.value === '审批'){
      const {id, ...data} = JSON.parse(JSON.stringify(state.form))
      data.code = data.code.toUpperCase()
      const res = await addCompany(data)
      if(res.code === 200){
        ElMessage({
          type: 'success',
          message: '新增成功'
        });
      }else{
        ElMessage.warning(res.message)
      }
      emit("getList")
      busRef.value.clearValidate();
      reset();
      dialogVisible.value = false;
    }
  }
}
const handleClose = () => {
  busRef.value.clearValidate();
  reset();
  dialogVisible.value = false;
  emit("getList")
}
const reset = () => {
  state.form = {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: ''
  }
}
const addObject = () => {
  state.form.hazardousTargets.push({name:'',num:null})
}
const handleDelete = (val) => {
  state.form.hazardousTargets = state.form.hazardousTargets.filter(item=> item != val)
}
const selectValue =  (val) => {
  if(!val){
    state.form.userId = null
  }
  state.peopleList.forEach(item => {
    if(item.name === val){
      state.form.userId  = item.id
    }
  })
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
defineExpose({
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
src/views/hazardousChemicals/subscribeApplyManage/approval/index.vue
New file
@@ -0,0 +1,262 @@
<template>
  <div class="app-container">
    <div style="display: flex;justify-content: space-between">
      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
<!--        <el-form-item>-->
<!--          <el-button-->
<!--              type="primary"-->
<!--              plain-->
<!--              icon="Plus"-->
<!--              @click="openDialog('add',{})"-->
<!--          >新增</el-button>-->
<!--        </el-form-item>-->
        <el-form-item label="审批状态:" >
          <el-select v-model="data.queryParams.state" placeholder="请选择" >
            <el-option
                v-for="item in data.stateOptions"
                :key="item.id"
                :label="item.value"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="审批人:" prop="userId" >
          <el-select
              clearable
              v-model="data.userName"
              filterable
              remote
              @change="selectValue"
              reserve-keyword
              placeholder="请输入审批人名称"
              remote-show-suffix
              :remote-method="getPeopleList"
              style="width: 100%"
          >
            <el-option
                v-for="item in data.peopleList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item >
          <el-button
              type="primary"
              @click="getList"
          >查询</el-button>
          <el-button
              type="primary"
              plain
              @click="reset"
          >重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="dataList" :border="true">
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="申购单号" prop="creditCode" align="center"  />
      <el-table-column label="危化品名称" prop="code" align="center" />
      <el-table-column label="申购数量" prop="name" align="center" />
      <el-table-column label="申购人" prop="major" align="center"  />
      <el-table-column label="审批人" prop="phone" align="center"/>
      <el-table-column label="申购文件" prop="phone" align="center">
        <template #default="scope">
          <el-link v-if="scope.row.fileName" style="" type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName}}</el-link>
        </template>
      </el-table-column>
      <el-table-column label="审批状态" prop="phone" align="center">
        <template #default="scope">
          <span> {{scope.row.state == 0 ? '未审批' :scope.row.state == 1 ?'审批通过':scope.row.state == 2 ?'审批驳回':''}}</span><span></span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" >
        <template #default="scope">
          <el-button v-if="scope.row.state == 0" link type="primary" @click="openDialog('approval',scope.row)">审批</el-button>
          <el-button link type="primary" @click="openDialog('view',scope.row)">查看</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <editDialog ref="dialogRef" @getList=getList></editDialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/hazardousChemicals/company";
import editDialog from "./components/editDialog.vue";
import {getUser} from "@/api/hazardousChemicals/user";
import {renderAsync} from "docx-preview";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  },
  total: 0,
  dataList: [],
  stateOptions:[
    {
      id: 0,
      value: '未审批'
    },
    {
      id: 1,
      value: '审批通过'
    },
    {
      id: 2,
      value: '审批驳回'
    },
  ],
  peopleList:[],
  userName:''
});
const { queryParams, total, dataList } = toRefs(data);
const classHourRef = ref();
onMounted(()=>{
  getList()
  getPeopleList("")
})
onUnmounted(()=>{
})
const getList = async () => {
  loading.value = true
  // const res = await getCompany(data.queryParams)
  // if(res.code == 200){
  //   data.dataList = res.data.list
  //   data.total = res.data.total
  // }else{
  //   ElMessage.warning(res.message)
  // }
  loading.value = false
}
const openDialog = (type, value) => {
  dialogRef.value.openDialog(type, value);
}
/** 重置新增的表单以及其他数据  */
function reset() {
  data.queryParams = {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  }
  data.userName = ''
  getList()
  getPeopleList("")
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delCompany(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
const getPeopleList = async (val)=>{
  let queryParams = {}
  if(val != ""){
     queryParams = {
      name: val
    }
  }else {
     queryParams = {
      pageNum: 1,
      pageSize: 10
    }
  }
  const res = await getUser(queryParams)
  if (res.code == 200) {
    data.peopleList = res.data.list
  } else {
    ElMessage.warning(res.message)
  }
}
const selectValue =  (val) => {
  if(!val){
    data.queryParams.userId = null
  }
  data.peopleList.forEach(item => {
    if(item.name === val){
      data.queryParams.userId = item.id
    }
  })
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
</script>
src/views/hazardousChemicals/subscribeApplyManage/record/components/editDialog.vue
New file
@@ -0,0 +1,201 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="800px"
        :before-close="handleClose"
        :close-on-press-escape="false"
        :close-on-click-modal="false"
    >
      <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" >
        <el-form-item required label= "危化品列表" >
          <div style="display: flex;width: 100%;margin-top: 5px">
            <el-table :data="state.form.hazardousTargets" :border="true">
              <el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
              <el-table-column label="危化品名称" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.name'" >
                    <span>{{row.name}}</span>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="数量" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.num'">
                    <span>{{row.num}}</span>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="title !== '查看'" >
                <template #default="scope" >
                  <el-button link type="danger"  @click="handleDelete(scope.row)" >删除</el-button>
                </template>
              </el-table-column>
            </el-table>
            <el-button
                v-if="title !== '查看'"
                style="margin-left: 20px"
                type="primary"
                @click="addObject"
            >新增</el-button>
          </div>
        </el-form-item>
        <el-form-item label="申购文件:">
          <div>
            <el-link  type="primary" @click="openFile(state.form.filePath)">{{state.form.fileName}}</el-link>
          </div>
        </el-form-item>
        <el-form-item label="申购人:" >
          <span>{{state.form.people }}</span>
        </el-form-item>
        <el-form-item label="审批人:" >
          <span>{{state.form.people }}</span>
        </el-form-item>
        <el-form-item label="审批状态:" v-if="title === '查看' ">
          <span>{{state.form.state == 1 ? '审批通过' : state.form.state == 2 ?'审批驳回': '未审批' }}</span>
        </el-form-item>
        <el-form-item label="审批意见:" v-if="title === '查看' && state.form.state == 2" style="width: 70%">
          <el-input v-model="state.form.regest" type="textarea" :rows="5"></el-input>
        </el-form-item>
      </el-form>
      <template #footer v-if="title !=='查看'">
        <span class="dialog-footer">
            <el-button @click="handleClose" size="default">取 消</el-button>
            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {reactive, ref, toRefs} from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import {addCompany, checkName, distributeCompany, editCompany} from "@/api/hazardousChemicals/company";
import {verifyPhone, verifyPwd} from "@/utils/validate";
import {getUser} from "@/api/hazardousChemicals/user";
import {getToken} from "@/utils/auth";
import {renderAsync} from "docx-preview";
const dialogVisible = ref(false);
const title = ref("");
const busRef = ref();
const length = ref()
const emit = defineEmits(["getList"]);
const state = reactive({
  form: {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: '',
    hazardousTargets:[],
  },
})
const openDialog = async (type, value) => {
  title.value = type === 'approval' ? '审批' : type ==='edit' ? '申购编辑' : '查看' ;
  state.form = JSON.parse(JSON.stringify(value));
  dialogVisible.value = true;
}
const handleClose = () => {
  busRef.value.clearValidate();
  reset();
  dialogVisible.value = false;
  emit("getList")
}
const reset = () => {
  state.form = {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: ''
  }
}
const addObject = () => {
  state.form.hazardousTargets.push({name:'',num:null})
}
const handleDelete = (val) => {
  state.form.hazardousTargets = state.form.hazardousTargets.filter(item=> item != val)
}
const selectValue =  (val) => {
  if(!val){
    state.form.userId = null
  }
  state.peopleList.forEach(item => {
    if(item.name === val){
      state.form.userId  = item.id
    }
  })
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
defineExpose({
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
src/views/hazardousChemicals/subscribeApplyManage/record/index.vue
New file
@@ -0,0 +1,266 @@
<template>
  <div class="app-container">
    <div style="display: flex;justify-content: space-between">
      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
<!--        <el-form-item>-->
<!--          <el-button-->
<!--              type="primary"-->
<!--              plain-->
<!--              icon="Plus"-->
<!--              @click="openDialog('add',{})"-->
<!--          >新增</el-button>-->
<!--        </el-form-item>-->
        <el-form-item label="审批状态:" >
          <el-select v-model="data.queryParams.state" placeholder="请选择" >
            <el-option
                v-for="item in data.stateOptions"
                :key="item.id"
                :label="item.value"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="审批人:" prop="userId" >
          <el-select
              clearable
              v-model="data.userName"
              filterable
              remote
              @change="selectValue"
              reserve-keyword
              placeholder="请输入审批人名称"
              remote-show-suffix
              :remote-method="getPeopleList"
              style="width: 100%"
          >
            <el-option
                v-for="item in data.peopleList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item >
          <el-button
              type="primary"
              @click="getList"
          >查询</el-button>
          <el-button
              type="primary"
              plain
              @click="reset"
          >重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="dataList" :border="true">
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="申购单号" prop="creditCode" align="center"  />
      <el-table-column label="危化品名称" prop="code" align="center" />
      <el-table-column label="申购数量" prop="name" align="center" />
      <el-table-column label="申购人" prop="major" align="center"  />
      <el-table-column label="审批人" prop="phone" align="center"/>
      <el-table-column label="申购文件" prop="phone" align="center">
        <template #default="scope">
          <el-link v-if="scope.row.fileName" style="" type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName}}</el-link>
        </template>
      </el-table-column>
      <el-table-column label="审批状态" prop="phone" align="center">
        <template #default="scope">
          <span> {{scope.row.state == 0 ? '未审批' :scope.row.state == 1 ?'审批通过':scope.row.state == 2 ?'审批驳回':''}}</span><span></span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" >
        <template #default="scope">
          <el-button link type="primary" @click="openDialog('view',scope.row)">查看</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <editDialog ref="dialogRef" @getList=getList></editDialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/hazardousChemicals/company";
import editDialog from "./components/editDialog.vue";
import {getUser} from "@/api/hazardousChemicals/user";
import {renderAsync} from "docx-preview";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  },
  total: 0,
  dataList: [
    {
      id:1,
      state:2
    }
  ],
  stateOptions:[
    {
      id: 0,
      value: '未审批'
    },
    {
      id: 1,
      value: '审批通过'
    },
    {
      id: 2,
      value: '审批驳回'
    },
  ],
  peopleList:[],
  userName:''
});
const { queryParams, total, dataList } = toRefs(data);
const classHourRef = ref();
onMounted(()=>{
  getList()
  getPeopleList("")
})
onUnmounted(()=>{
})
const getList = async () => {
  loading.value = true
  // const res = await getCompany(data.queryParams)
  // if(res.code == 200){
  //   data.dataList = res.data.list
  //   data.total = res.data.total
  // }else{
  //   ElMessage.warning(res.message)
  // }
  loading.value = false
}
const openDialog = (type, value) => {
  dialogRef.value.openDialog(type, value);
}
/** 重置新增的表单以及其他数据  */
function reset() {
  data.queryParams = {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  }
  data.userName = ''
  getList()
  getPeopleList("")
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delCompany(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
const getPeopleList = async (val)=>{
  let queryParams = {}
  if(val != ""){
     queryParams = {
      name: val
    }
  }else {
     queryParams = {
      pageNum: 1,
      pageSize: 10
    }
  }
  const res = await getUser(queryParams)
  if (res.code == 200) {
    data.peopleList = res.data.list
  } else {
    ElMessage.warning(res.message)
  }
}
const selectValue =  (val) => {
  if(!val){
    data.queryParams.userId = null
  }
  data.peopleList.forEach(item => {
    if(item.name === val){
      data.queryParams.userId = item.id
    }
  })
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
</script>
src/views/hazardousChemicals/subscribeApplyManage/subscribe/components/editDialog.vue
New file
@@ -0,0 +1,335 @@
<template>
  <div class="notice">
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="800px"
        :before-close="handleClose"
        :close-on-press-escape="false"
        :close-on-click-modal="false"
    >
      <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" >
        <el-form-item required label= "危化品列表" >
          <div style="display: flex;width: 100%;margin-top: 5px">
            <el-table :data="state.form.hazardousTargets" :border="true">
              <el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
              <el-table-column label="危化品名称" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.name'" :rules="state.formRules.name">
                    <el-input :disabled="title === '查看'" type="textarea" v-model="row.name" placeholder="请输入危化品名称"></el-input>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="数量" align="center"  >
                <template #default="{row,$index}">
                  <el-form-item :prop="'hazardousTargets.' + '[' + $index + ']' + '.num'" :rules="state.formRules.num">
                    <el-input :disabled="title === '查看'"  v-model="row.num" @input="row.num = row.num.replace(/[^0-9]/g,'')" placeholder="请输入数量"></el-input>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" class-name="small-padding fixed-width" v-if="title !== '查看'" >
                <template #default="scope" >
                  <el-button link type="danger"  @click="handleDelete(scope.row)" >删除</el-button>
                </template>
              </el-table-column>
            </el-table>
            <el-button
                v-if="title !== '查看'"
                style="margin-left: 20px"
                type="primary"
                @click="addObject"
            >新增</el-button>
          </div>
        </el-form-item>
        <el-form-item label="审批人:" prop="userId" >
          <el-select
              clearable
              v-model="state.userName"
              filterable
              remote
              @change="selectValue"
              reserve-keyword
              placeholder="请输入审批人名称"
              remote-show-suffix
              :remote-method="getPeopleList"
              style="width: 50%"
          >
            <el-option
                v-for="item in state.peopleList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="申购文件:">
          <el-upload accept=".doc,.docx,.xls,.xlsx" v-if="title!=='查看'" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.fileLimit' v-model:file-list="state.fileList" :before-upload="picSize" :on-remove="(file)=>handleRemove(file)" >
            <el-button type="primary">点击上传</el-button>
            <template #tip>
              <div class="el-upload__tip">支持上传.doc、.docx、.xls、.xlsx格式文档,尺寸小于50M,最多可上传1张</div>
            </template>
          </el-upload>
          <div v-else>
            <el-link v-if="state.form.fileName" style="" type="primary" @click="openFile(state.form.filePath)">{{state.form.fileName}}</el-link>
          </div>
        </el-form-item>
        <el-form-item label="审批意见:" v-if="title === '查看' && state.form.state == 2" style="width: 70%">
          <el-input v-model="state.form.regest" type="textarea" :rows="2"></el-input>
        </el-form-item>
      </el-form>
      <template #footer v-if="title !=='查看'">
        <span class="dialog-footer">
            <el-button @click="handleClose" size="default">取 消</el-button>
            <el-button type="primary"  @click="onSubmit" size="default" v-preReClick>确认</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {reactive, ref, toRefs} from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import {addCompany, checkName, distributeCompany, editCompany} from "@/api/hazardousChemicals/company";
import {verifyPhone, verifyPwd} from "@/utils/validate";
import {getUser} from "@/api/hazardousChemicals/user";
import {getToken} from "@/utils/auth";
import {renderAsync} from "docx-preview";
const dialogVisible = ref(false);
const title = ref("");
const busRef = ref();
const length = ref()
const emit = defineEmits(["getList"]);
const state = reactive({
  form: {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: '',
    hazardousTargets:[],
  },
  formRules:{
    creditCode: [{ required: true, message: '请输入企业信用代码', trigger: 'blur' }],
    major:[{ required: true, message: '请输入负责人', trigger: 'blur' }],
    type: [{ required: true, message: '请选择企业类型', trigger: 'blur' }],
    name: [{ required: true, message: '', trigger: 'blur' }],
    num: [{ required: true, message: '', trigger: 'blur' }],
  },
  peopleList:[],
  fileList:[],
  uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile',
  header: {
    Authorization: getToken()
  },
  fileLimit: 1,
})
const openDialog = async (type, value) => {
  await getPeopleList("")
  title.value = type === 'add' ? '申购申请' : type ==='edit' ? '申购编辑' : '查看' ;
  if(type === 'edit') {
    state.form = JSON.parse(JSON.stringify(value));
  }
  dialogVisible.value = true;
}
const onSubmit = async () => {
  const valid = await busRef.value.validate();
  if(valid){
    if(title.value === '新增'){
      const {id, ...data} = JSON.parse(JSON.stringify(state.form))
      data.code = data.code.toUpperCase()
      const res = await addCompany(data)
      if(res.code === 200){
        ElMessage({
          type: 'success',
          message: '新增成功'
        });
      }else{
        ElMessage.warning(res.message)
      }
      emit("getList")
      busRef.value.clearValidate();
      reset();
      dialogVisible.value = false;
    }else if(title.value === '编辑'){
      const {...data} = JSON.parse(JSON.stringify(state.form))
      data.code = data.code.toUpperCase()
      const res = await editCompany(data)
      if(res.code === 200){
        ElMessage({
          type: 'success',
          message: '编辑成功'
        });
      }else{
        ElMessage.warning(res.message)
      }
      emit("getList")
      busRef.value.clearValidate();
      reset();
      dialogVisible.value = false;
    }
  }
}
const handleClose = () => {
  busRef.value.clearValidate();
  reset();
  dialogVisible.value = false;
  emit("getList")
}
const reset = () => {
  state.form = {
    id: '',
    creditCode: '',
    name: '',
    major: '',
    phone: '',
    code: ''
  }
}
const requiredDurationEventMetering =(value) =>{
  let dat =
      ("" + value)
          .replace(/[^\d^\.]+/g, "")
          .replace(/^0+(\d)/, "$1")
          .replace(/^\./, "0.")
          .match(/^\d*(\.?\d{0,2})/g)[0] || "";
  return dat
}
const addObject = () => {
  state.form.hazardousTargets.push({name:'',num:null})
}
const handleDelete = (val) => {
  state.form.hazardousTargets = state.form.hazardousTargets.filter(item=> item != val)
}
const selectValue =  (val) => {
  if(!val){
    state.form.userId = null
  }
  state.peopleList.forEach(item => {
    if(item.name === val){
      state.form.userId  = item.id
    }
  })
}
const getPeopleList = async (val)=>{
  let queryParams = {}
  if(val != ""){
    queryParams = {
      name: val
    }
  }else {
    queryParams = {
      pageNum: 1,
      pageSize: 10
    }
  }
  const res = await getUser(queryParams)
  if (res.code == 200) {
    state.peopleList = res.data.list
  } else {
    ElMessage.warning(res.message)
  }
}
const handleAvatarSuccess = (res, uploadFile) => {
  if(res.code == 200){
    state.form.fileName = res.data.originName
    state.form.filePath = res.data.path
    state.form.format = '.' + res.data.filename.split('.')[1]
  }else{
    state.fileList = []
    ElMessage({
      type: 'warning',
      message: '文件上传失败'
    })
  }
}
const showTip =()=>{
  ElMessage({
    type: 'warning',
    message: '超出文件上传数量'
  });
}
const picSize = async (rawFile) => {
  if(rawFile.size / 1024 / 1024 > 50){
    ElMessage({
      type: 'warning',
      message: '文件大小不能超过50M'
    });
    return false
  }
};
const handleRemove = async (file) => {
  const index = state.fileList.indexOf(file)
  const newFileList = state.fileList.slice()
  newFileList.splice(index, 1)
  state.fileList = newFileList;
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
defineExpose({
  openDialog
});
</script>
<style scoped lang="scss">
.notice{
  :deep(.el-form .el-form-item__label) {
    font-size: 15px;
  }
  .file {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
src/views/hazardousChemicals/subscribeApplyManage/subscribe/index.vue
New file
@@ -0,0 +1,263 @@
<template>
  <div class="app-container">
    <div style="display: flex;justify-content: space-between">
      <el-form :inline="true" style="display: flex;align-items: center;flex-wrap: wrap;" >
        <el-form-item>
          <el-button
              type="primary"
              plain
              icon="Plus"
              @click="openDialog('add',{})"
          >新增</el-button>
        </el-form-item>
        <el-form-item label="审批状态:" >
          <el-select v-model="data.queryParams.state" placeholder="请选择" >
            <el-option
                v-for="item in data.stateOptions"
                :key="item.id"
                :label="item.value"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="审批人:" prop="userId" >
          <el-select
              clearable
              v-model="data.userName"
              filterable
              remote
              @change="selectValue"
              reserve-keyword
              placeholder="请输入审批人名称"
              remote-show-suffix
              :remote-method="getPeopleList"
              style="width: 100%"
          >
            <el-option
                v-for="item in data.peopleList"
                :key="item.id"
                :label="item.name"
                :value="item.name"
            />
          </el-select>
        </el-form-item>
        <el-form-item >
          <el-button
              type="primary"
              @click="getList"
          >查询</el-button>
          <el-button
              type="primary"
              plain
              @click="reset"
          >重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 表格数据 -->
    <el-table v-loading="loading" :data="dataList" :border="true">
      <el-table-column label="序号" type="index" align="center" width="80" />
      <el-table-column label="申购单号" prop="creditCode" align="center"  />
      <el-table-column label="危化品名称" prop="code" align="center" />
      <el-table-column label="申购数量" prop="name" align="center" />
      <el-table-column label="申购人" prop="major" align="center"  />
      <el-table-column label="审批人" prop="phone" align="center"/>
      <el-table-column label="申购文件" prop="phone" align="center">
        <template #default="scope">
          <el-link v-if="scope.row.fileName" style="" type="primary" @click="openFile(scope.row.filePath)">{{scope.row.fileName}}</el-link>
        </template>
      </el-table-column>
      <el-table-column label="审批状态" prop="phone" align="center">
        <template #default="scope">
          <span> {{scope.row.state == 0 ? '未审批' :scope.row.state == 1 ?'审批通过':scope.row.state == 2 ?'审批驳回':''}}</span><span></span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" >
        <template #default="scope">
          <el-button v-if="scope.row.state == 0" link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button>
          <el-button v-if="scope.row.state == 0" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
          <el-button link type="primary" @click="openDialog('view',scope.row)">查看</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.pageNum"
        v-model:limit="queryParams.pageSize"
        @pagination="getList"
    />
    <editDialog ref="dialogRef" @getList=getList></editDialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {delCompany, getCompany} from "@/api/hazardousChemicals/company";
import editDialog from "./components/editDialog.vue";
import {getUser} from "@/api/hazardousChemicals/user";
import {renderAsync} from "docx-preview";
const { proxy } = getCurrentInstance();
const loading = ref(false);
const dialogRef = ref();
const data = reactive({
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  },
  total: 0,
  dataList: [],
  stateOptions:[
    {
      id: 0,
      value: '未审批'
    },
    {
      id: 1,
      value: '审批通过'
    },
    {
      id: 2,
      value: '审批驳回'
    },
  ],
  peopleList:[],
  userName:''
});
const { queryParams, total, dataList } = toRefs(data);
const classHourRef = ref();
onMounted(()=>{
  getList()
  getPeopleList("")
})
onUnmounted(()=>{
})
const getList = async () => {
  loading.value = true
  // const res = await getCompany(data.queryParams)
  // if(res.code == 200){
  //   data.dataList = res.data.list
  //   data.total = res.data.total
  // }else{
  //   ElMessage.warning(res.message)
  // }
  loading.value = false
}
const openDialog = (type, value) => {
  dialogRef.value.openDialog(type, value);
}
/** 重置新增的表单以及其他数据  */
function reset() {
  data.queryParams = {
    pageNum: 1,
    pageSize: 10,
    state: null,
    userId:null
  }
  data.userName = ''
  getList()
  getPeopleList("")
}
const handleDelete = (val) => {
  ElMessageBox.confirm(
      '确定删除此条数据?',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      .then( async() => {
        const res = await delCompany(val.id)
        if(res.code == 200){
          ElMessage.success('数据删除成功')
          await getList()
        }else{
          ElMessage.warning(res.message)
        }
      })
}
const getPeopleList = async (val)=>{
  let queryParams = {}
  if(val != ""){
     queryParams = {
      name: val
    }
  }else {
     queryParams = {
      pageNum: 1,
      pageSize: 10
    }
  }
  const res = await getUser(queryParams)
  if (res.code == 200) {
    data.peopleList = res.data.list
  } else {
    ElMessage.warning(res.message)
  }
}
const selectValue =  (val) => {
  if(!val){
    data.queryParams.userId = null
  }
  data.peopleList.forEach(item => {
    if(item.name === val){
      data.queryParams.userId = item.id
    }
  })
}
const openFile = async(path)=>{
  const ext = path.split('.').pop().toLowerCase();
  if (ext === 'doc' || ext === 'xls' || ext === 'xlsx' || ext === 'ppt' || ext === 'pptx') {
    ElMessageBox.confirm(`暂不支持线上预览.${ext}文件,是否下载查看?`, '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }).then(() => {
      window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
    }).catch(() => {
      console.log('取消预览')
    });
    return
  }else if(ext === 'pdf' || ext === 'jpg'||  ext === 'jpeg' || ext === 'png' ){
    window.open(`${import.meta.env.VITE_APP_BASE_API}/${path}`, '_blank');
  }else{
    try {
      // 1. 获取文件
      const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/' + path);
      const arrayBuffer = await response.arrayBuffer();
      // 2. 创建新窗口
      const win = window.open('', '_blank');
      win.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>预览</title>
          <style>
            body { margin: 20px; font-family: Arial; }
            .docx-container { width: 100%; height: 100%; }
          </style>
        </head>
        <body>
          <div id="container" class="docx-container"></div>
        </body>
      </html>
    `);
      // 3. 渲染 DOCX
      await renderAsync(arrayBuffer, win.document.getElementById('container'));
    } catch (error) {
      console.error('预览失败:', error);
      alert(`预览失败: ${error.message}`);
    }
  }
}
</script>
src/views/hazardousChemicals/systemManage/config/components/mapLocation.vue
@@ -25,14 +25,14 @@
        <span>纬度:</span>
        <el-input class="mapLocation_latlng_input" id="lat" v-model.trim="state.latitude" @change="getAdress"></el-input>
      </div>
      <div class="mapLocation_latlng">
        <span>地址:</span>
        <el-input class="mapLocation_latlng_input" id="lat" v-model.trim="state.BAddress"></el-input>
      </div>
<!--      <div class="mapLocation_latlng">-->
<!--        <span>地址:</span>-->
<!--        <el-input class="mapLocation_latlng_input" id="lat" v-model.trim="state.BAddress"></el-input>-->
<!--      </div>-->
    </div>
    <div class="mapLocation_body">
      <baidu-map class="map"  @ready="getAdress" ak="BkZdiHBj9aGrMdVFM48r2njNiMzsekga" v="3.0" type="API" :center="state.center" :zoom="state.zoom" scroll-wheel-zoom  @click="getPosition">
      <baidu-map class="map"  @ready="getAdress" ak="oF71jf1ELX29VmdqXBBNveZ7DevpqsuI" v="3.0" type="API" :center="state.center" :zoom="state.zoom" scroll-wheel-zoom  @click="getPosition">
<!--        <div style="position: absolute;z-index: 999;margin-top: -495px">-->
<!--          <label>搜索:<input v-model="state.keyword"></label>-->
<!--          <bm-local-search-->
@@ -116,13 +116,13 @@
}
const myGeo = ref(null)
const getAdress = () => {
  myGeo.value = new BMap.Geocoder();
  const pt = new BMap.Point(state.longitude, state.latitude);
  myGeo.value.getLocation(pt,function(result){
        state.BAddress = result.address; //获取到当前定位的详细地址信息
      },
      { enableHighAccuracy: true }
  );
  // myGeo.value = new BMap.Geocoder();
  // const pt = new BMap.Point(state.longitude, state.latitude);
  // myGeo.value.getLocation(pt,function(result){
  //       state.BAddress = result.address; //获取到当前定位的详细地址信息
  //     },
  //     { enableHighAccuracy: true }
  // );
}
const handleClose = () => {
  state.longitude = ''
src/views/hazardousChemicals/traceableQueryMenu/traceableQuery/index.vue
File was renamed from src/views/hazardousChemicals/traceableQuery/index.vue
@@ -30,7 +30,7 @@
</template>
<script setup>
import {nextTick, reactive, ref} from "vue";
import flowDeail from '../../components/flowDetail.vue'
import flowDeail from '../../../components/flowDetail.vue'
import {getFlowByCode, getProFlow} from "@/api/hazardousChemicals/productRecord";
import {ElMessage} from "element-plus";
src/views/hazardousChemicals/useCountMenu/useCount/index.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/addProDialog.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/addRawDialog.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/printCode.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/printCodeMore.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/proDetail.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/productTable.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/rawDetail.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/components/rawTable.vue
src/views/hazardousChemicals/warehouseManageMenu/warehouseManage/index.vue
src/views/homePage.vue
@@ -218,6 +218,108 @@
}, { immediate: true });
const sidebarRouters = ref([])
// function handleLogin() {
//   proxy.$refs.loginRef.validate(valid => {
//     if (valid) {
//       loading.value = true;
//       // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
//       // if (loginForm.value.rememberMe) {
//       Cookies.set("username", loginForm.value.username, { expires: 30 });
//       Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
//       //   Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
//       // } else {
//       // 否则移除
//       //   Cookies.remove("username");
//       //   Cookies.remove("password");
//       //   Cookies.remove("rememberMe");
//       // }
//       // 调用action的登录方法
//       // loginForm.value.password = Base64.encode(loginForm.value.password)
//       const param = {
//         username: loginForm.value.username,
//         password: Base64.encode(loginForm.value.password),
//         code: loginForm.value.code,
//         uuid: loginForm.value.uuid,
//         identity: loginForm.value.role
//       }
//       userStore.login(param).then(() => {
//         // const query = route.query;
//         // const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
//         //   if (cur !== "redirect") {
//         //     acc[cur] = query[cur];
//         //   }
//         //   return acc;
//         // }, {});
//
//         //暂时不分角色
//         // const userInfo = JSON.parse(Cookies.get('userInfo'))
//         // if(userInfo.identity === 0 ) {
//         //   //监管用户(管理员)
//         //   sidebarRouters.value =  menu.adminMenu
//         //   Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//         // }else if(userInfo.identity === 1) {
//         //   sidebarRouters.value =  menu.agencyMenu
//         //   Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//         // }
//         const userInfo = JSON.parse(Cookies.get('userInfo'))
//
//         if(userInfo.userType === 0) {
//           sidebarRouters.value =  menu.adminMenu
//           Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//         }else if(userInfo.userType === 1){
//           const config = JSON.parse(Cookies.get('configInfo'))
//           if(config){
//             if(config.useProd === 0){
//               sidebarRouters.value =  menu.companyMenu.filter(item => item.path != '/finishedBasicInfo')
//             }else {
//               sidebarRouters.value =  menu.companyMenu
//
//             }
//           }else {
//             ElMessage.warning('请联系管理员完善企业配置')
//             sidebarRouters.value =  menu.companyMenu
//           }
//           Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//         }else if(userInfo.userType === 2){
//           const config = JSON.parse(Cookies.get('configInfo'))
//           if(config){
//             if(config.useProd === 0){
//               sidebarRouters.value =  menu.commonMenu.filter(item => item.path != '/finishedBasicInfo')
//             }else {
//               sidebarRouters.value =  menu.commonMenu
//             }
//           }else {
//             // ElMessage.warning('请联系管理员完善企业配置')
//             sidebarRouters.value =  menu.commonMenu
//           }
//           Cookies.set('routers',JSON.stringify(sidebarRouters.value))
//         }else {
//           ElMessage.warning('监管部门账号不可登录')
//           loading.value = false
//           return
//         }
//         let path = ""
//         if(sidebarRouters.value[0].children && sidebarRouters.value[0].children.length > 0){
//           path = sidebarRouters.value[0].path + '/'+ sidebarRouters.value[0].children[0].path
//         }else {
//           path = sidebarRouters.value[0].path
//         }
//
//         router.push({
//           path: path
//         })
//         // router.push({ path: redirect.value || "/", query: otherQueryParams });
//       }).catch(() => {
//         loading.value = false;
//         // 重新获取验证码
//         if (captchaEnabled.value) {
//           getCode();
//         }
//       });
//     }
//   });
// }
function handleLogin() {
  proxy.$refs.loginRef.validate(valid => {
    if (valid) {
@@ -225,7 +327,8 @@
      // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
      // if (loginForm.value.rememberMe) {
      Cookies.set("username", loginForm.value.username, { expires: 30 });
      Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
      Cookies.set("password", loginForm.value.password, { expires: 30 });
      // Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
      //   Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
      // } else {
      // 否则移除
@@ -240,75 +343,16 @@
        password: Base64.encode(loginForm.value.password),
        code: loginForm.value.code,
        uuid: loginForm.value.uuid,
        identity: loginForm.value.role
      }
      userStore.login(param).then(() => {
        // const query = route.query;
        // const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
        //   if (cur !== "redirect") {
        //     acc[cur] = query[cur];
        //   }
        //   return acc;
        // }, {});
        //暂时不分角色
        // const userInfo = JSON.parse(Cookies.get('userInfo'))
        // if(userInfo.identity === 0 ) {
        //   //监管用户(管理员)
        //   sidebarRouters.value =  menu.adminMenu
        //   Cookies.set('routers',JSON.stringify(sidebarRouters.value))
        // }else if(userInfo.identity === 1) {
        //   sidebarRouters.value =  menu.agencyMenu
        //   Cookies.set('routers',JSON.stringify(sidebarRouters.value))
        // }
        const userInfo = JSON.parse(Cookies.get('userInfo'))
        if(userInfo.userType === 0) {
          sidebarRouters.value =  menu.adminMenu
          Cookies.set('routers',JSON.stringify(sidebarRouters.value))
        }else if(userInfo.userType === 1){
          const config = JSON.parse(Cookies.get('configInfo'))
          if(config){
            if(config.useProd === 0){
              sidebarRouters.value =  menu.companyMenu.filter(item => item.path != '/finishedBasicInfo')
            }else {
              sidebarRouters.value =  menu.companyMenu
            }
          }else {
            ElMessage.warning('请联系管理员完善企业配置')
            sidebarRouters.value =  menu.companyMenu
        const query = route.query;
        const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
          if (cur !== "redirect") {
            acc[cur] = query[cur];
          }
          Cookies.set('routers',JSON.stringify(sidebarRouters.value))
        }else if(userInfo.userType === 2){
          const config = JSON.parse(Cookies.get('configInfo'))
          if(config){
            if(config.useProd === 0){
              sidebarRouters.value =  menu.commonMenu.filter(item => item.path != '/finishedBasicInfo')
            }else {
              sidebarRouters.value =  menu.commonMenu
            }
          }else {
            // ElMessage.warning('请联系管理员完善企业配置')
            sidebarRouters.value =  menu.commonMenu
          }
          Cookies.set('routers',JSON.stringify(sidebarRouters.value))
        }else {
          ElMessage.warning('监管部门账号不可登录')
          loading.value = false
          return
        }
        let path = ""
        if(sidebarRouters.value[0].children && sidebarRouters.value[0].children.length > 0){
          path = sidebarRouters.value[0].path + '/'+ sidebarRouters.value[0].children[0].path
        }else {
          path = sidebarRouters.value[0].path
        }
        router.push({
          path: path
        })
        // router.push({ path: redirect.value || "/", query: otherQueryParams });
          return acc;
        }, {});
        router.push({ path: redirect.value || "/", query: otherQueryParams });
      }).catch(() => {
        loading.value = false;
        // 重新获取验证码
@@ -319,7 +363,6 @@
    }
  });
}
function getCode() {
  getCodeImg().then(res => {
    // captchaEnabled.value = res.data.captchaEnabled
src/views/index.vue
New file
@@ -0,0 +1,101 @@
<template>
  <div class="app-container">
    <div style="margin: 150px 100px;color: #3173ea" class="login-img">
      <div >
        <div style="font-size:50px">欢迎进入</div>
        <div style="font-size: 35px;margin-top: 20px">危化品全生命周期管理系统</div>
      </div>
    </div>
  </div>
</template>
<script setup name="Index">
const version = ref('3.8.6')
function goTarget(url) {
  window.open(url, '__blank')
}
</script>
<style scoped lang="scss">
.login-img{
  //display: flex;
  //justify-content: center;
  //align-items: center;
  font-family: 'AliMa';
  color: #fff;
  font-size: 3.2rem;
  //text-align: center;
  //line-height: 1.5;
  //transform: translateY(-80px);
  text-shadow: -10px 10px 20px rgba(0,0,0,.4);
}
.home {
  font-size: 35px;
  font-weight: 600;
  text-align: center;
  margin-top: 10%;
  blockquote {
    padding: 10px 20px;
    margin: 0 0 20px;
    //font-size: 17.5px;
    border-left: 5px solid #eee;
  }
  hr {
    margin-top: 20px;
    margin-bottom: 20px;
    border: 0;
    border-top: 1px solid #eee;
  }
  .col-item {
    margin-bottom: 20px;
  }
  ul {
    padding: 0;
    margin: 0;
  }
  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #676a6c;
  overflow-x: hidden;
  ul {
    list-style-type: none;
  }
  h4 {
    margin-top: 0px;
  }
  h2 {
    margin-top: 10px;
    font-size: 26px;
    font-weight: 100;
  }
  p {
    margin-top: 10px;
    b {
      font-weight: 700;
    }
  }
  .update-log {
    ol {
      display: block;
      list-style-type: decimal;
      margin-block-start: 1em;
      margin-block-end: 1em;
      margin-inline-start: 0;
      margin-inline-end: 0;
      padding-inline-start: 40px;
    }
  }
}
</style>
src/views/system/menu/index.vue
@@ -37,7 +37,7 @@
            >新增</el-button>
         </el-col>
         <el-col :span="1.5">
            <el-button
            <el-button
               type="info"
               plain
               icon="Sort"
src/views/system/role/authUser.vue
@@ -2,18 +2,18 @@
<template>
   <div class="app-container">
      <el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true">
         <el-form-item label="用户名称" prop="userName">
         <el-form-item label="用户名称" prop="username">
            <el-input
               v-model="queryParams.userName"
               v-model="queryParams.username"
               placeholder="请输入用户名称"
               clearable
               style="width: 240px"
               @keyup.enter="handleQuery"
            />
         </el-form-item>
         <el-form-item label="手机号码" prop="phonenumber">
         <el-form-item label="手机号码" prop="phone">
            <el-input
               v-model="queryParams.phonenumber"
               v-model="queryParams.phone"
               placeholder="请输入手机号码"
               clearable
               style="width: 240px"
@@ -47,9 +47,9 @@
            >批量取消授权</el-button>
         </el-col>
         <el-col :span="1.5">
            <el-button
               type="warning"
               plain
            <el-button
               type="warning"
               plain
               icon="Close"
               @click="handleClose"
            >关闭</el-button>
@@ -59,20 +59,24 @@
      <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
         <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
         <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
         <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
         <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
         <el-table-column label="状态" align="center" prop="status">
            <template #default="scope">
               <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
            </template>
         </el-table-column>
         <el-table-column label="创建时间" align="center" prop="createTime" width="180">
            <template #default="scope">
               <span>{{ parseTime(scope.row.createTime) }}</span>
            </template>
         </el-table-column>
        <el-table-column label="用户名称" prop="username" :show-overflow-tooltip="true" />
        <el-table-column label="姓名" prop="name" align="center"  />
        <el-table-column label="性别" prop="sex" align="center">
          <template #default="scope">
            <span>{{scope.row.sex == 0 ?'男':'女'}}</span>
          </template>
        </el-table-column>
        <el-table-column label="手机" prop="phone" :show-overflow-tooltip="true" />
        <el-table-column label="用户类型" prop="userType" align="center">
          <template #default="scope">
            <span>{{scope.row.userType == 0 ?'管理员':scope.row.userType == 1 ? '企业用户':scope.row.userType ==2 ? '普通用户' : '监管用户'}}</span>
          </template>
        </el-table-column>
        <el-table-column label="状态" align="center" prop="status">
          <template #default="scope">
            <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
          </template>
        </el-table-column>
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
            <template #default="scope">
               <el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']">取消授权</el-button>
@@ -110,16 +114,16 @@
  pageNum: 1,
  pageSize: 10,
  roleId: route.params.roleId,
  userName: undefined,
  phonenumber: undefined,
  username: undefined,
  phone: undefined,
});
/** 查询授权用户列表 */
function getList() {
  loading.value = true;
  allocatedUserList(queryParams).then(response => {
    userList.value = response.rows;
    total.value = response.total;
    userList.value = response.data.list;
    total.value = response.data.total;
    loading.value = false;
  });
}
@@ -140,7 +144,7 @@
}
// 多选框选中数据
function handleSelectionChange(selection) {
  userIds.value = selection.map(item => item.userId);
  userIds.value = selection.map(item => item.id);
  multiple.value = !selection.length;
}
/** 打开授权用户表弹窗 */
@@ -149,8 +153,8 @@
}
/** 取消授权按钮操作 */
function cancelAuthUser(row) {
  proxy.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?').then(function () {
    return authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
  proxy.$modal.confirm('确认要取消该用户"' + row.username + '"角色吗?').then(function () {
    return authUserCancel({ userId: row.id, roleId: queryParams.roleId });
  }).then(() => {
    getList();
    proxy.$modal.msgSuccess("取消授权成功");
src/views/system/role/index.vue
@@ -121,9 +121,9 @@
              <el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1">
                <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
              </el-tooltip>
              <el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1">
                <el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
              </el-tooltip>
<!--              <el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1">-->
<!--                <el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>-->
<!--              </el-tooltip>-->
              <el-tooltip content="分配用户" placement="top" v-if="scope.row.roleId !== 1">
                <el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
              </el-tooltip>
@@ -300,8 +300,8 @@
function getList() {
  loading.value = true;
  listRole(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
    roleList.value = response.rows;
    total.value = response.total;
    roleList.value = response.data.list;
    total.value = response.data.total;
    loading.value = false;
  });
}
@@ -422,7 +422,7 @@
    open.value = true;
    nextTick(() => {
      roleMenu.then((res) => {
        let checkedKeys = res.checkedKeys;
        let checkedKeys = res.data.checkedKeys;
        checkedKeys.forEach((v) => {
          nextTick(() => {
            menuRef.value.setChecked(v, true, false);
@@ -436,7 +436,7 @@
/** 根据角色ID查询菜单树结构 */
function getRoleMenuTreeselect(roleId) {
  return roleMenuTreeselect(roleId).then(response => {
    menuOptions.value = response.menus;
    menuOptions.value = response.data.menus;
    return response;
  });
}
src/views/system/role/selectUser.vue
@@ -2,18 +2,18 @@
   <!-- 授权用户 -->
   <el-dialog title="选择用户" v-model="visible" width="800px" top="5vh" append-to-body>
      <el-form :model="queryParams" ref="queryRef" :inline="true">
         <el-form-item label="用户名称" prop="userName">
         <el-form-item label="用户名称" prop="username">
            <el-input
               v-model="queryParams.userName"
               v-model="queryParams.username"
               placeholder="请输入用户名称"
               clearable
               style="width: 200px"
               @keyup.enter="handleQuery"
            />
         </el-form-item>
         <el-form-item label="手机号码" prop="phonenumber">
         <el-form-item label="手机号码" prop="phone">
            <el-input
               v-model="queryParams.phonenumber"
               v-model="queryParams.phone"
               placeholder="请输入手机号码"
               clearable
               style="width: 200px"
@@ -28,18 +28,22 @@
      <el-row>
         <el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px">
            <el-table-column type="selection" width="55"></el-table-column>
            <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
            <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
            <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
            <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
            <el-table-column label="用户名称" prop="username" :show-overflow-tooltip="true" />
           <el-table-column label="姓名" prop="name" align="center"  />
           <el-table-column label="性别" prop="sex" align="center">
             <template #default="scope">
               <span>{{scope.row.sex == 0 ?'男':'女'}}</span>
             </template>
           </el-table-column>
           <el-table-column label="手机" prop="phone" :show-overflow-tooltip="true" />
           <el-table-column label="用户类型" prop="userType" align="center">
             <template #default="scope">
               <span>{{scope.row.userType == 0 ?'管理员':scope.row.userType == 1 ? '企业用户':scope.row.userType ==2 ? '普通用户' : '监管用户'}}</span>
             </template>
           </el-table-column>
            <el-table-column label="状态" align="center" prop="status">
               <template #default="scope">
                  <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
               </template>
            </el-table-column>
            <el-table-column label="创建时间" align="center" prop="createTime" width="180">
               <template #default="scope">
                  <span>{{ parseTime(scope.row.createTime) }}</span>
               </template>
            </el-table-column>
         </el-table>
@@ -81,8 +85,8 @@
  pageNum: 1,
  pageSize: 10,
  roleId: undefined,
  userName: undefined,
  phonenumber: undefined
  username: undefined,
  phone: undefined
});
// 显示弹框
@@ -97,13 +101,13 @@
}
// 多选框选中数据
function handleSelectionChange(selection) {
  userIds.value = selection.map(item => item.userId);
  userIds.value = selection.map(item => item.id);
}
// 查询表数据
function getList() {
  unallocatedUserList(queryParams).then(res => {
    userList.value = res.rows;
    total.value = res.total;
    userList.value = res.data.list;
    total.value = res.data.total;
  });
}
/** 搜索按钮操作 */
@@ -126,7 +130,7 @@
    return;
  }
  authUserSelectAll({ roleId: roleId, userIds: uIds }).then(res => {
    proxy.$modal.msgSuccess(res.msg);
    proxy.$modal.msgSuccess('分配成功');
    if (res.code === 200) {
      visible.value = false;
      emit("ok");