Sfoglia il codice sorgente

!1808 库表管理 操作添加列位置移动功能
Merge pull request !1808 from CyrusZhou/v2

zuohuaijun 10 mesi fa
parent
commit
9a1d74fd69

+ 23 - 0
Admin.NET/Admin.NET.Core/Service/DataBase/Dto/DbColumnInput.cs

@@ -46,6 +46,29 @@ public class UpdateDbColumnInput
     public string DefaultValue { get; set; }
 }
 
+public class MoveDbColumnInput
+{
+    /// <summary>
+    /// 数据库配置ID
+    /// </summary>
+    public string ConfigId { get; set; }
+
+    /// <summary>
+    /// 目标表名
+    /// </summary>
+    public string TableName { get; set; }
+
+    /// <summary>
+    ///要移动的列名
+    /// </summary>
+    public string ColumnName { get; set; }
+
+    /// <summary>
+    /// 移动到该列后方(为空时移动到首列)
+    /// </summary>
+    public string AfterColumnName { get; set; }
+}
+
 public class DeleteDbColumnInput
 {
     public string ConfigId { get; set; }

+ 68 - 0
Admin.NET/Admin.NET.Core/Service/DataBase/SysDatabaseService.cs

@@ -178,6 +178,74 @@ public class SysDatabaseService : IDynamicApiController, ITransient
         db.DbMaintenance.AddColumnRemark(input.ColumnName, input.TableName, string.IsNullOrWhiteSpace(input.Description) ? input.ColumnName : input.Description);
     }
 
+    /// <summary>
+    /// 移动列位置 🔖
+    /// </summary>
+    /// <param name="input"></param>
+    [ApiDescriptionSettings(Name = "MoveColumn"), HttpPost]
+    [DisplayName("移动列")]
+    public void MoveColumn(MoveDbColumnInput input)
+    {
+        var db = _db.AsTenant().GetConnectionScope(input.ConfigId);
+        var dbMaintenance = db.DbMaintenance;
+
+        var dbType = db.CurrentConnectionConfig.DbType;
+
+        var columns = dbMaintenance.GetColumnInfosByTableName(input.TableName, false);
+        var targetColumn = columns.FirstOrDefault(c =>
+            c.DbColumnName.Equals(input.ColumnName, StringComparison.OrdinalIgnoreCase));
+
+        if (targetColumn == null)
+            throw new Exception($"列 {input.ColumnName} 在表 {input.TableName} 中不存在");
+
+        switch (dbType)
+        {
+            case SqlSugar.DbType.MySql:
+                MoveColumnInMySQL(db, input.TableName, input.ColumnName, input.AfterColumnName);
+                break;
+            default:
+                throw new NotSupportedException($"暂不支持 {dbType} 数据库的列移动操作");
+        }
+    }
+
+    // MySQL 列移动实现
+    private void MoveColumnInMySQL(ISqlSugarClient db, string tableName, string columnName, string afterColumnName)
+    {
+        // 1. 获取完整的列定义(修复原方法)
+        var columnDef = db.Ado.SqlQuery<dynamic>(
+            $"SHOW FULL COLUMNS FROM `{tableName}` WHERE Field = '{columnName}'"
+        ).FirstOrDefault();
+
+        if (columnDef == null)
+            throw new Exception($"Column {columnName} not found");
+
+        // 2. 构建列定义字符串
+        var definition = new StringBuilder();
+        definition.Append($"`{columnName}` ");  // 列名
+        definition.Append($"{columnDef.Type} "); // 数据类型
+
+        // 处理约束条件
+        definition.Append(columnDef.Null == "YES" ? "NULL " : "NOT NULL ");
+        if (columnDef.Default != null)
+            definition.Append($"DEFAULT '{columnDef.Default}' ");
+        if (!string.IsNullOrEmpty(columnDef.Extra))
+            definition.Append($"{columnDef.Extra} ");
+        if (!string.IsNullOrEmpty(columnDef.Comment))
+            definition.Append($"COMMENT '{columnDef.Comment.Replace("'", "''")}'");
+
+        // 3. 构建移动SQL
+        var sql = new StringBuilder();
+        sql.Append($"ALTER TABLE `{tableName}` MODIFY COLUMN {definition}");
+
+        if (string.IsNullOrEmpty(afterColumnName))
+            sql.Append(" FIRST");
+        else
+            sql.Append($" AFTER `{afterColumnName}`");
+
+        // 4. 执行命令
+        db.Ado.ExecuteCommand(sql.ToString());
+    }
+
     /// <summary>
     /// 获取表列表 🔖
     /// </summary>

+ 3 - 0
Web/src/views/system/database/component/addColumn.vue

@@ -92,6 +92,9 @@ const state = reactive({
 // 打开弹窗
 const openDialog = (addRow: DbColumnInput) => {
 	state.ruleForm = addRow;
+	if (state.ruleForm.length === 0) {
+		state.ruleForm.length = 32;
+	}
 	state.isShowDialog = true;
 	ruleFormRef.value?.resetFields();
 };

+ 52 - 5
Web/src/views/system/database/index.vue

@@ -59,10 +59,12 @@
 				<el-table-column prop="decimalDigits" label="精度" width="70" align="center" show-overflow-tooltip />
 				<el-table-column prop="defaultValue" label="默认值" align="center" show-overflow-tooltip />
 				<el-table-column prop="columnDescription" label="描述" header-align="center" show-overflow-tooltip />
-				<el-table-column label="操作" width="145" fixed="right" align="center" show-overflow-tooltip>
+				<el-table-column label="操作" width="195" fixed="right" align="center" show-overflow-tooltip>
 					<template #default="scope">
-						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditColumn(scope.row)"> 编辑 </el-button>
-						<el-button icon="ele-Delete" size="small" text type="danger" @click="delColumn(scope.row)"> 删除 </el-button>
+						<el-button icon="ele-Top" size="small" text type="primary" @click="moveColumn(scope.row, 'up')" :disabled="scope.$index === 0" title="上移"></el-button>
+						<el-button icon="ele-Bottom" size="small" text type="primary" @click="moveColumn(scope.row, 'down')" :disabled="scope.$index === state.columnData.length - 1" title="下移"></el-button>
+						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditColumn(scope.row)">编辑</el-button>
+						<el-button icon="ele-Delete" size="small" text type="danger" @click="delColumn(scope.row)">删除</el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -90,7 +92,7 @@ import GenSeedData from '/@/views/system/database/component/genSeedData.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysDatabaseApi, SysCodeGenApi } from '/@/api-services/api';
-import { DbColumnOutput, DbTableInfo, DbColumnInput, DeleteDbTableInput, DeleteDbColumnInput } from '/@/api-services/models';
+import { DbColumnOutput, DbTableInfo, DbColumnInput, DeleteDbTableInput, DeleteDbColumnInput, MoveDbColumnInput } from '/@/api-services/models';
 
 const editTableRef = ref<InstanceType<typeof EditTable>>();
 const editColumnRef = ref<InstanceType<typeof EditColumn>>();
@@ -312,12 +314,57 @@ const delColumn = (row: any) => {
 				dbColumnName: row.dbColumnName,
 			};
 			await getAPI(SysDatabaseApi).apiSysDatabaseDeleteColumnPost(eleteDbColumnInput);
-			handleQueryTable();
+			handleQueryColumn();
 			ElMessage.success('列删除成功');
 		})
 		.catch(() => {});
 };
 
+const moveColumn = (row: any, direction: 'up' | 'down') => {
+	const { columnData, tableName, configId } = state;
+	const currentIndex = columnData.findIndex((item) => item.dbColumnName === row.dbColumnName);
+
+	// 边界检查与反馈
+	if (direction === 'up' && currentIndex === 0) {
+		ElMessage.warning('已处于首位,无法上移');
+		return;
+	}
+	if (direction === 'down' && currentIndex === columnData.length - 1) {
+		ElMessage.warning('已处于末位,无法下移');
+		return;
+	}
+
+	// 计算目标位置
+	const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
+	const targetColumn = columnData[targetIndex];
+	const columnName = direction === 'up' ? targetColumn.dbColumnName : row.dbColumnName;
+	const afterColumnName = direction === 'up' ? row.dbColumnName : targetColumn.dbColumnName;
+
+	ElMessageBox.confirm(`确定将列【${row.dbColumnName}】${direction === 'up' ? '上移' : '下移'}?`, '操作确认', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+			try {
+				const moveParams: MoveDbColumnInput = {
+					configId,
+					tableName,
+					columnName,
+					afterColumnName,
+				};
+
+				// 调用API
+				await getAPI(SysDatabaseApi).apiSysDatabaseMoveColumnPost(moveParams);
+
+				handleQueryColumn();
+				ElMessage.success('列位置已更新');
+			} catch (error: any) {
+				ElMessage.error(`操作失败: ${error.message || '未知错误'}`);
+			}
+		})
+		.catch(() => {});
+};
+
 // 可视化表
 const visualTable = () => {
 	if (state.configId == '') {