Vue3 + TS 项目实战 - 后台管理系统 - 按钮权限

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾   

网站的打赏 —— 新一代的思路-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_57904695/article/details/136704914?spm=1001.2014.3001.5501

目录

🚩 XX银行_系统管理_按钮权限控制_前端_提测单

项目信息

提测版本信息

功能列表

测试范围

测试环境

✅ 步骤拆解分析 

 🤖 代码实现

第一步:接口返回权限信息

第二步:获取当前角色所拥有的菜单

第三步:根据项目路径url找到权限按钮中的按钮权限

第四步:如何在页面使用?如何使用更加方便?如何优化性能问题?如何在页面使用:

♻️ 完整实现过程代码


🚩 XX银行_系统管理_按钮权限控制_前端_提测单

项目信息

  • 项目名称:演示 银行
  • 产品负责人:xxx
  • 研发负责人:彩色之外
  • 测试负责人:xxx

提测版本信息

  • 版本号:V1.1
  • 源码分支:前端仓库 平台端组 / cc-management-bb· GitLab
  • 提测时间:2024.4.24
  • 预计上线时间:暂无
  • 需求WIKI:暂无

功能列表


本版本包含以下功能列表:

1、开关控制是否进行权限校验(开关在系统管理员角色下的主题配置中)

2、在菜单管理处配置按钮菜单、在角色配置处给角色勾选对应的权限,系统根据当前用户所拥有的角色对其按钮权限进行控制,主要表现为:无权限前端页面不显示按钮。

3、按钮权限控制不包含列表查询按钮

4、需要对拥有不同角色的用户进行验证

测试范围


本版本需要测试以下内容:

1、开关控制是否生效,关闭开关时,所有用户都可以看到全部的按钮,并且点击可用。打开开关时,按钮根据用户的权限进行显示。

2、在开关打开情况下,修改角色权限或者删除菜单,用户不能看到按钮。

测试环境


本版本应在以下环境下测试:

  1. 环境描述:Test 环境
  2. 访问方式 : -
  3. 登录信息:不同角色对应的用户都需要进行验证,普通用户、管理员

✅ 步骤拆解分析 

(1: )总按钮权限开关控制,是否开启。进行整个系统按钮权限校验。

  (2:  )  使用系统管理员角色的账号,配置好页面的按钮,格式如 模块:菜单:  具体按钮 类型  ( system:user:insert )

 在菜单管理处配置按钮菜单、在角色配置处给角色勾选对应的权限,系统根据当前用户所拥有的角色对其按钮权限进行控制,主要表现为:无权限前端页面不显示按钮。 

 🤖 代码实现

先说实现思路,

第一步:接口返回权限信息

键是菜单id(比如首页、用户管理每个页面都有id),值是具体权限 Array[]

{
    "code": "000000",
    "message": "操作成功",
    "data": {
        "id": 1686574963472044033,
        "loginName": "zk",
        "username": "zk",
        "userNumber": 1152254775148937216,
        "tenantId": null,
        "departmentId": null,
        "roleTag": 3,
        "buttonAuthorities": {
            "1160155571547013120": [
                "delete",
                "look"
            ],
            "1144277890263678976": [
                "update",
                "delete",
                "dataImport",
                "importHistory"
            ],
          
            "1144292976927703040": [
                "insert",
                "delete"
            ],
           
        }
    }
}

第二步:获取当前角色所拥有的菜单

获取当前角色所拥有的菜单,做一下数据映射,或者接口这样格式返回。

key是项目的动态正则url,值是菜单id,然后存入pinia。

当前角色所拥有的菜单接口响应返回如下:

{
    "code": "000000",
    "message": "操作成功",
    "data": [
        {
            "id": 201,
            "menuCode": "1160155571547013120",
            "parentMenuCode": 0,
            "menuType": "menu",
            "menuChineseName": null,
            "component": "/home/index.vue",
            "meta": {
                "icon": "ant-BankOutlined",
                "title": "message.router.home",
                "isHide": false,
                "isKeepAlive": false,
                "isAffix": false,
                "isLink": "",
                "isIframe": false,
                "roles": [
                    1,
                    2,
                    3
                ]
            },
            "children": [],
            "menuSuperior": "",
            "btnPower": "",
            "path": "/home",
            "name": "home",
            "menuSort": 0,
            "isLink": false,
            "redirect": ""
        },
       ]

格式处理成这种 取出path和menuCode

{
  "/home": "1160155571547013120",
  "/desensitizationCenter": "1144277890263678976",
  "/desensitizationCenter/policyRuleLibrary/policyLibraryView/:type/:id/:tagsViewName": "1144292976927703040",
}

Q: 为什么当前所拥有的菜单,要使用动态路径匹配url作为key?

A:因为直接精准根据url找到当前的菜单id,可以会失败,比如项目中使用了动态路由

 "/desensitizationCenter/policyRuleLibrary/policyLibraryView/:type/:id/:tagsViewName": "1144292976927703040",

第三步:根据项目路径url找到权限按钮中的按钮权限

截至目前为止,我们已经得到了按钮权限 

和当前账号下的菜单数据:
 

我们可以切换页面、使用URL来获取 mencCode与按钮权限接口做对比,

/**
 * 处理菜单和按钮数据映射-通过正则表达式来匹配动态路径
 * @param permission {string} 按钮权限
 * @param path {string} 当前路径
 * @test
 *  // 动态路径模式
 *	const menuPath = "/user/:userId/profile";
 *	// 将动态部分替换为正则表达式,以匹配任意非斜杠字符序列
 *	const regexPath = menuPath.replace(/:\w+/g, '[^/]+');
 *	// 创建正则表达式对象
 *	const regex = new RegExp(`^${regexPath}$`);
 *	// 实际路径
 *	const actualPath = "/user/123/profile";
 *	// 测试实际路径是否匹配
 *	const isMatch = regex.test(actualPath);
 *	console.log(isMatch); // 输出:true
 *
 * @returns boolean true-有权限 false-无权限
 */
const handleMenuDataMap = (
	permission: string,
	path: string = window.location.href.split('#')[1]
) => {
	if (!dataReady.value) return false;

	let matchedMenuId = null;
	// 遍历菜单数据映射对象的键
	for (const menuPath in menuDataMap.value) {
		// 将菜单路径中的动态部分替换为正则表达式-对路径进行模式匹配
		const regexPath = menuPath.replace(/:\w+/g, '[^/]+');
		// 创建正则表达式对象
		const regex = new RegExp(`^${regexPath}$`);
		// 测试实际路径是否匹配
		if (regex.test(path)) {
			matchedMenuId = menuDataMap.value[menuPath];
			break;
		}
	}
	console.log('🤺🤺 matchedMenuId 🚀 ==>:', matchedMenuId);
	if (!matchedMenuId) return;
	// 获取当前菜单的权限数组
	const permissions = Session.get('buttonAuthorities')?.[matchedMenuId] || [];
	// 判断权限数组是否包含当前权限(payload)
	return permissions.includes(permission);
};

 第四步:如何在页面使用?如何使用更加方便?如何优化性能问题?
如何在页面使用:

1:按钮权限两种使用方式:

<template>
	<div>
		<!-- 按钮权限utils版本使用(需要引入utils-Hook) -->
		<el-button v-if="permissionExports.hasDeletePermission.value">utils-删除</el-button>
		<el-button v-if="permissionExports.hasUpdatePermission.value">utils-更新</el-button>

		<!-- 按钮权限组件版本使用 -->
		<zw-permission-button class="ml15" permission="look" type="primary" @zwClick="onClickEvent"
			>components-查看</zw-permission-button
		>
		<zw-permission-button
			class="ml15"
			permission="update"
			type="danger"
			@zwClick="onClickDiyParams('自定义参数')"
			>components-更新</zw-permission-button
		>
	</div>
</template>

<script setup lang="ts" name="home">
import { permissionExports } from '/@/utils/Hooks/hasBtnPermission';

function onClickEvent(e: MouseEvent) {
	console.log(e);
}
function onClickDiyParams(params: string) {
	console.log(params);
}
</script>


2:如何使用更加方便:

本次创建了两种使用方式,一种是utils函数引入,一种作为全局组件无需引入,整个系统直接使用

3:如何优化性能问题 :

系统中无数页面,页面中不知几何的按钮,可以将数据存入本地会话,这样只有在刷新页面了才会重新发起请求,或者使用接口缓存

(axios超级封装Vue3 + Ts + Vite 封装一套企业级axiso全流程-CSDN博客)

截至为止,已经实现基本功能,当然实现过程拥有无穷无尽的,Js|Vue给一套Api,我们使用api组合各种各样的场景 ……



♻️ 完整实现过程代码

src\stores\authManage.ts  pinia仓储处理全局接口调用

import { defineStore, storeToRefs } from 'pinia';
import { getMenuTree } from '/@/api/auth-manage/menu';
import { i18n } from '/@/i18n/index';
import { Local } from '/@/utils/storage';
import { useThemeConfig } from '/@/stores/themeConfig';
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);

export const useAuthManage = defineStore('authManage', {
	state: (): any => ({
		// 菜单树
		menuTreeList: [],
		// 权限标识与描述的映射
		roleIdentifyMap: {},
		// 菜单数据拍平映射
		menuDataMap: {},
		// 按钮权限总开关
		switchBtnPermission: themeConfig.value.isEnableButtonPermissions,
	}),
	actions: {
		// 获取菜单树-当前角色树
		async queryCurrentMenuTree() {
			const { data, code } = await getMenuTree();
			if (code === '000000') {
				this.menuTreeList = this.getMenuData(data);
			}
		},

		// 处理获取到的menu数据
		getMenuData(routes: RouteItems) {
			const arr: RouteItems = [];
			routes.forEach((val: RouteItem) => {
				val['title'] = i18n.global.t(val.meta?.title as string);
				arr.push({ ...val });
				this.menuDataMap[val.path] = val.menuCode;
				if (val.children) this.getMenuData(val.children);
			});
			return arr;
		},
		// 按钮权限总开关
		async fetchAuthConfig() {
			if (Local.get('userInfo').username === 'admin') {
				this.switchBtnPermission = false;
				return;
			}
			this.switchBtnPermission = themeConfig.value.isEnableButtonPermissions;
		},
	},
});

 src\utils\Hooks\hasBtnPermission.ts  utils方法封装has权限

import { ref, computed, ComputedRef, watch } from 'vue';
// pinia
import { storeToRefs } from 'pinia';
import { useAuthManage } from '/@/stores/authManage';
const authManage = useAuthManage(); //方法直接调用
const { menuDataMap, switchBtnPermission } = storeToRefs(authManage); // 响应式变量使用storeToRefs

// utils
import { Session } from '/@/utils/storage';
import { STATUS_CODE } from '/@/enum/global';
// axios-Api
import { getUserInfo } from '/@/api/auth-manage/user';
/**
 * 在非setup 函数上下文中,无法直接使用useRoute 或 useRouter,
 * 可以通过路由示例来获取
 */
import router from '/@/router/index';

// data-refs
const buttonAuth = ref<string[]>([]); // 按钮权限数组
const dataReady = ref(false); // 数据是否准备完毕

// init-permissions-check
const initPermissionsCheck = async () => {
	await authManage.fetchAuthConfig();
	console.log('按钮权限总开关', switchBtnPermission.value);
	// 按钮权限控制打开状态
	if (switchBtnPermission.value) {
		// 本地没有菜单树数据
		notMenuDataMap();
		// 本地没有按钮权限数组
		notButtonAuthorities();
	} else {
		// 按钮权限控制已关闭,系统所有按钮显示;
		Session.remove('menuDataMap');
		Session.remove('buttonAuthorities');
		buttonAuth.value = [];
	}
	dataReady.value = true;
};

// 本地没有菜单树数据
async function notMenuDataMap() {
	if (!Session.get('menuDataMap')) {
		// 获取当前菜单拍平树数据-pinia
		await authManage.queryCurrentMenuTree();
		// 存储拍平树数据
		Session.set('menuDataMap', menuDataMap.value);
	} else {
		// 本地有菜单树数据
		menuDataMap.value = Session.get('menuDataMap');
		// console.log('🤺🤺 menuDataMap.value  🚀 ==>:', menuDataMap.value);
	}
}
// 本地没有按钮权限数组
async function notButtonAuthorities() {
	if (!Session.get('buttonAuthorities')) {
		// 获取按钮权限数组
		const { code = '', data: { buttonAuthorities = [] } = {} } = await getUserInfo();

		if (code === STATUS_CODE.SUCCESS) {
			// console.log('🤺🤺 当前菜单拍平树 🚀 ==>:', menuDataMap.value);
			// console.log('🤺🤺 按钮权限数组 🚀 ==>:', buttonAuthorities);
			// 存储按钮权限数组
			Session.set('buttonAuthorities', buttonAuthorities);
			buttonAuth.value = buttonAuthorities;
		}
	} else {
		// 本地有按钮权限数组
		buttonAuth.value = Session.get('buttonAuthorities');
		// console.log('🤺🤺 buttonAuth.value  🚀 ==>:', buttonAuth.value);
	}
}
/**
 * 使用watch来侦听路由变化,并在路由变化时重新执行权限检查逻辑
 * @warning watch侦听路由变化时不能直接监听router.currentRoute.value.path
 *   这是计算好的静态值,不是响应式数据
 */
watch(
	() => router.currentRoute.value,
	async () => {
		await initPermissionsCheck();
		// console.log('路由变化,当前路径:', router.currentRoute.value.path);
	},
	{ immediate: true }
);

/**
 * 处理菜单和按钮数据映射-通过正则表达式来匹配动态路径
 * @param permission {string} 按钮权限
 * @param path {string} 当前路径
 * @test
 *  // 动态路径模式
 *	const menuPath = "/user/:userId/profile";
 *	// 将动态部分替换为正则表达式,以匹配任意非斜杠字符序列
 *	const regexPath = menuPath.replace(/:\w+/g, '[^/]+');
 *	// 创建正则表达式对象
 *	const regex = new RegExp(`^${regexPath}$`);
 *	// 实际路径
 *	const actualPath = "/user/123/profile";
 *	// 测试实际路径是否匹配
 *	const isMatch = regex.test(actualPath);
 *	console.log(isMatch); // 输出:true
 *
 * @returns boolean true-有权限 false-无权限
 */
const handleMenuDataMap = (
	permission: string,
	path: string = window.location.href.split('#')[1]
) => {
	if (!dataReady.value) return false;

	let matchedMenuId = null;
	// 遍历菜单数据映射对象的键
	for (const menuPath in menuDataMap.value) {
		// 将菜单路径中的动态部分替换为正则表达式-对路径进行模式匹配
		const regexPath = menuPath.replace(/:\w+/g, '[^/]+');
		// 创建正则表达式对象
		const regex = new RegExp(`^${regexPath}$`);
		// 测试实际路径是否匹配
		if (regex.test(path)) {
			matchedMenuId = menuDataMap.value[menuPath];
			break;
		}
	}
	console.log('🤺🤺 matchedMenuId 🚀 ==>:', matchedMenuId);
	if (!matchedMenuId) return;
	// 获取当前菜单的权限数组
	const permissions = Session.get('buttonAuthorities')?.[matchedMenuId] || [];
	// 判断权限数组是否包含当前权限(payload)
	return permissions.includes(permission);
};

// 权限控制-按钮权限
interface PermissionComputeds {
	[key: string]: ComputedRef<boolean>;
}
/* 
  export const hasAddPermission = computed(() => dataReady.value && handleMenuDataMap('Add'));
  export const hasDelPermission = computed(() => dataReady.value && handleMenuDataMap('Del'));

*/
export const permissionsKeys = [
	'insert',
	'update',
	'delete',
	'detail',
	'apply',
	'runOnce',
	'glueIDE',
	'start',
	'stop',
	'copy',
	'updateSetting',
	'synchronization',
	'updateConfig',
	'backupHistory',
	'backup',
	'download',
	'upload',
	'dataImport',
	'importHistory',
	'publish',
	'updateDirectory',
	'updateQuestionnaire',
	'continueAnswerQuestion',
	'allocate',
	'handle',
	'downloadReportTemplate',
	'look',
	'configureMap',
	'audit',
	'customizeReport',
	'generateReport',
	'preview',
	'generateForm',
	'insertNode',
	'updateNode',
	'deleteNode',
	'updateLine',
	'insertQuestionnaire',
	'updateNodeLine',
	'deleteLine',
	'deleteQuestionnaire',
	'insertRegistration',
	'detectRegistration',
	'insertClassGrade',
	'deleteRegistrationClassGrade',
	'detectClassGrade',
	'complianceInformationInput',
	'detectComplianceInformation',
	'insertLog',
	'deleteLog',
];
const permissionComputeds: PermissionComputeds = {};

permissionsKeys.forEach((key) => {
	/**
	 * 将首字母大写,用于生成计算属性的名称
	 * 如果 key 是 'update',那么 permissionName 将会是 'hasUpdatePermission'。
	 * charAt(0) 返回字符串中的第一个字符。如果字符串是 'update',charAt(0) 将返回 'u'。
	 * slice(1) 返回从索引 1 开始到字符串末尾的子字符串。如果字符串是 'update',slice(1) 将返回 'pdate'。
	 */
	const permissionName = `has${key.charAt(0).toUpperCase() + key.slice(1)}Permission`;
	// 如果 按钮数据准备完毕且有权限并且按钮权限总开关开启
	permissionComputeds[permissionName] = computed(() => dataReady.value && handleMenuDataMap(key));
});
/* 当 switchBtnPermission.value 为 false 时,创建一个对象,其所有属性都返回 true 的计算属性。显示所有按钮,
 *   用于utils使用方法,按钮总开关权限关闭的情况
 *	Object.fromEntries将键值对列表转换为一个对象
 *	[
 *		['name', 'zk'],
 *		['age', 18],
 *	];
 *	输出: { name: 'zk', age: 18 }
 * */
export const permissionExports = switchBtnPermission.value
	? permissionComputeds
	: Object.fromEntries(
			permissionsKeys.map((key) => {
				const permissionName = `has${key.charAt(0).toUpperCase() + key.slice(1)}Permission`;
				return [permissionName, computed(() => true)];
			})
	  );

src\components\GlobalComponents\zwPermissionButton.vue 全局自动引入组件方便使用

<template>
	<div style="display: inline-block" v-bind="$attrs">
		<!-- <div>按钮总开关: {{ switchBtnPermission }}</div>
		<div>是否有权限: {{ hasPermission }}</div> -->
		<template v-if="hasPermission && switchBtnPermission">
			<!-- 权限开启 -->
			<el-button v-if="componentType === 'button'" @click="handleClick" v-bind="$attrs">
				<slot></slot>
			</el-button>
			<el-link
				v-else-if="componentType === 'link'"
				:underline="false"
				@click="handleClick"
				v-bind="$attrs"
			>
				<slot></slot>
			</el-link>
		</template>

		<template v-if="!switchBtnPermission">
			<!-- 权限关闭 -->
			<el-button v-if="componentType === 'button'" @click="handleClick" v-bind="$attrs">
				<slot></slot>
			</el-button>
			<el-link
				v-else-if="componentType === 'link'"
				:underline="false"
				@click="handleClick"
				v-bind="$attrs"
			>
				<slot></slot>
			</el-link>
		</template>
	</div>
</template>

<script setup lang="ts">
// <!-- 权限控制的按钮组件 Component -->

import { computed, ComputedRef } from 'vue';
import { permissionExports, permissionsKeys } from '/@/utils/Hooks/hasBtnPermission';
import { storeToRefs } from 'pinia';
import { useAuthManage } from '/@/stores/authManage';
const authManage = useAuthManage();
const { switchBtnPermission } = storeToRefs(authManage);

const props = defineProps({
	permission: {
		type: String,
		required: true,
	},
	componentType: {
		type: String,
		default: 'link', // 可以是'button'或'link'
	},
});

const emit = defineEmits(['zwClick']);

interface PermissionMap {
	[key: string]: ComputedRef<boolean>;
}

// 创建一个权限映射对象
// const permissionMap: PermissionMap = {
// 	list: permissionExports.hasListPermission,
// 	insert: permissionExports.hasInsertPermission,
// };
const permissionMap: PermissionMap = permissionsKeys.reduce((acc, key) => {
	// update --> hasUpdatePermission
	const permissionName = `has${key.charAt(0).toUpperCase() + key.slice(1)}Permission`;
	acc[key] = permissionExports[permissionName];
	return acc;
}, {} as PermissionMap);
// console.log('🤺🤺 permissionMap 🚀 ==>:', permissionMap);

const hasPermission: ComputedRef<boolean> = computed(() => {
	// 使用权限字符串直接从映射中获取计算属性
	const permissionCheck = permissionMap[props.permission as keyof typeof permissionMap];
	return permissionCheck ? permissionCheck.value : false;
});

function handleClick(event: MouseEvent) {
	// 触发父组件绑定的click事件
	emit('zwClick', event);
	/* 
		得到子组件的event
		<zw-permission-button
						@zwClick="handleIsEfficacy"
						>{{ statusTypeComputed(row.status)[2] }}</zw-permission-button
					>
		自定义参数
		<zw-permission-button
					@zwClick="handleIsEfficacy(row.id, row.status)"
					>{{ statusTypeComputed(row.status)[2] }}</zw-permission-button
				>
	*/
}
defineExpose({
	hasPermission: hasPermission.value,
});
</script>

 按钮权限两种使用方式

<template>
	<div>
		<!-- 按钮权限utils版本使用(需要引入utils-Hook) -->
		<el-button v-if="permissionExports.hasDeletePermission.value">utils-删除</el-button>
		<el-button v-if="permissionExports.hasUpdatePermission.value">utils-更新</el-button>

		<!-- 按钮权限组件版本使用 -->
		<zw-permission-button class="ml15" permission="look" type="primary" @zwClick="onClickEvent"
			>components-查看</zw-permission-button
		>
		<zw-permission-button
			class="ml15"
			permission="update"
			type="danger"
			@zwClick="onClickDiyParams('自定义参数')"
			>components-更新</zw-permission-button
		>
	</div>
</template>

<script setup lang="ts" name="home">
import { permissionExports } from '/@/utils/Hooks/hasBtnPermission';

function onClickEvent(e: MouseEvent) {
	console.log(e);
}
function onClickDiyParams(params: string) {
	console.log(params);
}
</script>


扩展:Q1:如何全局组件封装?Q2:如何封装一套企业级的axios前端接口

A1:Vue3项目 —— Vite / Webpack 批量注册组件_vue3批量注册组件-CSDN博客

A2:Vue3 + Ts + Vite 封装一套企业级axiso全流程-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[paper note]代码生成评估模型-CodeBLEU原理分析

论文信息 论文标题&#xff1a;CodeBLEU: a Method for Automatic Evaluation of Code Synthesis 发表时间&#xff1a;2020年9月 论文原文&#xff1a;CodeBLEU: a Method for Automatic Evaluation of Code Synthesis 论文内容 摘要 评价指标对一个领域的发展起着至关重…

大厂常见算法50题-替换空格

专栏持续更新50道算法题&#xff0c;都是大厂高频算法题&#xff0c;建议关注, 一起巧‘背’算法! 文章目录 题目解法一 String类replace方法解法二 遍历替换总结 题目 解法一 String类replace方法 String类自带的replace&#xff0c;方法传入两个char类型的参数&#xff0c;分…

分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测

分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测 目录 分类预测 | Matlab实现CNN-GRU-SAM-Attention卷积门控循环单元融合空间注意力机制的数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现CNN-GRU…

蓝牙低能耗安全连接 – 数值比较

除了 LE Legacy 配对之外&#xff0c;LE Secure Connections 是另一种配对选项。 LE 安全连接是蓝牙 v4.2 中引入的增强安全功能。它使用符合联邦信息处理标准 (FIPS) 的算法&#xff08;称为椭圆曲线 Diffie Hellman (ECDH)&#xff09;来生成密钥。对于 LE 安全连接&#xff…

【Stream流基础篇】Java中的函数、函数对象、函数接口和方法引用及转换

什么是函数 在数学中&#xff0c;函数是这样定义的&#xff1a;它是给定一个数集A&#xff0c;假设其中的元素为x&#xff0c;对A中的元素x施加对应法则f&#xff0c;记作f&#xff08;x&#xff09;&#xff0c;得到另一数集B&#xff0c;假设B中的元素为y&#xff0c;则y与x…

pytorch中的过拟合和欠拟合

基本概念 我们知道&#xff0c;所谓的神经网络其实就是一个复杂的非线性函数&#xff0c;网络越深&#xff0c;这个函数就越复杂&#xff0c;相应的表达能力也就越强&#xff0c;神经网络的训练则是一个拟合的过程。   当模型的复杂度小于真实数据的复杂度&#xff0c;模型表…

GMSSL编译iOS

一、GMSSL-2.x 国密SDK源码下载&#xff0c;对GMSSL库进行编译生成对应的静态库。执行如下命令&#xff1a; cd到SDK源码目录 cd /Users/xxxx/Downloads/GMSSLV2-master查看SDK适用环境 ./config上图中错误解决方法 使用文本编辑器打开SDK目录下Configure、test/build.info、…

【STM32F407+CUBEMX+FreeRTOS+lwIP之UDP记录】

STM32F407CUBEMXFreeRTOSlwIP之UDP记录 基本信息cubemx配置GPIONVICRCCSYSETHFREERTOSlwIP UDP&#xff08;SOCKET&#xff09;效果 UDP广播&#xff08;SOCKET&#xff09;效果 UDP组播&#xff08;SOCKET&#xff09;cubemx注意以下ethernetif.c效果 可参考正点原子和野火的手…

深度学习论文:Local Feature Matching Using Deep Learning: A Survey

深度学习论文: Local Feature Matching Using Deep Learning: A Survey Local Feature Matching Using Deep Learning: A Survey PDF: https://arxiv.org/pdf/2401.17592 1 概述 近年来&#xff0c;深度学习模型的引入引发了对局部特征匹配技术的广泛探索。本文旨在全面概述局…

go语言实现简单ngnix样例

目录 1、代码实现样例&#xff1a; 2、postman调用ngnix&#xff0c;转发&#xff1a; 1、代码实现样例&#xff1a; package mainimport ("bytes""encoding/json""io""log""net/http""net/http/httputil""…

防止核心研发数据流失:管理者跳槽怎么办?

在高速发展的科技行业中&#xff0c;核心研发数据是企业最宝贵的资产之一。然而&#xff0c;当高层管理人员或核心技术人员因跳槽等原因离开公司时&#xff0c;他们可能会无意中或有意地携带走企业的核心研发数据&#xff0c;这对于任何企业来说都是一个巨大的风险。为了有效地…

Vue---组件

Vue—组件 目录 Vue---组件定义组件全局组件局部组件 组件通讯***重点***父子通信之父传子&#xff08;props&#xff09;父子通信之子传父&#xff08;$emit&#xff09;ref属性&#xff08;$refs&#xff09; 动态组件插槽命名插槽 定义组件 全局组件 vue2中template只能传…

ArcGIS小技巧—基于点数据的密度分析(含练习数据)

关于空间点数据的密度分析&#xff0c;Arcgis Map中提供了基础的点密度分析和核密度分析。核密度分析可以通过手动设置搜索半径&#xff0c;调整密度分布的合理性。 但有时由于实际工作的需要&#xff0c;我们需要对研究范围做特定划分&#xff0c;比如根据格网规则划分做密度…

Flask框架进阶-Flask流式输出和受访配置--纯净详解版

Flask流式输出&#x1f680; 在工作的项目当中遇到了一种情况&#xff0c;当前端页面需要对某个展示信息进行批量更新&#xff0c;如果直接将全部的数据算完之后&#xff0c;再返回更新&#xff0c;则会导致&#xff0c;前端点击刷新之后等待时间过长&#xff0c;开始考虑到用进…

电脑录制视频快捷键,一键开启录屏新时代(干货)

“最近尝试录制一些电脑上的操作视频&#xff0c;用来制作教学教程。不过&#xff0c;每次录制都要通过菜单或搜索来打开录屏软件&#xff0c;实在是有些繁琐。有没有人知道哪些电脑录制视频的快捷键呀&#xff1f;或者有没有通用的快捷键设置方法&#xff1f;” 在当今数字时…

CMake+qt+Visual Studio

#使用qt Creator 创建Cmake 项目,使用Cmake Gui 生成sln 工程&#xff0c;使用Visual Studio 开发 ##使用qt Creator 创建CMake项目 和创建pro工程的步骤一致&#xff0c;只是在选择构建系统的步骤上选择CMake,接下来步骤完全相同 工程新建完成之后&#xff0c;构建cmake 项…

PE文件(三)节表作业

本次作业以notepad进行演示&#xff0c;如下是其在硬盘上的内存 1.手动解析节表 由标准pe头可知&#xff0c;一共由7个节也就是7个节表&#xff0c;可选pe头的大小是0X00F0&#xff0c;即240字节大小 根据上述我们所获取的信息&#xff0c;找到节表的首地址为0x01F8 .text …

微服务:Nacos注册中心

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Nacos注册中心 一、服务注册与发现1.启动Nacos…

Vite proxy-rewrite 属性详解

在前端开发中&#xff0c;为了避免跨域问题&#xff0c;我们会在vite.config.ts 中配置如下问题 rewrite: 由于不了解Nginx的知识&#xff0c;这个属性一直困扰着我&#xff0c;这个重写有啥用&#xff0c;加和不加有啥影响 server: {host: 0.0.0.0,proxy: {/api: {target: ht…

手机通讯录删除了怎么恢复?这里几个方法超快找回!

当我们不小心删除了手机通讯录中的联系人&#xff0c;或者手机丢失导致通讯录信息丢失&#xff0c;恢复通讯录就变得非常重要了。手机通讯录删除了怎么恢复&#xff1f;我们该如何快速找回这些重要的联系人信息呢&#xff1f;下面我们将介绍2种简单易行的方法&#xff0c;帮助您…