Tailwind CSS 實戰:表單設計與驗證實現

在 Web 開發中,表單就像是一位盡職的接待員,負責收集和驗證用戶的輸入信息。記得在一個企業級項目中,我們通過重新設計表單交互流程,將表單的完成率提升了 42%。今天,我想和大家分享如何使用 Tailwind CSS 打造一個既美觀又實用的表單系統。

設計理念

設計表單就像是在設計一次愉快的對話。一個好的表單應該像一個耐心的助手,引導用戶一步步完成信息填寫,并在用戶遇到問題時及時提供幫助。在開始編碼之前,我們需要考慮以下幾個關鍵點:

  1. 布局要清晰,讓用戶一目了然地知道需要填寫什么
  2. 交互要友好,在用戶輸入過程中提供即時反饋
  3. 驗證要智能,在適當的時機進行數據校驗
  4. 錯誤提示要明確,幫助用戶快速定位和解決問題

基礎表單組件

首先,讓我們從一些常用的表單組件開始:

<!-- 文本輸入框 -->
<div class="space-y-1"><label for="username" class="block text-sm font-medium text-gray-700">用戶名</label><div class="relative rounded-md shadow-sm"><input type="text" name="username" id="username" class="block w-full pr-10 border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="請輸入用戶名"required><!-- 驗證狀態圖標 --><div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none"><svg class="h-5 w-5 text-green-500 hidden success-icon" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" /></svg><svg class="h-5 w-5 text-red-500 hidden error-icon" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /></svg></div></div><!-- 錯誤提示 --><p class="mt-1 text-sm text-red-600 hidden error-message" id="username-error"></p>
</div><!-- 密碼輸入框 -->
<div class="space-y-1"><label for="password" class="block text-sm font-medium text-gray-700">密碼</label><div class="relative rounded-md shadow-sm"><input type="password" name="password" id="password" class="block w-full pr-10 border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="請輸入密碼"required><!-- 密碼可見性切換 --><button type="button" class="absolute inset-y-0 right-0 pr-3 flex items-center"οnclick="togglePasswordVisibility(this)"><svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg></button></div><!-- 密碼強度指示器 --><div class="mt-1"><div class="h-1 w-full bg-gray-200 rounded-full overflow-hidden"><div class="h-full bg-gray-400 transition-all duration-300" id="password-strength"></div></div><p class="mt-1 text-xs text-gray-500">密碼強度: <span id="strength-text">弱</span></p></div>
</div><!-- 下拉選擇框 -->
<div class="space-y-1"><label for="country" class="block text-sm font-medium text-gray-700">國家/地區</label><div class="relative"><select id="country" name="country" class="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"><option value="">請選擇</option><option value="CN">中國</option><option value="US">美國</option><option value="JP">日本</option><option value="GB">英國</option></select><div class="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none"><svg class="h-4 w-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" /></svg></div></div>
</div><!-- 復選框組 -->
<div class="space-y-2"><label class="block text-sm font-medium text-gray-700">興趣愛好</label><div class="space-y-2"><label class="inline-flex items-center"><input type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="interests" value="reading"><span class="ml-2 text-sm text-gray-600">閱讀</span></label><label class="inline-flex items-center"><input type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="interests" value="music"><span class="ml-2 text-sm text-gray-600">音樂</span></label><label class="inline-flex items-center"><input type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="interests" value="sports"><span class="ml-2 text-sm text-gray-600">運動</span></label></div>
</div><!-- 單選按鈕組 -->
<div class="space-y-2"><label class="block text-sm font-medium text-gray-700">性別</label><div class="space-x-4"><label class="inline-flex items-center"><input type="radio" class="form-radio border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="gender" value="male"><span class="ml-2 text-sm text-gray-600">男</span></label><label class="inline-flex items-center"><input type="radio" class="form-radio border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" name="gender" value="female"><span class="ml-2 text-sm text-gray-600">女</span></label></div>
</div><!-- 文件上傳 -->
<div class="space-y-1"><label class="block text-sm font-medium text-gray-700">頭像上傳</label><div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"><div class="space-y-1 text-center"><svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48"><path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg><div class="flex text-sm text-gray-600"><label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"><span>上傳文件</span><input id="file-upload" name="file-upload" type="file" class="sr-only" accept="image/*"></label><p class="pl-1">或拖拽文件到這里</p></div><p class="text-xs text-gray-500">PNG, JPG, GIF 最大 2MB</p></div></div>
</div>

表單驗證實現

接下來,我們來實現表單的驗證邏輯:

// 表單驗證類
class FormValidator {constructor(form) {this.form = form;this.validators = {username: this.validateUsername.bind(this),password: this.validatePassword.bind(this),email: this.validateEmail.bind(this),phone: this.validatePhone.bind(this)};this.init();}init() {// 綁定輸入事件this.form.querySelectorAll('input[data-validate]').forEach(input => {input.addEventListener('input', () => this.validateField(input));input.addEventListener('blur', () => this.validateField(input));});// 綁定表單提交事件this.form.addEventListener('submit', (e) => {e.preventDefault();if (this.validateAll()) {this.submitForm();}});}// 驗證單個字段validateField(input) {const field = input.name;const value = input.value;const validator = this.validators[field];if (validator) {const result = validator(value);this.updateFieldStatus(input, result);return result.isValid;}return true;}// 驗證所有字段validateAll() {let isValid = true;this.form.querySelectorAll('input[data-validate]').forEach(input => {if (!this.validateField(input)) {isValid = false;}});return isValid;}// 更新字段狀態updateFieldStatus(input, result) {const container = input.closest('.form-field');const errorMessage = container.querySelector('.error-message');const successIcon = container.querySelector('.success-icon');const errorIcon = container.querySelector('.error-icon');if (result.isValid) {input.classList.remove('border-red-500');input.classList.add('border-green-500');errorMessage.classList.add('hidden');successIcon.classList.remove('hidden');errorIcon.classList.add('hidden');} else {input.classList.remove('border-green-500');input.classList.add('border-red-500');errorMessage.textContent = result.message;errorMessage.classList.remove('hidden');successIcon.classList.add('hidden');errorIcon.classList.remove('hidden');}}// 驗證規則validateUsername(value) {if (!value) {return {isValid: false,message: '用戶名不能為空'};}if (value.length < 3) {return {isValid: false,message: '用戶名至少需要3個字符'};}return {isValid: true};}validatePassword(value) {const hasNumber = /\d/.test(value);const hasLetter = /[a-zA-Z]/.test(value);const hasSpecial = /[!@#$%^&*]/.test(value);if (!value) {return {isValid: false,message: '密碼不能為空'};}if (value.length < 8) {return {isValid: false,message: '密碼至少需要8個字符'};}if (!(hasNumber && hasLetter && hasSpecial)) {return {isValid: false,message: '密碼需要包含數字、字母和特殊字符'};}return {isValid: true};}validateEmail(value) {const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;if (!value) {return {isValid: false,message: '郵箱不能為空'};}if (!emailRegex.test(value)) {return {isValid: false,message: '請輸入有效的郵箱地址'};}return {isValid: true};}validatePhone(value) {const phoneRegex = /^1[3-9]\d{9}$/;if (!value) {return {isValid: false,message: '手機號不能為空'};}if (!phoneRegex.test(value)) {return {isValid: false,message: '請輸入有效的手機號'};}return {isValid: true};}// 提交表單async submitForm() {try {const formData = new FormData(this.form);const response = await fetch(this.form.action, {method: 'POST',body: formData});if (response.ok) {this.showSuccess();} else {this.showError();}} catch (error) {this.showError();}}// 顯示成功提示showSuccess() {const toast = document.createElement('div');toast.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300';toast.textContent = '提交成功!';document.body.appendChild(toast);setTimeout(() => {toast.remove();}, 3000);}// 顯示錯誤提示showError() {const toast = document.createElement('div');toast.className = 'fixed bottom-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300';toast.textContent = '提交失敗,請重試';document.body.appendChild(toast);setTimeout(() => {toast.remove();}, 3000);}
}

密碼強度檢測

為了提升用戶體驗,我們可以添加實時的密碼強度檢測:

class PasswordStrengthMeter {constructor(input, indicator, text) {this.input = input;this.indicator = indicator;this.text = text;this.init();}init() {this.input.addEventListener('input', () => {const strength = this.calculateStrength(this.input.value);this.updateUI(strength);});}calculateStrength(password) {let score = 0;// 長度檢查if (password.length >= 8) score += 1;if (password.length >= 12) score += 1;// 復雜度檢查if (/[0-9]/.test(password)) score += 1;if (/[a-z]/.test(password)) score += 1;if (/[A-Z]/.test(password)) score += 1;if (/[^0-9a-zA-Z]/.test(password)) score += 1;// 重復字符檢查if (!/(.)\1{2,}/.test(password)) score += 1;return score;}updateUI(score) {let strength, color;if (score <= 2) {strength = '弱';color = 'bg-red-500';} else if (score <= 4) {strength = '中';color = 'bg-yellow-500';} else {strength = '強';color = 'bg-green-500';}// 更新進度條this.indicator.className = `h-full transition-all duration-300 ${color}`;this.indicator.style.width = `${(score / 7) * 100}%`;// 更新文本this.text.textContent = strength;}
}

文件上傳預覽

對于文件上傳,我們可以添加拖拽上傳和預覽功能:

class FileUploader {constructor(container) {this.container = container;this.input = container.querySelector('input[type="file"]');this.preview = container.querySelector('.preview');this.dropZone = container.querySelector('.drop-zone');this.init();}init() {// 點擊上傳this.input.addEventListener('change', (e) => {this.handleFiles(e.target.files);});// 拖拽上傳this.dropZone.addEventListener('dragover', (e) => {e.preventDefault();this.dropZone.classList.add('border-indigo-500');});this.dropZone.addEventListener('dragleave', () => {this.dropZone.classList.remove('border-indigo-500');});this.dropZone.addEventListener('drop', (e) => {e.preventDefault();this.dropZone.classList.remove('border-indigo-500');this.handleFiles(e.dataTransfer.files);});}handleFiles(files) {Array.from(files).forEach(file => {// 檢查文件類型if (!file.type.startsWith('image/')) {this.showError('請上傳圖片文件');return;}// 檢查文件大小if (file.size > 2 * 1024 * 1024) {this.showError('文件大小不能超過2MB');return;}// 創建預覽const reader = new FileReader();reader.onload = (e) => {this.createPreview(e.target.result, file.name);};reader.readAsDataURL(file);});}createPreview(src, name) {const preview = document.createElement('div');preview.className = 'relative group';preview.innerHTML = `<img src="${src}" alt="${name}" class="w-20 h-20 object-cover rounded-lg"><button type="button" class="absolute top-0 right-0 -mt-2 -mr-2 bg-red-500 text-white rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200"><svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg></button>`;// 刪除預覽preview.querySelector('button').addEventListener('click', () => {preview.remove();this.input.value = '';});this.preview.appendChild(preview);}showError(message) {const error = document.createElement('div');error.className = 'text-sm text-red-500 mt-1';error.textContent = message;this.container.appendChild(error);setTimeout(() => {error.remove();}, 3000);}
}

動態表單字段

有時我們需要動態添加或刪除表單字段:

class DynamicFields {constructor(container) {this.container = container;this.template = container.querySelector('.field-template');this.fieldsList = container.querySelector('.fields-list');this.addButton = container.querySelector('.add-field');this.init();}init() {this.addButton.addEventListener('click', () => {this.addField();});}addField() {const field = this.template.cloneNode(true);field.classList.remove('hidden', 'field-template');// 更新字段索引const index = this.fieldsList.children.length;field.querySelectorAll('[name]').forEach(input => {input.name = input.name.replace('__INDEX__', index);});// 添加刪除按鈕const deleteButton = field.querySelector('.delete-field');deleteButton.addEventListener('click', () => {field.remove();this.updateIndexes();});this.fieldsList.appendChild(field);}updateIndexes() {Array.from(this.fieldsList.children).forEach((field, index) => {field.querySelectorAll('[name]').forEach(input => {input.name = input.name.replace(/\d+/, index);});});}
}

表單狀態管理

為了更好地管理表單狀態,我們可以使用發布訂閱模式:

class FormState {constructor() {this.subscribers = [];this.state = {};}subscribe(callback) {this.subscribers.push(callback);return () => {this.subscribers = this.subscribers.filter(cb => cb !== callback);};}notify() {this.subscribers.forEach(callback => callback(this.state));}setState(newState) {this.state = { ...this.state, ...newState };this.notify();}getState() {return this.state;}
}// 使用示例
const formState = new FormState();// 訂閱狀態變化
formState.subscribe((state) => {// 更新UIObject.entries(state).forEach(([field, value]) => {const input = document.querySelector(`[name="${field}"]`);if (input) {input.value = value;}});
});// 監聽輸入變化
document.querySelectorAll('input, select, textarea').forEach(input => {input.addEventListener('input', (e) => {formState.setState({[e.target.name]: e.target.value});});
});

寫在最后

通過這篇文章,我們詳細探討了如何使用 Tailwind CSS 構建一個現代化的表單系統。從基礎組件到驗證邏輯,從文件上傳到狀態管理,我們不僅關注了視覺效果,更注重了用戶體驗和代碼質量。

記住,一個優秀的表單就像一個稱職的接待員,需要耐心地引導用戶完成信息填寫,并在遇到問題時及時提供幫助。在實際開發中,我們要始終以用戶需求為中心,在易用性和安全性之間找到最佳平衡點。

如果覺得這篇文章對你有幫助,別忘了點個贊 👍

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

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

相關文章

信息系統項目管理師——第8章章 項目整合管理 筆記

8 項目整合管理&#xff08;最后反過來看&#xff09; 項目整合過程&#xff1a;①制定項目章程&#xff08;啟動過程&#xff09;、②制訂項目管理計劃&#xff08;規劃過程&#xff09;、③指導和管理項目工作、管理項目知識&#xff08;執行過程&#xff09;、④監控項目工…

MLP、CNN、Transformer 的區別解析

親愛的小伙伴們&#x1f618;&#xff0c;在求知的漫漫旅途中&#xff0c;若你對深度學習的奧秘、Java 與 Python 的奇妙世界&#xff0c;亦或是讀研論文的撰寫攻略有所探尋&#x1f9d0;&#xff0c;那不妨給我一個小小的關注吧&#x1f970;。我會精心籌備&#xff0c;在未來…

WebRTC線程的啟動與運行

WebRTC線程運行的基本邏輯&#xff1a; while(true) {…Get(&msg, …);…Dispatch(&msg);… }Dispatch(Message *pmsg) {…pmsg->handler->OnMessage(pmsg);… }在執行函數內部&#xff0c;就是一個while死循環&#xff0c;只做兩件事&#xff0c;從隊列里Get取…

CSS 學習之 padding 與圖形繪制

padding 屬性和 background-clip 屬性配合&#xff0c;可以在有限的標簽下實現一些 CSS 圖形繪制效果&#xff0c;我這里舉兩個小例子&#xff0c;重在展示可行性。 例 1:不使用偽元素&#xff0c;僅一層標簽實現大隊長的“三道杠”分類圖標效果。此效果在移動端比較常見&…

yolov5核查數據標注漏報和誤報

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、誤報二、漏報三、源碼總結 前言 本文主要用于記錄數據標注和模型預測之間的漏報和誤報思想及其源碼 提示&#xff1a;以下是本篇文章正文內容&#xff0c;…

UnityRenderStreaming使用記錄(四)

測試把UnityRenderStreaming部署在docker&#xff0c;劇透一下&#xff0c;嘎了…… 當然webserver運行的妥妥的 那么打包出的程序運行log Mono path[0] /home/unity/Broadcast/Broadcast_Data/Managed Mono config path /home/unity/Broadcast/Broadcast_Data/MonoBleedingE…

salesforce addMonths()的問題

如果使用 Salesforce 的 addMonths(1) 方法&#xff0c;將 1月30日 或 1月31日 加一個月&#xff0c;都會得到 2月28日&#xff08;或 2月29日&#xff0c;如果是閏年&#xff09;。這是因為 Salesforce 的 addMonths 方法在跨月份時會自動調整日期&#xff0c;確保結果是有效日…

3. C語言 數據類型

本章目錄&#xff1a; 前言&#xff1a;C語言中的數據類型分類1. 基本數據類型1.1 整數類型1.2 浮點類型1.3 字符型常量1.4 字符串常量 2. 枚舉類型3. void 類型void類型的使用示例&#xff1a; 4. 類型轉換4.1 隱式類型轉換4.2 顯式類型轉換類型轉換的注意事項 5. 小結 前言&a…

JUnit注解,枚舉

一、JUnit注解&#xff08;Annotations&#xff09; JUnit 是 Java 中用于編寫和運行單元測試的框架。JUnit 提供了許多注解&#xff0c;用于控制測試的執行順序、測試生命周期、斷言結果等。以下是一些常用的 JUnit 注解及其作用&#xff1a; 1. Test 用于標記一個方法是測…

富芮坤FR800X系列之軟件開發工具鏈(如IDE、編譯器、調試器等)

文章目錄 一、IDE&#xff08;集成開發環境&#xff09;二、編譯器三、調試器四、其他輔助工具五、小結 FR800x系列作為一款低功耗藍牙芯片&#xff0c;其軟件開發工具鏈對于開發者來說至關重要。以下是對FR800x軟件開發工具鏈的詳細介紹&#xff0c;包括IDE&#xff08;集成開…

數據賦能電商:API如何助力品牌成長

在數字時代&#xff0c;數據已成為電商品牌發展的核心驅動力。API&#xff08;應用程序編程接口&#xff09;作為數據交互的橋梁&#xff0c;不僅促進了數據的高效流通&#xff0c;更為電商品牌帶來了前所未有的增長機遇。本文將深入探討API如何助力電商品牌實現數據賦能&#…

「Java 數據結構全面解讀」:從基礎到進階的實戰指南

「Java 數據結構全面解讀」&#xff1a;從基礎到進階的實戰指南 數據結構是程序設計中的核心部分&#xff0c;用于組織和管理數據。Java 提供了豐富的集合框架和工具類&#xff0c;涵蓋了常見的數據結構如數組、鏈表、棧、隊列和樹等。本文將系統性地介紹這些數據結構的概念、…

LeetCode - 初級算法 數組(旋轉數組)

旋轉數組 這篇文章討論如何通過編程實現數組元素的旋轉操作。 免責聲明:本文來源于個人知識與公開資料,僅用于學術交流。 描述 給定一個整數數組 nums,將數組中的元素向右輪轉 k 個位置,其中 k 是非負數。 示例: 輸入: nums = [1,2,3,

c#集合詳解-Dictionary、List、Queue、Stack等

目錄 一&#xff0c;非泛型集合 1&#xff0c;ArrayList &#xff08;1&#xff09;創建和初始化ArrayList() &#xff08;2&#xff09;常用方法 ①Add(value) ②AddRange(value) ③Insert(index,value) ④Remove(value) ⑤RemoveAt(index) ⑥clear() ⑦Contains(v…

記一次網閘部署經歷

1.在成功獲取某大廠偉思網閘設備&#xff0c;并與客戶就現有網絡架構&#xff08;包括防火墻與交換機&#xff09;進行了詳盡的溝通與評估后&#xff0c;我們團隊精心構思并提出了一個創新的實施方案——采用透明網橋模式。這一模式以其獨特的優勢&#xff0c;即無需對客戶現有…

SpringCloud(一)--SpringCloud簡介

一. 引言 ? 在微服務架構日益盛行的今天&#xff0c;Spring Cloud憑借其簡單易用、功能強大的特性&#xff0c;成為了眾多開發者的首選。本文僅為學習所用&#xff0c;聯系侵刪。 二. SpringCloud概述 2.1 定義 ? Spring Cloud是一系列框架的有序集合&#xff0c;它巧妙地…

SQLALchemy如何將SQL語句編譯為特定數據庫方言

最近在一個使用fastapitortoise-orm的項目中&#xff0c;需要將orm的語句編譯成特定數據庫方言&#xff0c;但是查詢了官方文檔及一些資料卻找不到合適的方法論&#x1f614;&#xff0c;于是乎我就把目光放到了sqlalchemy身上&#xff0c;東找西找給我找著了。話不多說&#x…

廬山派K230學習日記2 MicroPython基礎

MicroPython文檔&#xff1a; https://docs.micropython.org/ MicroPython是編程語言 Python3 的精簡高效實現&#xff0c;語法和 Python3 保持一致&#xff0c;但只實現了 Python 標準庫的一小部分&#xff0c;并且經過優化&#xff0c;適用于物聯網 (IoT)、消費電子和嵌入式…

《計算機組成及匯編語言原理》閱讀筆記:p177-p177

《計算機組成及匯編語言原理》學習第 13 天&#xff0c;p177-p177 總結&#xff0c;總計 1 頁。 一、技術總結 1.real mode A programming model where the program has access to the entire capability of the machine, bypassing security and memory management. Useful…

2000-2020年各省財政一般預算支出面板數據

2000-2020年各省財政一般預算支出面板數據 1、時間&#xff1a;2000-2020年 2、來源&#xff1a;國家統計局 3、指標&#xff1a;年份、省份、地方財政一般預算支出 4、范圍&#xff1a;31省 指標解釋&#xff1a;地方財政一般預算支出?是指地方ZF根據預算安排&#xff0…