Browse Source

update

pull/1/head
wanggang 1 year ago
parent
commit
d0a7e514b7
  1. 7
      code/AuthServer/AuthServer.Host/Services/Token/LoginModel.cs
  2. 63
      code/AuthServer/AuthServer.Host/Services/Token/TokenService.cs
  3. 25
      code/WebApp/vanilla/.eslintrc.json
  4. 21
      code/WebApp/vanilla/.prettierrc.json
  5. 5
      code/WebApp/vanilla/.vscode/extensions.json
  6. 7
      code/WebApp/vanilla/.vscode/settings.json
  7. 169
      code/WebApp/vanilla/api/site.js
  8. 80
      code/WebApp/vanilla/api/user.js
  9. 27
      code/WebApp/vanilla/app.js
  10. 18
      code/WebApp/vanilla/assets/docs/test.md
  11. 1
      code/WebApp/vanilla/assets/icons/create.svg
  12. 1
      code/WebApp/vanilla/assets/icons/delete.svg
  13. 1
      code/WebApp/vanilla/assets/icons/details.svg
  14. 1
      code/WebApp/vanilla/assets/icons/export.svg
  15. 1
      code/WebApp/vanilla/assets/icons/file.svg
  16. 1
      code/WebApp/vanilla/assets/icons/fold.svg
  17. 1
      code/WebApp/vanilla/assets/icons/folder.svg
  18. 4
      code/WebApp/vanilla/assets/icons/fullscreen-exit.svg
  19. 4
      code/WebApp/vanilla/assets/icons/fullscreen.svg
  20. 1
      code/WebApp/vanilla/assets/icons/home.svg
  21. 1
      code/WebApp/vanilla/assets/icons/import.svg
  22. 1
      code/WebApp/vanilla/assets/icons/index.svg
  23. 1
      code/WebApp/vanilla/assets/icons/lang.svg
  24. 1
      code/WebApp/vanilla/assets/icons/loading.svg
  25. 1
      code/WebApp/vanilla/assets/icons/unfold.svg
  26. 1
      code/WebApp/vanilla/assets/icons/update.svg
  27. 4
      code/WebApp/vanilla/assets/logo.svg
  28. 37
      code/WebApp/vanilla/components/chart/index.js
  29. 93
      code/WebApp/vanilla/components/form/form-input.js
  30. 94
      code/WebApp/vanilla/components/form/form-item.js
  31. 75
      code/WebApp/vanilla/components/form/index.js
  32. 34
      code/WebApp/vanilla/components/icon/index.js
  33. 390
      code/WebApp/vanilla/components/list/index.js
  34. 41
      code/WebApp/vanilla/components/markdown/index.js
  35. 4
      code/WebApp/vanilla/config/settings.js
  36. BIN
      code/WebApp/vanilla/favicon.ico
  37. 72
      code/WebApp/vanilla/index.html
  38. 12
      code/WebApp/vanilla/layouts/footer.js
  39. 168
      code/WebApp/vanilla/layouts/header.js
  40. 57
      code/WebApp/vanilla/layouts/index.js
  41. 36
      code/WebApp/vanilla/layouts/locale.js
  42. 17
      code/WebApp/vanilla/layouts/logo.js
  43. 55
      code/WebApp/vanilla/layouts/menu-item.js
  44. 24
      code/WebApp/vanilla/layouts/menu.js
  45. 171
      code/WebApp/vanilla/layouts/tabs.js
  46. 9349
      code/WebApp/vanilla/lib/@element-plus/icons-vue/index.js
  47. 2568
      code/WebApp/vanilla/lib/@microsoft/signalr/signalr.esm.js
  48. 765
      code/WebApp/vanilla/lib/@vue-office/excel/index.css
  49. 54424
      code/WebApp/vanilla/lib/@vue-office/excel/vue-office-excel.mjs
  50. 160
      code/WebApp/vanilla/lib/@vue/devtools-api/shim.js
  51. 7387
      code/WebApp/vanilla/lib/@vueuse/core/index.mjs
  52. 1790
      code/WebApp/vanilla/lib/@vueuse/shared/index.mjs
  53. 8842
      code/WebApp/vanilla/lib/better-mock/mock.browser.esm.js
  54. 8
      code/WebApp/vanilla/lib/detect-it/detect-it.esm.js
  55. 45
      code/WebApp/vanilla/lib/echarts/echarts.esm.min.js
  56. 1
      code/WebApp/vanilla/lib/element-plus/index.css
  57. 78
      code/WebApp/vanilla/lib/element-plus/index.full.min.mjs
  58. 2
      code/WebApp/vanilla/lib/element-plus/locale/en.min.mjs
  59. 2
      code/WebApp/vanilla/lib/element-plus/locale/zh-cn.min.mjs
  60. 1
      code/WebApp/vanilla/lib/element-plus/theme-chalk/dark/css-vars.css
  61. 881
      code/WebApp/vanilla/lib/github-markdown-css/github-markdown.min.css
  62. 111
      code/WebApp/vanilla/lib/highlightjs/highlight.css
  63. 1174
      code/WebApp/vanilla/lib/highlightjs/highlight.min.js
  64. 2
      code/WebApp/vanilla/lib/jwt-decode/jwt-decode.esm.js
  65. 1
      code/WebApp/vanilla/lib/linq/linq.min.js
  66. 18
      code/WebApp/vanilla/lib/lodash/lodash.esm.js
  67. 2778
      code/WebApp/vanilla/lib/marked/marked.esm.js
  68. 3
      code/WebApp/vanilla/lib/mermaid/mermaid.esm.min.mjs
  69. 74
      code/WebApp/vanilla/lib/nprogress/nprogress.css
  70. 288
      code/WebApp/vanilla/lib/nprogress/nprogress.vite-esm.js
  71. 1987
      code/WebApp/vanilla/lib/pinia/pinia.esm-browser.js
  72. 206
      code/WebApp/vanilla/lib/pubsub-js/pubsub.esm.js
  73. 1837
      code/WebApp/vanilla/lib/qs/shim.js
  74. 314
      code/WebApp/vanilla/lib/resize-detector/index.js
  75. 1
      code/WebApp/vanilla/lib/tailwindcss/tailwind.min.css
  76. 34
      code/WebApp/vanilla/lib/vue-demi/shim.js
  77. 2
      code/WebApp/vanilla/lib/vue-echarts/index.esm.min.js
  78. 6
      code/WebApp/vanilla/lib/vue-i18n/vue-i18n.esm-browser.prod.js
  79. 3613
      code/WebApp/vanilla/lib/vue-router/vue-router.esm-browser.js
  80. 15377
      code/WebApp/vanilla/lib/vue/vue.esm-browser.js
  81. 1
      code/WebApp/vanilla/lib/vue/vue.esm-browser.prod.js
  82. 13
      code/WebApp/vanilla/locale/index.js
  83. 8
      code/WebApp/vanilla/main.css
  84. 20
      code/WebApp/vanilla/main.js
  85. 60
      code/WebApp/vanilla/mixins/style.js
  86. 90
      code/WebApp/vanilla/request/index.js
  87. 314
      code/WebApp/vanilla/resize-detector/index.js
  88. 133
      code/WebApp/vanilla/router/index.js
  89. 42
      code/WebApp/vanilla/signalr/index.js
  90. 41
      code/WebApp/vanilla/store/app.js
  91. 7
      code/WebApp/vanilla/store/index.js
  92. 137
      code/WebApp/vanilla/styles/site.css
  93. 120
      code/WebApp/vanilla/utils/index.js
  94. 3
      code/WebApp/vanilla/views/403.js
  95. 3
      code/WebApp/vanilla/views/404.js
  96. 7
      code/WebApp/vanilla/views/home.js
  97. 32
      code/WebApp/vanilla/views/list.js
  98. 90
      code/WebApp/vanilla/views/login.js
  99. 216
      code/WebApp/vanilla/views/monitor.js
  100. 2
      docs/demo/src/WTA/wwwroot/components/form/form-input.js

7
code/AuthServer/AuthServer.Host/Services/Token/LoginModel.cs

@ -0,0 +1,7 @@
namespace AuthServer.Host.Services.Token;
public class LoginModel
{
public string UserName { get; set;}
public string Password { get; set;}
}

63
code/AuthServer/AuthServer.Host/Services/Token/TokenService.cs

@ -0,0 +1,63 @@
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Web.Pages.Account;
using Volo.Abp.Application.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace AuthServer.Host.Services.Token;
[Route("/api/[controller]/[action]")]
public class TokenService : ApplicationService
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TokenService> _logger;
private readonly IConfiguration _configuration;
public TokenService(IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<TokenService> logger)
{
this._httpContextAccessor = httpContextAccessor;
this._httpClientFactory = httpClientFactory;
this._configuration = configuration;
this._logger = logger;
}
[HttpPost("token")]
[AllowAnonymous]
public async Task<IActionResult> CreateAsync(LoginModel model)
{
var address = _configuration["AuthServer:Authority"];
var clientId = _configuration["AuthServer:ClientId"];
var clientSecret = _configuration["AuthServer:ClientSecret"];
var result = await _httpClientFactory.CreateClient().RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = $"{address.TrimEnd('/')}/connect/token",
GrantType = "password",
ClientId = clientId,
ClientSecret = clientSecret,
UserName = model.UserName,
Password = model.Password
}).ConfigureAwait(false);
return new JsonResult(new
{
result.TokenType,
result.AccessToken,
result.ExpiresIn,
result.RefreshToken,
result.Scope,
result.HttpStatusCode,
result.Error,
result.HttpErrorReason,
result.ErrorDescription,
result.ErrorType,
result.Exception?.Message,
Exception = result.Exception?.ToString()
});
}
}

25
code/WebApp/vanilla/.eslintrc.json

@ -0,0 +1,25 @@
{
"root": true,
"rules": {
"import/extensions": [
2,
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
],
"sort-imports": [
"error",
{
"ignoreCase": false,
"ignoreDeclarationSort": false,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"],
"allowSeparatedGroups": false
}
]
}
}

21
code/WebApp/vanilla/.prettierrc.json

@ -0,0 +1,21 @@
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"ssingleQuote": true,
"squoteProps": "as-needed",
"sjsxSingleQuote": false,
"strailingComma": "all",
"sbracketSpacing": true,
"sjsxBracketSameLine": false,
"sarrowParens": "always",
"srangeStart": 0,
"srangeEnd": "Infinity",
"srequirePragma": false,
"sinsertPragma": false,
"sproseWrap": "preserve",
"shtmlWhitespaceSensitivity": "css",
"svueIndentScriptAndStyle": false,
"endOfLine": "lf"
}

5
code/WebApp/vanilla/.vscode/extensions.json

@ -0,0 +1,5 @@
{
"recommendations": [
"Vue.volar",
]
}

7
code/WebApp/vanilla/.vscode/settings.json

@ -0,0 +1,7 @@
{
"search.exclude": {
"lib": true
},
"editor.formatOnSave": true,
"liveServer.settings.port": 9527
}

169
code/WebApp/vanilla/api/site.js

@ -0,0 +1,169 @@
import { get, post } from "../request/index.js";
async function getLocalizationAsync() {
// const url = "abp/application-configuration";
// const data = (await get(url, null, null, true, true)).data;
return {
options: [
{
value: "zh",
label: "中文",
},
{
value: "en",
label: "English",
},
],
locale: "zh",
messages: {
zh: {
application: "北京北汽模塑-SAS结算分析系统",
copyright: "长春市闻荫科技有限公司 ©2023",
test: "测试",
compareAttribute: "{0}”和{1}不匹配",
fileExtensionsAttribute: "{0}只接受一下扩展名的文件: {1}",
maxLengthAttribute: "{0}的最大长度为 {1}",
minLengthAttribute: "{0}的最小长度为 {1}",
rangeAttribute: "{0}必需在 {1} 和 {2} 之间",
regularExpressionAttribute: "{0}”必需匹配{1}",
requiredAttribute: "{0}不能为空",
stringLengthAttribute: "{0}的最大长度为 {1}",
stringLengthAttributeIncludingMinimum: "{0}的长度在 {2} 和 {1} 之间",
dataTypeAttribute_CreditCard: "{0}不是有效的信用卡号码",
dataTypeAttribute_EmailAddress: "{0}不是有效的 Email 地址",
dataTypeAttribute_PhoneNumber: "{0}不是有效的手机号码",
dataTypeAttribute_Url: "{0}不是有效的 Url",
dataTypeAttribute_Upload: "{0}的扩展名必须为:{1}",
dataTypeAttribute_DateTime: "{0}不是有效的日期格式",
customValidationAttribute: "{0}验证失败",
validationAttribute: "{0}验证失败",
true: "是",
false: "否",
select: "选择",
confirm: "确定",
reset: "重置",
rowIndex: "行号",
name: "名称",
number: "编号",
value: "值",
order: "序号",
isDisabled: "禁用",
properties: "属性",
parentId: "上级",
lockoutEnabled: "启用锁定",
lockoutEnd: "锁定截止",
accessFailedCount: "登录失败次数",
isSystem: "系统内置",
isReadonly: "只读",
audit: "审计",
selectAll: "全选",
selectInverse: "反选",
filter: "过滤",
createdOn: "创建时间",
createdBy: "创建人",
updatedOn: "修改时间",
updatedBy: "修改人",
deletedOn: "删除时间",
deletedBy: "删除人",
concurrencyStamp: "并发戳",
operations: "操作",
disabled: "已禁用",
displayOrder: "序号",
isDeleted: "已删除",
path: "路径",
method: "方法",
isTop: "顶部",
htmlClass: "class",
internalPath: "内部路径",
component: "组件",
serverTime: "服务器时间",
osArchitecture: "系统架构",
osDescription: "操作系统",
processArchitecture: "进程架构",
tip: "提示",
cancel: "操作取消",
index: "查询",
details: "详情",
create: "新建",
update: "更新",
import: "导入",
export: "导出",
remove: "移除",
restore: "还原",
delete: "删除",
authenticate: "验证",
loginModel: "登录",
login: "登录",
logout: "注销",
confirmLogout: "确认退出?",
register: "注册",
userName: "用户名",
password: "密码",
email: "邮箱",
emailConfirmed: "邮箱已确认",
rememberMe: "记住我",
resetPassword: "重置密码",
userCenter: "用户中心",
avatar: "头像",
tenant: "租户",
connectionString: "连接字符串",
tenantId: "租户",
tenants: "租户管理",
identity: "认证中心",
systemManagement: "基础数据",
roleId: "角色",
permissionId: "权限",
userRoles: "用户角色",
enableColumnLimit: "列权限",
enableRowLimit: "行权限",
rolePermissions: "角色权限",
departmentId: "部门",
cron: "定时器",
icon: "图标",
type: "类型",
isExternal: "外链",
isHidden: "隐藏",
redirect: "跳转",
columns: "列",
identityModule: "系统管理",
user: "用户",
role: "角色",
permission: "权限",
department: "部门",
post: "岗位",
dict: "字典",
systemMonitor: "系统监控",
monitor: "服务监控",
monitorModule: "系统监控",
jobItem: "定时任务",
captcha: "验证码",
captchaExpired: "验证码已过期",
captchaError: "验证码错误",
dictionaryItem: "数据字典",
userLogin: "登录历史",
connectionId: "连接Id",
isOnline: "在线",
heartbeat: "心跳",
userAgent: "用户代理",
},
en: {
application: "WTA Framework",
copyright: "all rights reserved © copyright",
test: "test",
login: "Login",
logout: "Logout",
confirmLogout: "Confirm Logout?",
register: "Register",
tip: "Tip",
cancel: "Cancel",
userName: "User Name",
password: "Password",
rememberMe: "Remember Me",
resetPassword: "Reset Password",
userCenter: "User Center",
},
},
};
}
export { getLocalizationAsync };

80
code/WebApp/vanilla/api/user.js

@ -0,0 +1,80 @@
import router from "../router/index.js";
import { get, post } from "../request/index.js";
import jwt_decode from "../lib/jwt-decode/jwt-decode.esm.js";
import qs from "../lib/qs/shim.js";
import { useAppStore } from "../store/index.js";
import { refreshRouter } from "../router/index.js";
import Enumerable from "linq";
import { connection } from "../signalr/index.js";
const isLogin = async () => {
const appStore = useAppStore();
// 有 token,判断是否过期,失败设置 token 为 null
if (appStore.token) {
const exp = new Date(jwt_decode(appStore.token).exp * 1000);
if (exp > new Date()) {
return true;
} else {
appStore.token = null;
}
}
return false;
};
const login = async (data) => {
const url = "connect-token";
const appStore = useAppStore();
const result = await post(url, data, { headers: { "Content-Type": "application/x-www-form-urlencoded" } }, true);
if (!result.errors) {
appStore.token = result.data.access_token;
setRefreshToken(result.data.refresh_token);
appStore.user = await getUser();
await refreshRouter();
const redirect = router.currentRoute.value.query?.redirect ?? "/";
router.push(redirect);
}
return result;
};
const logout = () => {
const appStore = useAppStore();
appStore.token = null;
removeRefreshToken();
router.push({ path: "/login", query: { redirect: router.currentRoute.value.fullPath } });
};
const getUser = async () => {
const result = await get("abp/application-configuration");
const data = result.data;
const user = {};
user.name = data.currentUser.userName;
user.email = data.currentUser.email;
user.roles = data.currentUser.roles;
const menus = (await get("base/role-menus")).data;
user.permissions = menus.items;
return user;
};
const hasPermission = (to) => {
const appStore = useAppStore();
const permission = to.meta?.permission;
if (permission) {
const hasPermission = Enumerable.from(appStore.user.permissions).any((o) => o.number === permission);
return hasPermission;
} else {
return true;
}
};
const refreshTokenKey = "refresh_token";
const getRefreshToken = () => localStorage.getItem(refreshTokenKey);
const setRefreshToken = (refreshToken) => localStorage.setItem(refreshTokenKey, refreshToken);
const removeRefreshToken = () => {
localStorage.removeItem(refreshTokenKey);
connection.stop();
};
export { isLogin, login, logout, getUser, hasPermission };

27
code/WebApp/vanilla/app.js

@ -0,0 +1,27 @@
import html from "html";
import { ElConfigProvider } from "element-plus";
import zh from "./lib/element-plus/locale/zh-cn.min.mjs";
import en from "./lib/element-plus/locale/en.min.mjs";
import { Suspense, reactive, onMounted } from "vue";
export default {
components: { ElConfigProvider, Suspense },
template: html`<suspense>
<el-config-provider :locale="localeMap.get($i18n.locale)">
<router-view></router-view>
</el-config-provider>
<template #fallback> Loading... </template>
</suspense>`,
setup() {
const localeMap = reactive(
new Map([
["zh", zh],
["en", en],
])
);
onMounted(() => document.querySelector("#loading.loading").classList.remove("loading"));
return {
localeMap,
};
},
};

18
code/WebApp/vanilla/assets/docs/test.md

@ -0,0 +1,18 @@
Markdown
========
## flowchart
```mermaid
flowchart LR
Start --> Stop
```
## highlight
```javascript
function (){
let a=1;
alert(a);
}
```

1
code/WebApp/vanilla/assets/icons/create.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M480 480V128a32 32 0 0 1 64 0v352h352a32 32 0 1 1 0 64H544v352a32 32 0 1 1-64 0V544H128a32 32 0 0 1 0-64h352z"></path></svg>

After

Width:  |  Height:  |  Size: 237 B

1
code/WebApp/vanilla/assets/icons/delete.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32zm192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32z"></path></svg>

After

Width:  |  Height:  |  Size: 471 B

1
code/WebApp/vanilla/assets/icons/details.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M512 160c320 0 512 352 512 352S832 864 512 864 0 512 0 512s192-352 512-352zm0 64c-225.28 0-384.128 208.064-436.8 288 52.608 79.872 211.456 288 436.8 288 225.28 0 384.128-208.064 436.8-288-52.608-79.872-211.456-288-436.8-288zm0 64a224 224 0 1 1 0 448 224 224 0 0 1 0-448zm0 64a160.192 160.192 0 0 0-160 160c0 88.192 71.744 160 160 160s160-71.808 160-160-71.744-160-160-160z"></path></svg>

After

Width:  |  Height:  |  Size: 500 B

1
code/WebApp/vanilla/assets/icons/export.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64zm384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64v450.304z"></path></svg>

After

Width:  |  Height:  |  Size: 283 B

1
code/WebApp/vanilla/assets/icons/file.svg

@ -0,0 +1 @@
<svg t="1661515418615" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3168" width="128" height="128"><path d="M809.021787 916.341025l-590.652338 0c-48.40443 0-87.799672-39.395242-87.799672-87.799672l0-654.506644c0-48.40443 39.395242-87.799672 87.799672-87.799672l590.652338 0c48.40443 0 87.799672 39.395242 87.799672 87.799672l0 654.506644C896.821459 876.945783 857.426217 916.341025 809.021787 916.341025zM218.36945 134.125767c-22.01234 0-39.908942 17.896602-39.908942 39.908942l0 654.506644c0 22.01234 17.896602 39.908942 39.908942 39.908942l590.652338 0c22.01234 0 39.908942-17.896602 39.908942-39.908942l0-654.506644c0-22.01234-17.896602-39.908942-39.908942-39.908942L218.36945 134.125767z" p-id="3169"></path><path d="M322.132698 708.814528l383.125841 0 0 31.927153-383.125841 0 0-31.927153Z" p-id="3170"></path><path d="M322.132698 389.542994l383.125841 0 0 31.927153-383.125841 0 0-31.927153Z" p-id="3171"></path><path d="M322.132698 245.870804l383.125841 0 0 31.927153-383.125841 0 0-31.927153Z" p-id="3172"></path><path d="M322.132698 549.178761l383.125841 0 0 31.927153-383.125841 0 0-31.927153Z" p-id="3173"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

1
code/WebApp/vanilla/assets/icons/fold.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024"><path fill="currentColor" d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/></svg>

After

Width:  |  Height:  |  Size: 595 B

1
code/WebApp/vanilla/assets/icons/folder.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa=""><path fill="currentColor" d="M128 192v640h768V320H485.76L357.504 192H128zm-32-64h287.872l128.384 128H928a32 32 0 0 1 32 32v576a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32z"></path></svg>

After

Width:  |  Height:  |  Size: 287 B

4
code/WebApp/vanilla/assets/icons/fullscreen-exit.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="M391 240.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L200 146.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L401 410c5.1.6 9.5-3.7 8.9-8.9L391 240.9zm10.1 373.2L240.8 633c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L146.3 824a8.03 8.03 0 0 0 0 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 0 0 391 783l18.9-160.1c.6-5.1-3.7-9.4-8.8-8.8zm221.8-204.2L783.2 391c6.6-.8 9.4-8.9 4.7-13.6L744 333.6L877.7 200c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.3a8.03 8.03 0 0 0-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L614.1 401c-.6 5.2 3.7 9.5 8.8 8.9zM744 690.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L623 614c-5.1-.6-9.5 3.7-8.9 8.9L633 783.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L824 877.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L744 690.4z" />
</svg>

After

Width:  |  Height:  |  Size: 942 B

4
code/WebApp/vanilla/assets/icons/fullscreen.svg

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="m290 236.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L169 160c-5.1-.6-9.5 3.7-8.9 8.9L179 329.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L370 423.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L290 236.4zm352.7 187.3c3.1 3.1 8.2 3.1 11.3 0l133.7-133.6l43.7 43.7a8.01 8.01 0 0 0 13.6-4.7L863.9 169c.6-5.1-3.7-9.5-8.9-8.9L694.8 179c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L600.3 370a8.03 8.03 0 0 0 0 11.3l42.4 42.4zM845 694.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L654 600.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9L845 694.9zm-463.7-94.6a8.03 8.03 0 0 0-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L160.1 855c-.6 5.1 3.7 9.5 8.9 8.9L329.2 845c6.6-.8 9.4-8.9 4.7-13.6L290 787.6L423.7 654c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.4z" />
</svg>

After

Width:  |  Height:  |  Size: 945 B

1
code/WebApp/vanilla/assets/icons/home.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><path fill="currentColor" d="m33.71 17.29l-15-15a1 1 0 0 0-1.41 0l-15 15a1 1 0 0 0 1.41 1.41L18 4.41l14.29 14.3a1 1 0 0 0 1.41-1.41Z" class="clr-i-outline clr-i-outline-path-1"/><path fill="currentColor" d="M28 32h-5V22H13v10H8V18l-2 2v12a2 2 0 0 0 2 2h7V24h6v10h7a2 2 0 0 0 2-2V19.76l-2-2Z" class="clr-i-outline clr-i-outline-path-2"/><path fill="none" d="M0 0h36v36H0z"/></svg>

After

Width:  |  Height:  |  Size: 463 B

1
code/WebApp/vanilla/assets/icons/import.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64zm384-578.304V704h-64V247.296L237.248 490.048 192 444.8 508.8 128l316.8 316.8-45.312 45.248L544 253.696z"></path></svg>

After

Width:  |  Height:  |  Size: 282 B

1
code/WebApp/vanilla/assets/icons/index.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704z"></path></svg>

After

Width:  |  Height:  |  Size: 279 B

1
code/WebApp/vanilla/assets/icons/lang.svg

@ -0,0 +1 @@
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" width="1.2em" height="1.2em" data-v-12008bb2=""><path fill="currentColor" d="m18.5 10l4.4 11h-2.155l-1.201-3h-4.09l-1.199 3h-2.154L16.5 10h2zM10 2v2h6v2h-1.968a18.222 18.222 0 0 1-3.62 6.301a14.864 14.864 0 0 0 2.336 1.707l-.751 1.878A17.015 17.015 0 0 1 9 13.725a16.676 16.676 0 0 1-6.201 3.548l-.536-1.929a14.7 14.7 0 0 0 5.327-3.042A18.078 18.078 0 0 1 4.767 8h2.24A16.032 16.032 0 0 0 9 10.877a16.165 16.165 0 0 0 2.91-4.876L2 6V4h6V2h2zm7.5 10.885L16.253 16h2.492L17.5 12.885z"></path></svg>

After

Width:  |  Height:  |  Size: 558 B

1
code/WebApp/vanilla/assets/icons/loading.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z"></path></svg>

After

Width:  |  Height:  |  Size: 856 B

1
code/WebApp/vanilla/assets/icons/unfold.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024"><path fill="currentColor" d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"/></svg>

After

Width:  |  Height:  |  Size: 594 B

1
code/WebApp/vanilla/assets/icons/update.svg

@ -0,0 +1 @@
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728=""><path fill="currentColor" d="M832 512a32 32 0 1 1 64 0v352a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h352a32 32 0 0 1 0 64H192v640h640V512z"></path><path fill="currentColor" d="m469.952 554.24 52.8-7.552L847.104 222.4a32 32 0 1 0-45.248-45.248L477.44 501.44l-7.552 52.8zm422.4-422.4a96 96 0 0 1 0 135.808l-331.84 331.84a32 32 0 0 1-18.112 9.088L436.8 623.68a32 32 0 0 1-36.224-36.224l15.104-105.6a32 32 0 0 1 9.024-18.112l331.904-331.84a96 96 0 0 1 135.744 0z"></path></svg>

After

Width:  |  Height:  |  Size: 577 B

4
code/WebApp/vanilla/assets/logo.svg

@ -0,0 +1,4 @@
<svg t="1660918198222" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="12973" width="128" height="128">
<path d="M512 0C230.4 0 0 230.4 0 512s230.4 512 512 512 512-230.4 512-512-230.4-512-512-512z m0 960c-38.4 0-89.6-96-115.2-262.4 38.4 6.4 76.8 6.4 115.2 6.4s76.8 0 115.2-6.4C601.6 864 550.4 960 512 960z m0-320c-44.8 0-83.2 0-121.6-6.4C384 595.2 384 556.8 384 512s0-83.2 6.4-121.6C428.8 384 467.2 384 512 384s83.2 0 121.6 6.4c6.4 38.4 6.4 76.8 6.4 121.6s0 83.2-6.4 121.6c-38.4 6.4-76.8 6.4-121.6 6.4z m-185.6-12.8C160 601.6 64 550.4 64 512s96-89.6 262.4-115.2C320 435.2 320 473.6 320 512s0 76.8 6.4 115.2zM512 64c38.4 0 89.6 96 115.2 262.4C588.8 320 550.4 320 512 320s-76.8 0-115.2 6.4C422.4 160 473.6 64 512 64z m185.6 332.8C864 422.4 960 473.6 960 512s-96 89.6-262.4 115.2c6.4-38.4 6.4-76.8 6.4-115.2s0-76.8-6.4-115.2z m249.6 6.4c-64-38.4-153.6-64-256-76.8-12.8-102.4-38.4-192-76.8-256 166.4 44.8 294.4 172.8 332.8 332.8zM403.2 76.8c-38.4 64-64 153.6-76.8 256-102.4 12.8-192 38.4-256 76.8 44.8-166.4 172.8-294.4 332.8-332.8zM76.8 620.8c64 38.4 153.6 64 256 76.8 12.8 102.4 38.4 192 76.8 256-166.4-44.8-294.4-172.8-332.8-332.8z m544 326.4c38.4-64 64-153.6 76.8-256 102.4-12.8 192-38.4 256-76.8-44.8 166.4-172.8 294.4-332.8 332.8z" p-id="12974" fill="#1296db"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

37
code/WebApp/vanilla/components/chart/index.js

@ -0,0 +1,37 @@
import { nextTick, ref } from 'vue';
import VCharts from 'vue-echarts';
const template = `<VCharts
v-if="renderChart"
:option="options"
:autoresize="autoresize"
:style="{ width, height }"
/>`;
export default {
template,
components: { VCharts },
props: {
options: {
default: {},
},
autoresize: {
default: true,
},
width: {
default: '100%',
},
height: {
default: '100%',
},
},
setup() {
const renderChart = ref(false);
nextTick(() => {
renderChart.value = true;
});
return {
renderChart,
};
},
};

93
code/WebApp/vanilla/components/form/form-input.js

@ -0,0 +1,93 @@
import html from "html";
import { ref, reactive, watch } from "vue";
import { dayjs } from "element-plus";
export default {
template: html`
<template v-if="getDisabled()">
<template v-if="model[prop]!==null">
<el-switch disabled v-model="model[prop]" type="checked" v-if="schema.type==='boolean'" />
<template v-else-if="schema.format==='datetime'">{{dayjs(model[prop]).format('YYYY-MM-DD HH:mm:ss')}}</template>
<template v-else-if="schema.format==='date'">{{dayjs(model[prop]).format('YYYY-MM-DD')}}</template>
<template v-else>{{model[prop]}}</template>
</template>
</template>
<template v-else>
<template v-if="getInput(schema)==='select'">
<el-select
v-model="model[prop]"
:placeholder="$t('select')"
:multiple="!!schema.multiple"
clearable
style="width:100%"
>
<el-option v-for="item in schema.options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<el-input
:disabled="getDisabled()"
:placeholder="schema.title"
v-model="model[prop]"
type="number"
v-else-if="schema.type==='number'"
/>
<el-input-number
:disabled="getDisabled()"
:placeholder="schema.title"
v-model="model[prop]"
:precision="0"
v-else-if="schema.type==='integer'"
/>
<template v-else-if="schema.type==='boolean'">
<el-select :disabled="getDisabled()" v-model="model[prop]" :placeholder="schema.title" v-if="schema.nullable">
<el-option prop="select" :value="null" :label="$t('select')" />
<el-option prop="true" :value="true" :label="$t('true')" />
<el-option prop="false" :value="false" :label="$t('false')" />
</el-select>
<el-switch v-model="model[prop]" type="checked" v-else />
</template>
<template v-else>
<el-input
:disabled="getDisabled()"
:placeholder="schema.title"
v-model="model[prop]"
type="password"
show-password
v-if="schema.format==='password'"
/>
<el-input :disabled="getDisabled()" :placeholder="schema.title" v-model="model[prop]" type="text" v-else />
</template>
</template>
`,
props: ["modelValue", "schema", "prop", "isReadOnly"],
emit: ["update:modelValue"],
async setup(props, context) {
const model = reactive(props.modelValue);
watch(model, (value) => {
context.emit("update:modelValue", value);
});
/*start*/
const getDisabled = () => {
if (props.isReadOnly && props.isReadOnly === true) {
return true;
}
if (props.schema.displayOnly) {
return true;
}
if (props.mode === "update" && props.schema.addOnly) {
return true;
}
return false;
};
const getInput = (schema) => {
return schema.input ?? schema.type;
};
/*end*/
return {
model,
getDisabled,
getInput,
dayjs,
};
},
};

94
code/WebApp/vanilla/components/form/form-item.js

@ -0,0 +1,94 @@
import html from "html";
import { ref, reactive, watch } from "vue";
import AppFormInput from "./form-input.js";
export default {
name: "formItem",
components: { AppFormInput },
template: html`
<template v-if="showItem()">
<template v-if="schema.type==='object'"></template>
<template v-if="schema.type!=='array'||(schema.items.type!=='object'&&schema.items.type!=='array')"> </template>
<el-form-item
:title="getProp(prop)"
:label="schema.title"
:prop="getProp(prop)"
:rules="getRules(parentSchema,schema,model)"
:error="mode==='query'?null:getError(prop)"
>
<app-form-input :schema="schema" :prop="prop" v-model="model" :isReadOnly="mode==='details'" />
</el-form-item>
</template>
</template>
`,
props: ["modelValue", "mode", "parentSchema", "schema", "prop", "errors"],
emit: ["update:modelValue"],
async setup(props, context) {
const model = reactive(props.modelValue);
watch(model, (value) => {
context.emit("update:modelValue", value);
});
/*start*/
const showItem = () => {
if (props.schema.hidden) {
return false;
}
if (props.schema.readOnly && (props.mode === "query" || props.mode === "create" || props.mode === "update")) {
return false;
}
return true;
};
//
const getProp = (prop) => {
return prop;
};
//
const getError = (prop) => {
return props.errors[prop];
};
//
const getRules = (parentSchema, property, data) => {
if (props.mode === "query" || props.mode === "details" || !property.rules) {
return null;
}
const rules = [...(Array.isArray(property.rules) ? property.rules : [property.rules])].map((o) =>
JSON.parse(JSON.stringify(o))
);
Object.values(rules).forEach((rule) => {
rule.data = data;
rule.schema = parentSchema;
rule.title = rule.title ?? property.title;
rule.type = property.type;
if (rule.validator) {
rule.validator = validators[rule.validator];
}
if (!rule.message) {
if (rule.required) {
rule.message = format(schema.messages.required, property.title);
} else if (rule.pattern) {
rule.message = format(schema.messages.pattern, property.title);
} else if (property.type === "string" || property.type === "number" || property.type === "array") {
if (rule.len) {
rule.message = format(schema.messages[property.type].len, property.title, rule.len);
} else if (rule.min) {
rule.message = format(schema.messages[property.type].min, property.title, rule.min);
} else if (rule.max) {
rule.message = format(schema.messages[property.type].max, property.title, rule.max);
} else if (rule.range) {
rule.message = format(schema.messages[property.type].range, property.title, rule.range);
}
}
}
});
return rules;
};
/*end*/
return {
model,
showItem,
getProp,
getError,
getRules,
};
},
};

75
code/WebApp/vanilla/components/form/index.js

@ -0,0 +1,75 @@
import html from "html";
import { ref, reactive, watch } from "vue";
import AppFormItem from "./form-item.js";
export default {
components: { AppFormItem },
name: "AppForm",
template: html`<el-form ref="formRef" :model="model" label-width="auto">
<template v-for="(value, prop) in schema.properties">
<app-form-item
:parentSchema="schema"
:schema="value"
v-model="model"
:prop="prop"
:mode="mode"
:errors="errors"
/>
</template>
<slot name="items"></slot>
<el-form-item v-if="!hideButton">
<template #label></template>
<el-button type="primary" @click="submit" :disabled="loading"><slot>$t('confirm')</slot></el-button>
</el-form-item>
</el-form>`,
props: ["modelValue", "schema", "action", "hideButton", "isQueryForm", "mode"],
emits: ["update:modelValue", "submit"],
setup(props, context) {
// init
const model = reactive(props.modelValue);
watch(model, (value) => {
context.emit("update:modelValue", value);
});
// ref
const formRef = ref(null);
const loading = ref(false);
//
const errors = ref({});
// reset
const reset = () => {
formRef.value.resetFields();
};
// validate
const validate = async () => {
return formRef.value.validate();
};
// submit
const submit = async () => {
try {
//const valid = await validate();
//if (valid) {
loading.value = true;
context.emit("submit", (serverErrors) => {
if (serverErrors) {
errors.value = serverErrors;
}
});
//}
} catch (error) {
console.error(error);
} finally {
loading.value = false;
}
};
// expose
context.expose({ validate, reset });
return {
model,
formRef,
loading,
errors,
reset,
submit,
};
},
};

34
code/WebApp/vanilla/components/icon/index.js

@ -0,0 +1,34 @@
import { onMounted, ref } from "vue";
const template = `<component v-if="name.indexOf('ep-')===0" :is="name" /> <v-else g v-html="svg" /> `;
export default {
props: {
name: {
default: "file",
},
},
template,
setup(props) {
const svg = ref("");
onMounted(async () => {
if (props.name.indexOf("ep-") !== 0) {
try {
const response = await fetch(`./assets/icons/${props.name}.svg`);
if (response.ok && response.status === 200) {
svg.value = await response.text();
}
} catch (error) {
console.error(error);
}
if (!svg.value) {
const response = await fetch("./assets/icons/file.svg");
svg.value = await response.text();
}
}
});
return {
svg,
};
},
};

390
code/WebApp/vanilla/components/list/index.js

@ -0,0 +1,390 @@
import html, { getProp } from "html";
import AppForm from "../form/index.js";
import { get, post } from "../../request/index.js";
import { ref, reactive } from "vue";
import { useRoute } from "vue-router";
import { useI18n } from "vue-i18n";
import SvgIcon from "../../components/icon/index.js";
import { schemaToModel } from "../../utils/index.js";
import qs from "../../lib/qs/shim.js";
import AppFormInput from "../form/form-input.js";
import VueOfficeExcel from "@vue-office/excel";
import { camelCase, capitalize } from "lodash";
export default {
components: { AppForm, SvgIcon, AppFormInput, VueOfficeExcel },
template: html`
<el-row>
<el-col>
<app-form
inline
mode="query"
label-position="left"
:schema="queryFromSchema"
v-model="data.query"
@submit="load"
:hideButton="true"
:isQueryForm="true"
/>
</el-col>
</el-row>
<el-row style="padding-bottom:20px;">
<el-col>
<template v-for="item in $route.meta.buttons">
<el-button
:class="item.meta.htmlClass??'el-button--primary'"
v-if="item.meta.isTop"
@click="click(item,selectedRows)"
>
<el-icon v-if="item.meta.icon"><svg-icon :name="item.meta.icon" /></el-icon>
<span>{{item.meta.title}}</span>
</el-button>
</template>
<slot name="tableButtons" :rows="selectedRows"></slot>
</el-col>
</el-row>
<el-row>
<el-col>
<el-scrollbar>
<el-table
ref="tableRef"
v-loading="tableLoading"
row-key="id"
table-layout="auto"
border
fit
:data="data.items"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
:header-cell-class-name="getClass"
v-if="data.items"
>
<el-table-column fixed="left" type="selection" />
<el-table-column type="index" :label="$t('rowIndex')">
<template #default="scope"> {{ (data.pageIndex - 1) * data.pageSize + scope.$index + 1 }} </template>
</el-table-column>
<template v-for="(item,key) in tableSchema.items.properties">
<template v-if="key==='properties'">
<el-table-column :label="subKey" v-for="(subItem,subKey) in item.properties">
<template #default="scope">{{ scope.row[key][subKey] }} </template>
</el-table-column>
</template>
<template v-else-if="item.navigation">
<el-table-column :prop="key" :label="item.title">
<template #default="scope">{{getProp(scope.row,item.navigation)}}</template>
</el-table-column>
</template>
<template v-else>
<template v-if="showColumn(item,key)">
<el-table-column :prop="key" sortable="custom" :sort-orders="['descending', 'ascending', null]">
<template #header="scope">{{item.title}}</template>
<template #default="scope">
<app-form-input :isReadOnly="true" :schema="item" :prop="key" v-model="scope.row" />
</template>
</el-table-column>
</template>
</template>
</template>
<slot name="columns"></slot>
<el-table-column fixed="right">
<template #header>
<el-button @click="filterDrawer = true">
{{$t('operations')}}
<el-icon class="el-icon--right"><ep-filter /></el-icon>
</el-button>
</template>
<template #default="scope">
<div class="flex">
<template v-for="item in $route.meta.buttons">
<el-button
:class="item.meta.htmlClass??'el-button--primary'"
v-if="!item.meta.isTop"
@click="click(item,[scope.row])"
>
<el-icon v-if="item.meta.icon"><svg-icon :name="item.meta.icon" /></el-icon>
<span>{{item.meta.title}}</span>
</el-button>
</template>
<slot name="rowButtons" :rows="[scope.row]"></slot>
</div>
</template>
</el-table-column>
</el-table>
</el-scrollbar>
</el-col>
</el-row>
<el-row>
<el-col>
<el-pagination
v-if="data.items&&data.pageSize<data.totalCount"
v-model:currentPage="data.pageIndex"
v-model:page-size="data.pageSize"
:total="data.totalCount"
:page-sizes="[20, 50, 100]"
class="justify-end"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
@size-change="onPageSizeChange"
@current-change="onPageIndexChange"
style="margin-top:20px"
/>
</el-col>
</el-row>
<el-drawer v-model="filterDrawer" destroy-on-close @close="tableRef.doLayout()">
<template #header> <span class="el-dialog__title"> {{$t('filter')}} </span> </template>
<el-scrollbar>
<el-row>
<el-col style="max-height:calc(100vh - 180px);">
<el-form inline>
<el-form-item>
<el-button type="primary" @click="columns.forEach(o=>o.checked=true)"> {{$t('selectAll')}} </el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="columns.forEach(o=>o.checked=!o.checked)">
{{$t('selectInverse')}}
</el-button>
</el-form-item>
<el-form-item v-for="item in columns">
<el-checkbox v-model="item.checked" :label="item.title" size="large" />
</el-form-item>
</el-form>
</el-col>
</el-row>
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="filterDrawer=false"> {{$t('confirm')}} </el-button>
</span>
</template>
</el-drawer>
<el-dialog v-model="dialogVisible" align-center destroy-on-close width="700">
<template #header> <span class="el-dialog__title"> {{editFormTitle}} </span> </template>
<el-row>
<el-col style="max-height:calc(100vh - 180px );">
<el-scrollbar>
<app-form
v-loading="editFormloading"
:disabled="editFormMode==='details'"
:mode="editFormMode"
ref="editFormRef"
inline
label-position="left"
:hideButton="true"
:schema="editFormSchema"
v-model="editFormModel"
v-if="editFormMode!=='import'"
/>
</el-scrollbar>
</el-col>
</el-row>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submit"> {{$t('confirm')}} </el-button>
</span>
</template>
</el-dialog>
`,
props: ["modelValue"],
emits: ["command"],
async setup(props, context) {
const tableRef = ref(null);
const columns = ref([]);
const filterDrawer = ref(false);
const tableLoading = ref(false);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const route = useRoute();
const { t } = useI18n();
const baseUrl = `${route.meta.path}`.substring(1);
const indexUrl = `${baseUrl}/index`;
const vm = (await get(indexUrl)).data;
const schema = vm.schema;
const data = reactive(vm.model ?? schemaToModel(schema));
const sortColumns = ref(new Map());
const getSortModel = (model) => {
const orderBy = model.orderBy
.split(",")
.map((o) => o.trim())
.filter((o) => o)
.map((o) => ({
prop: camelCase(o.split(" ")[0]),
order: (o.split(" ").filter((o) => o)[1] ?? "asc") + "ending",
}))
.forEach((o) => sortColumns.value.set(o.prop, o.order));
return orderBy;
};
const sortModel = reactive(getSortModel(data));
const getClass = ({ row, column }) => {
if (column.property) {
column.order = sortColumns.value.get(column.property);
}
};
const sortChange = ({ column, prop, order }) => {
if (order === null) {
sortColumns.value.delete(prop);
} else {
sortColumns.value.set(prop, order);
}
data.orderBy = Array.from(sortColumns.value)
.map((o) => capitalize(o[0]) + (o[1] === "ascending" ? "" : ` DESC`))
.join(",");
load(indexUrl);
};
const getColumns = (schema) => {
Object.keys(schema.properties).forEach((propertyName) => {
const property = schema.properties[propertyName];
if (property.type !== "object" && property.type !== "array" && !property.hidden) {
columns.value.push({ name: propertyName, title: property.title, checked: true });
}
});
};
const showColumn = (item, prop) => {
return (
item.type !== "object" &&
item.type !== "array" &&
!item.hidden &&
columns.value.findIndex((o) => o.name === prop && o.checked) >= 0
);
};
getColumns(schema.properties.query);
const queryFromSchema = schema.properties.query;
const tableSchema = schema.properties.items;
const editFormRef = ref(null);
const editFormloading = ref(false);
const editFormMode = ref(null);
const editFormTitle = ref("");
const editFormSchema = reactive({});
const editFormModel = reactive({});
const exportModel = reactive({
includeAll: false,
includeDeleted: false,
});
const handleSelectionChange = (rows) => (selectedRows.value = rows);
const load = async (url) => {
tableLoading.value = true;
try {
const postData = JSON.parse(JSON.stringify(data));
delete postData["Id"];
delete postData["items"];
Object.assign(data, (await post(url, postData)).data);
} catch (error) {
console.log(error);
} finally {
tableLoading.value = false;
}
};
const onPageIndexChange = () => load(indexUrl);
const onPageSizeChange = () => load(indexUrl);
const click = async (item, rows) => {
editFormMode.value = item.path;
context.emit("command", item, rows);
if (item.path === "index") {
//list
await load(indexUrl);
} else if (item.path === "details") {
//details
const detailsUrl = `${baseUrl}/${item.path}?${qs.stringify({ id: rows[0].id })}`;
Object.assign(editFormSchema, schema.properties.items.items);
Object.assign(editFormModel, (await post(detailsUrl)).data);
editFormTitle.value = `${t("details")}${schema.title}`;
dialogVisible.value = true;
} else if (item.path === "create") {
//create
const url = `${baseUrl}/${item.path}`;
const vm = (await get(url)).data;
Object.assign(editFormSchema, vm.schema);
Object.assign(editFormModel, vm.model);
editFormTitle.value = `${t("create")}${schema.title}`;
dialogVisible.value = true;
} else if (item.path === "update") {
//update
const url = `${baseUrl}/${item.path}`;
const vm = (await get(url, { id: rows[0].id })).data;
Object.assign(editFormSchema, vm.schema);
Object.assign(editFormModel, vm.model);
editFormTitle.value = `${t("update")}${schema.title}`;
dialogVisible.value = true;
} else if (item.path === "delete") {
//delete
if (!rows.length) {
return;
}
const url = `${baseUrl}/${item.path}`;
await post(
url,
rows.map((o) => o.id)
);
await load(indexUrl);
} else if (item.path === "export") {
//export
const url = `${baseUrl}/${item.path}`;
const exportUrl = `${url}?${qs.stringify(exportModel)}`;
await load(exportUrl);
} else if (item.path === "import") {
//import
const url = `${baseUrl}/${item.path}`;
editFormTitle.value = `${t("import")}${schema.title}`;
dialogVisible.value = true;
}
};
const submit = async () => {
if (editFormMode.value === "create" || editFormMode.value === "update") {
try {
const valid = await editFormRef.value.validate();
if (valid) {
editFormloading.value = true;
const url = `${baseUrl}/${editFormMode.value}`;
const result = await post(url, editFormModel);
if (result.errors) {
model.errors = result.errors; //??
} else {
await load(indexUrl);
editFormMode.value = null;
dialogVisible.value = false;
}
}
} catch (error) {
console.error(error);
} finally {
editFormloading.value = false;
}
} else if (editFormMode.value === "details") {
load(indexUrl);
editFormMode.value = null;
dialogVisible.value = false;
}
};
await load(indexUrl);
return {
route,
tableRef,
tableLoading,
columns,
showColumn,
filterDrawer,
dialogVisible,
selectedRows,
schema,
queryFromSchema,
tableSchema,
data,
sortModel,
getClass,
sortChange,
getProp,
editFormRef,
editFormMode,
editFormTitle,
editFormSchema,
editFormModel,
exportModel,
onPageSizeChange,
onPageIndexChange,
handleSelectionChange,
load,
click,
submit,
};
},
};

41
code/WebApp/vanilla/components/markdown/index.js

@ -0,0 +1,41 @@
import { ref, onMounted } from 'vue';
import { marked, setOptions } from '../../lib/marked/marked.esm.js';
import mermaid from '../../lib/mermaid/mermaid.esm.min.mjs';
import hljs from '../../lib/highlightjs/highlight.min.js';
export default {
template: `<div ref="tplRef"><div class="source" style="display:none;"><slot /></div><div class="markdown-body"></div></template>`,
props: {
name: {
default: null
}
},
setup(props) {
const tplRef = ref(null);
mermaid.initialize({ startOnLoad: false });
let id = 0;
onMounted(async () => {
setOptions({
highlight: function (code, lang) {
if (lang === 'mermaid') {
return mermaid.mermaidAPI.render(`mermaid${id++}`, code, undefined);
} else {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
}
},
langPrefix: 'hljs language-',
});
let mdText = tplRef.value.querySelector('.source').innerText;
if (props.name !== null) {
const response = await fetch(`./assets/docs/${props.name}.md`);
mdText = await response.text();
}
tplRef.value.querySelector('.markdown-body').innerHTML = marked(mdText);
tplRef.value.querySelector('.source').remove();
});
return {
tplRef
};
}
}

4
code/WebApp/vanilla/config/settings.js

@ -0,0 +1,4 @@
export default {
enableLocale: false,
baseURL: "http://dev.ccwin-in.com:10582/api",
};

BIN
code/WebApp/vanilla/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

72
code/WebApp/vanilla/index.html

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<style>
@keyframes loading-rotate {
to {
transform: rotate(360deg)
}
}
#loading {
display: none;
animation: loading-rotate 2s linear infinite;
}
#loading.loading {
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 50px;
height: 50px;
}
</style>
<link rel="stylesheet" href="./main.css" />
<title></title>
</head>
<body>
<div id="app"></div>
<img src="./assets/icons/loading.svg" id="loading" class="loading">
<script type="importmap">
{
"imports": {
"html":"./utils/index.js",
"detect-it":"./lib/detect-it/detect-it.esm.js",
"lodash":"./lib/lodash/lodash.esm.js",
"vue": "./lib/vue/vue.esm-browser.js",
"vue-router": "./lib/vue-router/vue-router.esm-browser.js",
"vue-i18n":"./lib/vue-i18n/vue-i18n.esm-browser.prod.js",
"pinia": "./lib/pinia/pinia.esm-browser.js",
"pubsub-js": "./lib/pubsub-js/pubsub.esm.js",
"linq": "./lib/linq/linq.min.js",
"@microsoft/signalr": "./lib/@microsoft/signalr/signalr.esm.js",
"@vueuse/shared": "./lib/@vueuse/shared/index.mjs",
"@vueuse/core": "./lib/@vueuse/core/index.mjs",
"element-plus": "./lib/element-plus/index.full.min.mjs",
"@element-plus/icons-vue":"./lib/@element-plus/icons-vue/index.js",
"nprogress": "./lib/nprogress/nprogress.vite-esm.js",
"echarts/core": "./lib/echarts/echarts.esm.min.js",
"vue-echarts": "./lib/vue-echarts/index.esm.min.js",
"resize-detector": "./lib/resize-detector/index.js",
"@vue-office/excel": "./lib/@vue-office/excel/vue-office-excel.mjs",
"@vue/devtools-api": "./lib/@vue/devtools-api/shim.js",
"vue-demi": "./lib/vue-demi/shim.js"
}
}
</script>
<script>
window.process = { env: { NODE_ENV: 'production' } };
</script>
<script type="module" src="./main.js"></script>
</body>
</html>

12
code/WebApp/vanilla/layouts/footer.js

@ -0,0 +1,12 @@
import html from "html";
import { useAppStore } from "../store/index.js";
export default {
template: html`<div class="footer flex items-center justify-center">{{$t('copyright')}}</div>`,
setup() {
const appStore = useAppStore();
return {
appStore,
};
},
};

168
code/WebApp/vanilla/layouts/header.js

@ -0,0 +1,168 @@
import html from "html";
import { ref, onMounted, onUnmounted } from "vue";
import { useAppStore } from "../store/index.js";
import SvgIcon from "../components/icon/index.js";
import LayoutLogo from "./logo.js";
import { useDark, useFullscreen, useToggle } from "@vueuse/core";
import { ElMessage, ElMessageBox } from "element-plus";
import { useI18n } from "vue-i18n";
import { logout } from "../api/user.js";
import LayoutLocale from "./locale.js";
import router from "../router/index.js";
import { treeToList } from "../utils/index.js";
export default {
components: { SvgIcon, LayoutLogo, LayoutLocale, ElMessage, ElMessageBox },
template: html`
<div class="flex items-center justify-between">
<div class="flex items-center justify-center">
<layout-logo />
<el-icon @click="toggleMenuCollapse" class="cursor-pointer">
<svg-icon name="unfold" v-if="appStore.isMenuCollapse" />
<svg-icon name="fold" v-else />
</el-icon>
</div>
<div class="flex">
<el-space>
<el-icon class="cursor-pointer" @click="clickSearch">
<ep-search />
</el-icon>
<el-select
ref="searchRef"
placeholder="search"
v-show="showSearch"
@blur="hideSearch"
filterable
remote
:remote-method="searchMenu"
v-model="searchModel"
:loading="searchLoading"
>
<el-option
v-for="item in searchOptions"
:key="item.meta.path"
:value="item.meta.path"
:label="item.meta.fullName"
@click="searchChange(item)"
/>
</el-select>
<el-icon v-model="isDark" @click="toggleDark()" :size="18" class="cursor-pointer">
<ep-sunny v-if="isDark" />
<ep-moon v-else />
</el-icon>
<el-icon @click="toggleFullscreen" :size="18" class="cursor-pointer">
<svg-icon name="fullscreen-exit" v-if="isFullscreen" />
<svg-icon name="fullscreen" v-else />
</el-icon>
<el-dropdown class="cursor-pointer" v-if="appStore.token">
<span class="el-dropdown-link flex">
<el-icon class="el-icon--left" :size="18">
<img v-if="appStore.user.avatar" />
<ep-user v-else />
</el-icon>
{{ appStore.user.name }}
<el-icon class="el-icon--right">
<ep-arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<router-link to="/account">
<el-icon> <ep-user /> </el-icon>{{$t('userCenter')}}
</router-link>
</el-dropdown-item>
<el-dropdown-item divided @click="confirmLogout">
<el-icon> <ep-switch-button /> </el-icon>{{$t('logout')}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-link type="info" v-else>
<router-link to="/register"> {{$t('register')}}</router-link>
</el-link>
<layout-locale />
</el-space>
</div>
</div>
`,
setup() {
const i18n = useI18n();
const appStore = useAppStore();
//
const searchRef = ref(null);
const searchLoading = ref(false);
const searchModel = ref("");
const searchOptions = ref([]);
const showSearch = ref(false);
const hideSearch = () => {
showSearch.value = false;
};
const clickSearch = () => {
showSearch.value = !showSearch.value;
if (showSearch.value) {
searchRef.value.focus();
}
};
const searchMenu = (query) => {
if (query) {
try {
searchLoading.value = true;
const menus = treeToList(router.getRoutes().find((o) => o.path === "/").children);
searchOptions.value = menus
.filter((o) => !o.children || o.children.length === 0)
.filter((o) => o.meta.fullName.indexOf(query) > -1);
} finally {
searchLoading.value = false;
}
}
};
const searchChange = (route) => {
if (!route.meta.isExternal) {
router.push(route.meta.path);
searchModel.value = "";
searchOptions.value = [];
showSearch.value = false;
} else {
window.open(route.path);
}
};
//
const isDark = useDark();
const toggleDark = useToggle(isDark);
const toggleMenuCollapse = () => (appStore.isMenuCollapse = !appStore.isMenuCollapse);
//
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(document.documentElement);
const confirmLogout = async () => {
try {
await ElMessageBox.confirm(i18n.t("confirmLogout"), i18n.t("tip"), { type: "warning" });
logout();
} catch (error) {
if (error === "cancel") {
ElMessage({
type: "info",
message: i18n.t("cancel"),
});
}
}
};
return {
appStore,
showSearch,
hideSearch,
clickSearch,
searchRef,
searchLoading,
searchModel,
searchOptions,
searchMenu,
searchChange,
isDark,
toggleDark,
toggleMenuCollapse,
isFullscreen,
toggleFullscreen,
confirmLogout,
};
},
};

57
code/WebApp/vanilla/layouts/index.js

@ -0,0 +1,57 @@
import html from "html";
import LayoutHeader from "./header.js";
import LayoutMenu from "./menu.js";
import LayoutTabs from "./tabs.js";
import LayoutFooter from "./footer.js";
import Icon from "../components/icon/index.js";
import { useAppStore } from "../store/index.js";
import { computed } from "vue";
export default {
components: { Icon, LayoutHeader, LayoutMenu, LayoutTabs, LayoutFooter },
template: html`<el-container>
<el-header><layout-header /></el-header>
<el-container>
<el-aside width="auto">
<el-scrollbar><layout-menu /></el-scrollbar>
</el-aside>
<el-container class="backtop">
<el-scrollbar>
<layout-tabs />
<el-main>
<router-view v-if="!isRefreshing" v-slot="{ Component, route }">
<component
:is="Component"
v-if="!appStore.isUseTabsRouter || !route.meta?.cached"
:key="$route.fullPath"
/>
<keep-alive>
<component
:is="Component"
v-if="appStore.isUseTabsRouter && route.meta?.cached"
:key="route.fullPath"
/>
</keep-alive>
</router-view>
</el-main>
<el-footer>
<layout-footer />
</el-footer>
<el-backtop target=".backtop > .el-scrollbar > .el-scrollbar__wrap" />
</el-scrollbar>
</el-container>
</el-container>
</el-container>`,
setup() {
const appStore = useAppStore();
const isRefreshing = computed(() => appStore.isRefreshing);
const path = computed(() => useRoute().matched[0].path);
const items = computed(() => useRoute().matched[0].children);
return {
appStore,
isRefreshing,
path,
items,
};
},
};

36
code/WebApp/vanilla/layouts/locale.js

@ -0,0 +1,36 @@
import html from "html";
import { useAppStore } from "../store/index.js";
import { useI18n } from "vue-i18n";
import Icon from "../components/icon/index.js";
export default {
components: { Icon },
template: html`<el-dropdown class="cursor-pointer" v-if="appStore.settings.enableLocale">
<span class="el-dropdown-link flex">
<el-icon :size="18">
<icon name="lang" />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="locale in $i18n.availableLocales" @click="changeLocale(locale)">
{{appStore.localization.options.find(o=>o.value===locale).label}}
<el-icon class="el-icon--right" v-if="locale===$i18n.locale">
<ep-select />
</el-icon>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>`,
setup() {
const appStore = useAppStore();
const i18n = useI18n();
const changeLocale = (locale) => {
appStore.localization.locale = locale;
i18n.locale.value = locale;
};
return {
appStore,
changeLocale,
};
},
};

17
code/WebApp/vanilla/layouts/logo.js

@ -0,0 +1,17 @@
import html from "html";
import { useAppStore } from "../store/index.js";
export default {
template: html`<router-link to="/" class="logo">
<div class="flex h-full items-center">
<img src="/assets/logo.svg" />
<h1 v-if="!appStore.isMenuCollapse">{{$t('application')}}</h1>
</div>
</router-link>`,
setup() {
const appStore = useAppStore();
return {
appStore,
};
},
};

55
code/WebApp/vanilla/layouts/menu-item.js

@ -0,0 +1,55 @@
import html from "html";
import { reactive, watch } from "vue";
import Icon from "../components/icon/index.js";
import { useRouter } from "vue-router";
export default {
name: "menuItem",
components: { Icon },
template: html`<el-sub-menu :index="modelValue.meta.path" v-if="modelValue.children">
<template #title>
<el-icon><icon :name="modelValue.meta.icon??'folder'" /></el-icon>
<span>{{modelValue.meta.title}}</span>
</template>
<menu-item v-for="item in modelValue.children" v-model="item" />
</el-sub-menu>
<el-menu-item
v-else
:index="modelValue.meta.isExternal?null:modelValue.meta.path"
@click.native="click(modelValue)"
>
<el-icon><icon :name="modelValue.meta.icon??file" /></el-icon>
<template #title>
<span>{{modelValue.meta.title}}</span>
</template>
</el-menu-item>`,
props: {
modelValue: {
typeof: Object,
},
},
setup(props, context) {
const router = useRouter();
const model = reactive(props.modelValue);
watch(
model,
(value) => {
context.emit("update:modelValue", value);
},
{ deep: true }
);
//
const click = (route) => {
if (!route.meta.isExternal) {
router.push(route.meta.path);
} else {
window.open(route.path);
}
};
//
return {
model,
click,
};
},
};

24
code/WebApp/vanilla/layouts/menu.js

@ -0,0 +1,24 @@
import html from "html";
import Icon from "../components/icon/index.js";
import { useAppStore } from "../store/index.js";
import MenuItem from "./menu-item.js";
import router from "../router/index.js";
export default {
components: { Icon, MenuItem },
template: html`<el-menu
:collapse="appStore.isMenuCollapse"
:collapse-transition="false"
:default-active="$route.fullPath"
>
<menu-item v-for="item in menus" v-model="item" />
</el-menu>`,
setup() {
const appStore = useAppStore();
const menus = router.getRoutes().find((o) => o.name === "layout").children;
return {
appStore,
menus,
};
},
};

171
code/WebApp/vanilla/layouts/tabs.js

@ -0,0 +1,171 @@
import html from "html";
import { ref, nextTick } from "vue";
import { useRoute, onBeforeRouteUpdate, useRouter } from "vue-router";
import Icon from "../components/icon/index.js";
import { useAppStore } from "../store/index.js";
import MenuItem from "./menu-item.js";
export default {
components: { Icon, MenuItem },
template: html`<el-tabs
v-model="model"
type="border-card"
class="router-tab"
@tab-remove="remove"
@tab-click="onClick"
>
<template v-for="(item, index) in appStore.routes" :key="item.fullPath">
<el-tab-pane v-model="item.fullPath" :name="item.fullPath" :closable="appStore.routes.length > 1">
<template #label>
<el-dropdown
:ref="(el) => setRef(index, el)"
class="h-full"
trigger="contextmenu"
@visible-change="showContextMenu(index, $event)"
>
<span class="inline-flex items-center">
<el-icon><icon v-if="item.meta.icon" :name="item.meta.icon" /></el-icon>
{{ item.meta?.title ?? item.fullPath }}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="refresh(index)"><i-ep-refresh />刷新</el-dropdown-item>
<el-dropdown-item :disabled="index === 0" @click="removeLeft(index)">
<i-ep-back />关闭左侧
</el-dropdown-item>
<el-dropdown-item :disabled="index === appStore.routes.length - 1" @click="removeRight(index)">
<i-ep-right />关闭右侧
</el-dropdown-item>
<el-dropdown-item
:disabled="index === 0 && index === appStore.routes.length - 1"
@click="removeOthers(index)"
>
<i-ep-switch />
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tab-pane>
</template>
</el-tabs>`,
styles: html`
<style>
.router-tab {
box-sizing: border-box;
height: 40px !important;
background-color: var(--el-fill-color-blank);
border-width: 0 !important;
}
.router-tab .el-tabs__item {
padding: 13px !important;
border-bottom-width: 0;
}
.router-tab .el-tabs__content {
display: none;
}
</style>
`,
setup() {
const appStore = useAppStore();
const itemRefs = ref([]);
const currentRoute = useRoute();
const router = useRouter();
const model = ref(currentRoute.fullPath);
onBeforeRouteUpdate((to) => {
model.value = to.fullPath;
});
const setRef = (index, el) => {
if (el) {
itemRefs.value[index] = el;
} else {
itemRefs.value.splice(index, 1);
}
};
const showContextMenu = (index, show) => {
if (show) {
itemRefs.value.forEach((item, i) => {
if (i !== index) {
item?.handleClose();
}
});
}
};
const refresh = (index) => {
const currentIndex = appStore.routes.findIndex((o) => o.fullPath === currentRoute.fullPath);
const route = appStore.routes[index];
if (index !== currentIndex) {
router.push({ path: route.fullPath });
}
appStore.isRefreshing = true;
nextTick(() => {
appStore.isRefreshing = false;
});
};
const remove = (name) => {
if (appStore.routes.length > 1) {
const index = appStore.routes.findIndex((o) => o.fullPath === name);
const currentIndex = appStore.routes.findIndex((o) => o.fullPath === currentRoute.fullPath);
appStore.routes.splice(index, 1);
if (index === currentIndex) {
if (appStore.routes[index]) {
router.push(appStore.routes[index]);
} else {
router.push(appStore.routes[index - 1]);
}
}
}
};
const removeLeft = (index) => {
const currentIndex = appStore.routes.findIndex((o) => o.fullPath === currentRoute.fullPath);
const route = appStore.routes[index];
appStore.routes.splice(0, index);
if (currentIndex < index) {
router.push(route);
}
};
const removeRight = (index) => {
const currentIndex = appStore.routes.findIndex((o) => o.fullPath === currentRoute.fullPath);
appStore.routes.splice(index + 1, appStore.routes.length - index);
if (currentIndex > index) {
router.push(appStore.routes[index]);
}
};
const removeOthers = (index) => {
removeRight(index);
removeLeft(index);
if (appStore.routes[0].fullPath !== currentRoute.fullPath) {
router.push(appStore.routes[0]);
}
};
const onClick = (context) => {
if (!context.active) {
router.push(context.props.name);
}
};
return {
model,
appStore,
itemRefs,
onBeforeRouteUpdate,
setRef,
showContextMenu,
refresh,
remove,
removeLeft,
removeRight,
removeOthers,
onClick,
};
},
};

9349
code/WebApp/vanilla/lib/@element-plus/icons-vue/index.js

File diff suppressed because it is too large

2568
code/WebApp/vanilla/lib/@microsoft/signalr/signalr.esm.js

File diff suppressed because it is too large

765
code/WebApp/vanilla/lib/@vue-office/excel/index.css

@ -0,0 +1,765 @@
body {
margin: 0;
}
.x-spreadsheet {
font-size: 13px;
line-height: normal;
user-select: none;
-moz-user-select: none;
font-family: 'Lato', 'Source Sans Pro', Roboto, Helvetica, Arial, sans-serif;
box-sizing: content-box;
background: #fff;
-webkit-font-smoothing: antialiased;
}
.x-spreadsheet textarea {
font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
}
.x-spreadsheet-sheet {
position: relative;
overflow: hidden;
}
.x-spreadsheet-table {
vertical-align: bottom;
}
.x-spreadsheet-tooltip {
font-family: inherit;
position: absolute;
padding: 5px 10px;
color: #fff;
border-radius: 1px;
background: #000000;
font-size: 12px;
z-index: 201;
}
.x-spreadsheet-tooltip:before {
pointer-events: none;
position: absolute;
left: calc(50% - 4px);
top: -4px;
content: "";
width: 8px;
height: 8px;
background: inherit;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
z-index: 1;
box-shadow: 1px 1px 3px -1px rgba(0, 0, 0, 0.3);
}
.x-spreadsheet-color-palette {
padding: 5px;
}
.x-spreadsheet-color-palette table {
margin: 0;
padding: 0;
border-collapse: separate;
border-spacing: 2;
background: #fff;
}
.x-spreadsheet-color-palette table td {
margin: 0;
cursor: pointer;
border: 1px solid transparent;
}
.x-spreadsheet-color-palette table td:hover {
border-color: #ddd;
}
.x-spreadsheet-color-palette table td .x-spreadsheet-color-palette-cell {
width: 16px;
height: 16px;
}
.x-spreadsheet-border-palette {
padding: 6px;
}
.x-spreadsheet-border-palette table {
margin: 0;
padding: 0;
border-collapse: separate;
border-spacing: 0;
background: #fff;
table-layout: fixed;
}
.x-spreadsheet-border-palette table td {
margin: 0;
}
.x-spreadsheet-border-palette .x-spreadsheet-border-palette-left {
border-right: 1px solid #eee;
padding-right: 6px;
}
.x-spreadsheet-border-palette .x-spreadsheet-border-palette-left .x-spreadsheet-border-palette-cell {
width: 30px;
height: 30px;
cursor: pointer;
text-align: center;
}
.x-spreadsheet-border-palette .x-spreadsheet-border-palette-left .x-spreadsheet-border-palette-cell:hover {
background-color: #eee;
}
.x-spreadsheet-border-palette .x-spreadsheet-border-palette-right {
padding-left: 6px;
}
.x-spreadsheet-border-palette .x-spreadsheet-border-palette-right .x-spreadsheet-line-type {
position: relative;
left: 0;
top: -3px;
}
.x-spreadsheet-dropdown {
position: relative;
}
.x-spreadsheet-dropdown .x-spreadsheet-dropdown-content {
position: absolute;
z-index: 200;
background: #fff;
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
}
.x-spreadsheet-dropdown.bottom-left .x-spreadsheet-dropdown-content {
top: calc(100% + 5px);
left: 0;
}
.x-spreadsheet-dropdown.bottom-right .x-spreadsheet-dropdown-content {
top: calc(100% + 5px);
right: 0;
}
.x-spreadsheet-dropdown.top-left .x-spreadsheet-dropdown-content {
bottom: calc(100% + 5px);
left: 0;
}
.x-spreadsheet-dropdown.top-right .x-spreadsheet-dropdown-content {
bottom: calc(100% + 5px);
right: 0;
}
.x-spreadsheet-dropdown .x-spreadsheet-dropdown-title {
padding: 0 5px;
display: inline-block;
}
/* resizer **/
.x-spreadsheet-resizer {
position: absolute;
z-index: 11;
}
.x-spreadsheet-resizer .x-spreadsheet-resizer-hover {
background-color: rgba(75, 137, 255, 0.25);
}
.x-spreadsheet-resizer .x-spreadsheet-resizer-line {
position: absolute;
}
.x-spreadsheet-resizer.horizontal {
cursor: row-resize;
}
.x-spreadsheet-resizer.horizontal .x-spreadsheet-resizer-line {
border-bottom: 2px dashed #4b89ff;
left: 0;
bottom: 0;
}
.x-spreadsheet-resizer.vertical {
cursor: col-resize;
}
.x-spreadsheet-resizer.vertical .x-spreadsheet-resizer-line {
border-right: 2px dashed #4b89ff;
top: 0;
right: 0;
}
/* scrollbar */
.x-spreadsheet-scrollbar {
position: absolute;
bottom: 0;
right: 0;
background-color: #f4f5f8;
opacity: 0.9;
z-index: 12;
}
.x-spreadsheet-scrollbar.horizontal {
right: 15px;
overflow-x: scroll;
overflow-y: hidden;
}
.x-spreadsheet-scrollbar.horizontal > div {
height: 1px;
background: #ddd;
}
.x-spreadsheet-scrollbar.vertical {
bottom: 15px;
overflow-x: hidden;
overflow-y: scroll;
}
.x-spreadsheet-scrollbar.vertical > div {
width: 1px;
background: #ddd;
}
/* @{css-prefix}-overlayer */
.x-spreadsheet-overlayer {
position: absolute;
left: 0;
top: 0;
z-index: 10;
}
.x-spreadsheet-overlayer .x-spreadsheet-overlayer-content {
position: absolute;
overflow: hidden;
pointer-events: none;
width: 100%;
height: 100%;
}
.x-spreadsheet-editor,
.x-spreadsheet-selector {
box-sizing: content-box;
position: absolute;
overflow: hidden;
pointer-events: none;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* @{css-prefix}-selector */
.x-spreadsheet-selector .hide-input {
position: absolute;
z-index: 0;
}
.x-spreadsheet-selector .hide-input input {
padding: 0;
width: 0;
border: none!important;
}
.x-spreadsheet-selector .x-spreadsheet-selector-area {
position: absolute;
border: 2px solid #4b89ff;
background: rgba(75, 137, 255, 0.1);
z-index: 5;
}
.x-spreadsheet-selector .x-spreadsheet-selector-clipboard,
.x-spreadsheet-selector .x-spreadsheet-selector-autofill {
position: absolute;
background: transparent;
z-index: 100;
}
.x-spreadsheet-selector .x-spreadsheet-selector-clipboard {
border: 2px dashed #4b89ff;
}
.x-spreadsheet-selector .x-spreadsheet-selector-autofill {
border: 1px dashed rgba(0, 0, 0, 0.45);
}
.x-spreadsheet-selector .x-spreadsheet-selector-corner {
pointer-events: auto;
position: absolute;
cursor: crosshair;
font-size: 0;
height: 5px;
width: 5px;
right: -5px;
bottom: -5px;
border: 2px solid #ffffff;
background: #4b89ff;
}
.x-spreadsheet-editor {
z-index: 20;
}
.x-spreadsheet-editor .x-spreadsheet-editor-area {
position: absolute;
text-align: left;
border: 2px solid #4b89ff;
line-height: 0;
z-index: 100;
pointer-events: auto;
}
.x-spreadsheet-editor .x-spreadsheet-editor-area textarea {
box-sizing: content-box;
border: none;
padding: 0 3px;
outline: none;
resize: none;
text-align: start;
overflow-y: hidden;
font: 400 13px Arial, 'Lato', 'Source Sans Pro', Roboto, Helvetica, sans-serif;
color: inherit;
white-space: normal;
word-wrap: break-word;
line-height: 22px;
margin: 0;
}
.x-spreadsheet-editor .x-spreadsheet-editor-area .textline {
overflow: hidden;
visibility: hidden;
position: fixed;
top: 0;
left: 0;
}
.x-spreadsheet-item {
user-select: none;
background: 0;
border: 1px solid transparent;
outline: none;
height: 26px;
color: rgba(0, 0, 0, 0.9);
line-height: 26px;
list-style: none;
padding: 2px 10px;
cursor: default;
text-align: left;
overflow: hidden;
}
.x-spreadsheet-item.disabled {
pointer-events: none;
opacity: 0.5;
}
.x-spreadsheet-item:hover,
.x-spreadsheet-item.active {
background: rgba(0, 0, 0, 0.05);
}
.x-spreadsheet-item.divider {
height: 0;
padding: 0;
margin: 5px 0;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.x-spreadsheet-item .label {
float: right;
opacity: 0.65;
font-size: 1em;
}
.x-spreadsheet-item.state,
.x-spreadsheet-header.state {
padding-left: 35px!important;
position: relative;
}
.x-spreadsheet-item.state:before,
.x-spreadsheet-header.state:before {
content: '';
position: absolute;
width: 10px;
height: 10px;
left: 12px;
top: calc(50% - 5px);
background: rgba(0, 0, 0, 0.08);
border-radius: 2px;
}
.x-spreadsheet-item.state.checked:before,
.x-spreadsheet-header.state.checked:before {
background: #4b89ff;
}
.x-spreadsheet-checkbox {
position: relative;
display: inline-block;
backface-visibility: hidden;
outline: 0;
vertical-align: baseline;
font-style: normal;
font-size: 1rem;
line-height: 1em;
}
.x-spreadsheet-checkbox > input {
position: absolute;
top: 0;
left: 0;
opacity: 0!important;
outline: 0;
z-index: -1;
}
.x-spreadsheet-suggest,
.x-spreadsheet-contextmenu,
.x-spreadsheet-sort-filter {
position: absolute;
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
background: #fff;
z-index: 100;
width: 260px;
pointer-events: auto;
overflow: auto;
}
.x-spreadsheet-suggest {
width: 200px;
}
.x-spreadsheet-filter {
border: 1px solid #e9e9e9;
font-size: 12px;
margin: 10px;
}
.x-spreadsheet-filter .x-spreadsheet-header {
padding: 0.5em 0.75em;
background: #f8f8f9;
border-bottom: 1px solid #e9e9e9;
border-left: 1px solid transparent;
}
.x-spreadsheet-filter .x-spreadsheet-body {
height: 200px;
overflow-y: auto;
}
.x-spreadsheet-filter .x-spreadsheet-body .x-spreadsheet-item {
height: 20px;
line-height: 20px;
}
.x-spreadsheet-sort-filter .x-spreadsheet-buttons {
margin: 10px;
}
.x-spreadsheet-bottombar {
height: 40px;
padding: 0 30px;
text-align: left;
background: #f5f6f7;
display: flex;
}
.x-spreadsheet-bottombar {
position: relative;
border-top: 1px solid #e0e2e4;
}
.x-spreadsheet-bottombar .x-spreadsheet-menu > li {
line-height: 40px;
height: 40px;
padding-top: 0;
padding-bottom: 0;
vertical-align: middle;
border-right: 1px solid #e8eaed;
}
.x-spreadsheet-menu {
list-style: none;
margin: 0;
padding: 0;
user-select: none;
}
.x-spreadsheet-menu > li {
float: left;
line-height: 1.25em;
padding: 0.785em 1em;
margin: 0;
vertical-align: middle;
text-align: left;
font-weight: 400;
color: #80868b;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
font-weight: bold;
}
.x-spreadsheet-menu > li.active {
background-color: #fff;
color: rgba(0, 0, 0, 0.65);
}
.x-spreadsheet-menu > li .x-spreadsheet-dropdown {
display: inline-block;
}
.x-spreadsheet-print {
position: absolute;
left: 0;
top: 0;
z-index: 100;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.x-spreadsheet-print-bar {
background: #424242;
height: 60px;
line-height: 60px;
padding: 0 30px;
}
.x-spreadsheet-print-bar .-title {
color: #fff;
font-weight: bold;
font-size: 1.2em;
float: left;
}
.x-spreadsheet-print-bar .-right {
float: right;
margin-top: 12px;
}
.x-spreadsheet-print-content {
display: flex;
flex: auto;
flex-direction: row;
background: #d0d0d0;
height: calc(100% - 60px);
}
.x-spreadsheet-print-content .-sider {
flex: 0 0 300px;
width: 300px;
border-left: 2px solid #ccc;
background: #fff;
}
.x-spreadsheet-print-content .-content {
flex: auto;
overflow-x: auto;
overflow-y: scroll;
height: 100%;
}
.x-spreadsheet-canvas-card-wraper {
margin: 40px 20px;
}
.x-spreadsheet-canvas-card {
background: #fff;
margin: auto;
page-break-before: auto;
page-break-after: always;
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 3px rgba(0, 0, 0, 0.12), 0 4px 5px 0 rgba(0, 0, 0, 0.2);
}
.x-spreadsheet-calendar {
color: rgba(0, 0, 0, 0.65);
background: #ffffff;
user-select: none;
}
.x-spreadsheet-calendar .calendar-header {
font-weight: 700;
line-height: 30px;
text-align: center;
width: 100%;
float: left;
background: #f9fafb;
}
.x-spreadsheet-calendar .calendar-header .calendar-header-left {
padding-left: 5px;
float: left;
}
.x-spreadsheet-calendar .calendar-header .calendar-header-right {
float: right;
}
.x-spreadsheet-calendar .calendar-header .calendar-header-right a {
padding: 3px 0;
margin-right: 2px;
border-radius: 2px;
}
.x-spreadsheet-calendar .calendar-header .calendar-header-right a:hover {
background: rgba(0, 0, 0, 0.08);
}
.x-spreadsheet-calendar .calendar-body {
border-collapse: collapse;
border-spacing: 0;
}
.x-spreadsheet-calendar .calendar-body th,
.x-spreadsheet-calendar .calendar-body td {
width: 14.28571429%;
min-width: 32px;
text-align: center;
font-weight: 700;
line-height: 30px;
padding: 0;
}
.x-spreadsheet-calendar .calendar-body td > .cell:hover {
background: #ecf6fd;
}
.x-spreadsheet-calendar .calendar-body td > .cell.active,
.x-spreadsheet-calendar .calendar-body td > .cell.active:hover {
background: #ecf6fd;
color: #2185D0;
}
.x-spreadsheet-calendar .calendar-body td > .cell.disabled {
pointer-events: none;
opacity: 0.5;
}
.x-spreadsheet-datepicker {
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
position: absolute;
left: 0;
top: calc(100% + 5px);
z-index: 10;
width: auto;
}
.x-spreadsheet-buttons {
display: flex;
justify-content: flex-end;
}
.x-spreadsheet-buttons .x-spreadsheet-button {
margin-left: 8px;
}
.x-spreadsheet-button {
display: inline-block;
border-radius: 3px;
line-height: 1em;
min-height: 1em;
white-space: nowrap;
text-align: center;
cursor: pointer;
font-size: 1em;
font-weight: 700;
padding: 0.75em 1em;
color: rgba(0, 0, 0, 0.6);
background: #E0E1E2;
text-decoration: none;
font-family: "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
outline: none;
vertical-align: baseline;
zoom: 1;
user-select: none;
transition: all 0.1s linear;
}
.x-spreadsheet-button.active,
.x-spreadsheet-button:hover {
background-color: #C0C1C2;
color: rgba(0, 0, 0, 0.8);
}
.x-spreadsheet-button.primary {
color: #fff;
background-color: #2185D0;
}
.x-spreadsheet-button.primary:hover,
.x-spreadsheet-button.primary.active {
color: #fff;
background-color: #1678c2;
}
.x-spreadsheet-form-input {
font-size: 1em;
position: relative;
font-weight: 400;
display: inline-flex;
color: rgba(0, 0, 0, 0.87);
}
.x-spreadsheet-form-input input {
z-index: 1;
margin: 0;
max-width: 100%;
flex: 1 0 auto;
outline: 0;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
text-align: left;
line-height: 30px;
height: 30px;
padding: 0 8px;
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 3px;
transition: box-shadow 0.1s ease, border-color 0.1s ease;
box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
}
.x-spreadsheet-form-input input:focus {
border-color: #4b89ff;
box-shadow: inset 0 1px 2px rgba(75, 137, 255, 0.2);
}
.x-spreadsheet-form-select {
position: relative;
display: inline-block;
background: #fff;
border: 1px solid #e9e9e9;
border-radius: 2px;
cursor: pointer;
color: rgba(0, 0, 0, 0.87);
user-select: none;
box-shadow: inset 0 1px 2px hsla(0, 0%, 4%, 0.06);
}
.x-spreadsheet-form-select .input-text {
text-overflow: ellipsis;
white-space: nowrap;
min-width: 60px;
width: auto;
height: 30px;
line-height: 30px;
padding: 0 8px;
}
.x-spreadsheet-form-fields {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.x-spreadsheet-form-fields .x-spreadsheet-form-field {
flex: 0 1 auto;
}
.x-spreadsheet-form-fields .x-spreadsheet-form-field .label {
display: inline-block;
margin: 0 10px 0 0;
}
.x-spreadsheet-form-field {
display: block;
vertical-align: middle;
margin-left: 10px;
margin-bottom: 10px;
}
.x-spreadsheet-form-field:first-child {
margin-left: 0;
}
.x-spreadsheet-form-field.error .x-spreadsheet-form-select,
.x-spreadsheet-form-field.error input {
border-color: #f04134;
}
.x-spreadsheet-form-field .tip {
color: #f04134;
font-size: 0.9em;
}
.x-spreadsheet-dimmer {
display: none;
position: absolute;
top: 0 !important;
left: 0 !important;
width: 100%;
height: 100%;
text-align: center;
vertical-align: middle;
background-color: rgba(0, 0, 0, 0.6);
opacity: 0;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation-duration: 0.5s;
animation-duration: 0.5s;
transition: background-color 0.5s linear;
user-select: none;
z-index: 1000;
}
.x-spreadsheet-dimmer.active {
display: block;
opacity: 1;
}
form fieldset {
border: none;
}
form fieldset label {
display: block;
margin-bottom: 0.5em;
font-size: 1em;
color: #666;
}
form fieldset select {
font-size: 1.1em;
width: 100%;
background-color: #fff;
border: none;
border-bottom: 2px solid #ddd;
padding: 0.5em 0.85em;
border-radius: 2px;
}
.x-spreadsheet-modal,
.x-spreadsheet-toast {
font-size: 13px;
position: fixed;
z-index: 1001;
text-align: left;
line-height: 1.25em;
min-width: 360px;
color: rgba(0, 0, 0, 0.87);
font-family: 'Lato', 'Source Sans Pro', Roboto, Helvetica, Arial, sans-serif;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: #fff;
background-clip: padding-box;
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 8px;
}
.x-spreadsheet-toast {
background-color: rgba(255, 255, 255, 0.85);
}
.x-spreadsheet-modal-header,
.x-spreadsheet-toast-header {
font-weight: 600;
background-clip: padding-box;
background-color: rgba(255, 255, 255, 0.85);
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px 4px 0 0;
}
.x-spreadsheet-toast-header {
color: #F2711C;
}
.x-spreadsheet-modal-header {
border-bottom: 1px solid #e0e2e4;
background: rgba(0, 0, 0, 0.08);
font-size: 1.0785em;
}
.x-spreadsheet-modal-header,
.x-spreadsheet-modal-content,
.x-spreadsheet-toast-header,
.x-spreadsheet-toast-content {
padding: 0.75em 1em;
}
.x-spreadsheet-menu li:first-child {
display: none;
}
.vue-office-excel {
height: 100%;
}

54424
code/WebApp/vanilla/lib/@vue-office/excel/vue-office-excel.mjs

File diff suppressed because one or more lines are too long

160
code/WebApp/vanilla/lib/@vue/devtools-api/shim.js

@ -0,0 +1,160 @@
// node_modules/@vue/devtools-api/lib/esm/env.js
function getDevtoolsGlobalHook() {
return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
}
function getTarget() {
return typeof navigator !== "undefined" && typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {};
}
var isProxyAvailable = typeof Proxy === "function";
// node_modules/@vue/devtools-api/lib/esm/const.js
var HOOK_SETUP = "devtools-plugin:setup";
var HOOK_PLUGIN_SETTINGS_SET = "plugin:settings:set";
// node_modules/@vue/devtools-api/lib/esm/time.js
var supported;
var perf;
function isPerformanceSupported() {
var _a;
if (supported !== void 0) {
return supported;
}
if (typeof window !== "undefined" && window.performance) {
supported = true;
perf = window.performance;
} else if (typeof global !== "undefined" && ((_a = global.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) {
supported = true;
perf = global.perf_hooks.performance;
} else {
supported = false;
}
return supported;
}
function now() {
return isPerformanceSupported() ? perf.now() : Date.now();
}
// node_modules/@vue/devtools-api/lib/esm/proxy.js
var ApiProxy = class {
constructor(plugin, hook) {
this.target = null;
this.targetQueue = [];
this.onQueue = [];
this.plugin = plugin;
this.hook = hook;
const defaultSettings = {};
if (plugin.settings) {
for (const id in plugin.settings) {
const item = plugin.settings[id];
defaultSettings[id] = item.defaultValue;
}
}
const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`;
let currentSettings = Object.assign({}, defaultSettings);
try {
const raw = localStorage.getItem(localSettingsSaveId);
const data = JSON.parse(raw);
Object.assign(currentSettings, data);
} catch (e) {
}
this.fallbacks = {
getSettings() {
return currentSettings;
},
setSettings(value) {
try {
localStorage.setItem(localSettingsSaveId, JSON.stringify(value));
} catch (e) {
}
currentSettings = value;
},
now() {
return now();
}
};
if (hook) {
hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
if (pluginId === this.plugin.id) {
this.fallbacks.setSettings(value);
}
});
}
this.proxiedOn = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target.on[prop];
} else {
return (...args) => {
this.onQueue.push({
method: prop,
args
});
};
}
}
});
this.proxiedTarget = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target[prop];
} else if (prop === "on") {
return this.proxiedOn;
} else if (Object.keys(this.fallbacks).includes(prop)) {
return (...args) => {
this.targetQueue.push({
method: prop,
args,
resolve: () => {
}
});
return this.fallbacks[prop](...args);
};
} else {
return (...args) => {
return new Promise((resolve) => {
this.targetQueue.push({
method: prop,
args,
resolve
});
});
};
}
}
});
}
async setRealTarget(target) {
this.target = target;
for (const item of this.onQueue) {
this.target.on[item.method](...item.args);
}
for (const item of this.targetQueue) {
item.resolve(await this.target[item.method](...item.args));
}
}
};
// node_modules/@vue/devtools-api/lib/esm/index.js
function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
const descriptor = pluginDescriptor;
const target = getTarget();
const hook = getDevtoolsGlobalHook();
const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
} else {
const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
list.push({
pluginDescriptor: descriptor,
setupFn,
proxy
});
if (proxy)
setupFn(proxy.proxiedTarget);
}
}
export {
setupDevtoolsPlugin
};

7387
code/WebApp/vanilla/lib/@vueuse/core/index.mjs

File diff suppressed because it is too large

1790
code/WebApp/vanilla/lib/@vueuse/shared/index.mjs

File diff suppressed because it is too large

8842
code/WebApp/vanilla/lib/better-mock/mock.browser.esm.js

File diff suppressed because it is too large

8
code/WebApp/vanilla/lib/detect-it/detect-it.esm.js

@ -0,0 +1,8 @@
/**
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.17.1.
* Original file: /npm/detect-it@4.0.1/dist/detect-it.esm.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
var e="undefined"!=typeof window?window:{screen:{},navigator:{}},n=(e.matchMedia||function(){return{matches:!1}}).bind(e),t=!1,o={get passive(){return t=!0}},i=function(){};e.addEventListener&&e.addEventListener("p",i,o),e.removeEventListener&&e.removeEventListener("p",i,!1);var r=t,a="PointerEvent"in e,s="ontouchstart"in e,c=s||"TouchEvent"in e&&n("(any-pointer: coarse)").matches,h=(e.navigator.maxTouchPoints||0)>0||c,u=e.navigator.userAgent||"",m=n("(pointer: coarse)").matches&&/iPad|Macintosh/.test(u)&&Math.min(e.screen.width||0,e.screen.height||0)>=768,v=(n("(pointer: coarse)").matches||!n("(pointer: fine)").matches&&s)&&!/Windows.*Firefox/.test(u),d=n("(any-pointer: fine)").matches||n("(any-hover: hover)").matches||m||!s,p=!h||!d&&v?h?"touchOnly":"mouseOnly":"hybrid",y="mouseOnly"===p?"mouse":"touchOnly"===p||v?"touch":"mouse";export{p as deviceType,y as primaryInput,r as supportsPassiveEvents,a as supportsPointerEvents,c as supportsTouchEvents};export default null;
//# sourceMappingURL=/sm/4280e12bc6c9669987adcde477eb6347c379a3a324091a23187d02e5284db1bb.map

45
code/WebApp/vanilla/lib/echarts/echarts.esm.min.js

File diff suppressed because one or more lines are too long

1
code/WebApp/vanilla/lib/element-plus/index.css

File diff suppressed because one or more lines are too long

78
code/WebApp/vanilla/lib/element-plus/index.full.min.mjs

File diff suppressed because one or more lines are too long

2
code/WebApp/vanilla/lib/element-plus/locale/en.min.mjs

@ -0,0 +1,2 @@
/*! Element Plus v2.3.6 */var e={name:"en",el:{colorpicker:{confirm:"OK",clear:"Clear",defaultLabel:"color picker",description:"current color is {color}. press enter to select a new color."},datepicker:{now:"Now",today:"Today",cancel:"Cancel",clear:"Clear",confirm:"OK",dateTablePrompt:"Use the arrow keys and enter to select the day of the month",monthTablePrompt:"Use the arrow keys and enter to select the month",yearTablePrompt:"Use the arrow keys and enter to select the year",selectedDate:"Selected date",selectDate:"Select date",selectTime:"Select time",startDate:"Start Date",startTime:"Start Time",endDate:"End Date",endTime:"End Time",prevYear:"Previous Year",nextYear:"Next Year",prevMonth:"Previous Month",nextMonth:"Next Month",year:"",month1:"January",month2:"February",month3:"March",month4:"April",month5:"May",month6:"June",month7:"July",month8:"August",month9:"September",month10:"October",month11:"November",month12:"December",week:"week",weeks:{sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},weeksFull:{sun:"Sunday",mon:"Monday",tue:"Tuesday",wed:"Wednesday",thu:"Thursday",fri:"Friday",sat:"Saturday"},months:{jan:"Jan",feb:"Feb",mar:"Mar",apr:"Apr",may:"May",jun:"Jun",jul:"Jul",aug:"Aug",sep:"Sep",oct:"Oct",nov:"Nov",dec:"Dec"}},inputNumber:{decrease:"decrease number",increase:"increase number"},select:{loading:"Loading",noMatch:"No matching data",noData:"No data",placeholder:"Select"},dropdown:{toggleDropdown:"Toggle Dropdown"},cascader:{noMatch:"No matching data",loading:"Loading",placeholder:"Select",noData:"No data"},pagination:{goto:"Go to",pagesize:"/page",total:"Total {total}",pageClassifier:"",page:"Page",prev:"Go to previous page",next:"Go to next page",currentPage:"page {pager}",prevPages:"Previous {pager} pages",nextPages:"Next {pager} pages",deprecationWarning:"Deprecated usages detected, please refer to the el-pagination documentation for more details"},dialog:{close:"Close this dialog"},drawer:{close:"Close this dialog"},messagebox:{title:"Message",confirm:"OK",cancel:"Cancel",error:"Illegal input",close:"Close this dialog"},upload:{deleteTip:"press delete to remove",delete:"Delete",preview:"Preview",continue:"Continue"},slider:{defaultLabel:"slider between {min} and {max}",defaultRangeStartLabel:"pick start value",defaultRangeEndLabel:"pick end value"},table:{emptyText:"No Data",confirmFilter:"Confirm",resetFilter:"Reset",clearFilter:"All",sumText:"Sum"},tree:{emptyText:"No Data"},transfer:{noMatch:"No matching data",noData:"No data",titles:["List 1","List 2"],filterPlaceholder:"Enter keyword",noCheckedFormat:"{total} items",hasCheckedFormat:"{checked}/{total} checked"},image:{error:"FAILED"},pageHeader:{title:"Back"},popconfirm:{confirmButtonText:"Yes",cancelButtonText:"No"}}};export{e as default};
//# sourceMappingURL=en.min.mjs.map

2
code/WebApp/vanilla/lib/element-plus/locale/zh-cn.min.mjs

@ -0,0 +1,2 @@
/*! Element Plus v2.3.4 */var u={name:"zh-cn",el:{colorpicker:{confirm:"\u786E\u5B9A",clear:"\u6E05\u7A7A"},datepicker:{now:"\u6B64\u523B",today:"\u4ECA\u5929",cancel:"\u53D6\u6D88",clear:"\u6E05\u7A7A",confirm:"\u786E\u5B9A",selectDate:"\u9009\u62E9\u65E5\u671F",selectTime:"\u9009\u62E9\u65F6\u95F4",startDate:"\u5F00\u59CB\u65E5\u671F",startTime:"\u5F00\u59CB\u65F6\u95F4",endDate:"\u7ED3\u675F\u65E5\u671F",endTime:"\u7ED3\u675F\u65F6\u95F4",prevYear:"\u524D\u4E00\u5E74",nextYear:"\u540E\u4E00\u5E74",prevMonth:"\u4E0A\u4E2A\u6708",nextMonth:"\u4E0B\u4E2A\u6708",year:"\u5E74",month1:"1 \u6708",month2:"2 \u6708",month3:"3 \u6708",month4:"4 \u6708",month5:"5 \u6708",month6:"6 \u6708",month7:"7 \u6708",month8:"8 \u6708",month9:"9 \u6708",month10:"10 \u6708",month11:"11 \u6708",month12:"12 \u6708",weeks:{sun:"\u65E5",mon:"\u4E00",tue:"\u4E8C",wed:"\u4E09",thu:"\u56DB",fri:"\u4E94",sat:"\u516D"},months:{jan:"\u4E00\u6708",feb:"\u4E8C\u6708",mar:"\u4E09\u6708",apr:"\u56DB\u6708",may:"\u4E94\u6708",jun:"\u516D\u6708",jul:"\u4E03\u6708",aug:"\u516B\u6708",sep:"\u4E5D\u6708",oct:"\u5341\u6708",nov:"\u5341\u4E00\u6708",dec:"\u5341\u4E8C\u6708"}},select:{loading:"\u52A0\u8F7D\u4E2D",noMatch:"\u65E0\u5339\u914D\u6570\u636E",noData:"\u65E0\u6570\u636E",placeholder:"\u8BF7\u9009\u62E9"},cascader:{noMatch:"\u65E0\u5339\u914D\u6570\u636E",loading:"\u52A0\u8F7D\u4E2D",placeholder:"\u8BF7\u9009\u62E9",noData:"\u6682\u65E0\u6570\u636E"},pagination:{goto:"\u524D\u5F80",pagesize:"\u6761/\u9875",total:"\u5171 {total} \u6761",pageClassifier:"\u9875",page:"\u9875",prev:"\u4E0A\u4E00\u9875",next:"\u4E0B\u4E00\u9875",currentPage:"\u7B2C {pager} \u9875",prevPages:"\u5411\u524D {pager} \u9875",nextPages:"\u5411\u540E {pager} \u9875",deprecationWarning:"\u4F60\u4F7F\u7528\u4E86\u4E00\u4E9B\u5DF2\u88AB\u5E9F\u5F03\u7684\u7528\u6CD5\uFF0C\u8BF7\u53C2\u8003 el-pagination \u7684\u5B98\u65B9\u6587\u6863"},messagebox:{title:"\u63D0\u793A",confirm:"\u786E\u5B9A",cancel:"\u53D6\u6D88",error:"\u8F93\u5165\u7684\u6570\u636E\u4E0D\u5408\u6CD5!"},upload:{deleteTip:"\u6309 delete \u952E\u53EF\u5220\u9664",delete:"\u5220\u9664",preview:"\u67E5\u770B\u56FE\u7247",continue:"\u7EE7\u7EED\u4E0A\u4F20"},table:{emptyText:"\u6682\u65E0\u6570\u636E",confirmFilter:"\u7B5B\u9009",resetFilter:"\u91CD\u7F6E",clearFilter:"\u5168\u90E8",sumText:"\u5408\u8BA1"},tree:{emptyText:"\u6682\u65E0\u6570\u636E"},transfer:{noMatch:"\u65E0\u5339\u914D\u6570\u636E",noData:"\u65E0\u6570\u636E",titles:["\u5217\u8868 1","\u5217\u8868 2"],filterPlaceholder:"\u8BF7\u8F93\u5165\u641C\u7D22\u5185\u5BB9",noCheckedFormat:"\u5171 {total} \u9879",hasCheckedFormat:"\u5DF2\u9009 {checked}/{total} \u9879"},image:{error:"\u52A0\u8F7D\u5931\u8D25"},pageHeader:{title:"\u8FD4\u56DE"},popconfirm:{confirmButtonText:"\u786E\u5B9A",cancelButtonText:"\u53D6\u6D88"}}};export{u as default};
//# sourceMappingURL=zh-cn.min.mjs.map

1
code/WebApp/vanilla/lib/element-plus/theme-chalk/dark/css-vars.css

@ -0,0 +1 @@
html.dark{color-scheme:dark;--el-color-primary:#409eff;--el-color-primary-light-3:#3375b9;--el-color-primary-light-5:#2a598a;--el-color-primary-light-7:#213d5b;--el-color-primary-light-8:#1d3043;--el-color-primary-light-9:#18222c;--el-color-primary-dark-2:#66b1ff;--el-color-success:#67c23a;--el-color-success-light-3:#4e8e2f;--el-color-success-light-5:#3e6b27;--el-color-success-light-7:#2d481f;--el-color-success-light-8:#25371c;--el-color-success-light-9:#1c2518;--el-color-success-dark-2:#85ce61;--el-color-warning:#e6a23c;--el-color-warning-light-3:#a77730;--el-color-warning-light-5:#7d5b28;--el-color-warning-light-7:#533f20;--el-color-warning-light-8:#3e301c;--el-color-warning-light-9:#292218;--el-color-warning-dark-2:#ebb563;--el-color-danger:#f56c6c;--el-color-danger-light-3:#b25252;--el-color-danger-light-5:#854040;--el-color-danger-light-7:#582e2e;--el-color-danger-light-8:#412626;--el-color-danger-light-9:#2b1d1d;--el-color-danger-dark-2:#f78989;--el-color-error:#f56c6c;--el-color-error-light-3:#b25252;--el-color-error-light-5:#854040;--el-color-error-light-7:#582e2e;--el-color-error-light-8:#412626;--el-color-error-light-9:#2b1d1d;--el-color-error-dark-2:#f78989;--el-color-info:#909399;--el-color-info-light-3:#6b6d71;--el-color-info-light-5:#525457;--el-color-info-light-7:#393a3c;--el-color-info-light-8:#2d2d2f;--el-color-info-light-9:#202121;--el-color-info-dark-2:#a6a9ad;--el-box-shadow:0px 12px 32px 4px rgba(0, 0, 0, 0.36),0px 8px 20px rgba(0, 0, 0, 0.72);--el-box-shadow-light:0px 0px 12px rgba(0, 0, 0, 0.72);--el-box-shadow-lighter:0px 0px 6px rgba(0, 0, 0, 0.72);--el-box-shadow-dark:0px 16px 48px 16px rgba(0, 0, 0, 0.72),0px 12px 32px #000000,0px 8px 16px -8px #000000;--el-bg-color-page:#0a0a0a;--el-bg-color:#141414;--el-bg-color-overlay:#1d1e1f;--el-text-color-primary:#E5EAF3;--el-text-color-regular:#CFD3DC;--el-text-color-secondary:#A3A6AD;--el-text-color-placeholder:#8D9095;--el-text-color-disabled:#6C6E72;--el-border-color-darker:#636466;--el-border-color-dark:#58585B;--el-border-color:#4C4D4F;--el-border-color-light:#414243;--el-border-color-lighter:#363637;--el-border-color-extra-light:#2B2B2C;--el-fill-color-darker:#424243;--el-fill-color-dark:#39393A;--el-fill-color:#303030;--el-fill-color-light:#262727;--el-fill-color-lighter:#1D1D1D;--el-fill-color-extra-light:#191919;--el-fill-color-blank:transparent;--el-mask-color:rgba(0, 0, 0, 0.8);--el-mask-color-extra-light:rgba(0, 0, 0, 0.3)}html.dark .el-button{--el-button-disabled-text-color:rgba(255, 255, 255, 0.5)}html.dark .el-card{--el-card-bg-color:var(--el-bg-color-overlay)}html.dark .el-empty{--el-empty-fill-color-0:var(--el-color-black);--el-empty-fill-color-1:#4b4b52;--el-empty-fill-color-2:#36383d;--el-empty-fill-color-3:#1e1e20;--el-empty-fill-color-4:#262629;--el-empty-fill-color-5:#202124;--el-empty-fill-color-6:#212224;--el-empty-fill-color-7:#1b1c1f;--el-empty-fill-color-8:#1c1d1f;--el-empty-fill-color-9:#18181a}

881
code/WebApp/vanilla/lib/github-markdown-css/github-markdown.min.css

@ -0,0 +1,881 @@
/**
* Minified by jsDelivr using clean-css v5.2.2.
* Original file: /npm/github-markdown-css@5.1.0/github-markdown.css
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
.markdown-body {
color-scheme: light;
--color-prettylights-syntax-comment: #6e7781;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #8250df;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #24292f;
--color-fg-muted: #57606a;
--color-fg-subtle: #6e7781;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210, 18%, 87%, 1);
--color-neutral-muted: rgba(175, 184, 193, 0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #cf222e;
}
html.dark .markdown-body {
color-scheme: dark;
--color-prettylights-syntax-comment: #8b949e;
--color-prettylights-syntax-constant: #79c0ff;
--color-prettylights-syntax-entity: #d2a8ff;
--color-prettylights-syntax-storage-modifier-import: #c9d1d9;
--color-prettylights-syntax-entity-tag: #7ee787;
--color-prettylights-syntax-keyword: #ff7b72;
--color-prettylights-syntax-string: #a5d6ff;
--color-prettylights-syntax-variable: #ffa657;
--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
--color-prettylights-syntax-invalid-illegal-bg: #8e1519;
--color-prettylights-syntax-carriage-return-text: #f0f6fc;
--color-prettylights-syntax-carriage-return-bg: #b62324;
--color-prettylights-syntax-string-regexp: #7ee787;
--color-prettylights-syntax-markup-list: #f2cc60;
--color-prettylights-syntax-markup-heading: #1f6feb;
--color-prettylights-syntax-markup-italic: #c9d1d9;
--color-prettylights-syntax-markup-bold: #c9d1d9;
--color-prettylights-syntax-markup-deleted-text: #ffdcd7;
--color-prettylights-syntax-markup-deleted-bg: #67060c;
--color-prettylights-syntax-markup-inserted-text: #aff5b4;
--color-prettylights-syntax-markup-inserted-bg: #033a16;
--color-prettylights-syntax-markup-changed-text: #ffdfb6;
--color-prettylights-syntax-markup-changed-bg: #5a1e02;
--color-prettylights-syntax-markup-ignored-text: #c9d1d9;
--color-prettylights-syntax-markup-ignored-bg: #1158c7;
--color-prettylights-syntax-meta-diff-range: #d2a8ff;
--color-prettylights-syntax-brackethighlighter-angle: #8b949e;
--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
--color-fg-default: #c9d1d9;
--color-fg-muted: #8b949e;
--color-fg-subtle: #484f58;
--color-canvas-default: #0d1117;
--color-canvas-subtle: #161b22;
--color-border-default: #30363d;
--color-border-muted: #21262d;
--color-neutral-muted: rgba(110, 118, 129, 0.4);
--color-accent-fg: #58a6ff;
--color-accent-emphasis: #1f6feb;
--color-attention-subtle: rgba(187, 128, 9, 0.15);
--color-danger-fg: #f85149;
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
margin: 0;
color: var(--color-fg-default);
background-color: var(--color-canvas-default);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji";
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.markdown-body .octicon {
display: inline-block;
fill: currentColor;
vertical-align: text-bottom;
}
.markdown-body h1:hover .anchor .octicon-link:before,
.markdown-body h2:hover .anchor .octicon-link:before,
.markdown-body h3:hover .anchor .octicon-link:before,
.markdown-body h4:hover .anchor .octicon-link:before,
.markdown-body h5:hover .anchor .octicon-link:before,
.markdown-body h6:hover .anchor .octicon-link:before {
width: 16px;
height: 16px;
content: " ";
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}
.markdown-body details,
.markdown-body figcaption,
.markdown-body figure {
display: block;
}
.markdown-body summary {
display: list-item;
}
.markdown-body [hidden] {
display: none !important;
}
.markdown-body a {
background-color: transparent;
color: var(--color-accent-fg);
text-decoration: none;
}
.markdown-body a:active,
.markdown-body a:hover {
outline-width: 0;
}
.markdown-body abbr[title] {
border-bottom: none;
text-decoration: underline dotted;
}
.markdown-body b,
.markdown-body strong {
font-weight: 600;
}
.markdown-body dfn {
font-style: italic;
}
.markdown-body h1 {
margin: 0.67em 0;
font-weight: 600;
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid var(--color-border-muted);
}
.markdown-body mark {
background-color: var(--color-attention-subtle);
color: var(--color-text-primary);
}
.markdown-body small {
font-size: 90%;
}
.markdown-body sub,
.markdown-body sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.markdown-body sub {
bottom: -0.25em;
}
.markdown-body sup {
top: -0.5em;
}
.markdown-body img {
border-style: none;
max-width: 100%;
box-sizing: content-box;
background-color: var(--color-canvas-default);
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre,
.markdown-body samp {
font-family: monospace, monospace;
font-size: 1em;
}
.markdown-body figure {
margin: 1em 40px;
}
.markdown-body hr {
box-sizing: content-box;
overflow: hidden;
background: 0 0;
border-bottom: 1px solid var(--color-border-muted);
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: var(--color-border-default);
border: 0;
}
.markdown-body input {
font: inherit;
margin: 0;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body [type="button"],
.markdown-body [type="reset"],
.markdown-body [type="submit"] {
-webkit-appearance: button;
}
.markdown-body [type="button"]::-moz-focus-inner,
.markdown-body [type="reset"]::-moz-focus-inner,
.markdown-body [type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
.markdown-body [type="button"]:-moz-focusring,
.markdown-body [type="reset"]:-moz-focusring,
.markdown-body [type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
.markdown-body [type="checkbox"],
.markdown-body [type="radio"] {
box-sizing: border-box;
padding: 0;
}
.markdown-body [type="number"]::-webkit-inner-spin-button,
.markdown-body [type="number"]::-webkit-outer-spin-button {
height: auto;
}
.markdown-body [type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
.markdown-body [type="search"]::-webkit-search-cancel-button,
.markdown-body [type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
.markdown-body ::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
.markdown-body ::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
display: block;
width: max-content;
max-width: 100%;
overflow: auto;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body details summary {
cursor: pointer;
}
.markdown-body details:not([open]) > :not(summary) {
display: none !important;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
line-height: 10px;
color: var(--color-fg-default);
vertical-align: middle;
background-color: var(--color-canvas-subtle);
border: solid 1px var(--color-neutral-muted);
border-bottom-color: var(--color-neutral-muted);
border-radius: 6px;
box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.markdown-body h2 {
font-weight: 600;
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid var(--color-border-muted);
}
.markdown-body h3 {
font-weight: 600;
font-size: 1.25em;
}
.markdown-body h4 {
font-weight: 600;
font-size: 1em;
}
.markdown-body h5 {
font-weight: 600;
font-size: 0.875em;
}
.markdown-body h6 {
font-weight: 600;
font-size: 0.85em;
color: var(--color-fg-muted);
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
padding: 0 1em;
color: var(--color-fg-muted);
border-left: 0.25em solid var(--color-border-default);
}
.markdown-body ol,
.markdown-body ul {
margin-top: 0;
margin-bottom: 0;
padding-left: 2em;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ol ol ol,
.markdown-body ol ul ol,
.markdown-body ul ol ol,
.markdown-body ul ul ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body code,
.markdown-body tt {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
font-size: 12px;
word-wrap: normal;
}
.markdown-body .octicon {
display: inline-block;
overflow: visible !important;
vertical-align: text-bottom;
fill: currentColor;
}
.markdown-body ::placeholder {
color: var(--color-fg-subtle);
opacity: 1;
}
.markdown-body input::-webkit-inner-spin-button,
.markdown-body input::-webkit-outer-spin-button {
margin: 0;
-webkit-appearance: none;
appearance: none;
}
.markdown-body .pl-c {
color: var(--color-prettylights-syntax-comment);
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: var(--color-prettylights-syntax-constant);
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: var(--color-prettylights-syntax-entity);
}
.markdown-body .pl-s .pl-s1,
.markdown-body .pl-smi {
color: var(--color-prettylights-syntax-storage-modifier-import);
}
.markdown-body .pl-ent {
color: var(--color-prettylights-syntax-entity-tag);
}
.markdown-body .pl-k {
color: var(--color-prettylights-syntax-keyword);
}
.markdown-body .pl-pds,
.markdown-body .pl-s,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sra,
.markdown-body .pl-sr .pl-sre {
color: var(--color-prettylights-syntax-string);
}
.markdown-body .pl-smw,
.markdown-body .pl-v {
color: var(--color-prettylights-syntax-variable);
}
.markdown-body .pl-bu {
color: var(--color-prettylights-syntax-brackethighlighter-unmatched);
}
.markdown-body .pl-ii {
color: var(--color-prettylights-syntax-invalid-illegal-text);
background-color: var(--color-prettylights-syntax-invalid-illegal-bg);
}
.markdown-body .pl-c2 {
color: var(--color-prettylights-syntax-carriage-return-text);
background-color: var(--color-prettylights-syntax-carriage-return-bg);
}
.markdown-body .pl-sr .pl-cce {
font-weight: 700;
color: var(--color-prettylights-syntax-string-regexp);
}
.markdown-body .pl-ml {
color: var(--color-prettylights-syntax-markup-list);
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: 700;
color: var(--color-prettylights-syntax-markup-heading);
}
.markdown-body .pl-mi {
font-style: italic;
color: var(--color-prettylights-syntax-markup-italic);
}
.markdown-body .pl-mb {
font-weight: 700;
color: var(--color-prettylights-syntax-markup-bold);
}
.markdown-body .pl-md {
color: var(--color-prettylights-syntax-markup-deleted-text);
background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}
.markdown-body .pl-mi1 {
color: var(--color-prettylights-syntax-markup-inserted-text);
background-color: var(--color-prettylights-syntax-markup-inserted-bg);
}
.markdown-body .pl-mc {
color: var(--color-prettylights-syntax-markup-changed-text);
background-color: var(--color-prettylights-syntax-markup-changed-bg);
}
.markdown-body .pl-mi2 {
color: var(--color-prettylights-syntax-markup-ignored-text);
background-color: var(--color-prettylights-syntax-markup-ignored-bg);
}
.markdown-body .pl-mdr {
font-weight: 700;
color: var(--color-prettylights-syntax-meta-diff-range);
}
.markdown-body .pl-ba {
color: var(--color-prettylights-syntax-brackethighlighter-angle);
}
.markdown-body .pl-sg {
color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);
}
.markdown-body .pl-corl {
text-decoration: underline;
color: var(--color-prettylights-syntax-constant-other-reference-link);
}
.markdown-body [data-catalyst] {
display: block;
}
.markdown-body g-emoji {
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1em;
font-style: normal !important;
font-weight: 400;
line-height: 1;
vertical-align: -0.075em;
}
.markdown-body g-emoji img {
width: 1em;
height: 1em;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body > :first-child {
margin-top: 0 !important;
}
.markdown-body > :last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .absent {
color: var(--color-danger-fg);
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: 0;
}
.markdown-body blockquote,
.markdown-body details,
.markdown-body dl,
.markdown-body ol,
.markdown-body p,
.markdown-body pre,
.markdown-body table,
.markdown-body ul {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body blockquote > :first-child {
margin-top: 0;
}
.markdown-body blockquote > :last-child {
margin-bottom: 0;
}
.markdown-body sup > a::before {
content: "[";
}
.markdown-body sup > a::after {
content: "]";
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: var(--color-fg-default);
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 code,
.markdown-body h1 tt,
.markdown-body h2 code,
.markdown-body h2 tt,
.markdown-body h3 code,
.markdown-body h3 tt,
.markdown-body h4 code,
.markdown-body h4 tt,
.markdown-body h5 code,
.markdown-body h5 tt,
.markdown-body h6 code,
.markdown-body h6 tt {
padding: 0 0.2em;
font-size: inherit;
}
.markdown-body ol.no-list,
.markdown-body ul.no-list {
padding: 0;
list-style-type: none;
}
.markdown-body ol[type="1"] {
list-style-type: decimal;
}
.markdown-body ol[type="a"] {
list-style-type: lower-alpha;
}
.markdown-body ol[type="i"] {
list-style-type: lower-roman;
}
.markdown-body div > ol:not([type]) {
list-style-type: decimal;
}
.markdown-body ol ol,
.markdown-body ol ul,
.markdown-body ul ol,
.markdown-body ul ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li > p {
margin-top: 16px;
}
.markdown-body li + li {
margin-top: 0.25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table th {
font-weight: 600;
}
.markdown-body table td,
.markdown-body table th {
padding: 6px 13px;
border: 1px solid var(--color-border-default);
}
.markdown-body table tr {
background-color: var(--color-canvas-default);
border-top: 1px solid var(--color-border-muted);
}
.markdown-body table tr:nth-child(2n) {
background-color: var(--color-canvas-subtle);
}
.markdown-body table img {
background-color: transparent;
}
.markdown-body img[align="right"] {
padding-left: 20px;
}
.markdown-body img[align="left"] {
padding-right: 20px;
}
.markdown-body .emoji {
max-width: none;
vertical-align: text-top;
background-color: transparent;
}
.markdown-body span.frame {
display: block;
overflow: hidden;
}
.markdown-body span.frame > span {
display: block;
float: left;
width: auto;
padding: 7px;
margin: 13px 0 0;
overflow: hidden;
border: 1px solid var(--color-border-default);
}
.markdown-body span.frame span img {
display: block;
float: left;
}
.markdown-body span.frame span span {
display: block;
padding: 5px 0 0;
clear: both;
color: var(--color-fg-default);
}
.markdown-body span.align-center {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-center > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown-body span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown-body span.align-right {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-right > span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown-body span.align-right span img {
margin: 0;
text-align: right;
}
.markdown-body span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown-body span.float-left span {
margin: 13px 0 0;
}
.markdown-body span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown-body span.float-right > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown-body code,
.markdown-body tt {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: var(--color-neutral-muted);
border-radius: 6px;
}
.markdown-body code br,
.markdown-body tt br {
display: none;
}
.markdown-body del code {
text-decoration: inherit;
}
.markdown-body pre code {
font-size: 100%;
}
.markdown-body pre > code {
padding: 0;
margin: 0;
word-break: normal;
white-space: pre;
background: 0 0;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: var(--color-canvas-subtle);
border-radius: 6px;
}
.markdown-body pre code,
.markdown-body pre tt {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body .csv-data td,
.markdown-body .csv-data th {
padding: 5px;
overflow: hidden;
font-size: 12px;
line-height: 1;
text-align: left;
white-space: nowrap;
}
.markdown-body .csv-data .blob-num {
padding: 10px 8px 9px;
text-align: right;
background: var(--color-canvas-default);
border: 0;
}
.markdown-body .csv-data tr {
border-top: 0;
}
.markdown-body .csv-data th {
font-weight: 600;
background: var(--color-canvas-subtle);
border-top: 0;
}
.markdown-body .footnotes {
font-size: 12px;
color: var(--color-fg-muted);
border-top: 1px solid var(--color-border-default);
}
.markdown-body .footnotes ol {
padding-left: 16px;
}
.markdown-body .footnotes li {
position: relative;
}
.markdown-body .footnotes li:target::before {
position: absolute;
top: -8px;
right: -8px;
bottom: -8px;
left: -24px;
pointer-events: none;
content: "";
border: 2px solid var(--color-accent-emphasis);
border-radius: 6px;
}
.markdown-body .footnotes li:target {
color: var(--color-fg-default);
}
.markdown-body .footnotes .data-footnote-backref g-emoji {
font-family: monospace;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item label {
font-weight: 400;
}
.markdown-body .task-list-item.enabled label {
cursor: pointer;
}
.markdown-body .task-list-item + .task-list-item {
margin-top: 3px;
}
.markdown-body .task-list-item .handle {
display: none;
}
.markdown-body .task-list-item-checkbox {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {
margin: 0 -1.6em 0.25em 0.2em;
}
.markdown-body ::-webkit-calendar-picker-indicator {
filter: invert(50%);
}
/*# sourceMappingURL=/sm/64d6754651b08011e56029ac6df83bb47c5a570dab69e24e289f71fd97eb927d.map */

111
code/WebApp/vanilla/lib/highlightjs/highlight.css

@ -0,0 +1,111 @@
/*!
Theme: Default
Description: Original highlight.js style
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
Maintainer: @highlightjs/core-team
Website: https://highlightjs.org/
License: see project LICENSE
Touched: 2021
*/
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
--color: #444;
--background: #f3f3f3;
--comment: #697070;
--tag: #444a;
--tag-attr: #444;
--type: #800;
--title: #800;
--link: #ab5656;
--literal: #695;
--code: #397300;
--meta: #1f7199;
--meta-string: #38a;
}
body.dark .hljs {
--color: var(--dark-text-color);
--background: var(--dark-bg-color);
--keyword: #fff;
--comment: #979797;
--tag: #fff;
--type: #d88;
}
.hljs {
background: var(--background);
color: var(--color);
}
.hljs-comment {
color: var(--comment);
}
.hljs-punctuation,
.hljs-tag {
color: var(--tag);
}
.hljs-tag .hljs-attr,
.hljs-tag .hljs-name {
color: var(--tag-attr);
}
.hljs-attribute,
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-name,
.hljs-selector-tag {
font-weight: 700;
}
.hljs-deletion,
.hljs-number,
.hljs-quote,
.hljs-selector-class,
.hljs-selector-id,
.hljs-string,
.hljs-template-tag,
.hljs-type {
color: var(--type);
}
.hljs-section,
.hljs-title {
color: var(--title);
font-weight: 700;
}
.hljs-link,
.hljs-operator,
.hljs-regexp,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-symbol,
.hljs-template-variable,
.hljs-variable {
color: var(--link);
}
.hljs-literal {
color: var(--literal);
}
.hljs-addition,
.hljs-built_in,
.hljs-bullet,
.hljs-code {
color: var(--code);
}
.hljs-meta {
color: var(--meta);
}
.hljs-meta .hljs-string {
color: var(--meta-string);
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}

1174
code/WebApp/vanilla/lib/highlightjs/highlight.min.js

File diff suppressed because one or more lines are too long

2
code/WebApp/vanilla/lib/jwt-decode/jwt-decode.esm.js

@ -0,0 +1,2 @@
function e(e){this.message=e}e.prototype=new Error,e.prototype.name="InvalidCharacterError";var r="undefined"!=typeof window&&window.atob&&window.atob.bind(window)||function(r){var t=String(r).replace(/=+$/,"");if(t.length%4==1)throw new e("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,o,a=0,i=0,c="";o=t.charAt(i++);~o&&(n=a%4?64*n+o:o,a++%4)?c+=String.fromCharCode(255&n>>(-2*a&6)):0)o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(o);return c};function t(e){var t=e.replace(/-/g,"+").replace(/_/g,"/");switch(t.length%4){case 0:break;case 2:t+="==";break;case 3:t+="=";break;default:throw"Illegal base64url string!"}try{return function(e){return decodeURIComponent(r(e).replace(/(.)/g,(function(e,r){var t=r.charCodeAt(0).toString(16).toUpperCase();return t.length<2&&(t="0"+t),"%"+t})))}(t)}catch(e){return r(t)}}function n(e){this.message=e}function o(e,r){if("string"!=typeof e)throw new n("Invalid token specified");var o=!0===(r=r||{}).header?0:1;try{return JSON.parse(t(e.split(".")[o]))}catch(e){throw new n("Invalid token specified: "+e.message)}}n.prototype=new Error,n.prototype.name="InvalidTokenError";export default o;export{n as InvalidTokenError};
//# sourceMappingURL=jwt-decode.esm.js.map

1
code/WebApp/vanilla/lib/linq/linq.min.js

File diff suppressed because one or more lines are too long

18
code/WebApp/vanilla/lib/lodash/lodash.esm.js

File diff suppressed because one or more lines are too long

2778
code/WebApp/vanilla/lib/marked/marked.esm.js

File diff suppressed because it is too large

3
code/WebApp/vanilla/lib/mermaid/mermaid.esm.min.mjs

File diff suppressed because one or more lines are too long

74
code/WebApp/vanilla/lib/nprogress/nprogress.css

@ -0,0 +1,74 @@
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: #29d;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: #29d;
border-left-color: #29d;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

288
code/WebApp/vanilla/lib/nprogress/nprogress.vite-esm.js

@ -0,0 +1,288 @@
const __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[Object.getOwnPropertyNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// node_modules/nprogress/nprogress.js
var require_nprogress = __commonJS({
"node_modules/nprogress/nprogress.js"(exports, module) {
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
module.exports = factory();
} else {
root.NProgress = factory();
}
})(exports, function () {
var NProgress = {};
NProgress.version = "0.2.0";
var Settings = NProgress.settings = {
minimum: 0.08,
easing: "ease",
positionUsing: "",
speed: 200,
trickle: true,
trickleRate: 0.02,
trickleSpeed: 800,
showSpinner: true,
barSelector: '[role="bar"]',
spinnerSelector: '[role="spinner"]',
parent: "body",
template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'
};
NProgress.configure = function (options) {
var key, value;
for (key in options) {
value = options[key];
if (value !== void 0 && options.hasOwnProperty(key))
Settings[key] = value;
}
return this;
};
NProgress.status = null;
NProgress.set = function (n) {
var started = NProgress.isStarted();
n = clamp(n, Settings.minimum, 1);
NProgress.status = n === 1 ? null : n;
var progress = NProgress.render(!started), bar = progress.querySelector(Settings.barSelector), speed = Settings.speed, ease = Settings.easing;
progress.offsetWidth;
queue(function (next) {
if (Settings.positionUsing === "")
Settings.positionUsing = NProgress.getPositioningCSS();
css(bar, barPositionCSS(n, speed, ease));
if (n === 1) {
css(progress, {
transition: "none",
opacity: 1
});
progress.offsetWidth;
setTimeout(function () {
css(progress, {
transition: "all " + speed + "ms linear",
opacity: 0
});
setTimeout(function () {
NProgress.remove();
next();
}, speed);
}, speed);
} else {
setTimeout(next, speed);
}
});
return this;
};
NProgress.isStarted = function () {
return typeof NProgress.status === "number";
};
NProgress.start = function () {
if (!NProgress.status)
NProgress.set(0);
var work = function () {
setTimeout(function () {
if (!NProgress.status)
return;
NProgress.trickle();
work();
}, Settings.trickleSpeed);
};
if (Settings.trickle)
work();
return this;
};
NProgress.done = function (force) {
if (!force && !NProgress.status)
return this;
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
};
NProgress.inc = function (amount) {
var n = NProgress.status;
if (!n) {
return NProgress.start();
} else {
if (typeof amount !== "number") {
amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
}
n = clamp(n + amount, 0, 0.994);
return NProgress.set(n);
}
};
NProgress.trickle = function () {
return NProgress.inc(Math.random() * Settings.trickleRate);
};
(function () {
var initial = 0, current = 0;
NProgress.promise = function ($promise) {
if (!$promise || $promise.state() === "resolved") {
return this;
}
if (current === 0) {
NProgress.start();
}
initial++;
current++;
$promise.always(function () {
current--;
if (current === 0) {
initial = 0;
NProgress.done();
} else {
NProgress.set((initial - current) / initial);
}
});
return this;
};
})();
NProgress.render = function (fromStart) {
if (NProgress.isRendered())
return document.getElementById("nprogress");
addClass(document.documentElement, "nprogress-busy");
var progress = document.createElement("div");
progress.id = "nprogress";
progress.innerHTML = Settings.template;
var bar = progress.querySelector(Settings.barSelector), perc = fromStart ? "-100" : toBarPerc(NProgress.status || 0), parent = document.querySelector(Settings.parent), spinner;
css(bar, {
transition: "all 0 linear",
transform: "translate3d(" + perc + "%,0,0)"
});
if (!Settings.showSpinner) {
spinner = progress.querySelector(Settings.spinnerSelector);
spinner && removeElement(spinner);
}
if (parent != document.body) {
addClass(parent, "nprogress-custom-parent");
}
parent.appendChild(progress);
return progress;
};
NProgress.remove = function () {
removeClass(document.documentElement, "nprogress-busy");
removeClass(document.querySelector(Settings.parent), "nprogress-custom-parent");
var progress = document.getElementById("nprogress");
progress && removeElement(progress);
};
NProgress.isRendered = function () {
return !!document.getElementById("nprogress");
};
NProgress.getPositioningCSS = function () {
var bodyStyle = document.body.style;
var vendorPrefix = "WebkitTransform" in bodyStyle ? "Webkit" : "MozTransform" in bodyStyle ? "Moz" : "msTransform" in bodyStyle ? "ms" : "OTransform" in bodyStyle ? "O" : "";
if (vendorPrefix + "Perspective" in bodyStyle) {
return "translate3d";
} else if (vendorPrefix + "Transform" in bodyStyle) {
return "translate";
} else {
return "margin";
}
};
function clamp(n, min, max) {
if (n < min)
return min;
if (n > max)
return max;
return n;
}
function toBarPerc(n) {
return (-1 + n) * 100;
}
function barPositionCSS(n, speed, ease) {
var barCSS;
if (Settings.positionUsing === "translate3d") {
barCSS = { transform: "translate3d(" + toBarPerc(n) + "%,0,0)" };
} else if (Settings.positionUsing === "translate") {
barCSS = { transform: "translate(" + toBarPerc(n) + "%,0)" };
} else {
barCSS = { "margin-left": toBarPerc(n) + "%" };
}
barCSS.transition = "all " + speed + "ms " + ease;
return barCSS;
}
var queue = function () {
var pending = [];
function next() {
var fn = pending.shift();
if (fn) {
fn(next);
}
}
return function (fn) {
pending.push(fn);
if (pending.length == 1)
next();
};
}();
var css = function () {
var cssPrefixes = ["Webkit", "O", "Moz", "ms"], cssProps = {};
function camelCase(string) {
return string.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (match, letter) {
return letter.toUpperCase();
});
}
function getVendorProp(name) {
var style = document.body.style;
if (name in style)
return name;
var i = cssPrefixes.length, capName = name.charAt(0).toUpperCase() + name.slice(1), vendorName;
while (i--) {
vendorName = cssPrefixes[i] + capName;
if (vendorName in style)
return vendorName;
}
return name;
}
function getStyleProp(name) {
name = camelCase(name);
return cssProps[name] || (cssProps[name] = getVendorProp(name));
}
function applyCss(element, prop, value) {
prop = getStyleProp(prop);
element.style[prop] = value;
}
return function (element, properties) {
var args = arguments, prop, value;
if (args.length == 2) {
for (prop in properties) {
value = properties[prop];
if (value !== void 0 && properties.hasOwnProperty(prop))
applyCss(element, prop, value);
}
} else {
applyCss(element, args[1], args[2]);
}
};
}();
function hasClass(element, name) {
var list = typeof element == "string" ? element : classList(element);
return list.indexOf(" " + name + " ") >= 0;
}
function addClass(element, name) {
var oldList = classList(element), newList = oldList + name;
if (hasClass(oldList, name))
return;
element.className = newList.substring(1);
}
function removeClass(element, name) {
var oldList = classList(element), newList;
if (!hasClass(element, name))
return;
newList = oldList.replace(" " + name + " ", " ");
element.className = newList.substring(1, newList.length - 1);
}
function classList(element) {
return (" " + (element.className || "") + " ").replace(/\s+/gi, " ");
}
function removeElement(element) {
element && element.parentNode && element.parentNode.removeChild(element);
}
return NProgress;
});
}
});
export default require_nprogress();
/*! Bundled license information:
nprogress/nprogress.js:
(* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
* @license MIT *)
*/

1987
code/WebApp/vanilla/lib/pinia/pinia.esm-browser.js

File diff suppressed because it is too large

206
code/WebApp/vanilla/lib/pubsub-js/pubsub.esm.js

@ -0,0 +1,206 @@
const __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[Object.getOwnPropertyNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// node_modules/pubsub-js/src/pubsub.js
var require_pubsub = __commonJS({
"node_modules/pubsub-js/src/pubsub.js"(exports, module) {
(function(root, factory) {
"use strict";
var PubSub = {};
if (root.PubSub) {
PubSub = root.PubSub;
console.warn("PubSub already loaded, using existing version");
} else {
root.PubSub = PubSub;
factory(PubSub);
}
if (typeof exports === "object") {
if (module !== void 0 && module.exports) {
exports = module.exports = PubSub;
}
exports.PubSub = PubSub;
module.exports = exports = PubSub;
} else if (typeof define === "function" && define.amd) {
define(function() {
return PubSub;
});
}
})(typeof window === "object" && window || exports, function(PubSub) {
"use strict";
var messages = {}, lastUid = -1, ALL_SUBSCRIBING_MSG = "*";
function hasKeys(obj) {
var key;
for (key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return true;
}
}
return false;
}
function throwException(ex) {
return function reThrowException() {
throw ex;
};
}
function callSubscriberWithDelayedExceptions(subscriber, message, data) {
try {
subscriber(message, data);
} catch (ex) {
setTimeout(throwException(ex), 0);
}
}
function callSubscriberWithImmediateExceptions(subscriber, message, data) {
subscriber(message, data);
}
function deliverMessage(originalMessage, matchedMessage, data, immediateExceptions) {
var subscribers = messages[matchedMessage], callSubscriber = immediateExceptions ? callSubscriberWithImmediateExceptions : callSubscriberWithDelayedExceptions, s;
if (!Object.prototype.hasOwnProperty.call(messages, matchedMessage)) {
return;
}
for (s in subscribers) {
if (Object.prototype.hasOwnProperty.call(subscribers, s)) {
callSubscriber(subscribers[s], originalMessage, data);
}
}
}
function createDeliveryFunction(message, data, immediateExceptions) {
return function deliverNamespaced() {
var topic = String(message), position = topic.lastIndexOf(".");
deliverMessage(message, message, data, immediateExceptions);
while (position !== -1) {
topic = topic.substr(0, position);
position = topic.lastIndexOf(".");
deliverMessage(message, topic, data, immediateExceptions);
}
deliverMessage(message, ALL_SUBSCRIBING_MSG, data, immediateExceptions);
};
}
function hasDirectSubscribersFor(message) {
var topic = String(message), found = Boolean(Object.prototype.hasOwnProperty.call(messages, topic) && hasKeys(messages[topic]));
return found;
}
function messageHasSubscribers(message) {
var topic = String(message), found = hasDirectSubscribersFor(topic) || hasDirectSubscribersFor(ALL_SUBSCRIBING_MSG), position = topic.lastIndexOf(".");
while (!found && position !== -1) {
topic = topic.substr(0, position);
position = topic.lastIndexOf(".");
found = hasDirectSubscribersFor(topic);
}
return found;
}
function publish(message, data, sync, immediateExceptions) {
message = typeof message === "symbol" ? message.toString() : message;
var deliver = createDeliveryFunction(message, data, immediateExceptions), hasSubscribers = messageHasSubscribers(message);
if (!hasSubscribers) {
return false;
}
if (sync === true) {
deliver();
} else {
setTimeout(deliver, 0);
}
return true;
}
PubSub.publish = function(message, data) {
return publish(message, data, false, PubSub.immediateExceptions);
};
PubSub.publishSync = function(message, data) {
return publish(message, data, true, PubSub.immediateExceptions);
};
PubSub.subscribe = function(message, func) {
if (typeof func !== "function") {
return false;
}
message = typeof message === "symbol" ? message.toString() : message;
if (!Object.prototype.hasOwnProperty.call(messages, message)) {
messages[message] = {};
}
var token = "uid_" + String(++lastUid);
messages[message][token] = func;
return token;
};
PubSub.subscribeAll = function(func) {
return PubSub.subscribe(ALL_SUBSCRIBING_MSG, func);
};
PubSub.subscribeOnce = function(message, func) {
var token = PubSub.subscribe(message, function() {
PubSub.unsubscribe(token);
func.apply(this, arguments);
});
return PubSub;
};
PubSub.clearAllSubscriptions = function clearAllSubscriptions() {
messages = {};
};
PubSub.clearSubscriptions = function clearSubscriptions(topic) {
var m;
for (m in messages) {
if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0) {
delete messages[m];
}
}
};
PubSub.countSubscriptions = function countSubscriptions(topic) {
var m;
var token;
var count = 0;
for (m in messages) {
if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0) {
for (token in messages[m]) {
count++;
}
break;
}
}
return count;
};
PubSub.getSubscriptions = function getSubscriptions(topic) {
var m;
var list = [];
for (m in messages) {
if (Object.prototype.hasOwnProperty.call(messages, m) && m.indexOf(topic) === 0) {
list.push(m);
}
}
return list;
};
PubSub.unsubscribe = function(value) {
var descendantTopicExists = function(topic) {
var m2;
for (m2 in messages) {
if (Object.prototype.hasOwnProperty.call(messages, m2) && m2.indexOf(topic) === 0) {
return true;
}
}
return false;
}, isTopic = typeof value === "string" && (Object.prototype.hasOwnProperty.call(messages, value) || descendantTopicExists(value)), isToken = !isTopic && typeof value === "string", isFunction = typeof value === "function", result = false, m, message, t;
if (isTopic) {
PubSub.clearSubscriptions(value);
return;
}
for (m in messages) {
if (Object.prototype.hasOwnProperty.call(messages, m)) {
message = messages[m];
if (isToken && message[value]) {
delete message[value];
result = value;
break;
}
if (isFunction) {
for (t in message) {
if (Object.prototype.hasOwnProperty.call(message, t) && message[t] === value) {
delete message[t];
result = true;
}
}
}
}
}
return result;
};
});
}
});
export default require_pubsub();

1837
code/WebApp/vanilla/lib/qs/shim.js

File diff suppressed because it is too large

314
code/WebApp/vanilla/lib/resize-detector/index.js

@ -0,0 +1,314 @@
var raf = null;
function requestAnimationFrame (callback) {
if (!raf) {
raf = (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
return setTimeout(callback, 16)
}
).bind(window);
}
return raf(callback)
}
var caf = null;
function cancelAnimationFrame (id) {
if (!caf) {
caf = (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function (id) {
clearTimeout(id);
}
).bind(window);
}
caf(id);
}
function createStyles (styleText) {
var style = document.createElement('style');
if (style.styleSheet) {
style.styleSheet.cssText = styleText;
} else {
style.appendChild(document.createTextNode(styleText));
}
(document.querySelector('head') || document.body).appendChild(style);
return style
}
function createElement (tagName, props) {
if ( props === void 0 ) props = {};
var elem = document.createElement(tagName);
Object.keys(props).forEach(function (key) {
elem[key] = props[key];
});
return elem
}
function getComputedStyle (elem, prop, pseudo) {
// for older versions of Firefox, `getComputedStyle` required
// the second argument and may return `null` for some elements
// when `display: none`
var computedStyle = window.getComputedStyle(elem, pseudo || null) || {
display: 'none'
};
return computedStyle[prop]
}
function getRenderInfo (elem) {
if (!document.documentElement.contains(elem)) {
return {
detached: true,
rendered: false
}
}
var current = elem;
while (current !== document) {
if (getComputedStyle(current, 'display') === 'none') {
return {
detached: false,
rendered: false
}
}
current = current.parentNode;
}
return {
detached: false,
rendered: true
}
}
var css_248z = ".resize-triggers{visibility:hidden;opacity:0;pointer-events:none}.resize-contract-trigger,.resize-contract-trigger:before,.resize-expand-trigger,.resize-triggers{content:\"\";position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden}.resize-contract-trigger,.resize-expand-trigger{background:#eee;overflow:auto}.resize-contract-trigger:before{width:200%;height:200%}";
var total = 0;
var style = null;
function addListener (elem, callback) {
if (!elem.__resize_mutation_handler__) {
elem.__resize_mutation_handler__ = handleMutation.bind(elem);
}
var listeners = elem.__resize_listeners__;
if (!listeners) {
elem.__resize_listeners__ = [];
if (window.ResizeObserver) {
var offsetWidth = elem.offsetWidth;
var offsetHeight = elem.offsetHeight;
var ro = new ResizeObserver(function () {
if (!elem.__resize_observer_triggered__) {
elem.__resize_observer_triggered__ = true;
if (elem.offsetWidth === offsetWidth && elem.offsetHeight === offsetHeight) {
return
}
}
runCallbacks(elem);
});
// initially display none won't trigger ResizeObserver callback
var ref = getRenderInfo(elem);
var detached = ref.detached;
var rendered = ref.rendered;
elem.__resize_observer_triggered__ = detached === false && rendered === false;
elem.__resize_observer__ = ro;
ro.observe(elem);
} else if (elem.attachEvent && elem.addEventListener) {
// targeting IE9/10
elem.__resize_legacy_resize_handler__ = function handleLegacyResize () {
runCallbacks(elem);
};
elem.attachEvent('onresize', elem.__resize_legacy_resize_handler__);
document.addEventListener('DOMSubtreeModified', elem.__resize_mutation_handler__);
} else {
if (!total) {
style = createStyles(css_248z);
}
initTriggers(elem);
elem.__resize_rendered__ = getRenderInfo(elem).rendered;
if (window.MutationObserver) {
var mo = new MutationObserver(elem.__resize_mutation_handler__);
mo.observe(document, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
elem.__resize_mutation_observer__ = mo;
}
}
}
elem.__resize_listeners__.push(callback);
total++;
}
function removeListener (elem, callback) {
var listeners = elem.__resize_listeners__;
if (!listeners) {
return
}
if (callback) {
listeners.splice(listeners.indexOf(callback), 1);
}
// no listeners exist, or removing all listeners
if (!listeners.length || !callback) {
// targeting IE9/10
if (elem.detachEvent && elem.removeEventListener) {
elem.detachEvent('onresize', elem.__resize_legacy_resize_handler__);
document.removeEventListener('DOMSubtreeModified', elem.__resize_mutation_handler__);
return
}
if (elem.__resize_observer__) {
elem.__resize_observer__.unobserve(elem);
elem.__resize_observer__.disconnect();
elem.__resize_observer__ = null;
} else {
if (elem.__resize_mutation_observer__) {
elem.__resize_mutation_observer__.disconnect();
elem.__resize_mutation_observer__ = null;
}
elem.removeEventListener('scroll', handleScroll);
elem.removeChild(elem.__resize_triggers__.triggers);
elem.__resize_triggers__ = null;
}
elem.__resize_listeners__ = null;
}
if (!--total && style) {
style.parentNode.removeChild(style);
}
}
function getUpdatedSize (elem) {
var ref = elem.__resize_last__;
var width = ref.width;
var height = ref.height;
var offsetWidth = elem.offsetWidth;
var offsetHeight = elem.offsetHeight;
if (offsetWidth !== width || offsetHeight !== height) {
return {
width: offsetWidth,
height: offsetHeight
}
}
return null
}
function handleMutation () {
// `this` denotes the scrolling element
var ref = getRenderInfo(this);
var rendered = ref.rendered;
var detached = ref.detached;
if (rendered !== this.__resize_rendered__) {
if (!detached && this.__resize_triggers__) {
resetTriggers(this);
this.addEventListener('scroll', handleScroll, true);
}
this.__resize_rendered__ = rendered;
runCallbacks(this);
}
}
function handleScroll () {
var this$1 = this;
// `this` denotes the scrolling element
resetTriggers(this);
if (this.__resize_raf__) {
cancelAnimationFrame(this.__resize_raf__);
}
this.__resize_raf__ = requestAnimationFrame(function () {
var updated = getUpdatedSize(this$1);
if (updated) {
this$1.__resize_last__ = updated;
runCallbacks(this$1);
}
});
}
function runCallbacks (elem) {
if (!elem || !elem.__resize_listeners__) {
return
}
elem.__resize_listeners__.forEach(function (callback) {
callback.call(elem, elem);
});
}
function initTriggers (elem) {
var position = getComputedStyle(elem, 'position');
if (!position || position === 'static') {
elem.style.position = 'relative';
}
elem.__resize_old_position__ = position;
elem.__resize_last__ = {};
var triggers = createElement('div', {
className: 'resize-triggers'
});
var expand = createElement('div', {
className: 'resize-expand-trigger'
});
var expandChild = createElement('div');
var contract = createElement('div', {
className: 'resize-contract-trigger'
});
expand.appendChild(expandChild);
triggers.appendChild(expand);
triggers.appendChild(contract);
elem.appendChild(triggers);
elem.__resize_triggers__ = {
triggers: triggers,
expand: expand,
expandChild: expandChild,
contract: contract
};
resetTriggers(elem);
elem.addEventListener('scroll', handleScroll, true);
elem.__resize_last__ = {
width: elem.offsetWidth,
height: elem.offsetHeight
};
}
function resetTriggers (elem) {
var ref = elem.__resize_triggers__;
var expand = ref.expand;
var expandChild = ref.expandChild;
var contract = ref.contract;
// batch read
var csw = contract.scrollWidth;
var csh = contract.scrollHeight;
var eow = expand.offsetWidth;
var eoh = expand.offsetHeight;
var esw = expand.scrollWidth;
var esh = expand.scrollHeight;
// batch write
contract.scrollLeft = csw;
contract.scrollTop = csh;
expandChild.style.width = eow + 1 + 'px';
expandChild.style.height = eoh + 1 + 'px';
expand.scrollLeft = esw;
expand.scrollTop = esh;
}
export { addListener, removeListener };

1
code/WebApp/vanilla/lib/tailwindcss/tailwind.min.css

File diff suppressed because one or more lines are too long

34
code/WebApp/vanilla/lib/vue-demi/shim.js

@ -0,0 +1,34 @@
import * as Vue from 'vue'
var isVue2 = false
var isVue3 = true
var Vue2 = undefined
function install() {}
export function set(target, key, val) {
if (Array.isArray(target)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
target[key] = val
return val
}
export function del(target, key) {
if (Array.isArray(target)) {
target.splice(key, 1)
return
}
delete target[key]
}
export * from 'vue'
export {
Vue,
Vue2,
isVue2,
isVue3,
install,
}

2
code/WebApp/vanilla/lib/vue-echarts/index.esm.min.js

@ -0,0 +1,2 @@
import{watch as e,inject as t,computed as n,unref as o,watchEffect as r,defineComponent as i,shallowRef as a,toRefs as u,onMounted as c,onUnmounted as s,h as l,Vue2 as p,nextTick as v}from"vue-demi";import{throttle as f,init as d}from"echarts/core";import{addListener as g,removeListener as h}from"resize-detector";var O=function(){return(O=Object.assign||function(e){for(var t,n=1,o=arguments.length;n<o;n++)for(var r in t=arguments[n])Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e}).apply(this,arguments)},m=["getWidth","getHeight","getDom","getOption","resize","dispatchAction","convertToPixel","convertFromPixel","containPixel","getDataURL","getConnectedDataURL","appendData","clear","isDisposed","dispose"];function b(e){return t=Object.create(null),m.forEach((function(n){t[n]=function(t){return function(){for(var n=[],o=0;o<arguments.length;o++)n[o]=arguments[o];if(!e.value)throw new Error("ECharts is not initialized yet.");return e.value[t].apply(e.value,n)}}(n)})),t;var t}var y={autoresize:Boolean},x="ecLoadingOptions";var j={loading:Boolean,loadingOptions:Object},E=[],z=[];!function(e,t){if(e&&"undefined"!=typeof document){var n,o=!0===t.prepend?"prepend":"append",r=!0===t.singleTag,i="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(r){var a=E.indexOf(i);-1===a&&(a=E.push(i)-1,z[a]={}),n=z[a]&&z[a][o]?z[a][o]:z[a][o]=u()}else n=u();65279===e.charCodeAt(0)&&(e=e.substring(1)),n.styleSheet?n.styleSheet.cssText+=e:n.appendChild(document.createTextNode(e))}function u(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var n=Object.keys(t.attributes),r=0;r<n.length;r++)e.setAttribute(n[r],t.attributes[n[r]]);var a="prepend"===o?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}("x-vue-echarts{display:block;width:100%;height:100%}",{});var A=/^on[^a-z]/,L=function(e){return A.test(e)};p&&p.config.ignoredElements.push("x-vue-echarts");var w="ecTheme",C="ecInitOptions",T="ecUpdateOptions",U=i({name:"echarts",props:O(O({option:Object,theme:{type:[Object,String]},initOptions:Object,updateOptions:Object,group:String,manualUpdate:Boolean},y),j),inheritAttrs:!1,setup:function(i,l){var p=l.attrs,m=l.listeners,y=a(),x=a(),j=a(),E=t("ecTheme",null),z=t("ecInitOptions",null),A=t("ecUpdateOptions",null),w=u(i),C=w.autoresize,T=w.manualUpdate,U=w.loading,D=w.loadingOptions,S=n((function(){return j.value||i.option||null})),k=n((function(){return i.theme||o(E)||{}})),B=n((function(){return i.initOptions||o(z)||{}})),P=n((function(){return i.updateOptions||o(A)||{}})),I=n((function(){return function(e){var t={};for(var n in e)L(n)||(t[n]=e[n]);return t}(p)}));function N(e){if(y.value){var t=x.value=d(y.value,k.value,B.value);i.group&&(t.group=i.group);var n=m;n||(n={},Object.keys(p).filter((function(e){return 0===e.indexOf("on")&&e.length>2})).forEach((function(e){var t=e.charAt(2).toLowerCase()+e.slice(3);n[t]=p[e]}))),Object.keys(n).forEach((function(e){var o=n[e];o&&(0===e.indexOf("zr:")?t.getZr().on(e.slice(3).toLowerCase(),o):t.on(e.toLowerCase(),o))})),C.value?v((function(){t&&!t.isDisposed()&&t.resize(),o()})):o()}function o(){var n=e||S.value;n&&t.setOption(n,P.value)}}function R(){x.value&&(x.value.dispose(),x.value=void 0)}var q=null;e(T,(function(t){"function"==typeof q&&(q(),q=null),t||(q=e((function(){return i.option}),(function(e,t){e&&(x.value?x.value.setOption(e,O({notMerge:e.value!==(null==t?void 0:t.value)},P.value)):N())}),{deep:!0}))}),{immediate:!0}),e([k,B],(function(){R(),N()}),{deep:!0}),r((function(){i.group&&x.value&&(x.value.group=i.group)}));var F=b(x);return function(e,i,a){var u=t("ecLoadingOptions",{}),c=n((function(){return O(O({},o(u)),null==a?void 0:a.value)}));r((function(){var t=e.value;t&&(i.value?t.showLoading(c.value):t.hideLoading())}))}(x,U,D),function(t,n,o){var r=null;e([o,t,n],(function(e,t,n){var o=e[0],i=e[1],a=e[2];o&&i&&a&&(r=f((function(){i.resize()}),100),g(o,r)),n((function(){r&&o&&h(o,r)}))}))}(x,C,y),c((function(){N()})),s(R),O({chart:x,root:y,setOption:function(e,t){i.manualUpdate&&(j.value=e),x.value?x.value.setOption(e,t||{}):N(e)},nonEventAttrs:I},F)},render:function(){var e=O({},this.nonEventAttrs);return e.ref="root",e.class=e.class?["echarts"].concat(e.class):"echarts",l("x-vue-echarts",e)}});export default U;export{C as INIT_OPTIONS_KEY,x as LOADING_OPTIONS_KEY,w as THEME_KEY,T as UPDATE_OPTIONS_KEY};
//# sourceMappingURL=index.esm.min.js.map

6
code/WebApp/vanilla/lib/vue-i18n/vue-i18n.esm-browser.prod.js

File diff suppressed because one or more lines are too long

3613
code/WebApp/vanilla/lib/vue-router/vue-router.esm-browser.js

File diff suppressed because it is too large

15377
code/WebApp/vanilla/lib/vue/vue.esm-browser.js

File diff suppressed because it is too large

1
code/WebApp/vanilla/lib/vue/vue.esm-browser.prod.js

File diff suppressed because one or more lines are too long

13
code/WebApp/vanilla/locale/index.js

@ -0,0 +1,13 @@
import { createI18n } from "vue-i18n";
import { useAppStore } from "../store/index.js";
function useLocale() {
const appStore = useAppStore();
const i18n = createI18n({
legacy: false,
...appStore.localization,
});
return i18n;
}
export default useLocale;

8
code/WebApp/vanilla/main.css

@ -0,0 +1,8 @@
@import url("./lib/nprogress/nprogress.css");
@import url("./lib/element-plus/index.css");
@import url("./lib/element-plus/theme-chalk/dark/css-vars.css");
@import url("./lib/github-markdown-css/github-markdown.min.css");
@import url("./lib/highlightjs/highlight.css");
@import url("./lib/@vue-office/excel/index.css");
@import url("./lib/tailwindcss/tailwind.min.css");
@import url("./styles/site.css");

20
code/WebApp/vanilla/main.js

@ -0,0 +1,20 @@
import { createApp } from "vue";
import style from './mixins/style.js';
import store, { useAppStore } from "./store/index.js";
import router from "./router/index.js";
import ElementPlus from "element-plus";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import App from "/app.js";
import useLocale from "./locale/index.js";
const app = createApp(App);
app.use(store);
await useAppStore().init();
app.use(useLocale());
app.use(router);
app.use(ElementPlus);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(`Ep${key}`, component);
}
app.mixin(style);
app.mount("#app");

60
code/WebApp/vanilla/mixins/style.js

@ -0,0 +1,60 @@
const styleCounterName = 'data-vue-component-instances';
function getStyleList(name) {
return document.querySelectorAll(`head style.${name},head link[rel='stylesheet'].${name}`);
}
function append(parent, html) {
parent.insertAdjacentHTML('beforeend', html);
}
function addStyles(name, styleSource) {
var styleList = getStyleList(name);
if (styleList.length > 0) {
for (let i = 0; i < styleList.length; i++) {
const style = styleList[i];
var counter = parseInt(style.getAttribute(styleCounterName));
style.setAttribute(styleCounterName, counter + 1);
}
} else {
const doc = new DOMParser().parseFromString(styleSource, 'text/html');
const styles = doc.querySelectorAll("style,link[rel='stylesheet']");
for (let i = 0; i < styles.length; i++) {
const style = styles[i];
style.setAttribute('class', name);
style.setAttribute(styleCounterName, 1);
append(document.head, style.outerHTML);
}
}
}
function removeStyles(name) {
var styleList = getStyleList(name);
if (styleList.length > 0) {
for (var i = 0; i < styleList.length; i++) {
var style = styleList[i];
var counter = parseInt(style.getAttribute(styleCounterName));
if (counter - 1 > 0) {
style.setAttribute(styleCounterName, counter - 1);
} else {
document.head.removeChild(style);
}
}
}
}
let id = 0;
export default {
created() {
if (this._.type.styles) {
if (!this._.type.uid) {
this._.type.uid = `uid_${id++}`;
}
addStyles(this._.type.uid, this._.type.styles);
}
},
unmounted() {
removeStyles(this._.type.uid);
},
};

90
code/WebApp/vanilla/request/index.js

@ -0,0 +1,90 @@
import qs from "../lib/qs/shim.js";
import { isLogin } from "../api/user.js";
import { useAppStore } from "../store/index.js";
import settings from "../config/settings.js";
const addToken = async (options) => {
if (await isLogin()) {
const appStore = useAppStore();
options.headers ??= {};
options.headers.Authorization = `Bearer ${appStore.token}`;
}
};
const getUrl = (url) => {
if (url.indexOf("/") === 0) {
return url;
}
let result = settings.baseURL;
return (result += `/${url}`);
};
const getResult = async (response) => {
const messages = new Map([
[200, "操作成功"],
[201, "已创建"],
[204, "无返回值"],
[400, "请求参数错误"],
[401, "未登录"],
[403, "权限不足"],
[500, "服务器异常"],
]);
const result = {
status: response.status,
message: messages.get(response.status),
};
if (response.status == 200) {
result.data = await response.json();
} else if (response.status === 400 || response.status === 500) {
result.errors = await response.json();
}
return result;
};
const get = async (url, data, options, withoutToken = false, withoutCulture = false) => {
url = getUrl(url, withoutCulture);
if (data) {
url = `${url}?${qs.stringify(data)}`;
}
const defaultOptions = {
headers: {
"Accept-Language": "zh-Hans",
},
};
if (options) {
Object.assign(defaultOptions, options);
}
if (!withoutToken) {
await addToken(defaultOptions);
}
const response = await fetch(url, defaultOptions);
return getResult(response);
};
const post = async (url, data, options, withoutToken = false, withoutCulture = false) => {
url = getUrl(url, withoutCulture);
const defaultOptions = {
method: "POST",
headers: {
"Accept-Language": "zh-Hans",
"Content-Type": "application/json",
},
};
if (options) {
Object.assign(defaultOptions, options);
}
if (data && !defaultOptions.body) {
if (defaultOptions.headers["Content-Type"] === "application/x-www-form-urlencoded") {
defaultOptions.body = qs.stringify(data);
} else {
defaultOptions.body = JSON.stringify(data);
}
}
if (!withoutToken) {
await addToken(defaultOptions);
}
const response = await fetch(url, defaultOptions);
return getResult(response);
};
export { getUrl, get, post };

314
code/WebApp/vanilla/resize-detector/index.js

@ -0,0 +1,314 @@
var raf = null;
function requestAnimationFrame (callback) {
if (!raf) {
raf = (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
return setTimeout(callback, 16)
}
).bind(window);
}
return raf(callback)
}
var caf = null;
function cancelAnimationFrame (id) {
if (!caf) {
caf = (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function (id) {
clearTimeout(id);
}
).bind(window);
}
caf(id);
}
function createStyles (styleText) {
var style = document.createElement('style');
if (style.styleSheet) {
style.styleSheet.cssText = styleText;
} else {
style.appendChild(document.createTextNode(styleText));
}
(document.querySelector('head') || document.body).appendChild(style);
return style
}
function createElement (tagName, props) {
if ( props === void 0 ) props = {};
var elem = document.createElement(tagName);
Object.keys(props).forEach(function (key) {
elem[key] = props[key];
});
return elem
}
function getComputedStyle (elem, prop, pseudo) {
// for older versions of Firefox, `getComputedStyle` required
// the second argument and may return `null` for some elements
// when `display: none`
var computedStyle = window.getComputedStyle(elem, pseudo || null) || {
display: 'none'
};
return computedStyle[prop]
}
function getRenderInfo (elem) {
if (!document.documentElement.contains(elem)) {
return {
detached: true,
rendered: false
}
}
var current = elem;
while (current !== document) {
if (getComputedStyle(current, 'display') === 'none') {
return {
detached: false,
rendered: false
}
}
current = current.parentNode;
}
return {
detached: false,
rendered: true
}
}
var css_248z = ".resize-triggers{visibility:hidden;opacity:0;pointer-events:none}.resize-contract-trigger,.resize-contract-trigger:before,.resize-expand-trigger,.resize-triggers{content:\"\";position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden}.resize-contract-trigger,.resize-expand-trigger{background:#eee;overflow:auto}.resize-contract-trigger:before{width:200%;height:200%}";
var total = 0;
var style = null;
function addListener (elem, callback) {
if (!elem.__resize_mutation_handler__) {
elem.__resize_mutation_handler__ = handleMutation.bind(elem);
}
var listeners = elem.__resize_listeners__;
if (!listeners) {
elem.__resize_listeners__ = [];
if (window.ResizeObserver) {
var offsetWidth = elem.offsetWidth;
var offsetHeight = elem.offsetHeight;
var ro = new ResizeObserver(function () {
if (!elem.__resize_observer_triggered__) {
elem.__resize_observer_triggered__ = true;
if (elem.offsetWidth === offsetWidth && elem.offsetHeight === offsetHeight) {
return
}
}
runCallbacks(elem);
});
// initially display none won't trigger ResizeObserver callback
var ref = getRenderInfo(elem);
var detached = ref.detached;
var rendered = ref.rendered;
elem.__resize_observer_triggered__ = detached === false && rendered === false;
elem.__resize_observer__ = ro;
ro.observe(elem);
} else if (elem.attachEvent && elem.addEventListener) {
// targeting IE9/10
elem.__resize_legacy_resize_handler__ = function handleLegacyResize () {
runCallbacks(elem);
};
elem.attachEvent('onresize', elem.__resize_legacy_resize_handler__);
document.addEventListener('DOMSubtreeModified', elem.__resize_mutation_handler__);
} else {
if (!total) {
style = createStyles(css_248z);
}
initTriggers(elem);
elem.__resize_rendered__ = getRenderInfo(elem).rendered;
if (window.MutationObserver) {
var mo = new MutationObserver(elem.__resize_mutation_handler__);
mo.observe(document, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
elem.__resize_mutation_observer__ = mo;
}
}
}
elem.__resize_listeners__.push(callback);
total++;
}
function removeListener (elem, callback) {
var listeners = elem.__resize_listeners__;
if (!listeners) {
return
}
if (callback) {
listeners.splice(listeners.indexOf(callback), 1);
}
// no listeners exist, or removing all listeners
if (!listeners.length || !callback) {
// targeting IE9/10
if (elem.detachEvent && elem.removeEventListener) {
elem.detachEvent('onresize', elem.__resize_legacy_resize_handler__);
document.removeEventListener('DOMSubtreeModified', elem.__resize_mutation_handler__);
return
}
if (elem.__resize_observer__) {
elem.__resize_observer__.unobserve(elem);
elem.__resize_observer__.disconnect();
elem.__resize_observer__ = null;
} else {
if (elem.__resize_mutation_observer__) {
elem.__resize_mutation_observer__.disconnect();
elem.__resize_mutation_observer__ = null;
}
elem.removeEventListener('scroll', handleScroll);
elem.removeChild(elem.__resize_triggers__.triggers);
elem.__resize_triggers__ = null;
}
elem.__resize_listeners__ = null;
}
if (!--total && style) {
style.parentNode.removeChild(style);
}
}
function getUpdatedSize (elem) {
var ref = elem.__resize_last__;
var width = ref.width;
var height = ref.height;
var offsetWidth = elem.offsetWidth;
var offsetHeight = elem.offsetHeight;
if (offsetWidth !== width || offsetHeight !== height) {
return {
width: offsetWidth,
height: offsetHeight
}
}
return null
}
function handleMutation () {
// `this` denotes the scrolling element
var ref = getRenderInfo(this);
var rendered = ref.rendered;
var detached = ref.detached;
if (rendered !== this.__resize_rendered__) {
if (!detached && this.__resize_triggers__) {
resetTriggers(this);
this.addEventListener('scroll', handleScroll, true);
}
this.__resize_rendered__ = rendered;
runCallbacks(this);
}
}
function handleScroll () {
var this$1 = this;
// `this` denotes the scrolling element
resetTriggers(this);
if (this.__resize_raf__) {
cancelAnimationFrame(this.__resize_raf__);
}
this.__resize_raf__ = requestAnimationFrame(function () {
var updated = getUpdatedSize(this$1);
if (updated) {
this$1.__resize_last__ = updated;
runCallbacks(this$1);
}
});
}
function runCallbacks (elem) {
if (!elem || !elem.__resize_listeners__) {
return
}
elem.__resize_listeners__.forEach(function (callback) {
callback.call(elem, elem);
});
}
function initTriggers (elem) {
var position = getComputedStyle(elem, 'position');
if (!position || position === 'static') {
elem.style.position = 'relative';
}
elem.__resize_old_position__ = position;
elem.__resize_last__ = {};
var triggers = createElement('div', {
className: 'resize-triggers'
});
var expand = createElement('div', {
className: 'resize-expand-trigger'
});
var expandChild = createElement('div');
var contract = createElement('div', {
className: 'resize-contract-trigger'
});
expand.appendChild(expandChild);
triggers.appendChild(expand);
triggers.appendChild(contract);
elem.appendChild(triggers);
elem.__resize_triggers__ = {
triggers: triggers,
expand: expand,
expandChild: expandChild,
contract: contract
};
resetTriggers(elem);
elem.addEventListener('scroll', handleScroll, true);
elem.__resize_last__ = {
width: elem.offsetWidth,
height: elem.offsetHeight
};
}
function resetTriggers (elem) {
var ref = elem.__resize_triggers__;
var expand = ref.expand;
var expandChild = ref.expandChild;
var contract = ref.contract;
// batch read
var csw = contract.scrollWidth;
var csh = contract.scrollHeight;
var eow = expand.offsetWidth;
var eoh = expand.offsetHeight;
var esw = expand.scrollWidth;
var esh = expand.scrollHeight;
// batch write
contract.scrollLeft = csw;
contract.scrollTop = csh;
expandChild.style.width = eow + 1 + 'px';
expandChild.style.height = eoh + 1 + 'px';
expand.scrollLeft = esw;
expand.scrollTop = esh;
}
export { addListener, removeListener };

133
code/WebApp/vanilla/router/index.js

@ -0,0 +1,133 @@
import { createRouter, createWebHashHistory } from "vue-router";
import { useTitle } from "@vueuse/core";
import NProgress from "../lib/nprogress/nprogress.vite-esm.js";
import { isLogin, hasPermission } from "../api/user.js";
import { useAppStore } from "../store/index.js";
import { listToTree } from "../utils/index.js";
import { connection, connect } from "../signalr/index.js";
NProgress.configure({ showSpinner: false });
const routes = [
{
path: "/",
redirect: "/home",
component: () => import("../layouts/index.js"),
children: [
{
path: "home",
component: () => import("../views/home.js"),
meta: {
title: "首页",
icon: "home",
},
},
],
},
{
path: "/login",
component: () => import("../views/login.js"),
meta: {
title: "登录",
},
},
{
path: "/403",
component: () => import("../views/403.js"),
meta: {
title: "权限不足",
},
},
{
path: "/:pathMatch(.*)*",
component: () => import("../views/404.js"),
meta: {
title: "无法找到",
},
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
router.beforeEach(async (to, from, next) => {
console.log(`before: ${from.path} -> ${to.path}`);
NProgress.start();
try {
if (to.path !== "/login") {
if (!(await isLogin())) {
next({ path: "/login", query: { redirect: to.fullPath } });
} else {
if (!hasPermission(to)) {
next({ path: "/403", query: { redirect: to.fullPath } });
} else {
next();
}
}
} else {
next();
}
} catch (error) {
NProgress.done();
}
});
router.afterEach((to, from) => {
console.log(`after: ${from.path} -> ${to.path}`);
try {
if (to.meta.title) {
useTitle().value = `${to.meta.title}`;
}
if (to.fullPath.startsWith("/")) {
const appStore = useAppStore();
appStore.add(to);
}
} finally {
NProgress.done();
}
});
const reset = (list, parent = null) => {
return list.map((o) => {
const item = {
path: o.path,
meta: o.meta,
};
if (item.component && item.component !== "Layout") {
item.component = import(`../views/${o.component ? o.component : "list"}.js`);
}
item.meta.path = `${parent === null ? "" : parent.meta.path + "/"}${item.path}`;
item.meta.fullName = `${parent === null ? "" : parent.meta.title + " > "}${item.meta.title}`;
if (o.type === "Resource") {
if (o.children.length) {
item.meta.buttons = o.children.map((b) => {
return {
path: b.path,
meta: b.meta,
};
});
}
} else if (o.type !== "Operation" && o.children?.length) {
item.children = reset(o.children, item);
}
return item;
});
};
const refreshRouter = async () => {
await connect();
const appStore = useAppStore();
const permissions = appStore.user.permissions.filter((o) => !o.isHidden);
const serverRoutes = reset(permissions);
const route = {
name: "layout",
path: "",
children: serverRoutes,
};
router.removeRoute("layout");
router.addRoute("/", route);
};
export default router;
export { refreshRouter };

42
code/WebApp/vanilla/signalr/index.js

@ -0,0 +1,42 @@
import * as signalR from "@microsoft/signalr";
import PubSub from "pubsub-js";
import useAppStore from "../store/app.js";
import { isLogin } from "../api/user.js";
let connectionId = null;
const connection = new signalR.HubConnectionBuilder()
.withUrl("./api/hub", {
accessTokenFactory: () => {
const appStore = useAppStore();
return appStore.token;
},
})
.withAutomaticReconnect()
.build();
const connect = async () => {
return;
if (await isLogin()) {
if (connection.state === signalR.HubConnectionState.Disconnected) {
connection
.start()
.then(function () {
console.log("signalr connected");
})
.catch(function (error) {
console.error(error);
//setTimeout(connect, 5000);
});
}
}
};
connection.onclose(async () => {
await connect();
});
connection.on("connected", (id) => {
connectionId = id;
});
connection.on("ServerToClient", (method, data) => {
PubSub.publish(method, data);
});
export { connection, connect };

41
code/WebApp/vanilla/store/app.js

@ -0,0 +1,41 @@
import { defineStore } from "pinia";
import settings from "../config/settings.js";
import { getUser, isLogin } from "../api/user.js";
import { get, post } from "../request/index.js";
import { refreshRouter } from "../router/index.js";
import { getLocalizationAsync } from "../api/site.js";
const useAppStore = defineStore("app", {
state: () => {
const state = {
settings: { ...settings },
isMenuCollapse: false,
isRefreshing: false,
routes: [],
};
const localSettings = JSON.parse(localStorage.getItem("settings") ?? "{}");
Object.assign(state.settings, localSettings);
return state;
},
actions: {
async init() {
// 获取站点信息
this.localization = await getLocalizationAsync();
// 获取用户信息
if (await isLogin()) {
this.user = await getUser();
await refreshRouter();
}
},
add(route) {
if (!this.routes.find((o) => o.fullPath === route.fullPath)) {
this.routes.push(route);
} else {
const index = this.routes.findIndex((o) => o.fullPath === route.fullPath);
this.routes[index] = route;
}
},
},
});
export default useAppStore;

7
code/WebApp/vanilla/store/index.js

@ -0,0 +1,7 @@
import { createPinia } from 'pinia';
import useAppStore from './app.js';
const store = createPinia();
export { useAppStore };
export default store;

137
code/WebApp/vanilla/styles/site.css

@ -0,0 +1,137 @@
html * {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
text-rendering: optimizeLegibility;
}
a {
color: inherit;
text-decoration: none;
}
#app {
width: 100vw;
height: 100vh;
--header: 60px;
}
#app > .el-container {
width: 100%;
height: 100%;
overflow: hidden;
}
#app > .el-container > .el-container {
margin-top: var(--header);
height: calc(100% - var(--header));
overflow: auto;
}
.el-scrollbar,
.el-scrollbar__view {
width: 100%;
height: 100%;
}
.el-aside {
min-height: 100%;
overflow: auto;
}
.el-menu--vertical {
min-height: 100%;
}
.el-main {
min-height: calc(100% - 100px);
overflow: auto;
}
.el-header {
width: 100%;
position: fixed;
border-bottom: 1px solid var(--el-border-color);
background-color: var(--el-menu-bg-color);
z-index: 10;
overflow: hidden;
}
.el-footer {
width: 100%;
border-top: 1px solid var(--el-border-color);
background-color: var(--el-menu-bg-color);
overflow: hidden;
}
a.logo {
display: block;
height: var(--header);
}
.footer {
height: var(--header);
}
.logo img {
min-width: 28px;
height: 28px;
margin-right: 16px;
}
.logo h1 {
height: 32px;
line-height: 32px;
padding-right: 20px;
}
.el-dropdown-link:focus-visible {
outline: unset;
}
.el-form--inline .el-form-item__content {
width: 192px;
}
.el-table .cell {
white-space: nowrap;
}
.el-icon {
margin-right: 5px;
}
.el-dialog__header,
.el-dialog__footer,
.el-drawer__header,
.el-drawer__footer {
height: var(--header);
padding: 15px;
}
.el-dialog__header,
.el-drawer__header {
border-bottom: 1px solid var(--el-border-color);
}
.el-dialog__footer,
.el-drawer__footer {
border-top: 1px solid var(--el-border-color);
}
.el-select,
.el-input-number {
width: 100%;
}
/* markdown component */
.markdown-body {
box-sizing: border-box;
margin: 0 auto;
}

120
code/WebApp/vanilla/utils/index.js

@ -0,0 +1,120 @@
import { get } from "lodash";
// format html`...` by vscode lit-html
function html(strings, ...values) {
let output = "";
let index;
for (index = 0; index < values.length; index += 1) {
output += strings[index] + values[index];
}
output += strings[index];
return output;
}
// format %
function persentFormat(number) {
return `${parseFloat(number * 100).toFixed(2)} %`;
}
// format bytes
function bytesFormat(bytes) {
if (isNaN(bytes)) {
return "";
}
var symbols = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var exp = Math.floor(Math.log(bytes) / Math.log(2));
if (exp < 1) {
exp = 0;
}
var i = Math.floor(exp / 10);
bytes = bytes / Math.pow(2, 10 * i);
if (bytes.toString().length > bytes.toFixed(2).toString().length) {
bytes = bytes.toFixed(2);
}
return bytes + " " + symbols[i];
}
// string format
function format(template, ...args) {
const formatRegExp = /%[sdj%]/g;
let counter = 0;
return template.replace(formatRegExp, (match) => {
const index = counter;
counter += 1;
if (match === "%%") {
return "%";
}
if (index > args.length - 1) {
return match;
}
if (match === "%s") {
return String(args[index]);
}
if (match === "%d") {
return Number(args[index]);
}
if (match === "%j") {
return JSON.stringify(args[index]);
}
return match;
});
}
function schemaToModel(schema) {
const entity = {};
Object.keys(schema.properties).forEach((propertyName) => {
const property = schema.properties[propertyName];
if (property.type === "object") {
entity[propertyName] = schemaToModel(property);
} else if ("default" in property) {
entity[propertyName] = property.default;
} else if (property.type === "array") {
entity[propertyName] = [];
} else if (property.type === "boolean") {
entity[propertyName] = property.nullable ? null : false;
} else if (property.type === "number" || property.type === "integer") {
entity[propertyName] = property.nullable ? null : 0;
} else if (property.type === "string") {
entity[propertyName] = null;
} else {
entity[propertyName] = null;
}
});
return entity;
}
function listToTree(list, func) {
const tree = [];
list.forEach((item) => {
if (!item.parentId) {
tree.push(item);
} else {
const parent = list.find((node) => node.id === item.parentId);
if (parent) {
parent.children = parent.children || [];
parent.children.push(item);
}
}
if (func) {
func(item);
}
});
return tree;
}
function treeToList(tree, list = []) {
tree.forEach((o) => {
list.push(o);
if (o.children?.length) {
treeToList(o.children, list);
}
});
return list;
}
function getProp(instance, propPath) {
return get(instance, propPath);
}
export default html;
export { persentFormat, bytesFormat, format, schemaToModel, listToTree, treeToList, getProp };

3
code/WebApp/vanilla/views/403.js

@ -0,0 +1,3 @@
export default {
template: `403`,
};

3
code/WebApp/vanilla/views/404.js

@ -0,0 +1,3 @@
export default {
template: `404:{{$route}}`,
};

7
code/WebApp/vanilla/views/home.js

@ -0,0 +1,7 @@
import html from 'html';
import Md from '../components/markdown/index.js'
export default {
components: { Md },
template: html`Home <md name="test"></md>`
}

32
code/WebApp/vanilla/views/list.js

@ -0,0 +1,32 @@
import AppList from "../components/list/index.js";
import html from "html";
import router from "../router/index.js";
export default {
components: { AppList },
template: html`<el-scrollbar>
<app-list @command="onCommand">
<template #columns="scope">
<el-table-column label="自定义列测试1" prop="id">
<template #default="scope"> {{scope.row.id}} </template>
</el-table-column>
<el-table-column label="自定义列测试2">
<template #default="scope"> {{scope.row.parent?.id}} </template>
</el-table-column>
</template>
<template #tableButtons="{rows}">
<el-button class="el-button--primary" @click="()=>(console.log(rows))">{{$t('test')}}</el-button>
</template>
<template #rowButtons="{rows}">
<el-button class="el-button--primary" @click="()=>(console.log(rows))">{{$t('test')}}</el-button>
</template>
</app-list>
</el-scrollbar>`,
setup() {
console.log(router.currentRoute.value.fullPath);
const onCommand = (item, rows) => {
console.log(item.path, item, rows);
};
return { onCommand };
},
};

90
code/WebApp/vanilla/views/login.js

@ -0,0 +1,90 @@
import html, { schemaToModel } from "html";
import { ref, reactive } from "vue";
import AppForm from "../components/form/index.js";
import { login } from "../api/user.js";
import { get } from "../request/index.js";
import LayoutLogo from "../layouts/logo.js";
import LayoutLocale from "../layouts/locale.js";
import LayoutFooter from "../layouts/footer.js";
export default {
components: { AppForm, LayoutLogo, LayoutLocale, LayoutFooter },
template: html`<el-container>
<el-main class="flex justify-center">
<div>
<div class="flex items-center justify-center">
<layout-logo />
<layout-locale />
</div>
<el-card class="box-card" style="width:400px;">
<app-form :schema="schema" v-model="model" :action="action" @submit="submit">{{$t('login')}}</app-form>
</el-card>
<layout-footer />
</div>
</el-main>
</el-container>`,
async setup() {
const schema = reactive({
title: "LoginRequestModel",
type: "object",
properties: {
username: {
title: "用户名",
type: "string",
rules: [
{
required: true,
message: "用户名不能为空",
},
{
max: 64,
message: "用户名的最大长度为 64",
},
],
},
password: {
title: "密码",
type: "string",
format: "password",
rules: [
{
required: true,
message: "密码不能为空",
},
{
max: 64,
message: "密码的最大长度为 64",
},
{
message: "DataTypeAttribute",
},
],
},
client_id: {
default: "basic-web",
hidden: true,
},
grant_type: {
default: "password",
hidden: true,
},
scope: {
default: "WebAppGateway BaseService",
hidden: true,
},
},
});
const model = reactive(schemaToModel(schema));
const submit = async (callback) => {
const result = await login(model);
if (result.errors) {
callback(result.errors);
}
};
return {
schema,
model,
submit,
};
},
};

216
code/WebApp/vanilla/views/monitor.js

@ -0,0 +1,216 @@
import html from "html";
import { ref, reactive, onMounted, onUnmounted } from "vue";
import { useRoute } from "vue-router";
import Chart from "../components/chart/index.js";
import { get, post } from "../request/index.js";
import { ElMessage, dayjs } from "element-plus";
import { bytesFormat, persentFormat } from "../utils/index.js";
import PubSub from "pubsub-js";
export default {
components: { Chart },
template: html` <template v-if="model">
<el-row :gutter="20" style="margin-bottom:20px;">
<el-col :span="24">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>操作系统</span>
</div>
</template>
<el-descriptions border direction="vertical" :column="4">
<el-descriptions-item label="架构">{{model.osArchitecture}}</el-descriptions-item>
<el-descriptions-item label="名称">{{model.osDescription}}</el-descriptions-item>
<el-descriptions-item label="主机">{{model.hostName}}</el-descriptions-item>
<el-descriptions-item label="用户">{{model.userName}}</el-descriptions-item>
<el-descriptions-item label="时间">{{dayjs(model.serverTime).format()}}</el-descriptions-item>
<el-descriptions-item label="地址">{{model.hostAddresses}}</el-descriptions-item>
<el-descriptions-item label="进程">{{model.processCount}}</el-descriptions-item>
<el-descriptions-item label="线程">{{model.threadCount}}</el-descriptions-item>
<el-descriptions-item label="名称">{{model.driveName}}</el-descriptions-item>
<el-descriptions-item label="大小">{{bytesFormat(model.drivieTotalSize)}}</el-descriptions-item>
<el-descriptions-item label="剩余">{{bytesFormat(model.driveAvailableFreeSpace)}}</el-descriptions-item>
<el-descriptions-item label="占用">{{persentFormat(1-model.driveAvailableFreeSpace/model.drivieTotalSize)}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-bottom:20px;">
<el-col :span="12">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>处理器 {{model.processorCount}} </span>
</div>
</template>
<chart :option="cpuModel" height="300px" />
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>内存 {{bytesFormat(model.totalMemory)}}</span>
</div>
</template>
<chart :option="memoryModel" height="300px" />
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-bottom:20px;">
<el-col :span="12">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>磁盘</span>
</div>
</template>
<el-descriptions border direction="vertical">
<el-descriptions-item label="读取">{{bytesFormat(model.diskRead)}}</el-descriptions-item>
<el-descriptions-item label="写入">{{bytesFormat(model.diskWrite)}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>网络</span>
</div>
</template>
<el-descriptions border direction="vertical">
<el-descriptions-item label="下载">{{bytesFormat(model.netReceived)}}</el-descriptions-item>
<el-descriptions-item label="上传">{{bytesFormat(model.netSent)}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-bottom:20px;">
<el-col :span="24">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>.NET</span>
</div>
</template>
<el-descriptions border direction="vertical" :column="4">
<el-descriptions-item label="名称">{{model.frameworkDescription}}</el-descriptions-item>
<el-descriptions-item label="已分配内存">{{bytesFormat(model.gcTotalMemory)}}</el-descriptions-item>
<el-descriptions-item label="可回收对象">{{model.finalizationPendingCount}}</el-descriptions-item>
<el-descriptions-item label="堆大小">{{bytesFormat(model.heapSizeBytes)}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-bottom:20px;">
<el-col :span="24">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>应用</span>
</div>
</template>
<el-descriptions border direction="vertical" :column="5">
<el-descriptions-item label="架构">{{model.processArchitecture}}</el-descriptions-item>
<el-descriptions-item label="启动时间">{{dayjs(model.processStartTime).format()}}</el-descriptions-item>
<el-descriptions-item label="启动位置">{{model.processFileName}}</el-descriptions-item>
<el-descriptions-item label="参数">{{model.processArguments}}</el-descriptions-item>
<el-descriptions-item label="进程Id">{{model.processId}}</el-descriptions-item>
<el-descriptions-item label="进程名称">{{model.processName}}</el-descriptions-item>
<el-descriptions-item label="CPU">{{persentFormat(model.processCpuUsage)}}</el-descriptions-item>
<el-descriptions-item label="内存">{{bytesFormat(model.processMemory)}}</el-descriptions-item>
<el-descriptions-item label="磁盘读写"
>{{bytesFormat(model.processDiskRead)}} / {{bytesFormat(model.processDiskWrite)}}</el-descriptions-item
>
<el-descriptions-item label="线程">{{model.processThreadCount}}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
</template>`,
setup(props) {
const route = useRoute();
const baseUrl = `${route.meta.path}`.substring(1);
const url = `${baseUrl}/index`;
const schema = reactive({});
const model = reactive({});
//
const cpuModel = reactive({
title: {
text: "处理器",
},
xAxis: {
type: "category",
data: Object.keys(Array(30).fill()),
},
yAxis: {
type: "value",
min: 0,
max: 100,
},
series: [
{
data: [],
type: "line",
smooth: true,
},
],
});
const memoryModel = reactive({
title: {
text: "内存",
},
xAxis: {
type: "category",
data: Object.keys(Array(30).fill()),
},
yAxis: {
type: "value",
min: 0,
max: 100,
},
series: [
{
data: [],
type: "bar",
smooth: true,
},
],
});
//
const onMonitor = (method, data) => {
Object.assign(model, data);
// cpu
if (cpuModel.series[0].data.length > 30) {
cpuModel.series[0].data.shift();
}
cpuModel.title.text = `处理器 ${persentFormat(model.cpuUsage)}`;
cpuModel.series[0].data.push(model.cpuUsage * 100);
// memory
if (memoryModel.series[0].data.length > 30) {
memoryModel.series[0].data.shift();
}
memoryModel.title.text = `内存 ${persentFormat(model.memoryUsage)}`;
memoryModel.series[0].data.push(model.memoryUsage * 100);
};
onMounted(async () => {
Object.assign(schema, (await get(url)).data);
Object.assign(model, (await post(url)).data);
PubSub.subscribe("monitor", onMonitor);
});
onUnmounted(() => {
PubSub.unsubscribe(onMonitor);
});
//
return {
schema,
model,
cpuModel,
memoryModel,
dayjs,
bytesFormat,
persentFormat,
};
},
};

2
docs/demo/src/WTA/wwwroot/components/form/form-input.js

@ -91,7 +91,7 @@ export default {
} else if (props.schema.url) {
try {
const url = `${props.schema.url}/index`;
const result = await post(url, { queryAll: true });
const result = await post(url, { queryAll: true, query: { isReadonly: null } });
options.value = result.data?.items;
} catch (error) {
console.log(error);

Loading…
Cancel
Save