|
|
@@ -223,16 +223,21 @@
|
|
223
|
223
|
overflow: auto;
|
|
224
|
224
|
padding: 20px;
|
|
225
|
225
|
background: white;
|
|
|
226
|
+ position: relative;
|
|
226
|
227
|
}
|
|
227
|
228
|
|
|
228
|
229
|
.table-container {
|
|
229
|
230
|
width: 100%;
|
|
230
|
231
|
overflow-x: auto;
|
|
|
232
|
+ max-height: calc(100vh - 380px); /* 减去其他元素的高度 */
|
|
|
233
|
+ overflow-y: auto;
|
|
|
234
|
+ position: relative; /* 为内部元素提供定位上下文 */
|
|
231
|
235
|
}
|
|
232
|
236
|
|
|
233
|
237
|
.data-table {
|
|
234
|
238
|
width: 100%;
|
|
235
|
|
- border-collapse: collapse;
|
|
|
239
|
+ border-collapse: separate; /* 改为separate以支持边框样式 */
|
|
|
240
|
+ border-spacing: 0; /* 消除单元格间距 */
|
|
236
|
241
|
font-size: 14px;
|
|
237
|
242
|
}
|
|
238
|
243
|
|
|
|
@@ -244,6 +249,21 @@
|
|
244
|
249
|
position: sticky;
|
|
245
|
250
|
top: 0;
|
|
246
|
251
|
z-index: 10;
|
|
|
252
|
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
|
|
|
253
|
+ }
|
|
|
254
|
+
|
|
|
255
|
+ /* 确保表头单元格背景色完整覆盖 */
|
|
|
256
|
+ .data-table thead th {
|
|
|
257
|
+ background: #F5F5F5;
|
|
|
258
|
+ }
|
|
|
259
|
+
|
|
|
260
|
+ /* 优化表头在滚动时的视觉效果 */
|
|
|
261
|
+ .table-container:not(:hover) .data-table th {
|
|
|
262
|
+ transition: box-shadow 0.3s ease;
|
|
|
263
|
+ }
|
|
|
264
|
+
|
|
|
265
|
+ .table-container:hover .data-table th {
|
|
|
266
|
+ box-shadow: 0 3px 5px -2px rgba(0, 0, 0, 0.2);
|
|
247
|
267
|
}
|
|
248
|
268
|
|
|
249
|
269
|
.data-table td {
|
|
|
@@ -481,6 +501,77 @@
|
|
481
|
501
|
color: #CCCCCC;
|
|
482
|
502
|
cursor: not-allowed;
|
|
483
|
503
|
}
|
|
|
504
|
+
|
|
|
505
|
+ /* 完整内容弹出层样式 - 改进版 */
|
|
|
506
|
+ .complete-content-overlay {
|
|
|
507
|
+ position: fixed;
|
|
|
508
|
+ top: 0;
|
|
|
509
|
+ left: 0;
|
|
|
510
|
+ right: 0;
|
|
|
511
|
+ bottom: 0;
|
|
|
512
|
+ display: flex;
|
|
|
513
|
+ justify-content: center;
|
|
|
514
|
+ align-items: center;
|
|
|
515
|
+ z-index: 1000;
|
|
|
516
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
517
|
+ pointer-events: auto;
|
|
|
518
|
+ }
|
|
|
519
|
+
|
|
|
520
|
+ .complete-content-container {
|
|
|
521
|
+ width: 80%;
|
|
|
522
|
+ max-width: 800px;
|
|
|
523
|
+ max-height: 80vh;
|
|
|
524
|
+ background-color: white;
|
|
|
525
|
+ border-radius: 8px;
|
|
|
526
|
+ padding: 20px;
|
|
|
527
|
+ padding-top: 50px; /* 为固定的关闭按钮留出空间 */
|
|
|
528
|
+ position: relative;
|
|
|
529
|
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
|
530
|
+ overflow: auto;
|
|
|
531
|
+ animation: fadeIn 0.3s ease-out;
|
|
|
532
|
+ }
|
|
|
533
|
+
|
|
|
534
|
+ @keyframes fadeIn {
|
|
|
535
|
+ from {
|
|
|
536
|
+ opacity: 0;
|
|
|
537
|
+ transform: translateY(20px);
|
|
|
538
|
+ }
|
|
|
539
|
+ to {
|
|
|
540
|
+ opacity: 1;
|
|
|
541
|
+ transform: translateY(0);
|
|
|
542
|
+ }
|
|
|
543
|
+ }
|
|
|
544
|
+
|
|
|
545
|
+ .close-btn {
|
|
|
546
|
+ position: absolute;
|
|
|
547
|
+ right: 15px; /* 根据弹窗宽度计算位置 */
|
|
|
548
|
+ top: 15px; /* 根据弹窗位置计算 */
|
|
|
549
|
+ z-index: 1010; /* 确保按钮在最上层 */
|
|
|
550
|
+ width: 40px;
|
|
|
551
|
+ height: 40px;
|
|
|
552
|
+ display: flex;
|
|
|
553
|
+ align-items: center;
|
|
|
554
|
+ justify-content: center;
|
|
|
555
|
+ background: rgba(255, 255, 255, 0.9);
|
|
|
556
|
+ border: none;
|
|
|
557
|
+ border-radius: 50%;
|
|
|
558
|
+ font-size: 24px;
|
|
|
559
|
+ cursor: pointer;
|
|
|
560
|
+ color: #999;
|
|
|
561
|
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
|
|
562
|
+ }
|
|
|
563
|
+
|
|
|
564
|
+ .close-btn:hover {
|
|
|
565
|
+ color: #333;
|
|
|
566
|
+ background: #fff;
|
|
|
567
|
+ }
|
|
|
568
|
+
|
|
|
569
|
+ .complete-content {
|
|
|
570
|
+ white-space: pre-wrap;
|
|
|
571
|
+ word-break: break-word;
|
|
|
572
|
+ font-family: monospace;
|
|
|
573
|
+ line-height: 1.5;
|
|
|
574
|
+ }
|
|
484
|
575
|
</style>
|
|
485
|
576
|
</head>
|
|
486
|
577
|
|
|
|
@@ -610,7 +701,9 @@
|
|
610
|
701
|
:key="'tr-'+rowIndex">
|
|
611
|
702
|
<td v-for="(column, colIndex) in tableColumns"
|
|
612
|
703
|
:key="'td-'+rowIndex+'-'+colIndex"
|
|
613
|
|
- v-show="!column.startsWith('__')">
|
|
|
704
|
+ v-show="!column.startsWith('__')"
|
|
|
705
|
+ @dblclick="formatTableCell(row[column]).endsWith('...') ? showCompleteField(row, column) : copyColumnName(formatTableCell(row[column]))"
|
|
|
706
|
+ >
|
|
614
|
707
|
{{ formatTableCell(row[column]) }}
|
|
615
|
708
|
</td>
|
|
616
|
709
|
</tr>
|
|
|
@@ -641,6 +734,16 @@
|
|
641
|
734
|
|
|
642
|
735
|
<!-- Toast提示 -->
|
|
643
|
736
|
<div class="toast" :class="{ show: showToast, [toastType]: showToast }">{{ toastMessage }}</div>
|
|
|
737
|
+
|
|
|
738
|
+ <!-- 完整内容弹出层 -->
|
|
|
739
|
+ <div v-if="showCompleteContent" class="complete-content-overlay" @click.self="closeCompleteContent">
|
|
|
740
|
+ <div class="complete-content-container">
|
|
|
741
|
+ <button class="close-btn" @click="closeCompleteContent">×</button>
|
|
|
742
|
+ <div class="complete-content" @dblclick="copyToClipboard(completeContent)">
|
|
|
743
|
+ {{ completeContent }}
|
|
|
744
|
+ </div>
|
|
|
745
|
+ </div>
|
|
|
746
|
+ </div>
|
|
644
|
747
|
</div>
|
|
645
|
748
|
|
|
646
|
749
|
<script>
|
|
|
@@ -665,7 +768,11 @@
|
|
665
|
768
|
allResults: [],
|
|
666
|
769
|
tableColumnsList: [], // 存储表格字段列表
|
|
667
|
770
|
limitOptions: [10, 100, 500, 1000, 2000, 3000], // LIMIT选项
|
|
668
|
|
- selectedLimit: 100 // 默认选中的LIMIT值
|
|
|
771
|
+ selectedLimit: 100, // 默认选中的LIMIT值
|
|
|
772
|
+ showCompleteContent: false,
|
|
|
773
|
+ completeContent: '',
|
|
|
774
|
+ currentRow: null,
|
|
|
775
|
+ currentColumn: null
|
|
669
|
776
|
},
|
|
670
|
777
|
computed: {
|
|
671
|
778
|
|
|
|
@@ -824,10 +931,19 @@
|
|
824
|
931
|
// 选择表格
|
|
825
|
932
|
selectTable(table) {
|
|
826
|
933
|
this.selectedTable = table;
|
|
827
|
|
- this.sqlQuery = `SELECT * FROM ${table} LIMIT 100;`;
|
|
828
|
|
-
|
|
|
934
|
+
|
|
829
|
935
|
// 获取表格字段列表
|
|
830
|
936
|
this.loadTableColumns(table);
|
|
|
937
|
+
|
|
|
938
|
+ // 监听字段列表变化,当加载完成后构建查询语句
|
|
|
939
|
+ this.$watch('tableColumnsList', (newVal) => {
|
|
|
940
|
+ if (newVal && newVal.length > 0) {
|
|
|
941
|
+ const columns = newVal.map(col => `\`${col.name}\``).join(',');
|
|
|
942
|
+ this.sqlQuery = `SELECT ${columns} FROM \`${table}\` LIMIT 100;`;
|
|
|
943
|
+ } else {
|
|
|
944
|
+ this.sqlQuery = `SELECT * FROM \`${table}\` LIMIT 100;`;
|
|
|
945
|
+ }
|
|
|
946
|
+ }, {immediate: true});
|
|
831
|
947
|
},
|
|
832
|
948
|
|
|
833
|
949
|
// 加载表格字段列表
|
|
|
@@ -1009,6 +1125,8 @@
|
|
1009
|
1125
|
insertColumnName(columnName) {
|
|
1010
|
1126
|
// 获取文本框元素
|
|
1011
|
1127
|
const textarea = document.querySelector('.sql-textarea');
|
|
|
1128
|
+ // 添加反引号的字段名
|
|
|
1129
|
+ const quotedColumnName = `\`${columnName}\``;
|
|
1012
|
1130
|
|
|
1013
|
1131
|
// 如果文本框存在
|
|
1014
|
1132
|
if (textarea) {
|
|
|
@@ -1021,19 +1139,19 @@
|
|
1021
|
1139
|
const textAfter = this.sqlQuery.substring(endPos);
|
|
1022
|
1140
|
|
|
1023
|
1141
|
// 更新SQL查询
|
|
1024
|
|
- this.sqlQuery = textBefore + columnName + textAfter;
|
|
|
1142
|
+ this.sqlQuery = textBefore + quotedColumnName + textAfter;
|
|
1025
|
1143
|
|
|
1026
|
1144
|
// 设置新的光标位置
|
|
1027
|
1145
|
this.$nextTick(() => {
|
|
1028
|
1146
|
textarea.focus();
|
|
1029
|
|
- textarea.selectionStart = startPos + columnName.length;
|
|
1030
|
|
- textarea.selectionEnd = startPos + columnName.length;
|
|
|
1147
|
+ textarea.selectionStart = startPos + quotedColumnName.length;
|
|
|
1148
|
+ textarea.selectionEnd = startPos + quotedColumnName.length;
|
|
1031
|
1149
|
});
|
|
1032
|
1150
|
|
|
1033
|
1151
|
this.showToastMessage(`已插入字段 "${columnName}" 到查询`, 'info');
|
|
1034
|
1152
|
} else {
|
|
1035
|
1153
|
// 如果无法获取文本框元素,则直接在末尾添加
|
|
1036
|
|
- this.sqlQuery += ' ' + columnName;
|
|
|
1154
|
+ this.sqlQuery += ' ' + quotedColumnName;
|
|
1037
|
1155
|
}
|
|
1038
|
1156
|
},
|
|
1039
|
1157
|
|
|
|
@@ -1236,6 +1354,95 @@
|
|
1236
|
1354
|
return value;
|
|
1237
|
1355
|
},
|
|
1238
|
1356
|
|
|
|
1357
|
+ // 显示完整字段内容
|
|
|
1358
|
+ showCompleteField(row, column) {
|
|
|
1359
|
+ // 检查当前单元格内容是否有省略
|
|
|
1360
|
+ const cellContent = this.formatTableCell(row[column]);
|
|
|
1361
|
+ if (!cellContent.includes('...')) {
|
|
|
1362
|
+ return;
|
|
|
1363
|
+ }
|
|
|
1364
|
+
|
|
|
1365
|
+ this.currentRow = row;
|
|
|
1366
|
+ this.currentColumn = column;
|
|
|
1367
|
+ this.showCompleteContent = true;
|
|
|
1368
|
+ this.completeContent = '加载中...';
|
|
|
1369
|
+
|
|
|
1370
|
+ // 查找主键字段 - 优先使用实际存在的列名
|
|
|
1371
|
+ let primaryKey = null;
|
|
|
1372
|
+
|
|
|
1373
|
+ // 检查当前行是否有id字段
|
|
|
1374
|
+ if ('id' in row) {
|
|
|
1375
|
+ primaryKey = 'id';
|
|
|
1376
|
+ }
|
|
|
1377
|
+ // 检查是否有ID字段(大写)
|
|
|
1378
|
+ else if ('ID' in row) {
|
|
|
1379
|
+ primaryKey = 'ID';
|
|
|
1380
|
+ }
|
|
|
1381
|
+ // 检查表字段列表中是否有id字段
|
|
|
1382
|
+ else if (this.tableColumnsList.some(col => col.name.toLowerCase() === 'id')) {
|
|
|
1383
|
+ primaryKey = 'id';
|
|
|
1384
|
+ }
|
|
|
1385
|
+ // 检查表字段列表中是否有ID字段(大写)
|
|
|
1386
|
+ else if (this.tableColumnsList.some(col => col.name === 'ID')) {
|
|
|
1387
|
+ primaryKey = 'ID';
|
|
|
1388
|
+ }
|
|
|
1389
|
+ // 使用第一个存在的字段作为主键
|
|
|
1390
|
+ else if (this.tableColumnsList.length > 0) {
|
|
|
1391
|
+ const firstCol = this.tableColumnsList[0].name;
|
|
|
1392
|
+ if (firstCol in row) {
|
|
|
1393
|
+ primaryKey = firstCol;
|
|
|
1394
|
+ }
|
|
|
1395
|
+ }
|
|
|
1396
|
+
|
|
|
1397
|
+ if (!primaryKey || !row[primaryKey]) {
|
|
|
1398
|
+ this.completeContent = '无法确定主键字段或主键值为空';
|
|
|
1399
|
+ return;
|
|
|
1400
|
+ }
|
|
|
1401
|
+
|
|
|
1402
|
+ // 构建查询SQL
|
|
|
1403
|
+ const sql = `SELECT \`${column}\` FROM \`${this.selectedTable}\` WHERE \`${primaryKey}\`='${row[primaryKey]}';`;
|
|
|
1404
|
+
|
|
|
1405
|
+ // 调用API获取完整数据
|
|
|
1406
|
+ fetch('/api/RunKylx365DBSql', {
|
|
|
1407
|
+ method: 'POST',
|
|
|
1408
|
+ headers: {
|
|
|
1409
|
+ 'Content-Type': 'application/json',
|
|
|
1410
|
+ },
|
|
|
1411
|
+ body: JSON.stringify({
|
|
|
1412
|
+ sql: sql,
|
|
|
1413
|
+ IsCompleteField: true
|
|
|
1414
|
+ })
|
|
|
1415
|
+ })
|
|
|
1416
|
+ .then(response => response.json())
|
|
|
1417
|
+ .then(data => {
|
|
|
1418
|
+ if (data && data.result && data.result.length > 0) {
|
|
|
1419
|
+ this.completeContent = this.formatTableCell(data.result[0][column]);
|
|
|
1420
|
+ } else {
|
|
|
1421
|
+ this.completeContent = '未获取到完整数据';
|
|
|
1422
|
+ }
|
|
|
1423
|
+ })
|
|
|
1424
|
+ .catch(error => {
|
|
|
1425
|
+ this.completeContent = '获取完整数据失败: ' + error.message;
|
|
|
1426
|
+ });
|
|
|
1427
|
+ },
|
|
|
1428
|
+
|
|
|
1429
|
+ // 关闭完整内容弹出层
|
|
|
1430
|
+ closeCompleteContent() {
|
|
|
1431
|
+ this.showCompleteContent = false;
|
|
|
1432
|
+ this.completeContent = '';
|
|
|
1433
|
+ this.currentRow = null;
|
|
|
1434
|
+ this.currentColumn = null;
|
|
|
1435
|
+ },
|
|
|
1436
|
+
|
|
|
1437
|
+ // 复制到剪贴板
|
|
|
1438
|
+ copyToClipboard(text) {
|
|
|
1439
|
+ navigator.clipboard.writeText(text).then(() => {
|
|
|
1440
|
+ this.showToastMessage('已复制到剪贴板', 'success');
|
|
|
1441
|
+ }).catch(err => {
|
|
|
1442
|
+ this.showToastMessage('复制失败: ' + err, 'error');
|
|
|
1443
|
+ });
|
|
|
1444
|
+ },
|
|
|
1445
|
+
|
|
1239
|
1446
|
// 显示提示消息
|
|
1240
|
1447
|
showToastMessage(message, type = 'info') {
|
|
1241
|
1448
|
this.toastMessage = message;
|