vue+Nodejs+Koa搭建前后端系统(八)-- vue router路由嵌套、stroe状态管理

news2024/9/22 10:08:46

前言

本文是在该系列的基础上,针对前端的修改。其中前端采用vue3框架,脚手架为vite,ui组件库为ElementPlus,路由为vue-router,状态管理库为Pinia。

路由嵌套

整合模块数据文件(路由、菜单)

整合模块数据为路由、菜单所用数据

/** /src/router/modules.ts */
import type { ModulesItemType, MenuItemType } from "./modules.type"
//从 ./modules/ 目录中导入多个模块 适用于vite
const modules = import.meta.glob('./modules/*.ts', { eager: true }) as { [propName: string]: { default: ModulesItemType } }

const parentPath = "/";//所有模块的父路径path
/** 首字母大写方法 */
function __FirstWordUpperCase(letters: string[]) {
    return letters.reduce((s: string, c: string) => {
        return s += c.charAt(0).toUpperCase() + c.slice(1)
    }, '')
}
/** 
去掉字母前缀,并首字母小写方法 
letters:要处理的字母
ignoredPre:要去掉的前缀
*/
function __FirstWordLowerCase(letters: string, ignoredPre = "") {
    if (ignoredPre) {
        const regx = new RegExp("^" + ignoredPre, "gim");
        letters = letters.replace(regx, "")
    }
    return letters.charAt(0).toLowerCase() + letters.slice(1);
}
/**
整合单个模块数据为路由文件所需格式
nameSpace:模块的命名空间
item:模块中的数据
routes:用于存储路由数据
*/
function __makeModulesRoutesData(nameSpace: string, item: ModulesItemType, routes: any[] = []) {
	//将模块数据中以menu为前缀的key过滤掉
    const routeKeys = Object.keys(item).filter((key: string) => /^[^menu]/.test(key));
    //模块数据中有key为path,则处理为路由数据
    if ('path' in item) {
        const routePath = `${nameSpace}/${item.path}`;//路由path:【命名空间】+【当前path值】 
        const routeName = __FirstWordUpperCase([nameSpace, item.path || '']);//路由name:【命名空间】+【当前path值】 (驼峰式)
        //将过滤的key的值整合:除了path值是以【命名空间】+【当前path值】,其他值不变
        const routeItem = routeKeys.reduce((m: any, key: string) => {
            if (key === 'path') m['path'] = routePath;
            else m[key] = item[key as keyof ModulesItemType];
            return m
        }, {});
        //将整合后的路由存储到routes变量中
        routes.push({ ...routeItem, name: routeName });
    }
    //模块数据还有子数据 - menuChildren 则递归再次重复以上动作
    if (item['menuChildren']) {
        for (let i in item['menuChildren']) {
            __makeModulesRoutesData(nameSpace, item['menuChildren'][i], routes)
        }
    }
    return routes;
}
/**
整合各个模块数据为路由文件所需格式
routes:用于存储路由数据
*/
function getModulesRoutes(routes: any[] = []) {
    for (let key in modules) {
    	//命名空间:以模块文件与	./modules/ 目录的相对路径为命名空间
    	//比如 ./modules/first.ts 命名空间为first
        const nameSpace = key.replace(/^\.\/modules\//, "").replace(/\.\w+$/, "");
        //模块文件中的数据
        const modulesItem = modules[key].default;
        __makeModulesRoutesData(nameSpace, modulesItem, routes)
    }
    return routes;
}
/**
整合单个模块数据为菜单所需格式
nameSpace:模块的命名空间
item:模块中的数据
menu:用于存储菜单数据
index:每个菜单数据中的key,相当于id
*/
function __makeModulesMenusData(nameSpace: string, item: ModulesItemType, menu: any = {}, index = "") {
	//将模块数据中以非menu为前缀的key过滤掉
    const menuKeys = Object.keys(item).filter((key: string) => /^menu/.test(key));
    //模块数据中有key为menuName,则处理为菜单数据
    if ('menuName' in item) {
    	//将过滤的key的值整合:去掉menu前缀并首字母小写。将菜单数据存储到menu中
        for (let key of menuKeys) {
            const mkey = __FirstWordLowerCase(key, 'menu');
            menu[mkey] = item[key as keyof ModulesItemType];
        }
        //处理key为path的数据:【所有模块的父路径path】+【命名空间】+【当前path值】
        if ('path' in item) {
            menu['path'] = `${parentPath}${nameSpace}/${item.path}`;
        }
        //设置菜单数据key
        menu['key'] = index;
    }
    //模块数据还有子数据 - menuChildren 则递归再次重复以上动作
    if (item['menuChildren']) {
        menu['children'] = [];//创建子数据children
        let cIndex = 1;//子数据索引:用作设置菜单key
        for (let i in item['menuChildren']) {
            if ('menuName' in item) {
                const menuItem = {}
                __makeModulesMenusData(nameSpace, item['menuChildren'][i], menuItem, `${index}-${cIndex++}`)
                menu['children'].push(menuItem)
            }
        }
    }
    return menu;
}
/**
整合各个模块数据为菜单所需格式
*/
function getModulesMenus(): MenuItemType[] {
    const menus: MenuItemType[] = [];//menu:用于存储菜单数据
    const MAX_ORDER = 999999;//最大顺序,用于模块间的菜单排序
    //将各个模块asc排序:以menuOrder设置数字顺序,没有该值(或设置为0)则按最大值处理
    const modulesKeys = Object.keys(modules).sort((key1: string, key2: string) => (modules[key1].default.menuOrder || MAX_ORDER) - (modules[key2].default.menuOrder || MAX_ORDER));
    //模块间菜单数据的处理
    modulesKeys.forEach((key: string, index: number) => {
    	//命名空间:以模块文件与	./modules/ 目录的相对路径为命名空间
    	//比如 ./modules/first/index.ts 命名空间为first/index
        const nameSpace = key.replace(/^\.\/modules\//, "").replace(/\.\w+$/, "");
        //模块文件中的数据
        const modulesItem = modules[key].default;
        if (modulesItem['menuName']) {
            const modulesMenuItem = __makeModulesMenusData(nameSpace, modulesItem, {}, String(index))
            menus.push(modulesMenuItem);
        }
    })
    return menus;
}
export {
    getModulesRoutes,
    getModulesMenus,
    parentPath
}

模块接口文件

/** /src/router/modules.type.ts */
import { DefineComponent } from "vue";
type ComponentType = DefineComponent<{}, {}, {}, import("vue").ComputedOptions, import("vue").MethodOptions, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}>
/**各模块单个数据接口:凡是不带menu前缀的都是vue-route用的 */
export interface ModulesItemType {
    menuName?: string;//菜单名称,有值则显示
    menuOrder?: number;//菜单顺序
    menuIcon?: string | ComponentType;//菜单图标
    menuChildren?: ModulesItemType[];//子菜单
    path?: string;
    component?: () => Promise<typeof import("*.vue")>;
    meta?: { [propName: string]: any }
}
/**菜单接口 */
export interface MenuItemType {
    name?: string;//菜单名称,有值则显示
    order?: number;//菜单顺序
    icon?: string | ComponentType;//菜单图标
    children?: MenuItemType[];//子菜单
    path?: string;
}

模块文件

模块文件在都必须在 /src/router/modules/ 目录下,并且每个模块必须有导出默认,export default

/** /src/router/modules/first.ts */
import { Location } from "@element-plus/icons-vue";
import { type ModulesItemType } from "../modules.type"
export default {
    menuName: '菜单1',
    menuOrder:1,
    path: 'index',
    menuIcon: Location,//ElementPlus icon图标
    component: () => import("@/pages/first/index.vue"),
} as ModulesItemType
/** /src/router/modules/second.ts */
import { type ModulesItemType } from "../modules.type"
export default {
    menuName: '菜单2',
    menuOrder: 2,
    path: 'index',
    menuIcon: 'iconfont icon-ding',//iconfont图标
    component: () => import("@/pages/second/index.vue"),
} as ModulesItemType
/** /src/router/modules/third.ts */
import { Location } from "@element-plus/icons-vue";
import { type ModulesItemType } from "../modules.type"
export default {
    menuName: '菜单3',
    menuOrder: 3,
    menuIcon: Location,
    menuChildren: [
        {
            menuName: '菜单3-1',
            menuIcon: Location,
            menuChildren: [
                { menuName: '菜单3-1-1', path: 'third_1_1', component: () => import("@/pages/third/third_1_1.vue"), menuIcon: Location },
                { menuName: '菜单3-1-2', path: 'third_1_2', component: () => import("@/pages/third/third_1_2.vue"), menuIcon: '' }
            ]
        },
        { menuName: '菜单3-2', path: 'third_2', component: () => import("@/pages/third/third_2.vue"), menuIcon: '' },
        { menuName: '菜单3-3', path: 'third_3', component: () => import("@/pages/third/third_3.vue"), menuIcon: '' }
    ]
} as ModulesItemType
/** /src/router/modules/four.ts */
import { type ModulesItemType } from "../modules.type"
export default {
    menuName: '菜单4',
    menuOrder: 4,
    path: 'index',
    menuIcon: '',
    component: () => import("@/pages/four/index.vue"),
} as ModulesItemType

路由文件

/** /src/router/index.ts */
import {
    createRouter,
    createWebHistory,
    RouteRecordRaw,
    createWebHashHistory,
} from "vue-router";
import { getModulesRoutes, parentPath } from "./modules"//整合路由的文件

const routes: Array<RouteRecordRaw> = [
    {
        path: "/login",
        name: "Login",
        component: () => import("@/pages/login/login.vue"),
    },
    {
        path: "/register",
        name: "Register",
        component: () => import("@/pages/register/register.vue"),
        meta: {}
    },
    {
        path: parentPath,//各模块的父路径
        component: () => import("@/pages/index.vue"),
        children: [
            ...getModulesRoutes(),//获取各模块的路由
            {
                path: "",
                redirect: "first/index"
            },
            // 以上都未匹配,跳转404页面
            {
                path: '/:pathMatch(.*)*',
                name: 'NotFound',
                component: () => import("@/pages/not-found.vue"),
            },
        ]
    },
]

const router = createRouter({
    history: createWebHashHistory(),
    routes,
});
export default router;

getModulesRoutes()最终生成的数据

[
    {
        "path": "first/index",
        "name": "FirstIndex",
        "component": () => import("@/pages/first/index.vue")
    },
    {
        "path": "four/index",
        "name": "FourIndex",
        "component": () => import("@/pages/four/index.vue")
    },
    {
        "path": "second/index",
        "name": "SecondIndex",
        "component": () => import("@/pages/second/index.vue")
    },
    {
        "path": "third/third_1_1",
        "name": "ThirdThird_1_1",
        "component": () => import("@/pages/third/third_1_1.vue")
    },
    {
        "path": "third/third_1_2",
        "name": "ThirdThird_1_2",
        "component": () => import("@/pages/third/third_1_2.vue")
    },
    {
        "path": "third/third_2",
        "name": "ThirdThird_2",
        "component": () => import("@/pages/third/third_2.vue")
    },
    {
        "path": "third/third_3",
        "name": "ThirdThird_3",
        "component": () => import("@/pages/third/third_3.vue")
    }
]

嵌套路由index页面

<!-- /src/pages/index.vue -->
<script setup lang="ts">
import Menu from "./menu/menu.vue";//左侧菜单栏组件
import TopHeader from "./top-header/top-header.vue";//顶部组件
</script>
<template>
  <div class="index">
  	<!--顶部显示区域-->
    <header><TopHeader /></header>
    <section>
      <!--左侧菜单显示区域-->
      <nav><Menu /></nav>
      <!--中间内容显示区域-->
      <main>
      	<!--嵌套路由(嵌套在App.vue中的路由下的路由)-->
        <router-view v-slot="{ Component, route }">
          <transition name="fade">
            <component :is="Component" :key="route.path" />
          </transition>
        </router-view>
      </main>
    </section>
  </div>
</template>
<style lang="less" scoped>
.index {
  @headerH: 60px;
  & > header {
    height: @headerH;
    box-sizing: border-box;
    line-height: @headerH;
    background: #000;
  }
  & > section {
    height: calc(100vh - @headerH);
    display: flex;

    & > nav {
      height: 100%;
    }
    & > main {
      box-sizing: border-box;
      flex-grow: 1;
      height: 100%;
      overflow: auto;
      padding: 15px 15px 10px;
    }
  }
}
</style>

顶部TopHeader页面

<!-- /src/pages/top-header/top-header.vue -->
<template>
  <div class="top-header">我是顶部区域</div>
</template>

菜单页面

左侧菜单Menu页面,由于菜单数据是一种树形结构,所以该页面采用vue的渲染函数(h函数)写法

<!-- /src/pages/menu/menu.vue -->
<script lang="ts">
import { h, watch, getCurrentInstance } from "vue";
import { RouteLocationNormalizedLoaded, useRoute } from "vue-router";
import { ElMenu, ElSubMenu, ElMenuItem, ElIcon } from "element-plus";
import { getModulesMenus } from "@/router/modules";
import { MenuItemType } from "@/router/modules.types";
export default {
  setup() {
    const internalInstance = getCurrentInstance();
    const route = useRoute();
    const menuList = getModulesMenus();
    //ElMenu组件属性
    const elMenuAttrs = {
      class: "menu-component",
      "active-text-color": "#ffd04b",
      "background-color": "#545c64",
      "default-active": route.path,
      "text-color": "#fff",
      router: true,
    };
    //生成ElMenuItem组件下面内容或ElSubMenu组件的title slot
    const getMenuTitleVNode = (item: MenuItemType) => {
      const v = [];
      if (item.icon) {//如果数据中有icon属性有值
        if (typeof item.icon === "string") {//icon属性值是string类型(适用于iconfont)
          /** 相当于<el-icon :class="item.icon"></el-icon> */
          v.push(h(ElIcon, { class: item.icon }));
        } else {//icon属性值是导入的组件(适用于elementPlus图标组件)
          /** 相当于
          <el-icon :class="item.icon">
          	<component :is="item.icon"></component
          </el-icon> 
          */
          v.push(h(ElIcon, null, { default: () => [h(item.icon)] }));
        }
      }
      /**相当于<span>{{item.name}}</span>*/
      v.push(h("span", item.name));
      return v;
    };
    //生成ElMenu组件下面的ElMenuItem或 ElSubMenu组件
    const getMenuVNode = (m: MenuItemType[]) => {
      return m.map((item: MenuItemType) => {
        let node: any = null;
        if (item.children?.length) {//有下级菜单
          /** 相当于
			<el-sub-menu :index="item.key" :data-tree="item.key">
				<template #title>....</template>
				<template #default>...递归该函数</template>
			</el-sub-menu>
		  */
          node = h(
            ElSubMenu,
            {
              index: item.key,
              "data-tree": item.key,
            },
            {
              title: () => [...getMenuTitleVNode(item)],
              default: () => [...getMenuVNode(item.children)],
            }
          );
        } else {//没有下级菜单
          /** 相当于
			<el-menu-item :index="item.path" :route="item.path" :data-tree="item.key" :data-route="item.path">
				<template #default>...</template>
			</el-menu-item>
		  */
          node = h(
            ElMenuItem,
            {
              index: item.path,
              route: item.path,
              "data-tree": item.key,
              "data-route": item.path,
            },
            {
              default: () => [...getMenuTitleVNode(item)],
            }
          );
        }
        return node;
      });
    };
    /**监听路有变化:菜单重新渲染,自动高亮路由所在的菜单*/
    watch(
      route,
      (route: RouteLocationNormalizedLoaded) => {
        elMenuAttrs["default-active"] = route.path;
        //强制刷新组件视图
        internalInstance.proxy.$forceUpdate();
      },
      { deep: true }
    );
    /**
    <el-menu class="menu-componet" active-text-color="#ffd04b" background-color="#545c64" :default-active="route.path" text-color"="#fff" router>...</el-menu>
    */
    return () => h(ElMenu, elMenuAttrs, { default: () => [...getMenuVNode(menuList)] });
  },
};
</script>
<style lang="less" scoped>
.menu-component {
  height: 100%;
  width: 220px;
  overflow: auto;
  * {
    user-select: none;
  }
}
</style>

getModulesMenus()最终生成的数据

/** Location 是引入的ElIcon */
[
  { "name": "菜单1", "order": 1, "icon": Location, "path": "/first/index", "key": "1" },
  { "name": "菜单2", "order": 2, "icon": "iconfont icon-ding", "path": "/second/index", "key": "2" },
  {
     "name": "菜单3",
     "order": 3,
     "icon": Location,
     "key": "3",
     "children": [
       {
         "name": "菜单3-1",
         "icon": Location,
         "key": "3-1",
         "children": [
           { "name": "菜单3-1-1", "icon":Location, "path": "/third/third_1_1", "key": "3-1-1"  },
           { "name": "菜单3-1-2", "icon": "", "path": "/third/third_1_2", "key": "3-1-2" }
         ]
       },
       { "name": "菜单3-2", "icon": "", "path": "/third/third_2", "key": "3-2" },
       { "name": "菜单3-3", "icon": "", "path": "/third/third_3", "key": "3-3" }
     ]
  },
  { "name": "菜单4", "order": 4, "icon": "", "path": "/four/index", "key": "4" }
]

最终生成的模板为

<el-menu class="menu-componet" active-text-color="#ffd04b" background-color="#545c64" :default-active="route.path" text-color"="#fff" router>
	<el-menu-item index="/first/index" route="/first/index" data-tree="1" data-route="/first/index">
		<el-icon><Location /></el-icon>
		<span>菜单1</span>
	</el-menu-item>
	<el-menu-item index="/second/index" route="/second/index" data-tree="2" data-route="/second/index">
		<el-icon class="iconfont icon-ding"></el-icon>
		<span>菜单2</span>
	</el-menu-item>
	<el-sub-menu index="3" data-tree="3">
		<template #title>
			<el-icon><Location /></el-icon>
			<span>菜单3</span>
		</template>
		<el-sub-menu index="3-1" data-tree="3-1">
			<template #title>
				<el-icon><Location /></el-icon>
				<span>菜单3-1</span>
			</template>
			<el-menu-item index="/third/third_1_1" route="/third/third_1_1" data-tree="3-1-1" data-route="/third/third_1_1">
				<el-icon><Location /></el-icon>
				<span>菜单3-1-1</span>
			</el-menu-item>
			<el-menu-item index="/third/third_1_2" route="/third/third_1_2"  data-tree="3-1-2" data-route="/third/third_1_2">
				<span>菜单3-1-2</span>
			</el-menu-item>
		</el-sub-menu>
		<el-menu-item index="/third/third_2" route="/third/third_2" data-tree="3-2" data-route="/third/third_2">
			<span>菜单3-2</span>
		</el-menu-item>
		<el-menu-item index="/third/third_3" route="/third/third_3" data-tree="3-3" data-route="/third/third_3">
			<span>菜单3-3</span>
		</el-menu-item>
	</el-sub-menu>
	<el-menu-item index="/four/index" route="/four/index" data-tree="4" data-route="/four/index">
		<span>菜单4</span>
	</el-menu-item>
</el-menu>

路由显示的页面

即模块文件中component字段import的页面

<!-- /src/pages/first/index.vue -->
<script setup lang="ts">
import { ref } from "vue";
import http from "@/http";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";

const isload = ref(false);
const list = ref([]);
const router = useRouter();

const logout = async () => {
  window.localStorage.removeItem("secret_key");
  window.localStorage.removeItem("token");
  window.location.href = `/#/login`;
};
const lookUser = async () => {
  const params = {};
  isload.value = true;
  await http
    .post("users/look", params)
    .then((data: any) => (list.value = data.list))
    .catch((err: any) => {
      ElMessage({
        message: err.message,
        type: "error",
      });
    });
  isload.value = false;
};
const goToMenu2 = ()=>{
  router.push("/second/index")
}
lookUser();
</script>
<template>
  <div class="user-index">
    <el-table :data="list" style="width: 100%" v-loading="isload">
      <el-table-column prop="username" label="用户名" />
      <el-table-column prop="password" label="密码" />
      <el-table-column prop="create_time" label="创建时间" />
    </el-table>
    <el-button class="refresh-btn" @click="lookUser">刷新列表</el-button>
    <el-button class="refresh-btn" @click="logout">注销</el-button>
    <el-button class="refresh-btn" @click="goToMenu2">跳转菜单2</el-button>
  </div>
</template>
<style lang="less" scoped>
.user-index {
  width: 100%;
  .refresh-btn {
    margin-top: 20px;
  }
}
</style>
<!-- /src/pages/second/second.vue -->
<template>
  <div class="user-second">user-second</div>
</template>
<!-- /src/pages/third/third_1_1.vue -->
<template>
  <div class="user-third_1_1">third_1_1</div>
</template>
<!-- /src/pages/third/third_1_2.vue -->
<template>
  <div class="user-third_1_2">third_1_2</div>
</template>
<!-- /src/pages/third/third_2.vue -->
<template>
  <div class="user-third_2">third_2</div>
</template>
<!-- /src/pages/third/third_3.vue -->
<template>
  <div class="user-third_3">third_3</div>
</template>
<!-- /src/pages/four/index.vue -->
<template>
  <div class="user-third_3">user-four</div>
</template>

NotFound页面

<!-- /src/pages/not-found.vue -->
<script setup lang="ts">
import { useRouter } from "vue-router";

const router = useRouter();
const goBack = () => {
  router.back();
};
</script>
<template>
  <div class="user-not-found">
    <h2>404</h2>
    <div>未找到页面</div>
    <el-button @click="goBack">返回</el-button>
  </div>
</template>
<style lang="less" scoped>
.user-not-found {
  text-align: center;
}
</style>

最后修改登录页 /src/pages/login/login.vue 登录成功后的路由跳转:将router.push("/index");改为router.push("/");

最终页面效果:

在这里插入图片描述

总结一下:modules.ts文件是枢纽,用于分发路由和菜单。getModulesRoutes()分发路由,getModulesMenus()分发菜单。modules/目录是数据源,其下的模块文件是modules.ts各方法获取数据的资源库。若添加菜单menuName + [path + component],若仅添加路由path + component

状态管理

Vue状态管理可以看成是用于设置或获取Vue下的全局变量,可以供整个Vue项目使用。以前Vue使用VUEX来管理状态,Vue3推荐Pinia。不管哪个,他们都是Vue项目状态的仓储。

安装Pinia

npm install pinia

VUE中应用Pinia

/** /src/main.ts */
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import VueCookies from 'vue-cookies'
import { createPinia } from "pinia"
import "./assets/iconfont/iconfont.css" //iconfont

const pinia = createPinia();

createApp(App).use(router).use(ElementPlus).use(VueCookies).use(pinia).mount('#app')

创建Pinia仓储

/** /src/store/index.ts */
import { defineStore } from "pinia"

export const useUserStore = defineStore('user', {
    state: () => ({
        userId: 0,//存储登录用户ID
    }),
    actions: {
    	//设置登录用户ID方法
        setUserId(id: number) {
            this.userId = id;
        }
    }
})

使用仓储创建和获取信息

在登录页用户点击登录,成功后将后端返回的用户ID存储到Pinia仓储中

import { useUserStore } from "@/store/index";
//获取仓储
const userStore = useUserStore();
const submit = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      http
        .post("login/loginIn", {
          ...formData,
          password: md5(formData.password),
        })
        .then(async (data: any) => {
          if (data.code == 0) {
          	//设置用户ID到Pinia仓储
            userStore.setUserId(data.id);
            /** 其他代码省略 */
            
          } else {
            ElMessage({
              message: data.message,
              type: "error",
            });
          }
        })
        .catch((err: any) => {
          ElMessage({
            message: err.message,
            type: "error",
          });
        });
    } else {
      ElMessage({
        message: "请按提示登录",
        type: "error",
      });
    }
  });
};

在需要的页面获取仓储中存储的信息

import { useUserStore } from "@/store/index";
//获取仓储
const userStore = useUserStore();
//获取仓储中的UserId
const userId = userStore.userId;

最后,本篇文章所构建的目录结构

在这里插入图片描述

参考资料

CSDN:在vue3中使用 forceUpdate()
简书:(十四)Vue3.x核心之getCurrentInstance
Vite Glob 导入
Pinia 中文文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/834720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTB- Sau

HTB- Sau 信息收集立足root 信息收集 访问55555端口&#xff0c;得到Request Baskets。 最底下发现将详细版本暴露了出来。 这有一篇可能的ssrf漏洞文章&#xff08;这个图是postman&#xff09;。 不过似乎没有明白使用方法。在最上面还有几个功能没有尝试过。 在Settings上…

k8s之Helm安装

一、最快安装–官网提供的脚本–默认获取最新版本 cd /usr/local/src/ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 chmod 700 get_helm.sh ./get_helm.shhelm search hub wordpresssource <(helm completion bash) h…

思迈特CEO吴华夫:大模型加持下的ABI平台,彻底解决指标平台与BI的割裂之痛丨数据猿专访...

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 在现代商业环境中&#xff0c;企业的业务需求日趋复杂&#xff0c;对数据分析的依赖也越来越深。从实时销售情况到市场趋势分析&#xff0c;从客户行为研究到产品优化调整&#xff0c;每一个环节都离不开数据的支撑。然而&a…

vue3.0 bpmn-js + TS 简易教程

前言 bpmn.js是一个BPMN2.0渲染工具包和web建模器, 使得画流程图的功能在前端来完成. 这里主要记录本人在开发bpmn中的流程 参照了 LinDaiDai_霖呆呆 大佬的文档 实战 安装 bpmn npm install --save bpmn-jsHTML <template><div class"designer-container&quo…

【深度学习】Collage Diffusion,拼接扩散,论文,实战

论文&#xff1a;https://arxiv.org/abs/2303.00262 代码&#xff1a;https://github.com/VSAnimator/collage-diffusion 文章目录 AbstractIntroductionProblem Definition and Goals论文其他内容实战 Abstract 基于文本条件的扩散模型能够生成高质量、多样化的图像。然而&a…

WEB集群——tomcat

1. 简述静态网页和动态网页的区别。 2. 简述 Webl.0 和 Web2.0 的区别。 3. 安装tomcat8&#xff0c;配置服务启动脚本&#xff0c;部署jpress应用。 一、简述静态网页和动态网页的区别 &#xff08;1&#xff09;静态网页 1.什么是静态网页 请求响应信息&#xff0c;发…

解码产品经理:深度剖析这个角色的五大职责

了解产品经理的角色 在一个组织中&#xff0c;产品经理的角色是至关重要的。他们不仅需要理解市场动态和用户需求&#xff0c;还需要将这些信息转化为实际的产品策略和行动计划。产品经理在团队中的地位是核心的&#xff0c;他们需要协调各个部门&#xff0c;包括开发、设计、…

可缝合神经网络

文章目录 Stitchable Neural Networks摘要本文方法实验结果 Stitchable Neural Networks 摘要 包含大量强大的预训练模型族(如ResNet/DeiT)的model zoo已经达到了前所未有的范围&#xff0c;这对深度学习的成功有重要贡献。由于每个模型族都由具有不同尺度的预训练模型(例如&…

W5100S-EVB-PICO做DNS Client进行域名解析(四)

前言 在上一章节中我们用W5100S-EVB-PICO通过dhcp获取ip地址&#xff08;网关&#xff0c;子网掩码&#xff0c;dns服务器&#xff09;等信息&#xff0c;给我们的开发板配置网络信息&#xff0c;成功的接入网络中&#xff0c;那么本章将教大家如何让我们的开发板进行DNS域名解…

ModaHub魔搭社区——阿里云通义千问宣布开源!70亿参数模型上线魔搭社区,免费可商用

通义千问开源!8月3日,AI模型社区魔搭ModaHub上架两款开源模型Qwen-7B和Qwen-7B-Chat,阿里云确认其为通义千问70亿参数通用模型和对话模型,两款模型均开源、免费、可商用。在多个权威测评中,通义千问7B模型取得了远超国内外同等尺寸模型的效果,成为当下业界最强的中英文7B…

月报总结|Moonbeam 7月份大事一览

炎炎夏日&#xff0c;Moonbeam于越南举办了线下交流会&#xff0c;在EthCC 2023和以太坊社区成员共同讨论多链应用&#xff0c;在Polkadot Decoded中分享了Moonbeam的与众不同之处。 Bear Necessities Hackathon也于本月圆满结束&#xff0c;选出了每个赛道最杰出的项目&#…

记一次公众号-cs客户端捡漏

声明&#xff1a;文中涉及到的技术和工具&#xff0c;仅供学习使用&#xff0c;禁止从事任何非法活动&#xff0c;如因此造成的直接或间接损失&#xff0c;均由使用者自行承担责任。 点点关注不迷路&#xff0c;每周不定时持续分享各种干货。 原文链接&#xff1a;众亦信安&a…

load、unload和pagehide、pageshow

一、load、unload和pagehide、pageshow的主要应用 1&#xff09;load 和 unload 事件监听web页面的进入和离开&#xff0c;一般用于页面的首次加载、刷新和关闭等操作的监听&#xff1b; 2&#xff09;pageshow 和 pagehide 事件多用于监听浏览器的前进和后退等。 二、pagesh…

第3集丨Vue 江湖 —— Vue中的事件

目录 一、Vue中事件使用二、事件修饰符2.1 prevent2.2 stop2.3 once2.4 capture2.5 self2.6 passive2.7 修饰符连续写2.8 整合案例 三、键盘事件3.1 Vue 中常用的按键别名3.2 key值绑定3.3 系统修饰符键3.4 keyCode绑定3.5 自定义别名3.6 组合键3.7 案例整合 一、Vue中事件使用…

k8s概念-pv和pvc

回到目录 kubernetes存储卷的分类太丰富了,每种类型都要写相应的接口与参数才行&#xff0c;这就让维护与管理难度加大。 persistenvolume(PV) 是配置好的一段存储(可以是任意类型的存储卷) 也就是说将网络存储共享出来,配置定义成PV。 PersistentVolumeClaim(PVC)是用户pod使…

实例030 窗口颜色的渐变

实例说明 在程序设计时&#xff0c;可以通过设置窗体的BackColor属性来改变窗口的背景颜色。但是这个属性改变后整个窗体的客户区都会变成这种颜色&#xff0c;并且非常单调。如果窗体的客户区可以向标题栏一样能够体现颜色的渐变效果&#xff0c;那么窗体风格将会另有一番风味…

【前端实习生备战秋招】—HTML 和 CSS面试题总结(二)

【前端实习生备战秋招】—HTML 和 CSS面试题总结&#xff08;二&#xff09; 1.有哪些方式可以对一个 DOM 设置它的 CSS 样式&#xff1f; 外部样式表&#xff0c;引入一个外部 css 文件内部样式表&#xff0c;将 css 代码放在 <head> 标签内部内联样式&#xff0c;将 c…

2023年电赛E题报告模板(K210版)--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本文使用K210芯片设计了一个运动目标控制与自动追踪系统。系统包括使用深度学习进行识别激光位置&#xff0c;其中红色激…

Vue2:基础入门

Vue2&#xff1a;基础入门1 Date: April 10, 2023 Sum: vue简介、vue的基本使用、vue的指令、修饰符、过滤器&#xff08;废&#xff09;、小黑的书架、记事本 Tags: * 目标&#xff1a; 能够知道 vue 的基本使用步骤 掌握插值表达式和 v-bind 指令的用法 能够掌握如何使用…

以太网接口指示灯状态分析和电路设计

一、RJ45以太网连接器介绍 以带网络隔离变压器的RJ45接头为例&#xff0c;如HR911105A&#xff0c;其技术参数如下 原理框图 指示灯部分 二、PHY芯片 phy芯片以DP83848CVV/NOPB为例&#xff0c;查看数据手册。引脚26&#xff0c;引脚27和引脚28和LED灯相关&#xff0c;如下截…