Explorar el Código

个人中心页面增加电子签名及其他优化

zuohuaijun hace 3 años
padre
commit
d2c5b16e93
Se han modificado 33 ficheros con 947 adiciones y 700 borrados
  1. 3 3
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  2. 43 16
      Admin.NET/Admin.NET.Core/Admin.NET.Core.xml
  3. 11 4
      Admin.NET/Admin.NET.Core/Entity/SysUser.cs
  4. 3 1
      Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs
  5. 18 5
      Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs
  6. 12 7
      Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs
  7. 7 0
      Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs
  8. 47 11
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  9. 0 5
      Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs
  10. 15 4
      Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs
  11. 2 1
      Web/package.json
  12. 1 0
      Web/plugins.d.ts
  13. 175 7
      Web/src/api-services/apis/sys-file-api.ts
  14. 118 49
      Web/src/api-services/apis/sys-user-api.ts
  15. 6 0
      Web/src/api-services/models/add-user-input.ts
  16. 2 0
      Web/src/api-services/models/index.ts
  17. 15 9
      Web/src/api-services/models/login-user-output.ts
  18. 26 0
      Web/src/api-services/models/sys-file-upload-avatar-body.ts
  19. 26 0
      Web/src/api-services/models/sys-file-upload-signature-body.ts
  20. 6 0
      Web/src/api-services/models/sys-user.ts
  21. 6 0
      Web/src/api-services/models/update-user-input.ts
  22. 1 1
      Web/src/layout/navBars/breadcrumb/user.vue
  23. 4 3
      Web/src/main.ts
  24. 4 0
      Web/src/stores/interface/index.ts
  25. 6 9
      Web/src/stores/userInfo.ts
  26. 71 28
      Web/src/utils/base64Conver.ts
  27. 0 387
      Web/src/views/personal/index.vue
  28. 0 66
      Web/src/views/personal/mock.ts
  29. 1 1
      Web/src/views/system/menu/index.vue
  30. 1 1
      Web/src/views/system/onlineUser/index.vue
  31. 1 1
      Web/src/views/system/org/index.vue
  32. 0 81
      Web/src/views/system/password/index.vue
  33. 316 0
      Web/src/views/system/user/component/userCenter.vue

+ 3 - 3
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -25,9 +25,9 @@
   <ItemGroup>
     <PackageReference Include="AngleSharp" Version="0.17.1" />
     <PackageReference Include="AspNetCoreRateLimit" Version="4.0.2" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.1" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.1" />
-    <PackageReference Include="Furion.Pure" Version="4.7.1" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.2" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.2" />
+    <PackageReference Include="Furion.Pure" Version="4.7.2" />
     <PackageReference Include="Lazy.Captcha.Core" Version="1.1.6" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.6.9" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.6.9" />

+ 43 - 16
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -1623,6 +1623,11 @@
             最新登录设备
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.SysUser.Signature">
+            <summary>
+            电子签名
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.SysUserExtOrg">
             <summary>
             系统用户扩展机构表
@@ -3815,11 +3820,6 @@
             用户登录信息
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.Service.LoginUserOutput.UserId">
-            <summary>
-            用户Id
-            </summary>
-        </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.Account">
             <summary>
             账号名称
@@ -3840,9 +3840,14 @@
             个人简介
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.Service.LoginUserOutput.OrgId">
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.Address">
             <summary>
-            机构Id
+            地址
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.Signature">
+            <summary>
+            电子签名
             </summary>
         </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.OrgName">
@@ -3850,6 +3855,11 @@
             机构名称
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.Service.LoginUserOutput.PosName">
+            <summary>
+            职位名称
+            </summary>
+        </member>
         <member name="P:Admin.NET.Core.Service.LoginUserOutput.Buttons">
             <summary>
             按钮权限集合
@@ -5052,11 +5062,12 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysFileService.UploadFile(Microsoft.AspNetCore.Http.IFormFile)">
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadFile(Microsoft.AspNetCore.Http.IFormFile,System.String)">
             <summary>
             上传文件
             </summary>
             <param name="file"></param>
+            <param name="path"></param>
             <returns></returns>
         </member>
         <member name="M:Admin.NET.Core.Service.SysFileService.UploadFiles(System.Collections.Generic.List{Microsoft.AspNetCore.Http.IFormFile})">
@@ -5087,11 +5098,12 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysFileService.HandleUploadFile(Microsoft.AspNetCore.Http.IFormFile)">
+        <member name="M:Admin.NET.Core.Service.SysFileService.HandleUploadFile(Microsoft.AspNetCore.Http.IFormFile,System.String)">
             <summary>
             上传文件
             </summary>
             <param name="file">文件</param>
+            <param name="savePath">路径</param>
             <returns></returns>
         </member>
         <member name="M:Admin.NET.Core.Service.SysFileService.GetMinioPreviewFileUrl(System.String,System.String)">
@@ -5102,6 +5114,20 @@
             <param name="fileName">文件名</param>
             <returns></returns>
         </member>
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadAvatar(Microsoft.AspNetCore.Http.IFormFile)">
+            <summary>
+            上传头像
+            </summary>
+            <param name="file"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysFileService.UploadSignature(Microsoft.AspNetCore.Http.IFormFile)">
+            <summary>
+            上传电子签名
+            </summary>
+            <param name="file"></param>
+            <returns></returns>
+        </member>
         <member name="P:Admin.NET.Core.Service.PageLogInput.StartTime">
             <summary>
             开始时间
@@ -6473,11 +6499,6 @@
             真实姓名
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.Service.AddUserInput.Password">
-            <summary>
-            账号
-            </summary>
-        </member>
         <member name="P:Admin.NET.Core.Service.AddUserInput.RoleIdList">
             <summary>
             角色集合
@@ -6646,9 +6667,15 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="M:Admin.NET.Core.Service.SysUserService.GetUser(System.Int64)">
+        <member name="M:Admin.NET.Core.Service.SysUserService.GetUserBase">
+            <summary>
+            查看用户基本信息
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysUserService.UpdateUserBase(Admin.NET.Core.SysUser)">
             <summary>
-            查看用户
+            设置用户基本信息
             </summary>
             <returns></returns>
         </member>

+ 11 - 4
Admin.NET/Admin.NET.Core/Entity/SysUser.cs

@@ -17,10 +17,10 @@ public class SysUser : EntityTenant
     /// 密码(MD5加密)
     /// </summary>
     [SugarColumn(ColumnDescription = "密码", Length = 64)]
-    [Required, MaxLength(64)]
+    [MaxLength(64)]
     [System.Text.Json.Serialization.JsonIgnore]
     [JsonIgnore]
-    public virtual string Password { get; set; }
+    public virtual string Password { get; set; }  = CommonConst.SysPassword;
 
     /// <summary>
     /// 真实姓名
@@ -39,8 +39,8 @@ public class SysUser : EntityTenant
     /// <summary>
     /// 头像
     /// </summary>
-    [SugarColumn(ColumnDescription = "头像", Length = 256)]
-    [MaxLength(256)]
+    [SugarColumn(ColumnDescription = "头像", Length = 512)]
+    [MaxLength(512)]
     public string Avatar { get; set; }
 
     /// <summary>
@@ -254,4 +254,11 @@ public class SysUser : EntityTenant
     [SugarColumn(ColumnDescription = "最新登录设备", Length = 128)]
     [MaxLength(128)]
     public string LastLoginDevice { get; set; }
+
+    /// <summary>
+    /// 电子签名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "电子签名", Length = 512)]
+    [MaxLength(512)]
+    public string Signature { get; set; }
 }

+ 3 - 1
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -58,8 +58,10 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=252885263002253, Pid=252885263002250, Title="职位增加", Permission="sysPos:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
             new SysMenu{ Id=252885263002254, Pid=252885263002250, Title="职位删除", Permission="sysPos:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
-            new SysMenu{ Id=252885263002260, Pid=252885263002200, Title="修改密码", Path="/system/password", Name="sysPassword", Component="/system/password/index",Icon="ele-Hide", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=150 },
+            new SysMenu{ Id=252885263002260, Pid=252885263002200, Title="个人中心", Path="/system/userCenter", Name="sysUserCenter", Component="/system/user/component/userCenter",Icon="ele-Stamp", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=150 },
             new SysMenu{ Id=252885263002261, Pid=252885263002260, Title="修改密码", Permission="sysUser:changePwd", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263002262, Pid=252885263002260, Title="更新信息", Permission="sysUser:updateBase", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
+            new SysMenu{ Id=252885263002263, Pid=252885263002260, Title="电子签名", Permission="sysUser:signature", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=100 },
 
             new SysMenu{ Id=252885263002300, Pid=0, Title="平台管理", Path="/platform", Name="platform", Component="Layout", Redirect="/platform/tenant", Icon="ele-Menu", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), Order=200 },
 

+ 18 - 5
Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs

@@ -59,8 +59,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263003143, RoleId=252885263003721, MenuId=252885263002252 },
             new SysRoleMenu{ Id=252885263003144, RoleId=252885263003721, MenuId=252885263002253 },
             new SysRoleMenu{ Id=252885263003145, RoleId=252885263003721, MenuId=252885263002254 },
-            // 修改密码
+            // 个人中心
             new SysRoleMenu{ Id=252885263003151, RoleId=252885263003721, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263003152, RoleId=252885263003721, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263003153, RoleId=252885263003721, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263003154, RoleId=252885263003721, MenuId=252885263002263 },
 
             // 平台管理
             new SysRoleMenu{ Id=252885263003200, RoleId=252885263003721, MenuId=252885263002300 },
@@ -87,12 +90,13 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263003241, RoleId=252885263003721, MenuId=252885263002370 },
             new SysRoleMenu{ Id=252885263003242, RoleId=252885263003721, MenuId=252885263002371 },
             new SysRoleMenu{ Id=252885263003243, RoleId=252885263003721, MenuId=252885263002372 },
-            // 数据资源
+            // 行政区域
             new SysRoleMenu{ Id=252885263003251, RoleId=252885263003721, MenuId=252885263002380 },
             new SysRoleMenu{ Id=252885263003252, RoleId=252885263003721, MenuId=252885263002381 },
             new SysRoleMenu{ Id=252885263003253, RoleId=252885263003721, MenuId=252885263002382 },
             new SysRoleMenu{ Id=252885263003254, RoleId=252885263003721, MenuId=252885263002383 },
             new SysRoleMenu{ Id=252885263003255, RoleId=252885263003721, MenuId=252885263002384 },
+            new SysRoleMenu{ Id=252885263003256, RoleId=252885263003721, MenuId=252885263002385 },
             // 文件管理
             new SysRoleMenu{ Id=252885263003261, RoleId=252885263003721, MenuId=252885263002390 },
             new SysRoleMenu{ Id=252885263003262, RoleId=252885263003721, MenuId=252885263002391 },
@@ -122,8 +126,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             //new SysRoleMenu{ Id=252885263004002, RoleId=252885263003722, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263004100, RoleId=252885263003722, MenuId=252885263002200 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263004151, RoleId=252885263003722, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263004152, RoleId=252885263003722, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263004153, RoleId=252885263003722, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263004154, RoleId=252885263003722, MenuId=252885263002263 },
 
             // 数据面板【user3/252885263003724】
             new SysRoleMenu{ Id=252885263005000, RoleId=252885263003724, MenuId=252885263002100 },
@@ -131,8 +138,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             //new SysRoleMenu{ Id=252885263005002, RoleId=252885263003724, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263005100, RoleId=252885263003724, MenuId=252885263002200 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263005151, RoleId=252885263003724, MenuId=252885263002260},
+            new SysRoleMenu{ Id=252885263005152, RoleId=252885263003724, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263005153, RoleId=252885263003724, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263005154, RoleId=252885263003724, MenuId=252885263002263 },
 
             // 其他租户
             // 数据面板【admin1/252885263004721】
@@ -180,8 +190,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263103143, RoleId=252885263004721, MenuId=252885263002252 },
             new SysRoleMenu{ Id=252885263103144, RoleId=252885263004721, MenuId=252885263002253 },
             new SysRoleMenu{ Id=252885263103145, RoleId=252885263004721, MenuId=252885263002254 },
-            // 密码修改
+            // 个人中心
             new SysRoleMenu{ Id=252885263103151, RoleId=252885263004721, MenuId=252885263002260 },
+            new SysRoleMenu{ Id=252885263103152, RoleId=252885263004721, MenuId=252885263002261 },
+            new SysRoleMenu{ Id=252885263103153, RoleId=252885263004721, MenuId=252885263002262 },
+            new SysRoleMenu{ Id=252885263103154, RoleId=252885263004721, MenuId=252885263002263 },
         };
     }
 }

+ 12 - 7
Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginUserOutput.cs

@@ -5,11 +5,6 @@
 /// </summary>
 public class LoginUserOutput
 {
-    /// <summary>
-    /// 用户Id
-    /// </summary>
-    public long UserId { get; set; }
-
     /// <summary>
     /// 账号名称
     /// </summary>
@@ -31,15 +26,25 @@ public class LoginUserOutput
     public string Introduction { get; set; }
 
     /// <summary>
-    /// 机构Id
+    /// 地址
     /// </summary>
-    public long OrgId { get; set; }
+    public string Address { get; set; }
+
+    /// <summary>
+    /// 电子签名
+    /// </summary>
+    public string Signature { get; set; }
 
     /// <summary>
     /// 机构名称
     /// </summary>
     public string OrgName { get; set; }
 
+    /// <summary>
+    /// 职位名称
+    /// </summary>
+    public string PosName { get; set; }
+
     /// <summary>
     /// 按钮权限集合
     /// </summary>

+ 7 - 0
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -110,6 +110,9 @@ public class SysAuthService : IDynamicApiController, ITransient
         if (user == null)
             throw Oops.Oh(ErrorCodeEnum.D1011);
 
+        var org = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOrg>>().GetFirstAsync(u => u.Id == user.OrgId);
+        var pos = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysPos>>().GetFirstAsync(u => u.Id == user.PosId);
+
         // 按钮权限集合
         var buttons = await _sysMenuService.GetPermCodeList();
 
@@ -136,6 +139,10 @@ public class SysAuthService : IDynamicApiController, ITransient
             Account = user.Account,
             RealName = user.RealName,
             Avatar = user.Avatar,
+            Address = user.Address,
+            Signature = user.Signature,
+            OrgName = org?.Name,
+            PosName = pos?.Name,
             Buttons = buttons
         };
     }

+ 47 - 11
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -49,11 +49,12 @@ public class SysFileService : IDynamicApiController, ITransient
     /// 上传文件
     /// </summary>
     /// <param name="file"></param>
+    /// <param name="path"></param>
     /// <returns></returns>
     [HttpPost("/sysFile/upload")]
-    public async Task<FileOutput> UploadFile([Required] IFormFile file)
+    public async Task<FileOutput> UploadFile([Required] IFormFile file, string path = "")
     {
-        var sysFile = await HandleUploadFile(file);
+        var sysFile = await HandleUploadFile(file, path);
         return new FileOutput
         {
             Id = sysFile.Id,
@@ -145,19 +146,24 @@ public class SysFileService : IDynamicApiController, ITransient
     /// 上传文件
     /// </summary>
     /// <param name="file">文件</param>
+    /// <param name="savePath">路径</param>
     /// <returns></returns>
-    private async Task<SysFile> HandleUploadFile(IFormFile file)
+    private async Task<SysFile> HandleUploadFile(IFormFile file, string savePath = "")
     {
         if (file == null) throw Oops.Oh(ErrorCodeEnum.D8000);
 
-        string path = _uploadOptions.Path;
-        var reg = new Regex(@"(\{.+?})");
-        var match = reg.Matches(path);
-        match.ToList().ForEach(a =>
+        var path = savePath;
+        if (string.IsNullOrWhiteSpace(savePath))
         {
-            var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2));
-            path = path.Replace(a.ToString(), str);
-        });
+            path = _uploadOptions.Path;
+            var reg = new Regex(@"(\{.+?})");
+            var match = reg.Matches(path);
+            match.ToList().ForEach(a =>
+            {
+                var str = DateTime.Now.ToString(a.ToString().Substring(1, a.Length - 2)); // 每天一个目录
+                path = path.Replace(a.ToString(), str);
+            });
+        }
 
         if (!_uploadOptions.ContentType.Contains(file.ContentType))
             throw Oops.Oh(ErrorCodeEnum.D8001);
@@ -210,7 +216,7 @@ public class SysFileService : IDynamicApiController, ITransient
 
             var realFile = Path.Combine(filePath, finalName);
             IDetector detector;
-            using (var stream = File.Create(realFile)) 
+            using (var stream = File.Create(realFile))
             {
                 await file.CopyToAsync(stream);
                 detector = stream.DetectFiletype();
@@ -242,4 +248,34 @@ public class SysFileService : IDynamicApiController, ITransient
     {
         return await _OSSService.PresignedGetObjectAsync(bucketName, fileName, 7);
     }
+
+    /// <summary>
+    /// 上传头像
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    [HttpPost("/sysFile/uploadAvatar")]
+    public async Task<FileOutput> UploadAvatar([Required] IFormFile file)
+    {
+        var res = await UploadFile(file, "Avatar");
+        var userId = App.GetService<UserManager>().UserId;
+        await _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+            .UpdateAsync(u => new SysUser() { Avatar = res.Url }, u => u.Id == userId);
+        return res;
+    }
+
+    /// <summary>
+    /// 上传电子签名
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    [HttpPost("/sysFile/uploadSignature")]
+    public async Task<FileOutput> UploadSignature([Required] IFormFile file)
+    {
+        var res = await UploadFile(file, "Signature");
+        var userId = App.GetService<UserManager>().UserId;
+        await _sysFileRep.ChangeRepository<SqlSugarRepository<SysUser>>()
+            .UpdateAsync(u => new SysUser() { Signature = res.Url }, u => u.Id == userId);
+        return res;
+    }
 }

+ 0 - 5
Admin.NET/Admin.NET.Core/Service/User/Dto/UserInput.cs

@@ -46,11 +46,6 @@ public class AddUserInput : SysUser
     [Required(ErrorMessage = "真实姓名不能为空")]
     public override string RealName { get; set; }
 
-    /// <summary>
-    /// 账号
-    /// </summary>
-    public override string Password { get; set; } = CommonConst.SysPassword;
-
     /// <summary>
     /// 角色集合
     /// </summary>

+ 15 - 4
Admin.NET/Admin.NET.Core/Service/User/SysUserService.cs

@@ -122,13 +122,24 @@ public class SysUserService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 查看用户
+    /// 查看用户基本信息
     /// </summary>
     /// <returns></returns>
-    [HttpGet("/sysUser/detail")]
-    public async Task<SysUser> GetUser(long id)
+    [HttpGet("/sysUser/base")]
+    public async Task<SysUser> GetUserBase()
     {
-        return await _sysUserRep.GetFirstAsync(u => u.Id == id);
+        return await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
+    }
+
+    /// <summary>
+    /// 设置用户基本信息
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysUser/base")]
+    public async Task<int> UpdateUserBase(SysUser user)
+    {
+        return await _sysUserRep.AsUpdateable(user)
+            .IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
     }
 
     /// <summary>

+ 2 - 1
Web/package.json

@@ -36,7 +36,8 @@
 		"vue-grid-layout": "^3.0.0-beta1",
 		"vue-i18n": "^9.2.2",
 		"vue-router": "^4.1.2",
-		"vue-json-pretty": "^2.2.2"
+		"vue-json-pretty": "^2.2.2",
+		"vue-signature-pad":"^3.0.2"
 	},
 	"devDependencies": {
 		"@types/node": "^18.0.6",

+ 1 - 0
Web/plugins.d.ts

@@ -3,3 +3,4 @@ declare module 'qrcodejs2-fixes';
 declare module 'splitpanes';
 declare module 'js-cookie';
 declare module 'vform3-builds'
+declare module 'vue-signature-pad'

+ 175 - 7
Web/src/api-services/apis/sys-file-api.ts

@@ -190,14 +190,60 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
                 options: localVarRequestOptions,
             };
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysFileUploadAvatarPostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysFile/uploadAvatar`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+            const localVarFormParams = new FormData();
+
+            // authentication Bearer required
+
+
+            if (file !== undefined) { 
+                localVarFormParams.append('file', file as any);
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            localVarRequestOptions.data = localVarFormParams;
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysFileUploadPostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        sysFileUploadPostForm: async (file?: Blob, path?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/sysFile/upload`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -212,6 +258,55 @@ export const SysFileApiAxiosParamCreator = function (configuration?: Configurati
 
             // authentication Bearer required
 
+            if (path !== undefined) {
+                localVarQueryParameter['path'] = path;
+            }
+
+
+            if (file !== undefined) { 
+                localVarFormParams.append('file', file as any);
+            }
+
+            localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            localVarRequestOptions.data = localVarFormParams;
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysFileUploadSignaturePostForm: async (file?: Blob, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysFile/uploadSignature`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+            const localVarFormParams = new FormData();
+
+            // authentication Bearer required
+
 
             if (file !== undefined) { 
                 localVarFormParams.append('file', file as any);
@@ -339,15 +434,44 @@ export const SysFileApiFp = function(configuration?: Configuration) {
                 return axios.request(axiosRequestArgs);
             };
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadAvatarPostForm(file, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
-            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadPostForm(file, options);
+        async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadPostForm(file, path, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultFileOutput>>> {
+            const localVarAxiosArgs = await SysFileApiAxiosParamCreator(configuration).sysFileUploadSignaturePostForm(file, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -413,15 +537,36 @@ export const SysFileApiFactory = function (configuration?: Configuration, basePa
         async sysFilePageGet(fileName?: string, startTime?: Date, endTime?: Date, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysFile>> {
             return SysFileApiFp(configuration).sysFilePageGet(fileName, startTime, endTime, page, pageSize, field, order, descStr, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 上传头像
+         * @param {Blob} [file] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadAvatarPostForm(file, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 上传文件
          * @param {Blob} [file] 
+         * @param {string} [path] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadPostForm(file, path, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 上传电子签名
+         * @param {Blob} [file] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
-            return SysFileApiFp(configuration).sysFileUploadPostForm(file, options).then((request) => request(axios, basePath));
+        async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultFileOutput>> {
+            return SysFileApiFp(configuration).sysFileUploadSignaturePostForm(file, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -483,16 +628,39 @@ export class SysFileApi extends BaseAPI {
     public async sysFilePageGet(fileName?: string, startTime?: Date, endTime?: Date, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysFile>> {
         return SysFileApiFp(this.configuration).sysFilePageGet(fileName, startTime, endTime, page, pageSize, field, order, descStr, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 上传头像
+     * @param {Blob} [file] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysFileApi
+     */
+    public async sysFileUploadAvatarPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadAvatarPostForm(file, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 上传文件
      * @param {Blob} [file] 
+     * @param {string} [path] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysFileApi
+     */
+    public async sysFileUploadPostForm(file?: Blob, path?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadPostForm(file, path, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 上传电子签名
+     * @param {Blob} [file] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysFileApi
      */
-    public async sysFileUploadPostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
-        return SysFileApiFp(this.configuration).sysFileUploadPostForm(file, options).then((request) => request(this.axios, this.basePath));
+    public async sysFileUploadSignaturePostForm(file?: Blob, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultFileOutput>> {
+        return SysFileApiFp(this.configuration).sysFileUploadSignaturePostForm(file, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 118 - 49
Web/src/api-services/apis/sys-user-api.ts

@@ -25,6 +25,7 @@ import { AdminResultSysUser } from '../models';
 import { ChangePwdInput } from '../models';
 import { DeleteUserInput } from '../models';
 import { ResetPwdUserInput } from '../models';
+import { SysUser } from '../models';
 import { UpdateUserInput } from '../models';
 import { UserInput } from '../models';
 import { UserRoleInput } from '../models';
@@ -77,13 +78,49 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 修改用户密码
-         * @param {ChangePwdInput} [body] 
+         * @summary 查看用户基本信息
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserChangePwdPost: async (body?: ChangePwdInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/changePwd`;
+        sysUserBaseGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/base`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 设置用户基本信息
+         * @param {SysUser} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysUserBasePost: async (body?: SysUser, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/base`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
             let baseOptions;
@@ -118,13 +155,13 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 删除用户
-         * @param {DeleteUserInput} [body] 
+         * @summary 修改用户密码
+         * @param {ChangePwdInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserDeletePost: async (body?: DeleteUserInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/delete`;
+        sysUserChangePwdPost: async (body?: ChangePwdInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/changePwd`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
             let baseOptions;
@@ -159,28 +196,26 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
         },
         /**
          * 
-         * @summary 查看用户
-         * @param {number} [id] 
+         * @summary 删除用户
+         * @param {DeleteUserInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        sysUserDetailGet: async (id?: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            const localVarPath = `/sysUser/detail`;
+        sysUserDeletePost: async (body?: DeleteUserInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysUser/delete`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
             let baseOptions;
             if (configuration) {
                 baseOptions = configuration.baseOptions;
             }
-            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
             const localVarHeaderParameter = {} as any;
             const localVarQueryParameter = {} as any;
 
             // authentication Bearer required
 
-            if (id !== undefined) {
-                localVarQueryParameter['id'] = id;
-            }
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
 
             const query = new URLSearchParams(localVarUrlObj.search);
             for (const key in localVarQueryParameter) {
@@ -192,6 +227,8 @@ export const SysUserApiAxiosParamCreator = function (configuration?: Configurati
             localVarUrlObj.search = (new URLSearchParams(query)).toString();
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
 
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
@@ -552,13 +589,12 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 修改用户密码
-         * @param {ChangePwdInput} [body] 
+         * @summary 查看用户基本信息
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserChangePwdPost(body?: ChangePwdInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserChangePwdPost(body, options);
+        async sysUserBaseGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysUser>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserBaseGet(options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -566,13 +602,27 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 删除用户
-         * @param {DeleteUserInput} [body] 
+         * @summary 设置用户基本信息
+         * @param {SysUser} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDeletePost(body, options);
+        async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserBasePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 修改用户密码
+         * @param {ChangePwdInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserChangePwdPost(body?: ChangePwdInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt32>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserChangePwdPost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -580,13 +630,13 @@ export const SysUserApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 查看用户
-         * @param {number} [id] 
+         * @summary 删除用户
+         * @param {DeleteUserInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async sysUserDetailGet(id?: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSysUser>>> {
-            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDetailGet(id, options);
+        async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysUserApiAxiosParamCreator(configuration).sysUserDeletePost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -717,6 +767,25 @@ export const SysUserApiFactory = function (configuration?: Configuration, basePa
         async sysUserAddPost(body?: AddUserInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysUserApiFp(configuration).sysUserAddPost(body, options).then((request) => request(axios, basePath));
         },
+        /**
+         * 
+         * @summary 查看用户基本信息
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserBaseGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysUser>> {
+            return SysUserApiFp(configuration).sysUserBaseGet(options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 设置用户基本信息
+         * @param {SysUser} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultInt32>> {
+            return SysUserApiFp(configuration).sysUserBasePost(body, options).then((request) => request(axios, basePath));
+        },
         /**
          * 
          * @summary 修改用户密码
@@ -737,16 +806,6 @@ export const SysUserApiFactory = function (configuration?: Configuration, basePa
         async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
             return SysUserApiFp(configuration).sysUserDeletePost(body, options).then((request) => request(axios, basePath));
         },
-        /**
-         * 
-         * @summary 查看用户
-         * @param {number} [id] 
-         * @param {*} [options] Override http request option.
-         * @throws {RequiredError}
-         */
-        async sysUserDetailGet(id?: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSysUser>> {
-            return SysUserApiFp(configuration).sysUserDetailGet(id, options).then((request) => request(axios, basePath));
-        },
         /**
          * 
          * @summary 授权用户角色
@@ -846,6 +905,27 @@ export class SysUserApi extends BaseAPI {
     public async sysUserAddPost(body?: AddUserInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysUserApiFp(this.configuration).sysUserAddPost(body, options).then((request) => request(this.axios, this.basePath));
     }
+    /**
+     * 
+     * @summary 查看用户基本信息
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserApi
+     */
+    public async sysUserBaseGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysUser>> {
+        return SysUserApiFp(this.configuration).sysUserBaseGet(options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 设置用户基本信息
+     * @param {SysUser} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysUserApi
+     */
+    public async sysUserBasePost(body?: SysUser, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultInt32>> {
+        return SysUserApiFp(this.configuration).sysUserBasePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
     /**
      * 
      * @summary 修改用户密码
@@ -868,17 +948,6 @@ export class SysUserApi extends BaseAPI {
     public async sysUserDeletePost(body?: DeleteUserInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
         return SysUserApiFp(this.configuration).sysUserDeletePost(body, options).then((request) => request(this.axios, this.basePath));
     }
-    /**
-     * 
-     * @summary 查看用户
-     * @param {number} [id] 
-     * @param {*} [options] Override http request option.
-     * @throws {RequiredError}
-     * @memberof SysUserApi
-     */
-    public async sysUserDetailGet(id?: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSysUser>> {
-        return SysUserApiFp(this.configuration).sysUserDetailGet(id, options).then((request) => request(this.axios, this.basePath));
-    }
     /**
      * 
      * @summary 授权用户角色

+ 6 - 0
Web/src/api-services/models/add-user-input.ts

@@ -271,6 +271,12 @@ export interface AddUserInput {
      * @memberof AddUserInput
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof AddUserInput
+     */
+    signature?: string | null;
     /**
      * 账号
      * @type {string}

+ 2 - 0
Web/src/api-services/models/index.ts

@@ -151,7 +151,9 @@ export * from './sys-config';
 export * from './sys-dict-data';
 export * from './sys-dict-type';
 export * from './sys-file';
+export * from './sys-file-upload-avatar-body';
 export * from './sys-file-upload-body';
+export * from './sys-file-upload-signature-body';
 export * from './sys-file-uploads-body';
 export * from './sys-log-diff';
 export * from './sys-log-ex';

+ 15 - 9
Web/src/api-services/models/login-user-output.ts

@@ -17,12 +17,6 @@
  * @interface LoginUserOutput
  */
 export interface LoginUserOutput {
-    /**
-     * 用户Id
-     * @type {number}
-     * @memberof LoginUserOutput
-     */
-    userId?: number;
     /**
      * 账号名称
      * @type {string}
@@ -48,17 +42,29 @@ export interface LoginUserOutput {
      */
     introduction?: string | null;
     /**
-     * 机构Id
-     * @type {number}
+     * 地址
+     * @type {string}
+     * @memberof LoginUserOutput
+     */
+    address?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
      * @memberof LoginUserOutput
      */
-    orgId?: number;
+    signature?: string | null;
     /**
      * 机构名称
      * @type {string}
      * @memberof LoginUserOutput
      */
     orgName?: string | null;
+    /**
+     * 职位名称
+     * @type {string}
+     * @memberof LoginUserOutput
+     */
+    posName?: string | null;
     /**
      * 按钮权限集合
      * @type {Array<string>}

+ 26 - 0
Web/src/api-services/models/sys-file-upload-avatar-body.ts

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 
+ * @export
+ * @interface SysFileUploadAvatarBody
+ */
+export interface SysFileUploadAvatarBody {
+    /**
+     * 
+     * @type {Blob}
+     * @memberof SysFileUploadAvatarBody
+     */
+    file: Blob;
+}

+ 26 - 0
Web/src/api-services/models/sys-file-upload-signature-body.ts

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 
+ * @export
+ * @interface SysFileUploadSignatureBody
+ */
+export interface SysFileUploadSignatureBody {
+    /**
+     * 
+     * @type {Blob}
+     * @memberof SysFileUploadSignatureBody
+     */
+    file: Blob;
+}

+ 6 - 0
Web/src/api-services/models/sys-user.ts

@@ -282,4 +282,10 @@ export interface SysUser {
      * @memberof SysUser
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof SysUser
+     */
+    signature?: string | null;
 }

+ 6 - 0
Web/src/api-services/models/update-user-input.ts

@@ -271,6 +271,12 @@ export interface UpdateUserInput {
      * @memberof UpdateUserInput
      */
     lastLoginDevice?: string | null;
+    /**
+     * 电子签名
+     * @type {string}
+     * @memberof UpdateUserInput
+     */
+    signature?: string | null;
     /**
      * 账号
      * @type {string}

+ 1 - 1
Web/src/layout/navBars/breadcrumb/user.vue

@@ -66,7 +66,7 @@
 				<el-dropdown-menu>
 					<!-- <el-dropdown-item command="/dashboard/workbench">{{ $t('message.user.dropdown1') }}</el-dropdown-item> -->
 					<!-- <el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item> -->
-					<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
+					<el-dropdown-item command="/system/userCenter">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
 					<!-- <el-dropdown-item command="/404">{{ $t('message.user.dropdown3') }}</el-dropdown-item>
 					<el-dropdown-item command="/401">{{ $t('message.user.dropdown4') }}</el-dropdown-item> -->
 					<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>

+ 4 - 3
Web/src/main.ts

@@ -7,19 +7,20 @@ import { i18n } from '/@/i18n/index';
 import other from '/@/utils/other';
 
 import ElementPlus from 'element-plus';
-import VForm3 from 'vform3-builds'; // 引入VForm 3库
+import VForm3 from 'vform3-builds'; // VForm3表单设计
 import 'element-plus/dist/index.css';
-import 'vform3-builds/dist/designer.style.css'; // 引入VForm3样式
+import 'vform3-builds/dist/designer.style.css'; // VForm3表单设计样式
 import '/@/theme/index.scss';
 import mitt from 'mitt';
 import VueGridLayout from 'vue-grid-layout';
+import VueSignaturePad from 'vue-signature-pad'; // 电子签名
 
 const app = createApp(App);
 
 directive(app);
 other.elSvg(app);
 
-app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(VForm3).use(i18n).use(VueGridLayout).mount('#app');
+app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(VForm3).use(i18n).use(VueGridLayout).use(VueSignaturePad).mount('#app');
 
 const globalProperties = {
 	mittBus: mitt(),

+ 4 - 0
Web/src/stores/interface/index.ts

@@ -8,6 +8,10 @@ export interface UserInfosState {
 	account: string;
 	realName: string;
 	avatar: string;
+	orgName: string;
+	posName: string;
+	address: string;
+	signature: string;
 	roles: string[];
 	authBtnList: string[];
 	time: number;

+ 6 - 9
Web/src/stores/userInfo.ts

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia';
-import { UserInfosStates } from './interface';
+import { UserInfosState, UserInfosStates } from './interface';
 import { Session } from '/@/utils/storage';
 
 import { getAPI } from '/@/utils/axios-utils';
@@ -11,14 +11,7 @@ import { SysAuthApi } from '/@/api-services/api';
  */
 export const useUserInfo = defineStore('userInfo', {
 	state: (): UserInfosStates => ({
-		userInfos: {
-			account: '',
-			realName: '',
-			avatar: '',
-			roles: [],
-			authBtnList: [],
-			time: 0,
-		},
+		userInfos: {} as UserInfosState,
 	}),
 	actions: {
 		async setUserInfos() {
@@ -42,6 +35,10 @@ export const useUserInfo = defineStore('userInfo', {
 							account: d.account,
 							realName: d.realName,
 							avatar: d.avatar ? d.avatar : '/favicon.ico',
+							orgName: d.orgName,
+							posName: d.posName,
+							address: d.address,
+							signature: d.signature,
 							roles: [],
 							authBtnList: d.buttons,
 							time: new Date().getTime(),

+ 71 - 28
Web/src/utils/base64Conver.ts

@@ -2,16 +2,16 @@
  * @description: base64 to blob
  */
 export function dataURLtoBlob(base64Buf: string): Blob {
-  const arr = base64Buf.split(',');
-  const typeItem = arr[0];
-  const mime = typeItem.match(/:(.*?);/)![1];
-  const bstr = window.atob(arr[1]);
-  let n = bstr.length;
-  const u8arr = new Uint8Array(n);
-  while (n--) {
-    u8arr[n] = bstr.charCodeAt(n);
-  }
-  return new Blob([u8arr], { type: mime });
+	const arr = base64Buf.split(',');
+	const typeItem = arr[0];
+	const mime = typeItem.match(/:(.*?);/)![1];
+	const bstr = window.atob(arr[1]);
+	let n = bstr.length;
+	const u8arr = new Uint8Array(n);
+	while (n--) {
+		u8arr[n] = bstr.charCodeAt(n);
+	}
+	return new Blob([u8arr], { type: mime });
 }
 
 /**
@@ -19,23 +19,66 @@ export function dataURLtoBlob(base64Buf: string): Blob {
  * @param url
  */
 export function urlToBase64(url: string, mineType?: string): Promise<string> {
-  return new Promise((resolve, reject) => {
-    let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
-    const ctx = canvas!.getContext('2d');
+	return new Promise((resolve, reject) => {
+		let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
+		const ctx = canvas!.getContext('2d');
 
-    const img = new Image();
-    img.crossOrigin = '';
-    img.onload = function () {
-      if (!canvas || !ctx) {
-        return reject();
-      }
-      canvas.height = img.height;
-      canvas.width = img.width;
-      ctx.drawImage(img, 0, 0);
-      const dataURL = canvas.toDataURL(mineType || 'image/png');
-      canvas = null;
-      resolve(dataURL);
-    };
-    img.src = url;
-  });
+		const img = new Image();
+		img.crossOrigin = '';
+		img.onload = function () {
+			if (!canvas || !ctx) {
+				return reject();
+			}
+			canvas.height = img.height;
+			canvas.width = img.width;
+			ctx.drawImage(img, 0, 0);
+			const dataURL = canvas.toDataURL(mineType || 'image/png');
+			canvas = null;
+			resolve(dataURL);
+		};
+		img.src = url;
+	});
+}
+
+/**
+ * File转Base64
+ * @param file
+ */
+export function fileToBase64(file: Blob) {
+	return new Promise((resolve, reject) => {
+		const reader = new FileReader();
+		reader.readAsDataURL(file);
+		reader.onload = () => resolve(reader.result);
+		reader.onerror = (error) => reject(error);
+	});
+}
+
+/**
+ * Base64转File
+ * @param dataURL   {String}  base64
+ * @param fileName	{String}  文件名
+ * @param mimeType	{String}  [可选]文件类型,默认为base64中的类型
+ * @returns {File}
+ */
+export function base64ToFile(dataURL: string, fileName: string, mimeType = null) {
+	var arr = dataURL.split(',');
+	var defaultMimeType = arr[0].match(/:(.*?);/)[1];
+	var bStr = atob(arr[1]);
+	let n = bStr.length;
+	var u8arr = new Uint8Array(n);
+	while (n--) {
+		u8arr[n] = bStr.charCodeAt(n);
+	}
+	return new File([u8arr], fileName, { type: mimeType || defaultMimeType });
+}
+
+/**
+ * Blob转File
+ * @param blob     {Blob}   blob
+ * @param fileName {String} 文件名
+ * @param mimeType {String} 文件类型
+ * @return {File}
+ */
+export function blobToFile(blob: Blob, fileName: string, mimeType: any) {
+	return new File([blob], fileName, { type: mimeType });
 }

+ 0 - 387
Web/src/views/personal/index.vue

@@ -1,387 +0,0 @@
-<template>
-	<div class="personal">
-		<el-row>
-			<!-- 个人信息 -->
-			<el-col :xs="24" :sm="16">
-				<el-card shadow="hover" header="个人信息">
-					<div class="personal-user">
-						<div class="personal-user-left">
-							<el-upload class="h100 personal-user-left-upload" action="https://jsonplaceholder.typicode.com/posts/" multiple :limit="1">
-								<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
-							</el-upload>
-						</div>
-						<div class="personal-user-right">
-							<el-row>
-								<el-col :span="24" class="personal-title mb18">{{ currentTime }},admin,生活变的再糟糕,也不妨碍我变得更好! </el-col>
-								<el-col :span="24">
-									<el-row>
-										<el-col :xs="24" :sm="8" class="personal-item mb6">
-											<div class="personal-item-label">昵称:</div>
-											<div class="personal-item-value">小柒</div>
-										</el-col>
-										<el-col :xs="24" :sm="16" class="personal-item mb6">
-											<div class="personal-item-label">身份:</div>
-											<div class="personal-item-value">超级管理</div>
-										</el-col>
-									</el-row>
-								</el-col>
-								<el-col :span="24">
-									<el-row>
-										<el-col :xs="24" :sm="8" class="personal-item mb6">
-											<div class="personal-item-label">登录IP:</div>
-											<div class="personal-item-value">192.168.1.1</div>
-										</el-col>
-										<el-col :xs="24" :sm="16" class="personal-item mb6">
-											<div class="personal-item-label">登录时间:</div>
-											<div class="personal-item-value">2021-02-05 18:47:26</div>
-										</el-col>
-									</el-row>
-								</el-col>
-							</el-row>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-
-			<!-- 消息通知 -->
-			<el-col :xs="24" :sm="8" class="pl15 personal-info">
-				<el-card shadow="hover">
-					<template #header>
-						<span>消息通知</span>
-						<span class="personal-info-more">更多</span>
-					</template>
-					<div class="personal-info-box">
-						<ul class="personal-info-ul">
-							<li v-for="(v, k) in newsInfoList" :key="k" class="personal-info-li">
-								<a :href="v.link" target="_block" class="personal-info-li-title">{{ v.title }}</a>
-							</li>
-						</ul>
-					</div>
-				</el-card>
-			</el-col>
-
-			<!-- 营销推荐 -->
-			<el-col :span="24">
-				<el-card shadow="hover" class="mt15" header="营销推荐">
-					<el-row :gutter="15" class="personal-recommend-row">
-						<el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
-							<div class="personal-recommend" :style="{ 'background-color': v.bg }">
-								<SvgIcon :name="v.icon" :size="70" :style="{ color: v.iconColor }" />
-								<div class="personal-recommend-auto">
-									<div>{{ v.title }}</div>
-									<div class="personal-recommend-msg">{{ v.msg }}</div>
-								</div>
-							</div>
-						</el-col>
-					</el-row>
-				</el-card>
-			</el-col>
-
-			<!-- 更新信息 -->
-			<el-col :span="24">
-				<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
-					<div class="personal-edit-title">基本信息</div>
-					<el-form :model="personalForm" size="default" label-width="40px" class="mt35 mb35">
-						<el-row :gutter="35">
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="昵称">
-									<el-input v-model="personalForm.name" placeholder="请输入昵称" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="邮箱">
-									<el-input v-model="personalForm.email" placeholder="请输入邮箱" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="签名">
-									<el-input v-model="personalForm.autograph" placeholder="请输入签名" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="职业">
-									<el-select v-model="personalForm.occupation" placeholder="请选择职业" clearable class="w100">
-										<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
-										<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
-										<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
-									</el-select>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="手机">
-									<el-input v-model="personalForm.phone" placeholder="请输入手机" clearable></el-input>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
-								<el-form-item label="性别">
-									<el-select v-model="personalForm.sex" placeholder="请选择性别" clearable class="w100">
-										<el-option label="男" value="1"></el-option>
-										<el-option label="女" value="2"></el-option>
-									</el-select>
-								</el-form-item>
-							</el-col>
-							<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-								<el-form-item>
-									<el-button type="primary">
-										<el-icon>
-											<ele-Position />
-										</el-icon>
-										更新个人信息
-									</el-button>
-								</el-form-item>
-							</el-col>
-						</el-row>
-					</el-form>
-					<div class="personal-edit-title mb15">账号安全</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">账户密码</div>
-								<div class="personal-edit-safe-item-left-value">当前密码强度:强</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即修改</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">密保手机</div>
-								<div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即修改</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">密保问题</div>
-								<div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即设置</el-button>
-							</div>
-						</div>
-					</div>
-					<div class="personal-edit-safe-box">
-						<div class="personal-edit-safe-item">
-							<div class="personal-edit-safe-item-left">
-								<div class="personal-edit-safe-item-left-label">绑定QQ</div>
-								<div class="personal-edit-safe-item-left-value">已绑定QQ:110****566</div>
-							</div>
-							<div class="personal-edit-safe-item-right">
-								<el-button text type="primary">立即设置</el-button>
-							</div>
-						</div>
-					</div>
-				</el-card>
-			</el-col>
-		</el-row>
-	</div>
-</template>
-
-<script lang="ts">
-import { toRefs, reactive, computed, defineComponent } from 'vue';
-import { formatAxis } from '/@/utils/formatTime';
-import { newsInfoList, recommendList } from './mock';
-
-// 定义接口来定义对象的类型
-interface PersonalState {
-	newsInfoList: any;
-	recommendList: any;
-	personalForm: any;
-}
-
-export default defineComponent({
-	name: 'personal',
-	setup() {
-		const state = reactive<PersonalState>({
-			newsInfoList,
-			recommendList,
-			personalForm: {
-				name: '',
-				email: '',
-				autograph: '',
-				occupation: '',
-				phone: '',
-				sex: '',
-			},
-		});
-		// 当前时间提示语
-		const currentTime = computed(() => {
-			return formatAxis(new Date());
-		});
-		return {
-			currentTime,
-			...toRefs(state),
-		};
-	},
-});
-</script>
-
-<style scoped lang="scss">
-@import '../../theme/mixins/index.scss';
-.personal {
-	.personal-user {
-		height: 130px;
-		display: flex;
-		align-items: center;
-		.personal-user-left {
-			width: 100px;
-			height: 130px;
-			border-radius: 3px;
-			:deep(.el-upload) {
-				height: 100%;
-			}
-			.personal-user-left-upload {
-				img {
-					width: 100%;
-					height: 100%;
-					border-radius: 3px;
-				}
-				&:hover {
-					img {
-						animation: logoAnimation 0.3s ease-in-out;
-					}
-				}
-			}
-		}
-		.personal-user-right {
-			flex: 1;
-			padding: 0 15px;
-			.personal-title {
-				font-size: 18px;
-				@include text-ellipsis(1);
-			}
-			.personal-item {
-				display: flex;
-				align-items: center;
-				font-size: 13px;
-				.personal-item-label {
-					color: var(--el-text-color-secondary);
-					@include text-ellipsis(1);
-				}
-				.personal-item-value {
-					@include text-ellipsis(1);
-				}
-			}
-		}
-	}
-	.personal-info {
-		.personal-info-more {
-			float: right;
-			color: var(--el-text-color-secondary);
-			font-size: 13px;
-			&:hover {
-				color: var(--el-color-primary);
-				cursor: pointer;
-			}
-		}
-		.personal-info-box {
-			height: 130px;
-			overflow: hidden;
-			.personal-info-ul {
-				list-style: none;
-				.personal-info-li {
-					font-size: 13px;
-					padding-bottom: 10px;
-					.personal-info-li-title {
-						display: inline-block;
-						@include text-ellipsis(1);
-						color: var(--el-text-color-secondary);
-						text-decoration: none;
-					}
-					& a:hover {
-						color: var(--el-color-primary);
-						cursor: pointer;
-					}
-				}
-			}
-		}
-	}
-	.personal-recommend-row {
-		.personal-recommend-col {
-			.personal-recommend {
-				position: relative;
-				height: 100px;
-				border-radius: 3px;
-				overflow: hidden;
-				cursor: pointer;
-				&:hover {
-					i {
-						right: 0px !important;
-						bottom: 0px !important;
-						transition: all ease 0.3s;
-					}
-				}
-				i {
-					position: absolute;
-					right: -10px;
-					bottom: -10px;
-					font-size: 70px;
-					transform: rotate(-30deg);
-					transition: all ease 0.3s;
-				}
-				.personal-recommend-auto {
-					padding: 15px;
-					position: absolute;
-					left: 0;
-					top: 5%;
-					color: var(--next-color-white);
-					.personal-recommend-msg {
-						font-size: 12px;
-						margin-top: 10px;
-					}
-				}
-			}
-		}
-	}
-	.personal-edit {
-		.personal-edit-title {
-			position: relative;
-			padding-left: 10px;
-			color: var(--el-text-color-regular);
-			&::after {
-				content: '';
-				width: 2px;
-				height: 10px;
-				position: absolute;
-				left: 0;
-				top: 50%;
-				transform: translateY(-50%);
-				background: var(--el-color-primary);
-			}
-		}
-		.personal-edit-safe-box {
-			border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
-			padding: 15px 0;
-			.personal-edit-safe-item {
-				width: 100%;
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
-				.personal-edit-safe-item-left {
-					flex: 1;
-					overflow: hidden;
-					.personal-edit-safe-item-left-label {
-						color: var(--el-text-color-regular);
-						margin-bottom: 5px;
-					}
-					.personal-edit-safe-item-left-value {
-						color: var(--el-text-color-secondary);
-						@include text-ellipsis(1);
-						margin-right: 15px;
-					}
-				}
-			}
-			&:last-of-type {
-				padding-bottom: 0;
-				border-bottom: none;
-			}
-		}
-	}
-}
-</style>

+ 0 - 66
Web/src/views/personal/mock.ts

@@ -1,66 +0,0 @@
-/**
- * 消息通知
- * @returns 返回模拟数据
- */
-export const newsInfoList: Array<object> = [
-	{
-		title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本',
-		date: '02/28',
-		link: 'https://gitee.com/lyt-top/vue-next-admin',
-	},
-	{
-		title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本',
-		date: '04/15',
-		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
-	},
-	{
-		title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本',
-		date: '04/10',
-		link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
-	},
-	{
-		title: '[预览] 2020年12月08日,基于 vue3.x 版本后台模板的预览',
-		date: '12/08',
-		link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login',
-	},
-	{
-		title: '[预览] 2020年11月15日,基于 vue2.x 版本后台模板的预览',
-		date: '11/15',
-		link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login',
-	},
-];
-
-/**
- * 营销推荐
- * @returns 返回模拟数据
- */
-export const recommendList: Array<object> = [
-	{
-		title: '优惠券',
-		msg: '现金券、折扣券、营销必备',
-		icon: 'ele-Food',
-		bg: '#48D18D',
-		iconColor: '#64d89d',
-	},
-	{
-		title: '多人拼团',
-		msg: '社交电商、开辟流量',
-		icon: 'ele-ShoppingCart',
-		bg: '#F95959',
-		iconColor: '#F86C6B',
-	},
-	{
-		title: '分销中心',
-		msg: '轻松招募分销员,成功推广奖励',
-		icon: 'ele-School',
-		bg: '#8595F4',
-		iconColor: '#92A1F4',
-	},
-	{
-		title: '秒杀',
-		msg: '超低价抢购引导更多销量',
-		icon: 'ele-AlarmClock',
-		bg: '#FEBB50',
-		iconColor: '#FDC566',
-	},
-];

+ 1 - 1
Web/src/views/system/menu/index.vue

@@ -14,7 +14,7 @@
 				</el-form-item>
 				<el-form-item>
 					<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
-					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysMenu:page'"> 查询 </el-button>
+					<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysMenu:list'"> 查询 </el-button>
 					<el-button icon="ele-Plus" @click="openAddMenu" v-auth="'sysMenu:add'"> 新增 </el-button>
 				</el-form-item>
 			</el-form>

+ 1 - 1
Web/src/views/system/onlineUser/index.vue

@@ -73,7 +73,7 @@ const state = reactive({
 
 // 初始化SignalR对象
 const connection = new SignalR.HubConnectionBuilder()
-	.configureLogging(SignalR.LogLevel.Information)
+	//.configureLogging(SignalR.LogLevel.Information)
 	.withUrl(`${import.meta.env.VITE_API_URL}/hubs/onlineUser?access_token=${getToken()}`)
 	.withAutomaticReconnect({
 		nextRetryDelayInMilliseconds: (a) => {

+ 1 - 1
Web/src/views/system/org/index.vue

@@ -16,7 +16,7 @@
 						</el-form-item>
 						<el-form-item>
 							<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
-							<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOrg:page'"> 查询 </el-button>
+							<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysOrg:list'"> 查询 </el-button>
 							<el-button icon="ele-Plus" @click="openAddOrg" v-auth="'sysOrg:add'"> 新增 </el-button>
 						</el-form-item>
 					</el-form>

+ 0 - 81
Web/src/views/system/password/index.vue

@@ -1,81 +0,0 @@
-<template>
-	<div class="sys-password-container">
-		<NoticeBar text="账号密码修改,请慎重操作!" leftIcon="iconfont icon-tongzhi2" background="var(--el-color-primary-light-9)" color="var(--el-color-primary)" />
-		<el-card shadow="hover" header="修改当前账号密码" class="mt8">
-			<el-form ref="ruleFormRef" :model="ruleForm" status-icon label-width="80px">
-				<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
-					<el-input v-model="ruleForm.passwordOld" type="password" autocomplete="off" />
-				</el-form-item>
-				<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
-					<el-input v-model="ruleForm.passwordNew" type="password" autocomplete="off" />
-				</el-form-item>
-				<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
-					<el-input type="password" />
-				</el-form-item>
-				<el-form-item>
-					<el-button icon="ele-Refresh" @click="reset" size="default">重 置</el-button>
-					<el-button icon="ele-Select" type="primary" @click="submit" size="default" v-auth="'sysUser:changePwd'">确 定</el-button>
-				</el-form-item>
-			</el-form>
-		</el-card>
-	</div>
-</template>
-
-<script lang="ts">
-import { reactive, toRefs, defineComponent, ref } from 'vue';
-import NoticeBar from '/@/components/noticeBar/index.vue';
-import { ElMessageBox } from 'element-plus';
-import { Session } from '/@/utils/storage';
-
-import { getAPI } from '/@/utils/axios-utils';
-import { SysUserApi } from '/@/api-services/api';
-import { ChangePwdInput } from '/@/api-services/models';
-
-export default defineComponent({
-	name: 'sysPassword',
-	components: { NoticeBar },
-	setup() {
-		const ruleFormRef = ref();
-
-		const validatePassword = (rule: any, value: any, callback: any) => {
-			if (value != state.ruleForm.passwordNew) {
-				callback(new Error('两次密码不一致!'));
-			} else {
-				callback();
-			}
-		};
-		const state = reactive({
-			ruleForm: {} as ChangePwdInput,
-		});
-		// 重置
-		const reset = () => {
-			state.ruleForm.passwordOld = '';
-			state.ruleForm.passwordNew = '';
-		};
-		// 提交
-		const submit = () => {
-			ruleFormRef.value.validate(async (valid: boolean) => {
-				if (!valid) return;
-				await getAPI(SysUserApi).sysUserChangePwdPost(state.ruleForm);
-				// 退出系统
-				ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
-					confirmButtonText: '确定',
-					cancelButtonText: '取消',
-					type: 'warning',
-				}).then(async () => {
-					// 清除缓存
-					Session.clear();
-					window.location.reload();
-				});
-			});
-		};
-		return {
-			ruleFormRef,
-			reset,
-			submit,
-			validatePassword,
-			...toRefs(state),
-		};
-	},
-});
-</script>

+ 316 - 0
Web/src/views/system/user/component/userCenter.vue

@@ -0,0 +1,316 @@
+<template>
+	<div class="sys-userCenter-container">
+		<el-row :gutter="8" style="width: 100%">
+			<el-col :span="8" :xs="24">
+				<el-card shadow="hover">
+					<div class="account-center-avatarHolder">
+						<el-upload class="h100" ref="uploadAvatarRef" action="" :limit="1" :show-file-list="false" :auto-upload="false" :on-change="uploadAvatarFile" accept=".jpg,.png,.bmp,.gif">
+							<el-avatar :size="100" :src="userInfos.avatar" />
+						</el-upload>
+						<div class="username">{{ userInfos.realName }}</div>
+					</div>
+					<div class="account-center-org">
+						<p>
+							<el-icon><ele-School /></el-icon> <span>{{ userInfos.orgName ?? '超级管理员' }}</span>
+						</p>
+						<p>
+							<el-icon><ele-Mug /></el-icon> <span>{{ userInfos.posName ?? '超级管理员' }}</span>
+						</p>
+						<p>
+							<el-icon><ele-LocationInformation /></el-icon> <span>{{ userInfos.address ?? '家庭住址' }}</span>
+						</p>
+					</div>
+					<div class="image-signature">
+						<el-image :src="userInfos.signature" fit="contain" alt="电子签名" lazy style="width: 100%; height: 100%"> </el-image>
+					</div>
+					<el-button icon="ele-Edit" type="primary" @click="openSignDialog" v-auth="'sysUser:signature'"> 电子签名 </el-button>
+					<el-upload ref="uploadSignRef" action="" accept=".png" :limit="1" :show-file-list="false" :auto-upload="false" :on-change="uploadSignFile" style="display: inline-block; margin-left: 12px">
+						<el-button icon="ele-UploadFilled" style="display: inline-block">上传手写签名</el-button>
+					</el-upload>
+				</el-card>
+			</el-col>
+
+			<el-col :span="16" :xs="24" v-loading="loading">
+				<el-card shadow="hover">
+					<el-tabs>
+						<el-tab-pane label="基础信息">
+							<el-form :model="ruleFormBase" ref="ruleFormBaseRef" size="default" label-width="100px">
+								<el-row :gutter="35">
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="真实姓名" prop="realName" :rules="[{ required: true, message: '真实姓名不能为空', trigger: 'blur' }]">
+											<el-input v-model="ruleFormBase.realName" placeholder="真实姓名" clearable />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="昵称" prop="nickName">
+											<el-input v-model="ruleFormBase.nickName" placeholder="昵称" clearable />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="手机号码" prop="phone" :rules="[{ required: true, message: '手机号码不能为空', trigger: 'blur' }]">
+											<el-input v-model="ruleFormBase.phone" placeholder="手机号码" clearable />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="邮箱" prop="email">
+											<el-input v-model="ruleFormBase.email" placeholder="邮箱" clearable />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="出生日期" prop="birthday" :rules="[{ required: true, message: '出生日期不能为空', trigger: 'blur' }]">
+											<el-date-picker v-model="ruleFormBase.birthday" type="date" placeholder="出生日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+										<el-form-item label="性别" prop="sex">
+											<el-radio-group v-model="ruleFormBase.sex">
+												<el-radio :label="1">男</el-radio>
+												<el-radio :label="2">女</el-radio>
+											</el-radio-group>
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+										<el-form-item label="地址" prop="nation">
+											<el-input v-model="ruleFormBase.address" placeholder="地址" clearable type="textarea" />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+										<el-form-item label="备注" prop="remark">
+											<el-input v-model="ruleFormBase.remark" placeholder="备注" clearable type="textarea" />
+										</el-form-item>
+									</el-col>
+									<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+										<el-form-item>
+											<el-button icon="ele-SuccessFilled" type="primary" @click="submitUserBase" v-auth="'sysUser:updateBase'"> 保存基本信息 </el-button>
+										</el-form-item>
+									</el-col>
+								</el-row>
+							</el-form>
+						</el-tab-pane>
+						<el-tab-pane label="组织机构"></el-tab-pane>
+						<el-tab-pane label="修改密码">
+							<el-form ref="ruleFormPasswordRef" :model="ruleFormPassword" status-icon label-width="80px">
+								<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
+									<el-input v-model="ruleFormPassword.passwordOld" type="password" autocomplete="off" />
+								</el-form-item>
+								<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
+									<el-input v-model="ruleFormPassword.passwordNew" type="password" autocomplete="off" />
+								</el-form-item>
+								<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
+									<el-input type="password" />
+								</el-form-item>
+								<el-form-item>
+									<el-button icon="ele-Refresh" @click="resetPassword" size="default">重 置</el-button>
+									<el-button icon="ele-SuccessFilled" type="primary" @click="submitPassword" size="default" v-auth="'sysUser:changePwd'">确 定</el-button>
+								</el-form-item>
+							</el-form>
+						</el-tab-pane>
+					</el-tabs>
+				</el-card>
+			</el-col>
+		</el-row>
+
+		<el-dialog title="电子签名" v-model="signDialogVisible" draggable width="600px">
+			<div style="border: 1px dashed gray; width: 100%; height: 250px">
+				<VueSignaturePad ref="signaturePadRef" :options="signOptions" />
+			</div>
+			<div style="margin-top: 10px">
+				<div style="display: inline">画笔粗细:<el-input-number v-model="signOptions.minWidth" :min="0.5" :max="2.5" :step="0.1" size="small" /></div>
+				<div style="display: inline; margin-left: 30px">画笔颜色:<el-color-picker v-model="signOptions.penColor" color-format="hex" size="default"> </el-color-picker></div>
+			</div>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="unDoSign">撤销</el-button>
+					<el-button @click="clearSign">清屏</el-button>
+					<el-button type="primary" @click="saveUploadSign">保存</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { toRefs, reactive, defineComponent, ref, onMounted, watch } from 'vue';
+import { storeToRefs } from 'pinia';
+import { ElMessageBox, UploadInstance } from 'element-plus';
+import { useUserInfo } from '/@/stores/userInfo';
+import { Session } from '/@/utils/storage';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysFileApi, SysUserApi } from '/@/api-services/api';
+import { ChangePwdInput, SysUser } from '/@/api-services/models';
+import { base64ToFile } from '/@/utils/base64Conver';
+
+export default defineComponent({
+	name: 'sysUserCenter',
+	setup() {
+		const stores = useUserInfo();
+		const { userInfos } = storeToRefs(stores);
+		const uploadSignRef = ref<UploadInstance>();
+		const uploadAvatarRef = ref<UploadInstance>();
+		const signaturePadRef = ref();
+		const ruleFormBaseRef = ref();
+		const ruleFormPasswordRef = ref();
+		const state = reactive({
+			loading: false,
+			signDialogVisible: false,
+			ruleFormBase: {} as SysUser,
+			ruleFormPassword: {} as ChangePwdInput,
+			signOptions: {
+				penColor: '#000000',
+				minWidth: 1.0,
+				onBegin: () => {
+					signaturePadRef.value.resizeCanvas();
+				},
+			},
+			signFileList: [] as any,
+		});
+		onMounted(async () => {
+			state.loading = true;
+			var res = await getAPI(SysUserApi).sysUserBaseGet();
+			state.ruleFormBase = res.data.result ?? { account: '' };
+			state.loading = false;
+		});
+		watch(state.signOptions, () => {
+			signaturePadRef.value.signaturePad.penColor = state.signOptions.penColor;
+			signaturePadRef.value.signaturePad.minWidth = state.signOptions.minWidth;
+		});
+		// 打开电子签名页面
+		const openSignDialog = () => {
+			state.signDialogVisible = true;
+		};
+		// 保存并上传电子签名
+		const saveUploadSign = async () => {
+			const { isEmpty, data } = signaturePadRef.value.saveSignature();
+			if (isEmpty) return;
+
+			var res = await getAPI(SysFileApi).sysFileUploadSignaturePostForm(base64ToFile(data, userInfos.value.account + '.png'));
+			userInfos.value.signature = res.data.result?.url + '';
+
+			clearSign();
+			state.signDialogVisible = false;
+		};
+		// 撤销电子签名
+		const unDoSign = () => {
+			signaturePadRef.value.undoSignature();
+			console.log(signaturePadRef.value.options);
+		};
+		// 清空电子签名
+		const clearSign = () => {
+			signaturePadRef.value.clearSignature();
+		};
+		// 上传手写电子签名
+		const uploadSignFile = async (file: any) => {
+			var res = await getAPI(SysFileApi).sysFileUploadSignaturePostForm(file.raw);
+			userInfos.value.signature = res.data.result?.url + '';
+		};
+		// 获得电子签名文件列表
+		const handleChangeSignFile = (_file: any, fileList: []) => {
+			state.signFileList = fileList;
+		};
+		// 上传头像文件
+		const uploadAvatarFile = async (file: any) => {
+			var res = await getAPI(SysFileApi).sysFileUploadAvatarPostForm(file.raw);
+			userInfos.value.avatar = res.data.result?.url + '';
+			uploadAvatarRef.value?.clearFiles();
+		};
+		// 密码提交
+		const submitUserBase = () => {
+			ruleFormBaseRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				ElMessageBox.confirm('确定修改个人基础信息?', '提示', {
+					confirmButtonText: '确定',
+					cancelButtonText: '取消',
+					type: 'warning',
+				}).then(async () => {
+					await getAPI(SysUserApi).sysUserBasePost(state.ruleFormBase);
+				});
+			});
+		};
+		// 密码验证
+		const validatePassword = (_rule: any, value: any, callback: any) => {
+			if (value != state.ruleFormPassword.passwordNew) {
+				callback(new Error('两次密码不一致!'));
+			} else {
+				callback();
+			}
+		};
+		// 密码重置
+		const resetPassword = () => {
+			state.ruleFormPassword.passwordOld = '';
+			state.ruleFormPassword.passwordNew = '';
+		};
+		// 密码提交
+		const submitPassword = () => {
+			ruleFormPasswordRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				await getAPI(SysUserApi).sysUserChangePwdPost(state.ruleFormPassword);
+				// 退出系统
+				ElMessageBox.confirm('密码已修改,是否重新登录系统?', '提示', {
+					confirmButtonText: '确定',
+					cancelButtonText: '取消',
+					type: 'warning',
+				}).then(async () => {
+					// 清除缓存
+					Session.clear();
+					window.location.reload();
+				});
+			});
+		};
+		return {
+			userInfos,
+			uploadSignRef,
+			uploadAvatarRef,
+			signaturePadRef,
+			ruleFormBaseRef,
+			ruleFormPasswordRef,
+			uploadAvatarFile,
+			openSignDialog,
+			saveUploadSign,
+			unDoSign,
+			clearSign,
+			uploadSignFile,
+			handleChangeSignFile,
+			validatePassword,
+			resetPassword,
+			submitPassword,
+			submitUserBase,
+			...toRefs(state),
+		};
+	},
+});
+</script>
+
+<style scoped lang="scss">
+.account-center-avatarHolder {
+	text-align: center;
+	margin-bottom: 24px;
+
+	.username {
+		font-size: 20px;
+		line-height: 28px;
+		font-weight: 500;
+		margin-bottom: 4px;
+	}
+}
+.account-center-org {
+	margin-bottom: 8px;
+	position: relative;
+	p {
+		margin-top: 10px;
+	}
+	span {
+		padding-left: 10px;
+	}
+}
+
+.image-signature {
+	margin-top: 20px;
+	margin-bottom: 10px;
+	width: 100%;
+	height: 150px;
+	text-align: center;
+	vertical-align: middle;
+	border: solid 1px var(--el-border-color);
+}
+</style>