3
This commit is contained in:
		
							parent
							
								
									48e5220ec5
								
							
						
					
					
						commit
						c7ec0cea6c
					
				| @ -2,3 +2,4 @@ | ||||
| .idea/ | ||||
| .deploy/ | ||||
| logs/ | ||||
| .git/ | ||||
|  | ||||
| @ -1,22 +1,31 @@ | ||||
| from flask import Flask, render_template, request, jsonify,send_file | ||||
| from flask import Flask, render_template, request, jsonify, send_file, send_from_directory | ||||
| from utils.sql_parse import parse_create_table_sql | ||||
| from utils.log import Log | ||||
| 
 | ||||
| app = Flask(__name__) | ||||
| log = Log().getlog() | ||||
| 
 | ||||
| 
 | ||||
| @app.route('/css/<path:filename>') | ||||
| def css_files(filename): | ||||
|     return send_from_directory('templates/css', filename) | ||||
| 
 | ||||
| 
 | ||||
| @app.route('/') | ||||
| def index(): | ||||
|     return send_file('templates/index.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'] | ||||
| @ -39,6 +48,55 @@ def convert_sql(): | ||||
|     log.info("SQL result: %s", result) | ||||
|     return jsonify(result) | ||||
| 
 | ||||
| 
 | ||||
| @app.route('/convert1', methods=['POST']) | ||||
| def convert_sql1(): | ||||
|     createTableDDL = request.form['sql'] | ||||
|     if createTableDDL.strip() == '': | ||||
|         return jsonify({ | ||||
|             'parsed_result': "请在上面输入框输入hologres建表语句", | ||||
|             'message': 'SQL processed.' | ||||
|         }) | ||||
| 
 | ||||
|     log.info("SQL Input: %s", createTableDDL) | ||||
|     try: | ||||
| 
 | ||||
|         parsed_result = '' | ||||
|         createTableDDL = createTableDDL.split('CREATE')[1].split('with (')[0] | ||||
|         createTableDDL = 'CREATE' + createTableDDL | ||||
|         print(createTableDDL) | ||||
|         createTableDDL = createTableDDL.replace('public.', '').replace('integer', 'int') | ||||
|         createTableDDL = createTableDDL.replace('timestamp with time zone', 'DATETIME').replace('text', 'VARCHAR(64)') | ||||
|         createTableDDL = createTableDDL.replace(',PRIMARY KEY', 'UNIQUE KEY') | ||||
|         createTableDDL = createTableDDL.replace('numeric(10,2)', 'DECIMAL(10,2)') | ||||
|         createTableDDL = createTableDDL.replace('numeric(10,4)', 'DECIMAL(10,4)') | ||||
| 
 | ||||
|         createTableDDL = createTableDDL.split('\n') | ||||
|         createTableDDL = [(i + '\n').strip() for i in createTableDDL] | ||||
|         UNIQUE_str = '' | ||||
|         UNIQUE_key = '' | ||||
|         for i in createTableDDL: | ||||
|             if i.startswith('UNIQUE'): | ||||
|                 # 获取字符()里面的字符串 | ||||
|                 UNIQUE_key = i.replace('UNIQUE KEY (', '').replace(")", "") | ||||
|                 UNIQUE_str += i | ||||
|             else: | ||||
|                 if i: | ||||
|                     parsed_result += i + '\n' | ||||
| 
 | ||||
|         UNIQUE_str += f' DISTRIBUTED BY HASH ({UNIQUE_key}) BUCKETS 32;' | ||||
|         parsed_result += UNIQUE_str | ||||
|         result = { | ||||
|             'parsed_result': parsed_result, | ||||
|             'message': 'SQL processed successfully.' | ||||
|         } | ||||
|     except Exception as e: | ||||
|         result = {'error': f'请检查输入sql:\n\n  {str(e)}'} | ||||
|     log.info("SQL result: %s", result) | ||||
|     print(result) | ||||
|     return jsonify(result) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     # 指定host和port,这里使用0.0.0.0可以让服务器被外部访问 | ||||
|     app.run(host='0.0.0.0', port=8778, debug=True) | ||||
|  | ||||
							
								
								
									
										348
									
								
								templates/a.html
									
									
									
									
									
								
							
							
						
						
									
										348
									
								
								templates/a.html
									
									
									
									
									
								
							| @ -1,27 +1,119 @@ | ||||
| <!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="/css/codemirror.min.css"> | ||||
|     <link rel="stylesheet" href="/css/theme/monokai.min.css"> | ||||
|     <script src="/css/codemirror.min.js"></script> | ||||
|     <script src="/css/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; | ||||
|             /*设置高度*/ | ||||
|             overflow: hidden; | ||||
|             overflow-y: auto; | ||||
|             word-wrap: break-word; | ||||
|             overflow-wrap: break-word; | ||||
|             word-break: break-all; | ||||
|             /*设置宽度*/ | ||||
|             max-width: 100%; | ||||
|         } | ||||
| 
 | ||||
|         .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"> | ||||
| <div id="app"> | ||||
|     <!-- 基本结构:图片作为超链接内容 --> | ||||
|     <a href="/"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> | ||||
| @ -29,62 +121,198 @@ | ||||
|     <a href="/a"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> | ||||
|     </a> | ||||
|     <a href="/b" > | ||||
|     <a href="/"> | ||||
|         <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> | ||||
|     <header><h1>转odps外部表 SQL格式化工具</h1></header> | ||||
| 
 | ||||
|     <div class="container"> | ||||
|         <h2>输入 SQL</h2> | ||||
|         <div ref="sqlInput"></div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="buttons"> | ||||
|         <button @click="clearInput" title="清空输入框">清空</button> | ||||
|         <button @click="formatSQL" :disabled="isProcessing">格式化</button> | ||||
|         <button @click="copyToClipboard">复制</button> | ||||
|         <button @click="insertSample" title="插入示例语句">示例</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: '/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); | ||||
|             const statusMessage = ref(''); | ||||
|             const statusType = ref(''); | ||||
|             let editorInput = null; | ||||
|             let editorOutput = null; | ||||
| 
 | ||||
|             const outputContent = computed(() => { | ||||
|                 return editorOutput ? editorOutput.getValue().trim() : ''; | ||||
|             }); | ||||
| 
 | ||||
|             const statusClass = computed(() => { | ||||
|                 // 控制台打印 | ||||
|                 console.log(`1Status: ${statusType.value} - ${statusMessage.value}`); | ||||
| 
 | ||||
|                 return statusType.value ? `status-${statusType.value}` : ''; | ||||
|             }); | ||||
| 
 | ||||
|             const setStatus = (message, type = 'info', timeout = 10000) => { | ||||
|                 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 { | ||||
|             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(); | ||||
|                     isProcessing.value = true; | ||||
|                     setStatus('正在格式化SQL...', 'info', 0); | ||||
| 
 | ||||
|             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 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.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); | ||||
|                         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:', error); | ||||
|             const errorDiv = document.createElement('div'); | ||||
|             errorDiv.className = 'output-box error'; | ||||
|             errorDiv.textContent = 'An unexpected error occurred. Please try again.'; | ||||
|             resultArea.appendChild(errorDiv); | ||||
|                     console.error('复制失败:', error); | ||||
|                     setStatus(`复制失败: ${error.message}`, 'error'); | ||||
|                 } | ||||
|     }); | ||||
|             }; | ||||
| 
 | ||||
|             return { | ||||
|                 sqlInput, | ||||
|                 sqlOutput, | ||||
|                 isProcessing, | ||||
|                 statusMessage, | ||||
|                 statusClass, | ||||
|                 outputContent, | ||||
|                 insertSample, | ||||
|                 clearInput, | ||||
|                 formatSQL, | ||||
|                 copyToClipboard | ||||
|             }; | ||||
|         } | ||||
|     }).mount('#app'); | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
| @ -4,10 +4,10 @@ | ||||
|     <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> | ||||
|     <link rel="stylesheet" href="/css/codemirror.min.css"> | ||||
|     <link rel="stylesheet" href="/css/theme/monokai.min.css"> | ||||
|     <script src="/css/codemirror.min.js"></script> | ||||
|     <script src="/css/mode/sql/sql.min.js"></script> | ||||
|     <style> | ||||
|         :root { | ||||
|             --primary-color: #4a90e2; | ||||
| @ -75,7 +75,41 @@ | ||||
|             cursor: not-allowed; | ||||
|         } | ||||
| 
 | ||||
|         .status-message { | ||||
|             font-size: 0.9em; | ||||
|             margin-top: 10px; | ||||
|             min-height: 1.2em; | ||||
|             padding: 8px; | ||||
|             border-radius: 4px; | ||||
|             /*设置高度*/ | ||||
|             overflow: hidden; | ||||
|             overflow-y: auto; | ||||
|             word-wrap: break-word; | ||||
|             overflow-wrap: break-word; | ||||
|             word-break: break-all; | ||||
|             /*设置宽度*/ | ||||
|             max-width: 100%; | ||||
|         } | ||||
| 
 | ||||
|         .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> | ||||
| @ -87,9 +121,8 @@ | ||||
|     <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 href="/"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> | ||||
|     </a> | ||||
|     <header><h1>SQL格式化工具</h1></header> | ||||
| 
 | ||||
| @ -99,11 +132,14 @@ | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="buttons"> | ||||
|         <button @click="insertSample" title="插入示例语句">示例</button> | ||||
|         <button @click="clearInput" title="清空输入框">清空</button> | ||||
|         <button @click="formatSQL" :disabled="isProcessing">格式化</button> | ||||
|         <button @click="copyToClipboard">复制</button> | ||||
|         <button @click="insertSample" title="插入示例语句">示例</button> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="status-message" :class="statusClass">{{ statusMessage }}</div> | ||||
| 
 | ||||
|     <div class="container"> | ||||
|         <h2>输出 SQL</h2> | ||||
|         <div ref="sqlOutput"></div> | ||||
| @ -114,7 +150,7 @@ | ||||
|     const {createApp, ref, onMounted, computed} = Vue; | ||||
| 
 | ||||
|     const API_CONFIG = { | ||||
|         ENDPOINT: 'http://10.23.0.209:8778/convert', | ||||
|         ENDPOINT: '/convert', | ||||
|         DEFAULT_HEADERS: { | ||||
|             'Content-Type': 'application/x-www-form-urlencoded' | ||||
|         }, | ||||
| @ -142,6 +178,8 @@ | ||||
|             const sqlInput = ref(null); | ||||
|             const sqlOutput = ref(null); | ||||
|             const isProcessing = ref(false); | ||||
|             const statusMessage = ref(''); | ||||
|             const statusType = ref(''); | ||||
|             let editorInput = null; | ||||
|             let editorOutput = null; | ||||
| 
 | ||||
| @ -149,6 +187,24 @@ | ||||
|                 return editorOutput ? editorOutput.getValue().trim() : ''; | ||||
|             }); | ||||
| 
 | ||||
|             const statusClass = computed(() => { | ||||
|                 // 控制台打印 | ||||
|                 console.log(`1Status: ${statusType.value} - ${statusMessage.value}`); | ||||
| 
 | ||||
|                 return statusType.value ? `status-${statusType.value}` : ''; | ||||
|             }); | ||||
| 
 | ||||
|             const setStatus = (message, type = 'info', timeout = 10000) => { | ||||
|                 statusMessage.value = message; | ||||
|                 statusType.value = type; | ||||
|                 if (timeout) { | ||||
|                     setTimeout(() => { | ||||
|                         statusMessage.value = ''; | ||||
|                         statusType.value = ''; | ||||
|                     }, timeout); | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             onMounted(() => { | ||||
|                 if (sqlInput.value && sqlOutput.value) { | ||||
|                     editorInput = CodeMirror(sqlInput.value, { | ||||
| @ -165,20 +221,25 @@ | ||||
| 
 | ||||
|             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', | ||||
| @ -198,9 +259,11 @@ | ||||
|                     } | ||||
| 
 | ||||
|                     editorOutput.setValue(data.parsed_result); | ||||
|                     setStatus(`格式化成功 (${duration}ms)`, 'success'); | ||||
|                 } catch (error) { | ||||
|                     console.error('格式化错误:', error); | ||||
|                     editorOutput.setValue(''); | ||||
|                     setStatus(`格式化失败: ${error.message}`, 'error'); | ||||
|                 } finally { | ||||
|                     isProcessing.value = false; | ||||
|                 } | ||||
| @ -210,11 +273,13 @@ | ||||
|                 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'); | ||||
| @ -226,9 +291,11 @@ | ||||
|                         document.body.removeChild(textarea); | ||||
| 
 | ||||
|                         if (!success) throw new Error('传统复制方式失败'); | ||||
|                         setStatus('内容已复制(传统方式)', 'success'); | ||||
|                     } | ||||
|                 } catch (error) { | ||||
|                     console.error('复制失败:', error); | ||||
|                     setStatus(`复制失败: ${error.message}`, 'error'); | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
| @ -236,6 +303,8 @@ | ||||
|                 sqlInput, | ||||
|                 sqlOutput, | ||||
|                 isProcessing, | ||||
|                 statusMessage, | ||||
|                 statusClass, | ||||
|                 outputContent, | ||||
|                 insertSample, | ||||
|                 clearInput, | ||||
|  | ||||
							
								
								
									
										1
									
								
								templates/css/codemirror.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								templates/css/codemirror.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								templates/css/codemirror.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								templates/css/codemirror.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								templates/css/monokai.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								templates/css/monokai.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| .cm-s-monokai.CodeMirror{background:#272822;color:#f8f8f2}.cm-s-monokai div.CodeMirror-selected{background:#49483e}.cm-s-monokai .CodeMirror-line::selection,.cm-s-monokai .CodeMirror-line>span::selection,.cm-s-monokai .CodeMirror-line>span>span::selection{background:rgba(73,72,62,.99)}.cm-s-monokai .CodeMirror-line::-moz-selection,.cm-s-monokai .CodeMirror-line>span::-moz-selection,.cm-s-monokai .CodeMirror-line>span>span::-moz-selection{background:rgba(73,72,62,.99)}.cm-s-monokai .CodeMirror-gutters{background:#272822;border-right:0}.cm-s-monokai .CodeMirror-guttermarker{color:#fff}.cm-s-monokai .CodeMirror-guttermarker-subtle{color:#d0d0d0}.cm-s-monokai .CodeMirror-linenumber{color:#d0d0d0}.cm-s-monokai .CodeMirror-cursor{border-left:1px solid #f8f8f0}.cm-s-monokai span.cm-comment{color:#75715e}.cm-s-monokai span.cm-atom{color:#ae81ff}.cm-s-monokai span.cm-number{color:#ae81ff}.cm-s-monokai span.cm-comment.cm-attribute{color:#97b757}.cm-s-monokai span.cm-comment.cm-def{color:#bc9262}.cm-s-monokai span.cm-comment.cm-tag{color:#bc6283}.cm-s-monokai span.cm-comment.cm-type{color:#5998a6}.cm-s-monokai span.cm-attribute,.cm-s-monokai span.cm-property{color:#a6e22e}.cm-s-monokai span.cm-keyword{color:#f92672}.cm-s-monokai span.cm-builtin{color:#66d9ef}.cm-s-monokai span.cm-string{color:#e6db74}.cm-s-monokai span.cm-variable{color:#f8f8f2}.cm-s-monokai span.cm-variable-2{color:#9effff}.cm-s-monokai span.cm-type,.cm-s-monokai span.cm-variable-3{color:#66d9ef}.cm-s-monokai span.cm-def{color:#fd971f}.cm-s-monokai span.cm-bracket{color:#f8f8f2}.cm-s-monokai span.cm-tag{color:#f92672}.cm-s-monokai span.cm-header{color:#ae81ff}.cm-s-monokai span.cm-link{color:#ae81ff}.cm-s-monokai span.cm-error{background:#f92672;color:#f8f8f0}.cm-s-monokai .CodeMirror-activeline-background{background:#373831}.cm-s-monokai .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important} | ||||
							
								
								
									
										1
									
								
								templates/css/sql.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								templates/css/sql.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										18227
									
								
								templates/css/vue.global.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18227
									
								
								templates/css/vue.global.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -4,10 +4,10 @@ | ||||
|     <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> | ||||
|     <link rel="stylesheet" href="/css/codemirror.min.css"> | ||||
|     <link rel="stylesheet" href="/css/monokai.min.css"> | ||||
|     <script src="/css/codemirror.min.js"></script> | ||||
|     <script src="/css/sql.min.js"></script> | ||||
|     <style> | ||||
|         :root { | ||||
|             --primary-color: #4a90e2; | ||||
| @ -118,13 +118,13 @@ | ||||
|     <a href="/"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> | ||||
|     </a> | ||||
|     <a href="/"> | ||||
|     <a href="/a"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> | ||||
|     </a> | ||||
|     <a href="/"> | ||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> | ||||
|     </a> | ||||
|     <header><h1>SQL格式化工具</h1></header> | ||||
|     <header><h1>转doirs SQL格式化工具</h1></header> | ||||
| 
 | ||||
|     <div class="container"> | ||||
|         <h2>输入 SQL</h2> | ||||
| @ -150,7 +150,7 @@ | ||||
|     const {createApp, ref, onMounted, computed} = Vue; | ||||
| 
 | ||||
|     const API_CONFIG = { | ||||
|         ENDPOINT: '/convert', | ||||
|         ENDPOINT: '/convert1', | ||||
|         DEFAULT_HEADERS: { | ||||
|             'Content-Type': 'application/x-www-form-urlencoded' | ||||
|         }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user