This commit is contained in:
尹舟 2025-02-27 17:36:27 +08:00
parent be9c66a339
commit 5dde7eaa1f
5 changed files with 808 additions and 70 deletions

View File

@ -2,30 +2,37 @@ from flask import Flask, render_template, request, jsonify
from utils.sql_parse import parse_create_table_sql
from utils.log import Log
app = Flask(__name__)
log = Log().getlog()
@app.route('/')
def index():
return render_template('index.html')
return render_template('c.html')
@app.route('/a')
def index_a():
return render_template('a.html')
@app.route('/b')
def index_b():
return render_template('b.html')
@app.route('/convert', methods=['POST'])
def convert_sql():
sql_input = request.form['sql']
if sql_input.strip()=='':
if sql_input.strip() == '':
return jsonify({
'parsed_result': "请在上面输入框输入hologres建表语句",
'message': 'SQL processed.'
})
hologres_connection = request.form['hologresConnection']
log.info("SQL Input: %s", sql_input)
log.info("SQL hologres_connection: %s", hologres_connection)
try:
parsed_result=parse_create_table_sql(sql_input,hologres_connection)
parsed_result = parse_create_table_sql(sql_input, '')
print(parsed_result)
result = {
'parsed_result': parsed_result,
@ -36,6 +43,7 @@ def convert_sql():
log.info("SQL result: %s", result)
return jsonify(result)
if __name__ == '__main__':
# 指定host和port这里使用0.0.0.0可以让服务器被外部访问
app.run(host='0.0.0.0', port=8778, debug=True)
app.run(host='0.0.0.0', port=8778, debug=True)

90
templates/a.html Normal file
View File

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL Processor</title>
<style>
body { font: 1rem 'Roboto', Arial, sans-serif; margin: 0; padding: 0; background: #f5f5f5; color: #333; }
.container { max-width: 1200px; margin: 0 auto; padding: 1rem; }
header { text-align: center; padding: 1rem 0; background: #fff; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); margin-bottom: 1rem; }
h1 { font-size: 2.5rem; color: #4a90e2; margin: 0; font-weight: 500; }
form, .output-box { background: #fff; padding: 1rem; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
textarea, input[type="text"] { width: 100%; padding: 0.6rem; margin-bottom: 1rem; font: inherit; border: 1px solid #ddd; border-radius: 6px; background: #f9f9f9; resize: none; }
textarea { height: 200px; }
button { display: inline-block; background: #4a90e2; color: #fff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font: 600 1rem 'Roboto'; cursor: pointer; transition: background 0.3s ease; }
button:hover { background: #357ab8; }
.output-box { margin-bottom: 1rem; border: 1px solid #ddd; border-radius: 6px; background: #f9f9f9; }
.error { color: #dc3545; background: #ffebee; padding: 0.6rem; border-radius: 6px; }
.success { color: #28a745; background: #d4edda; padding: 0.6rem; border-radius: 6px; }
</style>
</head>
<body>
<div class="container">
<!-- 基本结构:图片作为超链接内容 -->
<a href="/" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50">
</a>
<a href="/a" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50">
</a>
<a href="/b" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50">
</a>
<header><h1>SQL格式化工具</h1></header>
<form id="sqlForm">
<textarea id="sqlInput" placeholder="Enter your SQL here..."></textarea>
<label for="hologresConnection">Hologres Connection Info:</label>
<input type="text" id="hologresConnection" name="hologresConnection" value="hgprecn-cn-i7m2ssubq004-cn-hangzhou-internal.hologres.aliyuncs.com:80" placeholder="Enter Hologres connection info...">
<button type="submit">Convert</button>
</form>
<div id="resultArea" class="output-box" style="display: none;"></div>
</div>
<script>
document.getElementById('sqlForm').addEventListener('submit', async (e) => {
e.preventDefault();
const resultArea = document.getElementById('resultArea');
resultArea.style.display = 'block';
resultArea.querySelector('.output-box')?.remove();
const sql = encodeURIComponent(document.getElementById('sqlInput').value);
const connection = encodeURIComponent(document.getElementById('hologresConnection').value);
try {
const response = await fetch('/convert', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `sql=${sql}&hologresConnection=${connection}`
});
const data = await response.json();
if (data.error) {
const div = document.createElement('div');
div.className = 'output-box error';
div.textContent = data.error;
resultArea.appendChild(div);
} else {
const div = document.createElement('div');
div.className = 'output-box success';
const textarea = document.createElement('textarea');
textarea.readOnly = true;
textarea.value = data.parsed_result || '请检查输入建表语句';
div.appendChild(textarea);
const messageP = document.createElement('p');
messageP.textContent = data.message;
div.appendChild(messageP);
resultArea.appendChild(div);
}
} catch (error) {
console.error('Error:', error);
const errorDiv = document.createElement('div');
errorDiv.className = 'output-box error';
errorDiv.textContent = 'An unexpected error occurred. Please try again.';
resultArea.appendChild(errorDiv);
}
});
</script>
</body>
</html>

162
templates/b.html Normal file
View File

@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<!-- 设置文档的字符编码和基本URL -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<base href="https://coding.tools/cn/sql-formatter"/>
<!-- 引入样式文件 -->
<link rel="stylesheet" href="/css/unit.css?ff=1&amp;v=20220622.1&amp;wps=false"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.4/codemirror.min.css"/>
<!-- 自定义样式内部编写 -->
<style>
/* 布局优化 */
.codewrapper {
margin: 15px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* 编辑器高度设置 */
.CodeMirror {
height: 500px !important;
font-family: 'Fira Code', monospace;
}
/* 按钮组样式 */
.btn-group {
gap: 8px;
margin: 10px 0;
}
</style>
</head>
<body>
<div id="content" class="container-fluid">
<div class="content_along_sidebar">
<a href="/" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50">
</a>
<a href="/a" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50">
</a>
<a href="/b" >
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50">
</a>
<div id="headline">
<h1>SQL格式化工具</h1>
</div>
<!-- 设置表单用于输入SQL语句 -->
<form id="options">
<div class="codewrapper size16codewrapper">
<textarea autofocus="" id="code1" name="code1" style="width: 100%;height: 800px"
placeholder="请在这里输入或者拖入文本文件."></textarea>
</div>
<br/>
<!-- 清空文本域的按钮 -->
<button type="button" class="btn btn-secondary" onclick="clear_txt()"><i class="far fa-trash-alt"></i> 清空
</button>
<!-- 格式化SQL语句的按钮 -->
<button type="button" id="submit_button" class="btn btn-primary" onclick="query()">格式化SQL</button>
<!-- 复制结果的按钮 -->
<button type="button" id="copy" class="btn btn-secondary" onclick="copy_to_clipboard()"><i
class="far fa-copy"></i> 复制结果
</button>
</form>
<!-- 显示格式化后的SQL语句 -->
<div class="codewrapper size16codewrapper">
<textarea id="code2" name="code" style="width: 100%;"
placeholder="sql格式化结果将会显示在这里."></textarea>
</div>
<br/>
</div>
</div>
<!-- 引入Codemirror库和SQL模式 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.4/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.4/mode/sql/sql.min.js"></script>
<!-- 初始化两个文本域为Codemirror编辑器 -->
<script>
var editor1 = CodeMirror.fromTextArea(document.getElementById("code1"), {
mode: "text/x-sql",
lineNumbers: true,
lineWrapping: true,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
});
var editor2 = CodeMirror.fromTextArea(document.getElementById("code2"), {
mode: "text/x-sql",
lineNumbers: true,
lineWrapping: true,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
});
</script>
<!-- 定义清空文本域的函数 -->
<script>
function clear_txt() {
editor1.setValue("");
editor2.setValue("");
}
// 定义复制结果到剪贴板的函数
function copy_to_clipboard() {
// 获取editor2中的SQL语句
var sql = editor2.getValue();
// 创建一个临时的textarea元素
var tempTextarea = document.createElement('textarea');
tempTextarea.value = sql;
document.body.appendChild(tempTextarea);
// 选择textarea中的文本
tempTextarea.select();
tempTextarea.setSelectionRange(0, 99999); // 适用于移动设备
// 复制选中的文本到剪贴板
document.execCommand('copy');
// 移除临时的textarea元素
document.body.removeChild(tempTextarea);
// 更新按钮文本以提示用户已复制
var copy_button = document.getElementById("copy");
copy_button.innerHTML = "已复制";
setTimeout(function () {
copy_button.innerHTML = "复制结果";
}, 3000);
}
</script>
<!-- 定义格式化SQL语句的函数 -->
<script>
function query() {
// 获取editor1中的SQL语句
var sql = editor1.getValue();
// 发送POST请求到/convert接口
fetch('http://10.23.0.209:8778/convert', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'sql=' + encodeURIComponent(sql),
})
.then(response => response.json())
.then(data => {
// 将格式化后的SQL语句设置到editor2
editor2.setValue(data.parsed_result);
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
</body>
</html>

249
templates/c.html Normal file
View File

@ -0,0 +1,249 @@
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<title>SQL 处理器</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js"></script>
<style>
:root {
--primary-color: #4a90e2;
--hover-color: #2c78c8;
--bg-color: #f0f2f5;
--border-color: #ddd;
--success-color: #28a745;
--error-color: #dc3545;
--warning-color: #ffc107;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: var(--bg-color);
}
h1 {
font-size: 2.5rem;
color: var(--primary-color);
margin: 0 0 20px;
font-weight: 500;
}
.container {
margin-bottom: 20px;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.CodeMirror {
height: 300px;
border: 1px solid var(--border-color);
border-radius: 6px;
resize: vertical;
}
.buttons {
margin: 15px 0;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
button {
background: var(--primary-color);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.75rem 1.5rem;
font: 600 1rem 'Roboto', sans-serif;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: var(--hover-color);
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<div id="app">
<!-- 基本结构:图片作为超链接内容 -->
<a href="/">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50">
</a>
<a href="/a">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50">
</a>
<a href="/b">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50"
height="50">
</a>
<header><h1>SQL格式化工具</h1></header>
<div class="container">
<h2>输入 SQL</h2>
<div ref="sqlInput"></div>
</div>
<div class="buttons">
<button @click="insertSample" title="插入示例语句">示例</button>
<button @click="clearInput" title="清空输入框">清空</button>
<button @click="formatSQL" :disabled="isProcessing">格式化</button>
<button @click="copyToClipboard">复制</button>
</div>
<div class="container">
<h2>输出 SQL</h2>
<div ref="sqlOutput"></div>
</div>
</div>
<script>
const {createApp, ref, onMounted, computed} = Vue;
const API_CONFIG = {
ENDPOINT: 'http://10.23.0.209:8778/convert',
DEFAULT_HEADERS: {
'Content-Type': 'application/x-www-form-urlencoded'
},
DEFAULT_SQL: "SELECT * FROM table WHERE condition;",
SAMPLE_SQL: "SELECT * FROM users WHERE age > 18;"
};
const createEditorConfig = (readOnly = false) => ({
mode: "sql",
lineNumbers: true,
theme: "monokai",
readOnly,
extraKeys: {"Ctrl-Space": "autocomplete"},
hintOptions: {
completeSingle: false,
tables: {
users: ["id", "name", "age"],
products: ["id", "name", "price"]
}
}
});
createApp({
setup() {
const sqlInput = ref(null);
const sqlOutput = ref(null);
const isProcessing = ref(false);
let editorInput = null;
let editorOutput = null;
const outputContent = computed(() => {
return editorOutput ? editorOutput.getValue().trim() : '';
});
onMounted(() => {
if (sqlInput.value && sqlOutput.value) {
editorInput = CodeMirror(sqlInput.value, {
...createEditorConfig(),
value: API_CONFIG.DEFAULT_SQL
});
editorOutput = CodeMirror(sqlOutput.value, {
...createEditorConfig(true),
value: "默认输出内容" // 给输出框一个默认值
});
}
});
const insertSample = () => {
editorInput.setValue(API_CONFIG.SAMPLE_SQL);
};
const clearInput = () => {
editorInput.setValue('');
};
const formatSQL = async () => {
const inputSQL = editorInput.getValue().trim();
if (!inputSQL) {
return;
}
try {
isProcessing.value = true;
const startTime = Date.now();
const response = await fetch(API_CONFIG.ENDPOINT, {
method: 'POST',
headers: API_CONFIG.DEFAULT_HEADERS,
body: `sql=${encodeURIComponent(inputSQL)}`
});
const duration = Date.now() - startTime;
if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (!data.parsed_result) {
throw new Error('服务器返回空结果');
}
editorOutput.setValue(data.parsed_result);
} catch (error) {
console.error('格式化错误:', error);
editorOutput.setValue('');
} finally {
isProcessing.value = false;
}
};
const copyToClipboard = async () => {
try {
const content = editorOutput.getValue().trim();
if (!content) {
return;
}
try {
await navigator.clipboard.writeText(content);
} catch (err) {
// 回退到传统复制方式
const textarea = document.createElement('textarea');
textarea.value = content;
document.body.appendChild(textarea);
textarea.select();
const success = document.execCommand('copy');
document.body.removeChild(textarea);
if (!success) throw new Error('传统复制方式失败');
}
} catch (error) {
console.error('复制失败:', error);
}
};
return {
sqlInput,
sqlOutput,
isProcessing,
outputContent,
insertSample,
clearInput,
formatSQL,
copyToClipboard
};
}
}).mount('#app');
</script>
</body>
</html>

View File

@ -1,79 +1,308 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL Processor</title>
<title>SQL 处理器</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js"></script>
<style>
body { font: 1rem 'Roboto', Arial, sans-serif; margin: 0; padding: 0; background: #f5f5f5; color: #333; }
.container { max-width: 1200px; margin: 0 auto; padding: 1rem; }
header { text-align: center; padding: 1rem 0; background: #fff; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); margin-bottom: 1rem; }
h1 { font-size: 2.5rem; color: #4a90e2; margin: 0; font-weight: 500; }
form, .output-box { background: #fff; padding: 1rem; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
textarea, input[type="text"] { width: 100%; padding: 0.6rem; margin-bottom: 1rem; font: inherit; border: 1px solid #ddd; border-radius: 6px; background: #f9f9f9; resize: none; }
textarea { height: 200px; }
button { display: inline-block; background: #4a90e2; color: #fff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font: 600 1rem 'Roboto'; cursor: pointer; transition: background 0.3s ease; }
button:hover { background: #357ab8; }
.output-box { margin-bottom: 1rem; border: 1px solid #ddd; border-radius: 6px; background: #f9f9f9; }
.error { color: #dc3545; background: #ffebee; padding: 0.6rem; border-radius: 6px; }
.success { color: #28a745; background: #d4edda; padding: 0.6rem; border-radius: 6px; }
:root {
--primary-color: #4a90e2;
--hover-color: #2c78c8;
--bg-color: #f0f2f5;
--border-color: #ddd;
--success-color: #28a745;
--error-color: #dc3545;
--warning-color: #ffc107;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: var(--bg-color);
}
h1 {
font-size: 2.5rem;
color: var(--primary-color);
margin: 0 0 20px;
font-weight: 500;
}
.container {
margin-bottom: 20px;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.CodeMirror {
height: 300px;
border: 1px solid var(--border-color);
border-radius: 6px;
resize: vertical;
}
.buttons {
margin: 15px 0;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
button {
background: var(--primary-color);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.75rem 1.5rem;
font: 600 1rem 'Roboto', sans-serif;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: var(--hover-color);
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.status-message {
font-size: 0.9em;
margin-top: 10px;
min-height: 1.2em;
padding: 8px;
border-radius: 4px;
}
.status-success {
background: #e8f5e9;
color: var(--success-color);
}
.status-error {
background: #ffebee;
color: var(--error-color);
}
.status-warning {
background: #fff3e0;
color: var(--warning-color);
}
.status-info {
background: #e3f2fd;
color: var(--primary-color);
}
</style>
</head>
<body>
<div class="container">
<header><h1>SQL Processor</h1></header>
<form id="sqlForm">
<textarea id="sqlInput" placeholder="Enter your SQL here..."></textarea>
<label for="hologresConnection">Hologres Connection Info:</label>
<input type="text" id="hologresConnection" name="hologresConnection" value="hgprecn-cn-i7m2ssubq004-cn-hangzhou-internal.hologres.aliyuncs.com:80" placeholder="Enter Hologres connection info...">
<button type="submit">Convert</button>
</form>
<div id="resultArea" class="output-box" style="display: none;"></div>
<div id="app">
<!-- 基本结构:图片作为超链接内容 -->
<a href="/">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50">
</a>
<a href="/a">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50">
</a>
<a href="/b">
<img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50">
</a>
<header><h1>SQL格式化工具</h1></header>
<div class="container">
<h2>输入 SQL</h2>
<div ref="sqlInput"></div>
</div>
<div class="buttons">
<button @click="insertSample" title="插入示例语句">示例</button>
<button @click="clearInput" title="清空输入框">清空</button>
<button @click="formatSQL" :disabled="isProcessing">格式化</button>
<button @click="copyToClipboard">复制</button>
</div>
<div class="status-message" :class="statusClass">{{ statusMessage }}</div>
<div class="container">
<h2>输出 SQL</h2>
<div ref="sqlOutput"></div>
</div>
</div>
<script>
document.getElementById('sqlForm').addEventListener('submit', async (e) => {
e.preventDefault();
const resultArea = document.getElementById('resultArea');
resultArea.style.display = 'block';
resultArea.querySelector('.output-box')?.remove();
const {createApp, ref, onMounted, computed} = Vue;
const sql = encodeURIComponent(document.getElementById('sqlInput').value);
const connection = encodeURIComponent(document.getElementById('hologresConnection').value);
const API_CONFIG = {
ENDPOINT: 'http://10.23.0.209:8778/convert',
DEFAULT_HEADERS: {
'Content-Type': 'application/x-www-form-urlencoded'
},
DEFAULT_SQL: "SELECT * FROM table WHERE condition;",
SAMPLE_SQL: "SELECT * FROM users WHERE age > 18;"
};
try {
const response = await fetch('/convert', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `sql=${sql}&hologresConnection=${connection}`
});
const data = await response.json();
if (data.error) {
const div = document.createElement('div');
div.className = 'output-box error';
div.textContent = data.error;
resultArea.appendChild(div);
} else {
const div = document.createElement('div');
div.className = 'output-box success';
const textarea = document.createElement('textarea');
textarea.readOnly = true;
textarea.value = data.parsed_result || '请检查输入建表语句';
div.appendChild(textarea);
const messageP = document.createElement('p');
messageP.textContent = data.message;
div.appendChild(messageP);
resultArea.appendChild(div);
const createEditorConfig = (readOnly = false) => ({
mode: "sql",
lineNumbers: true,
theme: "monokai",
readOnly,
extraKeys: {"Ctrl-Space": "autocomplete"},
hintOptions: {
completeSingle: false,
tables: {
users: ["id", "name", "age"],
products: ["id", "name", "price"]
}
} catch (error) {
console.error('Error:', error);
const errorDiv = document.createElement('div');
errorDiv.className = 'output-box error';
errorDiv.textContent = 'An unexpected error occurred. Please try again.';
resultArea.appendChild(errorDiv);
}
});
createApp({
setup() {
const sqlInput = ref(null);
const sqlOutput = ref(null);
const isProcessing = ref(false);
const statusMessage = ref('');
const statusType = ref('');
let editorInput = null;
let editorOutput = null;
const outputContent = computed(() => {
return editorOutput ? editorOutput.getValue().trim() : '';
});
const statusClass = computed(() => {
return statusType.value ? `status-${statusType.value}` : '';
});
const setStatus = (message, type = 'info', timeout = 3000) => {
statusMessage.value = message;
statusType.value = type;
if (timeout) {
setTimeout(() => {
statusMessage.value = '';
statusType.value = '';
}, timeout);
}
};
onMounted(() => {
if (sqlInput.value && sqlOutput.value) {
editorInput = CodeMirror(sqlInput.value, {
...createEditorConfig(),
value: API_CONFIG.DEFAULT_SQL
});
editorOutput = CodeMirror(sqlOutput.value, {
...createEditorConfig(true),
value: "默认输出内容" // 给输出框一个默认值
});
}
});
const insertSample = () => {
editorInput.setValue(API_CONFIG.SAMPLE_SQL);
setStatus('示例语句已插入', 'success');
};
const clearInput = () => {
editorInput.setValue('');
setStatus('输入已清空', 'info');
};
const formatSQL = async () => {
const inputSQL = editorInput.getValue().trim();
if (!inputSQL) {
setStatus('请输入要格式化的SQL内容', 'warning');
return;
}
try {
isProcessing.value = true;
setStatus('正在格式化SQL...', 'info', 0);
const startTime = Date.now();
const response = await fetch(API_CONFIG.ENDPOINT, {
method: 'POST',
headers: API_CONFIG.DEFAULT_HEADERS,
body: `sql=${encodeURIComponent(inputSQL)}`
});
const duration = Date.now() - startTime;
if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (!data.parsed_result) {
throw new Error('服务器返回空结果');
}
editorOutput.setValue(data.parsed_result);
setStatus(`格式化成功 (${duration}ms)`, 'success');
} catch (error) {
console.error('格式化错误:', error);
editorOutput.setValue('');
setStatus(`格式化失败: ${error.message}`, 'error');
} finally {
isProcessing.value = false;
}
};
const copyToClipboard = async () => {
try {
const content = editorOutput.getValue().trim();
if (!content) {
setStatus('无内容可复制', 'warning');
return;
}
try {
await navigator.clipboard.writeText(content);
setStatus('内容已复制到剪贴板', 'success');
} catch (err) {
// 回退到传统复制方式
const textarea = document.createElement('textarea');
textarea.value = content;
document.body.appendChild(textarea);
textarea.select();
const success = document.execCommand('copy');
document.body.removeChild(textarea);
if (!success) throw new Error('传统复制方式失败');
setStatus('内容已复制(传统方式)', 'success');
}
} catch (error) {
console.error('复制失败:', error);
setStatus(`复制失败: ${error.message}`, 'error');
}
};
return {
sqlInput,
sqlOutput,
isProcessing,
statusMessage,
statusClass,
outputContent,
insertSample,
clearInput,
formatSQL,
copyToClipboard
};
}
}).mount('#app');
</script>
</body>
</html>