基于 vuestic-ui 實戰教程 - 登錄篇

1. 簡介

登錄做為一個系統的門面,也是阻擋外界的一道防線,那在vuestic-ui中如何做登錄功能呢。在這里就之間沿用初始版本的Login頁面,作為一個演示模板,后續需要改進的讀者可以在此篇文章的基礎上修改。

在這里插入圖片描述

2. 登錄接口相關api 與 type編寫

在上一篇獲取動態數據中 我們已經定義好了與ts整合的axios,實現發送異步請求與遠程服務器交互(對于ts語法像是函數定義、基本數據類型還是有不懂的讀者可以跳轉到上一篇的2.1再學習學習)這里就直接引入登錄接口的api編寫, 具體位置如下我個人習慣創建一個api文件夾,里面專門存放一些與后端交互的api方法和類型定義(初始quickstart版本是寫在上面的page中的,就看個人的編寫習慣吧😁只要功能實現了就沒問題)

在這里插入圖片描述

對于index.ts中主要實現了三個基本的方法,登錄登出和獲取用戶信息

這里沒有實現注冊功能,因為我實現該網站主要是做一個流量監控的系統,注冊功能對于用戶不多的情況下其實不太需要,管理員可以直接操作加入數據庫中,要是對這快感興趣的讀者也可以自己嘗試嘗試注冊模塊的功能實現
本質就是add一個user到數據庫中,不過需要注意的是添加驗證碼等防護措施,防止有不法分子大量注冊短時間內打爆服務器!!

import { http } from '../../../utils/request'
import type { LoginData , UserInfoRes} from './types'const requestContent = '/simple/cloud/access'
/*** 登錄*/
export function login(loginVo: LoginData) {return http.post<UserInfoRes>(`${requestContent}/login`, loginVo);
}/*** 獲取登錄用戶信息*/
export function getUserInfo() {return http.post<UserInfoRes>(`${requestContent}/info`)
}  /*** 退出登錄*/
export function logout() {return http.post<string>(`${requestContent}/logout`)
}  

假設訪問的后端服務器使用URL - http:localhost:9001/simple/cloud/access/login 這里由于uri前綴是一樣的都是 ‘/simple/cloud/access’ 所以把它提取出來做為一個常量簡化編寫。只需要在使用的地方通過變量占位符引入就好啦(注意不是 ‘’ ,剛開始也踩過這個坑在vscode中看到上面的requestContent 由灰色變成高亮則說明引用成功)

`${key}`

而對于api中引入的數據類型定義在types.ts中,其中的返回值類型就根據后端提供的接口方法來寫,像是我后端返回的類型為一個Map<String,Object> 類型的對象如下圖所示,那我就根據這個map中的key和value一一對應寫出如下的接口UserInfoRes,然后使用export導出給外部使用
在這里插入圖片描述

注意編寫的過程中只用指定ts基本的類型(java的List對應的就是ts中的數組 - 使用 [ ] 進行初始化 ),而要是需要返回一個User類型的對象,那就需要重新定義一個UserInfoInterface ,或者在user對應的api處定義types.ts 再在該文件下在引入(我是更推薦這種做法👍

/* 登錄接口參數類型 */
export interface LoginData {email: string,password: string,
}/* 用戶信息接口返回值類型 */
export interface UserInfoRes {routers: [],buttons: [],roles: [],name: string,token: string,
}

3. 修改Login.vue

定義好與后端交互的方法api后,我們就可以回到前面的Login.vue處修改具體登錄邏輯啦,由于初始版本使用的全是靜態數據,所以很多功能其實都是不用的,具體刪除修改后的模板如下(只保留了一個忘記密碼的選項,該功能后續再完善😭先把主要的邏輯跑通先,感興趣的讀者可以先占個坑,后續我一定會回來填坑的!)

<template><VaForm ref="form" @submit.prevent="submit"><h1 class="font-semibold text-4xl mb-4">Log in</h1><VaInputv-model="formData.email":rules="[validators.required, validators.email]"class="mb-4"label="Email"type="email"/><VaValue v-slot="isPasswordVisible" :default-value="false"><VaInputv-model="formData.password":rules="[validators.required]":type="isPasswordVisible.value ? 'text' : 'password'"class="mb-4"label="Password"@clickAppendInner.stop="isPasswordVisible.value = !isPasswordVisible.value"><template #appendInner><VaIcon:name="isPasswordVisible.value ? 'mso-visibility_off' : 'mso-visibility'"class="cursor-pointer"color="secondary"/></template></VaInput></VaValue><div class="auth-layout__options flex flex-col sm:flex-row items-start sm:items-center justify-between"><RouterLink :to="{ name: 'recover-password' }" class="mt-2 sm:mt-0 sm:ml-1 font-semibold text-primary">Forgot password?</RouterLink></div><div class="flex justify-center mt-4"><VaButton class="w-full" @click="submit"> Login</VaButton></div></VaForm>
</template>

重寫綁定的submit點擊事件邏輯

const submit = () => {if (validate()) {login(formData).then((data: UserInfoRes) => {if (data) {// 在這里添加需要執行的操作const token = data.token;// 將token存儲到authStore中const authStore = useAuthStore()authStore.setToken(token)authStore.setIsAuthenticated(true)window.sessionStorage.setItem('isAuthenticated', 'true')authStore.setName(data.name)authStore.setButtons(data.buttons)authStore.setRoles(data.roles)authStore.setRouters(data.routers)init({ message: "logged in success", color: 'success' });// 登陸成功后就重定向到主頁面dashboardpush({ name: 'dashboard' })}}).catch(() => {init({ message: "logged in fail , please check carefully!", color: '#FF0000' });});}else{Message.error('error submit!!')return false}
}

看到這里我相信你肯定會疑惑,為什么我需要獲取到數據又存儲到store中,那這個store又在哪里定義的呢,作者也沒講啊😡
別急別急,請聽我細細道來

4. store實現

在Vue應用程序中,當需要管理共享狀態時,通常會使用Vuex庫,而store就是Vuex中用于存儲這些狀態的地方,而我們登錄后自然需要圍護當前登錄角色的一些關鍵信息(權限,姓名等等)需要的時候就直接到store中拿去,而不是反復的去數據庫中查找,廢話不多說下面就來定義一個store ,在初始版本中就已經定義好了store,只不過這個store里面是沒東西的,如下圖所示

在這里插入圖片描述

那我們就可以在原有的基礎上添加修改,下面的代碼都是在index.ts中實現的,如下代碼就是一個模板,對應pinia庫的描述如下
Pinia是Vue的另一種狀態管理方案,與Vuex類似,但設計上更簡潔、更易于上手。以下是關于Pinia的一些詳細說明:

  • 簡單易用:Pinia的目標是提供一個更簡單的狀態管理解決方案,它的API設計非常直觀,使得開發者可以快速上手并有效地管理狀態。
  • 獨立模塊:與Vuex不同,Pinia中的每個store都是一個獨立的模塊,它們可以單獨導入和導出,這有助于更好地組織和維護代碼。
  • 響應式:Pinia中的狀態是響應式的,當狀態發生變化時,依賴于這些狀態的組件會自動更新。
  • Devtools支持:Pinia具有良好的Devtools支持,可以幫助開發者更方便地跟蹤和調試狀態變化。
  • 插件化:Pinia被設計為一個插件,可以輕松地集成到現有的Vue應用中。
  • 與Vuex兼容:雖然Pinia是一個全新的狀態管理庫,但它也允許與Vuex共存于同一個項目中,方便開發者逐步遷移。

本次項目中store就基于Pinia實現,首先通過defineStore方法定義一個全局可供調用的store, 其中包括了一些屬性像是

  1. id (自己設定,但是要保證全局唯一)
  2. state (定義的所有狀態)
  3. getters (獲取狀態的方法)
  4. actions (有獲取肯定就有設置的方法啦)
// store.ts
import { createPinia, defineStore } from 'pinia'export const useAuthStore = defineStore({id: 'auth',state: () => ({}),getters: {},actions: {},
})export default createPinia()

4.1 state

在state中定義的狀態就是在一個瀏覽器會話內需要存儲的用戶信息(登錄后賦值,登出或者會話結束就銷毀)根據第2點中types定義的UserInfoRes 可以設計出來, 由于ts不像js一樣是弱語言,ts是有類型的上一講也提到過,所以為了能在后續的get set中拿到指定和設置其中的屬性值,我們需要通過as 參數類型的方式來指定

isAuthenticated 本意是為了阻止用戶登錄前就訪問其他的頁面(會被駁回,重定向到登錄頁面)后面發現存到瀏覽器緩存中也是可以的,這里就做個備選,看讀者喜歡哪一種方式

state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),

這里的RoleDta和RouterVo就分別對應了角色和菜單列表,具體實現如下(編寫在types.ts文件中,具體位置看下邊4.4的總體代碼)


/* sysUser參數類型 */
export interface RoleData {id: number,roleName: string,roleCode: string,description: string
}/* RouterVo參數類型 */
export interface RouterVo {path: string,hidden: boolean,alwaysShow: boolean,meta: MetaVo,children: RouterVo[],
}

4.2 getters

根據如下的指定格式獲取存在store中的參數

getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,
},

4.3 actions

actions中定義了一系列set方法,可以發現這里()內的參數都是指定類型的,如果我們在定義的時候不指定類型這就會報錯!!

actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},// 登出后的資源重置reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},
},

4.4 總體代碼

// store.ts
import { createPinia, defineStore } from 'pinia'
import { RoleData } from '@/api/system/sysRole/types'
import { RouterVo } from '@/api/system/sysMenu/types'export const useAuthStore = defineStore({id: 'auth',state: () => ({token : '',isAuthenticated : false,routers : [] as RouterVo[],buttons : [] as string[],name : '',roles : [] as RoleData[],}),getters: {getButtons: (state) => state.buttons,getToken: (state) => state.token,getIsAuthenticated: (state) => state.isAuthenticated,getRouters: (state) => state.routers,getName: (state) => state.name,getRoles: (state) => state.roles,},actions: {setRoles(roles : RoleData[]) {this.roles = roles},setButtons(buttons : string[]) {this.buttons = buttons},setRouters(routers : RouterVo[]) {this.routers = routers},setName(name : string) {this.name = name},setToken(token : string) {this.token = token},setIsAuthenticated(isAuthenticated : boolean){this.isAuthenticated = isAuthenticated},reset(){this.roles = []this.name = ''this.buttons = []this.routers = []this.isAuthenticated = falsethis.token = ''},},
})
// 記得要導出,不在就白定義了 外部通過調用createPinia() 獲取示例
export default createPinia()

4.5 main.ts中App引入

在Vue中引入App是因為App.vue通常作為項目的主組件和頁面入口文件,負責構建定義及頁面組件的歸集和切換。定義的組件自然要添加到其中,在初始化的時候就一同創建。在文件原有基礎上添加如下代碼

import stores from './stores'
import { createPinia } from 'pinia'app.use(createPinia)
app.use(stores)

最后保存就好啦,到這里在回看第3點的submit方法是不是就一目了然
這里提煉出使用store的核心代碼,有需要的讀者可以直接復制使用😁

// 導入剛剛定義的方法
import { useAuthStore } from '@/stores'// 外部調用創建一個示例(唯一的)
const authStore = useAuthStore()
// 在對應的操作方法里面使用我們在getters和actions中定義的方法
// set
authStore.setToken(token)
// get
const token = authStore.getToken

5. vue限制實現不登錄無法進入其他頁面

這個模塊可用的方法有很多網上也是有各種各樣的教程,在這里使用的是設置路由守衛的方法,在router/index.ts下修改,具體做三種判斷

  1. 防止重復登錄: 登錄后的用戶不能在登錄了,只能主動退出或者關閉瀏覽器(token失效也是一個,這個后面講)
  2. 白名單直接放行:對于可以供給全部用戶訪問的一些靜態資源、頁面(比如登錄頁面,和一些docs幫助文檔是可以直接訪問的)
  3. 沒有登錄:對于沒有登錄的用戶無法訪問系統的資源,為了提防有些通過導航欄修改URL的方法訪問
// 設置哪些頁面是屬于白名單的
const witheList = ["/auth/login"];function isWitheRoute(path : string) {return witheList.includes(path);
}// 全局前置守衛
router.beforeEach((to, from, next) => {const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//防止重復登錄if (isAuthenticated && (to.path === "/auth/login"))  {Message.info("You have successfully logged in. Please avoid logging in repeatedly! (You can log out if you wish)");return next({ path: from.path ? from.path : "/" });}// 判斷如果是白名單就直接放行if (isWitheRoute(to.path)) {next();return;}// 沒有登錄,強制跳轉到登錄頁面if (!isAuthenticated && to.path != "/auth/login") {Message.info("Please logging first");next({ path: "/auth/login" });return;}  next()
});

5.1. 瀏覽器緩存

上邊埋了一個坑,可以使用瀏覽器緩存的方法實現該功能,上邊代碼也看到了window.sessionStorage. 那么這到底是嘛玩意,作用范圍生命周期又是什么呢?下面將一一解答:

  1. sessionStorage為Web開發者提供了一種在用戶的瀏覽器中臨時存儲數據的方式。這種存儲方式特定于用戶打開的特定窗口或標簽頁,并且數據只在這個特定的窗口或標簽頁有效。當用戶關閉這個窗口或標簽頁時,存儲在sessionStorage中的所有數據將被清除。這就意味著不同的瀏覽器窗口或標簽頁,即使是打開相同的網頁,它們之間的sessionStorage數據是不共享的。
  2. sessionStorage的生命周期與用戶打開的窗口或標簽頁的持續時間同步。只要窗口或標簽頁保持打開狀態,即便是進行頁面刷新或切換到同源的其他頁面,sessionStorage中的數據都將持續存在。然而,一旦窗口或標簽頁被關閉,sessionStorage中的所有數據將立即失效并被清除。

可以見得通過該方法保存用戶的登錄狀態也是不錯之選,而且非正常退出時候也不用擔心數據泄露(會自動銷毀,后端的數據就需要通過勾子函數回調,或者直接設置redis過期時間就等它自動過期)下邊就是三個常用的方法:

對于我們的登錄功能來說,在登錄成功后設置為true,此時路由守衛判斷時候就能獲取到該值,而在登出的時候就刪除掉該數據,這樣就能保證統一

//設置對應的key-value
window.sessionStorage.setItem('isAuthenticated', 'true');//通過getItem獲取 (取不到時為null)
const isAuthenticated =  window.sessionStorage.getItem('isAuthenticated');//去除瀏覽器緩存
window.sessionStorage.removeItem('isAuthenticated')

6. 登出功能實現

登出功能本質上是跟登錄沒什么區別的,就是后端清除存儲的數據token , reids中權限數據等,前端清除login獲取到的所有數據(回到出廠設置的感覺)在初始版本中是沒有登出這個按鈕的,經常登錄網頁的朋友都知道,登出的按鈕一般是在右上角,那這里我們就遵循慣例先找找最上邊的欄目是在哪一個vue頁面里面(最笨的方法就是一個一個去搜索是否有相應的字眼)

那么我就以我的理解來告訴大家如何快速找到相應的模塊。首先要知道的是所有的組件都是放在src/components文件夾下的,那我們就去下邊找,一展開就很明顯看到navbar的字眼(導航欄嘛,也就是我們要找的上邊欄所在位置)點開后發現又有個components(根據上面的知識不用我說都知道這是放組件的吧)點開就看到GitHubButton 這不就是我們要找的上邊欄上的github按鈕嗎,說明我們找對地方了,最終就鎖定范圍在這兩個vue文件中,是不是一下子節省很多工作量😁 , 具體示例文件所在處如下圖所示
在這里插入圖片描述

找到這個文件后我們預期的效果是跟下圖這樣加一個Logout 按鈕 用戶點擊就可以退出登錄
在這里插入圖片描述
在AppNavbarActions這個文件中點開就發現其實實現起來很簡單,就是依葫蘆畫瓢,照抄原來有的button組件就好啦,具體代碼如下

<VaButtonv-if="!isMobile"preset="secondary"@click="logoutOper" <!--自定義點擊事件-->target="_blank"color="textPrimary"class="app-navbar-actions__item flex-shrink-0 mx-0"
>{{ t('Logout') }}
</VaButton>

因為我們綁定了點擊事件,自然要實現的啦(如下代碼在script中原有的基礎上添加)

import { logout } from '@/api/system/auth/index'
import { useAuthStore } from '@/stores'
import { useToast } from 'vuestic-ui'
import { useRouter } from 'vue-router'
const { push } = useRouter()
const { init } = useToast()const logoutOper = () => {logout().then(() => {const store = useAuthStore() // 獲取store實例store.reset() // 重置store//去除瀏覽器緩存window.sessionStorage.removeItem('isAuthenticated')//跳轉路由init({ message: "logout success", color: 'success' });push({ name: 'login' })}).catch(() => {init({ message: "logged out fail , please contact administration", color: '#FF0000' });});
}

7. 每次請求時帶上token訪問服務器

由于加入了權限認證功能,所以登錄后的每一次請求都必須攜帶上token(這里的token就遵循OAuth2的規范以"Bearer "開頭),不然會認為沒有登錄跳轉的登錄頁面重新登錄,在每一次請求中添加請求頭是不是就是定義一個全局filter,也就是在上一講中提到的axios請求攔截器,那如下代碼就在utils/request.ts下修改(還沒有的請看上一講)

import { useAuthStore } from '@/stores'/* 請求攔截器 */
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {  const authStore = useAuthStore()if (authStore != undefined) {//獲取tokenconst token = authStore.getTokenconfig.headers.Authorization = `Bearer ${token}`;} else {// 如果不存在 token,則拒絕請求并跳轉到登錄頁面window.location.href = '/auth/login';//去除瀏覽器緩存window.sessionStorage.removeItem('isAuthenticated')return Promise.reject('Authenticated fail');}return config;  }, (error: AxiosError) => {Message.error(error.message);return Promise.reject(error)
})

終于講完啦,這篇內容挺多的,給看到這里的讀者點贊👍,希望能夠對你們有所幫助(本篇主要實現前端的功能,后續會結合權限管理給出后端認證授權功能實現,敬請期待…)


各位讀者我回來填坑啦,對于上面的后端實現我又寫了點自己的想法,感興趣的讀者可以點擊查閱后端認證授權功能實現

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/18250.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18250.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/18250.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python連接mysql,并整理(去哪兒網)頁面數據到表

##引入requests/pymysql模塊 本地安裝mysql數據庫&#xff0c;安裝圖形化工具navicat import requests from pymysql import Connect#創建客戶端連接信息 client Connect(host127.0.0.1,port3306,userroot,password, ) #創建游標 cursor client.cursor() cursor.execute(cre…

17- PHP 開發-個人博客項目TP 框架路由訪問安全寫法歷史漏 洞

常見的php框架&#xff1a;laravel和thinkphp和yii 這里以thinkphp為例 thinkphp目錄訪問設置 這里只找到了這個3.多的源代碼&#xff0c;沒找點5.的&#xff0c;湊合一下 鏈接&#xff1a;GitHub - top-think/thinkphp: ThinkPHP3.2 ——基于PHP5的簡單快速的面向對象的PHP…

HTML用法介紹

文章目錄 一、HTML概念和模版二、常用標簽及用法1.p標簽2.span標簽3.h標簽4.hr標簽5.img標簽6.a標簽7.input標簽8.table標簽 一、HTML概念和模版 HTML的全稱為超文本標記語言&#xff0c;它包括一系列標簽組成&#xff0c;模版及各部分注釋如下&#xff1a; <!--聲明文檔類…

ROS基礎學習-話題通信機制研究

研究ROS通信機制 研究ROS通信機制 0.前言1.話題通信1.1 理論模型1.2 話題通訊的基本操作1.2.1 C++1.2.2 Python中使用自己的虛擬環境包1.2.2.1 參考11.2.2.2 參考21.2.2.3 /usr/bin/env:“python”:沒有那個文件或目錄1.2.3 Python1.2.2.1 發布方1.2.2.2 訂閱方1.2.2.3 添加可執…

【八股系列】談談關于對webpack熱更新的原理?

文章目錄 1. 熱更新原理2. 熱更新配置 1. 熱更新原理 Webpack 的熱模塊替換&#xff08;Hot Module Replacement&#xff0c;HMR&#xff09;是一種在不完全刷新頁面的情況下更新應用代碼的技術&#xff0c;從而提高了開發效率。以下是 HMR 的核心原理&#xff1a; 步驟描述1…

tcpdump抓包,抓包導出.pcap文件用wireshark看

1、抓所有口的包 tcpdump -i any host 設備的ip2、抓特定口的包 tcpdump -i eth2 port 61182 -nne3、將抓到的包導出到pacb文件 tcpdump -i eth2 port 61182 -nne -s0 -w /tmp/61182.pcap -s0: Sets the snapshot length to capture the entire packet. The 0 means that tcpd…

《征服數據結構》目錄

我們知道要想學好算法&#xff0c;必須熟練掌握數據結構&#xff0c;數據結構常見的有 8 大類&#xff0c;分別是數組&#xff0c;鏈表&#xff0c;隊列&#xff0c;棧&#xff0c;散列表&#xff0c;樹&#xff0c;堆&#xff0c;圖。但如果細分的話就比較多了&#xff0c;比如…

go-zero 實戰(2)

go-zero 實戰&#xff08;1&#xff09; 中&#xff0c;使用了go-zero 創建了order 和 user 兩個微服務。而order作為grpc的客戶端&#xff0c;user 作為grpc的服務端&#xff0c;打通了 order 到 user的調用。接下來&#xff0c;我們在user中&#xff0c;加入mysql組件。確保數…

我說同事咋找工作命中率這么高,原來是學習了這些招式

最近有兩個同事離職了&#xff0c;其中一個還是專科&#xff0c;他倆一個是前端開發&#xff0c;一個是python開發&#xff0c;兩個人都接近35歲了。我們還勸告他們&#xff0c;不要離職&#xff0c;要騎驢找馬。但了解后&#xff0c;他倆非常有信心的說&#xff1a;不怕&#…

富格林:遵守可信準則安全交易

富格林指出&#xff0c;當下的金融市場&#xff0c;投資者大多都會更傾向于盈利效率高的理財產品&#xff0c;而近年來興起的現貨黃金&#xff0c;正合投資者的心意。不過&#xff0c;投資現貨黃金若是不遵循其中的可信準則&#xff0c;是難以實現安全盈利的。那么有哪些可信準…

3D視覺技術|螺栓分揀測試

隨著制造業自動化程度的不斷提高&#xff0c;某大型汽配企業為提升生產效率、減少人力成本&#xff0c;提出了使用復合機器人完成螺栓分揀的需求。富唯智能通過采用復合機器人&#xff0c;結合3D工業相機和高性能控制器&#xff0c;實現螺栓的自動抓取&#xff0c;從而提升生產…

鴻蒙OS開發:【一次開發,多端部署】(一多天氣)項目

一多天氣 介紹 本示例展示一個天氣應用界面&#xff0c;包括首頁、城市管理、添加城市、更新時間彈窗&#xff0c;體現一次開發&#xff0c;多端部署的能力。 1.本示例參考一次開發&#xff0c;多端部署的指導&#xff0c;主要使用響應式布局的柵格斷點系統實現在不同尺寸窗…

【Qt 學習筆記】Qt窗口 | 工具欄 | QToolBar的使用及說明

博客主頁&#xff1a;Duck Bro 博客主頁系列專欄&#xff1a;Qt 專欄關注博主&#xff0c;后期持續更新系列文章如果有錯誤感謝請大家批評指出&#xff0c;及時修改感謝大家點贊&#x1f44d;收藏?評論? Qt窗口 | 工具欄 | QToolBar的使用及說明 文章編號&#xff1a;Qt 學習…

怎么看智慧城市的發展?

智慧城市&#xff0c;就像一個擁有高度智慧和感知能力的未來城市居民&#xff0c;正在不斷地學習、適應和進化。它通過無數的眼睛&#xff08;傳感器&#xff09;和耳朵&#xff08;數據收集設備&#xff09;來觀察和傾聽城市的脈動&#xff0c;通過強大的大腦&#xff08;數據…

opencv文檔py_contours示例整理

文章目錄 目錄說明contours_begin目標什么是輪廓?如何畫等高線?輪廓逼近法contour_features目標1.Moments 時刻2. Contour Area 輪廓面積3. Contour Perimeter 輪廓周長4. Contour Approximation 輪廓近似5. Convex Hull 凸包6. Checking Convexity 檢查凸性7. Bounding Rect…

B2118 驗證子串

驗證子串 題目描述 輸入兩個字符串&#xff0c;驗證其中一個串是否為另一個串的子串。 輸入格式 兩行&#xff0c;每行一個字符串。 輸出格式 若第一個串 s 1 s_1 s1? 是第二個串 s 2 s_2 s2? 的子串&#xff0c;則輸出(s1) is substring of (s2)&#xff1b; 否則&…

Python并發與異步編程

Python的并發與異步編程是兩個不同的概念&#xff0c;但它們經常一起使用&#xff0c;以提高程序的性能和響應能力。以下是對這兩個概念的詳細講解&#xff1a; 并發編程 (Concurrency) 并發編程是指在程序中同時執行多個任務的能力。Python提供了幾種實現并發的機制&#xff…

嵌入式進階——RTC時鐘

&#x1f3ac; 秋野醬&#xff1a;《個人主頁》 &#x1f525; 個人專欄:《Java專欄》《Python專欄》 ??心若有所向往,何懼道阻且長 文章目錄 RTC時鐘原理圖PCF8563寄存器控制與狀態寄存器 設備地址I2C環境初始化RTC寄存器數據讀取RTC寄存器數據寫入RTC鬧鐘設置RTC定時器設置…

2024.5.28晚訓題解

提前預告&#xff0c;市賽初中組會考算法題&#xff0c;應該會有兩道模板題 比如DFS BFS 二分 簡單動態規劃&#xff0c;雖然我們沒學多久&#xff0c;但是模板題你還是要會寫的 A題 編輯距離 動態規劃 注意多組輸入 #include<iostream> using namespace std; int dp[1…

9、C#【進階】特性

特性 文章目錄 1、特性概念2、自定義特性 Attribute3、特性的使用4、限制自定義特性的使用范圍5、系統自帶特性1、過時特性2、調用者信息特性3、條件編譯特性4、外部dll包函數特性 1、特性概念 特性是一種允許我們向程序的程序集添加元數據的語言結構 它是用于保存程序機構信息…