fdb2 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/README.md +9 -2
  2. package/bin/docker/.env +4 -0
  3. package/package.json +26 -18
  4. package/public/favicon.ico +0 -0
  5. package/public/favicon.png +0 -0
  6. package/view/modules/header.tpl +1 -1
  7. package/.dockerignore +0 -21
  8. package/.editorconfig +0 -11
  9. package/.eslintrc.cjs +0 -14
  10. package/.eslintrc.json +0 -7
  11. package/.prettierrc.js +0 -3
  12. package/.tpl.env +0 -22
  13. package/.vscodeignore +0 -45
  14. package/dist/package.json +0 -115
  15. package/dist/pnpm-lock.yaml +0 -8135
  16. package/dist/public/.vite/manifest.json +0 -82
  17. package/dist/public/bootstrap-icons.woff +0 -0
  18. package/dist/public/bootstrap-icons.woff2 +0 -0
  19. package/dist/public/bootstrap.css +0 -14152
  20. package/dist/public/bootstrap.js +0 -5038
  21. package/dist/public/explorer.css +0 -2379
  22. package/dist/public/explorer.js +0 -50586
  23. package/dist/public/index.css +0 -1099
  24. package/dist/public/index.js +0 -12862
  25. package/dist/public/layout.css +0 -318
  26. package/dist/public/layout.js +0 -25
  27. package/dist/public/vue.css +0 -1
  28. package/dist/public/vue.js +0 -9110
  29. package/dist/scripts/preinstall.js +0 -112
  30. package/dist/server/index.d.ts +0 -2
  31. package/dist/server/index.d.ts.map +0 -1
  32. package/dist/server/index.js +0 -601
  33. package/dist/server/index.js.map +0 -1
  34. package/dist/server/index.ts +0 -681
  35. package/dist/server/model/connection.entity.d.ts +0 -55
  36. package/dist/server/model/connection.entity.d.ts.map +0 -1
  37. package/dist/server/model/connection.entity.js +0 -59
  38. package/dist/server/model/connection.entity.js.map +0 -1
  39. package/dist/server/model/connection.entity.ts +0 -66
  40. package/dist/server/model/database.entity.d.ts +0 -203
  41. package/dist/server/model/database.entity.d.ts.map +0 -1
  42. package/dist/server/model/database.entity.js +0 -211
  43. package/dist/server/model/database.entity.js.map +0 -1
  44. package/dist/server/model/database.entity.ts +0 -246
  45. package/dist/server/service/connection.service.d.ts +0 -84
  46. package/dist/server/service/connection.service.d.ts.map +0 -1
  47. package/dist/server/service/connection.service.js +0 -369
  48. package/dist/server/service/connection.service.js.map +0 -1
  49. package/dist/server/service/connection.service.ts +0 -359
  50. package/dist/server/service/database/base.service.d.ts +0 -183
  51. package/dist/server/service/database/base.service.d.ts.map +0 -1
  52. package/dist/server/service/database/base.service.js +0 -253
  53. package/dist/server/service/database/base.service.js.map +0 -1
  54. package/dist/server/service/database/base.service.ts +0 -407
  55. package/dist/server/service/database/cockroachdb.service.d.ts +0 -116
  56. package/dist/server/service/database/cockroachdb.service.d.ts.map +0 -1
  57. package/dist/server/service/database/cockroachdb.service.js +0 -812
  58. package/dist/server/service/database/cockroachdb.service.js.map +0 -1
  59. package/dist/server/service/database/cockroachdb.service.ts +0 -871
  60. package/dist/server/service/database/database.service.d.ts +0 -495
  61. package/dist/server/service/database/database.service.d.ts.map +0 -1
  62. package/dist/server/service/database/database.service.js +0 -711
  63. package/dist/server/service/database/database.service.js.map +0 -1
  64. package/dist/server/service/database/database.service.ts +0 -776
  65. package/dist/server/service/database/index.d.ts +0 -8
  66. package/dist/server/service/database/index.d.ts.map +0 -1
  67. package/dist/server/service/database/index.js +0 -18
  68. package/dist/server/service/database/index.js.map +0 -1
  69. package/dist/server/service/database/index.ts +0 -7
  70. package/dist/server/service/database/mongodb.service.d.ts +0 -121
  71. package/dist/server/service/database/mongodb.service.d.ts.map +0 -1
  72. package/dist/server/service/database/mongodb.service.js +0 -502
  73. package/dist/server/service/database/mongodb.service.js.map +0 -1
  74. package/dist/server/service/database/mongodb.service.ts +0 -501
  75. package/dist/server/service/database/mssql.service.d.ts +0 -118
  76. package/dist/server/service/database/mssql.service.d.ts.map +0 -1
  77. package/dist/server/service/database/mssql.service.js +0 -871
  78. package/dist/server/service/database/mssql.service.js.map +0 -1
  79. package/dist/server/service/database/mssql.service.ts +0 -932
  80. package/dist/server/service/database/mysql.service.d.ts +0 -114
  81. package/dist/server/service/database/mysql.service.d.ts.map +0 -1
  82. package/dist/server/service/database/mysql.service.js +0 -960
  83. package/dist/server/service/database/mysql.service.js.map +0 -1
  84. package/dist/server/service/database/mysql.service.ts +0 -1026
  85. package/dist/server/service/database/oracle.service.d.ts +0 -126
  86. package/dist/server/service/database/oracle.service.d.ts.map +0 -1
  87. package/dist/server/service/database/oracle.service.js +0 -963
  88. package/dist/server/service/database/oracle.service.js.map +0 -1
  89. package/dist/server/service/database/oracle.service.ts +0 -1036
  90. package/dist/server/service/database/postgres.service.d.ts +0 -122
  91. package/dist/server/service/database/postgres.service.d.ts.map +0 -1
  92. package/dist/server/service/database/postgres.service.js +0 -882
  93. package/dist/server/service/database/postgres.service.js.map +0 -1
  94. package/dist/server/service/database/postgres.service.ts +0 -961
  95. package/dist/server/service/database/sap.service.d.ts +0 -115
  96. package/dist/server/service/database/sap.service.d.ts.map +0 -1
  97. package/dist/server/service/database/sap.service.js +0 -868
  98. package/dist/server/service/database/sap.service.js.map +0 -1
  99. package/dist/server/service/database/sap.service.ts +0 -922
  100. package/dist/server/service/database/sqlite.service.d.ts +0 -112
  101. package/dist/server/service/database/sqlite.service.d.ts.map +0 -1
  102. package/dist/server/service/database/sqlite.service.js +0 -723
  103. package/dist/server/service/database/sqlite.service.js.map +0 -1
  104. package/dist/server/service/database/sqlite.service.ts +0 -787
  105. package/dist/server/service/session.service.ts +0 -158
  106. package/dist/view/index.html +0 -45
  107. package/env.d.ts +0 -1
  108. package/packages/vscode/.vscodeignore +0 -44
  109. package/packages/vscode/README.md +0 -62
  110. package/packages/vscode/out/database-services/base.service.js +0 -236
  111. package/packages/vscode/out/database-services/base.service.js.map +0 -1
  112. package/packages/vscode/out/database-services/cockroachdb.service.js +0 -634
  113. package/packages/vscode/out/database-services/cockroachdb.service.js.map +0 -1
  114. package/packages/vscode/out/database-services/connection.service.js +0 -346
  115. package/packages/vscode/out/database-services/connection.service.js.map +0 -1
  116. package/packages/vscode/out/database-services/database.service.js +0 -571
  117. package/packages/vscode/out/database-services/database.service.js.map +0 -1
  118. package/packages/vscode/out/database-services/index.js +0 -18
  119. package/packages/vscode/out/database-services/index.js.map +0 -1
  120. package/packages/vscode/out/database-services/model/connection.entity.js +0 -11
  121. package/packages/vscode/out/database-services/model/connection.entity.js.map +0 -1
  122. package/packages/vscode/out/database-services/model/database.entity.js +0 -35
  123. package/packages/vscode/out/database-services/model/database.entity.js.map +0 -1
  124. package/packages/vscode/out/database-services/mongodb.service.js +0 -458
  125. package/packages/vscode/out/database-services/mongodb.service.js.map +0 -1
  126. package/packages/vscode/out/database-services/mssql.service.js +0 -694
  127. package/packages/vscode/out/database-services/mssql.service.js.map +0 -1
  128. package/packages/vscode/out/database-services/mysql.service.js +0 -735
  129. package/packages/vscode/out/database-services/mysql.service.js.map +0 -1
  130. package/packages/vscode/out/database-services/oracle.service.js +0 -787
  131. package/packages/vscode/out/database-services/oracle.service.js.map +0 -1
  132. package/packages/vscode/out/database-services/postgres.service.js +0 -696
  133. package/packages/vscode/out/database-services/postgres.service.js.map +0 -1
  134. package/packages/vscode/out/database-services/sap.service.js +0 -695
  135. package/packages/vscode/out/database-services/sap.service.js.map +0 -1
  136. package/packages/vscode/out/database-services/sqlite.service.js +0 -532
  137. package/packages/vscode/out/database-services/sqlite.service.js.map +0 -1
  138. package/packages/vscode/out/extension.js +0 -93
  139. package/packages/vscode/out/extension.js.map +0 -1
  140. package/packages/vscode/out/provider/DatabaseTreeProvider.js +0 -159
  141. package/packages/vscode/out/provider/DatabaseTreeProvider.js.map +0 -1
  142. package/packages/vscode/out/provider/WebViewProvider.js +0 -259
  143. package/packages/vscode/out/provider/WebViewProvider.js.map +0 -1
  144. package/packages/vscode/out/service/ConnectionManager.js +0 -105
  145. package/packages/vscode/out/service/ConnectionManager.js.map +0 -1
  146. package/packages/vscode/out/service/DatabaseServiceBridge.js +0 -395
  147. package/packages/vscode/out/service/DatabaseServiceBridge.js.map +0 -1
  148. package/packages/vscode/out/typings/connection.js +0 -3
  149. package/packages/vscode/out/typings/connection.js.map +0 -1
  150. package/packages/vscode/package.json +0 -144
  151. package/packages/vscode/resources/icon.svg +0 -5
  152. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js +0 -6529
  153. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js.map +0 -1
  154. package/packages/vscode/resources/webview/connection.css +0 -69
  155. package/packages/vscode/resources/webview/connection.js +0 -228
  156. package/packages/vscode/resources/webview/connection.js.map +0 -1
  157. package/packages/vscode/resources/webview/database.css +0 -259
  158. package/packages/vscode/resources/webview/database.js +0 -275
  159. package/packages/vscode/resources/webview/database.js.map +0 -1
  160. package/packages/vscode/resources/webview/favicon.ico +0 -0
  161. package/packages/vscode/resources/webview/index.html +0 -9
  162. package/packages/vscode/resources/webview/modules/header.tpl +0 -14
  163. package/packages/vscode/resources/webview/modules/initial_state.tpl +0 -55
  164. package/packages/vscode/resources/webview/query.css +0 -162
  165. package/packages/vscode/resources/webview/query.js +0 -198
  166. package/packages/vscode/resources/webview/query.js.map +0 -1
  167. package/packages/vscode/src/database-services/base.service.js.map +0 -1
  168. package/packages/vscode/src/database-services/base.service.ts +0 -363
  169. package/packages/vscode/src/database-services/cockroachdb.service.js.map +0 -1
  170. package/packages/vscode/src/database-services/cockroachdb.service.ts +0 -659
  171. package/packages/vscode/src/database-services/connection.service.ts +0 -341
  172. package/packages/vscode/src/database-services/database.service.ts +0 -630
  173. package/packages/vscode/src/database-services/index.ts +0 -7
  174. package/packages/vscode/src/database-services/model/connection.entity.js +0 -11
  175. package/packages/vscode/src/database-services/model/connection.entity.js.map +0 -1
  176. package/packages/vscode/src/database-services/model/connection.entity.ts +0 -66
  177. package/packages/vscode/src/database-services/model/database.entity.js +0 -35
  178. package/packages/vscode/src/database-services/model/database.entity.js.map +0 -1
  179. package/packages/vscode/src/database-services/model/database.entity.ts +0 -246
  180. package/packages/vscode/src/database-services/mongodb.service.js.map +0 -1
  181. package/packages/vscode/src/database-services/mongodb.service.ts +0 -454
  182. package/packages/vscode/src/database-services/mssql.service.js.map +0 -1
  183. package/packages/vscode/src/database-services/mssql.service.ts +0 -723
  184. package/packages/vscode/src/database-services/mysql.service.js.map +0 -1
  185. package/packages/vscode/src/database-services/mysql.service.ts +0 -761
  186. package/packages/vscode/src/database-services/oracle.service.js.map +0 -1
  187. package/packages/vscode/src/database-services/oracle.service.ts +0 -832
  188. package/packages/vscode/src/database-services/postgres.service.js.map +0 -1
  189. package/packages/vscode/src/database-services/postgres.service.ts +0 -741
  190. package/packages/vscode/src/database-services/sap.service.js.map +0 -1
  191. package/packages/vscode/src/database-services/sap.service.ts +0 -713
  192. package/packages/vscode/src/database-services/sqlite.service.js.map +0 -1
  193. package/packages/vscode/src/database-services/sqlite.service.ts +0 -558
  194. package/packages/vscode/src/extension.ts +0 -76
  195. package/packages/vscode/src/provider/DatabaseTreeProvider.ts +0 -167
  196. package/packages/vscode/src/provider/WebViewProvider.ts +0 -277
  197. package/packages/vscode/src/service/DatabaseServiceBridge.ts +0 -414
  198. package/packages/vscode/src/typings/connection.ts +0 -90
  199. package/packages/vscode/tsconfig.json +0 -21
  200. package/scripts/preinstall.js +0 -112
  201. package/server/index.ts +0 -681
  202. package/server/model/connection.entity.js +0 -11
  203. package/server/model/connection.entity.js.map +0 -1
  204. package/server/model/connection.entity.ts +0 -66
  205. package/server/model/database.entity.js +0 -35
  206. package/server/model/database.entity.js.map +0 -1
  207. package/server/model/database.entity.ts +0 -246
  208. package/server/service/connection.service.ts +0 -359
  209. package/server/service/database/base.service.ts +0 -407
  210. package/server/service/database/cockroachdb.service.ts +0 -871
  211. package/server/service/database/database.service.ts +0 -776
  212. package/server/service/database/index.ts +0 -7
  213. package/server/service/database/mongodb.service.ts +0 -501
  214. package/server/service/database/mssql.service.ts +0 -932
  215. package/server/service/database/mysql.service.ts +0 -1026
  216. package/server/service/database/oracle.service.ts +0 -1036
  217. package/server/service/database/postgres.service.ts +0 -961
  218. package/server/service/database/sap.service.ts +0 -922
  219. package/server/service/database/sqlite.service.ts +0 -787
  220. package/server/service/session.service.ts +0 -158
  221. package/server/tsconfig.json +0 -20
  222. package/src/adapter/ajax.ts +0 -135
  223. package/src/assets/base.css +0 -1
  224. package/src/assets/database.css +0 -950
  225. package/src/assets/images/collapse.png +0 -0
  226. package/src/assets/images/no-login.png +0 -0
  227. package/src/assets/images/svg/illustrations/illustration-1.svg +0 -1
  228. package/src/assets/images/svg/illustrations/illustration-2.svg +0 -2
  229. package/src/assets/images/svg/illustrations/illustration-3.svg +0 -50
  230. package/src/assets/images/svg/illustrations/illustration-4.svg +0 -1
  231. package/src/assets/images/svg/illustrations/illustration-5.svg +0 -73
  232. package/src/assets/images/svg/illustrations/illustration-6.svg +0 -89
  233. package/src/assets/images/svg/illustrations/illustration-7.svg +0 -39
  234. package/src/assets/images/svg/illustrations/illustration-8.svg +0 -1
  235. package/src/assets/images/svg/separators/curve-2.svg +0 -3
  236. package/src/assets/images/svg/separators/curve.svg +0 -3
  237. package/src/assets/images/svg/separators/line.svg +0 -3
  238. package/src/assets/images/theme/light/screen-1-1000x800.jpg +0 -0
  239. package/src/assets/images/theme/light/screen-2-1000x800.jpg +0 -0
  240. package/src/assets/login/bg.jpg +0 -0
  241. package/src/assets/login/bg.png +0 -0
  242. package/src/assets/login/left.jpg +0 -0
  243. package/src/assets/logo.svg +0 -73
  244. package/src/assets/logo.webp +0 -0
  245. package/src/assets/main.css +0 -1
  246. package/src/base/config.ts +0 -20
  247. package/src/base/detect.ts +0 -134
  248. package/src/base/entity.ts +0 -92
  249. package/src/base/eventBus.ts +0 -37
  250. package/src/components/connection-editor/index.vue +0 -589
  251. package/src/components/dataGrid/index.vue +0 -163
  252. package/src/components/dataGrid/pagination.vue +0 -106
  253. package/src/components/loading/index.vue +0 -43
  254. package/src/components/modal/index.ts +0 -181
  255. package/src/components/modal/index.vue +0 -560
  256. package/src/components/toast/index.ts +0 -44
  257. package/src/components/toast/toast.vue +0 -58
  258. package/src/components/user/name.vue +0 -104
  259. package/src/components/user/selector.vue +0 -416
  260. package/src/domain/SysConfig.ts +0 -74
  261. package/src/platform/App.vue +0 -8
  262. package/src/platform/database/components/connection-detail.vue +0 -1153
  263. package/src/platform/database/components/data-editor.vue +0 -478
  264. package/src/platform/database/components/data-import-export.vue +0 -1602
  265. package/src/platform/database/components/database-detail.vue +0 -1199
  266. package/src/platform/database/components/database-monitor.vue +0 -1086
  267. package/src/platform/database/components/db-tools.vue +0 -1265
  268. package/src/platform/database/components/query-history.vue +0 -1349
  269. package/src/platform/database/components/sql-executor.vue +0 -738
  270. package/src/platform/database/components/sql-query-editor.vue +0 -1046
  271. package/src/platform/database/components/table-data-grid.vue +0 -273
  272. package/src/platform/database/components/table-detail.vue +0 -1173
  273. package/src/platform/database/components/table-editor.vue +0 -917
  274. package/src/platform/database/explorer.vue +0 -1840
  275. package/src/platform/database/index.vue +0 -1193
  276. package/src/platform/database/layout.vue +0 -367
  277. package/src/platform/database/router.ts +0 -37
  278. package/src/platform/database/styles/common.scss +0 -602
  279. package/src/platform/database/types/common.ts +0 -445
  280. package/src/platform/database/utils/export.ts +0 -232
  281. package/src/platform/database/utils/helpers.ts +0 -437
  282. package/src/platform/index.ts +0 -33
  283. package/src/platform/router.ts +0 -41
  284. package/src/platform/vscode/bridge.ts +0 -121
  285. package/src/platform/vscode/components/ConnectionPanel.vue +0 -272
  286. package/src/platform/vscode/components/DatabasePanel.vue +0 -532
  287. package/src/platform/vscode/components/QueryPanel.vue +0 -371
  288. package/src/platform/vscode/entry/connection.ts +0 -13
  289. package/src/platform/vscode/entry/database.ts +0 -13
  290. package/src/platform/vscode/entry/query.ts +0 -13
  291. package/src/platform/vscode/index.ts +0 -5
  292. package/src/service/base.ts +0 -134
  293. package/src/service/database.ts +0 -506
  294. package/src/service/login.ts +0 -121
  295. package/src/shims-vue.d.ts +0 -7
  296. package/src/stores/connection.ts +0 -266
  297. package/src/stores/session.ts +0 -87
  298. package/src/typings/database-types.ts +0 -413
  299. package/src/typings/database.ts +0 -364
  300. package/src/typings/global.d.ts +0 -58
  301. package/src/typings/pinia.d.ts +0 -8
  302. package/src/utils/clipboard.ts +0 -30
  303. package/src/utils/database-types.ts +0 -243
  304. package/src/utils/modal.ts +0 -124
  305. package/src/utils/request.ts +0 -55
  306. package/src/utils/sleep.ts +0 -4
  307. package/src/utils/toast.ts +0 -73
  308. package/src/utils/util.ts +0 -171
  309. package/src/utils/xlsx.ts +0 -228
  310. package/tsconfig.json +0 -33
  311. package/tsconfig.server.json +0 -19
  312. package/vite.config.ts +0 -424
  313. package/vite.config.vscode.ts +0 -47
@@ -1,1349 +0,0 @@
1
- <template>
2
- <div class="query-history">
3
- <div class="history-header">
4
- <div class="header-title">
5
- <i class="bi bi-clock-history"></i>
6
- <span>查询历史</span>
7
- </div>
8
- <div class="header-controls">
9
- <div class="search-box">
10
- <i class="bi bi-search"></i>
11
- <input
12
- type="text"
13
- v-model="searchQuery"
14
- placeholder="搜索查询..."
15
- @input="handleSearch"
16
- >
17
- </div>
18
- <select v-model="filterType" class="filter-select" @change="filterQueries">
19
- <option value="all">所有类型</option>
20
- <option value="SELECT">SELECT</option>
21
- <option value="INSERT">INSERT</option>
22
- <option value="UPDATE">UPDATE</option>
23
- <option value="DELETE">DELETE</option>
24
- <option value="CREATE">CREATE</option>
25
- <option value="ALTER">ALTER</option>
26
- <option value="DROP">DROP</option>
27
- </select>
28
- <button class="btn-clear-history" @click="confirmClearHistory">
29
- <i class="bi bi-trash"></i>
30
- <span>清空历史</span>
31
- </button>
32
- </div>
33
- </div>
34
-
35
- <div class="history-content">
36
- <!-- 统计信息 -->
37
- <div class="stats-section">
38
- <div class="stat-card">
39
- <div class="stat-icon">
40
- <i class="bi bi-clock"></i>
41
- </div>
42
- <div class="stat-info">
43
- <div class="stat-number">{{ totalQueries }}</div>
44
- <div class="stat-label">总查询数</div>
45
- </div>
46
- </div>
47
- <div class="stat-card">
48
- <div class="stat-icon">
49
- <i class="bi bi-lightning"></i>
50
- </div>
51
- <div class="stat-info">
52
- <div class="stat-number">{{ recentQueries.length }}</div>
53
- <div class="stat-label">今日查询</div>
54
- </div>
55
- </div>
56
- <div class="stat-card">
57
- <div class="stat-icon">
58
- <i class="bi bi-star"></i>
59
- </div>
60
- <div class="stat-info">
61
- <div class="stat-number">{{ favoriteQueries.length }}</div>
62
- <div class="stat-label">收藏查询</div>
63
- </div>
64
- </div>
65
- <div class="stat-card">
66
- <div class="stat-icon">
67
- <i class="bi bi-activity"></i>
68
- </div>
69
- <div class="stat-info">
70
- <div class="stat-number">{{ Math.round(averageExecutionTime) }}ms</div>
71
- <div class="stat-label">平均执行时间</div>
72
- </div>
73
- </div>
74
- </div>
75
-
76
- <!-- 快速筛选标签 -->
77
- <div class="quick-filters">
78
- <button
79
- class="filter-tag"
80
- :class="{ active: activeFilter === 'all' }"
81
- @click="setActiveFilter('all')"
82
- >
83
- <i class="bi bi-grid"></i>
84
- <span>全部</span>
85
- </button>
86
- <button
87
- class="filter-tag"
88
- :class="{ active: activeFilter === 'recent' }"
89
- @click="setActiveFilter('recent')"
90
- >
91
- <i class="bi bi-clock"></i>
92
- <span>最近</span>
93
- </button>
94
- <button
95
- class="filter-tag"
96
- :class="{ active: activeFilter === 'favorites' }"
97
- @click="setActiveFilter('favorites')"
98
- >
99
- <i class="bi bi-star"></i>
100
- <span>收藏</span>
101
- </button>
102
- <button
103
- class="filter-tag"
104
- :class="{ active: activeFilter === 'slow' }"
105
- @click="setActiveFilter('slow')"
106
- >
107
- <i class="bi bi-exclamation-triangle"></i>
108
- <span>慢查询</span>
109
- </button>
110
- <button
111
- class="filter-tag"
112
- :class="{ active: activeFilter === 'failed' }"
113
- @click="setActiveFilter('failed')"
114
- >
115
- <i class="bi bi-x-circle"></i>
116
- <span>失败</span>
117
- </button>
118
- </div>
119
-
120
- <!-- 查询列表 -->
121
- <div class="queries-list">
122
- <div class="list-header">
123
- <div class="list-title">
124
- <span>查询记录 ({{ filteredQueries.length }})</span>
125
- </div>
126
- <div class="list-actions">
127
- <button class="btn-export" @click="exportHistory">
128
- <i class="bi bi-download"></i>
129
- <span>导出</span>
130
- </button>
131
- </div>
132
- </div>
133
-
134
- <div class="query-items">
135
- <div
136
- v-for="query in paginatedQueries"
137
- :key="query.id"
138
- class="query-item"
139
- :class="{
140
- 'expanded': expandedQuery === query.id,
141
- 'failed': query.status === 'failed',
142
- 'slow': query.executionTime > 2000
143
- }"
144
- >
145
- <div class="query-header" @click="toggleExpand(query.id)">
146
- <div class="query-info">
147
- <div class="query-type-badge" :class="query.type.toLowerCase()">
148
- {{ query.type }}
149
- </div>
150
- <div class="query-summary">
151
- <span class="query-text">{{ truncateSql(query.sql) }}</span>
152
- </div>
153
- <div class="query-meta">
154
- <span class="execution-time" :class="getExecutionTimeClass(query.executionTime)">
155
- {{ query.executionTime }}ms
156
- </span>
157
- <span class="timestamp">{{ formatTime(query.timestamp) }}</span>
158
- <span class="connection">{{ query.connectionName }}</span>
159
- </div>
160
- </div>
161
- <div class="query-actions">
162
- <button
163
- class="btn-favorite"
164
- :class="{ 'active': query.isFavorite }"
165
- @click.stop="toggleFavorite(query)"
166
- :title="query.isFavorite ? '取消收藏' : '收藏查询'"
167
- >
168
- <i class="bi bi-star-fill" v-if="query.isFavorite"></i>
169
- <i class="bi bi-star" v-else></i>
170
- </button>
171
- <button
172
- class="btn-execute"
173
- @click.stop="executeQuery(query)"
174
- title="重新执行"
175
- >
176
- <i class="bi bi-play-fill"></i>
177
- </button>
178
- <button
179
- class="btn-copy"
180
- @click.stop="copyQuery(query)"
181
- title="复制SQL"
182
- >
183
- <i class="bi bi-clipboard"></i>
184
- </button>
185
- <button
186
- class="btn-delete"
187
- @click.stop="deleteQuery(query)"
188
- title="删除记录"
189
- >
190
- <i class="bi bi-trash"></i>
191
- </button>
192
- </div>
193
- </div>
194
-
195
- <div class="query-details" v-show="expandedQuery === query.id">
196
- <div class="details-tabs">
197
- <button
198
- class="tab-btn"
199
- :class="{ active: activeDetailTab === 'sql' }"
200
- @click="activeDetailTab = 'sql'"
201
- >
202
- <span>SQL语句</span>
203
- </button>
204
- <button
205
- class="tab-btn"
206
- :class="{ active: activeDetailTab === 'result' }"
207
- @click="activeDetailTab = 'result'"
208
- v-if="query.status === 'success'"
209
- >
210
- <span>执行结果</span>
211
- </button>
212
- <button
213
- class="tab-btn"
214
- :class="{ active: activeDetailTab === 'error' }"
215
- @click="activeDetailTab = 'error'"
216
- v-if="query.status === 'failed'"
217
- >
218
- <span>错误信息</span>
219
- </button>
220
- <button
221
- class="tab-btn"
222
- :class="{ active: activeDetailTab === 'plan' }"
223
- @click="activeDetailTab = 'plan'"
224
- >
225
- <span>执行计划</span>
226
- </button>
227
- </div>
228
-
229
- <div class="details-content">
230
- <div class="detail-panel" v-if="activeDetailTab === 'sql'">
231
- <div class="sql-editor">
232
- <pre><code>{{ query.sql }}</code></pre>
233
- <button class="btn-copy-sql" @click="copySql(query.sql)">
234
- <i class="bi bi-clipboard"></i>
235
- 复制
236
- </button>
237
- </div>
238
- </div>
239
-
240
- <div class="detail-panel" v-if="activeDetailTab === 'result' && query.status === 'success'">
241
- <div class="result-info">
242
- <div class="result-stat">
243
- <span class="stat-label">影响行数:</span>
244
- <span class="stat-value">{{ query.affectedRows || 0 }}</span>
245
- </div>
246
- <div class="result-stat">
247
- <span class="stat-label">返回行数:</span>
248
- <span class="stat-value">{{ query.returnedRows || 0 }}</span>
249
- </div>
250
- <div class="result-stat">
251
- <span class="stat-label">执行时间:</span>
252
- <span class="stat-value">{{ query.executionTime }}ms</span>
253
- </div>
254
- </div>
255
- <div class="result-preview" v-if="query.resultPreview">
256
- <table class="preview-table">
257
- <thead>
258
- <tr>
259
- <th v-for="column in query.resultPreview.columns" :key="column">
260
- {{ column }}
261
- </th>
262
- </tr>
263
- </thead>
264
- <tbody>
265
- <tr v-for="(row, index) in query.resultPreview.data" :key="index">
266
- <td v-for="column in query.resultPreview.columns" :key="column">
267
- {{ row[column] }}
268
- </td>
269
- </tr>
270
- </tbody>
271
- </table>
272
- </div>
273
- </div>
274
-
275
- <div class="detail-panel" v-if="activeDetailTab === 'error' && query.status === 'failed'">
276
- <div class="error-message">
277
- <i class="bi bi-exclamation-triangle"></i>
278
- <div class="error-content">
279
- <h4>执行错误</h4>
280
- <pre>{{ query.errorMessage }}</pre>
281
- </div>
282
- </div>
283
- </div>
284
-
285
- <div class="detail-panel" v-if="activeDetailTab === 'plan'">
286
- <div class="execution-plan">
287
- <pre>{{ query.executionPlan || '暂无执行计划数据' }}</pre>
288
- </div>
289
- </div>
290
- </div>
291
- </div>
292
- </div>
293
- </div>
294
- </div>
295
-
296
- <!-- 分页 -->
297
- <div class="pagination" v-if="totalPages > 1">
298
- <button @click="prevPage" :disabled="currentPage === 1">
299
- <i class="bi bi-chevron-left"></i>
300
- </button>
301
- <span class="page-info">{{ currentPage }} / {{ totalPages }}</span>
302
- <button @click="nextPage" :disabled="currentPage === totalPages">
303
- <i class="bi bi-chevron-right"></i>
304
- </button>
305
- </div>
306
- </div>
307
-
308
- <!-- 确认清空对话框 -->
309
- <div class="modal-overlay" v-if="showClearConfirm">
310
- <div class="modal-content">
311
- <div class="modal-header">
312
- <h3>确认清空</h3>
313
- <button class="modal-close" @click="showClearConfirm = false">
314
- <i class="bi bi-x-lg"></i>
315
- </button>
316
- </div>
317
- <div class="modal-body">
318
- <p>确定要清空所有查询历史记录吗?此操作无法撤销。</p>
319
- </div>
320
- <div class="modal-footer">
321
- <button class="btn-cancel" @click="showClearConfirm = false">取消</button>
322
- <button class="btn-confirm" @click="clearHistory">确认清空</button>
323
- </div>
324
- </div>
325
- </div>
326
- </div>
327
- </template>
328
-
329
- <script lang="ts" setup>
330
- import { ref, computed, onMounted } from 'vue';
331
-
332
- // 响应式数据
333
- const searchQuery = ref('');
334
- const filterType = ref('all');
335
- const activeFilter = ref('all');
336
- const expandedQuery = ref<string | null>(null);
337
- const activeDetailTab = ref('sql');
338
- const currentPage = ref(1);
339
- const pageSize = ref(20);
340
- const showClearConfirm = ref(false);
341
-
342
- // 模拟查询历史数据
343
- const queryHistory = ref([
344
- {
345
- id: '1',
346
- type: 'SELECT',
347
- sql: 'SELECT * FROM users WHERE status = "active" AND created_at > "2024-01-01"',
348
- timestamp: new Date(Date.now() - 5 * 60000),
349
- executionTime: 245,
350
- status: 'success',
351
- connectionName: 'MySQL主库',
352
- isFavorite: true,
353
- affectedRows: 0,
354
- returnedRows: 125,
355
- resultPreview: {
356
- columns: ['id', 'name', 'email', 'status'],
357
- data: [
358
- { id: 1, name: '张三', email: 'zhangsan@example.com', status: 'active' },
359
- { id: 2, name: '李四', email: 'lisi@example.com', status: 'active' }
360
- ]
361
- }
362
- },
363
- {
364
- id: '2',
365
- type: 'UPDATE',
366
- sql: 'UPDATE products SET price = price * 1.1 WHERE category_id = 5',
367
- timestamp: new Date(Date.now() - 15 * 60000),
368
- executionTime: 3200,
369
- status: 'success',
370
- connectionName: 'MySQL主库',
371
- isFavorite: false,
372
- affectedRows: 48,
373
- returnedRows: 0
374
- },
375
- {
376
- id: '3',
377
- type: 'INSERT',
378
- sql: 'INSERT INTO orders (user_id, product_id, quantity, total_amount) VALUES (1, 10, 2, 299.99)',
379
- timestamp: new Date(Date.now() - 30 * 60000),
380
- executionTime: 120,
381
- status: 'success',
382
- connectionName: 'MySQL主库',
383
- isFavorite: false,
384
- affectedRows: 1,
385
- returnedRows: 0
386
- },
387
- {
388
- id: '4',
389
- type: 'SELECT',
390
- sql: 'SELECT u.name, COUNT(o.id) as order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.created_at > "2024-01-01" GROUP BY u.id, u.name HAVING COUNT(o.id) > 5',
391
- timestamp: new Date(Date.now() - 60 * 60000),
392
- executionTime: 5600,
393
- status: 'success',
394
- connectionName: 'MySQL主库',
395
- isFavorite: true,
396
- affectedRows: 0,
397
- returnedRows: 23
398
- },
399
- {
400
- id: '5',
401
- type: 'DELETE',
402
- sql: 'DELETE FROM user_sessions WHERE last_activity < DATE_SUB(NOW(), INTERVAL 30 DAY)',
403
- timestamp: new Date(Date.now() - 120 * 60000),
404
- executionTime: 1850,
405
- status: 'failed',
406
- connectionName: 'MySQL主库',
407
- isFavorite: false,
408
- errorMessage: 'Error 1451: Cannot delete or update a parent row: a foreign key constraint fails'
409
- }
410
- ]);
411
-
412
- // 计算属性
413
- const totalQueries = computed(() => queryHistory.value.length);
414
-
415
- const recentQueries = computed(() => {
416
- const today = new Date();
417
- today.setHours(0, 0, 0, 0);
418
- return queryHistory.value.filter(q => q.timestamp >= today);
419
- });
420
-
421
- const favoriteQueries = computed(() =>
422
- queryHistory.value.filter(q => q.isFavorite)
423
- );
424
-
425
- const averageExecutionTime = computed(() => {
426
- const successfulQueries = queryHistory.value.filter(q => q.status === 'success');
427
- if (successfulQueries.length === 0) return 0;
428
- const total = successfulQueries.reduce((sum, q) => sum + q.executionTime, 0);
429
- return total / successfulQueries.length;
430
- });
431
-
432
- const filteredQueries = computed(() => {
433
- let filtered = queryHistory.value;
434
-
435
- // 搜索过滤
436
- if (searchQuery.value) {
437
- const query = searchQuery.value.toLowerCase();
438
- filtered = filtered.filter(q =>
439
- q.sql.toLowerCase().includes(query) ||
440
- q.connectionName.toLowerCase().includes(query)
441
- );
442
- }
443
-
444
- // 类型过滤
445
- if (filterType.value !== 'all') {
446
- filtered = filtered.filter(q => q.type === filterType.value);
447
- }
448
-
449
- // 快速筛选
450
- switch (activeFilter.value) {
451
- case 'recent':
452
- const today = new Date();
453
- today.setHours(0, 0, 0, 0);
454
- filtered = filtered.filter(q => q.timestamp >= today);
455
- break;
456
- case 'favorites':
457
- filtered = filtered.filter(q => q.isFavorite);
458
- break;
459
- case 'slow':
460
- filtered = filtered.filter(q => q.executionTime > 2000);
461
- break;
462
- case 'failed':
463
- filtered = filtered.filter(q => q.status === 'failed');
464
- break;
465
- }
466
-
467
- // 按时间倒序
468
- return filtered.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
469
- });
470
-
471
- const totalPages = computed(() =>
472
- Math.ceil(filteredQueries.value.length / pageSize.value)
473
- );
474
-
475
- const paginatedQueries = computed(() => {
476
- const start = (currentPage.value - 1) * pageSize.value;
477
- const end = start + pageSize.value;
478
- return filteredQueries.value.slice(start, end);
479
- });
480
-
481
- // 方法
482
- function handleSearch() {
483
- currentPage.value = 1;
484
- }
485
-
486
- function filterQueries() {
487
- currentPage.value = 1;
488
- }
489
-
490
- function setActiveFilter(filter: string) {
491
- activeFilter.value = filter;
492
- currentPage.value = 1;
493
- }
494
-
495
- function toggleExpand(queryId: string) {
496
- expandedQuery.value = expandedQuery.value === queryId ? null : queryId;
497
- activeDetailTab.value = 'sql';
498
- }
499
-
500
- function toggleFavorite(query: any) {
501
- query.isFavorite = !query.isFavorite;
502
- // 这里可以调用API更新收藏状态
503
- }
504
-
505
- function executeQuery(query: any) {
506
- console.log('执行查询:', query);
507
- // 这里应该跳转到SQL编辑器并加载查询
508
- }
509
-
510
- function copyQuery(query: any) {
511
- copySql(query.sql);
512
- }
513
-
514
- function copySql(sql: string) {
515
- navigator.clipboard.writeText(sql).then(() => {
516
- // 这里可以显示复制成功提示
517
- console.log('SQL已复制到剪贴板');
518
- });
519
- }
520
-
521
- function deleteQuery(query: any) {
522
- const index = queryHistory.value.findIndex(q => q.id === query.id);
523
- if (index > -1) {
524
- queryHistory.value.splice(index, 1);
525
- // 这里可以调用API删除记录
526
- }
527
- }
528
-
529
- function truncateSql(sql: string, maxLength = 80): string {
530
- if (sql.length <= maxLength) return sql;
531
- return sql.substring(0, maxLength) + '...';
532
- }
533
-
534
- function formatTime(timestamp: Date): string {
535
- const now = new Date();
536
- const diff = now.getTime() - timestamp.getTime();
537
- const minutes = Math.floor(diff / 60000);
538
- const hours = Math.floor(diff / 3600000);
539
- const days = Math.floor(diff / 86400000);
540
-
541
- if (minutes < 1) return '刚刚';
542
- if (minutes < 60) return `${minutes}分钟前`;
543
- if (hours < 24) return `${hours}小时前`;
544
- if (days < 7) return `${days}天前`;
545
-
546
- return timestamp.toLocaleDateString('zh-CN');
547
- }
548
-
549
- function getExecutionTimeClass(time: number): string {
550
- if (time < 100) return 'fast';
551
- if (time < 1000) return 'normal';
552
- if (time < 2000) return 'slow';
553
- return 'very-slow';
554
- }
555
-
556
- function confirmClearHistory() {
557
- showClearConfirm.value = true;
558
- }
559
-
560
- function clearHistory() {
561
- queryHistory.value = [];
562
- showClearConfirm.value = false;
563
- currentPage.value = 1;
564
- }
565
-
566
- function exportHistory() {
567
- const data = filteredQueries.value.map(q => ({
568
- 时间: q.timestamp.toLocaleString('zh-CN'),
569
- 类型: q.type,
570
- 连接: q.connectionName,
571
- 执行时间: q.executionTime + 'ms',
572
- 状态: q.status,
573
- SQL: q.sql
574
- }));
575
-
576
- const csv = [
577
- Object.keys(data[0]).join(','),
578
- ...data.map(row => Object.values(row).map(v => `"${v}"`).join(','))
579
- ].join('\n');
580
-
581
- const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
582
- const link = document.createElement('a');
583
- link.href = URL.createObjectURL(blob);
584
- link.download = `query_history_${new Date().getTime()}.csv`;
585
- link.click();
586
- }
587
-
588
- function nextPage() {
589
- if (currentPage.value < totalPages.value) {
590
- currentPage.value++;
591
- }
592
- }
593
-
594
- function prevPage() {
595
- if (currentPage.value > 1) {
596
- currentPage.value--;
597
- }
598
- }
599
-
600
- // 生命周期
601
- onMounted(() => {
602
- // 加载查询历史数据
603
- });
604
- </script>
605
-
606
- <style scoped>
607
- .query-history {
608
- height: 100%;
609
- display: flex;
610
- flex-direction: column;
611
- background: white;
612
- border-radius: 12px;
613
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
614
- overflow: hidden;
615
- }
616
-
617
- .history-header {
618
- display: flex;
619
- justify-content: space-between;
620
- align-items: center;
621
- padding: 1rem 1.5rem;
622
- background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
623
- border-bottom: 1px solid #e2e8f0;
624
- }
625
-
626
- .header-title {
627
- display: flex;
628
- align-items: center;
629
- gap: 0.5rem;
630
- font-weight: 600;
631
- color: #1e293b;
632
- font-size: 1.1rem;
633
- }
634
-
635
- .header-controls {
636
- display: flex;
637
- align-items: center;
638
- gap: 1rem;
639
- }
640
-
641
- .search-box {
642
- position: relative;
643
- display: flex;
644
- align-items: center;
645
- }
646
-
647
- .search-box i {
648
- position: absolute;
649
- left: 0.75rem;
650
- color: #6b7280;
651
- font-size: 0.875rem;
652
- }
653
-
654
- .search-box input {
655
- padding: 0.5rem 0.75rem 0.5rem 2.25rem;
656
- border: 1px solid #d1d5db;
657
- border-radius: 8px;
658
- font-size: 0.875rem;
659
- width: 250px;
660
- }
661
-
662
- .search-box input:focus {
663
- outline: none;
664
- border-color: #667eea;
665
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
666
- }
667
-
668
- .filter-select {
669
- padding: 0.5rem 0.75rem;
670
- border: 1px solid #d1d5db;
671
- border-radius: 8px;
672
- background: white;
673
- font-size: 0.875rem;
674
- }
675
-
676
- .btn-clear-history {
677
- display: flex;
678
- align-items: center;
679
- gap: 0.25rem;
680
- padding: 0.5rem 1rem;
681
- border: 1px solid #ef4444;
682
- border-radius: 8px;
683
- background: white;
684
- color: #ef4444;
685
- font-size: 0.875rem;
686
- cursor: pointer;
687
- transition: all 0.2s ease;
688
- }
689
-
690
- .btn-clear-history:hover {
691
- background: #ef4444;
692
- color: white;
693
- }
694
-
695
- .history-content {
696
- flex: 1;
697
- overflow-y: auto;
698
- padding: 1.5rem;
699
- }
700
-
701
- /* 统计卡片 */
702
- .stats-section {
703
- display: grid;
704
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
705
- gap: 1rem;
706
- margin-bottom: 2rem;
707
- }
708
-
709
- .stat-card {
710
- display: flex;
711
- align-items: center;
712
- gap: 1rem;
713
- padding: 1.25rem;
714
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
715
- border: 1px solid #e2e8f0;
716
- border-radius: 12px;
717
- transition: all 0.2s ease;
718
- }
719
-
720
- .stat-card:hover {
721
- transform: translateY(-2px);
722
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
723
- }
724
-
725
- .stat-icon {
726
- width: 48px;
727
- height: 48px;
728
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
729
- border-radius: 12px;
730
- display: flex;
731
- align-items: center;
732
- justify-content: center;
733
- color: white;
734
- font-size: 1.25rem;
735
- }
736
-
737
- .stat-info {
738
- flex: 1;
739
- }
740
-
741
- .stat-number {
742
- font-size: 1.5rem;
743
- font-weight: 700;
744
- color: #1e293b;
745
- margin-bottom: 0.25rem;
746
- }
747
-
748
- .stat-label {
749
- font-size: 0.875rem;
750
- color: #64748b;
751
- }
752
-
753
- /* 快速筛选 */
754
- .quick-filters {
755
- display: flex;
756
- gap: 0.5rem;
757
- margin-bottom: 2rem;
758
- flex-wrap: wrap;
759
- }
760
-
761
- .filter-tag {
762
- display: flex;
763
- align-items: center;
764
- gap: 0.5rem;
765
- padding: 0.5rem 1rem;
766
- border: 1px solid #d1d5db;
767
- border-radius: 20px;
768
- background: white;
769
- color: #6b7280;
770
- font-size: 0.875rem;
771
- cursor: pointer;
772
- transition: all 0.2s ease;
773
- }
774
-
775
- .filter-tag:hover {
776
- background: #f3f4f6;
777
- border-color: #9ca3af;
778
- }
779
-
780
- .filter-tag.active {
781
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
782
- color: white;
783
- border-color: transparent;
784
- }
785
-
786
- /* 查询列表 */
787
- .queries-list {
788
- flex: 1;
789
- }
790
-
791
- .list-header {
792
- display: flex;
793
- justify-content: space-between;
794
- align-items: center;
795
- margin-bottom: 1rem;
796
- }
797
-
798
- .list-title {
799
- font-size: 1rem;
800
- font-weight: 600;
801
- color: #1e293b;
802
- }
803
-
804
- .list-actions {
805
- display: flex;
806
- gap: 0.5rem;
807
- }
808
-
809
- .btn-export {
810
- display: flex;
811
- align-items: center;
812
- gap: 0.25rem;
813
- padding: 0.5rem 1rem;
814
- border: 1px solid #667eea;
815
- border-radius: 8px;
816
- background: white;
817
- color: #667eea;
818
- font-size: 0.875rem;
819
- cursor: pointer;
820
- transition: all 0.2s ease;
821
- }
822
-
823
- .btn-export:hover {
824
- background: #667eea;
825
- color: white;
826
- }
827
-
828
- .query-items {
829
- display: flex;
830
- flex-direction: column;
831
- gap: 0.75rem;
832
- }
833
-
834
- .query-item {
835
- border: 1px solid #e5e7eb;
836
- border-radius: 12px;
837
- overflow: hidden;
838
- transition: all 0.2s ease;
839
- }
840
-
841
- .query-item:hover {
842
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
843
- }
844
-
845
- .query-item.failed {
846
- border-left: 4px solid #ef4444;
847
- }
848
-
849
- .query-item.slow {
850
- border-left: 4px solid #f59e0b;
851
- }
852
-
853
- .query-item.expanded {
854
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
855
- }
856
-
857
- .query-header {
858
- display: flex;
859
- justify-content: space-between;
860
- align-items: center;
861
- padding: 1rem 1.25rem;
862
- cursor: pointer;
863
- transition: background-color 0.2s ease;
864
- }
865
-
866
- .query-header:hover {
867
- background: #f8fafc;
868
- }
869
-
870
- .query-info {
871
- flex: 1;
872
- display: flex;
873
- align-items: center;
874
- gap: 1rem;
875
- min-width: 0;
876
- }
877
-
878
- .query-type-badge {
879
- padding: 0.25rem 0.75rem;
880
- border-radius: 20px;
881
- font-size: 0.75rem;
882
- font-weight: 600;
883
- color: white;
884
- white-space: nowrap;
885
- }
886
-
887
- .query-type-badge.select { background: #3b82f6; }
888
- .query-type-badge.insert { background: #10b981; }
889
- .query-type-badge.update { background: #f59e0b; }
890
- .query-type-badge.delete { background: #ef4444; }
891
- .query-type-badge.create { background: #8b5cf6; }
892
- .query-type-badge.alter { background: #06b6d4; }
893
- .query-type-badge.drop { background: #dc2626; }
894
-
895
- .query-summary {
896
- flex: 1;
897
- min-width: 0;
898
- }
899
-
900
- .query-text {
901
- font-family: 'Consolas', 'Monaco', monospace;
902
- font-size: 0.875rem;
903
- color: #374151;
904
- display: block;
905
- }
906
-
907
- .query-meta {
908
- display: flex;
909
- align-items: center;
910
- gap: 1rem;
911
- font-size: 0.75rem;
912
- color: #6b7280;
913
- }
914
-
915
- .execution-time {
916
- font-weight: 600;
917
- padding: 0.125rem 0.5rem;
918
- border-radius: 4px;
919
- }
920
-
921
- .execution-time.fast { color: #10b981; background: #d1fae5; }
922
- .execution-time.normal { color: #3b82f6; background: #dbeafe; }
923
- .execution-time.slow { color: #f59e0b; background: #fef3c7; }
924
- .execution-time.very-slow { color: #ef4444; background: #fee2e2; }
925
-
926
- .query-actions {
927
- display: flex;
928
- align-items: center;
929
- gap: 0.25rem;
930
- }
931
-
932
- .query-actions button {
933
- width: 32px;
934
- height: 32px;
935
- border: 1px solid #e5e7eb;
936
- background: white;
937
- border-radius: 6px;
938
- display: flex;
939
- align-items: center;
940
- justify-content: center;
941
- cursor: pointer;
942
- transition: all 0.2s ease;
943
- }
944
-
945
- .query-actions button:hover {
946
- background: #f3f4f6;
947
- border-color: #d1d5db;
948
- }
949
-
950
- .btn-favorite.active {
951
- background: #fbbf24;
952
- border-color: #fbbf24;
953
- color: white;
954
- }
955
-
956
- .btn-execute:hover {
957
- background: #10b981;
958
- border-color: #10b981;
959
- color: white;
960
- }
961
-
962
- .btn-copy:hover {
963
- background: #3b82f6;
964
- border-color: #3b82f6;
965
- color: white;
966
- }
967
-
968
- .btn-delete:hover {
969
- background: #ef4444;
970
- border-color: #ef4444;
971
- color: white;
972
- }
973
-
974
- .query-details {
975
- border-top: 1px solid #e5e7eb;
976
- background: #fafbfc;
977
- }
978
-
979
- .details-tabs {
980
- display: flex;
981
- border-bottom: 1px solid #e5e7eb;
982
- }
983
-
984
- .tab-btn {
985
- padding: 0.75rem 1.5rem;
986
- background: none;
987
- border: none;
988
- color: #6b7280;
989
- font-size: 0.875rem;
990
- cursor: pointer;
991
- transition: all 0.2s ease;
992
- border-bottom: 2px solid transparent;
993
- }
994
-
995
- .tab-btn:hover {
996
- color: #374151;
997
- }
998
-
999
- .tab-btn.active {
1000
- color: #667eea;
1001
- border-bottom-color: #667eea;
1002
- background: white;
1003
- }
1004
-
1005
- .details-content {
1006
- padding: 1.25rem;
1007
- }
1008
-
1009
- .detail-panel {
1010
- min-height: 100px;
1011
- }
1012
-
1013
- .sql-editor {
1014
- position: relative;
1015
- background: #1e293b;
1016
- border-radius: 8px;
1017
- padding: 1rem;
1018
- overflow-x: auto;
1019
- }
1020
-
1021
- .sql-editor pre {
1022
- margin: 0;
1023
- color: #e2e8f0;
1024
- font-family: 'Consolas', 'Monaco', monospace;
1025
- font-size: 0.875rem;
1026
- line-height: 1.5;
1027
- }
1028
-
1029
- .btn-copy-sql {
1030
- position: absolute;
1031
- top: 0.5rem;
1032
- right: 0.5rem;
1033
- padding: 0.25rem 0.5rem;
1034
- background: rgba(255, 255, 255, 0.1);
1035
- border: 1px solid rgba(255, 255, 255, 0.2);
1036
- border-radius: 4px;
1037
- color: white;
1038
- font-size: 0.75rem;
1039
- cursor: pointer;
1040
- transition: all 0.2s ease;
1041
- }
1042
-
1043
- .btn-copy-sql:hover {
1044
- background: rgba(255, 255, 255, 0.2);
1045
- }
1046
-
1047
- .result-info {
1048
- display: grid;
1049
- grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
1050
- gap: 1rem;
1051
- margin-bottom: 1.5rem;
1052
- }
1053
-
1054
- .result-stat {
1055
- display: flex;
1056
- flex-direction: column;
1057
- gap: 0.25rem;
1058
- }
1059
-
1060
- .stat-label {
1061
- font-size: 0.875rem;
1062
- color: #6b7280;
1063
- }
1064
-
1065
- .stat-value {
1066
- font-size: 1.125rem;
1067
- font-weight: 600;
1068
- color: #1e293b;
1069
- }
1070
-
1071
- .result-preview {
1072
- border: 1px solid #e5e7eb;
1073
- border-radius: 8px;
1074
- overflow: hidden;
1075
- }
1076
-
1077
- .preview-table {
1078
- width: 100%;
1079
- border-collapse: collapse;
1080
- font-size: 0.875rem;
1081
- }
1082
-
1083
- .preview-table th {
1084
- background: #f8fafc;
1085
- padding: 0.75rem;
1086
- text-align: left;
1087
- font-weight: 600;
1088
- color: #374151;
1089
- border-bottom: 1px solid #e5e7eb;
1090
- }
1091
-
1092
- .preview-table td {
1093
- padding: 0.75rem;
1094
- border-bottom: 1px solid #f3f4f6;
1095
- }
1096
-
1097
- .error-message {
1098
- display: flex;
1099
- align-items: flex-start;
1100
- gap: 1rem;
1101
- padding: 1rem;
1102
- background: #fef2f2;
1103
- border: 1px solid #fecaca;
1104
- border-radius: 8px;
1105
- color: #991b1b;
1106
- }
1107
-
1108
- .error-message i {
1109
- font-size: 1.25rem;
1110
- color: #ef4444;
1111
- margin-top: 0.125rem;
1112
- }
1113
-
1114
- .error-content h4 {
1115
- margin: 0 0 0.5rem 0;
1116
- font-size: 1rem;
1117
- }
1118
-
1119
- .error-content pre {
1120
- margin: 0;
1121
- background: #fee2e2;
1122
- padding: 0.75rem;
1123
- border-radius: 4px;
1124
- font-family: 'Consolas', 'Monaco', monospace;
1125
- font-size: 0.875rem;
1126
- }
1127
-
1128
- .execution-plan {
1129
- background: #f8fafc;
1130
- border: 1px solid #e5e7eb;
1131
- border-radius: 8px;
1132
- padding: 1rem;
1133
- }
1134
-
1135
- .execution-plan pre {
1136
- margin: 0;
1137
- font-family: 'Consolas', 'Monaco', monospace;
1138
- font-size: 0.875rem;
1139
- color: #374151;
1140
- }
1141
-
1142
- /* 分页 */
1143
- .pagination {
1144
- display: flex;
1145
- justify-content: center;
1146
- align-items: center;
1147
- gap: 0.5rem;
1148
- margin-top: 2rem;
1149
- padding: 1rem;
1150
- background: #f8fafc;
1151
- border-radius: 8px;
1152
- }
1153
-
1154
- .pagination button {
1155
- padding: 0.5rem 0.75rem;
1156
- border: 1px solid #d1d5db;
1157
- border-radius: 6px;
1158
- background: white;
1159
- cursor: pointer;
1160
- transition: all 0.2s ease;
1161
- }
1162
-
1163
- .pagination button:hover:not(:disabled) {
1164
- background: #667eea;
1165
- color: white;
1166
- border-color: #667eea;
1167
- }
1168
-
1169
- .pagination button:disabled {
1170
- opacity: 0.5;
1171
- cursor: not-allowed;
1172
- }
1173
-
1174
- .page-info {
1175
- padding: 0.5rem 1rem;
1176
- font-size: 0.875rem;
1177
- color: #6b7280;
1178
- }
1179
-
1180
- /* 模态框 */
1181
- .modal-overlay {
1182
- position: fixed;
1183
- top: 0;
1184
- left: 0;
1185
- right: 0;
1186
- bottom: 0;
1187
- background: rgba(0, 0, 0, 0.5);
1188
- display: flex;
1189
- justify-content: center;
1190
- align-items: center;
1191
- z-index: 1000;
1192
- }
1193
-
1194
- .modal-content {
1195
- background: white;
1196
- border-radius: 12px;
1197
- width: 90%;
1198
- max-width: 400px;
1199
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
1200
- }
1201
-
1202
- .modal-header {
1203
- display: flex;
1204
- justify-content: space-between;
1205
- align-items: center;
1206
- padding: 1.5rem;
1207
- border-bottom: 1px solid #e5e7eb;
1208
- }
1209
-
1210
- .modal-header h3 {
1211
- margin: 0;
1212
- font-size: 1.25rem;
1213
- font-weight: 600;
1214
- color: #1e293b;
1215
- }
1216
-
1217
- .modal-close {
1218
- background: none;
1219
- border: none;
1220
- font-size: 1.25rem;
1221
- cursor: pointer;
1222
- color: #6b7280;
1223
- padding: 0.25rem;
1224
- }
1225
-
1226
- .modal-close:hover {
1227
- color: #374151;
1228
- }
1229
-
1230
- .modal-body {
1231
- padding: 1.5rem;
1232
- }
1233
-
1234
- .modal-body p {
1235
- margin: 0;
1236
- color: #374151;
1237
- line-height: 1.5;
1238
- }
1239
-
1240
- .modal-footer {
1241
- display: flex;
1242
- justify-content: flex-end;
1243
- gap: 0.5rem;
1244
- padding: 1.5rem;
1245
- border-top: 1px solid #e5e7eb;
1246
- }
1247
-
1248
- .btn-cancel,
1249
- .btn-confirm {
1250
- padding: 0.75rem 1.5rem;
1251
- border-radius: 8px;
1252
- font-weight: 500;
1253
- cursor: pointer;
1254
- transition: all 0.2s ease;
1255
- }
1256
-
1257
- .btn-cancel {
1258
- background: white;
1259
- border: 1px solid #d1d5db;
1260
- color: #374151;
1261
- }
1262
-
1263
- .btn-cancel:hover {
1264
- background: #f3f4f6;
1265
- }
1266
-
1267
- .btn-confirm {
1268
- background: #ef4444;
1269
- border: none;
1270
- color: white;
1271
- }
1272
-
1273
- .btn-confirm:hover {
1274
- background: #dc2626;
1275
- }
1276
-
1277
- /* 响应式设计 */
1278
- @media (max-width: 768px) {
1279
- .history-header {
1280
- flex-direction: column;
1281
- gap: 1rem;
1282
- align-items: stretch;
1283
- }
1284
-
1285
- .header-controls {
1286
- flex-wrap: wrap;
1287
- }
1288
-
1289
- .search-box input {
1290
- width: 100%;
1291
- }
1292
-
1293
- .stats-section {
1294
- grid-template-columns: repeat(2, 1fr);
1295
- }
1296
-
1297
- .quick-filters {
1298
- justify-content: center;
1299
- }
1300
-
1301
- .query-header {
1302
- flex-direction: column;
1303
- gap: 1rem;
1304
- align-items: stretch;
1305
- }
1306
-
1307
- .query-info {
1308
- flex-direction: column;
1309
- align-items: stretch;
1310
- gap: 0.75rem;
1311
- }
1312
-
1313
- .query-meta {
1314
- justify-content: space-between;
1315
- }
1316
-
1317
- .query-actions {
1318
- justify-content: center;
1319
- }
1320
-
1321
- .details-tabs {
1322
- overflow-x: auto;
1323
- }
1324
-
1325
- .tab-btn {
1326
- flex-shrink: 0;
1327
- }
1328
- }
1329
-
1330
- @media (max-width: 480px) {
1331
- .history-content {
1332
- padding: 1rem;
1333
- }
1334
-
1335
- .stats-section {
1336
- grid-template-columns: 1fr;
1337
- }
1338
-
1339
- .list-header {
1340
- flex-direction: column;
1341
- gap: 1rem;
1342
- align-items: stretch;
1343
- }
1344
-
1345
- .result-info {
1346
- grid-template-columns: 1fr;
1347
- }
1348
- }
1349
- </style>