2025-02-27 17:36:27 +08:00

249 lines
7.7 KiB
HTML

<!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>