欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于SpringBoot與Vue開發(fā)Web版SQL執(zhí)行工具

 更新時間:2025年08月15日 09:40:30   作者:Derek_Smart  
這篇文章主要為大家詳細介紹了一個基于Web的SQL執(zhí)行工具的設(shè)計與實現(xiàn),該工具允許用戶通過瀏覽器直接執(zhí)行SQL語句并查看結(jié)果,需要的小伙伴可以了解下

簡介

本文介紹了一個基于Web的SQL執(zhí)行工具的設(shè)計與實現(xiàn),該工具允許用戶通過瀏覽器直接執(zhí)行SQL語句并查看結(jié)果。系統(tǒng)采用前后端分離架構(gòu),后端基于Spring Boot框架實現(xiàn)SQL執(zhí)行引擎,前端使用Vue.js構(gòu)建用戶界面。該系統(tǒng)提供了直觀的SQL編輯器、數(shù)據(jù)庫表管理、歷史查詢記錄和結(jié)果展示等功能,有效提高了數(shù)據(jù)庫操作效率。

(本質(zhì)就是客戶端不知為何連接不上服務(wù)器的數(shù)據(jù)庫,緊急情況下,手搓了一個sql 執(zhí)行器)

本文介紹的網(wǎng)頁版SQL執(zhí)行工具,通過整合Spring Boot后端與Vue前端,實現(xiàn)了數(shù)據(jù)庫管理的范式轉(zhuǎn)移,將復雜操作轉(zhuǎn)化為直觀的Web界面體驗。

技術(shù)架構(gòu)解析

后端核心設(shè)計

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.web.bind.annotation.*;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.*;

/**
 * @Author:Derek_Smart
 * @Date:2025/8/4 17:17
 */
@RestController
@RequestMapping("/sqlUtil")
public class SqlUtilResource {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private final Logger logger = LoggerFactory.getLogger(SqlUtilResource.class);

    @PostMapping("/executeSqlp")
    public Map<String, Object> executeSqlp(@RequestBody SqlRequest request) {
        try {
            String sql = request.getSql();
            List<Object> params = request.getParams();

            if (sql.trim().toUpperCase().startsWith("SELECT")) {
                // 查詢操作
                List<Map<String, Object>> result = jdbcTemplate.query(sql, params.toArray(), (rs, rowNum) -> {
                    Map<String, Object> row = new LinkedHashMap<>();
                    ResultSetMetaData metaData = rs.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = metaData.getColumnLabel(i);
                        row.put(columnName, rs.getObject(i));
                    }
                    return row;
                });

                return createSuccessResponse(result);
            } else {
                // 更新操作
                int affectedRows = jdbcTemplate.update(sql, params.toArray());
                return createSuccessResponse(Collections.singletonMap("affectedRows", affectedRows));
            }
        } catch (Exception e) {
            logger.error("SQL執(zhí)行錯誤: {}", e.getMessage(), e);
            return createErrorResponse("SQL執(zhí)行錯誤: " + e.getMessage());
        }
    }


    @PostMapping("/executeSql")
    public Map<String, Object> executeSql(@RequestBody SqlRequest request) {
        try {
            String sql = request.getSql();
            List<Object> params = request.getParams();

            if (sql.trim().toUpperCase().startsWith("SELECT")) {
                // 查詢操作 - 返回列和行數(shù)據(jù)
                return handleQuery(sql, params);
            } else {
                // 更新操作 - 返回受影響行數(shù)
                return handleUpdate(sql, params);
            }
        } catch (Exception e) {
            logger.error("SQL執(zhí)行錯誤: {}", e.getMessage(), e);
            return createErrorResponse("SQL執(zhí)行錯誤: " + e.getMessage());
        }
    }


    /**
     * 獲取數(shù)據(jù)庫所有表名
     * @return 表名列表
     */
    @GetMapping("/getTables")
    public Map<String, Object> getDatabaseTables() {
        try {
            return jdbcTemplate.execute((ConnectionCallback<Map<String, Object>>) connection -> {
                DatabaseMetaData metaData = connection.getMetaData();
                // 獲取所有表名(不包含系統(tǒng)表)
                ResultSet tables = metaData.getTables(
                        null, null, null, new String[]{"TABLE"});

                List<String> tableNames = new ArrayList<>();
                while (tables.next()) {
                    String tableName = tables.getString("TABLE_NAME");
                    tableNames.add(tableName);
                }

                // 按字母順序排序
                Collections.sort(tableNames);

                // 構(gòu)建響應(yīng)
                Map<String, Object> response = new LinkedHashMap<>();
                response.put("success", true);
                response.put("data", tableNames);
                return response;
            });
        } catch (Exception e) {
            logger.error("獲取數(shù)據(jù)庫表失敗: {}", e.getMessage(), e);
            Map<String, Object> errorResponse = new LinkedHashMap<>();
            errorResponse.put("success", false);
            errorResponse.put("message", "獲取數(shù)據(jù)庫表失敗: " + e.getMessage());
            return errorResponse;
        }
    }


    private Map<String, Object> handleQuery(String sql, List<Object> params) {
        try {
            return jdbcTemplate.query(sql, params.toArray(), (ResultSetExtractor<Map<String, Object>>) rs -> {
                ResultSetMetaData metaData = rs.getMetaData();
                int columnCount = metaData.getColumnCount();

                // 獲取列名
                List<String> columns = new ArrayList<>();
                for (int i = 1; i <= columnCount; i++) {
                    columns.add(metaData.getColumnLabel(i));
                }

                // 獲取行數(shù)據(jù)
                List<List<Object>> rows = new ArrayList<>();
                while (rs.next()) {
                    List<Object> row = new ArrayList<>();
                    for (int i = 1; i <= columnCount; i++) {
                        row.add(rs.getObject(i));
                    }
                    rows.add(row);
                }

                // 構(gòu)建響應(yīng)
                Map<String, Object> data = new LinkedHashMap<>();
                data.put("columns", columns);
                data.put("rows", rows);

                Map<String, Object> response = new LinkedHashMap<>();
                response.put("success", true);
                response.put("data", data);
                return response;
            });
        } catch (Exception e) {
            logger.error("查詢處理失敗: {}", e.getMessage(), e);
            return createErrorResponse("查詢處理失敗: " + e.getMessage());
        }
    }




    private Map<String, Object> handleUpdate(String sql, List<Object> params) {
        int affectedRows = jdbcTemplate.update(sql, params.toArray());

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("affectedRows", affectedRows);

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("success", true);
        response.put("data", data);
        return response;
    }

    // 創(chuàng)建成功響應(yīng)
    private Map<String, Object> createSuccessResponse(Object data) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("success", true);
        response.put("timestamp", System.currentTimeMillis());
        response.put("data", data);
        return response;
    }

    // 創(chuàng)建錯誤響應(yīng)
    private Map<String, Object> createErrorResponse(String message) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("success", false);
        response.put("timestamp", System.currentTimeMillis());
        response.put("message", message);
        return response;
    }

    public static class SqlRequest {
        private String sql;
        private List<Object> params = new ArrayList<>();

        // Getters and Setters
        public String getSql() {
            return sql;
        }

        public void setSql(String sql) {
            this.sql = sql;
        }

        public List<Object> getParams() {
            return params;
        }

        public void setParams(List<Object> params) {
            this.params = params;
        }
    }
}

后端采用策略模式實現(xiàn)SQL路由,自動區(qū)分查詢與更新操作。通過JDBC元數(shù)據(jù)接口實現(xiàn)數(shù)據(jù)庫自發(fā)現(xiàn)能力,為前端提供結(jié)構(gòu)化數(shù)據(jù)支撐。

前端創(chuàng)新交互

<template>
  <!-- 雙面板設(shè)計 -->
  <div class="editor-container">
    <!-- 元數(shù)據(jù)導航區(qū) -->
    <div class="sidebar">
      <div v-for="table in tables" @click="selectTable(table)">
        <i class="fas fa-table"></i> {{ table }}
      </div>
    </div>
    
    <!-- SQL工作區(qū) -->
    <textarea 
      v-model="sqlQuery"
      @keydown.ctrl.enter="executeSql"></textarea>
    
    <!-- 智能結(jié)果渲染 -->
    <el-table :data="result.data.rows">
      <el-table-column 
        v-for="(column, index) in result.data.columns"
        :label="column">
        <template v-slot="scope">
          <span v-if="scope.row[index] === null" class="null-value">NULL</span>
          <span v-else-if="typeof scope.row[index] === 'object'" 
                @click="showJsonDetail(scope.row[index])">
            {{ jsonPreview(scope.row[index]) }}
          </span>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

前端實現(xiàn)三區(qū)域布局:元數(shù)據(jù)導航、SQL編輯器和智能結(jié)果面板。采用動態(tài)類型檢測技術(shù),對NULL值、JSON對象等特殊數(shù)據(jù)類型進行可視化區(qū)分處理。

核心技術(shù)亮點

實時元數(shù)據(jù)發(fā)現(xiàn)

  • 自動加載數(shù)據(jù)庫表結(jié)構(gòu)
  • 表名智能排序與即時搜索
  • 點擊表名自動生成SELECT模板

智能SQL處理

// SQL執(zhí)行核心邏輯
async executeSql() {
  const paginatedSql = this.addPagination(this.sqlQuery);
  const response = await executeSql(paginatedSql);
  
  // 高級結(jié)果處理
  if (response.success) {
    this.result = {
      ...response,
      data: {
        ...response.data,
        executionTime: Date.now() - startTime
      }
    }
  }
}
  • 自動分頁機制
  • 執(zhí)行耗時精準統(tǒng)計
  • 語法錯誤實時反饋

歷史版本管理

// 歷史記錄管理
addToHistory(sql) {
  if (this.history.includes(sql)) return;
  this.history.unshift(sql);
  localStorage.setItem('sqlHistory', JSON.stringify(this.history));
}

本地存儲自動持久化

智能去重機制

一鍵恢復歷史查詢

數(shù)據(jù)可視化增強

  • JSON對象折疊預覽
  • NULL值特殊標識
  • 分頁控件動態(tài)加載

性能優(yōu)化策略

查詢優(yōu)化

  • 自動追加LIMIT子句
  • 分頁查詢按需加載
  • 結(jié)果集流式處理

緩存機制

// 表結(jié)構(gòu)緩存
async fetchDatabaseTables() {
  if (this.cachedTables) return this.cachedTables;
  const response = await fetchTables();
  this.cachedTables = response.data;
}

完整vue代碼:

<template>
  <div class="sql-executor-container">
    <!-- 頭部 -->
    <header>
      <div class="logo">
        <i class="fas fa-database logo-icon"></i>
        <div>
          <h1>SQL 執(zhí)行工具</h1>
          <p>網(wǎng)頁版數(shù)據(jù)庫客戶端工具</p>
        </div>
      </div>
      <div class="connection-info">
        <i class="fas fa-plug"></i> 已連接到 {{ connectionName }} 數(shù)據(jù)庫
      </div>
    </header>

    <!-- 主體內(nèi)容 -->
    <div class="main-content">
      <!-- 側(cè)邊欄 -->
      <div class="sidebar">
        <div class="sidebar-section">
          <h3>數(shù)據(jù)庫表</h3>
          <div
              v-for="table in tables"
              :key="table"
              class="schema-item"
              :class="{ active: selectedTable === table }"
              @click="selectTable(table)"
          >
            <i class="fas fa-table"></i> {{ table }}
          </div>
        </div>

        <div class="sidebar-section">
          <h3>歷史查詢</h3>
          <div
              v-for="(query, index) in history"
              :key="index"
              class="schema-item history-item"
              @click="loadHistoryQuery(query)"
          >
            <i class="fas fa-history"></i> {{ query.substring(0, 40) }}{{ query.length > 40 ? '...' : '' }}
          </div>
        </div>
      </div>

      <!-- 編輯器區(qū)域 -->
      <div class="editor-container">
        <div class="sql-editor-container">
          <div class="sql-editor-header">
            <h2>SQL 編輯器</h2>
            <div class="toolbar">
              <el-button
                  class="toolbar-btn run"
                  @click="executeSql"
                  :loading="loading"
              >
                <i class="fas fa-play"></i> 執(zhí)行
              </el-button>
              <el-button
                  class="toolbar-btn format"
                  @click="formatSql"
              >
                <i class="fas fa-indent"></i> 格式化
              </el-button>
              <el-button
                  class="toolbar-btn clear"
                  @click="clearEditor"
              >
                <i class="fas fa-trash-alt"></i> 清除
              </el-button>
            </div>
          </div>
          <textarea
              class="sql-editor"
              v-model="sqlQuery"
              placeholder="輸入 SQL 語句,例如:SELECT * FROM table_name"
              @keydown.ctrl.enter="executeSql"
          ></textarea>
        </div>

        <!-- 結(jié)果區(qū)域 -->
        <div class="result-container">
          <div class="result-header">
            <h2>執(zhí)行結(jié)果</h2>
            <div class="result-info">
              <div
                  class="result-status"
                  :class="result.success ? 'status-success' : 'status-error'"
                  v-if="result"
              >
                {{ result.success ? '執(zhí)行成功' : '執(zhí)行失敗' }}
              </div>
              <div class="rows-count" v-if="result && result.success">
                共 {{ result.data.total }} 行數(shù)據(jù) (顯示 {{ result.data.rows.length }} 行)
              </div>
              <div class="execution-time" v-if="result && result.success">
                <i class="fas fa-clock"></i> {{ result.data.executionTime }} ms
              </div>
            </div>
          </div>

          <div class="result-content">
            <!-- 加載狀態(tài) -->
            <div v-if="loading" class="no-data">
              <i class="el-icon-loading" style="font-size: 48px;"></i>
              <p>正在執(zhí)行查詢...</p>
            </div>

            <!-- 查詢結(jié)果 -->
            <div v-else-if="result && result.success && result.data.rows.length > 0" class="table-container">
              <div class="table-wrapper">
                <el-table
                    :data="result.data.rows"
                    height="100%"
                    style="width: 100%"
                    stripe
                    border
                    size="small"
                    :default-sort = "{prop: 'id', order: 'ascending'}"
                >
                  <el-table-column
                      v-for="(column, index) in result.data.columns"
                      :key="index"
                      :prop="'row.' + index"
                      :label="column"
                      min-width="150"
                  >
<!--                    <template slot-scope="scope">
                      <span v-if="scope.row[column] === null" class="null-value">NULL</span>
                      <span v-else-if="typeof scope.row[column] === 'object'" class="json-value" @click="showJsonDetail(scope.row[column])">
                        {{ jsonPreview(scope.row[column]) }}
                      </span>
                      <span v-else>{{ scope.row[column] }}</span>
                    </template>-->

                    <template slot-scope="scope">
                      <span v-if="scope.row[index] === null" class="null-value">NULL</span>
                      <span v-else-if="typeof scope.row[index] === 'object'" class="json-value" @click="showJsonDetail(scope.row[index])">
                      {{ jsonPreview(scope.row[index]) }}
                    </span>
                      <span v-else>{{ scope.row[index] }}</span>
                    </template>

                  </el-table-column>
                </el-table>
              </div>

              <!-- 分頁控件 -->
              <el-pagination
                  v-if="result.data.total > pageSize"
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
                  :current-page="currentPage"
                  :page-sizes="[10, 20, 50, 100]"
                  :page-size="pageSize"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="result.data.total"
                  class="pagination"
              >
              </el-pagination>
            </div>

            <!-- 空結(jié)果 -->
            <div v-else-if="result && result.success && result.data.rows.length === 0" class="no-data">
              <i class="el-icon-inbox" style="font-size: 48px;"></i>
              <p>未查詢到數(shù)據(jù)</p>
            </div>

            <!-- 錯誤信息 -->
            <div v-else-if="result && !result.success" class="error-container">
              <div class="error-header">
                <i class="el-icon-warning-outline" style="font-size: 20px;"></i>
                <h3>SQL 執(zhí)行錯誤</h3>
              </div>
              <div class="error-details">
                {{ result.message }}
              </div>
            </div>

            <!-- 初始狀態(tài) -->
            <div v-else class="no-data">
              <i class="el-icon-document" style="font-size: 48px;"></i>
              <p>輸入 SQL 并執(zhí)行以查看結(jié)果</p>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 頁腳 -->
    <div class="footer">
      <div class="copyright">
        ? {{ new Date().getFullYear() }} SQL執(zhí)行工具 - 網(wǎng)頁版數(shù)據(jù)庫客戶端
      </div>
      <div class="shortcuts">
        <div class="shortcut-item">
          <span class="key">Ctrl</span> + <span class="key">Enter</span> 執(zhí)行查詢
        </div>
        <div class="shortcut-item">
          <span class="key">Ctrl</span> + <span class="key">/</span> 格式化 SQL
        </div>
      </div>
    </div>

    <!-- JSON 詳情彈窗 -->
    <el-dialog
        title="JSON 詳情"
        :visible.sync="showJsonModal"
        width="70%"
        top="5vh"
    >
      <pre class="json-content">{{ formatJson(currentJson) }}</pre>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="showJsonModal = false">關(guān) 閉</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { fetchTables, executeSql } from '@/api/sqlUtil/sqlUtil';

export default {
  name: 'SqlUtil',
  data() {
    return {
      sqlQuery: '',
      result: null,
      loading: false,
      tables: [],
      selectedTable: null,
      history: [],
      showJsonModal: false,
      currentJson: null,
      connectionName: '生產(chǎn)',
      currentPage: 1,
      pageSize: 20
    }
  },
  mounted() {
    // 從本地存儲加載歷史記錄
    const savedHistory = localStorage.getItem('sqlHistory');
    if (savedHistory) {
      this.history = JSON.parse(savedHistory);
    }

    // 獲取當前連接信息
    this.getConnectionInfo();

    // 獲取數(shù)據(jù)庫表
    this.fetchDatabaseTables();
  },
  methods: {
    async fetchDatabaseTables() {
      try {
        this.loading = true;
        const response = await fetchTables();
        this.tables = response.data || [];
        if (this.tables.length > 0) {
          this.selectedTable = this.tables[0];
          this.sqlQuery = `SELECT * FROM ${this.selectedTable}`;
        }
      } catch (error) {
        console.error('獲取數(shù)據(jù)庫表失敗:', error);
        this.$message.error('獲取數(shù)據(jù)庫表失敗: ' + error.message);
      } finally {
        this.loading = false;
      }
    },

    async executeSql() {
      if (!this.sqlQuery.trim()) {
        this.result = {
          success: false,
          message: 'SQL 語句不能為空'
        };
        return;
      }

      this.loading = true;
      this.result = null;

      try {
        // 添加分頁參數(shù)
        const paginatedSql = this.addPagination(this.sqlQuery);
        const response = await executeSql(paginatedSql);
        this.result = response;

        // 保存到歷史記錄
        this.addToHistory(this.sqlQuery);
      } catch (error) {
        this.result = {
          success: false,
          message: `請求失敗: ${error.message || error}`
        };
      } finally {
        this.loading = false;
      }
    },

    // 添加分頁到SQL
    addPagination(sql) {
      // 如果是SELECT查詢,添加分頁
      /*if (sql.trim().toUpperCase().startsWith('SELECT')) {
        const offset = (this.currentPage - 1) * this.pageSize;
        return `${sql} LIMIT ${offset}, ${this.pageSize}`;
      }*/
      return sql;
    },

    handleSizeChange(val) {
      this.pageSize = val;
      this.currentPage = 1;
      if (this.sqlQuery.trim()) {
        this.executeSql();
      }
    },

    handleCurrentChange(val) {
      this.currentPage = val;
      if (this.sqlQuery.trim()) {
        this.executeSql();
      }
    },

    // 獲取數(shù)據(jù)庫連接信息
    getConnectionInfo() {
      // 這里可以根據(jù)實際情況從API獲取或從配置讀取
      const env = process.env.NODE_ENV;
      this.connectionName = env === 'production' ? '生產(chǎn)' :
          env === 'staging' ? '預發(fā)布' : '開發(fā)';
    },

    formatSql() {
      // 簡單的 SQL 格式化
      this.sqlQuery = this.sqlQuery
          .replace(/\b(SELECT|FROM|WHERE|AND|OR|ORDER BY|GROUP BY|HAVING|INSERT|UPDATE|DELETE|JOIN|INNER JOIN|LEFT JOIN|RIGHT JOIN|ON|AS|LIMIT)\b/gi, '\n$1')
          .replace(/,/g, ',\n')
          .replace(/;/g, ';\n')
          .replace(/\n\s+\n/g, '\n')
          .trim();
    },

    clearEditor() {
      this.sqlQuery = '';
      this.result = null;
    },

    selectTable(table) {
      this.selectedTable = table;
      this.sqlQuery = `SELECT * FROM ${table} LIMIT 100`;
    },

    addToHistory(sql) {
      // 避免重復添加
      if (this.history.includes(sql)) return;

      this.history.unshift(sql);

      // 限制歷史記錄數(shù)量
      if (this.history.length > 10) {
        this.history.pop();
      }

      // 保存到本地存儲
      localStorage.setItem('sqlHistory', JSON.stringify(this.history));
    },

    loadHistoryQuery(sql) {
      this.sqlQuery = sql;
    },

    jsonPreview(obj) {
      try {
        const str = JSON.stringify(obj);
        return str.length > 50 ? str.substring(0, 47) + '...' : str;
      } catch {
        return '[Object]';
      }
    },

    showJsonDetail(obj) {
      this.currentJson = obj;
      this.showJsonModal = true;
    },

    formatJson(obj) {
      return JSON.stringify(obj, null, 2);
    }
  }
}
</script>

<style scoped>
.sql-executor-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  max-width: 100%;
  margin: 0 auto;
  background-color: rgba(255, 255, 255, 0.95);
  overflow: hidden;
}

header {
  background: linear-gradient(90deg, #2c3e50, #4a6491);
  color: white;
  padding: 15px 30px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
}

.logo {
  display: flex;
  align-items: center;
  gap: 15px;
}

.logo-icon {
  font-size: 28px;
  color: #42b983;
}

.logo h1 {
  font-weight: 600;
  font-size: 22px;
}

.logo p {
  opacity: 0.8;
  font-size: 13px;
  margin-top: 4px;
}

.connection-info {
  background: rgba(255, 255, 255, 0.1);
  padding: 8px 15px;
  border-radius: 20px;
  font-size: 13px;
}

.main-content {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.sidebar {
  width: 260px;
  background: #f8f9fa;
  border-right: 1px solid #eaeaea;
  padding: 15px 0;
  overflow-y: auto;
  flex-shrink: 0;
}

.sidebar-section {
  margin-bottom: 20px;
}

.sidebar-section h3 {
  padding: 0 20px 10px;
  font-size: 16px;
  color: #4a5568;
  border-bottom: 1px solid #eaeaea;
  margin-bottom: 12px;
}

.schema-item {
  padding: 8px 20px 8px 35px;
  cursor: pointer;
  transition: all 0.2s;
  position: relative;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.schema-item i {
  font-size: 14px;
  width: 20px;
}

.schema-item:hover {
  background: #e9ecef;
}

.schema-item.active {
  background: #e3f2fd;
  color: #1a73e8;
  font-weight: 500;
}

.history-item {
  font-size: 13px;
  color: #666;
}

.editor-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.sql-editor-container {
  flex: 0 0 40%;
  display: flex;
  flex-direction: column;
  border-bottom: 1px solid #eaeaea;
  min-height: 200px;
  overflow: hidden;
}

.sql-editor-header {
  padding: 12px 20px;
  background: #f1f3f4;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #eaeaea;
  flex-shrink: 0;
}

.sql-editor-header h2 {
  font-size: 17px;
  color: #2d3748;
}

.toolbar {
  display: flex;
  gap: 8px;
}

.toolbar-btn {
  padding: 7px 12px;
  border: none;
  border-radius: 6px;
  background: #4a6491;
  color: white;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  transition: all 0.2s;
}

.toolbar-btn:hover {
  opacity: 0.9;
  transform: translateY(-1px);
}

.toolbar-btn i {
  font-size: 13px;
}

.toolbar-btn.run {
  background: #42b983;
}

.toolbar-btn.format {
  background: #f0ad4e;
}

.toolbar-btn.clear {
  background: #e74c3c;
}

.sql-editor {
  flex: 1;
  padding: 15px;
  font-family: 'Fira Code', 'Consolas', monospace;
  font-size: 14px;
  line-height: 1.5;
  border: none;
  resize: none;
  outline: none;
  background: #fcfcfc;
  min-height: 100px;
  overflow: auto;
}

.result-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.result-header {
  padding: 12px 20px;
  background: #f1f3f4;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #eaeaea;
  flex-shrink: 0;
}

.result-header h2 {
  font-size: 17px;
  color: #2d3748;
}

.result-info {
  display: flex;
  align-items: center;
  gap: 15px;
  font-size: 13px;
}

.result-status {
  padding: 4px 10px;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 500;
}

.status-success {
  background: #e8f5e9;
  color: #2e7d32;
}

.status-error {
  background: #ffebee;
  color: #c62828;
}

.rows-count, .execution-time {
  color: #5f6368;
  display: flex;
  align-items: center;
  gap: 4px;
}

.result-content {
  flex: 1;
  overflow: auto;
  position: relative;
  min-height: 0;
}

.table-container {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.table-wrapper {
  flex: 1;
  overflow: auto;
  min-height: 200px;
}

.el-table {
  width: 100%;
  min-width: 1000px;
}

.pagination {
  padding: 10px 15px;
  border-top: 1px solid #ebeef5;
  background: #fff;
  display: flex;
  justify-content: flex-end;
}

.null-value {
  color: #b0b0b0;
  font-style: italic;
}

.json-value {
  color: #d35400;
  cursor: pointer;
  text-decoration: underline dotted;
}

.no-data {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: #909399;
  font-size: 14px;
}

.error-container {
  padding: 20px;
  background-color: #ffebee;
  border-radius: 8px;
  margin: 20px;
  color: #c62828;
}

.error-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.error-details {
  font-family: monospace;
  font-size: 14px;
  white-space: pre-wrap;
  background: rgba(255, 255, 255, 0.7);
  padding: 15px;
  border-radius: 6px;
  margin-top: 10px;
  overflow-x: auto;
}

.footer {
  padding: 12px 30px;
  background: #f8f9fa;
  border-top: 1px solid #eaeaea;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: #6c757d;
  flex-shrink: 0;
}

.shortcuts {
  display: flex;
  gap: 15px;
}

.shortcut-item {
  display: flex;
  align-items: center;
  gap: 5px;
}

.key {
  background: #e9ecef;
  padding: 3px 8px;
  border-radius: 4px;
  font-weight: 500;
  font-size: 11px;
}

.json-content {
  background: #f8f8f8;
  padding: 15px;
  border-radius: 4px;
  max-height: 70vh;
  overflow: auto;
  font-family: monospace;
  line-height: 1.5;
  font-size: 14px;
}

@media (max-width: 992px) {
  .main-content {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    border-right: none;
    border-bottom: 1px solid #eaeaea;
    height: 200px;
    overflow: auto;
  }

  .result-info {
    flex-wrap: wrap;
    gap: 8px;
  }
}

@media (max-width: 768px) {
  header {
    flex-direction: column;
    gap: 10px;
    text-align: center;
  }

  .logo {
    flex-direction: column;
    gap: 5px;
  }

  .connection-info {
    margin-top: 5px;
  }

  .toolbar {
    flex-wrap: wrap;
    justify-content: center;
  }

  .footer {
    flex-direction: column;
    gap: 10px;
  }
}
</style>
import request from '@/utils/request'


export function executeSql(sql) {
    return request({
        url: '/sqlUtil/executeSql',
        method: 'post',
        data: { sql: sql }
    })
}


export function fetchTables() {
    return request({
        url: '/sqlUtil/getTables',
        method: 'get'
    })
}

import {Message} from 'element-ui'
import axios from 'axios'

axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8'
import store from "@/store";
import router from '@/router';
// create an axios instance
const service = axios.create({
    baseURL: `http://127.0.0.1:8080`,
    timeout: 75000,
})

export default service

效果:

總結(jié)

該SQL執(zhí)行工具通過四大創(chuàng)新設(shè)計重塑了數(shù)據(jù)庫交互體驗:

  • 元數(shù)據(jù)驅(qū)動:將數(shù)據(jù)庫結(jié)構(gòu)轉(zhuǎn)化為可視化導航
  • 上下文感知:自動識別SQL類型并優(yōu)化執(zhí)行
  • 漸進式渲染:平衡大數(shù)據(jù)量與用戶體驗
  • 歷史時空隧道:完整記錄操作軌跡

到此這篇關(guān)于基于SpringBoot與Vue開發(fā)Web版SQL執(zhí)行工具的文章就介紹到這了,更多相關(guān)SpringBoot開發(fā)SQL執(zhí)行工具內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能

    MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能

    這篇文章主要介紹了MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-06-06
  • 關(guān)于Spring的AnnotationAwareAspectJAutoProxyCreator類解析

    關(guān)于Spring的AnnotationAwareAspectJAutoProxyCreator類解析

    這篇文章主要介紹了關(guān)于Spring的AnnotationAwareAspectJAutoProxyCreator類解析,Spring是一個開源免費的框架 , 容器,是一個輕量級的框架 ,需要的朋友可以參考下
    2023-05-05
  • Sentinel的熔斷降級、資源規(guī)則詳解與實例

    Sentinel的熔斷降級、資源規(guī)則詳解與實例

    這篇文章主要介紹了Sentinel的熔斷降級、資源規(guī)則詳解與實例,Sentinel是阿里巴巴開源的一款流量控制和熔斷降級的框架,它主要用于保護分布式系統(tǒng)中的服務(wù)穩(wěn)定性,Sentinel通過對服務(wù)進行流量控制和熔斷降級,可以有效地保護系統(tǒng)的穩(wěn)定性,需要的朋友可以參考下
    2023-09-09
  • java實現(xiàn)員工工資管理系統(tǒng)

    java實現(xiàn)員工工資管理系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現(xiàn)員工工資管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Java如何實現(xiàn)N叉樹數(shù)據(jù)結(jié)構(gòu)

    Java如何實現(xiàn)N叉樹數(shù)據(jù)結(jié)構(gòu)

    這篇文章主要介紹了Java如何實現(xiàn)N叉樹數(shù)據(jù)結(jié)構(gòu)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • MyBatis傳入數(shù)組集合類并使用foreach遍歷

    MyBatis傳入數(shù)組集合類并使用foreach遍歷

    這篇文章主要介紹了MyBatis傳入數(shù)組集合類并使用foreach遍歷,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • 利用Maven入手Spring Boot第一個程序詳解

    利用Maven入手Spring Boot第一個程序詳解

    這篇文章主要給大家介紹了關(guān)于如何利用Maven入手Spring Boot第一個程序的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-02-02
  • JavaWeb如何實現(xiàn)限制單個賬號多處登錄

    JavaWeb如何實現(xiàn)限制單個賬號多處登錄

    這篇文章主要介紹了JavaWeb如何實現(xiàn)限制單個賬號多處登錄問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Spring項目使用Maven和BCrypt實現(xiàn)修改密碼功能方式

    Spring項目使用Maven和BCrypt實現(xiàn)修改密碼功能方式

    在數(shù)字時代,信息安全尤其是密碼安全至關(guān)重要,本文通過實例探討如何在Spring項目中利用Maven和BCrypt實現(xiàn)一個安全的密碼修改功能,我們將從環(huán)境搭建到編寫核心業(yè)務(wù)邏輯,再到完成功能測試,確保每一步都遵循最佳安全實踐,通過本文,你將了解到密碼安全的重要性
    2024-10-10
  • JAXB命名空間_動力節(jié)點Java學院整理

    JAXB命名空間_動力節(jié)點Java學院整理

    這篇文章主要為大家詳細介紹了JAXB命名空間的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08

最新評論