React+TS前台项目实战(二十一)-- Search业务组件封装实现全局搜索
文章目录
- 前言
- 一、Search组件封装
- 1. 效果展示
- 2. 功能分析
- 3. 代码+详细注释
- 4. 使用方式
- 二、搜索结果展示组件封装
- 1. 功能分析
- 2. 代码+详细注释
- 三、引用到文件,自行取用
- 总结
前言
今天,我们来封装一个业务灵巧的组件,它集成了全局搜索和展示搜索结果的功能。通过配置文件,我们可以为不同的模块定制展示和跳转逻辑,集中管理不同模块,当要加一个模块时,只需要通过配置即可,从而减少重复的代码,并方便地进行维护和扩展。同时,我们将使用React Query来实现搜索功能,并模拟请求成功、请求失败和中断请求的处理方式。
一、Search组件封装
1. 效果展示
(1)输入内容,当停止输入后,请求接口数据
注:如请求数据时添加加载状态,请求结束后取消加载状态
(2)点击清除按钮,清除输入框数据,并中止当前请求,重置react-query请求参数
(3)请求失败,展示失败界面
(4)是否显示搜索按钮
(5)移动端效果
2. 功能分析
(1)搜索功能灵活性: 使用防抖搜索,useMemo,以及react-query自带监听输入状态,只在输入框停止输入后,才会触发接口请求,避免在用户仍在输入时进行不必要的API调用
(2)请求库选择: 使用Tanstack React Query中的useQuery钩子来管理加载状态并获取搜索结果
(3)导航到搜索结果: 点击搜索结果项或在搜索结果显示后按下回车键时,会跳转到对应的页面
(4)清除搜索: 点击清空按钮,会清空输入框的内容,并取消接口请求,重置请求参数,隐藏搜索结果列表
(5)搜索结果展示: 一旦获取到搜索结果,该组件使用SearchResults组件渲染搜索结果。它还显示搜索结果的加载状态
(6)搜索按钮: 如果hasButton属性为true,还将渲染一个搜索按钮,当点击时触发搜索
(7)使用国际化语言,可全局切换使用;使用联合类型声明使用,不同模块,添加配置即可
(8)使用useCallback,useMemo,useEffect, memo,lodash.debounce等对组件进行性能优化
(9)提供一些回调事件,供外部调用
3. 代码+详细注释
引入之前文章封装的 输入框组件,可自行查看,以及下面封装的结果展示组件
// @/components/Search/index.tsx
import { FC, useCallback, useMemo, memo, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import debounce from "lodash.debounce";
import { useTranslation } from "react-i18next";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { SearchContainer, SearchButton } from "./styled";
import Input from "@/components/Input";
import { querySearchInfo } from "@/api/search";
import { useIsMobile } from "@/hooks";
import { SearchResults } from "./searchResults";
import { getURLBySearchResult } from "./utils";// 组件的属性类型
type Props = {defaultValue?: string;hasButton?: boolean;onClear?: () => void;
};
// 搜索框组件
const Search: FC<Props> = memo(({ defaultValue, hasButton, onClear: handleClear }) => {const queryClient = useQueryClient();const navigate = useNavigate();const { t } = useTranslation();const isMobile = useIsMobile();const [keyword, _setKeyword] = useState(defaultValue || "");const searchValue = keyword.trim();// 获取搜索结果数据const fetchData = async (searchValue: string) => {const { data } = await querySearchInfo({p: searchValue,});return {data,total: data.length,};};// 使用useQuery实现搜索const {refetch: refetchSearch,data: _searchResults,isFetching,} = useQuery(["moduleSearch", searchValue], () => fetchData(searchValue), {enabled: false,});// 从查询结果中获取搜索结果数据const searchResultData = _searchResults?.data;// 使用useMemo函数创建一个防抖函数debouncedSearch,用于实现搜索请求功能const debouncedSearch = useMemo(() => {return debounce(refetchSearch, 1500, { trailing: true }); // 在搜索值变化后1.5秒后触发refetchSearch函数}, [refetchSearch]); // 当refetchSearch函数发生变化时,重新创建防抖函数debouncedSearch// 监听搜索值变化,当有搜索值时,调用debouncedSearch函数进行搜索useEffect(() => {if (!searchValue) return;debouncedSearch();}, [searchValue]);// 重置搜索const resetSearch = useCallback(() => {debouncedSearch.cancel(); // 取消搜索轮询queryClient.resetQueries(["moduleSearch", searchValue]); // 重置查询缓存}, [debouncedSearch, queryClient, searchValue]);// 清空搜索const onClear = useCallback(() => {resetSearch(); // 调用重置方法handleClear?.(); // 调用清空回调方法}, [resetSearch, handleClear]);// 设置搜索内容,如果值为空,则调用清空方法const setKeyword = (value: string) => {if (value === "") onClear();_setKeyword(value);};// 搜索按钮点击事件const handleSearch = () => {// 如果没有搜索内容,或者搜索无数据则直接返回if (!searchValue || !searchResultData) return;// 根据搜索结果数据的第一个元素获取搜索结果对应的URLconst url = getURLBySearchResult(searchResultData[0]);// 跳转到对应的URL,如果获取不到URL,则跳转到失败的搜索页面navigate(url ?? `/search/fail?q=${searchValue}`);};return (<SearchContainer>{/* 搜索框 */}<Input loading={isFetching} value={keyword} hasPrefix placeholder={t("navbar.search_placeholder")} autoFocus={!isMobile} onChange={(event) => setKeyword(event.target.value)} onEnter={handleSearch} onClear={onClear} />{/* 搜索按钮,hasButton为true时显示 */}{hasButton && <SearchButton onClick={handleSearch}>{t("search.search")}</SearchButton>}{/* 搜索结果列表组件展示 */}{(isFetching || searchResultData && <SearchResults keyword={keyword} results={searchResultData ?? []} loading={isFetching} />}</SearchContainer>);
});export default Search;
------------------------------------------------------------------------------
// @/components/Search/styled.tsx
import styled from "styled-components";
import variables from "@/styles/variables.module.scss";
export const SearchContainer = styled.div`position: relative;margin: 0 auto;width: 100%;padding-right: 0;display: flex;align-items: center;justify-content: center;background: white;border: 0 solid white;border-radius: 4px;
`;
export const SearchButton = styled.div`flex-shrink: 0;width: 72px;height: calc(100% - 4px);margin: 2px 2px 2px 8px;border-radius: 0 4px 4px 0;background-color: #121212;text-align: center;line-height: 34px;color: #fff;letter-spacing: 0.2px;font-size: 14px;cursor: pointer;@media (max-width: ${variables.mobileBreakPoint}) {display: none;}
`;
4. 使用方式
// 引入组件
import Search from '@/components/Search'
// 使用
{/* 带搜索按钮 */}
<Search hasButton />
{/* 不带搜索按钮 */}
<Search />
二、搜索结果展示组件封装
注:这个组件在上面Search组件中引用,单独列出来讲讲。运用关注点分离的策略,将页面分割成多个片段,易维护,容易定位代码位置。
1. 功能分析
(1)组件接受搜索内容,是否显示loading加载,以及搜索列表这三个参数
(2)根据搜索结果列表,按模块类型分类数据,这里举例2种类型(如Transaction 和 Block)
(3)对搜索的模块类型列表,添加点击事件,当点击某个模块时,展示该模块的数据
(4)不同模块类型的列表,展示不同效果(例如类型是 Transaction,显示交易信息,包括交易名称和所在区块的编号;类型是 Block,则显示区块信息,包括区块编号)
(5)通过useEffect监听数据变化,发生变化时,重置激活的模块类型分类,默认不选中任何模块类型
(6)封装不同模块匹配对应的地址,名字的方法,统一管理
(7)采用联合等进行类型声明的定义
2. 代码+详细注释
// @/components/Search/SearchResults/index.tsx
import { useTranslation } from "react-i18next";
import classNames from "classnames";
import { FC, useEffect, useState } from "react";
import { SearchResultsContainer, CategoryFilterList, SearchResultList, SearchResultListItem } from "./styled";
import { useIsMobile } from "@/hooks";
import Loading from "@/components/Loading";
import { SearchResultType, SearchResult } from "@/models/Search";
// 引入不同模块匹配对应的地址,名字方法
import { getURLBySearchResult, getNameBySearchResult } from "../utils";// 组件的类型定义
type Props = {keyword?: string; // 搜索内容loading?: boolean; // 是否显示 loading 状态results: SearchResult[]; // 搜索结果列表
};// 列表数据每一项Item的渲染
const SearchResultItem: FC<{ keyword?: string; item: SearchResult }> = ({ item, keyword = "" }) => {const { t } = useTranslation(); // 使用国际化const to = getURLBySearchResult(item); // 根据搜索结果项获取对应的 URLconst displayName = getNameBySearchResult(item); // 根据搜索结果项获取显示名称// 如果搜索结果项类型是 Transaction,则显示交易信息if (item.type === SearchResultType.Transaction) {return (<SearchResultListItem to={to}><div className={classNames("content")}>{/* 显示交易名称 */}<div className={classNames("secondary-text")} title={displayName}>{displayName}</div>{/* 显示交易所在区块的编号 */}<div className={classNames("sub-title", "monospace")}>{t("search.block")} # {item.attributes.blockNumber}</div></div></SearchResultListItem>);}// 否则,类型是Block, 显示区块信息return (<SearchResultListItem to={to}><div className={classNames("content")} title={displayName}>{displayName}</div></SearchResultListItem>);
};// 搜索结果列表
export const SearchResults: FC<Props> = ({ keyword = "", results, loading }) => {const isMobile = useIsMobile(); // 判断是否是移动端const { t } = useTranslation(); // 使用国际化// 设置激活的模块类型分类const [activatedCategory, setActivatedCategory] = useState<SearchResultType | undefined>(undefined);// 当搜索结果列表发生变化时,重置激活的分类useEffect(() => {setActivatedCategory(undefined);}, [results]);// 根据搜索结果列表,按模块类型分类数据const categories = results.reduce((acc, result) => {if (!acc[result.type]) {acc[result.type] = [];}acc[result.type].push(result);return acc;}, {} as Record<SearchResultType, SearchResult[]>);// 按模块类型分类的列表const SearchResultBlock = (() => {return (<SearchResultList>{Object.entries(categories).filter(([type]) => (activatedCategory === undefined ? true : activatedCategory === type)).map(([type, items]) => (<div key={type} className={classNames("search-result-item")}><div className={classNames("title")}>{t(`search.${type}`)}</div><div className={classNames("list")}>{items.map((item) => (<SearchResultItem keyword={keyword} key={item.id} item={item} />))}</div></div>))}</SearchResultList>);})();// 如果搜索结果列表为空,则显示空数据提示;否则显示搜索结果列表return (<SearchResultsContainer>{!loading && Object.keys(categories).length > 0 && (<CategoryFilterList>{(Object.keys(categories) as SearchResultType[]).map((category) => (<div key={category} className={classNames("categoryTagItem", { active: activatedCategory === category })} onClick={() => setActivatedCategory((pre) => (pre === category ? undefined : category))}>{t(`search.${category}`)} {`(${categories[category].length})`}</div>))}</CategoryFilterList>)}{loading ? <Loading size={isMobile ? "small" : undefined} /> : results.length === 0 ? <div className={classNames("empty")}>{t("search.no_search_result")}</div> : SearchResultBlock}</SearchResultsContainer>);
};------------------------------------------------------------------------------
// @/components/Search/SearchResults/styled.tsx
import styled from "styled-components";
import Link from "@/components/Link";
export const SearchResultsContainer = styled.div`display: flex;flex-direction: column;gap: 12px;width: 100%;max-height: 292px;overflow-y: auto;background: #fff;color: #000;border-radius: 4px;box-shadow: 0 4px 4px 0 #1010100d;position: absolute;z-index: 2;top: calc(100% + 8px);left: 0;.empty {padding: 28px 0;text-align: center;font-size: 16px;color: #333;}
`;
export const CategoryFilterList = styled.div`display: flex;flex-wrap: wrap;padding: 12px 12px 0;gap: 4px;.categoryTagItem {border: 1px solid #e5e5e5;border-radius: 24px;padding: 4px 12px;cursor: pointer;transition: all 0.3s;&.active {border-color: var(--cd-primary-color);color: var(--cd-primary-color);}}
`;
export const SearchResultList = styled.div`.search-result-item {.title {color: #666;font-size: 0.65rem;letter-spacing: 0.5px;font-weight: 700;padding: 12px 12px 6px;background-color: #f5f5f5;text-align: left;}.list {padding: 6px 8px;}}
`;
export const SearchResultListItem = styled(Link)`display: block;width: 100%;padding: 4px 0;cursor: pointer;border-bottom: solid 1px #e5e5e5;.content {display: flex;align-items: center;justify-content: space-between;width: 100%;padding: 4px;border-radius: 4px;text-align: left;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: var(--cd-primary-color);}.secondary-text {flex: 1;width: 0;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}.sub-title {font-size: 14px;color: #666;overflow: hidden;margin: 0 4px;}&:last-child {border-bottom: none;}&:hover,&:focus-within {.content {background: #f5f5f5;}}
`;
三、引用到文件,自行取用
(1)获取不同模块地址,展示名称的方法
// @/components/Search/utils
import { SearchResultType, SearchResult } from "@/models/Search";
// 根据搜索结果项类型,返回对应的 URL 链接
export const getURLBySearchResult = (item: SearchResult) => {const { type, attributes } = item;switch (type) {case SearchResultType.Block:// 如果搜索结果项类型是 Block,则返回对应的区块详情页面链接return `/block/${attributes.blockHash}`;case SearchResultType.Transaction:// 如果搜索结果项类型是 Transaction,则返回对应的交易详情页面链接return `/transaction/${attributes.transactionHash}`;default:// 如果搜索结果项类型不是 Block 或者 Transaction,则返回空字符串return "";}
};
// 根据搜索结果项类型,返回不同显示名称
export const getNameBySearchResult = (item: SearchResult) => {const { type, attributes } = item;switch (type) {case SearchResultType.Block:return attributes?.number?.toString(); // 返回高度case SearchResultType.Transaction:return attributes?.transactionHash?.toString(); // 返回交易哈希default:return ""; // 返回空字符串}
};
(2)用到的类型声明
// @/models/Search/index.ts
import { Response } from '@/request/types'
import { Block } from '@/models/Block'
import { Transaction } from '@/models/Transaction'
export enum SearchResultType {Block = 'block',Transaction = 'ckb_transaction',
}
export type SearchResult =| Response.Wrapper<Block, SearchResultType.Block>| Response.Wrapper<Transaction, SearchResultType.Transaction>
-------------------------------------------------------------------------------------------------------
// @/models/Block/index.ts
export interface Block {blockHash: stringnumber: numbertransactionsCount: numberproposalsCount: numberunclesCount: numberuncleBlockHashes: string[]reward: stringrewardStatus: 'pending' | 'issued'totalTransactionFee: stringreceivedTxFee: stringreceivedTxFeeStatus: 'pending' | 'calculated'totalCellCapacity: stringminerHash: stringminerMessage: stringtimestamp: numberdifficulty: stringepoch: numberlength: stringstartNumber: numberversion: numbernonce: stringtransactionsRoot: stringblockIndexInEpoch: stringminerReward: stringliveCellChanges: stringsize: numberlargestBlockInEpoch: numberlargestBlock: numbercycles: number | nullmaxCyclesInEpoch: number | nullmaxCycles: number | null
}
-------------------------------------------------------------------------------------------------------
// @/models/Transaction/index.ts
export interface Transaction {isBtcTimeLock: booleanisRgbTransaction: booleanrgbTxid: string | nulltransactionHash: string// FIXME: this type declaration should be fixed by adding a transformation between internal state and response of APIblockNumber: number | stringblockTimestamp: number | stringtransactionFee: stringincome: stringisCellbase: booleantargetBlockNumber: numberversion: numberdisplayInputs: anydisplayOutputs: anyliveCellChanges: stringcapacityInvolved: stringrgbTransferStep: string | nulltxStatus: stringdetailedMessage: stringbytes: numberlargestTxInEpoch: numberlargestTx: numbercycles: number | nullmaxCyclesInEpoch: number | nullmaxCycles: number | nullcreateTimestamp?: number
}
总结
下一篇讲【全局常用Echarts组件封装】。关注本栏目,将实时更新。
相关文章:

React+TS前台项目实战(二十一)-- Search业务组件封装实现全局搜索
文章目录 前言一、Search组件封装1. 效果展示2. 功能分析3. 代码详细注释4. 使用方式 二、搜索结果展示组件封装1. 功能分析2. 代码详细注释 三、引用到文件,自行取用总结 前言 今天,我们来封装一个业务灵巧的组件,它集成了全局搜索和展示搜…...

SEO与AI的结合:如何用ChatGPT生成符合搜索引擎优化的内容
在当今数字时代,搜索引擎优化(SEO)已成为每个网站和内容创作者都必须掌握的一项技能。SEO的主要目标是通过优化内容,使其在搜索引擎结果页面(SERP)中排名更高,从而吸引更多的流量。然而…...

【信息系统项目管理师知识点速记】组织通用管理:知识管理
23.3 知识管理 23.3.1 知识管理基础 知识管理是通过利用各种知识和技术手段,帮助组织和个人生产、分享、应用和创新知识,以形成知识优势并在个人、组织、业务目标、经济绩效和社会效益方面产生价值的过程。它能为组织带来知识增值,创造新的价值,提升决策效能和水平,是提…...

CM-UNet: Hybrid CNN-Mamba UNet for Remote Sensing Image Semantic Segmentation
论文:CM-UNet: Hybrid :CNN-Mamba UNet for Remote Sensing Image Semantic Segmentation 代码:https://github.com/XiaoBuL/CM-UNet Abstrcat: 由于大规模图像尺寸和对象变化,当前基于 CNN 和 Transformer 的遥感图像语义分割方…...

DP:子序列问题
文章目录 什么是子序列子序列的特点举例说明常见问题 关于子序列问题的几个例题1.最长递增子序列2.摆动序列3.最长递增子序列的个数4.最长数对链5.最长定差子序列 总结 什么是子序列 在计算机科学和数学中,子序列(Subsequence)是指从一个序列…...

Spring Data与多数据源配置
Spring Data与多数据源配置 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来探讨如何在Spring Data中配置和使用多个数据源。 在现代应用程序中&…...

【前端vue3】TypeScrip-类型推论和类型别名
类型推论 TypeScript里,在有些没有明确指出类型的地方,类型推论会帮助提供类型。 例如: 变量xiaoc被推断类型为string 如重新给xiaoc赋值数字会报错 let xiaoc "xiaoc"xiaoc 1111111111111如没有给变量指定类型和赋值…...

javaEE——Servlet
1.web开发概述 所谓web开发,指的是从网页中向后端程序发送请求,与后端程序进行交互 2.java后端开发环境搭建 web后端(javaEE)程序需要运行在服务器中的,这样前端才可以访问得到 3.服务器是什么? ①服务器就是一款软件,可以向其发送请求&#…...

Kotlin扩展函数(also apply run let)和with函数
also apply run let with的使用例子 private fun testOperator() {/*** also*/val person Person("ZhangSan", 18)person.also {// 通常仅仅打印使用, 也可以通过it修改it.name "ZhangSan1"println("also inner name: " it.name)}println(&qu…...

C语言笔记27 •单链表介绍•
1.链表的概念及结构 链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的。 2. 顺序表带来的问题 (1)中间/头部的插⼊删除,时间复杂度为O(N) (2)增容需要申请新空间,拷⻉数据ÿ…...

C++编程(五)单例模式 友元
文章目录 一、单例模式(一)概念(二)实现方式1. 饿汉式2. 懒汉式 二、友元(一)概念(二)友元函数1.概念2.语法格式3. 使用示例访问静态成员变量访问非静态成员变量 (三&…...

012-GeoGebra基础篇-构造圆的切线
前边文章对于基础内容已经悉数覆盖了,这一篇我就不放具体的细节,若有需要可以复刻一下 目录 一、成品展示二、算式内容三、正确性检查五、文章最后 一、成品展示 二、算式内容 A(0,0) B(3,0) c: Circle(A,B) C(5,4) sSegment(A,C) DMidpoint(s) d: Circ…...

数据结构速成--查找
由于是速成专题,因此内容不会十分全面,只会涵盖考试重点,各学校课程要求不同 ,大家可以按照考纲复习,不全面的内容,可以看一下小编主页数据结构初阶的内容,找到对应专题详细学习一下。 目录 …...

SpringMVC的基本使用
SpringMVC简介 SpringMVC是Spring提供的一套建立在Servlet基础上,基于MVC模式的web解决方案 SpringMVC核心组件 DispatcherServlet:前置控制器,来自客户端的所有请求都经由DispatcherServlet进行处理和分发Handler:处理器&…...

【PYG】Cora数据集分类任务计算损失,cross_entropy为什么不能直接替换成mse_loss
cross_entropy计算误差方式,输入向量z为[1,2,3],预测y为[1],选择数为2,计算出一大坨e的式子为3.405,再用-23.405计算得到1.405MSE计算误差方式,输入z为[1,2,3],预测向量应该是[1,0,0]࿰…...

MyBatis-plus这么好用,不允许还有人不会
你好呀,我是 javapub. 做 Java 的同学都会用到的三件套,Spring、SpringMV、MyBatis。但是由于使用起来配置较多,依赖冲突频发。所有,各路大佬又在这上边做了包装,像我们常用的 SpringBoot、MyBatisPlus。 基于当前要…...

Linux驱动开发实战宝典:设备模型、模块编程、I2C/SPI/USB外设精讲
摘要: 本文将带你走进 Linux 驱动开发的世界,从设备驱动模型、内核模块开发基础开始,逐步深入 I2C、SPI、USB 等常用外设的驱动编写,结合实际案例,助你掌握 Linux 驱动开发技能。 关键词: Linux 驱动,设备驱动模型,内核模块,I2C,SPI,USB 一、Linux 设备驱动模型 Li…...

安全技术和防火墙
1、安全技术 1.1入侵检测系统 特点是不阻断网络访问,主要提供报警和事后监督。不主动介入,默默的看着你(类似于监控) 1.2入侵防御系统 透明模式工作, 数据包,网络监控,服务攻击,…...

Webpack: 开发 PWA、Node、Electron 应用
概述 毋庸置疑,对前端开发者而言,当下正是一个日升月恒的美好时代!在久远的过去,Web 页面的开发技术链条非常原始而粗糙,那时候的 JavaScript 更多用来点缀 Web 页面交互而不是用来构建一个完整的应用。直到 2009年5月…...

python处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一行
处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一个实例,使用Python的内置函数来读取文件,并逐行检查和处理数据。 一个txt文件,里面的数据是893.554382324,-119.955825806,0.0299997832626,-0.133618548512,28.1155740884,112.876833236,46.7922,19.62582…...

C++17中引入了什么新的重要特性
C17是C标准的一个重要版本,它在语言核心和标准库中引入了许多新特性和改进,使得C编程更加现代化和高效。以下是C17中引入的一些重要新特性: 语言核心新特性 结构化绑定(Structured Bindings): 结构化绑定…...

Andrej Karpathy提出未来计算机2.0构想: 完全由神经网络驱动!网友炸锅了
昨天凌晨,知名人工智能专家、OpenAI的联合创始人Andrej Karpathy提出了一个革命性的未来计算机的构想:完全由神经网络驱动的计算机,不再依赖传统的软件代码。 嗯,这是什么意思?全部原生LLM硬件设备的意思吗?…...

用国内镜像安装docker 和 docker-compose (ubuntu)
替代方案,改用国内的镜像站(网易镜像) 1.清除旧版本(可选操作) for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do apt-get remove $pkg; done 2.安装docker apt-get update 首先安装依赖 apt-g…...

Linux多线程【线程互斥】
文章目录 Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex模拟抢票代码 互斥量的接口初始化互斥量销毁互斥量互斥量加锁和解锁改进模拟抢票代码(加锁)小结对锁封装 lockGuard.hpp 互斥量实现原理探究可重入VS线程安全概念常见的线程不安全的情况常…...

os实训课程模拟考试(大题复习)
目录 一、Linux操作系统 (1)第1关:Linux初体验 (2)第2关:Linux常用命令 (3)第3关:Linux 查询命令帮助语句 二、Linux之进程管理—(重点) &…...

QT/QML国际化:中英文界面切换显示(cmake方式使用)
目录 前言 实现步骤 1. 准备翻译文件 2. 翻译字符串 3.设置应用程序语言 cmake 构建方式 示例代码 总结 1. 使用 file(GLOB ...) 2. 引入其他资源文件 再次生成翻译文件 5. 手动更新和生成.qm文件 其他资源 前言 在当今全球化的软件开发环境中,应用程…...

设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 引言 设计模式是软件开发中重要的思想工具,它提供了解决特定问题…...

js制作随机四位数验证码图片
<div class"lable lable2"><div class"l"><span>*</span>验证码</div><div class"r"><input type"number" name"vercode" placeholder"请输入验证码"></div>&l…...

[开源软件] 支持链接汇总
“Common rules: 1- If the repo is on github, the support/bug link is also on the github with issues”" label; 2- Could ask questions by email list;" 3rd party software support link Note gcc https://gcc.gnu.org openssh https://bugzilla.mindrot.o…...

从零开始搭建spring boot多模块项目
一、搭建父级模块 1、打开idea,选择file–new–project 2、选择Spring Initializr,选择相关java版本,点击“Next” 3、填写父级模块信息 选择/填写group、artifact、type、language、packaging(后面需要修改)、java version(后面需要修改成和第2步中版本一致)。点击“…...