快速上手Vue3国际化 (i18n)
文章目录
- 一、背景介绍
- 二、页面效果
- 三、使用步骤
- 四、代码
- 1.src/App.vue
- 2.src/main.js
- 3.src/locales/index.js
- 4.src/views/login/_request.js
- 5.src/locales/en.json
- 6.src/locales/zh.json
- 7.SystemParam.vue
- 8.I18NController.java
- 9.DataServiceConfigValue.java
- 10.ConfigValue.java
- 11.application.properties
- 12.SortedResourceBundle.java
- 13.messages_zh_CN.properties
- 14.messages_en_US.properties
- 15.messages_ru_RU.properties
- 16.LanguageTypeEnum
- 五、注意点
- 本人其他相关文章链接
一、背景介绍
项目前后端想支持统一的国际化词条,当前端修改语言类型选项时,页面自动刷新同时加载最新的语言词条效果。
二、页面效果
三、使用步骤
- 安装vue-i18n,在Vue 3项目中,使用npm或yarn安装vue-i18n插件。
# 使用 npm
npm install vue-i18n@next# 使用 yarn
yarn add vue-i18n@next
- 安装完成后,需要在项目入口文件(通常是main.js或main.ts)中配置vue-i18n。
- 创建语言包
src/
├── locales/
│ ├── en.json
│ └── zh.json
│ └── index.js
- 整合语言包,在src/locales/index.js或src/locales/index.ts中整合所有语言包。
- 在组件中使用国际化
<div class="param" style="margin-top: 0"><a-spin style="width: 70%"><div class="param-title"><div class="param-title-text">{{ $t("LanguageSetting") }}:</div></div><div class="param-content"><param-item style="display: flex; align-items: center; gap: 20px"><template #label><span class="label">{{ $t("LanguageChoice") }}:</span></template><a-selectv-model="addParam.languageChoice"style="width: 260px; margin-top: -6px"@change="changeLanguageChoice"><a-option value="0" :label="$t('English')"></a-option><a-option value="1" :label="$t('Chinese')"></a-option><a-option value="2" :label="$t('Russian')"></a-option></a-select></param-item></div></a-spin>
</div>
四、代码
1.src/App.vue
<template><router-view/>
</template><script setup>
import {reactive, provide} from "vue";
import {useI18n} from "vue-i18n";
import {useStore} from "@/stores";
import {getSystemLanguage} from "@/views/pages/system/system";
const userInfo = reactive({userId: '',token: ''
})
provide('t', useI18n().t)
provide('userInfo', userInfo)const getSystemMode = () => {getSystemLanguage().then(response => {const mode = response.dataif (mode) {localStorage.setItem('xnms-mode', mode)useStore().setMode(parseInt(mode))}})
}getSystemMode()
</script><style>
</style>
2.src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from "pinia";
import router from '@/router'
import getI18n from '@/locales';
import SelfComponents from "@/views/pages/_common/selfComponents/index";
import ArcoVue, {Message} from '@arco-design/web-vue';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import '@arco-design/web-vue/dist/arco.css';
import '@/assets/index.less';
window.Message = Message
if (process.env.NODE_ENV === 'development') {// import('@/mock')
}const store = createPinia()const promise = Promise.all([getI18n()])
const _beforeMount = await promiseconst app = createApp(App)
app.use(_beforeMount[0]).use(router).use(ArcoVue).use(ArcoVueIcon).use(store).use(SelfComponents)
app.mount('#app')
3.src/locales/index.js
import { createI18n } from 'vue-i18n'
import {getI18nLanguagePkg, getNowLanguageType} from "@/views/login/_request";const getNowLanguage = async () => {return new Promise((resolve, reject) => {getNowLanguageType().then(response => {if (response.code === 200) {resolve(response.data)} else {window.Message.warning(response.msg)}})})
}const loadRemoteMessages = async (language) => {return new Promise((resolve, reject) => {getI18nLanguagePkg(language).then(response => {if (response.code === 200) {const messages = {}messages[languageEnum[language]] = response.dataresolve(messages)} else {window.Message.warning(response.msg)}})})
}const getI18n = async () => {const language = await getNowLanguage()localStorage.setItem('xnms-language', language)const remoteMessages = await loadRemoteMessages(language)const i18n = createI18n({legacy: false,locale: languageEnum[language],fallbackLocale: 'zh',messages: remoteMessages,globalInjection: true})return new Promise((resolve) => {resolve(i18n)})
}const languageEnum = {0: 'en',1: 'zh',2: 'ru'
}export default getI18n
4.src/views/login/_request.js
import {$http, Method} from "@/http";export const getNowLanguageType = () => {return $http({url: `/api/i18n`,method: Method.GET,})
}export const getI18nLanguagePkg = (language) => {return $http({url: `/api/i18n/${language}`,method: Method.GET,})
}export const switchLanguage = (language) => {return $http({url: `/api/i18n/${language}`,method: Method.PUT,})
}// 获取系统类型 0:常规系统 1:XPT系统
export const getSystemType = () => {return $http({url: `/api/config`,method: Method.GET,})
}
5.src/locales/en.json
{"menu_topology": "Topology View","menu_alarm": "Monitoring Alarm","menu_device": "Equipment Parameters","menu_data": "Data Query","menu_business": "Business Statistics",
}
6.src/locales/zh.json
{"menu_topology": "拓扑展示","menu_alarm": "监控告警","menu_device": "设备参数","menu_data": "数据查询","menu_business": "业务统计",
}
7.SystemParam.vue
<div class="param" style="margin-top: 0"><a-spin style="width: 70%"><div class="param-title"><div class="param-title-text">{{ $t("LanguageSetting") }}:</div></div><div class="param-content"><param-item style="display: flex; align-items: center; gap: 20px"><template #label><span class="label">{{ $t("LanguageChoice") }}:</span></template><a-selectv-model="addParam.languageChoice"style="width: 260px; margin-top: -6px"@change="changeLanguageChoice"><a-option value="0" :label="$t('English')"></a-option><a-option value="1" :label="$t('Chinese')"></a-option><a-option value="2" :label="$t('Russian')"></a-option></a-select></param-item></a-spin>
</div>
8.I18NController.java
package com.xnms.client.service.controller.i18n;import com.xnms.client.service.controller.common.ResponseModel;
import com.xnms.client.service.exception.XnmsException;
import com.xnms.data.contract.util.SortedResourceBundle;
import com.xnms.data.service.util.DataServiceConfigValue;
import com.xnms.service.center.utility.ConfigValue;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.Map;@Api(tags = "国际化")
@RestController
@RequestMapping("/api/i18n")
public class I18NController {private static final Logger logger = LoggerFactory.getLogger(I18NController.class);@Autowiredprivate ConfigValue configValue;@Autowiredprivate DataServiceConfigValue dataServiceConfigValue;/*** // 英文* ENGLISH,* // 中文* CHINESE* RUSSIAN;** @return*/@Operation(summary = "根据语言获取国际化列表,ENGLISH: 0,CHINESE:1,RUSSIAN:2")@GetMapping(value = "/{language}")public ResponseModel<Map<Object, Object>> getLanguageProperties(@PathVariable("language") Integer language) {Map<Object, Object> objectObjectMap = SortedResourceBundle.keyValueFromProperties(language);if (objectObjectMap == null) {throw new XnmsException("GetLanguageDataFailed");}return ResponseModel.ofSuccess(objectObjectMap);}@Operation(summary = "获取当前系统的语言")@GetMappingpublic ResponseModel<Integer> getSystemLanguage() {return ResponseModel.ofSuccess(Integer.parseInt(configValue.obtainConfigValue("xnms.client.Language")));}@Operation(summary = "切换系统的语言")@PutMapping(value = "/{language}")public ResponseModel<String> switchLanguage(@PathVariable("language") Integer language) {String res = configValue.updateConfig("xnms.client.Language", language);dataServiceConfigValue.writeConfig("xnms.client.Language", String.valueOf(language));return ResponseModel.ofSuccess(res);}
}
9.DataServiceConfigValue.java
package com.xnms.data.service.util;import com.xnms.data.contract.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;@Component
public class DataServiceConfigValue {private static final Logger logger = LoggerFactory.getLogger(DataServiceConfigValue.class);//todo 标记一下,用这个和@value是不是都可以@Autowiredprivate Environment env;@Autowiredprivate ApplicationContext applicationContext;//todo 写入配置文件怎么生效在项目里/// 写入配置文件/// <param name="key">键</param>/// <param name="value">值</param>public void writeConfig(String key, String value) {String workMode = env.getProperty("xnms.service.center.WorkMode");// 获取配置文件路径Resource resource = applicationContext.getResource("classpath:application.properties");if (Objects.equals(workMode, "0")) {resource = applicationContext.getResource("classpath:application-cvt.properties");}if (Objects.equals(workMode, "1")) {resource = applicationContext.getResource("classpath:application-xpt.properties");}// 用于存储文件原始内容List<String> lines = new ArrayList<>();try (InputStream inputStream = resource.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = reader.readLine()) != null) {lines.add(line); // 保存每行内容}} catch (IOException e) {logger.error("ConfigService, writeConfig method, failed to read file:", e);return;}// 修改配置内容boolean keyFound = false;for (int i = 0; i < lines.size(); i++) {String line = lines.get(i).trim();// 跳过空行和注释行if (line.isEmpty() || line.startsWith("#")) {continue;}// 检查是否匹配到目标keyif (line.startsWith(key + "=")) {lines.set(i, key + "=" + value); // 修改指定键值对keyFound = true;break;}}if (keyFound) {try {// 获取文件的物理路径File file = resource.getFile();// 使用 FileWriter 覆盖原文件try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {for (String line : lines) {writer.write(line);writer.newLine(); // 保证每行后面换行}writer.flush();}} catch (IOException e) {logger.error("ConfigService, writeConfig method, failed to write file:", e);}} else {logger.warn("没有找到指定的key: {}", key);}}public String obtainConfigValue(String key) {String configValue = "";try {configValue = env.getProperty(key);} catch (Exception ex) {logger.error("[XNMSProxyService] ObtainConfigValue :" + ex.getMessage(), ex);}return configValue;}
}
10.ConfigValue.java
package com.xnms.service.center.utility;import com.xnms.data.contract.Utility;
import com.xnms.data.contract.database.db.QuerySystemParamParams;
import com.xnms.data.contract.security.DecryptionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;//todo 读取配置先这么写,打标记
@Component
public class ConfigValue {private static final Logger logger = LoggerFactory.getLogger(ConfigValue.class);private Map<String, Object> dynamicConfigMap = new HashMap<>();@Autowiredprivate Environment env;@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate ConfigurableApplicationContext context;public String updateConfig(String key, Integer value) {// 获取 ConfigurableEnvironmentConfigurableEnvironment environment = (ConfigurableEnvironment) applicationContext.getEnvironment();// 更新动态配置的 MapdynamicConfigMap.put(key, value);// 使用自定义的 PropertySource 来存储动态配置PropertySource<?> propertySource = new org.springframework.core.env.MapPropertySource("dynamicConfig",dynamicConfigMap);environment.getPropertySources().remove("dynamicConfig");environment.getPropertySources().addFirst(propertySource);return "ok";}
}
11.application.properties
xnms.client.Language=1
12.SortedResourceBundle.java
package com.xnms.data.contract.util;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class SortedResourceBundle extends ListResourceBundle {private static final Logger logger = LoggerFactory.getLogger(SortedResourceBundle.class);private final List<Object[]> content;// 构造函数:使用 LinkedHashMap 来确保顺序public SortedResourceBundle(Map<String, String> properties) {content = new ArrayList<>();for (Map.Entry<String, String> entry : properties.entrySet()) {content.add(new Object[]{entry.getKey(), entry.getValue()});}}@Overrideprotected Object[][] getContents() {return content.toArray(new Object[0][]);}// 使用 LinkedHashMap 读取 properties 文件并保持顺序public static SortedResourceBundle fromProperties(String fileName, boolean bool) throws IOException {Map<String, String> sortedProperties = new LinkedHashMap<>();try (InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) {if (inputStream == null) {throw new FileNotFoundException("Resource file not found: " + fileName);}try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = reader.readLine()) != null) {// 跳过空行和注释if (line.trim().isEmpty() || line.startsWith("#")) continue;// 分割键值对String[] keyValue = line.split("=", 2);if (keyValue.length == 2) {String v = null;if (keyValue[1] != null) {if (bool) {v = unicodeToString(keyValue[1].trim());} else {v = keyValue[1].trim();}}sortedProperties.put(keyValue[0].trim(), v);}}}}return new SortedResourceBundle(sortedProperties);}public static Map<Object, Object> keyValueFromProperties(Integer language) {Map<Object, Object> KeyValue = new LinkedHashMap<>();try {SortedResourceBundle bundle = null;// 使用自定义的方法加载 properties 文件if (Objects.equals(LanguageTypeEnum.CHINESE.ordinal(), language)) {bundle = SortedResourceBundle.fromProperties("messages_zh_CN.properties", Boolean.TRUE);} else if (Objects.equals(LanguageTypeEnum.ENGLISH.ordinal(), language)) {bundle = SortedResourceBundle.fromProperties("messages_en_US.properties", Boolean.FALSE);} else if (Objects.equals(LanguageTypeEnum.RUSSIAN.ordinal(), language)) {bundle = SortedResourceBundle.fromProperties("messages_ru_RU.properties", Boolean.TRUE);} else {return null;}// 输出所有的键值对,确保顺序for (Object[] entry : bundle.getContents()) {KeyValue.put(entry[0], entry[1]);}} catch (Exception e) {logger.error("SortedResourceBundle类,KeyValueFromProperties方法:", e);return null;}return KeyValue;}public static String unicodeToString(String unicodeStr) {// 如果字符串中没有 "\\u" 转义编码,直接返回原字符串if (!unicodeStr.contains("\\u")) {return unicodeStr;}// 正则表达式,用于匹配 "\\u" 后面的 4 位十六进制数字String regex = "\\\\u([0-9a-fA-F]{4})";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(unicodeStr);StringBuffer result = new StringBuffer();// 循环匹配并替换while (matcher.find()) {// 获取匹配的 Unicode 码点并转换为字符String unicode = matcher.group(1);char character = (char) Integer.parseInt(unicode, 16);matcher.appendReplacement(result, String.valueOf(character));}matcher.appendTail(result);return result.toString();}
}
13.messages_zh_CN.properties
Title=XNMS客户端
XPTTitle=XNMS
Login=登 录
LoginCancel=取消登录
LoginCheckPassword=验证中…
...
14.messages_en_US.properties
Title=XNMS Client
XPTTitle=XNMS
Login=Login
LoginCancel=Cancel
LoginCheckPassword=Verifying...
...
15.messages_ru_RU.properties
Title=XNMS клиент
XPTTitle=XNMS
Login=Логин
LoginCancel=Отмена
LoginCheckPassword=Проверка...
...
16.LanguageTypeEnum
package com.xnms.data.contract.util;public enum LanguageTypeEnum {// 英文ENGLISH,// 中文CHINESE,RUSSIAN;
}
五、注意点
注意点1:
配置文件对应的值,【0:英文、1:中文、2:俄文】
注意点2:
梳理下国际化完整执行流程
- 配置文件存储初始化的语言类型,默认“中文”显示
- 首先执行 main.js 文件。这个文件通常负责创建 Vue 实例,并将根组件(通常是 App.vue)挂载到 DOM 中。
- 读取index.js文件文件,调用接口分别用于获取当前语言类型和加载语言包
- 创建 i18n 实例
- getI18n: 这是主要的异步函数,用于创建 i18n 实例。
- 它首先调用 getNowLanguage 来获取当前语言,并将其存储在 localStorage 中。
- 然后,它调用 loadRemoteMessages 来加载该语言的消息数据。
- 使用 createI18n 创建一个 i18n 实例,配置如下:
- legacy: false: 表示使用 Composition API 的模式。
- locale: 当前语言。
- fallbackLocale: 设置回退语言为中文(zh)。
- messages: 加载的语言包。
- globalInjection: true: 允许在模板中全局使用 $t 和 $tc 方法。
- 最后,返回一个解析为 i18n 实例的 Promise。
- 返回main.js中执行APP挂载,此刻初始化完成,展示所有菜单和词条中文效果。
- 如果此刻进入系统参数配置,更改语言类型,比如更选“英文”,优先调接口更新配置文件值
- 然后页面会强制大刷新一遍,然后从步骤2再重新执行一遍,此刻页面就变成了“英文”效果了。
注意点3:
看过一次别人的博客,写法如下:
main.js
import { createApp } from 'vue';
import App from './App.vue';
import { createI18n } from 'vue-i18n';// 导入语言包
import messages from './locales';// 创建i18n实例
const i18n = createI18n({locale: 'en', // 默认语言fallbackLocale: 'en', // 回退语言messages, // 语言包
});const app = createApp(App);// 挂载i18n
app.use(i18n);app.mount('#app');
index.js
import en from './en.json';
import zh from './zh.json';export default {en,zh,
};
问题:他的写法和我的写法区别在哪?使用场景会有不同吗?
答案
:
您提到的两种实现方式都可以成功地将国际化(i18n)集成到 Vue 3 项目中,但它们在结构和实现细节上有所不同。以下是这两种方式的主要区别和各自的优缺点:
-
语言包的导入方式
第一种方式
:
import { createI18n } from 'vue-i18n'; import messages from './locales';
在这个示例中,语言包 (messages) 是直接从一个文件导入的,通常这个文件会导出一个对象,包含所有语言的翻译文本。这种方式比较直接,适合简单的项目。
第二种方式
:
import getI18n from '@/locales'; const promise = Promise.all([getI18n()]) const _beforeMount = await promise
在这个示例中,getI18n 是一个函数,它可能异步加载语言包。这种方式在处理大型项目或需要动态加载语言包的情况下非常有用,可以提高性能。
-
i18n 实例的创建方式
第一种方式
:
const i18n = createI18n({locale: 'en',fallbackLocale: 'en',messages, });
这里直接创建了一个 i18n 实例,并传入了语言设置和语言包。
第二种方式
:
const app = createApp(App) app.use(_beforeMount[0])
在这种方式中,getI18n() 函数返回的 i18n 实例可能是在异步操作中创建的。通过 Promise.all 等待所有必要的异步操作完成后,再将 i18n 实例挂载到 Vue 应用中。
-
错误处理和可扩展性
第一种方式
:适合小型项目,结构简单,易于理解和维护。第二种方式
:适合大型项目,可以更灵活地处理语言包的加载,支持异步加载和动态切换语言的需求。
- 性能
第一种方式
:所有语言包在应用启动时就加载到内存中,可能会导致初始加载时间较长,尤其是语言包比较大的时候。第二种方式
:可以实现按需加载语言包,提升性能,尤其是在用户只需要某种语言的情况下。
总结
两种方式都可以正常工作,选择哪种方式取决于您的项目需求和复杂性。如果您的应用需要支持多语言且语言包较大,或者希望在不同条件下动态加载语言包,第二种方式可能更合适。如果您的应用相对简单,第一种方式可能更直接且易于实现。
注意点4:
App.vue中的下方代码是为了给其他组件进行国际化解析的,无论是标签解析,还是方法中的词条解析。
import {useI18n} from "vue-i18n";
provide('t', useI18n().t)
注意点5:
- 标签中解析使用:
$t("LanguageChoice")
- 方法中解析使用:
t("invalidCerFileType")
<template #label><span class="label">{{ $t("LanguageChoice") }}:</span></template>
<a-option value="0" :label="$t('English')"></a-option>const importLicenseFunction = (fileList) => {...if (!allowedExtensions.exec(lastFileName)) {Message.error(t("invalidCerFileType"));return;}
}
注意点6:
App.vue中的下方代码只是在项目初始化加载时查询语言类型更新到相应缓存中而已,供后续其他组件从缓存直接获取使用。
const getSystemMode = () => {getSystemLanguage().then(response => {const mode = response.dataif (mode) {localStorage.setItem('xnms-mode', mode)useStore().setMode(parseInt(mode))}})
}
注意点7:
index.js中的代码写法,实际上是初始化空对象,给 messages 对象添加一个属性,属性名是 languageEnum[language] 的值,属性值是 response.data。
const messages = {}
messages[languageEnum[language]] = response.data
最后实际获取的messages 对象可能看起来像这样:
{'zh-CN': { /* response.data 的内容 */ }
}
注意点8:
获取的语言包数据赋值给messages,这是为了将从远程获取的语言包数据传递给国际化(i18n)库,以便在页面中进行语言解析和显示。(即此刻页面所有{{ $t(“LicenseManagement”) }}实际是从i18n库中获取的词条内容)
const remoteMessages = await loadRemoteMessages(language)
...
messages: remoteMessages,
本人其他相关文章链接
1.vue3 开发电子地图功能
2.vue java 实现大地图切片上传
3.java导入excel更新设备经纬度度数或者度分秒
相关文章:
快速上手Vue3国际化 (i18n)
文章目录 一、背景介绍二、页面效果三、使用步骤四、代码1.src/App.vue2.src/main.js3.src/locales/index.js4.src/views/login/_request.js5.src/locales/en.json6.src/locales/zh.json7.SystemParam.vue8.I18NController.java9.DataServiceConfigValue.java10.ConfigValue.ja…...
Mistral OCR:重新定义文档理解的下一代 OCR 技术
引言 在数字化时代,文档处理和理解是企业、科研机构以及个人工作流程中的重要环节。然而,传统的光学字符识别(OCR)技术往往难以应对复杂文档中的多语言、多模态内容。近日,法国 AI 明星创企 Mistral AI 推出了一款名为 Mistral OCR 的光学字符识别 API,以其卓越的性能和…...
前端面试核心知识点整理:从 JavaScript 到 Vue 全解析
一、JavaScript 异步编程核心:Promise 与 async/await 1. Promise 深度解析 定义:Promise 是处理异步操作的对象,代表一个异步操作的最终状态(成功 / 失败)。三种状态: pending(进行中):初始状态,异步操作未完成。fulfilled(已成功):异步操作成功,调用 resolve …...
npm fund 命令的作用
运行别人的项目遇到这个问题: npm fund 命令的作用 npm fund 是 npm 提供的命令,用于显示项目依赖中哪些包需要资金支持。这些信息来自包的 package.json 中定义的 funding 字段,目的是帮助开发者了解如何支持开源维护者。 典型场景示例 假…...
LeetCode344反转字符串
思路: 交换即可 void reverseString(char* s, int sSize) {int jsSize-1;for(int i0;i<sSize/2;i){int tmps[i];s[i]s[j];s[j]tmp;j--;} }...
[Python] 企业内部应用接入钉钉登录,端内免登录+浏览器授权登录
[Python] 为企业网站应用接入钉钉鉴权,实现钉钉客户端内自动免登授权,浏览器中手动钉钉授权登录两种逻辑。 操作步骤 企业内部获得 开发者权限,没有的话先申请。 访问 钉钉开放平台-应用开发 创建一个 企业内部应用-钉钉应用。 打开应用…...
设计模式-单例设计模式
目录 什么是单例设计模式? 为什么要使用单例模式? 资源方面 数据一致方面 系统性能方面 代码维护方面 如何设计单例类? 在说模式之前,我们需要先知道怎么设计才可以让一个类只能有一个实例化对象呢? 饿汉模式…...
Nextjs15 实战 - React Notes CURD 实现
本专栏内容均可在Github:notes_04 找到 完整项目使用技术栈: Nextjs15 MySQL Redis Auth Prisma i18n strapi Docker vercel 一、本节目标 本篇我们来实现右侧笔记CURD部分。 一、效果 当点击 New 按钮的时候进入编辑界面: 当点击…...
【KWDB 创作者计划】架构设计与AIoT场景实践
产品定位与核心价值主张 架构设计与技术实现 分布式架构设计 多模存储引擎实现 云边端协同机制 核心技术创新解析 就地计算技术 自适应时序引擎 混合事务处理 性能优化技术体系 高效存储机制 查询加速策略 资源管理与隔离 行业解决方案与典型应用 工业物联网平台…...
DeepSeek底层揭秘——《推理时Scaling方法》技术对比浅析
4月初,DeepSeek 提交到 arXiv 上的最新论文正在 AI 社区逐渐升温。 笔者尝试对比了“关于推理时Scaling”与现有技术,粗浅分析如下: 与LoRA的对比 区别: 应用场景:LoRA是一种参数高效微调方法,主要用于在…...
Spring MVC与Spring Boot文件上传配置差异对比及文件上传关键类详细说明与对比
一、Spring MVC与Spring Boot文件上传配置差异对比 1. 配置方式差异 框架配置方式依赖管理自动配置Spring MVC需手动配置MultipartResolver(如StandardServletMultipartResolver)需自行引入commons-fileupload等依赖无,默认不启用文件上传支…...
Linux网络配置与测试
目录 一.与网络配置相关的命令 1.1ifconfig命令 1.1.1作用 1.1.2网络接口的信息 接口信息的组成 1.1.3显示所有网卡包括没有启动的网卡 1.1.4查看指定网络接口 1.1.5开启或关闭网卡 1.1.6设置临时虚拟网卡 1.1.7网络通讯情况 编辑 1.1.8临时修改网卡属性 1.2hos…...
游戏赛季和数据处理
问题 游戏从无赛季到赛季机制会涉及哪些问题: 如何改动,增加赛季机制,涉及要修改的代码量最少如何改动,账号、角色部分数据继承问题,涉及要修改的代码量最少账号下角色的永久服共享或是永久服独立,需要做…...
京东店铺托管7*16小时全时护航
内容概要 京东店铺托管服务的*716小时全时护航模式,相当于给商家配了个全年无休的"运营管家"。专业团队每天从早7点到晚11点实时盯着运营数据和商品排名,连半夜流量波动都能通过智能系统秒级预警。这种全天候服务可不是单纯拼人力——系统自动…...
HTTP的Keep-Alive是什么?TCP 的 Keepalive 和 HTTP 的 Keep-Alive 是一个东西吗?
HTTP的Keep-Alive: HTTP Keep-Alive 是一种机制,允许客户端和服务器在单个 TCP 连接 上发送多个 HTTP 请求 和 响应,而不是每次请求和响应后都关闭连接。它的主要目的是提高性能,减少连接的开销,优化通信效率。 工作…...
使用scoop一键下载jdk和实现版本切换
安装 在 PowerShell 中输入下面内容,保证允许本地脚本的执行: set-executionpolicy remotesigned -scope currentuser然后执行下面的命令安装 Scoop: iwr -useb get.scoop.sh | iex国内用户可以使用镜像源安装:powershell iwr -us…...
PPIO × UI-TARS:用自然语言操控电脑,AI Agent 的极致体验
Manus的爆火预示着AI 正在从单纯的文本生成和图像识别迈向更复杂的交互场景。字节跳动近期推出的开源项目 UI-TARS Desktop 为我们展示了一种全新的可能性:能够通过自然语言理解和处理来控制计算机界面。这款工具代表了人工智能与人机交互领域的重大突破,…...
PG:incorrect prev-link
目录 WAL日志中"incorrect prev-link"错误解决方案错误原因分析解决步骤典型修复案例 WAL日志中"incorrect prev-link"错误解决方案 错误原因分析 WAL日志的prev-link字段用于确保日志记录的连续性。当出现incorrect prev-link 2/754ECB0 at 2/8000028错…...
SQL Server 数据库邮件配置失败:SMTP 连接与权限问题
问题现象: 配置数据库邮件时,发送测试邮件失败,提示 “邮件无法发送到 SMTP 服务器,操作超时”(错误 14661)或 “服务器拒绝发件人地址”(错误 15009)。 快速诊断 检查数据库邮件配置…...
深入浅出动态规划:从基础到蓝桥杯实战(Java版)
引言:为什么你需要掌握动态规划? 动态规划(DP)是算法竞赛和面试中的常客,不仅能大幅提升解题效率(时间复杂度通常为O(n)或O(n))[4],更是解决复杂优化问题的利器。统计显示ÿ…...
获取cookie的chrome插件:Get cookies.txt LOCALLY
接上一篇,在下载视频的时候需要网站的cookie,下面介绍一款可以获取网站cookie的chrome插件 https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc?utm_sourceitem-share-cb 备注需要科学上网 【使用方…...
opencv无法设置禁用RGB转换问题
树莓派连接摄像头,摄像头输出格式为YUYV(YUV422)。 通过执行 v4l2-ctl --list-formats --device/dev/video0 可以看的具体的摄像头的数据格式。 使用opencv获取视频流,通过cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)设置禁用自动转换RGB格式,但是打印输出…...
Ansible:roles角色
文章目录 Roles角色Ansible Roles目录编排Roles各目录作用创建 roleplaybook调用角色调用角色方法1:调用角色方法2:调用角色方法3: roles 中 tags 使用实战案例 Roles角色 角色是ansible自1.2版本引入的新特性,用于层次性、结构化…...
SAP系统采购信息记录失效
问题:采购信息记录失效 现象:最初主数据导入完成之后,单元测试的时采购信息记录是有效的,中间经过配置的变化,集成测试初期发现采购信息记录全部失效。 原因: 单元测试时发现采购订单里面的条件类型…...
JavaWeb 课堂笔记 —— 04 Ajax
本系列为笔者学习JavaWeb的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)》,章节分布参考视频教程,为同样学习…...
Pandas 库
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据 Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够…...
4.8学习总结
完成摆动序列的算法题(比较难,想不出方法) 学习了HashMap,TreeMap 的源码(看完一遍对其理解没有太清楚,还需再多刷几遍理解源码及其底层逻辑的概念) 学习了可变参数和Collections工具类...
C语言之九九乘法表
一、代码展示 二、运行结果 三、代码分析 首先->是外层循环是小于等于9的 然后->是内层循环是小于等于外层循环的 最后->就是\n让九九乘法表的格式更加美观(当然 电脑不同 有可能%2d 也有可能%3d) 四、与以下素数题目逻辑相似 五、运行结果...
【Linux操作系统】:信号
Linux操作系统下的信号 一、引言 首先我们可以简单理解一下信号的概念,信号,顾名思义,就是我们操作系统发送给进程的消息。举个简单的例子,我们在写C/C程序的时候,当执行a / 0类似的操作的时候,程序直接就挂…...
skynet.call使用详解
目录 skynet.call 详细解析1. 函数签名与参数2. 内部实现机制3. 会话ID与协程调度4. 超时与错误处理5. 返回值处理6. 协议类型的影响7. skynet.call vs skynet.send8. 示例代码分析9. 最佳实践10. 总结 skynet.call 详细解析 1. 函数签名与参数 函数签名: skynet…...
uniapp 打包 H5 向 打包的APP 使用 @dcloudio/uni-webview-js 传值
1.安装 dcloudio/uni-webview-js npm install dcloudio/uni-webview-js -save 这个模块的 uni. 会与H5的uniapp的 uni. 冲突,所以需要改下名称,一共需要改3处 2.引入并使用 import uniWeb from dcloudio/uni-webview-js;uniWeb.postMessage({data: {action: message,content…...
c语言 文件操作
c语言 文件操作 one 打开/usr/dev.txt文件,在第1行 覆盖写入 "MAC1q23456789" #include <fcntl.h> #include <unistd.h> #include <string.h> int main() { const char *line_1 "MAC1q23456789\n"; // 要写入的内容…...
企业电子招投标采购系统——功能模块功能描述+数字化采购管理 采购招投标
功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看…...
Python 序列构成的数组(序列的增量赋值)
序列的增量赋值 增量赋值运算符 和 * 的表现取决于它们的第一个操作对象。简单起 见,我们把讨论集中在增量加法()上,但是这些概念对 * 和其他 增量运算符来说都是一样的。 背后的特殊方法是 iadd (用于“就地加法”&…...
力扣hot100【链表】
160.相交链表 题目 我的思路:两个链表一长一短,先把长的提前遍历使两个链表的长度相等,然后同时遍历,如果遍历的节点相等时说明相交,否则不相交。 /*** Definition for singly-linked list.* struct ListNode {* …...
PyTorch 生态迎来新成员:SGLang 高效推理引擎解析
SGLang 现已正式融入 PyTorch 生态系统!此次集成确保了 SGLang 符合 PyTorch 的技术标准与最佳实践,为开发者提供了一个可靠且社区支持的框架,助力大规模语言模型(LLM)实现高效且灵活的推理。 如需深入了解 PyTorch…...
C++ Primer Plus 编程练习题 第六章 分支语句和逻辑运算符
1.大小写转换 使用cctype库里的函数进行大小写转换,但要注意使用toupper或tolower时要进行强制类型转换,否则会输出ASCII值 #include <iostream> #include<cctype> using namespace std;int main() {cout << "请输入字符串(大…...
一文详解OpenGL环境搭建:Windows使用CLion配置OpenGL开发环境
在计算机图形学的广阔领域中,OpenGL作为行业标准的图形库,为开发者提供了强大的工具集来创建从简单的2D图形到复杂的3D世界。然而,对于初学者和经验丰富的开发者而言,选择一个合适的开发环境是迈向成功的第一步。尤其是在Windows平台上,配置一个既支持现代C++编程实践又能…...
一次奇怪的enq: TX - row lock contention锁问题处理
某天上午客户告知数据库库有锁导致数据库卡死,需排查出问题的原因,从根本上解决问题。 按正常步骤,查询V$SESSION中BLOCKING_SESSION列不为空的,发现没有进程互相阻塞的情况;而查询ACTIVE会话,则有大量进程…...
STL常用容器整理
STL常用容器操作整理 STL常用容器操作整理(string/vector/set/map)一、string(字符串)构造函数元素访问修改操作容量操作子串与查找 二、vector(动态数组)构造函数元素访问修改操作容量操作 三、set&#x…...
深入 PostgreSQL 内部:5 个关键阶段拆解查询处理全流程
引言 当您向 PostgreSQL 发送查询时,后端会经历多个处理阶段。每个阶段承担着不同的职责,以确保您能在最短时间内获得准确响应。虽然这些阶段可能庞大而复杂,但理解它们在查询处理中的角色对 PostgreSQL 开发者至关重要。本文将概述每个查询…...
解析 LILIkoi 光纤力传感器:FBG 原理铸就耐高温抗干扰优势
LILIkoi光纤力传感器通过光纤光栅(FBG)技术实现高精度力测量。其核心原理基于光纤内光栅栅距的微小变化,用以感知外界施加的力。该传感器在高温、强辐射等恶劣环境中表现出色,能够有效抵抗电磁干扰和温度漂移。凭借卓越的性能&…...
SU-YOLO:基于脉冲神经网络的高效水下目标检测模型解析
论文地址:https://arxiv.org/pdf/2503.24389 目录 一、论文概述 二、创新点解析 1. 基于脉冲的水下图像去噪(SpikeDenoiser) 原理与结构 2. 分离批归一化(SeBN) 原理与结构 3. 优化的残差块(SU-Block) 原理与结构 三、代码复现指南 环境配置 模型训练 四、…...
有关eeprom以及pwm
a0 a1就是对应的 芯片的 写和读 0写 1读 使用操作 主函数读一次 然后信息里一直写入。 用level设置挡位 如 10个格子 设置2 3 这样占空比就有了...
JMeter教程|0到1学会接口性能压测第14课-JMeter接口性能测试全流程讲解
Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。 本文以百度搜索接口为例,全流程讲解JMeter接口性能测试。从JMeter下载安装到编写一个…...
系统思考:问题诊断
“做事不怕困难,怕的是不明白困难出在哪里。” —— 亨利福特 最近发现,有些领导者或者团队,常常急于给出解决方案,却忽视了最关键的一步——诊断问题的根源。团队甚至在集体心智模式的影响下,连问题本身都搞错了方向…...
有效压缩 Hyper-v linux Centos 的虚拟磁盘 VHDX
参考: http://www.360doc.com/content/22/0505/16/67252277_1029878535.shtml VHDX 有个不好的问题就是,如果在里面存放过文件再删除,那么已经使用过的空间不会压缩,导致空间一直被占用。那么就需要想办法压缩空间。 还有一点&a…...
使用 redis 实现消息队列
方案1: 使用list做消息队列问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案 2: zset实现消息队列方案 3: 发布/订阅(pub/sub)问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案 4: Stream 实现消息队列问题1: 如何保证消息不丢失问题 2: 重复消费/幂等 方案1: 使用li…...
2025 XYCTF Pwn-wp(含附件)
前言 总体来说Pwn方向题目难度属于中等,属于那种一眼看不出要咋做,但多试试又能做出来的那种,比赛的时候甚至有几只队伍AK了Pwn方向。感觉题目还是很不错的尽管比赛中有一些小意外像是有些题目附件给错了,但是XYCTF的师傅们都是无偿出题纯热爱向大伙分享自己的题目…...
verilog有符号数的乘法
1、单周期乘法器 对于低速要求的乘法器,可以简单的使用 * 实现。 module Mult(input wire [7:0] multiplicand ,input wire [7:0] multipliter ,output wire [7:0] product);assign product multiplicand * multipliter …...