226 lines
9.7 KiB
HTML
226 lines
9.7 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>SQL Editor</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
html, body { width: 100%; height: 100%; overflow: hidden; }
|
|
#container { width: 100%; height: 100%; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="container"></div>
|
|
|
|
<!-- 使用 CDN 加载 Monaco Editor -->
|
|
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.50.0/min/vs/loader.js"></script>
|
|
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
|
|
|
<script>
|
|
// SQL 关键字补全词库
|
|
var sqlKeywords = [
|
|
"SELECT", "FROM", "WHERE", "AND", "OR", "NOT", "IN", "IS", "NULL",
|
|
"INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE",
|
|
"CREATE", "TABLE", "DROP", "ALTER", "ADD", "COLUMN", "INDEX", "VIEW",
|
|
"JOIN", "LEFT", "RIGHT", "INNER", "OUTER", "CROSS", "ON",
|
|
"UNION", "ALL", "DISTINCT", "GROUP", "BY", "HAVING",
|
|
"ORDER", "ASC", "DESC", "LIMIT", "OFFSET",
|
|
"AS", "CASE", "WHEN", "THEN", "ELSE", "END", "IF", "EXISTS",
|
|
"BETWEEN", "LIKE", "ESCAPE", "RLIKE", "REGEXP",
|
|
"PRIMARY", "KEY", "FOREIGN", "REFERENCES", "CONSTRAINT",
|
|
"DEFAULT", "AUTO_INCREMENT", "IDENTITY", "UNIQUE", "CHECK",
|
|
"BEGIN", "COMMIT", "ROLLBACK", "TRANSACTION", "SAVEPOINT",
|
|
"GRANT", "REVOKE", "PRIVILEGES", "DATABASE", "SCHEMA",
|
|
"USE", "SHOW", "DESCRIBE", "EXPLAIN", "ANALYZE"
|
|
];
|
|
|
|
var sqlFunctions = [
|
|
"COUNT()", "SUM()", "AVG()", "MIN()", "MAX()",
|
|
"CONCAT()", "SUBSTRING()", "LENGTH()", "TRIM()", "LTRIM()", "RTRIM()",
|
|
"UPPER()", "LOWER()", "REPLACE()", "LEFT()", "RIGHT()", "LOCATE()",
|
|
"NOW()", "CURDATE()", "CURTIME()", "DATE()", "TIME()", "TIMESTAMP()",
|
|
"DATE_FORMAT()", "DATE_ADD()", "DATE_SUB()", "DATEDIFF()",
|
|
"COALESCE()", "IFNULL()", "NULLIF()", "CAST()", "CONVERT()",
|
|
"ROUND()", "CEIL()", "FLOOR()", "ABS()", "MOD()", "POWER()",
|
|
"RAND()", "UUID()", "MD5()", "SHA1()", "SHA2()"
|
|
];
|
|
|
|
var editor = null;
|
|
var cppBridge = null;
|
|
|
|
require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.50.0/min/vs' } });
|
|
|
|
require(['vs/editor/editor.main'], function () {
|
|
// 注册 SQL 语言
|
|
monaco.languages.register({ id: 'sql' });
|
|
|
|
// 定义 SQL 语法的 Tokenizer
|
|
monaco.languages.setMonarchTokensProvider('sql', {
|
|
keywords: sqlKeywords,
|
|
functions: sqlFunctions.map(function(f) { return f.replace('(', '').replace(')', ''); }),
|
|
|
|
tokenizer: {
|
|
root: [
|
|
[/--.*$/, 'comment'],
|
|
[/\/\*/, 'comment', '@comment'],
|
|
[/\b(?:SELECT|FROM|WHERE|AND|OR|NOT|IN|IS|NULL|INSERT|INTO|VALUES|UPDATE|SET|DELETE|CREATE|TABLE|DROP|ALTER|ADD|COLUMN|INDEX|VIEW|JOIN|LEFT|RIGHT|INNER|OUTER|CROSS|ON|UNION|ALL|DISTINCT|GROUP|BY|HAVING|ORDER|ASC|DESC|LIMIT|OFFSET|AS|CASE|WHEN|THEN|ELSE|END|IF|EXISTS|BETWEEN|LIKE|ESCAPE|PRIMARY|KEY|FOREIGN|REFERENCES|CONSTRAINT|DEFAULT|AUTO_INCREMENT|IDENTITY|UNIQUE|CHECK|BEGIN|COMMIT|ROLLBACK|TRANSACTION|SAVEPOINT|GRANT|REVOKE|PRIVILEGES|DATABASE|SCHEMA|USE|SHOW|DESCRIBE|EXPLAIN|ANALYZE)\b/, 'keyword'],
|
|
[/\b(?:COUNT|SUM|AVG|MIN|MAX|CONCAT|SUBSTRING|LENGTH|TRIM|LTRIM|RTRIM|UPPER|LOWER|REPLACE|LEFT|RIGHT|LOCATE|NOW|CURDATE|CURTIME|DATE|TIME|TIMESTAMP|DATE_FORMAT|DATE_ADD|DATE_SUB|DATEDIFF|COALESCE|IFNULL|NULLIF|CAST|CONVERT|ROUND|CEIL|FLOOR|ABS|MOD|POWER|RAND|UUID|MD5|SHA1|SHA2)\b(?=\s*\()/, 'type.identifier'],
|
|
[/'[^']*'/, 'string'],
|
|
/"[^"]*"/, 'string',
|
|
[/\b\d+\.?\d*\b/, 'number'],
|
|
],
|
|
comment: [
|
|
[/[^\/*]+/, 'comment'],
|
|
[/\*\//, 'comment', '@pop'],
|
|
[/[\/*]/, 'comment']
|
|
]
|
|
}
|
|
});
|
|
|
|
// 定义 SQL 主题(深色风格)
|
|
monaco.editor.defineTheme('sql-dark', {
|
|
base: 'vs-dark',
|
|
inherit: true,
|
|
rules: [
|
|
{ token: 'keyword', foreground: '569cd6', fontStyle: 'bold' },
|
|
{ token: 'type.identifier', foreground: 'dcdcaa' },
|
|
{ token: 'string', foreground: 'ce9178' },
|
|
{ token: 'number', foreground: 'b5cea8' },
|
|
{ token: 'comment', foreground: '6a9955', fontStyle: 'italic' },
|
|
],
|
|
colors: {
|
|
'editor.background': '#1e1e1e',
|
|
'editor.foreground': '#d4d4d4',
|
|
'editorLineNumber.foreground': '#858585',
|
|
'editor.selectionBackground': '#264f78',
|
|
'editor.lineHighlightBackground': '#2a2d2e',
|
|
}
|
|
});
|
|
|
|
// 创建编辑器实例
|
|
editor = monaco.editor.create(document.getElementById('container'), {
|
|
value: '-- 在这里编写你的SQL语句\n-- 按 Ctrl+Enter 执行SQL\n-- 输入关键字自动弹出补全建议',
|
|
language: 'sql',
|
|
theme: 'sql-dark',
|
|
fontSize: 14,
|
|
fontFamily: 'Consolas, "Courier New", monospace',
|
|
minimap: { enabled: false },
|
|
lineNumbers: 'on',
|
|
scrollBeyondLastLine: false,
|
|
automaticLayout: true,
|
|
tabSize: 4,
|
|
wordWrap: 'off',
|
|
suggestOnTriggerCharacters: true,
|
|
quickSuggestions: true,
|
|
parameterHints: { enabled: true },
|
|
formatOnPaste: true,
|
|
formatOnType: true,
|
|
bracketPairColorization: { enabled: true },
|
|
guides: { indentation: true, bracketPairs: true },
|
|
});
|
|
|
|
// 注册自动补全项
|
|
monaco.languages.registerCompletionItemProvider('sql', {
|
|
provideCompletionItems: function (model, position) {
|
|
var suggestions = [];
|
|
|
|
// 添加关键字
|
|
for (var i = 0; i < sqlKeywords.length; i++) {
|
|
suggestions.push({
|
|
label: sqlKeywords[i],
|
|
kind: monaco.languages.CompletionItemKind.Keyword,
|
|
insertText: sqlKeywords[i],
|
|
detail: 'SQL Keyword'
|
|
});
|
|
}
|
|
|
|
// 添加函数
|
|
for (var j = 0; j < sqlFunctions.length; j++) {
|
|
suggestions.push({
|
|
label: sqlFunctions[j],
|
|
kind: monaco.languages.CompletionItemKind.Function,
|
|
insertText: sqlFunctions[j],
|
|
detail: 'SQL Function'
|
|
});
|
|
}
|
|
|
|
return { suggestions: suggestions };
|
|
}
|
|
});
|
|
|
|
// Ctrl+Enter 快捷键执行 SQL
|
|
editor.addAction({
|
|
id: 'execute-sql',
|
|
label: 'Execute SQL',
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
|
|
run: function () {
|
|
if (cppBridge && cppBridge.executeSql) {
|
|
cppBridge.executeSql(editor.getValue());
|
|
}
|
|
}
|
|
});
|
|
|
|
// Ctrl+S 快捷键保存/提交
|
|
editor.addAction({
|
|
id: 'submit-changes',
|
|
label: 'Submit Changes',
|
|
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
|
|
run: function () {
|
|
if (cppBridge && cppBridge.submitChanges) {
|
|
cppBridge.submitChanges();
|
|
}
|
|
}
|
|
});
|
|
|
|
// 窗口大小变化时自适应
|
|
window.addEventListener('resize', function () {
|
|
editor.layout();
|
|
});
|
|
});
|
|
|
|
// 连接 Qt WebChannel
|
|
new QWebChannel(qt.webChannelTransport, function(channel) {
|
|
cppBridge = channel.objects.cppBridge;
|
|
|
|
// C++ 调用:获取编辑器内容
|
|
cppBridge.getEditorContent.connect(function() {
|
|
if (cppBridge.contentReady) {
|
|
cppBridge.contentReady(editor.getValue());
|
|
}
|
|
});
|
|
|
|
// C++ 调用:设置编辑器内容
|
|
cppBridge.setEditorContent.connect(function(content) {
|
|
if (editor) {
|
|
editor.setValue(content);
|
|
}
|
|
});
|
|
|
|
// C++ 调用:追加内容
|
|
cppBridge.appendContent.connect(function(content) {
|
|
if (editor) {
|
|
var pos = editor.getPosition();
|
|
editor.executeEdits('', [{
|
|
range: new monaco.Range(pos.lineNumber, pos.column, pos.lineNumber, pos.column),
|
|
text: content
|
|
}]);
|
|
editor.focus();
|
|
}
|
|
});
|
|
|
|
// C++ 调用:插入选中的表名
|
|
cppBridge.insertTableName.connect(function(tableName) {
|
|
if (editor) {
|
|
editor.trigger('source', 'editor.action.insertSnippet', { snippet: '`' + tableName + '`' });
|
|
}
|
|
});
|
|
|
|
// 通知 C++ 编辑器已就绪
|
|
if (cppBridge.editorReady) {
|
|
cppBridge.editorReady();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|