3
This commit is contained in:
		
							parent
							
								
									48e5220ec5
								
							
						
					
					
						commit
						c7ec0cea6c
					
				| @ -2,3 +2,4 @@ | |||||||
| .idea/ | .idea/ | ||||||
| .deploy/ | .deploy/ | ||||||
| logs/ | 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.sql_parse import parse_create_table_sql | ||||||
| from utils.log import Log | from utils.log import Log | ||||||
| 
 | 
 | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| log = Log().getlog() | log = Log().getlog() | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | @app.route('/css/<path:filename>') | ||||||
|  | def css_files(filename): | ||||||
|  |     return send_from_directory('templates/css', filename) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @app.route('/') | @app.route('/') | ||||||
| def index(): | def index(): | ||||||
|     return send_file('templates/index.html') |     return send_file('templates/index.html') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @app.route('/a') | @app.route('/a') | ||||||
| def index_a(): | def index_a(): | ||||||
|     return render_template('a.html') |     return render_template('a.html') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @app.route('/b') | @app.route('/b') | ||||||
| def index_b(): | def index_b(): | ||||||
|     return render_template('b.html') |     return render_template('b.html') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @app.route('/convert', methods=['POST']) | @app.route('/convert', methods=['POST']) | ||||||
| def convert_sql(): | def convert_sql(): | ||||||
|     sql_input = request.form['sql'] |     sql_input = request.form['sql'] | ||||||
| @ -39,6 +48,55 @@ def convert_sql(): | |||||||
|     log.info("SQL result: %s", result) |     log.info("SQL result: %s", result) | ||||||
|     return jsonify(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__': | if __name__ == '__main__': | ||||||
|     # 指定host和port,这里使用0.0.0.0可以让服务器被外部访问 |     # 指定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) | ||||||
|  | |||||||
							
								
								
									
										366
									
								
								templates/a.html
									
									
									
									
									
								
							
							
						
						
									
										366
									
								
								templates/a.html
									
									
									
									
									
								
							| @ -1,90 +1,318 @@ | |||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="en"> | <html lang="zh-Hans"> | ||||||
| <head> | <head> | ||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> |     <title>SQL 处理器</title> | ||||||
|     <title>SQL Processor</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> |     <style> | ||||||
|         body { font: 1rem 'Roboto', Arial, sans-serif; margin: 0; padding: 0; background: #f5f5f5; color: #333; } |         :root { | ||||||
|         .container { max-width: 1200px; margin: 0 auto; padding: 1rem; } |             --primary-color: #4a90e2; | ||||||
|         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; } |             --hover-color: #2c78c8; | ||||||
|         h1 { font-size: 2.5rem; color: #4a90e2; margin: 0; font-weight: 500; } |             --bg-color: #f0f2f5; | ||||||
|         form, .output-box { background: #fff; padding: 1rem; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } |             --border-color: #ddd; | ||||||
|         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; } |             --success-color: #28a745; | ||||||
|         textarea { height: 200px; } |             --error-color: #dc3545; | ||||||
|         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; } |             --warning-color: #ffc107; | ||||||
|         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; } |         body { | ||||||
|         .success { color: #28a745; background: #d4edda; padding: 0.6rem; border-radius: 6px; } |             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> |     </style> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
| 
 | <div id="app"> | ||||||
| <div class="container"> |  | ||||||
|     <!-- 基本结构:图片作为超链接内容 --> |     <!-- 基本结构:图片作为超链接内容 --> | ||||||
|     <a href="/" > |     <a href="/"> | ||||||
|       <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <a href="/a" > |     <a href="/a"> | ||||||
|       <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <a href="/b" > |     <a href="/"> | ||||||
|       <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <header><h1>SQL格式化工具</h1></header> |     <header><h1>转odps外部表 SQL格式化工具</h1></header> | ||||||
|     <form id="sqlForm"> | 
 | ||||||
|         <textarea id="sqlInput" placeholder="Enter your SQL here..."></textarea> |     <div class="container"> | ||||||
|         <label for="hologresConnection">Hologres Connection Info:</label> |         <h2>输入 SQL</h2> | ||||||
|         <input type="text" id="hologresConnection" name="hologresConnection" value="hgprecn-cn-i7m2ssubq004-cn-hangzhou-internal.hologres.aliyuncs.com:80" placeholder="Enter Hologres connection info..."> |         <div ref="sqlInput"></div> | ||||||
|         <button type="submit">Convert</button> |     </div> | ||||||
|     </form> | 
 | ||||||
|     <div id="resultArea" class="output-box" style="display: none;"></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> | </div> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
|     document.getElementById('sqlForm').addEventListener('submit', async (e) => { |     const {createApp, ref, onMounted, computed} = Vue; | ||||||
|         e.preventDefault(); |  | ||||||
|         const resultArea = document.getElementById('resultArea'); |  | ||||||
|         resultArea.style.display = 'block'; |  | ||||||
|         resultArea.querySelector('.output-box')?.remove(); |  | ||||||
| 
 | 
 | ||||||
|         const sql = encodeURIComponent(document.getElementById('sqlInput').value); |     const API_CONFIG = { | ||||||
|         const connection = encodeURIComponent(document.getElementById('hologresConnection').value); |         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;" | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|         try { |     const createEditorConfig = (readOnly = false) => ({ | ||||||
|             const response = await fetch('/convert', { |         mode: "sql", | ||||||
|                 method: 'POST', |         lineNumbers: true, | ||||||
|                 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |         theme: "monokai", | ||||||
|                 body: `sql=${sql}&hologresConnection=${connection}` |         readOnly, | ||||||
|             }); |         extraKeys: {"Ctrl-Space": "autocomplete"}, | ||||||
|             const data = await response.json(); |         hintOptions: { | ||||||
| 
 |             completeSingle: false, | ||||||
|             if (data.error) { |             tables: { | ||||||
|                 const div = document.createElement('div'); |                 users: ["id", "name", "age"], | ||||||
|                 div.className = 'output-box error'; |                 products: ["id", "name", "price"] | ||||||
|                 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); |  | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     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 { | ||||||
|  |                     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> | </script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
| @ -4,10 +4,10 @@ | |||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <title>SQL 处理器</title> |     <title>SQL 处理器</title> | ||||||
|     <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> |     <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="/css/codemirror.min.css"> | ||||||
|     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css"> |     <link rel="stylesheet" href="/css/theme/monokai.min.css"> | ||||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script> |     <script src="/css/codemirror.min.js"></script> | ||||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js"></script> |     <script src="/css/mode/sql/sql.min.js"></script> | ||||||
|     <style> |     <style> | ||||||
|         :root { |         :root { | ||||||
|             --primary-color: #4a90e2; |             --primary-color: #4a90e2; | ||||||
| @ -75,7 +75,41 @@ | |||||||
|             cursor: not-allowed; |             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> |     </style> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
| @ -87,9 +121,8 @@ | |||||||
|     <a href="/a"> |     <a href="/a"> | ||||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <a href="/b"> |     <a href="/"> | ||||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> | ||||||
|              height="50"> |  | ||||||
|     </a> |     </a> | ||||||
|     <header><h1>SQL格式化工具</h1></header> |     <header><h1>SQL格式化工具</h1></header> | ||||||
| 
 | 
 | ||||||
| @ -99,11 +132,14 @@ | |||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div class="buttons"> |     <div class="buttons"> | ||||||
|         <button @click="insertSample" title="插入示例语句">示例</button> |  | ||||||
|         <button @click="clearInput" title="清空输入框">清空</button> |         <button @click="clearInput" title="清空输入框">清空</button> | ||||||
|         <button @click="formatSQL" :disabled="isProcessing">格式化</button> |         <button @click="formatSQL" :disabled="isProcessing">格式化</button> | ||||||
|         <button @click="copyToClipboard">复制</button> |         <button @click="copyToClipboard">复制</button> | ||||||
|  |         <button @click="insertSample" title="插入示例语句">示例</button> | ||||||
|     </div> |     </div> | ||||||
|  | 
 | ||||||
|  |     <div class="status-message" :class="statusClass">{{ statusMessage }}</div> | ||||||
|  | 
 | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|         <h2>输出 SQL</h2> |         <h2>输出 SQL</h2> | ||||||
|         <div ref="sqlOutput"></div> |         <div ref="sqlOutput"></div> | ||||||
| @ -114,7 +150,7 @@ | |||||||
|     const {createApp, ref, onMounted, computed} = Vue; |     const {createApp, ref, onMounted, computed} = Vue; | ||||||
| 
 | 
 | ||||||
|     const API_CONFIG = { |     const API_CONFIG = { | ||||||
|         ENDPOINT: 'http://10.23.0.209:8778/convert', |         ENDPOINT: '/convert', | ||||||
|         DEFAULT_HEADERS: { |         DEFAULT_HEADERS: { | ||||||
|             'Content-Type': 'application/x-www-form-urlencoded' |             'Content-Type': 'application/x-www-form-urlencoded' | ||||||
|         }, |         }, | ||||||
| @ -142,6 +178,8 @@ | |||||||
|             const sqlInput = ref(null); |             const sqlInput = ref(null); | ||||||
|             const sqlOutput = ref(null); |             const sqlOutput = ref(null); | ||||||
|             const isProcessing = ref(false); |             const isProcessing = ref(false); | ||||||
|  |             const statusMessage = ref(''); | ||||||
|  |             const statusType = ref(''); | ||||||
|             let editorInput = null; |             let editorInput = null; | ||||||
|             let editorOutput = null; |             let editorOutput = null; | ||||||
| 
 | 
 | ||||||
| @ -149,6 +187,24 @@ | |||||||
|                 return editorOutput ? editorOutput.getValue().trim() : ''; |                 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(() => { |             onMounted(() => { | ||||||
|                 if (sqlInput.value && sqlOutput.value) { |                 if (sqlInput.value && sqlOutput.value) { | ||||||
|                     editorInput = CodeMirror(sqlInput.value, { |                     editorInput = CodeMirror(sqlInput.value, { | ||||||
| @ -165,20 +221,25 @@ | |||||||
| 
 | 
 | ||||||
|             const insertSample = () => { |             const insertSample = () => { | ||||||
|                 editorInput.setValue(API_CONFIG.SAMPLE_SQL); |                 editorInput.setValue(API_CONFIG.SAMPLE_SQL); | ||||||
|  |                 setStatus('示例语句已插入', 'success'); | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             const clearInput = () => { |             const clearInput = () => { | ||||||
|                 editorInput.setValue(''); |                 editorInput.setValue(''); | ||||||
|  |                 setStatus('输入已清空', 'info'); | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|             const formatSQL = async () => { |             const formatSQL = async () => { | ||||||
|                 const inputSQL = editorInput.getValue().trim(); |                 const inputSQL = editorInput.getValue().trim(); | ||||||
|                 if (!inputSQL) { |                 if (!inputSQL) { | ||||||
|  |                     setStatus('请输入要格式化的SQL内容', 'warning'); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 try { |                 try { | ||||||
|                     isProcessing.value = true; |                     isProcessing.value = true; | ||||||
|  |                     setStatus('正在格式化SQL...', 'info', 0); | ||||||
|  | 
 | ||||||
|                     const startTime = Date.now(); |                     const startTime = Date.now(); | ||||||
|                     const response = await fetch(API_CONFIG.ENDPOINT, { |                     const response = await fetch(API_CONFIG.ENDPOINT, { | ||||||
|                         method: 'POST', |                         method: 'POST', | ||||||
| @ -198,9 +259,11 @@ | |||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     editorOutput.setValue(data.parsed_result); |                     editorOutput.setValue(data.parsed_result); | ||||||
|  |                     setStatus(`格式化成功 (${duration}ms)`, 'success'); | ||||||
|                 } catch (error) { |                 } catch (error) { | ||||||
|                     console.error('格式化错误:', error); |                     console.error('格式化错误:', error); | ||||||
|                     editorOutput.setValue(''); |                     editorOutput.setValue(''); | ||||||
|  |                     setStatus(`格式化失败: ${error.message}`, 'error'); | ||||||
|                 } finally { |                 } finally { | ||||||
|                     isProcessing.value = false; |                     isProcessing.value = false; | ||||||
|                 } |                 } | ||||||
| @ -210,11 +273,13 @@ | |||||||
|                 try { |                 try { | ||||||
|                     const content = editorOutput.getValue().trim(); |                     const content = editorOutput.getValue().trim(); | ||||||
|                     if (!content) { |                     if (!content) { | ||||||
|  |                         setStatus('无内容可复制', 'warning'); | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     try { |                     try { | ||||||
|                         await navigator.clipboard.writeText(content); |                         await navigator.clipboard.writeText(content); | ||||||
|  |                         setStatus('内容已复制到剪贴板', 'success'); | ||||||
|                     } catch (err) { |                     } catch (err) { | ||||||
|                         // 回退到传统复制方式 |                         // 回退到传统复制方式 | ||||||
|                         const textarea = document.createElement('textarea'); |                         const textarea = document.createElement('textarea'); | ||||||
| @ -226,9 +291,11 @@ | |||||||
|                         document.body.removeChild(textarea); |                         document.body.removeChild(textarea); | ||||||
| 
 | 
 | ||||||
|                         if (!success) throw new Error('传统复制方式失败'); |                         if (!success) throw new Error('传统复制方式失败'); | ||||||
|  |                         setStatus('内容已复制(传统方式)', 'success'); | ||||||
|                     } |                     } | ||||||
|                 } catch (error) { |                 } catch (error) { | ||||||
|                     console.error('复制失败:', error); |                     console.error('复制失败:', error); | ||||||
|  |                     setStatus(`复制失败: ${error.message}`, 'error'); | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
| @ -236,6 +303,8 @@ | |||||||
|                 sqlInput, |                 sqlInput, | ||||||
|                 sqlOutput, |                 sqlOutput, | ||||||
|                 isProcessing, |                 isProcessing, | ||||||
|  |                 statusMessage, | ||||||
|  |                 statusClass, | ||||||
|                 outputContent, |                 outputContent, | ||||||
|                 insertSample, |                 insertSample, | ||||||
|                 clearInput, |                 clearInput, | ||||||
| @ -246,4 +315,4 @@ | |||||||
|     }).mount('#app'); |     }).mount('#app'); | ||||||
| </script> | </script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
							
								
								
									
										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"> |     <meta charset="UTF-8"> | ||||||
|     <title>SQL 处理器</title> |     <title>SQL 处理器</title> | ||||||
|     <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> |     <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="/css/codemirror.min.css"> | ||||||
|     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css"> |     <link rel="stylesheet" href="/css/monokai.min.css"> | ||||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script> |     <script src="/css/codemirror.min.js"></script> | ||||||
|     <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js"></script> |     <script src="/css/sql.min.js"></script> | ||||||
|     <style> |     <style> | ||||||
|         :root { |         :root { | ||||||
|             --primary-color: #4a90e2; |             --primary-color: #4a90e2; | ||||||
| @ -118,13 +118,13 @@ | |||||||
|     <a href="/"> |     <a href="/"> | ||||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/bianmu.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <a href="/"> |     <a href="/a"> | ||||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/buoumao.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <a href="/"> |     <a href="/"> | ||||||
|         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> |         <img src="https://yinzhou.oss-cn-shanghai.aliyuncs.com/image/chaiquan.png" width="50" height="50"> | ||||||
|     </a> |     </a> | ||||||
|     <header><h1>SQL格式化工具</h1></header> |     <header><h1>转doirs SQL格式化工具</h1></header> | ||||||
| 
 | 
 | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|         <h2>输入 SQL</h2> |         <h2>输入 SQL</h2> | ||||||
| @ -150,7 +150,7 @@ | |||||||
|     const {createApp, ref, onMounted, computed} = Vue; |     const {createApp, ref, onMounted, computed} = Vue; | ||||||
| 
 | 
 | ||||||
|     const API_CONFIG = { |     const API_CONFIG = { | ||||||
|         ENDPOINT: '/convert', |         ENDPOINT: '/convert1', | ||||||
|         DEFAULT_HEADERS: { |         DEFAULT_HEADERS: { | ||||||
|             'Content-Type': 'application/x-www-form-urlencoded' |             'Content-Type': 'application/x-www-form-urlencoded' | ||||||
|         }, |         }, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user