diff --git a/yy-admin-master/YY.Admin.Core/SqlSugar/SqlSugarSetup.cs b/yy-admin-master/YY.Admin.Core/SqlSugar/SqlSugarSetup.cs index 29111e9..e615330 100644 --- a/yy-admin-master/YY.Admin.Core/SqlSugar/SqlSugarSetup.cs +++ b/yy-admin-master/YY.Admin.Core/SqlSugar/SqlSugarSetup.cs @@ -163,9 +163,11 @@ namespace YY.Admin.Core.SqlSugar config.ConnectionString = CryptogramUtil.Decrypt(config.ConnectionString); } - // SQLite 相对路径默认依赖进程工作目录;快捷方式/从不同目录启动会在别处生成空库,表现为发布后菜单全空 + // SQLite 相对路径会解析到 LocalApplicationData\Data(见 ResolveSqliteConnectionToAbsolutePath) if (config.DbType == DbType.Sqlite && !string.IsNullOrWhiteSpace(config.ConnectionString)) { + // 安装包随带的 Admin.NET.db / Slave.db 在安装目录;运行时代码只读写可写目录。首启若无用户库则从安装目录复制模板。 + TrySeedSqliteFromInstallDirectory(config.ConnectionString); config.ConnectionString = ResolveSqliteConnectionToAbsolutePath(config.ConnectionString); } @@ -266,7 +268,41 @@ namespace YY.Admin.Core.SqlSugar } /// - /// 将 SQLite 连接串中的相对 DataSource 解析为基于应用程序基目录的绝对路径。 + /// 首次启动时:若用户数据目录中尚无该 SQLite 文件,且安装目录(exe 旁)存在同名库,则复制一份作为初始模板。 + /// 否则安装包内随带的菜单/配置永远不会被用到(实际始终读写 %LocalAppData%\YY.Admin\Data\)。 + /// + private static void TrySeedSqliteFromInstallDirectory(string connectionString) + { + try + { + var builder = new SqliteConnectionStringBuilder(connectionString); + var ds = builder.DataSource; + if (string.IsNullOrWhiteSpace(ds) || Path.IsPathFullyQualified(ds)) + return; + + var fileName = Path.GetFileName(ds.TrimStart('.', '/', '\\')); + if (string.IsNullOrWhiteSpace(fileName)) + fileName = "Admin.NET.db"; + + var dataDir = AppWritablePaths.EnsureDirectoryExists(AppWritablePaths.DataDirectory); + var targetPath = Path.Combine(dataDir, fileName); + if (File.Exists(targetPath)) + return; + + var bundledPath = Path.Combine(AppContext.BaseDirectory, fileName); + if (!File.Exists(bundledPath)) + return; + + File.Copy(bundledPath, targetPath, overwrite: false); + } + catch + { + // 忽略:后续仍可按空库走建表/基准种子 + } + } + + /// + /// 将 SQLite 连接串中的相对 DataSource 解析为 %LocalAppData%\YY.Admin\Data\ 下绝对路径(适配 Program Files 只读安装)。 /// private static string ResolveSqliteConnectionToAbsolutePath(string connectionString) { @@ -284,7 +320,7 @@ namespace YY.Admin.Core.SqlSugar return connectionString; } - // Program Files 等安装目录对普通用户只读;SQLite 库放到 LocalApplicationData + // Program Files 等安装目录对普通用户只读;运行期 SQLite 放到 LocalApplicationData(首启可由 TrySeedSqliteFromInstallDirectory 从安装目录拷贝模板) var dataDir = AppWritablePaths.EnsureDirectoryExists(AppWritablePaths.DataDirectory); var fileName = Path.GetFileName(ds.TrimStart('.', '/', '\\')); if (string.IsNullOrWhiteSpace(fileName)) diff --git a/yy-admin-master/YY.Admin/App.xaml.cs b/yy-admin-master/YY.Admin/App.xaml.cs index f6cc0cf..2622ebd 100644 --- a/yy-admin-master/YY.Admin/App.xaml.cs +++ b/yy-admin-master/YY.Admin/App.xaml.cs @@ -3,6 +3,7 @@ using FluentValidation; using Mapster; using Microsoft.Extensions.Configuration; using NewLife; +using System.Configuration; using System.IO; using System.Text; using System.Windows; @@ -73,6 +74,46 @@ namespace YY.Admin } } + /// + /// 写入用户级 AppSettings(user.config)。若文件损坏(如缺少 configSections),删除后重试一次。 + /// + private static void PersistSkinTypeUserSettingSafe() + { + for (var attempt = 0; attempt < 2; attempt++) + { + try + { + AppSettings.Default.SkinType = AppSettingsViewModel.GetSkinType().ToInt(); + AppSettings.Default.Save(); + return; + } + catch (ConfigurationErrorsException ex) + { + if (attempt == 0) + { + var path = ex.Filename; + if (string.IsNullOrWhiteSpace(path) && ex.InnerException is ConfigurationErrorsException inner) + path = inner.Filename; + + try + { + if (!string.IsNullOrWhiteSpace(path) && File.Exists(path)) + File.Delete(path); + AppSettings.Default.Reload(); + } + catch + { + // 删除/Reload 失败则向上抛出原异常 + } + + continue; + } + + throw; + } + } + } + protected override Window CreateShell() { return Container.Resolve(); @@ -178,9 +219,8 @@ namespace YY.Admin _logger = Container.Resolve(); // 初始化全局异常处理(通过解析触发构造函数注册) Container.Resolve(); - // 保存默认主题 - AppSettings.Default.SkinType = AppSettingsViewModel.GetSkinType().ToInt(); - AppSettings.Default.Save(); + // 保存默认主题(用户级 user.config;损坏时自动删文件并重载) + PersistSkinTypeUserSettingSafe(); _logger.Information("应用程序已启动"); diff --git a/yy-admin-master/YY.Admin/publish.cmd b/yy-admin-master/YY.Admin/publish.cmd index 1270cbe..99afd03 100644 --- a/yy-admin-master/YY.Admin/publish.cmd +++ b/yy-admin-master/YY.Admin/publish.cmd @@ -68,6 +68,16 @@ if errorlevel 1 ( exit /b 1 ) +REM ˵ҵ SQLiteĿ³Ϊȫһݣdotnet publish Ĭϲ +if exist "Admin.NET.db" ( + copy /Y "Admin.NET.db" "publish\" >nul + echo Ѹ Admin.NET.dbĿһ£˵ã +) +if exist "Slave.db" ( + copy /Y "Slave.db" "publish\" >nul + echo Ѹ Slave.db +) + REM Updates Ŀ¼ư汾ļ echo [3/4] Զļ... if not exist "publish\Updates" mkdir "publish\Updates" diff --git a/yy-admin-master/_installer_output/YY.Admin_Setup_1.1.0_win-x64.exe b/yy-admin-master/_installer_output/YY.Admin_Setup_1.1.3_win-x64.exe similarity index 91% rename from yy-admin-master/_installer_output/YY.Admin_Setup_1.1.0_win-x64.exe rename to yy-admin-master/_installer_output/YY.Admin_Setup_1.1.3_win-x64.exe index e3c586a..43e5816 100644 Binary files a/yy-admin-master/_installer_output/YY.Admin_Setup_1.1.0_win-x64.exe and b/yy-admin-master/_installer_output/YY.Admin_Setup_1.1.3_win-x64.exe differ diff --git a/yy-admin-master/installer/ChineseSimplified.isl b/yy-admin-master/installer/ChineseSimplified.isl new file mode 100644 index 0000000..e497cf8 --- /dev/null +++ b/yy-admin-master/installer/ChineseSimplified.isl @@ -0,0 +1,418 @@ +; *** Inno Setup version 6.5.0+ Chinese Simplified messages *** +; +; To download user-contributed translations of this file, go to: +; https://jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). +; +; Maintained by Zhenghan Yang +; Email: 847320916@QQ.com +; Translation based on network resource +; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation +; + +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=简体中文 +; If Language Name display incorrect, uncomment next line +; LanguageName=<7B80><4F53><4E2D><6587> +; About LanguageID, to reference link: +; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c +LanguageID=$0804 +; About CodePage, to reference link: +; https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers +LanguageCodePage=936 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=9 +;DialogFontBaseScaleWidth=7 +;DialogFontBaseScaleHeight=15 +;WelcomeFontName=Segoe UI +;WelcomeFontSize=14 + +[Messages] + +; *** 应用程序标题 +SetupAppTitle=安装 +SetupWindowTitle=安装 - %1 +UninstallAppTitle=卸载 +UninstallAppFullTitle=%1 卸载 + +; *** Misc. common +InformationTitle=信息 +ConfirmTitle=确认 +ErrorTitle=错误 + +; *** SetupLdr messages +SetupLdrStartupMessage=现在将安装 %1。您想要继续吗? +LdrCannotCreateTemp=无法创建临时文件。安装程序已中止 +LdrCannotExecTemp=无法执行临时目录中的文件。安装程序已中止 +HelpTextNote= + +; *** 启动错误消息 +LastErrorMessage=%1。%n%n错误 %2: %3 +SetupFileMissing=安装目录中缺少文件 %1。请修正这个问题或者获取程序的新副本。 +SetupFileCorrupt=安装文件已损坏。请获取程序的新副本。 +SetupFileCorruptOrWrongVer=安装文件已损坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。 +InvalidParameter=无效的命令行参数:%n%n%1 +SetupAlreadyRunning=安装程序正在运行。 +WindowsVersionNotSupported=此程序不支持当前计算机运行的 Windows 版本。 +WindowsServicePackRequired=此程序需要 %1 服务包 %2 或更高版本。 +NotOnThisPlatform=此程序不能在 %1 上运行。 +OnlyOnThisPlatform=此程序只能在 %1 上运行。 +OnlyOnTheseArchitectures=此程序只能安装到为下列处理器架构设计的 Windows 版本中:%n%n%1 +WinVersionTooLowError=此程序需要 %1 版本 %2 或更高。 +WinVersionTooHighError=此程序不能安装于 %1 版本 %2 或更高。 +AdminPrivilegesRequired=在安装此程序时您必须以管理员身份登录。 +PowerUserPrivilegesRequired=在安装此程序时您必须以管理员身份或有权限的用户组身份登录。 +SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序,然后点击“确定”继续,或点击“取消”退出。 +UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭正在运行的程序,然后点击“确定”继续,或点击“取消”退出。 + +; *** 启动问题 +PrivilegesRequiredOverrideTitle=选择安装程序模式 +PrivilegesRequiredOverrideInstruction=选择安装模式 +PrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限),或仅为您安装。 +PrivilegesRequiredOverrideText2=%1 可以仅为您安装,或为所有用户安装(需要管理员权限)。 +PrivilegesRequiredOverrideAllUsers=为所有用户安装(&A) +PrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(&A) (建议选项) +PrivilegesRequiredOverrideCurrentUser=仅为我安装(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=仅为我安装(&M) (建议选项) + +; *** 其他错误 +ErrorCreatingDir=安装程序无法创建目录“%1” +ErrorTooManyFilesInDir=无法在目录“%1”中创建文件,因为里面包含太多文件 + +; *** 安装程序公共消息 +ExitSetupTitle=退出安装程序 +ExitSetupMessage=安装程序尚未完成。如果现在退出,将不会安装该程序。%n%n您之后可以再次运行安装程序完成安装。%n%n现在退出安装程序吗? +AboutSetupMenuItem=关于安装程序(&A)... +AboutSetupTitle=关于安装程序 +AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4 +AboutSetupNote= +TranslatorNote=简体中文翻译由Kira(847320916@qq.com)维护。项目地址:https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation + +; *** 按钮 +ButtonBack=< 上一步(&B) +ButtonNext=下一步(&N) > +ButtonInstall=安装(&I) +ButtonOK=确定 +ButtonCancel=取消 +ButtonYes=是(&Y) +ButtonYesToAll=全是(&A) +ButtonNo=否(&N) +ButtonNoToAll=全否(&O) +ButtonFinish=完成(&F) +ButtonBrowse=浏览(&B)... +ButtonWizardBrowse=浏览(&R)... +ButtonNewFolder=新建文件夹(&M) + +; *** “选择语言”对话框消息 +SelectLanguageTitle=选择安装语言 +SelectLanguageLabel=选择安装时使用的语言。 + +; *** 公共向导文字 +ClickNext=点击“下一步”继续,或点击“取消”退出安装程序。 +BeveledLabel= +BrowseDialogTitle=浏览文件夹 +BrowseDialogLabel=在下面的列表中选择一个文件夹,然后点击“确定”。 +NewFolderName=新建文件夹 + +; *** “欢迎”向导页 +WelcomeLabel1=欢迎使用 [name] 安装向导 +WelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n建议您在继续安装前关闭所有其他应用程序。 + +; *** “密码”向导页 +WizardPassword=密码 +PasswordLabel1=这个安装程序有密码保护。 +PasswordLabel3=请输入密码,然后点击“下一步”继续。密码区分大小写。 +PasswordEditLabel=密码(&P): +IncorrectPassword=您输入的密码不正确,请重新输入。 + +; *** “许可协议”向导页 +WizardLicense=许可协议 +LicenseLabel=请在继续安装前阅读以下重要信息。 +LicenseLabel3=请仔细阅读下列许可协议。在继续安装前您必须同意这些协议条款。 +LicenseAccepted=我同意此协议(&A) +LicenseNotAccepted=我不同意此协议(&D) + +; *** “信息”向导页 +WizardInfoBefore=信息 +InfoBeforeLabel=请在继续安装前阅读以下重要信息。 +InfoBeforeClickLabel=准备好继续安装后,点击“下一步”。 +WizardInfoAfter=信息 +InfoAfterLabel=请在继续安装前阅读以下重要信息。 +InfoAfterClickLabel=准备好继续安装后,点击“下一步”。 + +; *** “用户信息”向导页 +WizardUserInfo=用户信息 +UserInfoDesc=请输入您的信息。 +UserInfoName=用户名(&U): +UserInfoOrg=组织(&O): +UserInfoSerial=序列号(&S): +UserInfoNameRequired=您必须输入用户名。 + +; *** “选择目标目录”向导页 +WizardSelectDir=选择目标位置 +SelectDirDesc=您想将 [name] 安装在哪里? +SelectDirLabel3=安装程序将安装 [name] 到下面的文件夹中。 +SelectDirBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。 +DiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。 +DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。 +CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。 +CannotInstallToUNCPath=安装程序无法安装到一个 UNC 路径。 +InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\APP%n%n或UNC路径:%n%n\\server\share +InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选择其他位置。 +DiskSpaceWarningTitle=磁盘空间不足 +DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗? +DirNameTooLong=文件夹名称或路径太长。 +InvalidDirName=文件夹名称无效。 +BadDirName32=文件夹名称不能包含下列任何字符:%n%n%1 +DirExistsTitle=文件夹已存在 +DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗? +DirDoesntExistTitle=文件夹不存在 +DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此文件夹吗? + +; *** “选择组件”向导页 +WizardSelectComponents=选择组件 +SelectComponentsDesc=您想安装哪些程序组件? +SelectComponentsLabel2=选中您想安装的组件;取消您不想安装的组件。然后点击“下一步”继续。 +FullInstallation=完全安装 +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=简洁安装 +CustomInstallation=自定义安装 +NoUninstallWarningTitle=组件已存在 +NoUninstallWarning=安装程序检测到下列组件已安装在您的电脑中:%n%n%1%n%n取消选中这些组件不会卸载它们。%n%n确定要继续吗? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceGBLabel=当前选择的组件需要至少 [gb] GB 的磁盘空间。 +ComponentsDiskSpaceMBLabel=当前选择的组件需要至少 [mb] MB 的磁盘空间。 + +; *** “选择附加任务”向导页 +WizardSelectTasks=选择附加任务 +SelectTasksDesc=您想要安装程序执行哪些附加任务? +SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后点击“下一步”。 + +; *** “选择开始菜单文件夹”向导页 +WizardSelectProgramGroup=选择开始菜单文件夹 +SelectStartMenuFolderDesc=安装程序应该在哪里放置程序的快捷方式? +SelectStartMenuFolderLabel3=安装程序将在下列“开始”菜单文件夹中创建程序的快捷方式。 +SelectStartMenuFolderBrowseLabel=点击“下一步”继续。如果您想选择其他文件夹,点击“浏览”。 +MustEnterGroupName=您必须输入一个文件夹名。 +GroupNameTooLong=文件夹名或路径太长。 +InvalidGroupName=无效的文件夹名字。 +BadGroupName=文件夹名不能包含下列任何字符:%n%n%1 +NoProgramGroupCheck2=不创建开始菜单文件夹(&D) + +; *** “准备安装”向导页 +WizardReady=准备安装 +ReadyLabel1=安装程序准备就绪,现在可以开始安装 [name] 到您的电脑。 +ReadyLabel2a=点击“安装”继续此安装程序。如果您想重新考虑或修改任何设置,点击“上一步”。 +ReadyLabel2b=点击“安装”继续此安装程序。 +ReadyMemoUserInfo=用户信息: +ReadyMemoDir=目标位置: +ReadyMemoType=安装类型: +ReadyMemoComponents=已选择组件: +ReadyMemoGroup=开始菜单文件夹: +ReadyMemoTasks=附加任务: + +; *** TExtractionWizardPage 向导页面与 ExtractArchive +ExtractingLabel=正在解压文件... +ButtonStopExtraction=停止解压(&S) +StopExtraction=您确定要停止解压吗? +ErrorExtractionAborted=解压已中止 +ErrorExtractionFailed=解压失败:%1 + +; *** 压缩文件解压失败详情 +ArchiveIncorrectPassword=压缩文件密码不正确 +ArchiveIsCorrupted=压缩文件已损坏 +ArchiveUnsupportedFormat=不支持的压缩文件格式 + +; *** TDownloadWizardPage 向导页面和 DownloadTemporaryFile +DownloadingLabel2=正在下载文件... +ButtonStopDownload=停止下载(&S) +StopDownload=您确定要停止下载吗? +ErrorDownloadAborted=下载已中止 +ErrorDownloadFailed=下载失败:%1 %2 +ErrorDownloadSizeFailed=获取下载大小失败:%1 %2 +ErrorProgress=无效的进度:%1 / %2 +ErrorFileSize=文件大小错误:预期 %1,实际 %2 + +; *** “正在准备安装”向导页 +WizardPreparing=正在准备安装 +PreparingDesc=安装程序正在准备安装 [name] 到您的电脑。 +PreviousInstallNotCompleted=先前的程序安装或卸载未完成,您需要重启您的电脑以完成。%n%n在重启电脑后,再次运行安装程序以完成 [name] 的安装。 +CannotContinue=安装程序不能继续。请点击“取消”退出。 +ApplicationsFound=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。 +ApplicationsFound2=以下应用程序正在使用将由安装程序更新的文件。建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动这些应用程序。 +CloseApplications=自动关闭应用程序(&A) +DontCloseApplications=不要关闭应用程序(&D) +ErrorCloseApplications=安装程序无法自动关闭所有应用程序。建议您在继续之前,关闭所有在使用需要由安装程序更新的文件的应用程序。 +PrepareToInstallNeedsRestart=安装程序必须重启您的计算机。计算机重启后,请再次运行安装程序以完成 [name] 的安装。%n%n是否立即重新启动? + +; *** “正在安装”向导页 +WizardInstalling=正在安装 +InstallingLabel=安装程序正在安装 [name] 到您的电脑,请稍候。 + +; *** “安装完成”向导页 +FinishedHeadingLabel=[name] 安装完成 +FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。 +FinishedLabel=安装程序已在您的电脑中安装了 [name]。您可以通过已安装的快捷方式运行此应用程序。 +ClickFinish=点击“完成”退出安装程序。 +FinishedRestartLabel=为完成 [name] 的安装,安装程序必须重新启动您的电脑。要立即重启吗? +FinishedRestartMessage=为完成 [name] 的安装,安装程序必须重新启动您的电脑。%n%n要立即重启吗? +ShowReadmeCheck=是,我想查阅自述文件 +YesRadio=是,立即重启电脑(&Y) +NoRadio=否,稍后重启电脑(&N) +; used for example as 'Run MyProg.exe' +RunEntryExec=运行 %1 +; used for example as 'View Readme.txt' +RunEntryShellExec=查阅 %1 + +; *** “安装程序需要下一张磁盘”提示 +ChangeDiskTitle=安装程序需要下一张磁盘 +SelectDiskLabel2=请插入磁盘 %1 并点击“确定”。%n%n如果这个磁盘中的文件可以在下列文件夹之外的文件夹中找到,请输入正确的路径或点击“浏览”。 +PathLabel=路径(&P): +FileNotInDir2=“%2”中找不到文件“%1”。请插入正确的磁盘或选择其他文件夹。 +SelectDirectoryLabel=请指定下一张磁盘的位置。 + +; *** 安装阶段消息 +SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。 +AbortRetryIgnoreSelectAction=选择操作 +AbortRetryIgnoreRetry=重试(&T) +AbortRetryIgnoreIgnore=忽略错误并继续(&I) +AbortRetryIgnoreCancel=关闭安装程序 +RetryCancelSelectAction=选择操作 +RetryCancelRetry=重试(&T) +RetryCancelCancel=取消(&C) + +; *** 安装状态消息 +StatusClosingApplications=正在关闭应用程序... +StatusCreateDirs=正在创建目录... +StatusExtractFiles=正在提取文件... +StatusDownloadFiles=正在下载文件... +StatusCreateIcons=正在创建快捷方式... +StatusCreateIniEntries=正在创建 INI 条目... +StatusCreateRegistryEntries=正在创建注册表条目... +StatusRegisterFiles=正在注册文件... +StatusSavingUninstall=正在保存卸载信息... +StatusRunProgram=正在完成安装... +StatusRestartingApplications=正在重启应用程序... +StatusRollback=正在撤销更改... + +; *** 其他错误 +ErrorInternal2=内部错误:%1 +ErrorFunctionFailedNoCode=%1 失败 +ErrorFunctionFailed=%1 失败;错误代码 %2 +ErrorFunctionFailedWithMessage=%1 失败;错误代码 %2.%n%3 +ErrorExecutingProgram=无法执行文件:%n%1 + +; *** 注册表错误 +ErrorRegOpenKey=打开注册表项时出错:%n%1\%2 +ErrorRegCreateKey=创建注册表项时出错:%n%1\%2 +ErrorRegWriteKey=写入注册表项时出错:%n%1\%2 + +; *** INI 错误 +ErrorIniEntry=在文件“%1”中创建 INI 条目时出错。 + +; *** 文件复制错误 +FileAbortRetryIgnoreSkipNotRecommended=跳过此文件(&S) (不推荐) +FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续(&I) (不推荐) +SourceIsCorrupted=源文件已损坏 +SourceDoesntExist=源文件“%1”不存在 +SourceVerificationFailed=源文件验证失败: %1 +VerificationSignatureDoesntExist=签名文件“%1”不存在 +VerificationSignatureInvalid=签名文件“%1”无效 +VerificationKeyNotFound=签名文件“%1”使用了未知密钥 +VerificationFileNameIncorrect=文件名不正确 +VerificationFileTagIncorrect=文件标签不正确 +VerificationFileSizeIncorrect=文件大小不正确 +VerificationFileHashIncorrect=文件哈希值不正确 +ExistingFileReadOnly2=无法替换现有文件,它是只读的。 +ExistingFileReadOnlyRetry=移除只读属性并重试(&R) +ExistingFileReadOnlyKeepExisting=保留现有文件(&K) +ErrorReadingExistingDest=尝试读取现有文件时出错: +FileExistsSelectAction=选择操作 +FileExists2=文件已经存在。 +FileExistsOverwriteExisting=覆盖已存在的文件(&O) +FileExistsKeepExisting=保留现有的文件(&K) +FileExistsOverwriteOrKeepAll=为所有冲突文件执行此操作(&D) +ExistingFileNewerSelectAction=选择操作 +ExistingFileNewer2=现有的文件比安装程序将要安装的文件还要新。 +ExistingFileNewerOverwriteExisting=覆盖已存在的文件(&O) +ExistingFileNewerKeepExisting=保留现有的文件(&K) (推荐) +ExistingFileNewerOverwriteOrKeepAll=为所有冲突文件执行此操作(&D) +ErrorChangingAttr=尝试更改下列现有文件的属性时出错: +ErrorCreatingTemp=尝试在目标目录创建文件时出错: +ErrorReadingSource=尝试读取下列源文件时出错: +ErrorCopying=尝试复制下列文件时出错: +ErrorDownloading=下载文件时出错: +ErrorExtracting=解压压缩文件时出错: +ErrorReplacingExistingFile=尝试替换现有文件时出错: +ErrorRestartReplace=重启并替换失败: +ErrorRenamingTemp=尝试重命名下列目标目录中的一个文件时出错: +ErrorRegisterServer=无法注册 DLL/OCX:%1 +ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1 +ErrorRegisterTypeLib=无法注册类库:%1 + +; *** 卸载显示名字标记 +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32 位 +UninstallDisplayNameMark64Bit=64 位 +UninstallDisplayNameMarkAllUsers=所有用户 +UninstallDisplayNameMarkCurrentUser=当前用户 + +; *** 安装后错误 +ErrorOpeningReadme=尝试打开自述文件时出错。 +ErrorRestartingComputer=安装程序无法重启电脑,请手动重启。 + +; *** 卸载消息 +UninstallNotFound=文件“%1”不存在。无法卸载。 +UninstallOpenError=文件“%1”不能被打开。无法卸载。 +UninstallUnsupportedVer=此版本的卸载程序无法识别卸载日志文件“%1”的格式。无法卸载 +UninstallUnknownEntry=卸载日志中遇到一个未知条目 (%1) +ConfirmUninstall=您确认要完全移除 %1 及其所有组件吗? +UninstallOnlyOnWin64=仅允许在 64 位 Windows 中卸载此程序。 +OnlyAdminCanUninstall=仅使用管理员权限的用户能完成此卸载。 +UninstallStatusLabel=正在从您的电脑中移除 %1,请稍候。 +UninstalledAll=已顺利从您的电脑中移除 %1。 +UninstalledMost=%1 卸载完成。%n%n有部分内容未能被删除,但您可以手动删除它们。 +UninstalledAndNeedsRestart=为完成 %1 的卸载,需要重启您的电脑。%n%n立即重启电脑吗? +UninstallDataCorrupted=文件“%1”已损坏。无法卸载 + +; *** 卸载状态消息 +ConfirmDeleteSharedFileTitle=删除共享的文件吗? +ConfirmDeleteSharedFile2=系统表示下列共享的文件已不有其他程序使用。您希望卸载程序删除这些共享的文件吗?%n%n如果删除这些文件,但仍有程序在使用这些文件,则这些程序可能出现异常。如果您不能确定,请选择“否”,在系统中保留这些文件以免引发问题。 +SharedFileNameLabel=文件名: +SharedFileLocationLabel=位置: +WizardUninstalling=卸载状态 +StatusUninstalling=正在卸载 %1... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=正在安装 %1。 +ShutdownBlockReasonUninstallingApp=正在卸载 %1。 + +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. + +[CustomMessages] + +NameAndVersion=%1 版本 %2 +AdditionalIcons=附加快捷方式: +CreateDesktopIcon=创建桌面快捷方式(&D) +CreateQuickLaunchIcon=创建快速启动栏快捷方式(&Q) +ProgramOnTheWeb=%1 网站 +UninstallProgram=卸载 %1 +LaunchProgram=运行 %1 +AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A) +AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联... +AutoStartProgramGroupDescription=启动: +AutoStartProgram=自动启动 %1 +AddonHostProgramNotFound=您选择的文件夹中无法找到 %1。%n%n您要继续吗? diff --git a/yy-admin-master/installer/YY.Admin.Setup.iss b/yy-admin-master/installer/YY.Admin.Setup.iss index 9fa4467..2ed3989 100644 --- a/yy-admin-master/installer/YY.Admin.Setup.iss +++ b/yy-admin-master/installer/YY.Admin.Setup.iss @@ -31,9 +31,9 @@ DisableDirPage=no UninstallDisplayIcon={app}\{#MyAppExeName} SetupLogging=yes -; 向导语言:使用 Inno 自带的 Default.isl(英文)。若本机存在 Languages\ChineseSimplified.isl, -; 可改为: Name: "chinesesimp"; MessagesFile: "compiler:Languages\ChineseSimplified.isl" +; 向导语言:默认简体中文;语言文件与本 .iss 同目录(ChineseSimplified.isl),无需复制到 Inno 目录 [Languages] +Name: "chinesesimplified"; MessagesFile: "ChineseSimplified.isl" Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] diff --git a/yy-admin-master/installer/build-installer.bat b/yy-admin-master/installer/build-installer.bat index 67c1948..fff4d5e 100644 --- a/yy-admin-master/installer/build-installer.bat +++ b/yy-admin-master/installer/build-installer.bat @@ -1,6 +1,19 @@ @echo off chcp 65001 >nul cd /d "%~dp0" + +if not exist "%~dp0build-installer.ps1" ( + echo [错误] 未找到同目录下的 build-installer.ps1 + echo. + echo 请从仓库内运行本脚本,例如: + echo qhmes\yy-admin-master\installer\build-installer.bat + echo 不要只复制 .bat 到桌面;需与 build-installer.ps1 放在同一目录。 + echo. + echo 当前目录: %~dp0 + pause + exit /b 1 +) + echo Running build-installer.ps1 ... powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0build-installer.ps1" echo. diff --git a/yy-admin-master/installer/build-installer.ps1 b/yy-admin-master/installer/build-installer.ps1 index c1a9d72..8eaaa73 100644 --- a/yy-admin-master/installer/build-installer.ps1 +++ b/yy-admin-master/installer/build-installer.ps1 @@ -1,16 +1,54 @@ # Release publish + WebView2 bootstrap download + Inno Setup ISCC # Requires Inno Setup 6 (ISCC.exe) +# +# 注意:与 YY.Admin\publish.cmd 输出目录不同;本脚本使用 bin\Release\...\win-x64\publish。 +# 本地 F5 / 双击 bin 下 exe 时,SQLite 实际写在 %LocalAppData%\YY.Admin\Data\(不是工程或 bin 目录)。 +# 同步步骤会优先把该目录下的库打进 publish,与其它机上「和你本机一致」的安装模板对齐。 + +param( + [switch]$NoLocalSync +) $ErrorActionPreference = 'Stop' $Root = Split-Path -Parent $PSScriptRoot $Csproj = Join-Path $Root 'YY.Admin\YY.Admin.csproj' -$PublishRel = 'YY.Admin\bin\Release\net8.0-windows10.0.19041\win-x64\publish' +$Tfm = 'net8.0-windows10.0.19041' +$Rid = 'win-x64' +$BinReleaseWin64 = Join-Path $Root "YY.Admin\bin\Release\$Tfm\$Rid" +$BinDebugWin64 = Join-Path $Root "YY.Admin\bin\Debug\$Tfm\$Rid" +$PublishRel = "YY.Admin\bin\Release\$Tfm\$Rid\publish" $PublishDir = Join-Path $Root $PublishRel +$ProjAdmin = Join-Path $Root 'YY.Admin' +# 与 SqlSugar 解析一致:相对路径 SQLite 落在当前 Windows 用户的 LocalAppData +$DevUserDataDir = if ($env:LOCALAPPDATA) { Join-Path $env:LOCALAPPDATA 'YY.Admin\Data' } else { '' } +$DevUserAppSettingsDir = if ($env:LOCALAPPDATA) { Join-Path $env:LOCALAPPDATA 'YY.Admin\AppSettings' } else { '' } $RedistDir = Join-Path $PSScriptRoot 'redist' $WebView2Exe = Join-Path $RedistDir 'MicrosoftEdgeWebview2Setup.exe' $WebView2Url = 'https://go.microsoft.com/fwlink/p/?LinkId=2124703' +$SnapshotDir = Join-Path ([System.IO.Path]::GetTempPath()) ("yyadmin-publish-snap-{0}" -f [Guid]::NewGuid().ToString('n')) Write-Host '>>> dotnet publish (Release, win-x64)...' +# publish 会清空输出目录:若你平时只在 publish 里测,先把其中的库暂存,供同步步骤一并参与“最新”比较 +$hadPublishRoot = Test-Path -LiteralPath $PublishDir +if ($hadPublishRoot -and -not $NoLocalSync) { + $snapDb = @() + foreach ($dbName in @('Admin.NET.db', 'Slave.db')) { + $pp = Join-Path $PublishDir $dbName + if (Test-Path -LiteralPath $pp) { $snapDb += , $pp } + } + $pubApps = Join-Path $PublishDir 'AppSettings' + $hasPubApps = Test-Path -LiteralPath $pubApps + if ($snapDb.Count -gt 0 -or $hasPubApps) { + New-Item -ItemType Directory -Force -Path $SnapshotDir | Out-Null + foreach ($p in $snapDb) { + Copy-Item -LiteralPath $p -Destination (Join-Path $SnapshotDir (Split-Path $p -Leaf)) -Force + } + if ($hasPubApps) { + Copy-Item -LiteralPath $pubApps -Destination (Join-Path $SnapshotDir 'AppSettings') -Recurse -Force + } + } +} + dotnet publish $Csproj -c Release ` -p:IncludeNativeLibrariesForSelfExtract=true ` -p:EnableCompressionInSingleFile=true ` @@ -25,6 +63,81 @@ if (-not (Test-Path $cfgPublish)) { throw "Missing Configuration\appsettings.json under publish (ExcludeFromSingleFile required). Path: $cfgPublish" } +if (-not $NoLocalSync) { + Write-Host '>>> Sync local runtime data into publish (databases + AppSettings, match local test)...' + + function Get-BestExistingPath { + param([string[]]$Paths) + $bestItem = $null + foreach ($p in $Paths) { + if (-not $p -or -not (Test-Path -LiteralPath $p)) { continue } + $fi = Get-Item -LiteralPath $p + if (-not $bestItem) { $bestItem = $fi; continue } + if ($fi.LastWriteTime -gt $bestItem.LastWriteTime) { $bestItem = $fi; continue } + # 修改时间相同(例如复制残留)时优先体积更大的一份,一般数据更全 + if ($fi.LastWriteTime -eq $bestItem.LastWriteTime -and $fi.Length -gt $bestItem.Length) { $bestItem = $fi } + } + if ($bestItem) { return $bestItem.FullName } + return $null + } + + # 同名库:当前用户 LocalAppData(F5 真数据源)、项目根、publish.cmd、bin、publish、快照… + foreach ($dbName in @('Admin.NET.db', 'Slave.db')) { + $paths = @() + if ($DevUserDataDir -and (Test-Path -LiteralPath $DevUserDataDir)) { + $paths += (Join-Path $DevUserDataDir $dbName) + } + $paths += @( + (Join-Path $ProjAdmin $dbName), + (Join-Path $ProjAdmin "publish\$dbName"), + (Join-Path $BinReleaseWin64 $dbName), + (Join-Path $BinDebugWin64 $dbName), + (Join-Path $BinReleaseWin64 "publish\$dbName"), + (Join-Path $BinDebugWin64 "publish\$dbName"), + (Join-Path $PublishDir $dbName) + ) + if (Test-Path -LiteralPath $SnapshotDir) { + $sp = Join-Path $SnapshotDir $dbName + if (Test-Path -LiteralPath $sp) { $paths += $sp } + } + $src = Get-BestExistingPath -Paths $paths + if ($src) { + $dst = Join-Path $PublishDir $dbName + Copy-Item -LiteralPath $src -Destination $dst -Force + $dstItem = Get-Item -LiteralPath $dst + $t = $dstItem.LastWriteTime.ToString('yyyy-MM-dd HH:mm') + $len = $dstItem.Length + Write-Host " $dbName <= $src (publish 时间: $t, 大小: $len 字节)" + } else { + Write-Host " (skip) $dbName — 未在任意候选路径找到(含当前 publish 文件夹)" + } + } + + # AppSettings\:先 Release、再 Debug,再上一版 publish 下的 AppSettings(后者覆盖,适配只在 publish 目录测试) + foreach ($bin in @($BinReleaseWin64, $BinDebugWin64)) { + $appSettingsSrc = Join-Path $bin 'AppSettings' + if (Test-Path -LiteralPath $appSettingsSrc) { + $appSettingsDst = Join-Path $PublishDir 'AppSettings' + Copy-Item -LiteralPath $appSettingsSrc -Destination $appSettingsDst -Recurse -Force + Write-Host " AppSettings\ <= $appSettingsSrc" + } + } + $snapApps = Join-Path $SnapshotDir 'AppSettings' + if (Test-Path -LiteralPath $snapApps) { + $appSettingsDst = Join-Path $PublishDir 'AppSettings' + Copy-Item -LiteralPath $snapApps -Destination $appSettingsDst -Recurse -Force + Write-Host " AppSettings\ <= $snapApps (previous publish)" + } + # 最后覆盖为当前登录 Windows 用户在 LocalAppData 下的设置(与 F5 调试一致) + if ($DevUserAppSettingsDir -and (Test-Path -LiteralPath $DevUserAppSettingsDir)) { + $appSettingsDst = Join-Path $PublishDir 'AppSettings' + Copy-Item -LiteralPath $DevUserAppSettingsDir -Destination $appSettingsDst -Recurse -Force + Write-Host " AppSettings\ <= $DevUserAppSettingsDir (LocalAppData)" + } +} else { + Write-Host '>>> Skip local sync (-NoLocalSync).' +} + New-Item -ItemType Directory -Force -Path $RedistDir | Out-Null if (-not (Test-Path $WebView2Exe)) { Write-Host '>>> Downloading WebView2 Evergreen bootstrapper...'