feat(i18n) multi-lang on uart page: Chinese, French and English.
This commit is contained in:
		
							parent
							
								
									dd1b9adc0d
								
							
						
					
					
						commit
						d7d7c94f53
					
				|  | @ -10,7 +10,7 @@ | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@vueuse/core": "^10.9.0", |         "@vueuse/core": "^10.9.0", | ||||||
|         "ansi_up": "^6.0.2", |         "ansi_up": "^6.0.2", | ||||||
|         "element-plus": "^2.7.3", |         "element-plus": "^2.8.1", | ||||||
|         "mitt": "^3.0.1", |         "mitt": "^3.0.1", | ||||||
|         "pinia": "^2.1.7", |         "pinia": "^2.1.7", | ||||||
|         "vue": "^3.4.21", |         "vue": "^3.4.21", | ||||||
|  | @ -979,31 +979,29 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@volar/language-core": { |     "node_modules/@volar/language-core": { | ||||||
|       "version": "2.1.4", |       "version": "2.4.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.1.4.tgz", |       "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0.tgz", | ||||||
|       "integrity": "sha512-ROfPepDxZ5Eq+Unbx3M9QcHT7MoE9tYdbkuzLTtxG5rfkEi5RwsDPncjANMOq/gHhIIDlWgqWwS2nXWMGsuj4w==", |       "integrity": "sha512-FTla+khE+sYK0qJP+6hwPAAUwiNHVMph4RUXpxf/FIPKUP61NFrVZorml4mjFShnueR2y9/j8/vnh09YwVdH7A==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@volar/source-map": "2.1.4" |         "@volar/source-map": "2.4.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@volar/source-map": { |     "node_modules/@volar/source-map": { | ||||||
|       "version": "2.1.4", |       "version": "2.4.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.1.4.tgz", |       "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0.tgz", | ||||||
|       "integrity": "sha512-mCg8IiPZmHZVzqL4Owg+BzQ5ZTG1cVwATxrkrFPZpcAin97Xa3MbchxVhHtHTWTT8ER8bJh5xVjeVxsSN++FUA==", |       "integrity": "sha512-2ceY8/NEZvN6F44TXw2qRP6AQsvCYhV2bxaBPWxV9HqIfkbRydSksTFObCF1DBDNBfKiZTS8G/4vqV6cvjdOIQ==", | ||||||
|       "dev": true, |       "dev": true | ||||||
|       "dependencies": { |  | ||||||
|         "muggle-string": "^0.4.0" |  | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
|     "node_modules/@volar/typescript": { |     "node_modules/@volar/typescript": { | ||||||
|       "version": "2.1.4", |       "version": "2.4.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.1.4.tgz", |       "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0.tgz", | ||||||
|       "integrity": "sha512-Mt7wOLPkomFnUfVpb5IHlPhSpD7FJAn+FHSsovePmqFNQzFLz16wrpHjAkorPiAnP0847w71NL5fIJyWbAsR8Q==", |       "integrity": "sha512-9zx3lQWgHmVd+JRRAHUSRiEhe4TlzL7U7e6ulWXOxHH/WNYxzKwCvZD7WYWEZFdw4dHfTD9vUR0yPQO6GilCaQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@volar/language-core": "2.1.4", |         "@volar/language-core": "2.4.0", | ||||||
|         "path-browserify": "^1.0.1" |         "path-browserify": "^1.0.1", | ||||||
|  |         "vscode-uri": "^3.0.8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@vue/compiler-core": { |     "node_modules/@vue/compiler-core": { | ||||||
|  | @ -1052,6 +1050,16 @@ | ||||||
|         "@vue/shared": "3.4.21" |         "@vue/shared": "3.4.21" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@vue/compiler-vue2": { | ||||||
|  |       "version": "2.7.16", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", | ||||||
|  |       "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", | ||||||
|  |       "dev": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "de-indent": "^1.0.2", | ||||||
|  |         "he": "^1.2.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/@vue/devtools-api": { |     "node_modules/@vue/devtools-api": { | ||||||
|       "version": "6.6.1", |       "version": "6.6.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", |       "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", | ||||||
|  | @ -1096,18 +1104,19 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@vue/language-core": { |     "node_modules/@vue/language-core": { | ||||||
|       "version": "2.0.7", |       "version": "2.0.29", | ||||||
|       "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.7.tgz", |       "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.29.tgz", | ||||||
|       "integrity": "sha512-Vh1yZX3XmYjn9yYLkjU8DN6L0ceBtEcapqiyclHne8guG84IaTzqtvizZB1Yfxm3h6m7EIvjerLO5fvOZO6IIQ==", |       "integrity": "sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@volar/language-core": "~2.1.3", |         "@volar/language-core": "~2.4.0-alpha.18", | ||||||
|         "@vue/compiler-dom": "^3.4.0", |         "@vue/compiler-dom": "^3.4.0", | ||||||
|  |         "@vue/compiler-vue2": "^2.7.16", | ||||||
|         "@vue/shared": "^3.4.0", |         "@vue/shared": "^3.4.0", | ||||||
|         "computeds": "^0.0.1", |         "computeds": "^0.0.1", | ||||||
|         "minimatch": "^9.0.3", |         "minimatch": "^9.0.3", | ||||||
|         "path-browserify": "^1.0.1", |         "muggle-string": "^0.4.1", | ||||||
|         "vue-template-compiler": "^2.7.14" |         "path-browserify": "^1.0.1" | ||||||
|       }, |       }, | ||||||
|       "peerDependencies": { |       "peerDependencies": { | ||||||
|         "typescript": "*" |         "typescript": "*" | ||||||
|  | @ -1977,9 +1986,9 @@ | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/element-plus": { |     "node_modules/element-plus": { | ||||||
|       "version": "2.7.3", |       "version": "2.8.1", | ||||||
|       "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.7.3.tgz", |       "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.8.1.tgz", | ||||||
|       "integrity": "sha512-OaqY1kQ2xzNyRFyge3fzM7jqMwux+464RBEqd+ybRV9xPiGxtgnj/sVK4iEbnKnzQIa9XK03DOIFzoToUhu1DA==", |       "integrity": "sha512-p11/6w/O0+hGvPhiN3jrcgh+XG+eg5jZlLdQVYvcPHZYhhCh3J3YeZWW1JO/REPES1vevkboT6VAi+9wHA8Dsg==", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@ctrl/tinycolor": "^3.4.1", |         "@ctrl/tinycolor": "^3.4.1", | ||||||
|         "@element-plus/icons-vue": "^2.3.1", |         "@element-plus/icons-vue": "^2.3.1", | ||||||
|  | @ -3249,9 +3258,9 @@ | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/micromatch": { |     "node_modules/micromatch": { | ||||||
|       "version": "4.0.7", |       "version": "4.0.8", | ||||||
|       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", |       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", | ||||||
|       "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", |       "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "braces": "^3.0.3", |         "braces": "^3.0.3", | ||||||
|  | @ -5042,6 +5051,12 @@ | ||||||
|         "vue": ">=3.2.13" |         "vue": ">=3.2.13" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/vscode-uri": { | ||||||
|  |       "version": "3.0.8", | ||||||
|  |       "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", | ||||||
|  |       "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "node_modules/vue": { |     "node_modules/vue": { | ||||||
|       "version": "3.4.21", |       "version": "3.4.21", | ||||||
|       "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", |       "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", | ||||||
|  | @ -5132,31 +5147,21 @@ | ||||||
|         "vue": "^3.2.0" |         "vue": "^3.2.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/vue-template-compiler": { |  | ||||||
|       "version": "2.7.16", |  | ||||||
|       "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", |  | ||||||
|       "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", |  | ||||||
|       "dev": true, |  | ||||||
|       "dependencies": { |  | ||||||
|         "de-indent": "^1.0.2", |  | ||||||
|         "he": "^1.2.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/vue-tsc": { |     "node_modules/vue-tsc": { | ||||||
|       "version": "2.0.7", |       "version": "2.0.29", | ||||||
|       "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.7.tgz", |       "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.29.tgz", | ||||||
|       "integrity": "sha512-LYa0nInkfcDBB7y8jQ9FQ4riJTRNTdh98zK/hzt4gEpBZQmf30dPhP+odzCa+cedGz6B/guvJEd0BavZaRptjg==", |       "integrity": "sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@volar/typescript": "~2.1.3", |         "@volar/typescript": "~2.4.0-alpha.18", | ||||||
|         "@vue/language-core": "2.0.7", |         "@vue/language-core": "2.0.29", | ||||||
|         "semver": "^7.5.4" |         "semver": "^7.5.4" | ||||||
|       }, |       }, | ||||||
|       "bin": { |       "bin": { | ||||||
|         "vue-tsc": "bin/vue-tsc.js" |         "vue-tsc": "bin/vue-tsc.js" | ||||||
|       }, |       }, | ||||||
|       "peerDependencies": { |       "peerDependencies": { | ||||||
|         "typescript": "*" |         "typescript": ">=5.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/vuetify": { |     "node_modules/vuetify": { | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vueuse/core": "^10.9.0", |     "@vueuse/core": "^10.9.0", | ||||||
|     "ansi_up": "^6.0.2", |     "ansi_up": "^6.0.2", | ||||||
|     "element-plus": "^2.7.3", |     "element-plus": "^2.8.1", | ||||||
|     "mitt": "^3.0.1", |     "mitt": "^3.0.1", | ||||||
|     "pinia": "^2.1.7", |     "pinia": "^2.1.7", | ||||||
|     "vue": "^3.4.21", |     "vue": "^3.4.21", | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></svg> | ||||||
| After Width: | Height: | Size: 411 B | 
							
								
								
									
										41
									
								
								src/i18n.ts
								
								
								
								
							
							
						
						
									
										41
									
								
								src/i18n.ts
								
								
								
								
							|  | @ -1,19 +1,48 @@ | ||||||
| import {createI18n} from 'vue-i18n'; | import {createI18n} from 'vue-i18n'; | ||||||
| import zh from '@/locales/zh' | import zh from '@/locales/zh' | ||||||
| import en from '@/locales/en' | import en from '@/locales/en' | ||||||
|  | import fr from '@/locales/fr' | ||||||
| 
 | 
 | ||||||
| // const locale = localStorage.getItem('lang') || 'zh';
 | const userLanguage = navigator.language || 'en'; | ||||||
| export const locale = 'zh'; | 
 | ||||||
|  | // Get the language code (e.g., 'en' from 'en-US')
 | ||||||
|  | export const locale = userLanguage.split('-')[0]; | ||||||
|  | const messages = { | ||||||
|  |     zh, | ||||||
|  |     en, | ||||||
|  |     fr, | ||||||
|  | } as const; | ||||||
|  | 
 | ||||||
|  | type Locale = keyof typeof messages; | ||||||
|  | 
 | ||||||
|  | export const availableLanguages = Object.keys(messages); | ||||||
|  | 
 | ||||||
|  | // export const locale = 'zh';
 | ||||||
|  | console.log(userLanguage, locale, availableLanguages) | ||||||
| 
 | 
 | ||||||
| const i18n = createI18n({ | const i18n = createI18n({ | ||||||
|     globalInjection: true, |     globalInjection: true, | ||||||
|     legacy: false, |     legacy: false, | ||||||
|     locale: locale, |     locale: locale, | ||||||
|     fallbackLocale: 'zh', |     fallbackLocale: 'zh', | ||||||
|     messages: { |     messages: messages | ||||||
|         zh, |  | ||||||
|         // en,
 |  | ||||||
|     } |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | export function getFlagFromLang(lang: string) { | ||||||
|  |     if (lang === 'zh') { | ||||||
|  |         return '🇨🇳'; | ||||||
|  |     } else if (lang === 'en') { | ||||||
|  |         return '🇺🇸'; | ||||||
|  |     } else if (lang === 'fr') { | ||||||
|  |         return '🇫🇷'; | ||||||
|  |     } | ||||||
|  |     return '🏳️'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function setLang(lang: string): void { | ||||||
|  |     if (availableLanguages.includes(lang)) { | ||||||
|  |         i18n.global.locale.value = lang as Locale; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export default i18n; | export default i18n; | ||||||
|  |  | ||||||
|  | @ -1,3 +1,127 @@ | ||||||
| export default { | export default { | ||||||
|     disconnected: "disconnected" |     emoji: { | ||||||
|  |         flag: "🇺🇸", | ||||||
|  |     }, | ||||||
|  |     disconnected: "Disconnected", | ||||||
|  |     connected: "Connected", | ||||||
|  |     connecting: "Connecting", | ||||||
|  | 
 | ||||||
|  |     ws: { | ||||||
|  |         disconnected: "Disconnected", | ||||||
|  |         connected: "Connected", | ||||||
|  |         connecting: "Connecting", | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     page: { | ||||||
|  |         home: "Home", | ||||||
|  |         wifi: "Wi-Fi", | ||||||
|  |         about: "About", | ||||||
|  |         uart: "Uart", | ||||||
|  |         feedback: "Feedback", | ||||||
|  |         close: "Close", | ||||||
|  |         update: "Update", | ||||||
|  |         fullscreen: "Fullscreen", | ||||||
|  |         windowed: "Windowed" | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     uart: { | ||||||
|  |         port: "Port", | ||||||
|  |         startCommunication: "Start Communication", | ||||||
|  |         stopCommunication: "Stop Communication", | ||||||
|  |         commonlyUsed: "Common", | ||||||
|  |         baudrate: "Baud Rate", | ||||||
|  |         customBaud: "Custom Baud", | ||||||
|  |         use: "Use", | ||||||
|  |         actual: "Actual", | ||||||
|  |         dataBits: "Data Bits", | ||||||
|  |         stopBits: "Stop Bits", | ||||||
|  |         parity: "Parity", | ||||||
|  |         parityNone: "None", | ||||||
|  |         parityOdd: "Odd", | ||||||
|  |         parityEven: "Even", | ||||||
|  |         flowControl: "Flow Control", | ||||||
|  |         send: "Send", | ||||||
|  |         clear: "Clear", | ||||||
|  |         clearTooltip: "Only clears the display area, can be restored with refresh.", | ||||||
|  |         updateTooltip: "Sync with cache + filter", | ||||||
|  |         autoUpdateTooltip: "Only stop refreshing the display area; the background continues to receive data.", | ||||||
|  |         receive: "Receive", | ||||||
|  | 
 | ||||||
|  |         displayOptions: "Display Options", | ||||||
|  |         display: "Display", | ||||||
|  |         show: "Show", | ||||||
|  |         text: "Text", | ||||||
|  |         timestamp: "Timestamp", | ||||||
|  |         enable: "Enable", | ||||||
|  |         lineWrap: "Line Wrap", | ||||||
|  |         highlight: "Highlight", | ||||||
|  | 
 | ||||||
|  |         frameBreakStrategy: "Frame Break Strategy", | ||||||
|  |         priority: "Priority", | ||||||
|  |         rule: "Rule", | ||||||
|  |         ruleTips: | ||||||
|  |             "<p>Timeout=-1: Disable timeout frame break</p>" + | ||||||
|  |             "<p>Timeout=0: Immediate break, any received data is considered complete</p>" + | ||||||
|  |             "<p>Match after break: Typical \\n scenario</p>" + | ||||||
|  |             "<p>Match before break: For scenarios with special frame headers</p>" + | ||||||
|  |             "<p>Fixed byte frame break: Useful for large data transfer, e.g., break frame every 1024 bytes for easy data viewing</p>", | ||||||
|  |         value: "Value", | ||||||
|  |         timeout: "Timeout", | ||||||
|  |         match: "Match", | ||||||
|  |         byte: "Byte", | ||||||
|  |         begin: "b", | ||||||
|  |         end: "b", | ||||||
|  | 
 | ||||||
|  |         other: "Other", | ||||||
|  |         decodeAnsiEscapeCodes: "Decode ANSI Escape Codes", | ||||||
|  |         ansiTooltips: | ||||||
|  |             "<p>ANSI escape codes have many uses for terminals and text, such as changing text colors, among other effects.</p>\n" + | ||||||
|  |             "<p>\n  Learn more ->\n  <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/ANSI_escape_code\">" + | ||||||
|  |             "https://en.wikipedia.org/wiki/ANSI_escape_code\n  </a></p>", | ||||||
|  |         filter: "Filter", | ||||||
|  |         textAndEscape: "Text with \\n\\x support", | ||||||
|  |         autoUpdateNewData: "Auto-refresh new data", | ||||||
|  |         updateFrequency: "Data Display Update Interval (ms)", | ||||||
|  |         updateFrequencyTooltip: "Increasing the interval can reduce CPU usage.", | ||||||
|  | 
 | ||||||
|  |         addHeader: "Add Header", | ||||||
|  |         addFooter: "Add Footer", | ||||||
|  | 
 | ||||||
|  |         passthrough: "Passthrough", | ||||||
|  |         proxy: "Proxy", | ||||||
|  |         serverPort: "Server Port", | ||||||
|  |         connectedClient: "Connected Client", | ||||||
|  |         refresh: "Refresh", | ||||||
|  |         interface: "Interface", | ||||||
|  |         noClientConnected: "No Client Connected", | ||||||
|  | 
 | ||||||
|  |         import: "Import", | ||||||
|  |         export: "Export", | ||||||
|  |         reset: "Reset", | ||||||
|  |         resetTooltip: "Takes effect after refreshing the page.", | ||||||
|  |         saveToLocal: "Save to Local", | ||||||
|  |         saveToLocalTooltip: "If multiple pages exist, they will overwrite each other.", | ||||||
|  |         add: "Add", | ||||||
|  |         edit: "Edit", | ||||||
|  |         drag: "Drag", | ||||||
|  |         ipChangeAlert: "Changing the IP address will cause the configuration to be lost.", | ||||||
|  | 
 | ||||||
|  |         layout: "Layout", | ||||||
|  |         landscape: "Landscape", | ||||||
|  |         portrait: "Portrait", | ||||||
|  |         responsive: "Responsive", | ||||||
|  |         configPannel: "Config", | ||||||
|  |         displayPannel: "Display", | ||||||
|  |         macroPannel: "Quick Send", | ||||||
|  |         autoScrollToBottom: "Auto Scroll", | ||||||
|  |         clearScreen: "Clear", | ||||||
|  |         autoUpdate: "Auto Update", | ||||||
|  |         tempDisplayTooltip: "Data that does not meet the frame-break rules (e.g., not timed out) is temporarily displayed in real-time in this area. If it exceeds 8192 bytes, it will automatically break frames.", | ||||||
|  |         loopSend: "Loop Send", | ||||||
|  |         loopSendTooltip: "The actual frequency is affected by the interface refresh rate. For more accuracy, you can try turning off 'auto-refresh'.", | ||||||
|  |         sendFormat: "Send Format", | ||||||
|  |         cachedFrame: "Cached", | ||||||
|  |         format: "Format", | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
|  | @ -0,0 +1,128 @@ | ||||||
|  | export default { | ||||||
|  |     emoji: { | ||||||
|  |         flag: "🇫🇷", | ||||||
|  |     }, | ||||||
|  |     disconnected: "Déconnecté", | ||||||
|  |     connected: "Connecté", | ||||||
|  |     connecting: "Connexion..", | ||||||
|  | 
 | ||||||
|  |     ws: { | ||||||
|  |         disconnected: "Déconnecté", | ||||||
|  |         connected: "Connecté", | ||||||
|  |         connecting: "Connexion..", | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     page: { | ||||||
|  |         home: "Accueil", | ||||||
|  |         wifi: "Wi-Fi", | ||||||
|  |         about: "À propos", | ||||||
|  |         uart: "Uart", | ||||||
|  |         feedback: "Feedback", | ||||||
|  |         close: "Fermer", | ||||||
|  |         update: "Mise à jour", | ||||||
|  |         fullscreen: "Plein écran", | ||||||
|  |         windowed: "Fenêtré", | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     uart: { | ||||||
|  |         port: "Port", | ||||||
|  |         startCommunication: "Démarrer la communication", | ||||||
|  |         stopCommunication: "Arrêter la communication", | ||||||
|  |         commonlyUsed: "Fréquemment utilisé", | ||||||
|  |         baudrate: "Taux de Baud", | ||||||
|  |         customBaud: "Baud", | ||||||
|  |         use: "Utiliser", | ||||||
|  |         actual: "Actuel", | ||||||
|  |         dataBits: "Bits de Données", | ||||||
|  |         stopBits: "Bits d'Arrêt", | ||||||
|  |         parity: "Parité", | ||||||
|  |         parityNone: "Aucune", | ||||||
|  |         parityOdd: "Impair(Odd)", | ||||||
|  |         parityEven: "Pair(Even)", | ||||||
|  |         flowControl: "Contrôle de Flux", | ||||||
|  |         send: "Envoyer", | ||||||
|  |         clear: "Effacer", | ||||||
|  |         clearTooltip: "Ne supprime que la zone d'affichage, peut être restaurée en actualisant.", | ||||||
|  |         updateTooltip: "Synchroniser avec le cache + filtrer", | ||||||
|  |         autoUpdateTooltip: "Arrête uniquement le rafraîchissement de la zone d'affichage ; l'arrière-plan continue de recevoir des données.", | ||||||
|  |         receive: "Recevoir", | ||||||
|  | 
 | ||||||
|  |         displayOptions: "Options d'Affichage", | ||||||
|  |         display: "Affichage", | ||||||
|  |         show: "Afficher", | ||||||
|  |         text: "Texte", | ||||||
|  |         timestamp: "Horodatage", | ||||||
|  |         enable: "Activer", | ||||||
|  |         lineWrap: "Retour à la Ligne", | ||||||
|  |         highlight: "Surligner", | ||||||
|  | 
 | ||||||
|  |         frameBreakStrategy: "Stratégie de Coupure de Trame", | ||||||
|  |         priority: "Priorité", | ||||||
|  |         rule: "Règle", | ||||||
|  |         ruleTips: | ||||||
|  |             "<p>Délai d'expiration=-1 : Désactiver la coupure de trame par délai d'expiration</p>" + | ||||||
|  |             "<p>Délai d'expiration=0 : Coupure immédiate, toutes données reçues sont considérées complètes</p>" + | ||||||
|  |             "<p>Match après coupure : Scénario typique \\n</p>" + | ||||||
|  |             "<p>Match avant coupure : Pour des scénarios avec en-têtes de trame spécifiques</p>" + | ||||||
|  |             "<p>Coupure de trame par octets fixes : Utile pour le transfert de grandes quantités de données, par exemple, couper la trame tous les 1024 octets pour faciliter la visualisation des données</p>", | ||||||
|  |         value: "Valeur", | ||||||
|  |         timeout: "Timeout", | ||||||
|  |         match: "Match", | ||||||
|  |         byte: "Byte", | ||||||
|  |         begin: "b", | ||||||
|  |         end: "b", | ||||||
|  | 
 | ||||||
|  |         other: "Autres", | ||||||
|  |         decodeAnsiEscapeCodes: "Décode Échappement ANSI", | ||||||
|  |         ansiTooltips: | ||||||
|  |             "<p>Les codes d'échappement ANSI ont de nombreuses utilisations pour les terminaux et le texte, comme changer les couleurs du texte, entre autres effets.</p>" + | ||||||
|  |             "<p>\n  En savoir plus ->\n  " + | ||||||
|  |             "<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/ANSI_escape_code\">\n" + | ||||||
|  |             "https://en.wikipedia.org/wiki/ANSI_escape_code\n  </a>\n</p>", | ||||||
|  |         filter: "Filtrer", | ||||||
|  |         textAndEscape: "Texte;supporte\\n\\x", | ||||||
|  |         autoUpdateNewData: "Auto-update nouvelles données", | ||||||
|  |         updateFrequency: "Délais rafraîchissement des Données (ms)", | ||||||
|  |         updateFrequencyTooltip: "Augmenter l'intervalle peut réduire l'utilisation des ressources CPU.", | ||||||
|  | 
 | ||||||
|  |         addHeader: "Ajouter un En-tête", | ||||||
|  |         addFooter: "Ajouter un Pied de page", | ||||||
|  | 
 | ||||||
|  |         passthrough: "Passage Direct", | ||||||
|  |         proxy: "Proxy", | ||||||
|  |         serverPort: "Port Serveur", | ||||||
|  |         connectedClient: "Client Connecté", | ||||||
|  |         refresh: "Rafraîchir", | ||||||
|  |         interface: "Interface", | ||||||
|  |         noClientConnected: "Aucun Client Connecté", | ||||||
|  | 
 | ||||||
|  |         import: "Importer", | ||||||
|  |         export: "Exporter", | ||||||
|  |         reset: "Réinitialiser", | ||||||
|  |         resetTooltip: "Prend effet après le rafraîchissement de la page.", | ||||||
|  |         saveToLocal: "Enregistrer Localement", | ||||||
|  |         saveToLocalTooltip: "S'il existe plusieurs pages, elles se chevaucheront mutuellement.", | ||||||
|  |         add: "Ajouter", | ||||||
|  |         edit: "Éditer", | ||||||
|  |         drag: "Glisser", | ||||||
|  |         ipChangeAlert: "Le changement d'adresse IP entraînera la perte de la configuration.", | ||||||
|  | 
 | ||||||
|  |         layout: "Disposition", | ||||||
|  |         landscape: "Paysage", | ||||||
|  |         portrait: "Portrait", | ||||||
|  |         responsive: "Résponsive", | ||||||
|  |         configPannel: "Configuration", | ||||||
|  |         displayPannel: "Données", | ||||||
|  |         macroPannel: "Envoie Rapide", | ||||||
|  |         autoScrollToBottom: "Auto Scroll", | ||||||
|  |         clearScreen: "Effacer", | ||||||
|  |         autoUpdate: "Auto Update", | ||||||
|  |         tempDisplayTooltip: "es données qui ne respectent pas les règles de rupture de trame (par exemple : non expirées) s'affichent temporairement en temps réel dans cette zone. Au-delà de 8192 octets, une rupture de trame est automatique.", | ||||||
|  |         loopSend: "Envoi en Boucle", | ||||||
|  |         loopSendTooltip: "La fréquence réelle est influencée par le taux de rafraîchissement de l'interface. Pour plus de précision, vous pouvez essayer de désactiver 'l'actualisation automatique'.", | ||||||
|  |         sendFormat: "Format d'Envoi", | ||||||
|  |         cachedFrame: "Cache", | ||||||
|  |         format: "Format", | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | @ -7,8 +7,8 @@ type NestedKeyOf<ObjectType extends object> = { | ||||||
|         : `${Key}` |         : `${Key}` | ||||||
| }[keyof ObjectType & (string | number)]; | }[keyof ObjectType & (string | number)]; | ||||||
| 
 | 
 | ||||||
| type TranslationKeys = NestedKeyOf<typeof zh>; | export type TranslationKeys = NestedKeyOf<typeof zh>; | ||||||
| 
 | 
 | ||||||
| export function translate<K extends TranslationKeys>(key: K | string): string { | export function translate(key: TranslationKeys | string): string { | ||||||
|     return i18n.global.t(key.toLowerCase()); |     return i18n.global.t(key); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,11 @@ | ||||||
| export default { | export default { | ||||||
|  |     emoji: { | ||||||
|  |         flag: "🇨🇳", | ||||||
|  |     }, | ||||||
|     disconnected: "未连接", |     disconnected: "未连接", | ||||||
|     connected: "已连接", |     connected: "已连接", | ||||||
|     connecting: "连接中", |     connecting: "连接中", | ||||||
|  |     use: "使用", | ||||||
| 
 | 
 | ||||||
|     ws: { |     ws: { | ||||||
|         disconnected: "未连接", |         disconnected: "未连接", | ||||||
|  | @ -13,9 +17,115 @@ export default { | ||||||
|         home: "主页", |         home: "主页", | ||||||
|         wifi: "Wi-Fi", |         wifi: "Wi-Fi", | ||||||
|         about: "关于", |         about: "关于", | ||||||
|         uart: "UART透传", |         uart: "UART", | ||||||
|         feedback: "反馈", |         feedback: "反馈", | ||||||
|         close: "关闭", |         close: "关闭", | ||||||
|         update: "更新", |         update: "更新", | ||||||
|  |         fullscreen: "全屏", | ||||||
|  |         windowed: "窗口", | ||||||
|     }, |     }, | ||||||
|  | 
 | ||||||
|  |     uart: { | ||||||
|  |         port: "接口", | ||||||
|  |         startCommunication: "开始数据收发", | ||||||
|  |         stopCommunication: "停止数据收发", | ||||||
|  |         commonlyUsed: "常用", | ||||||
|  |         baudrate: "波特率", | ||||||
|  |         customBaud: "自定义波特率", | ||||||
|  |         use: "使用", | ||||||
|  |         actual: "实际", | ||||||
|  |         dataBits: "数据位", | ||||||
|  |         stopBits: "停止位", | ||||||
|  |         parity: "校验位", | ||||||
|  |         parityNone: "无(None)", | ||||||
|  |         parityOdd: "奇(Odd)", | ||||||
|  |         parityEven: "偶(Even)", | ||||||
|  |         flowControl: "流控制", | ||||||
|  |         send: "发送", | ||||||
|  |         clear: "清空", | ||||||
|  |         clearTooltip: "仅清除显示区域,可用刷新恢复", | ||||||
|  |         updateTooltip: "与缓存同步+过滤", | ||||||
|  |         autoUpdateTooltip: "仅停止刷新显示区,后台继续接收数据", | ||||||
|  |         receive: "接收", | ||||||
|  | 
 | ||||||
|  |         displayOptions: "显示选项", | ||||||
|  |         display: "显示框", | ||||||
|  |         show: "显示", | ||||||
|  |         text: "文本", | ||||||
|  |         timestamp: "时间戳", | ||||||
|  |         enable: "启用", | ||||||
|  |         lineWrap: "换行", | ||||||
|  |         highlight: "高亮", | ||||||
|  | 
 | ||||||
|  |         frameBreakStrategy: "断帧策略", | ||||||
|  |         priority: "优先级", | ||||||
|  |         rule: "规则", | ||||||
|  |         ruleTips: | ||||||
|  |             "<p>超时=-1: 禁用超时断帧</p>" + | ||||||
|  |             "<p>超时=0: 当机立断,收到任何数据都视为完整数据</p>" + | ||||||
|  |             "<p>匹配断后:典型\\n的场景</p>" + | ||||||
|  |             "<p>匹配断前:用于有特殊帧头的场景</p>" + | ||||||
|  |             "<p>固定字节断帧:传输大量数据,比如可以每隔1024字节断帧,方便查看数据</p>", | ||||||
|  |         value: "值", | ||||||
|  |         timeout: "超时", | ||||||
|  |         match: "匹配", | ||||||
|  |         byte: "字节", | ||||||
|  |         begin: "断", | ||||||
|  |         end: "断", | ||||||
|  | 
 | ||||||
|  |         other: "其他", | ||||||
|  |         decodeAnsiEscapeCodes: "解码ANSI转义码", | ||||||
|  |         ansiTooltips: | ||||||
|  |             "<p>ANSI转义码对终端和文本有很多作用,比如改变文本颜色等。</p>\n" + | ||||||
|  |             "<p>\n" + | ||||||
|  |             "  简单了解->\n" + | ||||||
|  |             "  <a target=\"_blank\" href=\"https://yunsi.studio/wireless-debugger/docs/uart-webhost/ansi-escape-code\">\n" + | ||||||
|  |             "    https://yunsi.studio/wireless-debugger/docs/uart-webhost/ansi-escape-code\n" + | ||||||
|  |             "  </a>\n" + | ||||||
|  |             "</p>", | ||||||
|  |         filter: "过滤", | ||||||
|  |         textAndEscape: "文本,支持\\n\\x", | ||||||
|  |         autoUpdateNewData: "新数据自动刷新", | ||||||
|  |         updateFrequency: "数据显示刷新间隔(ms)", | ||||||
|  |         updateFrequencyTooltip: "提高间隔可减少CPU资源的使用", | ||||||
|  | 
 | ||||||
|  |         addHeader: "增加帧头", | ||||||
|  |         addFooter: "增加帧尾", | ||||||
|  | 
 | ||||||
|  |         passthrough: "透传", | ||||||
|  |         proxy: "透传", | ||||||
|  |         serverPort: "服务器端口", | ||||||
|  |         connectedClient: "已连接的客户端", | ||||||
|  |         refresh: "刷新", | ||||||
|  |         interface: "接口", | ||||||
|  |         noClientConnected: "无客户端连接", | ||||||
|  | 
 | ||||||
|  |         import: "导入", | ||||||
|  |         export: "导出", | ||||||
|  |         reset: "重置", | ||||||
|  |         resetTooltip: "刷新页面后生效", | ||||||
|  |         saveToLocal: "保存到本地", | ||||||
|  |         saveToLocalTooltip: "若存在多个页面,会相互覆盖", | ||||||
|  |         add: "添加", | ||||||
|  |         edit: "编辑", | ||||||
|  |         drag: "拖拽", | ||||||
|  |         ipChangeAlert: "IP地址改变会导致配置丢失", | ||||||
|  | 
 | ||||||
|  |         layout: "布局", | ||||||
|  |         landscape: "横/行", | ||||||
|  |         portrait: "竖/列", | ||||||
|  |         responsive: "自适应", | ||||||
|  |         configPannel: "设置窗", | ||||||
|  |         displayPannel: "数据窗", | ||||||
|  |         macroPannel: "快捷窗", | ||||||
|  |         autoScrollToBottom: "自动滚动到底部", | ||||||
|  |         clearScreen: "清屏", | ||||||
|  |         autoUpdate: "自动刷新", | ||||||
|  |         tempDisplayTooltip: "未满足断帧规则的数据(如:未超时),暂时实时显示在此区域。超过8192字节,自动断帧;", | ||||||
|  |         loopSend: "循环发送", | ||||||
|  |         loopSendTooltip: "实际频率受界面刷新率影响,如需要更精确,可以尝试关闭‘自动刷新’", | ||||||
|  |         sendFormat: "发送格式", | ||||||
|  |         cachedFrame: "缓存帧数", | ||||||
|  |         format: "格式化", | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | @ -364,18 +364,18 @@ export const useDataViewerStore = defineStore('text-viewer', () => { | ||||||
| 
 | 
 | ||||||
|     const frameBreakSize = ref(0); |     const frameBreakSize = ref(0); | ||||||
|     const frameBreakRules = ref([{ |     const frameBreakRules = ref([{ | ||||||
|         name: '超时(ms)', |         name: 'timeout', | ||||||
|         type: 'number', |         type: 'number', | ||||||
|         min: -1, |         min: -1, | ||||||
|         draggable: false, |         draggable: false, | ||||||
|         transformData: breakDelay, |         transformData: breakDelay, | ||||||
|     }, { |     }, { | ||||||
|         name: '匹配', |         name: 'match', | ||||||
|         type: 'text', |         type: 'text', | ||||||
|         draggable: true, |         draggable: true, | ||||||
|         transformData: breakSequence, |         transformData: breakSequence, | ||||||
|     }, { |     }, { | ||||||
|         name: '字节(B)', |         name: 'byte', | ||||||
|         type: 'number', |         type: 'number', | ||||||
|         min: 0, |         min: 0, | ||||||
|         draggable: true, |         draggable: true, | ||||||
|  | @ -437,9 +437,9 @@ export const useDataViewerStore = defineStore('text-viewer', () => { | ||||||
|     function reloadFrameBreak() { |     function reloadFrameBreak() { | ||||||
|         /* function and ref can not be stored in localStorage */ |         /* function and ref can not be stored in localStorage */ | ||||||
|         for (let i = 0; i < frameBreakRules.value.length; i++) { |         for (let i = 0; i < frameBreakRules.value.length; i++) { | ||||||
|             if (frameBreakRules.value[i].name === "超时(ms)") { |             if (frameBreakRules.value[i].name === "timeout") { | ||||||
|                 frameBreakRules.value[i].transformData = breakDelay; |                 frameBreakRules.value[i].transformData = breakDelay; | ||||||
|             } else if (frameBreakRules.value[i].name === "匹配") { |             } else if (frameBreakRules.value[i].name === "match") { | ||||||
|                 frameBreakRules.value[i].transformData = breakSequence; |                 frameBreakRules.value[i].transformData = breakSequence; | ||||||
|             } else { |             } else { | ||||||
|                 frameBreakRules.value[i].transformData = breakSize; |                 frameBreakRules.value[i].transformData = breakSize; | ||||||
|  |  | ||||||
|  | @ -38,15 +38,24 @@ | ||||||
|           <div class="custom-style flex justify-center"> |           <div class="custom-style flex justify-center"> | ||||||
|             <el-segmented v-model="store.winLayoutMode" :options="layoutOptions" size="small"/> |             <el-segmented v-model="store.winLayoutMode" :options="layoutOptions" size="small"/> | ||||||
|           </div> |           </div> | ||||||
|           <el-checkbox label="自适应" v-model="store.winAutoLayout" border size="small" |           <el-checkbox v-model="store.winAutoLayout" border size="small" | ||||||
|                        :disabled="store.winLayoutMode==='col'"/> |                        :disabled="store.winLayoutMode==='col'"> | ||||||
|           <el-checkbox label="设置窗" v-model="store.winLeft.show" border size="small" :disabled="store.winAutoLayout"/> |             {{ $t('uart.responsive') }} | ||||||
|           <el-checkbox label="数据窗" v-model="winDataView.show" border size="small" :disabled="store.winAutoLayout"/> |           </el-checkbox> | ||||||
|           <el-checkbox label="快捷窗" v-model="store.winRight.show" border size="small" :disabled="store.winAutoLayout"/> |           <el-checkbox v-model="store.winLeft.show" border size="small" :disabled="store.winAutoLayout"> | ||||||
|  |             {{ $t("uart.configPannel") }} | ||||||
|  |           </el-checkbox> | ||||||
|  |           <el-checkbox v-model="winDataView.show" border size="small" :disabled="store.winAutoLayout"> | ||||||
|  |             {{ $t('uart.displayPannel') }} | ||||||
|  |           </el-checkbox> | ||||||
|  |           <el-checkbox v-model="store.winRight.show" border size="small" :disabled="store.winAutoLayout"> | ||||||
|  |             {{ $t('uart.macroPannel') }} | ||||||
|  |           </el-checkbox> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <template #reference> |         <template #reference> | ||||||
|           <el-button class="min-h-full" type="primary" :size="layoutConf.isMedium ? 'small' : 'default'">布局 |           <el-button class="min-h-full" type="primary" :size="layoutConf.isMedium ? 'small' : 'default'"> | ||||||
|  |             {{ $t('uart.layout') }} | ||||||
|           </el-button> |           </el-button> | ||||||
|         </template> |         </template> | ||||||
|       </el-popover> |       </el-popover> | ||||||
|  | @ -56,7 +65,7 @@ | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import {onMounted, onUnmounted, reactive, type Ref, ref, type UnwrapRef, watch} from "vue"; | import {computed, onMounted, onUnmounted, reactive, type Ref, ref, type UnwrapRef, watch} from "vue"; | ||||||
| import {breakpointsTailwind, useBreakpoints} from '@vueuse/core' | import {breakpointsTailwind, useBreakpoints} from '@vueuse/core' | ||||||
| import {useDataViewerStore} from '@/stores/dataViewerStore'; | import {useDataViewerStore} from '@/stores/dataViewerStore'; | ||||||
| import * as api from '@/api'; | import * as api from '@/api'; | ||||||
|  | @ -83,6 +92,7 @@ import {isDevMode} from "@/composables/buildMode"; | ||||||
| import {useWsStore} from "@/stores/websocket"; | import {useWsStore} from "@/stores/websocket"; | ||||||
| import {useUartStore} from "@/stores/useUartStore"; | import {useUartStore} from "@/stores/useUartStore"; | ||||||
| import TextDataMacro from "@/views/text-data-viewer/textDataMacro.vue"; | import TextDataMacro from "@/views/text-data-viewer/textDataMacro.vue"; | ||||||
|  | import {translate} from "@/locales"; | ||||||
| 
 | 
 | ||||||
| const store = useDataViewerStore() | const store = useDataViewerStore() | ||||||
| const wsStore = useWsStore() | const wsStore = useWsStore() | ||||||
|  | @ -100,13 +110,13 @@ const layoutConf = reactive({ | ||||||
|   isMedium: breakpoints.smaller("lg"), |   isMedium: breakpoints.smaller("lg"), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const layoutOptions = [{ | const layoutOptions = computed(() => [{ | ||||||
|   label: '横/行', |   label: translate("uart.landscape"), | ||||||
|   value: 'row' |   value: 'row' | ||||||
| }, { | }, { | ||||||
|   label: '竖/列', |   label: translate("uart.portrait"), | ||||||
|   value: 'col' |   value: 'col' | ||||||
| }] | }]); | ||||||
| 
 | 
 | ||||||
| interface WinProperty { | interface WinProperty { | ||||||
|   show: boolean; |   show: boolean; | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <template> | <template> | ||||||
|   <nav class="relative px-2 py-0.5 sm:py-1 flex justify-between items-center border-b h-full"> |   <nav class="relative px-2 py-0.5 sm:py-1 flex justify-between items-center border-b h-full"> | ||||||
|     <div class="flex"> |     <div class="flex"> | ||||||
|       <button @click.prevent="sideMenuOpen=true" class="flex items-center hover:text-blue-600 pl-1 mx-4"> |       <button @click.prevent="sideMenuOpen=true" class="flex items-center hover:text-blue-600 pl-1 mx-2 sm:mx-4"> | ||||||
|         <svg class="block h-3 lg:h-4 lg:w-4 fill-current" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> |         <svg class="block h-3 lg:h-4 lg:w-4 fill-current" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> | ||||||
|           <title>导航侧栏</title> |           <title>导航侧栏</title> | ||||||
|           <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path> |           <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path> | ||||||
|  | @ -20,7 +20,7 @@ | ||||||
| <!--      <router-link to="/" class="flex items-center text-sm text-blue-600 font-bold">主页</router-link>--> | <!--      <router-link to="/" class="flex items-center text-sm text-blue-600 font-bold">主页</router-link>--> | ||||||
| <!--      <a class="flex items-center text-sm text-blue-600 font-bold" href="/">主页6</a>--> | <!--      <a class="flex items-center text-sm text-blue-600 font-bold" href="/">主页6</a>--> | ||||||
| 
 | 
 | ||||||
|       <div class="flex pt-0.5 sm:pt-1 ml-4 text-sm items-center sm:hidden"> |       <div class="flex pt-0.5 sm:pt-1 ml-4 text-xs items-center sm:hidden"> | ||||||
|         <router-link :to="route.fullPath">{{ route.meta.title }}</router-link> |         <router-link :to="route.fullPath">{{ route.meta.title }}</router-link> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|  | @ -36,6 +36,20 @@ | ||||||
|     <!--  <a class="md:ml-auto md:mr-3"></a>--> |     <!--  <a class="md:ml-auto md:mr-3"></a>--> | ||||||
|     <div class="flex h-full"> |     <div class="flex h-full"> | ||||||
|       <div id="page-spec-slot" class="content-center h-full flex flex-row"></div> |       <div id="page-spec-slot" class="content-center h-full flex flex-row"></div> | ||||||
|  |       <div class="mr-2"> | ||||||
|  |           <el-select v-model="language" class="min-w-20 h-full" @change="handleLanguageChange"> | ||||||
|  |             <el-option value="en">🇺🇸 English</el-option> | ||||||
|  |             <el-option value="zh">🇨🇳 简体中文</el-option> | ||||||
|  |             <el-option value="fr">🇫🇷 Français</el-option> | ||||||
|  |             <template #label> | ||||||
|  |               <div class="flex"> | ||||||
|  |                 <InlineSvg name="translate" class="w-4 mr-1"></InlineSvg> | ||||||
|  |                 {{ languageFlag }} | ||||||
|  |               </div> | ||||||
|  |             </template> | ||||||
|  |           </el-select> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|       <div class="lg:hidden"> |       <div class="lg:hidden"> | ||||||
|         <el-button :type="wsColor" size="small" class="transition duration-1000 min-h-full"> |         <el-button :type="wsColor" size="small" class="transition duration-1000 min-h-full"> | ||||||
|           <InlineSvg v-show="wsColor!=='success'" name="link-off" class="mr-2" width="20"></InlineSvg> |           <InlineSvg v-show="wsColor!=='success'" name="link-off" class="mr-2" width="20"></InlineSvg> | ||||||
|  | @ -81,9 +95,9 @@ | ||||||
|         <div> |         <div> | ||||||
|           <el-button @click="toggle"> |           <el-button @click="toggle"> | ||||||
|             <InlineSvg v-if="!isFullscreen" name="open-in-full" width="16px" fill="#000000"></InlineSvg> |             <InlineSvg v-if="!isFullscreen" name="open-in-full" width="16px" fill="#000000"></InlineSvg> | ||||||
|             <p v-if="!isFullscreen">全屏</p> |             <p v-if="!isFullscreen">{{ translate('page.fullscreen') }}</p> | ||||||
|             <InlineSvg v-if="isFullscreen" name="close-fullscreen" width="16px" fill="#000000"></InlineSvg> |             <InlineSvg v-if="isFullscreen" name="close-fullscreen" width="16px" fill="#000000"></InlineSvg> | ||||||
|             <p v-if="isFullscreen">缩小</p> |             <p v-if="isFullscreen">{{ translate('page.windowed') }}</p> | ||||||
|           </el-button> |           </el-button> | ||||||
|         </div> |         </div> | ||||||
|       </template> |       </template> | ||||||
|  | @ -109,7 +123,7 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import InlineSvg from "@/components/InlineSvg.vue"; | import InlineSvg from "@/components/InlineSvg.vue"; | ||||||
| import {computed, type Ref, ref} from "vue"; | import {computed, type ComputedRef, type Ref, ref} from "vue"; | ||||||
| import {useWsStore} from "@/stores/websocket"; | import {useWsStore} from "@/stores/websocket"; | ||||||
| import {translate} from "@/locales"; | import {translate} from "@/locales"; | ||||||
| import {ControlEvent} from "@/api"; | import {ControlEvent} from "@/api"; | ||||||
|  | @ -117,11 +131,21 @@ import {useRoute} from "vue-router"; | ||||||
| import { useFullscreen } from '@vueuse/core' | import { useFullscreen } from '@vueuse/core' | ||||||
| import {useUpdateStore} from "@/stores/useUpdateStore"; | import {useUpdateStore} from "@/stores/useUpdateStore"; | ||||||
| import {isOTAEnabled} from "@/composables/buildMode"; | import {isOTAEnabled} from "@/composables/buildMode"; | ||||||
|  | import {getFlagFromLang, locale, setLang} from "@/i18n" | ||||||
| 
 | 
 | ||||||
| const wsStore = useWsStore(); | const wsStore = useWsStore(); | ||||||
| const updateStore = useUpdateStore(); | const updateStore = useUpdateStore(); | ||||||
| const {isFullscreen, toggle} = useFullscreen(); | const {isFullscreen, toggle} = useFullscreen(); | ||||||
| const route = useRoute(); | const route = useRoute(); | ||||||
|  | const language = ref(locale); | ||||||
|  | 
 | ||||||
|  | const languageFlag = computed(() => { | ||||||
|  |   return getFlagFromLang(language.value); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | function handleLanguageChange(lang: string) { | ||||||
|  |   setLang(lang); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| const sideMenuItemClass = "block p-4 text-sm font-semibold hover:bg-blue-50 hover:text-blue-600 rounded flex" | const sideMenuItemClass = "block p-4 text-sm font-semibold hover:bg-blue-50 hover:text-blue-600 rounded flex" | ||||||
| const sideMenuOpen = ref(false); | const sideMenuOpen = ref(false); | ||||||
|  | @ -144,7 +168,7 @@ const wsColor = computed(() => { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const wsState = computed(() => { | const wsState = computed(() => { | ||||||
|   return translate(wsStore.state); |   return translate(wsStore.state.toLocaleLowerCase()); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| type Item = { | type Item = { | ||||||
|  | @ -154,7 +178,7 @@ type Item = { | ||||||
|   badge?: Ref<boolean>; |   badge?: Ref<boolean>; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const menuItems: Item[] = ([ | const menuItems: ComputedRef<Item[]> = computed(() => ([ | ||||||
|   { |   { | ||||||
|     name: translate("page.uart"), |     name: translate("page.uart"), | ||||||
|     href: "/uart", |     href: "/uart", | ||||||
|  | @ -165,9 +189,10 @@ const menuItems: Item[] = ([ | ||||||
|     name: translate("page.feedback"), |     name: translate("page.feedback"), | ||||||
|     href: "/feedback", |     href: "/feedback", | ||||||
|   }, |   }, | ||||||
| ]); | ])); | ||||||
| 
 | 
 | ||||||
| const sideBarItems: Item[] = ([ | const sideBarItems: ComputedRef<Item[]> = computed(() => { | ||||||
|  |   const items: Item[] = [ | ||||||
|     { |     { | ||||||
|       name: translate("page.uart"), |       name: translate("page.uart"), | ||||||
|       href: "/uart", |       href: "/uart", | ||||||
|  | @ -181,15 +206,18 @@ const sideBarItems: Item[] = ([ | ||||||
|       name: translate("page.feedback"), |       name: translate("page.feedback"), | ||||||
|       href: "/feedback", |       href: "/feedback", | ||||||
|     }, |     }, | ||||||
| ]); |   ]; | ||||||
| 
 |  | ||||||
|   if (isOTAEnabled()) { |   if (isOTAEnabled()) { | ||||||
|   sideBarItems.push({ |     items.push({ | ||||||
|       name: translate("page.update"), |       name: translate("page.update"), | ||||||
|       href: "/update", |       href: "/update", | ||||||
|       badge: computed(() => updateStore.canUpdate), |       badge: computed(() => updateStore.canUpdate), | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  |   return items; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -217,5 +245,9 @@ if (isOTAEnabled()) { | ||||||
|   padding: 0; |   padding: 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .el-select :deep(.el-select__wrapper) { | ||||||
|  |   @apply h-full; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| </style> | </style> | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|     <el-tabs v-model="store.configPanelTab" class="mx-2 custom-tabs fit"> |     <el-tabs v-model="store.configPanelTab" class="mx-2 custom-tabs fit"> | ||||||
|       <el-tab-pane label="接口" name="first" class="min-h-80"> |       <el-tab-pane name="first" class="min-h-80"> | ||||||
| 
 |         <template #label>{{ $t("uart.port") }}</template> | ||||||
|         <div class="flex flex-col gap-2"> |         <div class="flex flex-col gap-2"> | ||||||
|           <el-form :size="store.winLeft.show ? '' : 'small'"> |           <el-form :size="store.winLeft.show ? '' : 'small'"> | ||||||
|             <el-form-item |             <el-form-item | ||||||
|                 label="波特率" |  | ||||||
|                 class="mb-2" |                 class="mb-2" | ||||||
|             > |             > | ||||||
|  |               <template #label>{{ $t("uart.baudrate") }}</template> | ||||||
|               <div class="flex w-full"> |               <div class="flex w-full"> | ||||||
|                 <el-select v-model="store.uartBaud" :teleported="false" @change="onUartBaudChange"> |                 <el-select v-model="store.uartBaud" :teleported="false" @change="onUartBaudChange"> | ||||||
|                   <template #header> |                   <template #header> | ||||||
|  | @ -16,17 +16,17 @@ | ||||||
|                       <div class="flex gap-0"> |                       <div class="flex gap-0"> | ||||||
|                         <el-input-number |                         <el-input-number | ||||||
|                             v-model="uartCustomBaud" |                             v-model="uartCustomBaud" | ||||||
|                             placeholder="自定义波特率" |                             :placeholder="translate('uart.customBaud')" | ||||||
|                             size="small" |                             size="small" | ||||||
|                             :controls="false" |                             :controls="false" | ||||||
|                             :min="110" |                             :min="110" | ||||||
|                             class="flex-grow" |                             class="flex-grow" | ||||||
|                         ></el-input-number> |                         ></el-input-number> | ||||||
|                         <el-button size="small" @click="onUseCustomUartBaud">使用</el-button> |                         <el-button size="small" @click="onUseCustomUartBaud">{{ $t('uart.use') }}</el-button> | ||||||
|                         <!--                      <el-button size="small" @click="onConfirm" class="ml-0">增加</el-button>--> |                         <!--                      <el-button size="small" @click="onConfirm" class="ml-0">增加</el-button>--> | ||||||
|                       </div> |                       </div> | ||||||
| 
 | 
 | ||||||
|                       <el-option-group label="常用"> |                       <el-option-group :label="translate('uart.commonlyUsed')"> | ||||||
|                         <el-option |                         <el-option | ||||||
|                             v-for="item in store.predefinedUartBaudFrequent" |                             v-for="item in store.predefinedUartBaudFrequent" | ||||||
|                             :key="item.baud" |                             :key="item.baud" | ||||||
|  | @ -35,7 +35,7 @@ | ||||||
|                         /> |                         /> | ||||||
|                       </el-option-group> |                       </el-option-group> | ||||||
| 
 | 
 | ||||||
|                       <el-option-group label="其他"> |                       <el-option-group :label="translate('uart.other')"> | ||||||
|                         <el-option |                         <el-option | ||||||
|                             v-for="item in store.uartBaudList" |                             v-for="item in store.uartBaudList" | ||||||
|                             :key="item.baud" |                             :key="item.baud" | ||||||
|  | @ -48,9 +48,9 @@ | ||||||
|                 </el-select> |                 </el-select> | ||||||
|               </div> |               </div> | ||||||
|             </el-form-item> |             </el-form-item> | ||||||
|             <p class="text-xs">实际波特率:{{ store.uartBaudReal }}</p> |             <p class="text-xs">{{ $t('uart.actual') }} {{ $t('uart.baudrate') }}:{{ store.uartBaudReal }}</p> | ||||||
| 
 | 
 | ||||||
|             <el-form-item label="数据位" class="mb-2"> |             <el-form-item :label="translate('uart.dataBits')" class="mb-2"> | ||||||
|               <el-select v-model="store.uartConfig.data_bits" :teleported="false" |               <el-select v-model="store.uartConfig.data_bits" :teleported="false" | ||||||
|                          placeholder="Select" @change="onUartConfigChange"> |                          placeholder="Select" @change="onUartConfigChange"> | ||||||
|                 <el-option |                 <el-option | ||||||
|  | @ -62,7 +62,7 @@ | ||||||
|               </el-select> |               </el-select> | ||||||
|             </el-form-item> |             </el-form-item> | ||||||
| 
 | 
 | ||||||
|             <el-form-item label="校验位" class="mb-2"> |             <el-form-item :label="translate('uart.parity')" class="mb-2"> | ||||||
|               <el-select v-model="store.uartConfig.parity" :teleported="false" |               <el-select v-model="store.uartConfig.parity" :teleported="false" | ||||||
|                          placeholder="Select" @change="onUartConfigChange"> |                          placeholder="Select" @change="onUartConfigChange"> | ||||||
|                 <el-option |                 <el-option | ||||||
|  | @ -74,7 +74,7 @@ | ||||||
|               </el-select> |               </el-select> | ||||||
|             </el-form-item> |             </el-form-item> | ||||||
| 
 | 
 | ||||||
|             <el-form-item label="停止位"> |             <el-form-item :label="translate('uart.stopBits')"> | ||||||
|               <el-select v-model="store.uartConfig.stop_bits" :teleported="false" |               <el-select v-model="store.uartConfig.stop_bits" :teleported="false" | ||||||
|                          placeholder="Select" @change="onUartConfigChange"> |                          placeholder="Select" @change="onUartConfigChange"> | ||||||
|                 <el-option |                 <el-option | ||||||
|  | @ -92,7 +92,7 @@ | ||||||
|                            :disabled="wsStore.state !== ControlEvent.CONNECTED" |                            :disabled="wsStore.state !== ControlEvent.CONNECTED" | ||||||
|                            @click="store.acceptIncomingData = !store.acceptIncomingData" |                            @click="store.acceptIncomingData = !store.acceptIncomingData" | ||||||
|                 > |                 > | ||||||
|                   {{ store.acceptIncomingData ? "停止数据收发" : "开始数据收发" }} |                   {{ store.acceptIncomingData ? $t("uart.stopCommunication") : $t("uart.startCommunication") }} | ||||||
|                 </el-button> |                 </el-button> | ||||||
|               </div> |               </div> | ||||||
|             </el-form-item> |             </el-form-item> | ||||||
|  | @ -102,39 +102,40 @@ | ||||||
| 
 | 
 | ||||||
|       </el-tab-pane> |       </el-tab-pane> | ||||||
|       <!--  /////////////////////////////////////////////////////////////////    --> |       <!--  /////////////////////////////////////////////////////////////////    --> | ||||||
|       <el-tab-pane label="显示框" name="second"> |       <el-tab-pane name="second"> | ||||||
|  |         <template #label>{{ $t("uart.displayPannel") }}</template> | ||||||
|         <div class="flex flex-col"> |         <div class="flex flex-col"> | ||||||
|           <el-collapse v-model="collapseActiveName"> |           <el-collapse v-model="collapseActiveName"> | ||||||
|             <el-collapse-item name="1"> |             <el-collapse-item name="1"> | ||||||
|               <template #title> |               <template #title> | ||||||
|                 显示选项 |                 {{ $t('uart.displayOptions') }} | ||||||
|               </template> |               </template> | ||||||
|               <template #default> |               <template #default> | ||||||
|                 <div class="flex flex-col gap-2"> |                 <div class="flex flex-col gap-2"> | ||||||
|                   <div class="flex flex-col"> |                   <div class="flex flex-col"> | ||||||
|                     <el-checkbox border v-model="store.showText" label="显示文本"/> |                     <el-checkbox border v-model="store.showText" :label="translate('uart.text')"/> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div class="flex flex-col"> |                   <div class="flex flex-col"> | ||||||
|                     <el-checkbox border v-model="store.showHex" label="显示HEX"/> |                     <el-checkbox border v-model="store.showHex" label="HEX"/> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div class="flex flex-col"> |                   <div class="flex flex-col"> | ||||||
|                     <el-checkbox border v-model="store.showHexdump" label="显示HEXDUMP"/> |                     <el-checkbox border v-model="store.showHexdump" label="HEXDUMP"/> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div class="flex flex-col"> |                   <div class="flex flex-col"> | ||||||
|                     <el-checkbox border v-model="store.showTimestamp">显示时间戳</el-checkbox> |                     <el-checkbox border v-model="store.showTimestamp" :label="translate('uart.timestamp')"/> | ||||||
|                   </div> |                   </div> | ||||||
|                   <div class="flex flex-col"> |                   <div class="flex flex-col"> | ||||||
|                     <el-checkbox border v-model="store.enableLineWrap" label="启用换行"/> |                     <el-checkbox border v-model="store.enableLineWrap" :label="translate('uart.lineWrap')"/> | ||||||
|                   </div> |                   </div> | ||||||
| 
 | 
 | ||||||
|                   <el-tag type="success"> |                   <el-tag type="success"> | ||||||
|                     <el-text type="success">RX HEXDUMP高亮选色</el-text> |                     <el-text type="success">RX HEXDUMP {{ $t("uart.highlight") }}</el-text> | ||||||
|                     <el-color-picker v-model="store.RxHexdumpColor" show-alpha :predefine="store.predefineColors" |                     <el-color-picker v-model="store.RxHexdumpColor" show-alpha :predefine="store.predefineColors" | ||||||
|                                      size="small"/> |                                      size="small"/> | ||||||
|                   </el-tag> |                   </el-tag> | ||||||
| 
 | 
 | ||||||
|                   <el-tag type="primary"> |                   <el-tag type="primary"> | ||||||
|                     <el-text type="primary">TX HEXDUMP高亮选色</el-text> |                     <el-text type="primary">TX HEXDUMP {{ $t("uart.highlight") }}</el-text> | ||||||
|                     <el-color-picker v-model="store.TxHexdumpColor" show-alpha :predefine="store.predefineColors" |                     <el-color-picker v-model="store.TxHexdumpColor" show-alpha :predefine="store.predefineColors" | ||||||
|                                      size="small"/> |                                      size="small"/> | ||||||
|                   </el-tag> |                   </el-tag> | ||||||
|  | @ -142,33 +143,26 @@ | ||||||
|               </template> |               </template> | ||||||
|             </el-collapse-item> |             </el-collapse-item> | ||||||
| 
 | 
 | ||||||
|             <el-collapse-item name="2"> |             <el-collapse-item name="2" :title="translate('uart.frameBreakStrategy')"> | ||||||
|               <template #title> |  | ||||||
|                 断帧策略 |  | ||||||
|               </template> |  | ||||||
|               <VueDraggable v-model="store.frameBreakRules" target="tbody" handle=".sort-target" |               <VueDraggable v-model="store.frameBreakRules" target="tbody" handle=".sort-target" | ||||||
|                             :animation="150" |                             :animation="150" | ||||||
|                             :on-move="checkMove"> |                             :on-move="checkMove"> | ||||||
|                 <table class="w-full bg-white"> |                 <table class="w-full bg-white"> | ||||||
|                   <thead> |                   <thead> | ||||||
|                   <tr class="text-sm h-7"> |                   <tr class="text-sm h-7"> | ||||||
|                     <th>优先级</th> |                     <th>{{ $t('uart.priority') }}</th> | ||||||
|                     <th> |                     <th> | ||||||
|                       <div class="flex justify-center"> |                       <div class="flex justify-center"> | ||||||
|                         规则 |                         {{ translate('uart.rule' as TranslationKeys) }} | ||||||
|                         <el-tooltip placement="top" effect="light"> |                         <el-tooltip placement="top" effect="light"> | ||||||
|                           <template #content> |                           <template #content> | ||||||
|                             <p>超时=-1: 禁用超时断帧</p> |                             <div v-html="translate('uart.ruleTips')"></div> | ||||||
|                             <p>超时=0: 当机立断,收到任何数据都视为完整数据</p> |  | ||||||
|                             <p>匹配断后:典型\n的场景</p> |  | ||||||
|                             <p>匹配断前:用于有特殊帧头的场景</p> |  | ||||||
|                             <p>固定字节断帧:传输大量数据,比如可以每隔1024字节断帧,方便查看数据</p> |  | ||||||
|                           </template> |                           </template> | ||||||
|                           <InlineSvg name="help" class="w-4 text-gray-500 cursor-help"></InlineSvg> |                           <InlineSvg name="help" class="w-4 text-gray-500 cursor-help"></InlineSvg> | ||||||
|                         </el-tooltip> |                         </el-tooltip> | ||||||
|                       </div> |                       </div> | ||||||
|                     </th> |                     </th> | ||||||
|                     <th>值</th> |                     <th>{{ translate('uart.value' as TranslationKeys) }}</th> | ||||||
|                   </tr> |                   </tr> | ||||||
|                   </thead> |                   </thead> | ||||||
|                   <tbody class="text-xs text-center"> |                   <tbody class="text-xs text-center"> | ||||||
|  | @ -176,20 +170,22 @@ | ||||||
|                     <td :class="item.draggable ? 'sort-target' : ''"> |                     <td :class="item.draggable ? 'sort-target' : ''"> | ||||||
|                       {{ item.draggable ? index : 'NaN' }} |                       {{ item.draggable ? index : 'NaN' }} | ||||||
|                     </td> |                     </td> | ||||||
|                     <td :class="item.draggable ? 'sort-target' : ''">{{ item.name }}</td> |                     <td :class="item.draggable ? 'sort-target' : ''"> | ||||||
|  |                       {{ translate("uart." + item.name) }} | ||||||
|  |                     </td> | ||||||
|                     <td> |                     <td> | ||||||
|                       <div v-if="item.type === 'number'"> |                       <div v-if="item.type === 'number'"> | ||||||
|                         <el-input-number v-if="item.name === '超时(ms)'" v-model="store.frameBreakDelay" :min="item.min || 0" size="small" style="width: 100px"/> |                         <el-input-number v-if="item.name === 'timeout'" v-model="store.frameBreakDelay" :min="item.min || 0" size="small" style="width: 100px"/> | ||||||
|                         <el-input-number v-else v-model="store.frameBreakSize" :min="item.min || 0" size="small" style="width: 100px"/> |                         <el-input-number v-else v-model="store.frameBreakSize" :min="item.min || 0" size="small" style="width: 100px"/> | ||||||
|                       </div> |                       </div> | ||||||
|                       <div v-else> |                       <div v-else> | ||||||
|                         <el-input class="break-input" v-model="store.frameBreakSequence" placeholder="文本;支持\n\x" size="small" |                         <el-input class="break-input" v-model="store.frameBreakSequence" :placeholder="translate('uart.textAndEscape')" size="small" | ||||||
|                                   style="width: 100px"> |                                   style="width: 100px"> | ||||||
|                           <template #prepend> |                           <template #prepend> | ||||||
|                             <el-button size="small" @click="store.frameBreakAfterSequence = false"> |                             <el-button size="small" @click="store.frameBreakAfterSequence = false"> | ||||||
|                               <span |                               <span | ||||||
|                                   :class="store.frameBreakAfterSequence ? 'text-gray-400' : 'text-blue-400 font-bold'"> |                                   :class="store.frameBreakAfterSequence ? 'text-gray-400' : 'text-blue-400 font-bold'"> | ||||||
|                               断 |                               {{ translate("uart.begin") }} | ||||||
|                               </span> |                               </span> | ||||||
|                             </el-button> |                             </el-button> | ||||||
|                           </template> |                           </template> | ||||||
|  | @ -197,7 +193,7 @@ | ||||||
|                             <el-button size="small" @click="store.frameBreakAfterSequence = true"> |                             <el-button size="small" @click="store.frameBreakAfterSequence = true"> | ||||||
|                               <span |                               <span | ||||||
|                                   :class="store.frameBreakAfterSequence ? 'text-blue-400 font-bold' : 'text-gray-300'"> |                                   :class="store.frameBreakAfterSequence ? 'text-blue-400 font-bold' : 'text-gray-300'"> | ||||||
|                               断 |                               {{ translate("uart.end") }} | ||||||
|                               </span> |                               </span> | ||||||
|                             </el-button> |                             </el-button> | ||||||
|                           </template> |                           </template> | ||||||
|  | @ -210,10 +206,7 @@ | ||||||
|               </VueDraggable> |               </VueDraggable> | ||||||
|             </el-collapse-item> |             </el-collapse-item> | ||||||
| 
 | 
 | ||||||
|             <el-collapse-item name="3"> |             <el-collapse-item name="3" :title="translate('uart.other')"> | ||||||
|               <template #title> |  | ||||||
|                 其他 |  | ||||||
|               </template> |  | ||||||
|               <template #default> |               <template #default> | ||||||
|                 <div class="flex flex-col gap-2"> |                 <div class="flex flex-col gap-2"> | ||||||
|                   <el-tooltip |                   <el-tooltip | ||||||
|  | @ -222,30 +215,24 @@ | ||||||
|                       placement="right-start" |                       placement="right-start" | ||||||
|                   > |                   > | ||||||
|                     <template #content> |                     <template #content> | ||||||
|                       <p>ANSI转义码对终端和文本有很多作用,比如改变文本颜色等。</p> |                       <div v-html="translate('uart.ansiTooltips')"></div> | ||||||
|                       <p> |  | ||||||
|                         简单了解-> |  | ||||||
|                         <el-link target="_blank" href="https://zhuanlan.zhihu.com/p/390666800"> |  | ||||||
|                           https://zhuanlan.zhihu.com/p/390666800 |  | ||||||
|                         </el-link> |  | ||||||
|                       </p> |  | ||||||
|                     </template> |                     </template> | ||||||
|                     <el-checkbox border v-model="store.enableAnsiDecode">解析ANSI转义码</el-checkbox> |                     <el-checkbox border v-model="store.enableAnsiDecode">{{ translate('uart.decodeAnsiEscapeCodes') }}</el-checkbox> | ||||||
|                   </el-tooltip> |                   </el-tooltip> | ||||||
|                   <el-input v-model="store.filterValue" placeholder="文本;支持\n\x" clearable> |                   <el-input v-model="store.filterValue" :placeholder="translate('uart.textAndEscape')" clearable> | ||||||
|                     <template #prepend> |                     <template #prepend> | ||||||
|                       过滤 |                       {{ translate("uart.filter") }} | ||||||
|                     </template> |                     </template> | ||||||
|                   </el-input> |                   </el-input> | ||||||
| 
 | 
 | ||||||
|                   <div class="border rounded flex flex-col"> |                   <div class="border rounded flex flex-col"> | ||||||
| 
 | 
 | ||||||
|                     <el-checkbox border v-model="store.dataFilterAutoUpdate">新数据自动刷新</el-checkbox> |                     <el-checkbox border v-model="store.dataFilterAutoUpdate">{{ translate('uart.autoUpdateNewData') }}</el-checkbox> | ||||||
| 
 | 
 | ||||||
|                     <el-tooltip content="提高间隔可减少CPU资源的使用" placement="right" effect="light" |                     <el-tooltip :content="translate('uart.updateFrequencyTooltip')" placement="right" effect="light" | ||||||
|                                 :show-after="500"> |                                 :show-after="500"> | ||||||
|                       <div class="flex gap-4 p-2"> |                       <div class="flex gap-4 p-2"> | ||||||
|                         <el-text>数据显示刷新间隔(ms)</el-text> |                         <el-text>{{ translate('uart.updateFrequency') }}</el-text> | ||||||
|                         <el-input-number |                         <el-input-number | ||||||
|                             :step="10" |                             :step="10" | ||||||
|                             :min="10" |                             :min="10" | ||||||
|  | @ -300,34 +287,36 @@ | ||||||
|       </el-tab-pane> |       </el-tab-pane> | ||||||
| 
 | 
 | ||||||
|       <!--    /////////////////////////////////////////////////////////////  --> |       <!--    /////////////////////////////////////////////////////////////  --> | ||||||
|       <el-tab-pane label="发送" name="third"> |       <el-tab-pane :label="translate('uart.send')" name="third"> | ||||||
|  |         <template #label>{{ $t("uart.send") }}</template> | ||||||
|         <div class="flex flex-col gap-2"> |         <div class="flex flex-col gap-2"> | ||||||
|           <el-input v-model="store.textPrefixValue" placeholder="支持\n\x" clearable> |           <el-input v-model="store.textPrefixValue" :placeholder="translate('uart.textAndEscape')" clearable> | ||||||
|             <template #prepend> |             <template #prepend> | ||||||
|               {{ `添加帧头►` }} |               {{ translate('uart.addHeader') }}► | ||||||
|             </template> |             </template> | ||||||
|           </el-input> |           </el-input> | ||||||
|           <el-input v-model="store.textSuffixValue" placeholder="支持\n\x" clearable> |           <el-input v-model="store.textSuffixValue" :placeholder="translate('uart.textAndEscape')" clearable> | ||||||
|             <template #append> |             <template #append> | ||||||
|               ◄添加帧尾 |               ◄{{ translate('uart.addFooter') }} | ||||||
|             </template> |             </template> | ||||||
|           </el-input> |           </el-input> | ||||||
|         </div> |         </div> | ||||||
|       </el-tab-pane> |       </el-tab-pane> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|       <el-tab-pane label="透传" name="fourth" class="min-h-80"> |       <el-tab-pane :label="translate('uart.proxy')" name="fourth" class="min-h-80"> | ||||||
|  |         <template #label>{{ $t("uart.passthrough") }}</template> | ||||||
|         <div class="flex flex-col gap-2"> |         <div class="flex flex-col gap-2"> | ||||||
|           <div class="border rounded bg-white p-2"> |           <div class="border rounded bg-white p-2"> | ||||||
|             <span class="border-r px-2">TCP服务器端口</span> |             <span class="border-r px-2">TCP {{ translate('uart.serverPort') }}</span> | ||||||
|             <span class="px-2 cursor-not-allowed">1346</span> |             <span class="px-2 cursor-not-allowed">1346</span> | ||||||
|           </div> |           </div> | ||||||
|           <div> |           <div> | ||||||
|             <p><el-button @click="refreshTCPClientList" size="small" type="primary" :plain="true">刷新</el-button> 已连接的客户端:</p> |             <p><el-button @click="refreshTCPClientList" size="small" type="primary" :plain="true">{{ translate('uart.refresh') }}</el-button> {{ translate('uart.connectedClient') }}</p> | ||||||
| 
 | 
 | ||||||
|             <el-table :data="dfStore.instanceList.filter((item) => (item.port_info as ISocketInfo).local_port === 1346)" empty-text="无客户端连接"> |             <el-table :data="dfStore.instanceList.filter((item) => (item.port_info as ISocketInfo).local_port === 1346)" :empty-text="translate('uart.noClientConnected')"> | ||||||
|               <el-table-column label="IP" prop="port_info.foreign_ip" /> |               <el-table-column label="IP" prop="port_info.foreign_ip" /> | ||||||
|               <el-table-column label="端口" prop="port_info.foreign_port"/> |               <el-table-column :label="translate('uart.port')" prop="port_info.foreign_port"/> | ||||||
|             </el-table> |             </el-table> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | @ -338,7 +327,7 @@ | ||||||
| 
 | 
 | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import {VueDraggable} from 'vue-draggable-plus' | import {VueDraggable} from 'vue-draggable-plus' | ||||||
| import {ref} from "vue"; | import {computed, ref} from "vue"; | ||||||
| import {useDataViewerStore} from "@/stores/dataViewerStore"; | import {useDataViewerStore} from "@/stores/dataViewerStore"; | ||||||
| import {useWsStore} from "@/stores/websocket"; | import {useWsStore} from "@/stores/websocket"; | ||||||
| import {globalNotify} from "@/composables/notification"; | import {globalNotify} from "@/composables/notification"; | ||||||
|  | @ -349,6 +338,7 @@ import {useDataFlowStore} from "@/stores/useDataFlowStore"; | ||||||
| import {wt_data_flow_get_instance_list, type ISocketInfo} from "@/api/apiDataFlow"; | import {wt_data_flow_get_instance_list, type ISocketInfo} from "@/api/apiDataFlow"; | ||||||
| import {uart_set_baud, uart_set_config} from "@/api/apiUart"; | import {uart_set_baud, uart_set_config} from "@/api/apiUart"; | ||||||
| import {useUartStore} from "@/stores/useUartStore"; | import {useUartStore} from "@/stores/useUartStore"; | ||||||
|  | import {translate, type TranslationKeys} from "@/locales"; | ||||||
| 
 | 
 | ||||||
| const store = useDataViewerStore() | const store = useDataViewerStore() | ||||||
| const uartStore = useUartStore() | const uartStore = useUartStore() | ||||||
|  | @ -375,18 +365,18 @@ const uartDataBitsOptions = [ | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| const uartParityOptions = [ | const uartParityOptions = computed(() => [ | ||||||
|   { |   { | ||||||
|     key: 0, |     key: 0, | ||||||
|     label: "无(none)", |     label: translate("uart.parityNone"), | ||||||
|   }, { |   }, { | ||||||
|     key: 1, |     key: 1, | ||||||
|     label: "奇(odd)", |     label: translate("uart.parityOdd"), | ||||||
|   }, { |   }, { | ||||||
|     key: 2, |     key: 2, | ||||||
|     label: "偶(even)", |     label: translate("uart.parityEven"), | ||||||
|   } |   } | ||||||
| ] | ]); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const uartStopBitsOptions = [ | const uartStopBitsOptions = [ | ||||||
|  |  | ||||||
|  | @ -1,25 +1,27 @@ | ||||||
| <template> | <template> | ||||||
|   <div class="flex items-center mb-2 flex-wrap gap-2"> |   <div class="flex items-center mb-2 flex-wrap gap-2"> | ||||||
|     <el-button type="primary" @click="importSettings">导入</el-button> |     <el-button type="primary" @click="importSettings">{{ translate('uart.import') }}</el-button> | ||||||
|     <el-button type="warning" @click="exportSettings">导出</el-button> |     <el-button type="warning" @click="exportSettings">{{ translate('uart.export') }}</el-button> | ||||||
| 
 | 
 | ||||||
|     <el-tooltip |     <el-tooltip | ||||||
|         effect="light" |         effect="light" | ||||||
|         placement="top" |         placement="top" | ||||||
|  |         :show-after="500" | ||||||
|     > |     > | ||||||
|       <template #content> |       <template #content> | ||||||
|         <p>刷新页面后生效</p> |         <p>{{ translate('uart.resetTooltip') }}</p> | ||||||
|       </template> |       </template> | ||||||
|       <el-button type="info" @click="resetSettings">重置</el-button> |       <el-button type="info" @click="resetSettings">{{ translate('uart.reset') }}</el-button> | ||||||
|     </el-tooltip> |     </el-tooltip> | ||||||
|     <el-tooltip |     <el-tooltip | ||||||
|         effect="light" |         effect="light" | ||||||
|         placement="top" |         placement="top" | ||||||
|  |         :show-after="500" | ||||||
|     > |     > | ||||||
|       <template #content> |       <template #content> | ||||||
|         <p>若存在多个页面,会相互覆盖</p> |         <p>{{ translate('uart.saveToLocalTooltip') }}</p> | ||||||
|       </template> |       </template> | ||||||
|       <el-checkbox border v-model="store.autoSaveSettings">保存至本地</el-checkbox> |       <el-checkbox border v-model="store.autoSaveSettings">{{ translate('uart.saveToLocal') }}</el-checkbox> | ||||||
|     </el-tooltip> |     </el-tooltip> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|  | @ -27,17 +29,17 @@ | ||||||
|     <el-button type="primary" @click="() => { |     <el-button type="primary" @click="() => { | ||||||
|     store.macroData.push({ |     store.macroData.push({ | ||||||
|       value: '', |       value: '', | ||||||
|       label: '发送', |       label: translate('uart.send'), | ||||||
|       id: store.macroId, |       id: store.macroId, | ||||||
|     }) |     }) | ||||||
|     store.macroId++; |     store.macroId++; | ||||||
|   }">添加 |   }">{{ translate('uart.add') }} | ||||||
|     </el-button> |     </el-button> | ||||||
|     <el-checkbox v-model="editMode" border>编辑</el-checkbox> |     <el-checkbox v-model="editMode" border>{{ translate('uart.edit') }}</el-checkbox> | ||||||
|     <el-checkbox v-model="draggableEnabled" border>拖拽</el-checkbox> |     <el-checkbox v-model="draggableEnabled" border>{{ translate('uart.drag') }}</el-checkbox> | ||||||
|   </div> |   </div> | ||||||
|   <div> |   <div> | ||||||
|     <el-alert v-if="store.ipChangeAlert" @close="store.ipChangeAlert=false">IP地址改变会导致配置丢失</el-alert> |     <el-alert v-if="store.ipChangeAlert" @close="store.ipChangeAlert=false">{{ translate('uart.ipChangeAlert') }}</el-alert> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <VueDraggable v-model="store.macroData" handle=".sort-target" |   <VueDraggable v-model="store.macroData" handle=".sort-target" | ||||||
|  | @ -67,6 +69,7 @@ import {VueDraggable} from "vue-draggable-plus"; | ||||||
| import {onMounted, ref} from "vue"; | import {onMounted, ref} from "vue"; | ||||||
| import {globalNotify, globalNotifyRightSide} from "@/composables/notification"; | import {globalNotify, globalNotifyRightSide} from "@/composables/notification"; | ||||||
| import {useDataViewerStore} from "@/stores/dataViewerStore"; | import {useDataViewerStore} from "@/stores/dataViewerStore"; | ||||||
|  | import {translate} from "../../locales"; | ||||||
| 
 | 
 | ||||||
| const editMode = ref(false); | const editMode = ref(false); | ||||||
| const draggableEnabled = ref(true); | const draggableEnabled = ref(true); | ||||||
|  |  | ||||||
|  | @ -18,18 +18,18 @@ | ||||||
|     </el-popover> |     </el-popover> | ||||||
| 
 | 
 | ||||||
|     <div class="flex"> |     <div class="flex"> | ||||||
|       <el-checkbox size="small" v-model="store.forceToBottom" label="自动滚动至底部" border/> |       <el-checkbox size="small" v-model="store.forceToBottom" :label="translate('uart.autoScrollToBottom')" border/> | ||||||
|       <el-tooltip |       <el-tooltip | ||||||
|           class="box-item" |           class="box-item" | ||||||
|           effect="light" |           effect="light" | ||||||
|           placement="top" |           placement="top" | ||||||
|       > |       > | ||||||
|         <template #content> |         <template #content> | ||||||
|           <p>仅清除显示区域,可用刷新恢复</p> |           <p>{{ translate('uart.clearTooltip') }}</p> | ||||||
|         </template> |         </template> | ||||||
|         <el-button size="small" @click="store.clearFilteredBuff"> |         <el-button size="small" @click="store.clearFilteredBuff"> | ||||||
|           <InlineSvg class="h-5" name="trash"></InlineSvg> |           <InlineSvg class="h-5" name="trash"></InlineSvg> | ||||||
|           清屏 ⇩ |           {{ $t('uart.clearScreen') }} ⇩ | ||||||
|         </el-button> |         </el-button> | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
| 
 | 
 | ||||||
|  | @ -39,10 +39,10 @@ | ||||||
|           placement="top" |           placement="top" | ||||||
|       > |       > | ||||||
|         <template #content> |         <template #content> | ||||||
|           <p>与缓存同步+过滤</p> |           <p>{{ translate('uart.clearTooltip') }}</p> | ||||||
|         </template> |         </template> | ||||||
|         <el-button size="small" @click="store.refreshFilteredBuff"> |         <el-button size="small" @click="store.refreshFilteredBuff"> | ||||||
|           刷新 |           {{ $t('page.update') }} | ||||||
|         </el-button> |         </el-button> | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
|       <el-tooltip |       <el-tooltip | ||||||
|  | @ -51,10 +51,10 @@ | ||||||
|           placement="top" |           placement="top" | ||||||
|       > |       > | ||||||
|         <template #content> |         <template #content> | ||||||
|           <p>仅停止刷新显示区,后台继续接收数据</p> |           <p>{{ translate('uart.autoUpdateTooltip') }}</p> | ||||||
|         </template> |         </template> | ||||||
|         <el-checkbox size="small" border v-model="store.dataFilterAutoUpdate"> |         <el-checkbox size="small" border v-model="store.dataFilterAutoUpdate"> | ||||||
|           自动刷新 |           {{ $t('uart.autoUpdate') }} | ||||||
|         </el-checkbox> |         </el-checkbox> | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
|     </div> |     </div> | ||||||
|  | @ -77,7 +77,7 @@ | ||||||
|             <p class="text-nowrap text-sm text-sky-500" v-else-if="item.type === 0" type="primary" v-show="store.showTimestamp"> |             <p class="text-nowrap text-sm text-sky-500" v-else-if="item.type === 0" type="primary" v-show="store.showTimestamp"> | ||||||
|               <span>{{ item.time }}</span>TX-►|</p> |               <span>{{ item.time }}</span>TX-►|</p> | ||||||
|             <p class="text-nowrap text-sm text-amber-800" v-else type="primary" v-show="store.showTimestamp"> |             <p class="text-nowrap text-sm text-amber-800" v-else type="primary" v-show="store.showTimestamp"> | ||||||
|               <span>{{ item.time }}</span>未发送►|</p> |               <span>{{ item.time }}</span>NS-►|</p> | ||||||
| 
 | 
 | ||||||
|             <p v-show="store.showText" |             <p v-show="store.showText" | ||||||
|                v-html="item.str"></p> |                v-html="item.str"></p> | ||||||
|  | @ -111,7 +111,7 @@ | ||||||
|             <p class="text-nowrap text-sm text-sky-500" v-else-if="item.type === 0" type="primary" v-show="store.showTimestamp"> |             <p class="text-nowrap text-sm text-sky-500" v-else-if="item.type === 0" type="primary" v-show="store.showTimestamp"> | ||||||
|               <span>{{ item.time }}</span>TX-►|</p> |               <span>{{ item.time }}</span>TX-►|</p> | ||||||
|             <p class="text-nowrap text-sm text-amber-800" v-else type="primary" v-show="store.showTimestamp"> |             <p class="text-nowrap text-sm text-amber-800" v-else type="primary" v-show="store.showTimestamp"> | ||||||
|               <span>{{ item.time }}</span>未发送►|</p> |                 <span>{{ item.time }}</span>NS-►|</p> | ||||||
|             <p v-show="store.showText" |             <p v-show="store.showText" | ||||||
|                v-html="item.str"></p> |                v-html="item.str"></p> | ||||||
|           </div> |           </div> | ||||||
|  | @ -132,7 +132,7 @@ | ||||||
| 
 | 
 | ||||||
|   <div class="shrink-0 flex h-8 mt-0.5 text-xs"> |   <div class="shrink-0 flex h-8 mt-0.5 text-xs"> | ||||||
|     <div class="flex shrink-0"> |     <div class="flex shrink-0"> | ||||||
|       <el-tooltip content="未满足断帧规则的数据(如:未超时),暂时实时显示在此区域。超过8192字节,自动断帧;" effect="light"> |       <el-tooltip :content="translate('uart.tempDisplayTooltip')" effect="light"> | ||||||
|         <InlineSvg name="help" class="w-3.5 h-3.5 text-gray-500 cursor-help"></InlineSvg> |         <InlineSvg name="help" class="w-3.5 h-3.5 text-gray-500 cursor-help"></InlineSvg> | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
|       <p>►</p> |       <p>►</p> | ||||||
|  | @ -153,10 +153,10 @@ | ||||||
|         </el-tag> |         </el-tag> | ||||||
|       </el-link> |       </el-link> | ||||||
| 
 | 
 | ||||||
|       <el-tooltip content="实际频率受界面刷新率影响,如需要更精确,可以尝试关闭‘自动刷新’" placement="right" effect="light" :show-after="1000"> |       <el-tooltip :content="translate('uart.loopSendTooltip')" placement="right" effect="light" :show-after="1000"> | ||||||
|         <div class="flex align-center"> |         <div class="flex align-center"> | ||||||
|           <el-checkbox v-model="store.enableLoopSend" class="font-mono font-bold max-h-5" size="small" border> |           <el-checkbox v-model="store.enableLoopSend" class="font-mono font-bold max-h-5" size="small" border> | ||||||
|             循环发送(ms) |             {{ translate('uart.loopSend') }}(ms) | ||||||
|           </el-checkbox> |           </el-checkbox> | ||||||
|           <el-input-number |           <el-input-number | ||||||
|               v-model="store.loopSendFreq" |               v-model="store.loopSendFreq" | ||||||
|  | @ -170,7 +170,7 @@ | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
| 
 | 
 | ||||||
|       <el-link @click="store.isSendTextFormat = !store.isSendTextFormat"> |       <el-link @click="store.isSendTextFormat = !store.isSendTextFormat"> | ||||||
|         <el-tag class="font-mono font-bold" size="small">发送格式:{{ store.isSendTextFormat ? "文本" : "HEX" }}</el-tag> |         <el-tag class="font-mono font-bold" size="small">{{ translate('uart.sendFormat') }}:{{ store.isSendTextFormat ? translate("uart.text") : "HEX" }}</el-tag> | ||||||
|       </el-link> |       </el-link> | ||||||
|     </div> |     </div> | ||||||
|     <div class="flex gap-2"> |     <div class="flex gap-2"> | ||||||
|  | @ -189,7 +189,7 @@ | ||||||
|           <el-link class="flex" @click="store.clearDataBuff" type="warning"> |           <el-link class="flex" @click="store.clearDataBuff" type="warning"> | ||||||
|             <InlineSvg class="h-5" name="trash"></InlineSvg> |             <InlineSvg class="h-5" name="trash"></InlineSvg> | ||||||
|           </el-link> |           </el-link> | ||||||
|           <span class="align-text-bottom">缓存帧数: {{ store.dataBufLength }}/30000</span> |           <span class="align-text-bottom">{{ translate('uart.cachedFrame') }}: {{ store.dataBufLength }}/30000</span> | ||||||
|         </el-tag> |         </el-tag> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|  | @ -197,14 +197,14 @@ | ||||||
|   <div class="flex flex-row font-mono"> |   <div class="flex flex-row font-mono"> | ||||||
|     <el-input type="textarea" :autosize="{ minRows: 1, maxRows: 6}" v-model="store.uartInputTextBox" clearable |     <el-input type="textarea" :autosize="{ minRows: 1, maxRows: 6}" v-model="store.uartInputTextBox" clearable | ||||||
|               :placeholder="store.isSendTextFormat ? |               :placeholder="store.isSendTextFormat ? | ||||||
|               '输入文本,支持\\n\\x转义' : |               translate('uart.textAndEscape') : | ||||||
|               '输入HEX格式'" |               'HEX'" | ||||||
|               @keydown="handleTextboxKeydown" |               @keydown="handleTextboxKeydown" | ||||||
|     ></el-input> |     ></el-input> | ||||||
|     <el-tooltip content="Ctrl+回车" placement="top" :auto-close="500"> |     <el-tooltip content="Ctrl+Enter" placement="top" :auto-close="500"> | ||||||
|       <el-button type="primary" |       <el-button type="primary" | ||||||
|                  @click="onSendClick"> |                  @click="onSendClick"> | ||||||
|         {{ (store.isSendTextFormat || store.isHexStringValid) ? "发送" : "格式化" }} |         {{ (store.isSendTextFormat || store.isHexStringValid) ? translate("uart.send") : translate("格式化") }} | ||||||
|       </el-button> |       </el-button> | ||||||
|     </el-tooltip> |     </el-tooltip> | ||||||
|   </div> |   </div> | ||||||
|  | @ -217,6 +217,7 @@ import InlineSvg from "@/components/InlineSvg.vue"; | ||||||
| import TextDataConfig from "@/views/text-data-viewer/textDataConfig.vue"; | import TextDataConfig from "@/views/text-data-viewer/textDataConfig.vue"; | ||||||
| import {debouncedWatch} from "@vueuse/core"; | import {debouncedWatch} from "@vueuse/core"; | ||||||
| import {globalNotify} from "@/composables/notification"; | import {globalNotify} from "@/composables/notification"; | ||||||
|  | import {translate} from "../../locales"; | ||||||
| 
 | 
 | ||||||
| const count = ref(0); | const count = ref(0); | ||||||
| const vuetifyVirtualScrollBarRef = ref(document.body); | const vuetifyVirtualScrollBarRef = ref(document.body); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue