init
@ -0,0 +1,3 @@
|
|||||||
|
ENV=develop
|
||||||
|
VITE_APP_BASE_API=/api
|
||||||
|
VITE_TOKEN_KEY = X-Access-Token
|
@ -0,0 +1,3 @@
|
|||||||
|
ENV=production
|
||||||
|
VITE_APP_BASE_API=/api
|
||||||
|
VITE_TOKEN_KEY = X-Access-Token
|
@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
index.html
|
||||||
|
.vscode
|
||||||
|
docker
|
@ -0,0 +1,26 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: 'vue-eslint-parser',
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
'plugin:prettier/recommended'
|
||||||
|
],
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
// override/add rules settings here, such as:
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
NodeJS: 'readonly'
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,28 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
components.d.ts
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
.history
|
||||||
|
du-i18n.config.json
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx --no -- commitlint --edit ${1}
|
@ -0,0 +1,2 @@
|
|||||||
|
always-auth=true
|
||||||
|
registry=https://registry.npmjs.org/
|
@ -0,0 +1,37 @@
|
|||||||
|
module.exports = {
|
||||||
|
// 一行最多 80 字符
|
||||||
|
printWidth: 80,
|
||||||
|
// 使用 4 个空格缩进
|
||||||
|
tabWidth: 4,
|
||||||
|
// 不使用 tab 缩进,而使用空格
|
||||||
|
useTabs: false,
|
||||||
|
// 行尾需要有分号
|
||||||
|
semi: true,
|
||||||
|
// 使用单引号代替双引号
|
||||||
|
singleQuote: true,
|
||||||
|
// 对象的 key 仅在必要时用引号
|
||||||
|
quoteProps: 'as-needed',
|
||||||
|
// jsx 不使用单引号,而使用双引号
|
||||||
|
jsxSingleQuote: false,
|
||||||
|
// 末尾使用逗号
|
||||||
|
trailingComma: 'all',
|
||||||
|
// 大括号内的首尾需要空格 { foo: bar }
|
||||||
|
bracketSpacing: true,
|
||||||
|
// jsx 标签的反尖括号需要换行
|
||||||
|
jsxBracketSameLine: false,
|
||||||
|
// 箭头函数,只有一个参数的时候,也需要括号
|
||||||
|
arrowParens: 'always',
|
||||||
|
// 每个文件格式化的范围是文件的全部内容
|
||||||
|
rangeStart: 0,
|
||||||
|
rangeEnd: Infinity,
|
||||||
|
// 不需要写文件开头的 @prettier
|
||||||
|
requirePragma: false,
|
||||||
|
// 不需要自动在文件开头插入 @prettier
|
||||||
|
insertPragma: false,
|
||||||
|
// 使用默认的折行标准
|
||||||
|
proseWrap: 'preserve',
|
||||||
|
// 根据显示样式决定 html 要不要折行
|
||||||
|
htmlWhitespaceSensitivity: 'css',
|
||||||
|
// 换行符使用 lf
|
||||||
|
endOfLine: 'auto'
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
FROM nginx
|
||||||
|
ADD nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
ADD docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
COPY dist /usr/share/nginx/html
|
||||||
|
CMD ["sh","/docker-entrypoint.sh"]
|
||||||
|
#ADD oauth2 /usr/share/nginx/html/oauth2
|
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT .
|
||||||
|
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT
|
@ -0,0 +1,13 @@
|
|||||||
|
export default {
|
||||||
|
theme: {
|
||||||
|
'primary-color': '#1677FF',
|
||||||
|
},
|
||||||
|
logo: '/favicon.ico', // 浏览器标签页logo
|
||||||
|
title: 'Jetlinks', // 浏览器标签页title
|
||||||
|
layout: {
|
||||||
|
title: '物联网平台', // 平台title
|
||||||
|
logo: '/logo.png', // 平台logo
|
||||||
|
mode: 'inline',
|
||||||
|
theme: 'light', // 'dark' 'light'
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
API_BASE_PATH=$API_BASE_PATH;
|
||||||
|
SERVER_NAME=$SERVER_NAME;
|
||||||
|
|
||||||
|
NAMESERVERS=$(cat /etc/resolv.conf | grep "nameserver" | awk '{print $2}' | tr '\n' ' ')
|
||||||
|
if [ -z "$API_BASE_PATH" ]; then
|
||||||
|
API_BASE_PATH="http://jetlinks:8844/";
|
||||||
|
fi
|
||||||
|
|
||||||
|
apiUrl="proxy_pass $API_BASE_PATH\$1;"
|
||||||
|
resolver="resolver $NAMESERVERS ipv6=off;"
|
||||||
|
|
||||||
|
sed -i '11c '"$resolver"'' /etc/nginx/conf.d/default.conf
|
||||||
|
sed -i "s%{API_BASE_PATH}%$API_BASE_PATH%g" /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
if [ -z "$SERVER_NAME" ]; then
|
||||||
|
serverName="server_name localhost;"
|
||||||
|
sed -i '4c '"$serverName"'' /etc/nginx/conf.d/default.conf
|
||||||
|
sed -i "s%{SERVER_NAME}%\"0\"%g" /etc/nginx/conf.d/default.conf
|
||||||
|
else
|
||||||
|
serverName="server_name $SERVER_NAME;"
|
||||||
|
sed -i '4c '"$serverName"'' /etc/nginx/conf.d/default.conf
|
||||||
|
sed -i "s%{SERVER_NAME}%$SERVER_NAME%g" /etc/nginx/conf.d/default.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
#cat /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
nginx -g "daemon off;"
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<%- favicon %>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title><%- title %></title>
|
||||||
|
<script src="/js/liveplayer-lib.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,52 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name _;
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1k;
|
||||||
|
gzip_comp_level 9;
|
||||||
|
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_disable "MSIE [1-6]\.";
|
||||||
|
resolver $NAMESERVERS ipv6=off;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
set $my_result_code 200;
|
||||||
|
set $my_server_name {SERVER_NAME};
|
||||||
|
if ($http_Host !~* ^{SERVER_NAME}) {
|
||||||
|
set $my_result_code 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($my_server_name ~ "0") {
|
||||||
|
set $my_result_code 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($my_result_code = 403) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
index index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~/api/ {
|
||||||
|
if ($request_uri ~* ^/api/(.*)$) {
|
||||||
|
proxy_pass {API_BASE_PATH}$1;
|
||||||
|
}
|
||||||
|
#proxy_pass http://host.docker.internal:8840/;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_connect_timeout 1;
|
||||||
|
proxy_buffering off;
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
proxy_cache off;
|
||||||
|
proxy_send_timeout 30m;
|
||||||
|
proxy_read_timeout 30m;
|
||||||
|
client_max_body_size 500m;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"name": "jetlinks-vue",
|
||||||
|
"private": true,
|
||||||
|
"version": "2.2.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --mode develop",
|
||||||
|
"dev:force": "vite --force --mode develop",
|
||||||
|
"build": "node --max_old_space_size=1024000 ./node_modules/vite/bin/vite.js build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||||
|
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
|
||||||
|
"prettier": "prettier --write",
|
||||||
|
"prepare": "husky install"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
|
"@fullcalendar/core": "^6.1.13",
|
||||||
|
"@fullcalendar/daygrid": "^6.1.13",
|
||||||
|
"@fullcalendar/interaction": "^6.1.13",
|
||||||
|
"@fullcalendar/vue3": "^6.1.13",
|
||||||
|
"@liveqing/liveplayer-v3": "^3.7.10",
|
||||||
|
"@types/axios": "^0.14.0",
|
||||||
|
"@types/marked": "^4.0.8",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
|
"@vuemap/vue-amap": "^2.1.2",
|
||||||
|
"@vueuse/core": "^9.10.0",
|
||||||
|
"ant-design-vue": "^3.2.15",
|
||||||
|
"async-validator": "^4.2.5",
|
||||||
|
"axios": "^1.2.1",
|
||||||
|
"colorpicker-v3": "^2.10.2",
|
||||||
|
"cronstrue": "^2.50.0",
|
||||||
|
"dayjs": "^1.11.12",
|
||||||
|
"driver.js": "^0.9.8",
|
||||||
|
"echarts": "^5.4.1",
|
||||||
|
"event-source-polyfill": "^1.0.31",
|
||||||
|
"global": "^4.4.0",
|
||||||
|
"jetlinks-store": "^0.0.3",
|
||||||
|
"jetlinks-ui-components": "^1.0.47",
|
||||||
|
"jsencrypt": "^3.3.2",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
|
"less": "^4.1.3",
|
||||||
|
"less-loader": "^11.1.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
|
"markdown-it-abbr": "^2.0.0",
|
||||||
|
"markdown-it-anchor": "^8.6.7",
|
||||||
|
"markdown-it-deflist": "^3.0.0",
|
||||||
|
"markdown-it-emoji": "^3.0.0",
|
||||||
|
"markdown-it-footnote": "^4.0.0",
|
||||||
|
"markdown-it-highlightjs": "^4.0.1",
|
||||||
|
"markdown-it-ins": "^4.0.0",
|
||||||
|
"markdown-it-mark": "^4.0.0",
|
||||||
|
"markdown-it-sub": "^2.0.0",
|
||||||
|
"markdown-it-sup": "^2.0.0",
|
||||||
|
"markdown-it-task-lists": "^2.1.1",
|
||||||
|
"markdown-it-toc-done-right": "^4.2.0",
|
||||||
|
"marked": "^4.2.12",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"monaco-editor": "^0.50.0",
|
||||||
|
"nrm": "^1.2.5",
|
||||||
|
"pinia": "^2.0.28",
|
||||||
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
|
"rxjs": "^7.8.1",
|
||||||
|
"three": "0.146.0",
|
||||||
|
"unplugin-auto-import": "^0.12.1",
|
||||||
|
"unplugin-vue-components": "^0.22.12",
|
||||||
|
"v-clipboard3": "^0.1.4",
|
||||||
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
|
"vue": "3.3.4",
|
||||||
|
"vue-cropper": "^1.0.9",
|
||||||
|
"vue-i18n": "^9.13.1",
|
||||||
|
"vue-json-viewer": "^3.0.4",
|
||||||
|
"vue-router": "^4.1.6",
|
||||||
|
"vue3-json-viewer": "^2.2.2",
|
||||||
|
"vue3-ts-jsoneditor": "^2.7.1",
|
||||||
|
"xgplayer": "^3.0.19",
|
||||||
|
"xgplayer-flv": "^3.0.20-beta.0",
|
||||||
|
"xgplayer-hls": "^3.0.19",
|
||||||
|
"xgplayer-hls.js": "2.2.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^17.4.1",
|
||||||
|
"@commitlint/config-conventional": "^17.4.0",
|
||||||
|
"@types/leaflet": "^1.9.12",
|
||||||
|
"@types/lodash-es": "^4.17.6",
|
||||||
|
"@types/moment": "^2.13.0",
|
||||||
|
"@types/node": "^18.14.0",
|
||||||
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
|
"@vuemap/unplugin-resolver": "^1.0.4",
|
||||||
|
"autoprefixer": "^10.4.13",
|
||||||
|
"commitlint": "^17.4.1",
|
||||||
|
"husky": "^8.0.0",
|
||||||
|
"lint-staged": "^13.1.0",
|
||||||
|
"mrm": "^4.1.13",
|
||||||
|
"prettier": "^2.8.1",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"vite": "^4.0.0",
|
||||||
|
"vite-plugin-html": "^3.2.0",
|
||||||
|
"vite-plugin-progress": "^0.0.7",
|
||||||
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
|
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||||
|
"vue-tsc": "^1.0.11"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"**/*.{vue,js,jsx,ts,tsx}": [
|
||||||
|
"npm run lint",
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"**/*.{html,css,less,md}": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "18.14.0",
|
||||||
|
"yarn": "1.22.0"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
import type { IMatcher } from './jetlinks'
|
||||||
|
|
||||||
|
|
||||||
|
export function kebabCase(key: string) {
|
||||||
|
const result = key.replace(/([A-Z])/g, ' $1').trim()
|
||||||
|
return result.split(' ').join('-').toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AntdMatchComponents: IMatcher[] = [
|
||||||
|
{
|
||||||
|
pattern: /^Avatar/,
|
||||||
|
styleDir: 'avatar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^AutoComplete/,
|
||||||
|
styleDir: 'auto-complete',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Anchor/,
|
||||||
|
styleDir: 'anchor',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Badge/,
|
||||||
|
styleDir: 'badge',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Breadcrumb/,
|
||||||
|
styleDir: 'breadcrumb',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Button/,
|
||||||
|
styleDir: 'button',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Checkbox/,
|
||||||
|
styleDir: 'checkbox',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Card/,
|
||||||
|
styleDir: 'card',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Collapse/,
|
||||||
|
styleDir: 'collapse',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Descriptions/,
|
||||||
|
styleDir: 'descriptions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||||
|
styleDir: 'date-picker',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Dropdown/,
|
||||||
|
styleDir: 'dropdown',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Form/,
|
||||||
|
styleDir: 'form',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^InputNumber/,
|
||||||
|
styleDir: 'input-number',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Input|^Textarea/,
|
||||||
|
styleDir: 'input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Statistic/,
|
||||||
|
styleDir: 'statistic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^CheckableTag/,
|
||||||
|
styleDir: 'tag',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TimeRangePicker/,
|
||||||
|
styleDir: 'time-picker',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Layout/,
|
||||||
|
styleDir: 'layout',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Menu|^SubMenu/,
|
||||||
|
styleDir: 'menu',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Table/,
|
||||||
|
styleDir: 'table',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TimePicker|^TimeRangePicker/,
|
||||||
|
styleDir: 'time-picker',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Radio/,
|
||||||
|
styleDir: 'radio',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Image/,
|
||||||
|
styleDir: 'image',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^List/,
|
||||||
|
styleDir: 'list',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Tab/,
|
||||||
|
styleDir: 'tabs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Mentions/,
|
||||||
|
styleDir: 'mentions',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Step/,
|
||||||
|
styleDir: 'steps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Skeleton/,
|
||||||
|
styleDir: 'skeleton',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Select/,
|
||||||
|
styleDir: 'select',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TreeSelect/,
|
||||||
|
styleDir: 'tree-select',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Tree|^DirectoryTree/,
|
||||||
|
styleDir: 'tree',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Typography/,
|
||||||
|
styleDir: 'typography',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Timeline/,
|
||||||
|
styleDir: 'timeline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Upload/,
|
||||||
|
styleDir: 'upload',
|
||||||
|
},
|
||||||
|
]
|
@ -0,0 +1,397 @@
|
|||||||
|
import { AntdMatchComponents, kebabCase } from './antdv'
|
||||||
|
|
||||||
|
export interface IMatcher {
|
||||||
|
pattern: RegExp
|
||||||
|
styleDir: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchComponents: IMatcher[] = [
|
||||||
|
{
|
||||||
|
pattern: /^Avatar/,
|
||||||
|
styleDir: 'Avatar'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^AutoComplete/,
|
||||||
|
styleDir: 'AutoComplete'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Anchor/,
|
||||||
|
styleDir: 'Anchor'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Badge/,
|
||||||
|
styleDir: 'Badge'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Breadcrumb/,
|
||||||
|
styleDir: 'Breadcrumb'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Button/,
|
||||||
|
styleDir: 'Button'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Checkbox/,
|
||||||
|
styleDir: 'Checkbox'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^CardSelect/,
|
||||||
|
styleDir: 'CardSelect'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Card/,
|
||||||
|
styleDir: 'Card'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Collapse/,
|
||||||
|
styleDir: 'Collapse'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Descriptions/,
|
||||||
|
styleDir: 'Descriptions'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
|
||||||
|
styleDir: 'DatePicker'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Dropdown/,
|
||||||
|
styleDir: 'Dropdown'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Form/,
|
||||||
|
styleDir: 'Form'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^InputNumber/,
|
||||||
|
styleDir: 'InputNumber'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Input|^Textarea/,
|
||||||
|
styleDir: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Statistic/,
|
||||||
|
styleDir: 'Statistic'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^CheckableTag/,
|
||||||
|
styleDir: 'Tag'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TimeRangePicker/,
|
||||||
|
styleDir: 'TimePicker'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Layout/,
|
||||||
|
styleDir: 'Layout'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Menu|^SubMenu/,
|
||||||
|
styleDir: 'Menu'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Table/,
|
||||||
|
styleDir: 'Table'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TimePicker|^TimeRangePicker/,
|
||||||
|
styleDir: 'TimePicker'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Radio/,
|
||||||
|
styleDir: 'Radio'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Image/,
|
||||||
|
styleDir: 'Image'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^List/,
|
||||||
|
styleDir: 'List'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Tab/,
|
||||||
|
styleDir: 'Tabs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Mentions/,
|
||||||
|
styleDir: 'Mentions'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Step/,
|
||||||
|
styleDir: 'Steps'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Skeleton/,
|
||||||
|
styleDir: 'Skeleton'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Select|^SelectBoolean/,
|
||||||
|
styleDir: 'Select'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TreeSelect/,
|
||||||
|
styleDir: 'TreeSelect'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Tree|^DirectoryTree/,
|
||||||
|
styleDir: 'Tree'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Typography/,
|
||||||
|
styleDir: 'Typography'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Timeline/,
|
||||||
|
styleDir: 'Timeline'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Upload/,
|
||||||
|
styleDir: 'Upload'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^ProTable/,
|
||||||
|
styleDir: 'ProTable'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Search|^AdvancedSearch/,
|
||||||
|
styleDir: 'Search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Ellipsis/,
|
||||||
|
styleDir: 'Ellipsis'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^MonacoEditor/,
|
||||||
|
styleDir: 'MonacoEditor'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^ProLayout/,
|
||||||
|
styleDir: 'ProLayout'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^ScrollTable/,
|
||||||
|
styleDir: 'ScrollTable'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^TableCard/,
|
||||||
|
styleDir: 'TableCard'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Scrollbar/,
|
||||||
|
styleDir: 'Scrollbar'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^AIcon/,
|
||||||
|
styleDir: 'AIcon'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
pattern: /^Tooltip/,
|
||||||
|
styleDir: 'Tooltip'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Empty/,
|
||||||
|
styleDir: 'Empty'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^PopconfirmModal/,
|
||||||
|
styleDir: 'PopconfirmModal'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Popconfirm/,
|
||||||
|
styleDir: 'Popconfirm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^message/,
|
||||||
|
styleDir: 'Message'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^Notification/,
|
||||||
|
styleDir: 'Notification'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^DataTable/,
|
||||||
|
styleDir: 'DataTable'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^CheckButton/,
|
||||||
|
styleDir: 'CheckButton'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export interface JetlinksVueResolverOptions {
|
||||||
|
/**
|
||||||
|
* exclude components that do not require automatic import
|
||||||
|
*
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
exclude?: string[]
|
||||||
|
/**
|
||||||
|
* import style along with components
|
||||||
|
*
|
||||||
|
* @default 'css'
|
||||||
|
*/
|
||||||
|
importStyle?: boolean | 'css' | 'less'
|
||||||
|
/**
|
||||||
|
* resolve `ant-design-vue' icons
|
||||||
|
*
|
||||||
|
* requires package `@ant-design/icons-vue`
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
resolveIcons?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use `importStyle: 'css'` instead
|
||||||
|
*/
|
||||||
|
importCss?: boolean
|
||||||
|
/**
|
||||||
|
* @deprecated use `importStyle: 'less'` instead
|
||||||
|
*/
|
||||||
|
importLess?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use commonjs build default false
|
||||||
|
*/
|
||||||
|
cjs?: boolean
|
||||||
|
packageName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStyleDir(compName: string, _isAntd = false): string {
|
||||||
|
let styleDir
|
||||||
|
const components = _isAntd ? AntdMatchComponents : matchComponents
|
||||||
|
const total = components.length
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
|
const matcher = components[i]
|
||||||
|
if (compName.match(matcher.pattern)) {
|
||||||
|
styleDir = matcher.styleDir
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!styleDir)
|
||||||
|
styleDir = _isAntd ? kebabCase(compName) : compName
|
||||||
|
|
||||||
|
return styleDir
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSideEffects(compName: string, options: JetlinksVueResolverOptions, _isAntd = false): any {
|
||||||
|
const {
|
||||||
|
importStyle = true,
|
||||||
|
importLess = false
|
||||||
|
} = options
|
||||||
|
|
||||||
|
if (!importStyle)
|
||||||
|
return
|
||||||
|
const lib = options.cjs ? 'lib' : 'es'
|
||||||
|
const packageName = options?.packageName || 'jetlinks-ui-components'
|
||||||
|
if (importStyle === 'less' || importLess) {
|
||||||
|
const styleDir = getStyleDir(compName, _isAntd)
|
||||||
|
return `${packageName}/${lib}/${styleDir}/style`
|
||||||
|
} else {
|
||||||
|
const styleDir = getStyleDir(compName, _isAntd)
|
||||||
|
return `${packageName}/${lib}/${styleDir}/style/css`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterName = ['message', 'Notification']
|
||||||
|
const primitiveNames = ['AIcon','Affix', 'Anchor', 'AnchorLink', 'message', 'Notification', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', 'Badge', 'BadgeRibbon', 'Breadcrumb', 'BreadcrumbItem', 'BreadcrumbSeparator', 'Button', 'ButtonGroup', 'Calendar', 'Card', 'CardGrid', 'CardMeta', 'Collapse', 'CollapsePanel', 'Carousel', 'Cascader', 'Checkbox', 'CheckboxGroup', 'Col', 'Comment', 'ConfigProvider', 'DatePicker', 'MonthPicker', 'WeekPicker', 'RangePicker', 'QuarterPicker', 'Descriptions', 'DescriptionsItem', 'Divider', 'Dropdown', 'DropdownButton', 'Drawer', 'Empty', 'Form', 'FormItem', 'FormItemRest', 'Grid', 'Input', 'InputGroup', 'InputPassword', 'InputSearch', 'Textarea', 'Image', 'ImagePreviewGroup', 'InputNumber', 'Layout', 'LayoutHeader', 'LayoutSider', 'LayoutFooter', 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', 'SubMenu', 'Mentions', 'MentionsOption', 'Modal', 'Statistic', 'StatisticCountdown', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', 'Radio', 'RadioButton', 'RadioGroup', 'Rate', 'Result', 'Row', 'Select', 'SelectOptGroup', 'SelectOption', 'SelectBoolean', 'Skeleton', 'SkeletonButton', 'SkeletonAvatar', 'SkeletonInput', 'SkeletonImage', 'Slider', 'Space', 'Spin', 'Steps', 'Step', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', 'TableSummaryRow', 'TableSummaryCell', 'Transfer', 'Tree', 'TreeNode', 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', 'Tabs', 'TabPane', 'Tag', 'CheckableTag', 'TimePicker', 'TimeRangePicker', 'Timeline', 'TimelineItem', 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', 'TypographyText', 'TypographyTitle', 'Upload', 'UploadDragger', 'LocaleProvider', 'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'PopconfirmModal', 'DataTable',
|
||||||
|
'DataTableArray',
|
||||||
|
'DataTableString',
|
||||||
|
'DataTableInteger',
|
||||||
|
'DataTableDouble',
|
||||||
|
'DataTableBoolean',
|
||||||
|
'DataTableEnum',
|
||||||
|
'DataTableFile',
|
||||||
|
'DataTableDate',
|
||||||
|
'DataTableTypeSelect',
|
||||||
|
'DataTableObject',
|
||||||
|
'CheckButton',
|
||||||
|
]
|
||||||
|
|
||||||
|
const prefix = 'J'
|
||||||
|
|
||||||
|
const proComponents = [
|
||||||
|
'ProTable', 'Search', 'AdvancedSearch', 'Ellipsis', 'MonacoEditor', 'ProLayout', 'ScrollTable', 'TableCard', 'Scrollbar', 'CardSelect', 'PopconfirmModal', 'DataTable',
|
||||||
|
'DataTableArray',
|
||||||
|
'DataTableString',
|
||||||
|
'DataTableInteger',
|
||||||
|
'DataTableDouble',
|
||||||
|
'DataTableBoolean',
|
||||||
|
'DataTableEnum',
|
||||||
|
'DataTableFile',
|
||||||
|
'DataTableDate',
|
||||||
|
'DataTableTypeSelect',
|
||||||
|
'DataTableObject',
|
||||||
|
'CheckButton',
|
||||||
|
'ValueItem'
|
||||||
|
]
|
||||||
|
|
||||||
|
let jetlinksNames: Set<string>
|
||||||
|
|
||||||
|
function genJetlinksNames(primitiveNames: string[]): void {
|
||||||
|
jetlinksNames = new Set(primitiveNames.map(name => filterName.includes(name) ? name : `${prefix}${name}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
let antdvNames: Set<string>
|
||||||
|
|
||||||
|
function genAntdNames(primitiveNames: string[]): void {
|
||||||
|
antdvNames = new Set(primitiveNames.map(name => `A${name}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
genAntdNames(primitiveNames.filter(key => !filterName.includes(key)))
|
||||||
|
genJetlinksNames(primitiveNames)
|
||||||
|
|
||||||
|
function isJetlinks(compName: string): boolean {
|
||||||
|
return jetlinksNames.has(compName)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAntdv(compName: string): boolean {
|
||||||
|
return antdvNames.has(compName)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function JetlinksVueResolver(options: JetlinksVueResolverOptions = {}): any {
|
||||||
|
return {
|
||||||
|
type: 'component',
|
||||||
|
resolve: (name: string) => {
|
||||||
|
if (options.resolveIcons && name.match(/(Outlined|Filled|TwoTone)$/)) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
from: '@ant-design/icons-vue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const _isJetlinks = isJetlinks(name)
|
||||||
|
const _isAntd = isAntdv(name)
|
||||||
|
|
||||||
|
if ((_isJetlinks || _isAntd) && !options?.exclude?.includes(name)) {
|
||||||
|
// const importName = filterName.includes(name) ? name : name.slice(1)
|
||||||
|
//
|
||||||
|
// options.packageName = proComponents.includes(importName) ? 'jetlinks-ui-components' : 'ant-design-vue'
|
||||||
|
//
|
||||||
|
// const path = `${options.packageName}/${options.cjs ? 'lib' : 'es'}`
|
||||||
|
// const stylePath = getSideEffects(importName, options, !proComponents.includes(importName))
|
||||||
|
|
||||||
|
const importName = filterName.includes(name) ? name : name.slice(1)
|
||||||
|
options.packageName = _isJetlinks ? 'jetlinks-ui-components' : 'ant-design-vue'
|
||||||
|
const path = `${options.packageName}/${options.cjs ? 'lib' : 'es'}`
|
||||||
|
const stylePath = getSideEffects(importName, options, _isAntd)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: importName,
|
||||||
|
from: path,
|
||||||
|
sideEffects: stylePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
const rootPath = path.resolve(__dirname, '../')
|
||||||
|
|
||||||
|
function optimizeAntdComponents(moduleName: string): string[] {
|
||||||
|
const moduleESPath = `${moduleName}/es`
|
||||||
|
const nodeModulePath = `./node_modules/${moduleESPath}`
|
||||||
|
const includes: string[] = [moduleESPath]
|
||||||
|
|
||||||
|
const folders = fs.readdirSync(
|
||||||
|
path.resolve(rootPath, nodeModulePath)
|
||||||
|
)
|
||||||
|
|
||||||
|
folders.map(name => {
|
||||||
|
const folderName = path.resolve(
|
||||||
|
rootPath,
|
||||||
|
nodeModulePath,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
let stat = fs.lstatSync(folderName)
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
let styleFolder = path.resolve(folderName, 'style')
|
||||||
|
if (fs.existsSync((styleFolder))) {
|
||||||
|
let _stat = fs.lstatSync(styleFolder)
|
||||||
|
if (_stat.isDirectory()) {
|
||||||
|
includes.push(`${moduleESPath}/${name}/style`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return includes
|
||||||
|
}
|
||||||
|
|
||||||
|
export function optimizeDeps() {
|
||||||
|
return {
|
||||||
|
name: "optimizeDeps",
|
||||||
|
configResolved: async (config) => {
|
||||||
|
const components = [
|
||||||
|
...optimizeAntdComponents('ant-design-vue'),
|
||||||
|
...optimizeAntdComponents('jetlinks-ui-components')
|
||||||
|
]
|
||||||
|
let concat = config.optimizeDeps.include.concat(components)
|
||||||
|
config.optimizeDeps.include = Array.from(new Set(concat))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
preview.pro.ant.design
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 490 B |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 159 B |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 826 B |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 165 B |
After Width: | Height: | Size: 898 B |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 881 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.6 KiB |