|
|
@@ -139,7 +139,7 @@
|
|
139
|
139
|
}
|
|
140
|
140
|
|
|
141
|
141
|
.sql-editor-right {
|
|
142
|
|
- width: 300px;
|
|
|
142
|
+ width: 400px;
|
|
143
|
143
|
display: flex;
|
|
144
|
144
|
flex-direction: column;
|
|
145
|
145
|
border-left: 1px solid #EEEEEE;
|
|
|
@@ -160,20 +160,38 @@
|
|
160
|
160
|
border-bottom: 1px solid #EEEEEE;
|
|
161
|
161
|
cursor: pointer;
|
|
162
|
162
|
display: flex;
|
|
163
|
|
- flex-direction: column;
|
|
|
163
|
+ flex-direction: row;
|
|
|
164
|
+ justify-content: space-between;
|
|
164
|
165
|
}
|
|
165
|
166
|
|
|
166
|
167
|
.column-item:hover {
|
|
167
|
168
|
background-color: #F0F7FF;
|
|
168
|
169
|
}
|
|
169
|
170
|
|
|
|
171
|
+ .column-left {
|
|
|
172
|
+ display: flex;
|
|
|
173
|
+ flex-direction: column;
|
|
|
174
|
+ flex: 1;
|
|
|
175
|
+ }
|
|
|
176
|
+
|
|
|
177
|
+ .column-right {
|
|
|
178
|
+ display: flex;
|
|
|
179
|
+ align-items: center;
|
|
|
180
|
+ min-width: 100px;
|
|
|
181
|
+ padding-left: 10px;
|
|
|
182
|
+ }
|
|
|
183
|
+
|
|
170
|
184
|
.column-name {
|
|
171
|
185
|
font-weight: bold;
|
|
172
|
186
|
}
|
|
173
|
187
|
|
|
174
|
188
|
.column-type {
|
|
175
|
|
- font-size: 12px;
|
|
|
189
|
+ font-size: 13px;
|
|
176
|
190
|
color: #666;
|
|
|
191
|
+ background-color: #f5f5f5;
|
|
|
192
|
+ padding: 2px 6px;
|
|
|
193
|
+ border-radius: 3px;
|
|
|
194
|
+ white-space: nowrap;
|
|
177
|
195
|
}
|
|
178
|
196
|
|
|
179
|
197
|
.column-comment {
|
|
|
@@ -401,6 +419,7 @@
|
|
401
|
419
|
background: white;
|
|
402
|
420
|
border-radius: 4px;
|
|
403
|
421
|
cursor: pointer;
|
|
|
422
|
+ overflow-x: hidden;
|
|
404
|
423
|
transition: background-color 0.2s;
|
|
405
|
424
|
}
|
|
406
|
425
|
|
|
|
@@ -518,6 +537,11 @@
|
|
518
|
537
|
<div class="sql-editor-left">
|
|
519
|
538
|
<textarea class="sql-textarea" v-model="sqlQuery" placeholder="输入SQL查询语句..."></textarea>
|
|
520
|
539
|
<div class="btn-group">
|
|
|
540
|
+ <select class="btn btn-default" v-model="selectedLimit" @change="updateQueryLimit" style="margin-right: 8px;">
|
|
|
541
|
+ <option v-for="limit in limitOptions" :key="limit" :value="limit">
|
|
|
542
|
+ LIMIT {{ limit }}
|
|
|
543
|
+ </option>
|
|
|
544
|
+ </select>
|
|
521
|
545
|
<button type="button" class="btn btn-default" @click="clearQuery">清空</button>
|
|
522
|
546
|
<button type="button" class="btn btn-primary" @click="executeQuery">执行</button>
|
|
523
|
547
|
</div>
|
|
|
@@ -531,12 +555,17 @@
|
|
531
|
555
|
<div class="columns-list">
|
|
532
|
556
|
<div v-if="tableColumnsList && tableColumnsList.length > 0">
|
|
533
|
557
|
<div v-for="(column, index) in tableColumnsList"
|
|
534
|
|
- :key="index"
|
|
535
|
|
- class="column-item"
|
|
536
|
|
- @click="insertColumnName(column.name)">
|
|
537
|
|
- <div class="column-name">{{ column.name }}</div>
|
|
538
|
|
- <div class="column-type">{{ column.type }}</div>
|
|
539
|
|
- <div class="column-comment" v-if="column.comment">{{ column.comment }}</div>
|
|
|
558
|
+ :key="index"
|
|
|
559
|
+ class="column-item"
|
|
|
560
|
+ @click="copyColumnName(column.name)"
|
|
|
561
|
+ @dblclick="insertColumnName(column.name)">
|
|
|
562
|
+ <div class="column-left">
|
|
|
563
|
+ <div class="column-name">{{ column.name }}</div>
|
|
|
564
|
+ <div class="column-comment" v-if="column.comment">{{ column.comment }}</div>
|
|
|
565
|
+ </div>
|
|
|
566
|
+ <div class="column-right">
|
|
|
567
|
+ <div class="column-type">{{ column.type }}</div>
|
|
|
568
|
+ </div>
|
|
540
|
569
|
</div>
|
|
541
|
570
|
</div>
|
|
542
|
571
|
<div v-else class="no-data">
|
|
|
@@ -636,7 +665,9 @@
|
|
636
|
665
|
pageSize: 100,
|
|
637
|
666
|
totalPages: 1,
|
|
638
|
667
|
allResults: [],
|
|
639
|
|
- tableColumnsList: [] // 存储表格字段列表
|
|
|
668
|
+ tableColumnsList: [], // 存储表格字段列表
|
|
|
669
|
+ limitOptions: [10, 100, 500, 1000, 2000, 3000], // LIMIT选项
|
|
|
670
|
+ selectedLimit: 100 // 默认选中的LIMIT值
|
|
640
|
671
|
},
|
|
641
|
672
|
computed: {
|
|
642
|
673
|
displayedPages() {
|
|
|
@@ -1005,7 +1036,42 @@
|
|
1005
|
1036
|
this.sqlQuery = '';
|
|
1006
|
1037
|
},
|
|
1007
|
1038
|
|
|
1008
|
|
- // 插入字段名称到SQL查询
|
|
|
1039
|
+ // 复制字段名称到剪贴板
|
|
|
1040
|
+ copyColumnName(columnName) {
|
|
|
1041
|
+ // 创建一个临时文本区域元素
|
|
|
1042
|
+ const textarea = document.createElement('textarea');
|
|
|
1043
|
+ textarea.value = columnName;
|
|
|
1044
|
+ textarea.setAttribute('readonly', '');
|
|
|
1045
|
+ textarea.style.position = 'absolute';
|
|
|
1046
|
+ textarea.style.left = '-9999px';
|
|
|
1047
|
+ document.body.appendChild(textarea);
|
|
|
1048
|
+
|
|
|
1049
|
+ // 选择文本并复制
|
|
|
1050
|
+ textarea.select();
|
|
|
1051
|
+ let success = false;
|
|
|
1052
|
+ try {
|
|
|
1053
|
+ success = document.execCommand('copy');
|
|
|
1054
|
+ this.showToastMessage(`已复制字段 "${columnName}" 到剪贴板`, 'success');
|
|
|
1055
|
+ } catch (err) {
|
|
|
1056
|
+ success = false;
|
|
|
1057
|
+ this.showToastMessage('复制失败,请手动复制', 'error');
|
|
|
1058
|
+ console.error('复制到剪贴板失败:', err);
|
|
|
1059
|
+ }
|
|
|
1060
|
+
|
|
|
1061
|
+ // 移除临时元素
|
|
|
1062
|
+ document.body.removeChild(textarea);
|
|
|
1063
|
+
|
|
|
1064
|
+ // 如果浏览器支持现代剪贴板API,也尝试使用它
|
|
|
1065
|
+ if (navigator.clipboard && window.isSecureContext) {
|
|
|
1066
|
+ navigator.clipboard.writeText(columnName).catch(err => {
|
|
|
1067
|
+ console.log('剪贴板API失败,但已使用备用方法', err);
|
|
|
1068
|
+ });
|
|
|
1069
|
+ }
|
|
|
1070
|
+
|
|
|
1071
|
+ return success;
|
|
|
1072
|
+ },
|
|
|
1073
|
+
|
|
|
1074
|
+ // 插入字段名称到SQL查询(双击时触发)
|
|
1009
|
1075
|
insertColumnName(columnName) {
|
|
1010
|
1076
|
// 获取文本框元素
|
|
1011
|
1077
|
const textarea = document.querySelector('.sql-textarea');
|
|
|
@@ -1029,12 +1095,42 @@
|
|
1029
|
1095
|
textarea.selectionStart = startPos + columnName.length;
|
|
1030
|
1096
|
textarea.selectionEnd = startPos + columnName.length;
|
|
1031
|
1097
|
});
|
|
|
1098
|
+
|
|
|
1099
|
+ this.showToastMessage(`已插入字段 "${columnName}" 到查询`, 'info');
|
|
1032
|
1100
|
} else {
|
|
1033
|
1101
|
// 如果无法获取文本框元素,则直接在末尾添加
|
|
1034
|
1102
|
this.sqlQuery += ' ' + columnName;
|
|
1035
|
1103
|
}
|
|
1036
|
1104
|
},
|
|
1037
|
1105
|
|
|
|
1106
|
+ // 更新SQL查询中的LIMIT值
|
|
|
1107
|
+ updateQueryLimit() {
|
|
|
1108
|
+ // 检查SQL查询是否为空
|
|
|
1109
|
+ if (!this.sqlQuery.trim()) {
|
|
|
1110
|
+ return;
|
|
|
1111
|
+ }
|
|
|
1112
|
+
|
|
|
1113
|
+ // 正则表达式匹配LIMIT子句
|
|
|
1114
|
+ const limitRegex = /\bLIMIT\s+\d+\s*;?\s*$/i;
|
|
|
1115
|
+
|
|
|
1116
|
+ if (limitRegex.test(this.sqlQuery)) {
|
|
|
1117
|
+ // 如果已经有LIMIT子句,替换数字
|
|
|
1118
|
+ this.sqlQuery = this.sqlQuery.replace(limitRegex, `LIMIT ${this.selectedLimit}${this.sqlQuery.endsWith(';') ? ';' : ''}`);
|
|
|
1119
|
+ } else {
|
|
|
1120
|
+ // 如果没有LIMIT子句,添加到末尾
|
|
|
1121
|
+ // 检查SQL是否以分号结尾
|
|
|
1122
|
+ if (this.sqlQuery.trim().endsWith(';')) {
|
|
|
1123
|
+ // 在分号前添加LIMIT
|
|
|
1124
|
+ this.sqlQuery = this.sqlQuery.replace(/;\s*$/, ` LIMIT ${this.selectedLimit};`);
|
|
|
1125
|
+ } else {
|
|
|
1126
|
+ // 直接在末尾添加LIMIT
|
|
|
1127
|
+ this.sqlQuery = this.sqlQuery.trim() + ` LIMIT ${this.selectedLimit};`;
|
|
|
1128
|
+ }
|
|
|
1129
|
+ }
|
|
|
1130
|
+
|
|
|
1131
|
+ this.showToastMessage(`已设置查询限制为 ${this.selectedLimit} 条记录`, 'info');
|
|
|
1132
|
+ },
|
|
|
1133
|
+
|
|
1038
|
1134
|
// 执行查询
|
|
1039
|
1135
|
executeQuery() {
|
|
1040
|
1136
|
if (!this.sqlQuery.trim()) {
|