新增表字段迁移并发布1.0.19

This commit is contained in:
chen qiang
2026-08-26 17:00:40 +08:00
parent 7d4d3400e1
commit 1be3c31ec1
11 changed files with 206 additions and 32 deletions
+8 -4
View File
@@ -42,7 +42,7 @@
- `App.xaml`:应用资源、HandyControl 主题和 `MainViewModel` 资源注册;启动页是 `Views/HomeWindow.xaml`。 - `App.xaml`:应用资源、HandyControl 主题和 `MainViewModel` 资源注册;启动页是 `Views/HomeWindow.xaml`。
- `App.xaml.cs`:Velopack 初始化、旧版 `current/config.ini` 更新迁移、后台/手动更新、更新前后 `config.ini`、`data/` 与 `pre/` 备份恢复、全局异常日志。 - `App.xaml.cs`:Velopack 初始化、旧版 `current/config.ini` 更新迁移、后台/手动更新、更新前后 `config.ini`、`data/` 与 `pre/` 备份恢复、全局异常日志。
- `Views/HomeWindow.xaml`:单窗口主界面,包含五个迁移 Tab、连接状态、SQL 编辑区和批量操作。 - `Views/HomeWindow.xaml`:单窗口主界面,包含六个迁移 Tab、连接状态、SQL 编辑区和批量操作。
- `Views/HomeWindow.xaml.cs`:只处理窗口关闭和手动检查更新等窗口事件。 - `Views/HomeWindow.xaml.cs`:只处理窗口关闭和手动检查更新等窗口事件。
- `Views/ConnectionDialog.*`:源库/目标库连接配置与测试。 - `Views/ConnectionDialog.*`:源库/目标库连接配置与测试。
- `Views/ErrorDialog.*`:错误详情展示和复制。 - `Views/ErrorDialog.*`:错误详情展示和复制。
@@ -50,7 +50,7 @@
### 状态与编排 ### 状态与编排
- `ViewModels/MainViewModel.cs`:主状态容器和命令编排;初始化五个 `TabServices`,管理数据库连接、单行/批量读写、SQL 编辑与断点续写。 - `ViewModels/MainViewModel.cs`:主状态容器和命令编排;初始化六个 `TabServices`,管理数据库连接、单行/批量读写、SQL 编辑与断点续写。
- `Models/TableRowModel.cs`:每个迁移对象的名称、所属 Tab、读写状态。 - `Models/TableRowModel.cs`:每个迁移对象的名称、所属 Tab、读写状态。
- `Enums/`:Tab、状态、工作类型和输出扩展名枚举。 - `Enums/`:Tab、状态、工作类型和输出扩展名枚举。
@@ -60,6 +60,7 @@
- `Services/TabServices.cs`:索引列表加载/保存、单行读写、排序、删除和状态切换。 - `Services/TabServices.cs`:索引列表加载/保存、单行读写、排序、删除和状态切换。
- `Services/DbServices.cs`:Oracle SQL 的提取与执行核心: - `Services/DbServices.cs`:Oracle SQL 的提取与执行核心:
- `GetTableSql`:读取表 DDL、注释、主外键、唯一键和索引; - `GetTableSql`:读取表 DDL、注释、主外键、唯一键和索引;
- `GetTableField`:按 `TABLE.COLUMN` 读取单个物理表字段,不查询 BOS 元数据;目标字段不存在时新增,已存在时跳过;
- `GetTableBos`:读取 BOS 配置; - `GetTableBos`:读取 BOS 配置;
- `GetTableBosField`:按 `TABLE.COLUMN` 读取单个 BOS 字段配置,生成目标物理字段补充 SQL,并包含关联限定值/引用元数据; - `GetTableBosField`:按 `TABLE.COLUMN` 读取单个 BOS 字段配置,生成目标物理字段补充 SQL,并包含关联限定值/引用元数据;
- `GetPrcSql` / `GetAllPrc`:读取存储过程或函数源码; - `GetPrcSql` / `GetAllPrc`:读取存储过程或函数源码;
@@ -71,11 +72,12 @@
## 真实数据流 ## 真实数据流
五个 Tab 的映射由 `MainViewModel` 构造函数定义: 六个 Tab 的映射由 `MainViewModel` 构造函数定义:
| Tab | 索引文件 | 内容文件 | 源库读取方法 | | Tab | 索引文件 | 内容文件 | 源库读取方法 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 表配置 | `data/from/table.txt` | `data/{对象名}_creat.txt` | `DbServices.GetTableSql` | | 表配置 | `data/from/table.txt` | `data/{对象名}_creat.txt` | `DbServices.GetTableSql` |
| 表字段 | `data/from/table_field.txt` | `data/{表名}.{字段名}_table_field.txt` | `DbServices.GetTableField` |
| BOS 配置 | `data/from/bos.txt` | `data/{对象名}_bos.txt` | `DbServices.GetTableBos` | | BOS 配置 | `data/from/bos.txt` | `data/{对象名}_bos.txt` | `DbServices.GetTableBos` |
| BOS 字段配置 | `data/from/bos_field.txt` | `data/{表名}.{字段名}_bos_field.txt` | `DbServices.GetTableBosField` | | BOS 字段配置 | `data/from/bos_field.txt` | `data/{表名}.{字段名}_bos_field.txt` | `DbServices.GetTableBosField` |
| 存储过程 | `data/from/prc.txt` | `data/{对象名}_prc.txt` | `DbServices.GetPrcSql` | | 存储过程 | `data/from/prc.txt` | `data/{对象名}_prc.txt` | `DbServices.GetPrcSql` |
@@ -83,6 +85,8 @@
读取流程:索引项 -> 调用源库读取委托 -> 生成 SQL 文本 -> 保存到 `data/` -> 更新读取状态并显示内容。 读取流程:索引项 -> 调用源库读取委托 -> 生成 SQL 文本 -> 保存到 `data/` -> 更新读取状态并显示内容。
“表字段”位于“表配置”之后,列表项必须使用 `TABLE.COLUMN`。它只读取 Oracle 物理表字段定义和字段注释,不检查 `AD_TABLE`、`AD_COLUMN` 等 BOS 元数据。源物理表或字段不存在时读取失败;目标表不存在时写入失败;目标字段已存在时整项跳过,否则按源字段类型、默认值、可空性和注释新增。
读取 BOS 配置前会检查源库当前用户下的辅助过程。整表读取使用 `HMIG_GET_TABLE_BOS_31`,单字段读取使用 `HMIG_GET_TABLE_BOS_FIELD_31`;若过程不存在,程序从运行目录的同名 `pre/*.prc` 脚本安装,检查 `USER_ERRORS` 和对象状态后再读取。单字段列表项必须采用 `TABLE.COLUMN` 格式。界面不提供手动“预处理”入口。 读取 BOS 配置前会检查源库当前用户下的辅助过程。整表读取使用 `HMIG_GET_TABLE_BOS_31`,单字段读取使用 `HMIG_GET_TABLE_BOS_FIELD_31`;若过程不存在,程序从运行目录的同名 `pre/*.prc` 脚本安装,检查 `USER_ERRORS` 和对象状态后再读取。单字段列表项必须采用 `TABLE.COLUMN` 格式。界面不提供手动“预处理”入口。
单字段 SQL 写入目标库时,限定值组按名称、限定值按组和值判断,只补充缺失项;引用元数据按引用表名和字段 `DBNAME` 映射,目标引用字段不存在或不唯一时直接报错,不自动创建。 单字段 SQL 写入目标库时,限定值组按名称、限定值按组和值判断,只补充缺失项;引用元数据按引用表名和字段 `DBNAME` 映射,目标引用字段不存在或不唯一时直接报错,不自动创建。
@@ -147,7 +151,7 @@ dotnet build .\henlo_migration_wpf10\henlo_migration_wpf10.csproj -c Debug --no-
1. 所有 C#/XAML 修改至少执行一次主项目构建。 1. 所有 C#/XAML 修改至少执行一次主项目构建。
2. 仅文档修改可不构建,但要重新读取文件检查 Markdown、路径和命令。 2. 仅文档修改可不构建,但要重新读取文件检查 Markdown、路径和命令。
3. XAML/绑定修改除构建外,应在 Windows 上启动应用,检查五个 Tab、连接弹窗、按钮可用性和错误弹窗;不要因此连接生产库。 3. XAML/绑定修改除构建外,应在 Windows 上启动应用,检查六个 Tab、连接弹窗、按钮可用性和错误弹窗;不要因此连接生产库。
4. SQL 生成/拆分修改需用不含真实数据的代表性 SQL 验证表 DDL、注释、约束、PL/SQL 和 INSERT。 4. SQL 生成/拆分修改需用不含真实数据的代表性 SQL 验证表 DDL、注释、约束、PL/SQL 和 INSERT。
5. 更新逻辑修改需分别考虑普通 `dotnet run`(非 Velopack 安装)和 Velopack 安装态。 5. 更新逻辑修改需分别考虑普通 `dotnet run`(非 Velopack 安装)和 Velopack 安装态。
+7
View File
@@ -1,5 +1,12 @@
# 更新记录 # 更新记录
## 1.0.19 - 2026-08-26
### 新增
- 新增“表字段”迁移页签,支持按 `TABLE.COLUMN` 从源库读取物理字段定义并生成独立 SQL 文件。
- 写入目标库时,目标字段已存在则跳过;目标字段不存在时按源库字段类型、默认值、非空约束及注释新增,不依赖 BOS 字段配置。
## 1.0.18 - 2026-08-26 ## 1.0.18 - 2026-08-26
### 修复 ### 修复
+1 -1
View File
@@ -74,7 +74,7 @@ cd .\henlo_migration_wpf10
- `.csproj` 只复制 `pre/*.txt|*.fnc|*.prc` 等程序辅助脚本,不复制本机 `config.ini` 或 `data/from/*.txt`;`pack.bat` 还会在打包前再次删除 `publish/config.ini`,并创建空的 `data/from/` 目录。 - `.csproj` 只复制 `pre/*.txt|*.fnc|*.prc` 等程序辅助脚本,不复制本机 `config.ini` 或 `data/from/*.txt`;`pack.bat` 还会在打包前再次删除 `publish/config.ini`,并创建空的 `data/from/` 目录。
- `pre/hmig_get_table_bos_31.prc`、`pre/hmig_get_table_bos_field_31.prc` 等运行时辅助过程必须出现在发布输出中。 - `pre/hmig_get_table_bos_31.prc`、`pre/hmig_get_table_bos_field_31.prc` 等运行时辅助过程必须出现在发布输出中。
- 新安装的索引列表为空;程序添加第一条对应配置时会在 `data/from/` 创建 `table.txt`、`bos.txt`、`bos_field.txt`、`prc.txt` 或 `data.txt`。 - 新安装的索引列表为空;程序添加第一条对应配置时会在 `data/from/` 创建 `table.txt`、`table_field.txt`、`bos.txt`、`bos_field.txt`、`prc.txt` 或 `data.txt`。
- 新安装首次运行时会在 Velopack 根目录(与 `Update.exe` 同级)创建值为空的 `config.ini`;普通未打包运行时仍在程序目录创建。不得把真实或样例账号密码放入发布产物。 - 新安装首次运行时会在 Velopack 根目录(与 `Update.exe` 同级)创建值为空的 `config.ini`;普通未打包运行时仍在程序目录创建。不得把真实或样例账号密码放入发布产物。
## Velopack 产物 ## Velopack 产物
+2 -2
View File
@@ -38,7 +38,7 @@ namespace henlo_migration.Common
/// <summary>Oracle 连接配置文件路径</summary> /// <summary>Oracle 连接配置文件路径</summary>
public static string ConfigPath => Path.Combine(PersistentRootPath, "config.ini"); public static string ConfigPath => Path.Combine(PersistentRootPath, "config.ini");
/// <summary>源数据文件夹(存放 table.txt、bos.txt、prc.txt、data.txt 及各表的 .txt 文件)</summary> /// <summary>源数据文件夹(存放 table.txt、table_field.txt、bos.txt、prc.txt、data.txt 及各对象的 .txt 文件)</summary>
public static string DataPath public static string DataPath
{ {
get get
@@ -49,7 +49,7 @@ namespace henlo_migration.Common
} }
} }
/// <summary>索引文件目录(存放 table.txt、bos.txt、prc.txt、data.txt)</summary> /// <summary>索引文件目录(存放 table.txt、table_field.txt、bos.txt、bos_field.txt、prc.txt、data.txt)</summary>
public static string DataFromPath public static string DataFromPath
{ {
get get
+2 -1
View File
@@ -6,6 +6,7 @@ namespace henlo_migration.Enums
bos_field, bos_field,
creat, creat,
data, data,
prc prc,
table_field
} }
} }
+5 -4
View File
@@ -3,9 +3,10 @@ namespace henlo_migration.Enums
public enum TabsEnum public enum TabsEnum
{ {
表 = 0, 表 = 0,
BOS表配置 = 1, 表字段 = 1,
BOS字段配置 = 2, BOS表配置 = 2,
存储过程 = 3, BOS字段配置 = 3,
表数据 = 4 存储过程 = 4,
表数据 = 5
} }
} }
+6 -1
View File
@@ -11,6 +11,7 @@
| 功能 | 说明 | | 功能 | 说明 |
|------|------| |------|------|
| **表结构迁移** | 读取源库表结构(DDL)、表注释、字段注释、主键/外键/索引,写入目标库 | | **表结构迁移** | 读取源库表结构(DDL)、表注释、字段注释、主键/外键/索引,写入目标库 |
| **表字段迁移** | 按 `TABLE.COLUMN` 读取单个物理字段;目标字段不存在时新增,已存在时跳过 |
| **存储过程迁移** | 读取/写入存储过程和函数源码 | | **存储过程迁移** | 读取/写入存储过程和函数源码 |
| **BOS 配置迁移** | 读取 BOS 表配置写入目标库 | | **BOS 配置迁移** | 读取 BOS 表配置写入目标库 |
| **BOS 字段配置迁移** | 按 `TABLE.COLUMN` 补充目标物理字段并迁移字段配置、限定值和引用元数据 | | **BOS 字段配置迁移** | 按 `TABLE.COLUMN` 补充目标物理字段并迁移字段配置、限定值和引用元数据 |
@@ -101,12 +102,16 @@ henlo_migration_wpf10/
| 文件 | 用途 | | 文件 | 用途 |
|------|------| |------|------|
| `table.txt` | 表结构列表 | | `table.txt` | 表结构列表 |
| `table_field.txt` | 物理表字段列表,每行使用 `TABLE.COLUMN` 格式 |
| `bos.txt` | BOS 配置列表 | | `bos.txt` | BOS 配置列表 |
| `bos_field.txt` | BOS 单字段配置列表,每行使用 `TABLE.COLUMN` 格式 | | `bos_field.txt` | BOS 单字段配置列表,每行使用 `TABLE.COLUMN` 格式 |
| `prc.txt` | 存储过程/函数列表 | | `prc.txt` | 存储过程/函数列表 |
| `data.txt` | 表数据列表 | | `data.txt` | 表数据列表 |
每行一个对象名称,读取数据库时会自动更新内容。BOS 单字段配置生成 每行一个对象名称,读取数据库时会自动更新内容。表字段生成
`data/{表名}.{字段名}_table_field.txt`,只读取源库物理表字段和字段注释,不检查 BOS 是否维护该字段;写入时目标表不存在会报错,目标字段已存在则跳过,否则按源字段类型、默认值、可空性和注释新增。
BOS 单字段配置生成
`data/{表名}.{字段名}_bos_field.txt`;源库缺少单字段读取过程时,程序会从 `data/{表名}.{字段名}_bos_field.txt`;源库缺少单字段读取过程时,程序会从
`pre/hmig_get_table_bos_field_31.prc` 自动安装后再读取。 `pre/hmig_get_table_bos_field_31.prc` 自动安装后再读取。
+77 -3
View File
@@ -229,8 +229,8 @@ namespace henlo_migration.Services
return textResult.Rows.Count > 0 ? textResult.Rows[0][colName]?.ToString() ?? "" : ""; return textResult.Rows.Count > 0 ? textResult.Rows[0][colName]?.ToString() ?? "" : "";
} }
/// <summary>解析 TABLE.COLUMN 格式的 BOS 字段名称</summary> /// <summary>解析 TABLE.COLUMN 格式的表字段名称</summary>
public static bool TryParseBosFieldName(string qualifiedFieldName, out string tableName, out string columnName) public static bool TryParseTableFieldName(string qualifiedFieldName, out string tableName, out string columnName)
{ {
tableName = ""; tableName = "";
columnName = ""; columnName = "";
@@ -247,10 +247,84 @@ namespace henlo_migration.Services
return tableName.Length > 0 && columnName.Length > 0; return tableName.Length > 0 && columnName.Length > 0;
} }
/// <summary>读取源库单个物理表字段,生成目标字段不存在时才新增的 SQL;不读取 BOS 元数据</summary>
public static string GetTableField(string qualifiedFieldName)
{
if (!TryParseTableFieldName(qualifiedFieldName, out string tableName, out string columnName))
throw new ArgumentException("表字段必须使用 TABLE.COLUMN 格式。", nameof(qualifiedFieldName));
var db = SqlSugarHelper.DbOrig;
int sourceTableCount = db.Ado.GetInt(
"SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = :table_name",
new SugarParameter("table_name", tableName));
if (sourceTableCount == 0)
throw new InvalidOperationException($"源库不存在物理表:{tableName}");
var columnResult = db.Ado.GetDataTable(@"
SELECT DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE,
CHAR_LENGTH, CHAR_USED, NULLABLE, DATA_DEFAULT
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = :table_name
AND COLUMN_NAME = :column_name",
new SugarParameter("table_name", tableName),
new SugarParameter("column_name", columnName));
if (columnResult.Rows.Count == 0)
throw new InvalidOperationException($"源库物理表 {tableName} 不存在字段:{columnName}");
DataRow columnRow = columnResult.Rows[0];
string columnDefinition = BuildOracleColumnType(columnRow);
string dataDefault = columnRow["DATA_DEFAULT"] == DBNull.Value
? ""
: columnRow["DATA_DEFAULT"]?.ToString()?.Trim() ?? "";
if (!string.IsNullOrWhiteSpace(dataDefault))
columnDefinition += $" DEFAULT {dataDefault}";
if (string.Equals(columnRow["NULLABLE"]?.ToString(), "N", StringComparison.OrdinalIgnoreCase))
columnDefinition += " NOT NULL";
string quotedTable = QuoteOracleIdentifier(tableName);
string quotedColumn = QuoteOracleIdentifier(columnName);
string alterSql = $"ALTER TABLE {quotedTable} ADD ({quotedColumn} {columnDefinition})";
string commentSql = "";
var commentResult = db.Ado.GetDataTable(@"
SELECT COMMENTS
FROM USER_COL_COMMENTS
WHERE TABLE_NAME = :table_name
AND COLUMN_NAME = :column_name
AND COMMENTS IS NOT NULL",
new SugarParameter("table_name", tableName),
new SugarParameter("column_name", columnName));
if (commentResult.Rows.Count > 0)
{
string comment = EscapeQuote(commentResult.Rows[0]["COMMENTS"]?.ToString());
commentSql = $"COMMENT ON COLUMN {quotedTable}.{quotedColumn} IS '{comment}'";
}
var sql = new StringBuilder();
sql.AppendLine($"-- 新增物理表字段(已存在则跳过):{tableName}.{columnName}");
sql.AppendLine("DECLARE");
sql.AppendLine(" v_table_count NUMBER;");
sql.AppendLine(" v_column_count NUMBER;");
sql.AppendLine("BEGIN");
sql.AppendLine($" SELECT COUNT(*) INTO v_table_count FROM user_tables WHERE table_name = '{EscapeQuote(tableName)}';");
sql.AppendLine(" IF v_table_count = 0 THEN");
sql.AppendLine($" RAISE_APPLICATION_ERROR(-20201, '目标库缺少物理表:{EscapeQuote(tableName)}');");
sql.AppendLine(" END IF;");
sql.AppendLine($" SELECT COUNT(*) INTO v_column_count FROM user_tab_columns WHERE table_name = '{EscapeQuote(tableName)}' AND column_name = '{EscapeQuote(columnName)}';");
sql.AppendLine(" IF v_column_count = 0 THEN");
sql.AppendLine($" EXECUTE IMMEDIATE '{EscapeQuote(alterSql)}';");
if (!string.IsNullOrWhiteSpace(commentSql))
sql.AppendLine($" EXECUTE IMMEDIATE '{EscapeQuote(commentSql)}';");
sql.AppendLine(" END IF;");
sql.AppendLine("END;");
sql.AppendLine("/");
return sql.ToString();
}
/// <summary>获取单个 BOS 字段配置,返回可在目标库执行的完整 PL/SQL</summary> /// <summary>获取单个 BOS 字段配置,返回可在目标库执行的完整 PL/SQL</summary>
public static string GetTableBosField(string qualifiedFieldName) public static string GetTableBosField(string qualifiedFieldName)
{ {
if (!TryParseBosFieldName(qualifiedFieldName, out string tableName, out string columnName)) if (!TryParseTableFieldName(qualifiedFieldName, out string tableName, out string columnName))
throw new ArgumentException("BOS 字段配置必须使用 TABLE.COLUMN 格式。", nameof(qualifiedFieldName)); throw new ArgumentException("BOS 字段配置必须使用 TABLE.COLUMN 格式。", nameof(qualifiedFieldName));
string physicalColumnSql = BuildPhysicalColumnEnsureSql(tableName, columnName); string physicalColumnSql = BuildPhysicalColumnEnsureSql(tableName, columnName);
@@ -16,8 +16,9 @@ namespace henlo_migration.ViewModels
{ {
public class MainViewModel : INotifyPropertyChanged public class MainViewModel : INotifyPropertyChanged
{ {
// 五个 Tab 的数据 // 六个 Tab 的数据
public TabServices TableTab { get; } public TabServices TableTab { get; }
public TabServices TableFieldTab { get; }
public TabServices BosTab { get; } public TabServices BosTab { get; }
public TabServices BosFieldTab { get; } public TabServices BosFieldTab { get; }
public TabServices PrcTab { get; } public TabServices PrcTab { get; }
@@ -33,6 +34,7 @@ namespace henlo_migration.ViewModels
_currentTab = value; _currentTab = value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(TablesData)); OnPropertyChanged(nameof(TablesData));
OnPropertyChanged(nameof(TableFieldsData));
OnPropertyChanged(nameof(BosTablesData)); OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(BosFieldsData)); OnPropertyChanged(nameof(BosFieldsData));
OnPropertyChanged(nameof(StoredProcsData)); OnPropertyChanged(nameof(StoredProcsData));
@@ -51,14 +53,16 @@ namespace henlo_migration.ViewModels
CurrentTab = value switch CurrentTab = value switch
{ {
0 => TableTab, 0 => TableTab,
1 => BosTab, 1 => TableFieldTab,
2 => BosFieldTab, 2 => BosTab,
3 => PrcTab, 3 => BosFieldTab,
4 => DataTab, 4 => PrcTab,
5 => DataTab,
_ => TableTab _ => TableTab
}; };
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(TablesData)); OnPropertyChanged(nameof(TablesData));
OnPropertyChanged(nameof(TableFieldsData));
OnPropertyChanged(nameof(BosTablesData)); OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(BosFieldsData)); OnPropertyChanged(nameof(BosFieldsData));
OnPropertyChanged(nameof(StoredProcsData)); OnPropertyChanged(nameof(StoredProcsData));
@@ -68,8 +72,9 @@ namespace henlo_migration.ViewModels
} }
} }
// 五个Tab数据源别名(供DataGrid绑定) // 六个Tab数据源别名(供DataGrid绑定)
public ObservableCollection<TableRowModel> TablesData => TableTab.List; public ObservableCollection<TableRowModel> TablesData => TableTab.List;
public ObservableCollection<TableRowModel> TableFieldsData => TableFieldTab.List;
public ObservableCollection<TableRowModel> BosTablesData => BosTab.List; public ObservableCollection<TableRowModel> BosTablesData => BosTab.List;
public ObservableCollection<TableRowModel> BosFieldsData => BosFieldTab.List; public ObservableCollection<TableRowModel> BosFieldsData => BosFieldTab.List;
public ObservableCollection<TableRowModel> StoredProcsData => PrcTab.List; public ObservableCollection<TableRowModel> StoredProcsData => PrcTab.List;
@@ -78,10 +83,11 @@ namespace henlo_migration.ViewModels
public string CurrentTabName => SelectedTabIndex switch public string CurrentTabName => SelectedTabIndex switch
{ {
0 => "表配置", 0 => "表配置",
1 => "BOS配置", 1 => "表字段",
2 => "BOS字段配置", 2 => "BOS配置",
3 => "存储过程", 3 => "BOS字段配置",
4 => "表数据", 4 => "存储过程",
5 => "表数据",
_ => "" _ => ""
}; };
@@ -269,10 +275,13 @@ namespace henlo_migration.ViewModels
DestUser = SysConfig.GetDestUser(); DestUser = SysConfig.GetDestUser();
DestPwd = SysConfig.GetDestPwd(); DestPwd = SysConfig.GetDestPwd();
// 初始化五个 TabServices(索引文件在 from/,内容文件在 data/) // 初始化六个 TabServices(索引文件在 from/,内容文件在 data/)
TableTab = new TabServices(new TabConfig(TabsEnum.表, CommonHelper.DataFromPath, CommonHelper.DataPath, "table.txt", CodeExt.creat, TableTab = new TabServices(new TabConfig(TabsEnum.表, CommonHelper.DataFromPath, CommonHelper.DataPath, "table.txt", CodeExt.creat,
DbServices.GetTableSql, (p, c) => SqlCode = c)); DbServices.GetTableSql, (p, c) => SqlCode = c));
TableFieldTab = new TabServices(new TabConfig(TabsEnum.表字段, CommonHelper.DataFromPath, CommonHelper.DataPath, "table_field.txt", CodeExt.table_field,
DbServices.GetTableField, (p, c) => SqlCode = c));
BosTab = new TabServices(new TabConfig(TabsEnum.BOS表配置, CommonHelper.DataFromPath, CommonHelper.DataPath, "bos.txt", CodeExt.bos, BosTab = new TabServices(new TabConfig(TabsEnum.BOS表配置, CommonHelper.DataFromPath, CommonHelper.DataPath, "bos.txt", CodeExt.bos,
DbServices.GetTableBos, (p, c) => SqlCode = c)); DbServices.GetTableBos, (p, c) => SqlCode = c));
@@ -286,7 +295,7 @@ namespace henlo_migration.ViewModels
DbServices.GetTableData, (p, c) => SqlCode = c)); DbServices.GetTableData, (p, c) => SqlCode = c));
// Tab行点击 -> 显示SQL // Tab行点击 -> 显示SQL
foreach (var tab in new[] { TableTab, BosTab, BosFieldTab, PrcTab, DataTab }) foreach (var tab in new[] { TableTab, TableFieldTab, BosTab, BosFieldTab, PrcTab, DataTab })
{ {
tab.RowClickEvent += (path, code) => tab.RowClickEvent += (path, code) =>
{ {
@@ -380,11 +389,12 @@ namespace henlo_migration.ViewModels
if (!string.IsNullOrWhiteSpace(InputText)) if (!string.IsNullOrWhiteSpace(InputText))
{ {
string itemName = InputText.Trim(); string itemName = InputText.Trim();
if (CurrentTab == BosFieldTab) if (CurrentTab == TableFieldTab || CurrentTab == BosFieldTab)
{ {
if (!DbServices.TryParseBosFieldName(itemName, out string tableName, out string columnName)) if (!DbServices.TryParseTableFieldName(itemName, out string tableName, out string columnName))
{ {
HandyControl.Controls.Growl.Warning("BOS 字段配置必须使用 TABLE.COLUMN 格式"); string tabName = CurrentTab == TableFieldTab ? "表字段" : "BOS 字段配置";
HandyControl.Controls.Growl.Warning($"{tabName}必须使用 TABLE.COLUMN 格式");
return; return;
} }
itemName = $"{tableName}.{columnName}"; itemName = $"{tableName}.{columnName}";
@@ -345,6 +345,78 @@
</DataGrid> </DataGrid>
</TabItem> </TabItem>
<!-- 表字段 -->
<TabItem Header="表字段">
<DataGrid x:Name="TableFieldsGrid" ItemsSource="{Binding TableFieldsData}"
SelectedItem="{Binding SelectedRow}"
RowStyle="{StaticResource DataGridRowStyleFix}"
CellStyle="{StaticResource DataGridCellStyleFix}"
AutoGenerateColumns="False" CanUserAddRows="False"
IsReadOnly="True" HeadersVisibility="Column"
GridLinesVisibility="Horizontal" BorderThickness="1"
BorderBrush="#d0d7de">
<DataGrid.Columns>
<DataGridTemplateColumn Header="表名.字段名" Width="*" MinWidth="180">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource CopyableNameWhenSelected}" b:ClickToCopy.Selectable="True" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="状态" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Border Style="{StaticResource StatusBadge}">
<Border.Background>
<MultiBinding Converter="{StaticResource ReadStatusColorConverter}">
<Binding Path="RStatus"/>
</MultiBinding>
</Border.Background>
<TextBlock Text="{Binding ReadStatusText}" Style="{StaticResource StatusBadgeText}"/>
</Border>
<Border Style="{StaticResource StatusBadge}" Margin="6,0,0,0">
<Border.Background>
<MultiBinding Converter="{StaticResource WriteStatusColorConverter}">
<Binding Path="WStatus"/>
</MultiBinding>
</Border.Background>
<TextBlock Text="{Binding WriteStatusText}" Style="{StaticResource StatusBadgeText}"/>
</Border>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="操作" Width="320">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="读取" Style="{StaticResource SmallBtn}"
Command="{Binding DataContext.ReadCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
<Button Content="写入" Style="{StaticResource SmallSuccessBtn}"
Command="{Binding DataContext.WriteCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
<Button Content="删除" Style="{StaticResource SmallWarnBtn}"
Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
<Button Content="↑" Style="{StaticResource SmallDefaultBtn}" MinWidth="36"
Command="{Binding DataContext.MoveUpCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
<Button Content="↓" Style="{StaticResource SmallDefaultBtn}" MinWidth="36"
Command="{Binding DataContext.MoveDownCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
<Button Content="Top" Style="{StaticResource SmallDefaultBtn}" MinWidth="40"
Command="{Binding DataContext.MoveTopCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<!-- BOS配置 --> <!-- BOS配置 -->
<TabItem Header="BOS配置"> <TabItem Header="BOS配置">
<DataGrid x:Name="BosGrid" ItemsSource="{Binding BosTablesData}" <DataGrid x:Name="BosGrid" ItemsSource="{Binding BosTablesData}"
@@ -8,7 +8,7 @@
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<ApplicationIcon>Resources\favicon.ico</ApplicationIcon> <ApplicationIcon>Resources\favicon.ico</ApplicationIcon>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<Version>1.0.18</Version> <Version>1.0.19</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>