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 |
43
src/i18n.ts
43
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,31 +189,35 @@ const menuItems: Item[] = ([
|
||||||
name: translate("page.feedback"),
|
name: translate("page.feedback"),
|
||||||
href: "/feedback",
|
href: "/feedback",
|
||||||
},
|
},
|
||||||
]);
|
]));
|
||||||
|
|
||||||
|
const sideBarItems: ComputedRef<Item[]> = computed(() => {
|
||||||
|
const items: Item[] = [
|
||||||
|
{
|
||||||
|
name: translate("page.uart"),
|
||||||
|
href: "/uart",
|
||||||
|
}, {
|
||||||
|
name: translate("page.wifi"),
|
||||||
|
href: "/wifi",
|
||||||
|
}, {
|
||||||
|
name: translate("page.about"),
|
||||||
|
href: "/about",
|
||||||
|
}, {
|
||||||
|
name: translate("page.feedback"),
|
||||||
|
href: "/feedback",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (isOTAEnabled()) {
|
||||||
|
items.push({
|
||||||
|
name: translate("page.update"),
|
||||||
|
href: "/update",
|
||||||
|
badge: computed(() => updateStore.canUpdate),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
|
||||||
const sideBarItems: Item[] = ([
|
|
||||||
{
|
|
||||||
name: translate("page.uart"),
|
|
||||||
href: "/uart",
|
|
||||||
}, {
|
|
||||||
name: translate("page.wifi"),
|
|
||||||
href: "/wifi",
|
|
||||||
}, {
|
|
||||||
name: translate("page.about"),
|
|
||||||
href: "/about",
|
|
||||||
}, {
|
|
||||||
name: translate("page.feedback"),
|
|
||||||
href: "/feedback",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (isOTAEnabled()) {
|
|
||||||
sideBarItems.push({
|
|
||||||
name: translate("page.update"),
|
|
||||||
href: "/update",
|
|
||||||
badge: computed(() => updateStore.canUpdate),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</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