Merge branch 'StableVersion' of http://18.167.251.121:10003/aidlab/aida_front into StableVersion

This commit is contained in:
李志鹏
2026-01-26 16:47:09 +08:00
215 changed files with 17892 additions and 5117 deletions

View File

@@ -7,5 +7,7 @@ VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
# VITE_APP_BASE_URL = 'https://www.api.aida.com.hk'
# 徐佩
# VITE_APP_BASE_URL = 'http://192.168.31.118:5567'
# 李天祥
# VITE_APP_BASE_URL = 'http://192.168.31.82:5567'
# 海波
# VITE_APP_BASE_URL = 'http://192.168.31.34:5567'

1
.gitignore vendored
View File

@@ -24,3 +24,4 @@ dist.rar
*.sw?
.eslintrc-auto-import.json
components.d.ts
.cursor

View File

@@ -17,6 +17,7 @@
<link rel="stylesheet" href="/css/sloganFamily.css">
<link rel="stylesheet" href="/css/pingfang.css">
<link rel="stylesheet" href="/css/fonts/fontFamily.css">
</head>
<body>

260
package-lock.json generated
View File

@@ -34,6 +34,7 @@
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^9.6.1",
"vue-router": "^4.0.3",
"vue3-moveable": "^0.28.0",
"vuedraggable": "^4.1.0",
"vuex": "^4.0.0",
"x-sender": "^1.1.6"
@@ -232,6 +233,15 @@
"node": ">=6.9.0"
}
},
"node_modules/@cfcs/core": {
"version": "0.0.6",
"resolved": "https://registry.npmmirror.com/@cfcs/core/-/core-0.0.6.tgz",
"integrity": "sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==",
"license": "MIT",
"dependencies": {
"@egjs/component": "^3.0.2"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@@ -240,6 +250,39 @@
"node": ">=10"
}
},
"node_modules/@daybrush/utils": {
"version": "1.13.0",
"resolved": "https://registry.npmmirror.com/@daybrush/utils/-/utils-1.13.0.tgz",
"integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ==",
"license": "MIT"
},
"node_modules/@egjs/agent": {
"version": "2.4.4",
"resolved": "https://registry.npmmirror.com/@egjs/agent/-/agent-2.4.4.tgz",
"integrity": "sha512-cvAPSlUILhBBOakn2krdPnOGv5hAZq92f1YHxYcfu0p7uarix2C6Ia3AVizpS1SGRZGiEkIS5E+IVTLg1I2Iog==",
"license": "MIT"
},
"node_modules/@egjs/children-differ": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@egjs/children-differ/-/children-differ-1.0.1.tgz",
"integrity": "sha512-DRvyqMf+CPCOzAopQKHtW+X8iN6Hy6SFol+/7zCUiE5y4P/OB8JP8FtU4NxtZwtafvSL4faD5KoQYPj3JHzPFQ==",
"license": "MIT",
"dependencies": {
"@egjs/list-differ": "^1.0.0"
}
},
"node_modules/@egjs/component": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/@egjs/component/-/component-3.0.5.tgz",
"integrity": "sha512-cLcGizTrrUNA2EYE3MBmEDt2tQv1joVP1Q3oDisZ5nw0MZDx2kcgEXM+/kZpfa/PAkFvYVhRUZwytIQWoN3V/w==",
"license": "MIT"
},
"node_modules/@egjs/list-differ": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@egjs/list-differ/-/list-differ-1.0.1.tgz",
"integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg==",
"license": "MIT"
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
@@ -1224,6 +1267,34 @@
"win32"
]
},
"node_modules/@scena/dragscroll": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/@scena/dragscroll/-/dragscroll-1.4.0.tgz",
"integrity": "sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.6.0",
"@scena/event-emitter": "^1.0.2"
}
},
"node_modules/@scena/event-emitter": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/@scena/event-emitter/-/event-emitter-1.0.5.tgz",
"integrity": "sha512-AzY4OTb0+7ynefmWFQ6hxDdk0CySAq/D4efljfhtRHCOP7MBF9zUfhKG3TJiroVjASqVgkRJFdenS8ArZo6Olg==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.1.1"
}
},
"node_modules/@scena/matrix": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@scena/matrix/-/matrix-1.1.1.tgz",
"integrity": "sha512-JVKBhN0tm2Srl+Yt+Ywqu0oLgLcdemDQlD1OxmN9jaCTwaFPZ7tY8n6dhVgMEaR9qcR7r+kAlMXnSfNyYdE+Vg==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.4.0"
}
},
"node_modules/@simonwep/pickr": {
"version": "1.8.2",
"resolved": "https://registry.npmmirror.com/@simonwep/pickr/-/pickr-1.8.2.tgz",
@@ -2904,6 +2975,52 @@
"node": ">= 0.10"
}
},
"node_modules/croact": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/croact/-/croact-1.0.4.tgz",
"integrity": "sha512-9GhvyzTY/IVUrMQ2iz/mzgZ8+NcjczmIo/t4FkC1CU0CEcau6v6VsEih4jkTa4ZmRgYTF0qXEZLObCzdDFplpw==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@egjs/list-differ": "^1.0.0"
}
},
"node_modules/croact-css-styled": {
"version": "1.1.9",
"resolved": "https://registry.npmmirror.com/croact-css-styled/-/croact-css-styled-1.1.9.tgz",
"integrity": "sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"css-styled": "~1.0.8",
"framework-utils": "^1.1.0"
}
},
"node_modules/croact-moveable": {
"version": "0.9.0",
"resolved": "https://registry.npmmirror.com/croact-moveable/-/croact-moveable-0.9.0.tgz",
"integrity": "sha512-fc3bieV6CdqqZFtzsSLi9KmvUMFW3oakUfhPCls1BxKjOfUfn8rktteGED2341A/Qghy8tI3Hm6SdocIc68IKg==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@egjs/agent": "^2.2.1",
"@egjs/children-differ": "^1.0.1",
"@egjs/list-differ": "^1.0.0",
"@scena/dragscroll": "^1.4.0",
"@scena/event-emitter": "^1.0.5",
"@scena/matrix": "^1.1.1",
"croact-css-styled": "^1.1.9",
"css-to-mat": "^1.1.1",
"framework-utils": "^1.1.0",
"gesto": "^1.19.3",
"overlap-area": "^1.1.0",
"react-css-styled": "^1.1.9",
"react-moveable": "~0.56.0"
},
"peerDependencies": {
"croact": "^1.0.4"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3014,6 +3131,25 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/css-styled": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/css-styled/-/css-styled-1.0.8.tgz",
"integrity": "sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0"
}
},
"node_modules/css-to-mat": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/css-to-mat/-/css-to-mat-1.1.1.tgz",
"integrity": "sha512-kvpxFYZb27jRd2vium35G7q5XZ2WJ9rWjDUMNT36M3Hc41qCrLXFM5iEKMGXcrPsKfXEN+8l/riB4QzwwwiEyQ==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@scena/matrix": "^1.0.0"
}
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz",
@@ -4354,6 +4490,12 @@
"node": ">=0.10.0"
}
},
"node_modules/framework-utils": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/framework-utils/-/framework-utils-1.1.0.tgz",
"integrity": "sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg==",
"license": "MIT"
},
"node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -4485,6 +4627,16 @@
"node": ">=10"
}
},
"node_modules/gesto": {
"version": "1.19.4",
"resolved": "https://registry.npmmirror.com/gesto/-/gesto-1.19.4.tgz",
"integrity": "sha512-hfr/0dWwh0Bnbb88s3QVJd1ZRJeOWcgHPPwmiH6NnafDYvhTsxg+SLYu+q/oPNh9JS3V+nlr6fNs8kvPAtcRDQ==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@scena/event-emitter": "^1.0.2"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -5695,6 +5847,24 @@
"setimmediate": "^1.0.5"
}
},
"node_modules/keycode": {
"version": "2.2.1",
"resolved": "https://registry.npmmirror.com/keycode/-/keycode-2.2.1.tgz",
"integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==",
"license": "MIT"
},
"node_modules/keycon": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/keycon/-/keycon-1.4.0.tgz",
"integrity": "sha512-p1NAIxiRMH3jYfTeXRs2uWbVJ1WpEjpi8ktzUyBJsX7/wn2qu2VRXktneBLNtKNxJmlUYxRi9gOJt1DuthXR7A==",
"license": "MIT",
"dependencies": {
"@cfcs/core": "^0.0.6",
"@daybrush/utils": "^1.7.1",
"@scena/event-emitter": "^1.0.2",
"keycode": "^2.2.0"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
@@ -6212,6 +6382,19 @@
"pathe": "^2.0.1"
}
},
"node_modules/moveable": {
"version": "0.53.0",
"resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.53.0.tgz",
"integrity": "sha512-71jS9zIoQzMhnNvduhg4tUEdm23+fO/40FN7muVMbZvVwbTku2MIxxLhnU4qFvxI4oVxn75l79SbtgjuA+s7Pw==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@scena/event-emitter": "^1.0.5",
"croact": "^1.0.4",
"croact-moveable": "~0.9.0",
"react-moveable": "~0.56.0"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
@@ -6650,6 +6833,15 @@
"node": ">= 0.8.0"
}
},
"node_modules/overlap-area": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/overlap-area/-/overlap-area-1.1.0.tgz",
"integrity": "sha512-3dlJgJCaVeXH0/eZjYVJvQiLVVrPO4U1ZGqlATtx6QGO3b5eNM6+JgUKa7oStBTdYuGTk7gVoABCW6Tp+dhRdw==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.7.1"
}
},
"node_modules/own-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz",
@@ -7037,6 +7229,46 @@
"safe-buffer": "^5.1.0"
}
},
"node_modules/react-css-styled": {
"version": "1.1.9",
"resolved": "https://registry.npmmirror.com/react-css-styled/-/react-css-styled-1.1.9.tgz",
"integrity": "sha512-M7fJZ3IWFaIHcZEkoFOnkjdiUFmwd8d+gTh2bpqMOcnxy/0Gsykw4dsL4QBiKsxcGow6tETUa4NAUcmJF+/nfw==",
"license": "MIT",
"dependencies": {
"css-styled": "~1.0.8",
"framework-utils": "^1.1.0"
}
},
"node_modules/react-moveable": {
"version": "0.56.0",
"resolved": "https://registry.npmmirror.com/react-moveable/-/react-moveable-0.56.0.tgz",
"integrity": "sha512-FmJNmIOsOA36mdxbrc/huiE4wuXSRlmon/o+/OrfNhSiYYYL0AV5oObtPluEhb2Yr/7EfYWBHTxF5aWAvjg1SA==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@egjs/agent": "^2.2.1",
"@egjs/children-differ": "^1.0.1",
"@egjs/list-differ": "^1.0.0",
"@scena/dragscroll": "^1.4.0",
"@scena/event-emitter": "^1.0.5",
"@scena/matrix": "^1.1.1",
"css-to-mat": "^1.1.1",
"framework-utils": "^1.1.0",
"gesto": "^1.19.3",
"overlap-area": "^1.1.0",
"react-css-styled": "^1.1.9",
"react-selecto": "^1.25.0"
}
},
"node_modules/react-selecto": {
"version": "1.26.3",
"resolved": "https://registry.npmmirror.com/react-selecto/-/react-selecto-1.26.3.tgz",
"integrity": "sha512-Ubik7kWSnZyQEBNro+1k38hZaI1tJarE+5aD/qsqCOA1uUBSjgKVBy3EWRzGIbdmVex7DcxznFZLec/6KZNvwQ==",
"license": "MIT",
"dependencies": {
"selecto": "~1.26.3"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -7507,6 +7739,24 @@
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
"dev": true
},
"node_modules/selecto": {
"version": "1.26.3",
"resolved": "https://registry.npmmirror.com/selecto/-/selecto-1.26.3.tgz",
"integrity": "sha512-gZHgqMy5uyB6/2YDjv3Qqaf7bd2hTDOpPdxXlrez4R3/L0GiEWDCFaUfrflomgqdb3SxHF2IXY0Jw0EamZi7cw==",
"license": "MIT",
"dependencies": {
"@daybrush/utils": "^1.13.0",
"@egjs/children-differ": "^1.0.1",
"@scena/dragscroll": "^1.4.0",
"@scena/event-emitter": "^1.0.5",
"css-styled": "^1.0.8",
"css-to-mat": "^1.1.1",
"framework-utils": "^1.1.0",
"gesto": "^1.19.4",
"keycon": "^1.2.0",
"overlap-area": "^1.1.0"
}
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
@@ -9765,6 +10015,16 @@
"vue": "^3.0.0"
}
},
"node_modules/vue3-moveable": {
"version": "0.28.0",
"resolved": "https://registry.npmmirror.com/vue3-moveable/-/vue3-moveable-0.28.0.tgz",
"integrity": "sha512-vplQO0XkxVEtXMDh2/lZE+c5kMycGXAfYFMvbwFKi8UVYzVk8MTgVHr4fxO9Z+4i4Rb+U/IEIgkhHRMAbx8FJg==",
"license": "MIT",
"dependencies": {
"framework-utils": "^1.1.0",
"moveable": "~0.53.0"
}
},
"node_modules/vuedraggable": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",

View File

@@ -40,6 +40,7 @@
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^9.6.1",
"vue-router": "^4.0.3",
"vue3-moveable": "^0.28.0",
"vuedraggable": "^4.1.0",
"vuex": "^4.0.0",
"x-sender": "^1.1.6"

BIN
public/css/fonts/ARIAL.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,31 @@
/* 字体定义 */
@font-face {
font-family: 'Arial';
src: url('./fonts/ARIAL.ttf') format('ttf');
}
@font-face {
font-family: 'ArialBold';
src: url('./fonts/ARIALBD.ttf') format('ttf');
}
@font-face {
font-family: 'ArialMedium';
src: url('./fonts/ArialMdm.ttf') format('ttf');
}
@font-face {
font-family: 'Poppins';
src: url('./fonts/Poppins-Regular.ttf') format('ttf');
font-weight: normal;
}
@font-face {
font-family: 'PoppinsMedium';
src: url('./fonts/Poppins-Medium.ttf') format('ttf');
}
@font-face {
font-family: 'PoppinsBold';
src: url('./fonts/Poppins-SemiBold.ttf') format('ttf');
}

View File

@@ -54,6 +54,24 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe7a4;</span>
<div class="name">混合模式</div>
<div class="code-name">&amp;#xe7a4;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe60f;</span>
<div class="name">更多</div>
<div class="code-name">&amp;#xe60f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe8d7;</span>
<div class="name">平铺</div>
<div class="code-name">&amp;#xe8d7;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe650;</span>
<div class="name">裁剪</div>
@@ -276,9 +294,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1762934152017') format('woff2'),
url('iconfont.woff?t=1762934152017') format('woff'),
url('iconfont.ttf?t=1762934152017') format('truetype');
src: url('iconfont.woff2?t=1766460927921') format('woff2'),
url('iconfont.woff?t=1766460927921') format('woff'),
url('iconfont.ttf?t=1766460927921') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -304,6 +322,33 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-hunhemoshi"></span>
<div class="name">
混合模式
</div>
<div class="code-name">.icon-hunhemoshi
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-gengduo"></span>
<div class="name">
更多
</div>
<div class="code-name">.icon-gengduo
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-repeat"></span>
<div class="name">
平铺
</div>
<div class="code-name">.icon-repeat
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-caijian"></span>
<div class="name">
@@ -637,6 +682,30 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-hunhemoshi"></use>
</svg>
<div class="name">混合模式</div>
<div class="code-name">#icon-hunhemoshi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-gengduo"></use>
</svg>
<div class="name">更多</div>
<div class="code-name">#icon-gengduo</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-repeat"></use>
</svg>
<div class="name">平铺</div>
<div class="code-name">#icon-repeat</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-caijian"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4292253 */
src: url('iconfont.woff2?t=1762934152017') format('woff2'),
url('iconfont.woff?t=1762934152017') format('woff'),
url('iconfont.ttf?t=1762934152017') format('truetype');
src: url('iconfont.woff2?t=1766460927921') format('woff2'),
url('iconfont.woff?t=1766460927921') format('woff'),
url('iconfont.ttf?t=1766460927921') format('truetype');
}
.iconfont {
@@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-hunhemoshi:before {
content: "\e7a4";
}
.icon-gengduo:before {
content: "\e60f";
}
.icon-repeat:before {
content: "\e8d7";
}
.icon-caijian:before {
content: "\e650";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,27 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "42604348",
"name": "混合模式",
"font_class": "hunhemoshi",
"unicode": "e7a4",
"unicode_decimal": 59300
},
{
"icon_id": "45981931",
"name": "更多",
"font_class": "gengduo",
"unicode": "e60f",
"unicode_decimal": 58895
},
{
"icon_id": "17005660",
"name": "平铺",
"font_class": "repeat",
"unicode": "e8d7",
"unicode_decimal": 59607
},
{
"icon_id": "22138606",
"name": "裁剪",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="201.000000pt" height="200.000000pt" viewBox="0 0 201.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1654 1990 c-26 -8 -153 -110 -162 -129 -1 -3 81 -89 183 -191 l185
-185 56 55 c30 30 62 67 70 82 22 41 18 123 -8 176 -29 57 -126 158 -169 176
-47 20 -118 27 -155 16z"/>
<path d="M868 1243 c-525 -527 -498 -492 -568 -725 -54 -179 -59 -219 -30
-248 19 -19 29 -21 64 -16 113 18 361 105 431 152 28 18 251 235 498 481 l447
448 -188 188 -187 187 -467 -467z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 799 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M757 1273 c-214 -214 -402 -406 -418 -427 -32 -43 -37 -82 -15 -124
23 -44 327 -338 376 -363 l45 -24 463 -3 462 -3 0 46 0 45 -342 0 -343 0 342
343 c187 188 346 352 352 364 16 32 13 74 -7 105 -9 14 -110 116 -224 227
l-207 201 -47 0 -48 0 -389 -387z m207 -342 l209 -209 -128 -131 c-76 -78
-144 -138 -169 -151 -51 -24 -106 -26 -149 -4 -41 20 -337 314 -337 334 0 13
347 370 360 370 3 0 99 -94 214 -209z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M250 1545 l0 -205 80 0 80 0 0 -340 0 -340 -80 0 -80 0 0 -205 0
-205 205 0 205 0 0 80 0 80 340 0 340 0 0 -80 0 -80 205 0 205 0 0 205 0 205
-80 0 -80 0 0 340 0 340 80 0 80 0 0 205 0 205 -205 0 -205 0 0 -80 0 -80
-340 0 -340 0 0 80 0 80 -205 0 -205 0 0 -205z m320 0 l0 -125 -120 0 -120 0
0 125 0 125 120 0 120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125
120 0 120 0 0 -125z m-330 -125 l0 -80 85 0 85 0 0 -340 0 -340 -85 0 -85 0 0
-80 0 -80 -340 0 -340 0 0 80 0 80 -85 0 -85 0 0 340 0 340 85 0 85 0 0 80 0
80 340 0 340 0 0 -80z m-770 -965 l0 -125 -120 0 -120 0 0 125 0 125 120 0
120 0 0 -125z m1100 0 l0 -125 -120 0 -120 0 0 125 0 125 120 0 120 0 0 -125z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 184.06 163.52">
<defs>
<style>
.cls-1, .cls-2 {
fill: #333;
}
.cls-2 {
stroke: #333;
stroke-miterlimit: 10;
}
</style>
</defs>
<path class="cls-2" d="M35.53,54.52c-.76-.96-.84-1.16-2.06-1.12-4.02.13-16.7,6.5-19.81,4.48C12.45,52.21-1.21,21.14.68,18.17c56.98-23.47,125.74-23.63,182.7,0,1.88,2.98-11.77,34.04-12.98,39.72-3.11,2.01-15.78-4.36-19.81-4.48-1.23-.04-1.3.16-2.06,1.12v48c2.87,3.81,14.6,2.98,14.05,8.95-1.06,5.75-12.53,3.84-13.85,8.24l.34,39.85c.08.98-1.55,3.46-2.04,3.46H37.03c-2.08,0-1.5-3.59-1.5-4.5V54.52ZM41.53,47.52v108.5h101v-36c-7.66,1.58-6.82,23.41-12.7,24.86-1.18.17-2.67-.14-3.63-.79-2.98-2.02-6.47-24.26-10.3-25.81-.94-.38-4.46.95-5.31-1.9-1.01-3.4,3.85-4,4.01-5.02-.12-4.15-6.85-15.14-3.84-18.1,4.45-3.04,8.95,1.93,13.76,1.74-1.35-3.15-2.75-11.61,2.46-11.03s3.05,9.89,1.55,13.02l14,4.01v-53.5c0-5.17,22.25,4.69,23.94,3.46,1.7-8.98,7.54-18.42,9.64-26.94.32-1.3.79-1.48-.58-2.5-2.81-2.08-21.4-6.93-25.98-8.02-10.05-2.4-20.36-3.72-30.52-5.53-2.44.17-3.98,7.98-6.05,10.97-13.1,18.91-41.58,12.05-45.92-9.95l-2-1.02c-19.44,3.3-39.69,6.08-57.48,14.6l10.03,28.4c4.22.52,22.41-8.34,23.94-3.46ZM110.53,8.02h-37c4,20.53,33,20.53,37,0Z"/>
<path class="cls-1" d="M89.27,117.8c-.94.96-10.2,1.42-11.25-.78-4.1-8.61,17.67-5.76,11.25.78Z"/>
<path class="cls-1" d="M94.79,117.8c-6.42-6.54,15.35-9.39,11.25-.78-1.05,2.19-10.31,1.74-11.25.78Z"/>
<path class="cls-1" d="M60.62,113.11c1.1-1.64,14.59-2.57,12.84,3.27-1.14,3.81-16.87,2.76-12.84-3.27Z"/>
<path class="cls-1" d="M62.18,46.17h9.69s1.53,3.19,1.53,3.19c-1.22,4.73-18.53,3.97-11.22-3.19Z"/>
<path class="cls-1" d="M112.19,46.18l10.91.77.34,3.47c-.87,2.65-12.93,2.92-12.96-1.9l1.7-2.34Z"/>
<path class="cls-1" d="M56.23,84.18c6.63-1.86,6.45,14.76-.7,11.35-2.24-1.07-2.61-10.42.7-11.35Z"/>
<path class="cls-1" d="M89.37,46.16l.96,3.22c-.81,4.5-16.92,3.98-12.37-2.43l11.42-.79Z"/>
<path class="cls-1" d="M94.68,46.16l11.42.79.34,3.47c-1.03,3.13-16.69,2.9-11.76-4.26Z"/>
<path class="cls-1" d="M54.75,63.76c-1.01-.99-2.01-12.94,3.11-11.63,5.4,1.38,2.78,17.42-3.11,11.63Z"/>
<path class="cls-1" d="M129.31,63.76c-5.88,5.78-8.51-10.25-3.11-11.63,5.12-1.31,4.12,10.64,3.11,11.63Z"/>
<path class="cls-1" d="M59.44,68.12c1.19.8,3.28,13.19-3.02,12.43l-2.23-1.77c-.57-2.83-.34-14.39,5.25-10.66Z"/>
<path class="cls-1" d="M59.44,111.92c-5.59,3.73-5.82-7.83-5.25-10.66l2.23-1.77c6.3-.76,4.22,11.63,3.02,12.43Z"/>
<path class="cls-1" d="M129.32,79.79c-5.12,5.18-7.81-7.81-4.7-11.67l3.28.02,1.48,1.52c-.35,2.45,1.51,8.54-.07,10.13Z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="96.000000pt" height="96.000000pt" viewBox="0 0 96.000000 96.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,96.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M346 936 c-21 -13 -49 -41 -62 -62 -41 -67 -27 -180 27 -218 47 -32
53 -12 12 40 -71 94 -2 229 116 229 37 0 58 -7 87 -28 32 -22 40 -24 42 -12
14 64 -142 100 -222 51z"/>
<path d="M368 877 c-33 -28 -48 -57 -48 -96 0 -39 9 -61 26 -61 10 0 14 13 14
46 0 57 12 79 50 93 61 21 110 -22 110 -96 0 -48 14 -56 31 -19 31 67 -35 156
-114 156 -29 0 -50 -7 -69 -23z"/>
<path d="M580 794 c0 -58 -9 -84 -43 -121 -19 -21 -19 -23 -2 -29 34 -13 85
75 85 148 0 37 -10 58 -26 58 -10 0 -14 -15 -14 -56z"/>
<path d="M410 749 c-13 -6 -28 -15 -32 -22 -4 -7 -8 -106 -8 -222 l-1 -210
-27 34 c-62 80 -89 101 -126 101 -27 0 -39 -6 -52 -25 -15 -24 -15 -28 0 -68
21 -53 78 -123 94 -113 18 11 16 17 -28 75 -44 58 -48 72 -25 91 21 18 54 -6
93 -68 31 -47 74 -78 93 -67 5 4 9 101 9 224 0 225 3 241 42 241 10 0 19 -1
19 -2 1 -2 5 -86 8 -188 5 -165 8 -185 24 -188 15 -3 17 5 17 61 0 72 17 100
45 77 10 -9 15 -32 15 -77 0 -56 2 -64 18 -61 12 2 16 11 14 34 -5 48 14 86
41 82 20 -3 22 -9 25 -66 3 -53 6 -63 20 -60 12 2 19 16 22 43 4 32 10 41 27
43 36 5 46 -36 39 -166 -6 -127 -19 -160 -75 -195 -48 -29 -176 -34 -246 -10
-48 18 -56 25 -130 128 -16 22 -25 26 -37 19 -15 -8 -11 -18 31 -75 78 -105
94 -113 236 -117 119 -3 121 -3 167 27 69 44 82 74 86 214 6 179 -8 221 -72
216 -22 -2 -39 4 -55 20 -17 17 -32 22 -55 19 -19 -2 -38 2 -45 9 -6 7 -25 13
-40 13 l-29 0 -4 100 c-3 103 -11 122 -53 133 -11 3 -31 1 -45 -4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="24" height="24" fill="url(#pattern0_2641_12790)"/>
<defs>
<pattern id="pattern0_2641_12790" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_2641_12790" transform="scale(0.0078125)"/>
</pattern>
<image id="image0_2641_12790" width="128" height="128" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

View File

@@ -0,0 +1,3 @@
<svg width="22" height="12" viewBox="0 0 22 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L11 11L21 1" stroke="#585858" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.6144 18.7388L5.16736 4.29184C4.95412 4.0786 4.60838 4.0786 4.39514 4.29184C4.18189 4.50509 4.18189 4.85082 4.39514 5.06407L18.8421 19.5111C19.0554 19.7243 19.4011 19.7243 19.6144 19.5111C19.8276 19.2978 19.8276 18.9521 19.6144 18.7388Z" fill="#232323"/>
<path d="M5.15908 19.5378L19.6061 5.09079C19.8193 4.87755 19.8193 4.53181 19.6061 4.31857C19.3928 4.10533 19.0471 4.10533 18.8339 4.31857L4.38685 18.7656C4.17361 18.9788 4.17361 19.3246 4.38685 19.5378C4.6001 19.751 4.94583 19.751 5.15908 19.5378Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -0,0 +1,9 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="20" height="20" rx="2.79453" fill="url(#pattern0_226_198)"/>
<defs>
<pattern id="pattern0_226_198" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_226_198" transform="scale(0.0104167)"/>
</pattern>
<image id="image0_226_198" width="96" height="96" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@@ -0,0 +1,10 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_226_195)">
<path d="M-0.00195312 1.4335C-0.00195312 0.641696 0.656004 0 1.46782 0H18.5422C19.354 0 20.0119 0.641696 20.0119 1.4335V18.5804C20.0119 19.3722 19.354 20.0139 18.5422 20.0139H1.46782C0.656004 20.0139 -0.00195312 19.3722 -0.00195312 18.5804V1.4335ZM6.18109 16.7541V7.71661H3.17776V16.7541H6.18109ZM4.68005 6.482C5.72703 6.482 6.37873 5.78902 6.37873 4.92092C6.35996 4.03405 5.72828 3.35983 4.70006 3.35983C3.67185 3.35983 3.00013 4.0353 3.00013 4.92092C3.00013 5.78902 3.65183 6.482 4.66003 6.482H4.68005ZM10.8193 16.7541V11.7069C10.8193 11.4367 10.8393 11.1665 10.9194 10.9739C11.1358 10.4347 11.6299 9.87561 12.4605 9.87561C13.5475 9.87561 13.9815 10.7037 13.9815 11.9195V16.7541H16.9848V11.5705C16.9848 8.79361 15.5038 7.50271 13.5274 7.50271C11.9338 7.50271 11.2196 8.37832 10.8193 8.995V9.02627H10.7993L10.8193 8.995V7.71661H7.81723C7.85475 8.5647 7.81723 16.7541 7.81723 16.7541H10.8193Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_226_195">
<rect width="20.0139" height="20.0139" rx="1.55252" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,12 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_226_203)">
<path d="M20 17C20 18.6569 18.6569 20 17 20H3C1.34307 20 0 18.6569 0 17V3C0 1.34313 1.34313 0 3 0H17C18.6569 0 20 1.34313 20 3V17Z" fill="white"/>
<path d="M13.3881 7.45803C11.7739 7.54236 10.3702 8.03169 9.2306 9.13723C8.0792 10.2542 7.55366 11.6228 7.69726 13.3195C7.06633 13.2414 6.49166 13.1554 5.91373 13.1067C5.71413 13.0899 5.47726 13.1138 5.3082 13.2092C4.747 13.5258 4.20906 13.8834 3.57133 14.282C3.68833 13.7528 3.76406 13.2894 3.89813 12.8437C3.99673 12.5161 3.95106 12.3338 3.64926 12.1204C1.71153 10.7523 0.894728 8.70496 1.50599 6.59703C2.07153 4.64703 3.46033 3.46443 5.3474 2.84796C7.92313 2.00663 10.8177 2.86483 12.3839 4.90976C12.9495 5.64843 13.2964 6.47749 13.3881 7.45803ZM5.95893 6.80123C5.9738 6.41569 5.63973 6.06836 5.24293 6.05676C4.83666 6.04483 4.50253 6.35529 4.49066 6.75563C4.47866 7.16136 4.789 7.48649 5.1982 7.49689C5.60386 7.50716 5.94393 7.19629 5.95893 6.80123ZM9.835 6.05649C9.43673 6.06383 9.1002 6.40303 9.10726 6.79009C9.11453 7.19129 9.44466 7.50516 9.8542 7.50023C10.2648 7.49529 10.5762 7.17809 10.5723 6.76843C10.5689 6.36629 10.2403 6.04909 9.835 6.05649Z" fill="#232323"/>
<path d="M17.0146 17.5216C16.5035 17.294 16.0346 16.9525 15.5354 16.9004C15.0381 16.8484 14.5154 17.1353 13.9951 17.1885C12.4104 17.3506 10.9907 16.909 9.82001 15.8264C7.59355 13.767 7.91168 10.6094 10.4876 8.92184C12.777 7.42197 16.1345 7.92197 17.7487 10.0031C19.1573 11.8191 18.9917 14.2298 17.2721 15.7554C16.7745 16.1969 16.5955 16.5602 16.9148 17.1423C16.9737 17.2498 16.9804 17.3858 17.0146 17.5216ZM11.1963 11.8883C11.5217 11.8886 11.7897 11.634 11.802 11.3126C11.815 10.9723 11.5413 10.6869 11.2006 10.6855C10.8632 10.684 10.5806 10.9734 10.5924 11.3086C10.6035 11.6288 10.8733 11.8879 11.1963 11.8883ZM14.9471 10.6868C14.6314 10.6846 14.3631 10.9431 14.3502 11.2621C14.3365 11.6032 14.6017 11.8834 14.9393 11.8842C15.2658 11.8854 15.5239 11.6384 15.5357 11.3134C15.5483 10.9715 15.2831 10.6892 14.9471 10.6868Z" fill="#232323"/>
</g>
<defs>
<clipPath id="clip0_226_203">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -2488,3 +2488,22 @@ textarea:focus {
background: #ffffff;
color: #000;
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.flex-center {
justify-content: center;
align-items: center;
}
.align-center {
align-items: center;
}
.space-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}

View File

@@ -2406,3 +2406,22 @@ textarea:focus{
color: #000;
}
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.flex-center {
justify-content: center;
align-items: center;
}
.align-center {
align-items: center;
}
.space-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}

View File

@@ -96,7 +96,7 @@
style="width: 250px"
/>
</div>
<div class="admin_state_item" v-if="title?.value == 'Edit'">
<!-- <div class="admin_state_item" v-if="title?.value == 'Edit'">
<span>
{{ $t('admin.SubscribePlan') }}:
<span>*</span>
@@ -108,7 +108,7 @@
:field-names="{ label: 'name', value: 'id' }"
:placeholder="$t('admin.SelectPlan')"
></a-select>
</div>
</div> -->
</div>
<div class="allUserPoeration_btn admin_page">
<div class="admin_search_item" @click="cancelDsign">{{ $t('admin.Close') }}</div>

File diff suppressed because it is too large Load Diff

View File

@@ -36,27 +36,9 @@
</div>
<div class="admin_state_item">
<span>{{ $t("admin.Email") }}:</span>
<input
v-model="email"
:placeholder="$t('admin.enterEmail')"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>{{ $t("admin.UserName") }}:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
:placeholder="$t('admin.selectUserName')"
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
<SelectUser v-model="email" labelKey="email" valueKey="email" />
</div>
<div class="admin_state_item">
<span>Organization Name:</span>
<input
@@ -100,8 +82,9 @@
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import { useI18n } from "vue-i18n";
import SelectUser from "@/component/common/SelectUser.vue";
export default defineComponent({
components: {},
components: { SelectUser },
setup() {
const store: any = useStore();
let rangePickerValue: any = ref([]);
@@ -176,9 +159,6 @@
];
});
let allUserList: any = computed(() => {
return store.state.adminPage.allUserList;
});
let ids = ref([]);
let email = ref("");
let dataList: any = ref([]);
@@ -193,7 +173,6 @@
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
@@ -251,7 +230,7 @@
endTime: endDate,
startTime: startDate,
ids: ids,
email: this.email.trim(),
email: this.email?.trim(),
organizationName: this.organizationName,
};
Https.axiosGet(Https.httpUrls.getDesignStatistic, {

View File

@@ -220,7 +220,7 @@ export default defineComponent({
changeEvent:this.changeEvent,
size:this.pageSize,
page:this.currentPage,
email:this.email.trim(),
email:this.email?.trim(),
}
Https.axiosPost(Https.httpUrls.getGenerateFrequency,data).then((rv: any) => {
if (rv) {

View File

@@ -25,15 +25,7 @@
</div>
<div class="admin_state_item">
<span>{{ $t('admin.UserName') }}:</span>
<a-select
v-model:value="userIdList"
mode="multiple"
style="width: 280px"
:filter-option="filterOption"
:placeholder="$t('admin.selectUserName')"
max-tag-count="responsive"
:options="dataList"
></a-select>
<SelectUser v-model="userIdList" labelKey="email" multiple />
</div>
</div>
@@ -78,16 +70,15 @@ import { LabelLayout } from 'echarts/features';
import { useStore } from "vuex";
import { CanvasRenderer } from 'echarts/renderers';
import { useI18n } from 'vue-i18n'
import SelectUser from '@/component/common/SelectUser.vue'
export default defineComponent({
components: {
SelectUser
},
setup() {
const {t} = useI18n()
const store:any = useStore()
let filter:any = reactive({
dataList:computed(()=>{
return store.state.adminPage.allUserList
}),
})
let filterData:any = reactive({

View File

@@ -32,27 +32,16 @@
</div>
<div class="admin_state_item">
<span>Email:</span>
<input
<!-- <input
v-model="email"
placeholder="Please enter email"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>User Name:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
/> -->
<SelectUser v-model="email" labelKey="email" valueKey="email" />
</div>
<div class="admin_state_item">
<span>Organization Name:</span>
<input
@@ -95,8 +84,11 @@
import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import SelectUser from "@/component/common/SelectUser.vue";
export default defineComponent({
components: {},
components: {
SelectUser
},
setup() {
const store: any = useStore();
let rangePickerValue: any = ref([]);
@@ -238,9 +230,6 @@
];
});
let allUserList: any = computed(() => {
return store.state.adminPage.allUserList;
});
let ids = ref([]);
let email = ref("");
let dataList: any = ref([]);
@@ -255,7 +244,6 @@
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
@@ -312,7 +300,7 @@
endTime: endDate,
startTime: startDate,
ids: ids,
email: this.email.trim(),
email: this.email?.trim(),
organizationName: this.organizationName,
};
Https.axiosGet(Https.httpUrls.getDesignStatistic, {

View File

@@ -31,7 +31,7 @@
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="countryList"
:options="allCountry"
></a-select>
</div>
<div class="admin_state_item">
@@ -192,9 +192,6 @@ export default defineComponent({
cityList: computed(()=>{
return store.state.adminPage.city
}),
countryList: computed(()=>{
return store.state.adminPage.country
}),
isAwayOrUnfold:false,
});
let filterData: any = reactive({
@@ -471,9 +468,10 @@ export default defineComponent({
filter.dataList = rv.content;
filterData.total = rv.total;
filter.tableLoading = false;
rv.content.forEach((item: any) => {
filterData.totalPayer += Number(item.payerTotal)
})
filterData.totalPayer = rv.content.reduce((total: number, item: any) => {
const value = item && item.status === 'Success' ? parseFloat(item.payerTotal) : 0;
return total + (isNaN(value) ? 0 : value);
}, 0);
// this.workspaceItem.position = this.singleTypeList[0].label
}

View File

@@ -139,9 +139,6 @@ export default defineComponent({
let filter: any = reactive({
dataList: [],
tableLoading: false,
allUserList: computed(()=>{
return store.state.adminPage.allUserList
}),
rowSelection:computed(() => {
return {
selectedRowKeys: unref(selectedRowKeys),

View File

@@ -40,27 +40,16 @@
</div>
<div class="admin_state_item">
<span>Email:</span>
<input
<!-- <input
v-model="email"
placeholder="Please enter email"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>User Name:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
/> -->
<SelectUser v-model="email" labelKey="email" valueKey="email" />
</div>
<div class="admin_state_item">
<span>User Type:</span>
<a-select
@@ -160,16 +149,14 @@ import { formatTime } from "@/tool/util";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import allUserPoerationsVue from "./allUserPoerations.vue";
import SelectUser from '@/component/common/SelectUser.vue'
export default defineComponent({
components: {allUserPoerationsVue,},
components: {allUserPoerationsVue,SelectUser},
setup() {
const store:any = useStore()
let filter: any = reactive({
dataList: [],
tableLoading: false,
allUserList: computed(()=>{
return store.state.adminPage.allUserList
}),
allCountry:[],
isAwayOrUnfold:false
});
@@ -436,7 +423,7 @@ export default defineComponent({
page: filterData.currentPage,
systemUser: filterData.systemUser,
country: filterData.country,
email: filterData.email.trim(),
email: filterData.email?.trim(),
userType: filterData.userType,
ids: filterData.ids,
occupation: filterData.occupation,

View File

@@ -1,352 +1,392 @@
<template>
<div class="allUserPoerationModal" ref="allUserPoerationModal"></div>
<a-modal
class="allUserPoeration_modal generalModel"
v-model:visible="operationsModal"
:footer="null"
:get-container="() => $refs.allUserPoerationModal"
width="50%"
:maskClosable="false"
:centered="true"
:closable="false"
:mask="true"
wrapClassName="#app"
:keyboard="false"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
<svg width="100%" height="100%" viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3"/>
<rect x="32.5063" y="12" width="3" height="29" rx="1.5" transform="rotate(45 32.5063 12)" fill="white"/>
<rect x="34.6274" y="32.5059" width="3" height="29" rx="1.5" transform="rotate(135 34.6274 32.5059)" fill="white"/>
</svg>
</div>
</div>
<div class="modal_title_text">
<div>{{ title }} User</div>
</div>
<div class="allUserPoeration_center admin_page">
<div class="admin_state_item">
<span>User Name: <span>*</span></span>
<input
:disabled="title != 'Add'"
:class="{active:title != 'Add'}"
v-model="userName"
placeholder="Please enter user name"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>User Email: <span>*</span></span>
<input
:disabled="title != 'Add'"
:class="{active:title != 'Add'}"
v-model="userEmail"
placeholder="Please enter email"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>Create Time: <span>*</span></span>
<a-date-picker :disabled="title != 'Add'" style="width: 250px" valueFormat="YYYY-MM-DDTHH:mm:ss" class="range_picker" show-time placeholder="Create Time" v-model:value="validStartTime">
<template #suffixIcon>
<span
class="icon iconfont range_picker_icon icon-rili"
></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>End Time: <span>*</span></span>
<a-date-picker style="width: 250px" valueFormat="YYYY-MM-DDTHH:mm:ss" class="range_picker" show-time placeholder="End Time" v-model:value="validEndTime">
<template #suffixIcon>
<span
class="icon iconfont range_picker_icon icon-rili"
></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>User Type:<span>*</span></span>
<a-select
v-model:value="systemUser"
size="large"
style="width: 250px"
optionFilterProp="label"
:options="state"
placeholder="Please select"
allowClear
show-search
></a-select>
</div>
<div class="admin_state_item">
<span>Credits:</span>
<input
v-model="credits"
placeholder="Please enter credits"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>Country or Region:</span>
<input
<div class="allUserPoerationModal" ref="allUserPoerationModal"></div>
<a-modal
class="allUserPoeration_modal generalModel"
v-model:visible="operationsModal"
:footer="null"
:get-container="() => $refs.allUserPoerationModal"
width="50%"
:maskClosable="false"
:centered="true"
:closable="false"
:mask="true"
wrapClassName="#app"
:keyboard="false"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
<svg
width="100%"
height="100%"
viewBox="0 0 46 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="23" cy="23" r="23" fill="#000" fill-opacity="0.3" />
<rect
x="32.5063"
y="12"
width="3"
height="29"
rx="1.5"
transform="rotate(45 32.5063 12)"
fill="white"
/>
<rect
x="34.6274"
y="32.5059"
width="3"
height="29"
rx="1.5"
transform="rotate(135 34.6274 32.5059)"
fill="white"
/>
</svg>
</div>
</div>
<div class="modal_title_text">
<div>{{ title }} User</div>
</div>
<div class="allUserPoeration_center admin_page">
<div class="admin_state_item">
<span>
User Name:
<span>*</span>
</span>
<input
:disabled="title != 'Add'"
:class="{ active: title != 'Add' }"
v-model="userName"
placeholder="Please enter user name"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>
User Email:
<span>*</span>
</span>
<input
:disabled="title != 'Add'"
:class="{ active: title != 'Add' }"
v-model="userEmail"
placeholder="Please enter email"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>
Create Time:
<span>*</span>
</span>
<a-date-picker
:disabled="title != 'Add'"
style="width: 250px"
valueFormat="YYYY-MM-DDTHH:mm:ss"
class="range_picker"
show-time
placeholder="Create Time"
v-model:value="validStartTime"
>
<template #suffixIcon>
<span class="icon iconfont range_picker_icon icon-rili"></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>
End Time:
<span>*</span>
</span>
<a-date-picker
style="width: 250px"
valueFormat="YYYY-MM-DDTHH:mm:ss"
class="range_picker"
show-time
placeholder="End Time"
v-model:value="validEndTime"
>
<template #suffixIcon>
<span class="icon iconfont range_picker_icon icon-rili"></span>
</template>
</a-date-picker>
</div>
<div class="admin_state_item">
<span>
User Type:
<span>*</span>
</span>
<a-select
v-model:value="systemUser"
size="large"
style="width: 250px"
optionFilterProp="label"
:options="state"
placeholder="Please select"
allowClear
show-search
></a-select>
</div>
<div class="admin_state_item">
<span>Credits:</span>
<input
v-model="credits"
placeholder="Please enter credits"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>Country or Region:</span>
<!-- <input
:disabled="title != 'Add'"
:class="{active:title != 'Add'}"
v-model="country"
placeholder="Please enter country"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>Organization Name:</span>
<input
:disabled="title != 'Add'"
:class="{active:title != 'Add'}"
v-model="organizationName"
placeholder="Please enter Organization Name"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>Sub Account Num:</span>
<input
:disabled="title != 'Add'"
:class="{active:title != 'Add'}"
v-model="subAccountNum"
placeholder="Please enter Sub Account Num"
type="number"
style="width: 250px"
/>
</div>
</div>
<div class="allUserPoeration_btn admin_page">
<div class="admin_search_item" @click="cancelDsign">
Close
</div>
<div class="admin_search_item" @click="setOk">
OK
</div>
</div>
</a-modal>
<div class="mark_loading" v-show="loadingShow">
<a-spin size="large" />
</div>
/> -->
<a-select
v-model:value="country"
:disabled="title != 'Add'"
:class="{ active: title != 'Add' }"
:allowClear="true"
show-search
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Country or Region"
max-tag-count="responsive"
:options="allCountry"
/>
</div>
</div>
<div class="allUserPoeration_btn admin_page">
<div class="admin_search_item" @click="cancelDsign">Close</div>
<div class="admin_search_item" @click="setOk">OK</div>
</div>
</a-modal>
<div class="mark_loading" v-show="loadingShow">
<a-spin size="large" />
</div>
</template>
<script>
import { defineComponent, ref, reactive, watch, onMounted, nextTick, toRefs } from "vue";
import { Https } from "@/tool/https";
import { Modal, message } from "ant-design-vue";
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";
import { formatTime } from "@/tool/util";
import { defineComponent, ref, reactive, watch, onMounted, nextTick, toRefs } from 'vue'
import { Https } from '@/tool/https'
import { Modal, message } from 'ant-design-vue'
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { formatTime } from '@/tool/util'
export default defineComponent({
components: {
},
emits: ['searchHistoryList'],
setup(props,{emit}) {
let operations = reactive({
operationsModal:false,
operationsEdit:false,
loadingShow:false,
title:''
})
let operationsData = reactive({
accountId:-1,
userName:'',
userEmail:'',
validStartTime:'',
validEndTime:'',
systemUser:'',
credits:'',
country:'',
organizationName:'',
subAccountNum:0,
})
let state = ref([
{
label:'visitor',
value:'0',
},
{
label:'yearly',
value:'1',
},
{
label:'monthly',
value:'2',
},
{
label:'trial',
value:'3',
},
{
label: "userInEvent",
value: "4",
},
{
label: "Edu Admin",
value: "7",
},
]);
let init = (funStr,data)=>{
operations.operationsModal = true
operations.operationsEdit = true
operations.title = funStr
if(funStr == 'Add') operations.operationsEdit = false
if(funStr == 'Edit'){
operationsData.organizationName = data.organizationName
operationsData.subAccountNum = data.subAccountNum?data.subAccountNum:0
let startTime = data.validStartTime?formatTime(data.validStartTime / 1000,"YYYY-MM-DDThh:mm:ss"):''
let endTime = data.validEndTime?formatTime(data.validEndTime / 1000,"YYYY-MM-DDThh:mm:ss"):''
operationsData.accountId=data.id
operationsData.userName=data.userName
operationsData.userEmail=data.userEmail
// operationsData.validStartTime='2024-08-05T00:00:06'
// operationsData.validEndTime='2024-08-05T00:00:06'
operationsData.validStartTime=startTime
operationsData.validEndTime=endTime
operationsData.systemUser=String(data.systemUser)
operationsData.credits=data.credits
operationsData.country=data.country
// operationsData.accountId = data.accountId
// operationsData.userName = data.userName
// operationsData.userEmail = data.userEmail
// operationsData.validStartTime = formatTime(data.validStartTime)
// operationsData.validEndTime = formatTime(data.validEndTime)
}
components: {},
emits: ['searchHistoryList'],
setup(props, { emit }) {
let operations = reactive({
operationsModal: false,
operationsEdit: false,
loadingShow: false,
title: ''
})
let operationsData = reactive({
accountId: -1,
userName: '',
userEmail: '',
validStartTime: '',
validEndTime: '',
systemUser: '',
credits: '',
country: ''
})
let state = ref([
{
label: 'visitor',
value: '0'
},
{
label: 'yearly',
value: '1'
},
{
label: 'monthly',
value: '2'
},
{
label: 'trial',
value: '3'
},
{
label: 'userInEvent',
value: '4'
},
{
label: 'Edu Admin',
value: '7'
}
])
let init = (funStr, data) => {
operations.operationsModal = true
operations.operationsEdit = true
operations.title = funStr
if (funStr == 'Add') operations.operationsEdit = false
if (funStr == 'Edit') {
operationsData.organizationName = data.organizationName
operationsData.subAccountNum = data.subAccountNum ? data.subAccountNum : 0
let startTime = data.validStartTime
? formatTime(data.validStartTime / 1000, 'YYYY-MM-DDThh:mm:ss')
: ''
let endTime = data.validEndTime
? formatTime(data.validEndTime / 1000, 'YYYY-MM-DDThh:mm:ss')
: ''
operationsData.accountId = data.id
operationsData.userName = data.userName
operationsData.userEmail = data.userEmail
// operationsData.validStartTime='2024-08-05T00:00:06'
// operationsData.validEndTime='2024-08-05T00:00:06'
operationsData.validStartTime = startTime
operationsData.validEndTime = endTime
operationsData.systemUser = String(data.systemUser)
operationsData.credits = data.credits
operationsData.country = data.country
// operationsData.accountId = data.accountId
// operationsData.userName = data.userName
// operationsData.userEmail = data.userEmail
// operationsData.validStartTime = formatTime(data.validStartTime)
// operationsData.validEndTime = formatTime(data.validEndTime)
}
}
let setTime = time => {
if (time) {
const date = new Date(time)
const timestamp = date.getTime() // 转换为秒数
return timestamp
} else {
return ''
}
}
let setAddData = () => {
return {
country: operationsData.country,
credits: operationsData.credits,
systemUser: operationsData.systemUser,
userEmail: operationsData.userEmail,
userName: operationsData.userName,
validEndTime: setTime(operationsData.validEndTime),
validStartTime: setTime(operationsData.validStartTime),
organizationName: operationsData.organizationName
? operationsData.organizationName
: null,
subAccountNum: operationsData.subAccountNum
}
}
let setEditData = () => {
return {
accountId: operationsData.accountId,
credits: operationsData.credits,
systemUser: operationsData.systemUser,
validEndTime: setTime(operationsData.validEndTime),
userName: operationsData.userName,
userEmail: operationsData.userEmail
}
}
let cancelDsign = () => {
operationsData.accountId = -1
operationsData.userName = ''
operationsData.userEmail = ''
operationsData.validStartTime = ''
operationsData.validEndTime = ''
operationsData.systemUser = ''
operationsData.credits = ''
operationsData.country = ''
operations.operationsModal = false
}
let setOk = () => {
let data
if (operations.title == 'Add') {
data = setAddData()
if (
!data.userName ||
!data.userEmail ||
!data.validStartTime ||
!data.validEndTime ||
!data.systemUser
)
return message.warning('Please check the input box marked with *')
Https.axiosPost(Https.httpUrls.adminAddUser, data).then(rv => {
if (rv) {
cancelDsign()
emit('searchHistoryList')
}
})
} else {
data = setEditData()
if (!data.userName || !data.userEmail || !data.validEndTime || !data.systemUser)
return message.warning('Please check the input box marked with *')
Https.axiosPost(Https.httpUrls.modifyUser, {}, { params: data }).then(rv => {
if (rv) {
cancelDsign()
emit('searchHistoryList')
}
})
}
}
}
let setTime = (time) =>{
if(time){
const date = new Date(time);
const timestamp = date.getTime(); // 转换为秒数
return timestamp
}else{
return ''
}
}
let setAddData = ()=>{
return {
"country": operationsData.country,
"credits": operationsData.credits,
"systemUser": operationsData.systemUser,
"userEmail": operationsData.userEmail,
"userName": operationsData.userName,
"validEndTime": setTime(operationsData.validEndTime),
"validStartTime": setTime(operationsData.validStartTime),
"organizationName": operationsData.organizationName?operationsData.organizationName:null,
"subAccountNum": operationsData.subAccountNum,
}
}
let setEditData = ()=>{
return {
"accountId": operationsData.accountId,
"credits": operationsData.credits,
"systemUser": operationsData.systemUser,
"validEndTime": setTime(operationsData.validEndTime),
"userName": operationsData.userName,
"userEmail": operationsData.userEmail,
}
}
let cancelDsign = ()=>{
operationsData.accountId=-1
operationsData.userName=''
operationsData.userEmail=''
operationsData.validStartTime=''
operationsData.validEndTime=''
operationsData.systemUser=''
operationsData.credits=''
operationsData.country=''
operations.operationsModal = false
}
let setOk = ()=>{
let data
if(operations.title == 'Add'){
data = setAddData()
if(!data.userName || !data.userEmail || !data.validStartTime || !data.validEndTime || !data.systemUser)return message.warning('Please check the input box marked with *')
Https.axiosPost(Https.httpUrls.adminAddUser, data).then(
(rv) => {
if (rv) {
cancelDsign()
emit('searchHistoryList')
}
}
);
}else{
data = setEditData()
if(!data.userName || !data.userEmail || !data.validEndTime || !data.systemUser)return message.warning('Please check the input box marked with *')
Https.axiosPost(Https.httpUrls.modifyUser,{},{params:data}).then(
(rv) => {
if (rv) {
cancelDsign()
emit('searchHistoryList')
}
}
);
}
}
return {
...toRefs(operations),
...toRefs(operationsData),
state,
cancelDsign,
init,
setOk,
};
},
data() {
return {
};
},
mounted() {},
methods: {
},
});
const allCountry = ref([])
const filterOption = (input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
onMounted(() => {
const countryList = sessionStorage.getItem('allCountry')
if (countryList) {
allCountry.value = JSON.parse(countryList)
}
})
return {
...toRefs(operations),
...toRefs(operationsData),
state,
cancelDsign,
init,
setOk,
allCountry,
filterOption
}
}
})
</script>
<style lang="less" scoped>
:deep(.allUserPoeration_modal){
.ant-modal-body{
height: auto;
display: flex;
flex-direction: column;
}
:deep(.allUserPoeration_modal) {
.ant-modal-body {
height: auto;
display: flex;
flex-direction: column;
}
}
</style>
<style lang="less" scoped>
.allUserPoeration_modal {
.closeIcon {
z-index: 2;
.closeIcon {
z-index: 2;
}
> .admin_state_item {
> span {
width: 15rem;
}
> .admin_state_item{
> span{
width: 15rem;
}
}
.allUserPoeration_btn{
display: flex;
flex-direction: row;
height: auto;
justify-content: flex-end;
padding: 1rem 0;
.admin_search_item{
margin-bottom: 0;
}
}
.allUserPoeration_center{
flex: 1;
overflow-y: auto;
flex-direction: row;
flex-wrap: wrap;
}
}
.allUserPoeration_btn {
display: flex;
flex-direction: row;
height: auto;
justify-content: flex-end;
padding: 1rem 0;
.admin_search_item {
margin-bottom: 0;
}
}
.allUserPoeration_center {
flex: 1;
overflow-y: auto;
flex-direction: row;
flex-wrap: wrap;
}
}
</style>
</style>

View File

@@ -25,15 +25,7 @@
</div>
<div class="admin_state_item">
<span>User:</span>
<a-select
v-model:value="userIdList"
mode="multiple"
style="width: 280px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="dataList"
></a-select>
<SelectUser v-model="userIdList" labelKey="email" multiple />
</div>
</div>
@@ -62,15 +54,14 @@ import { PieChart } from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { useStore } from "vuex";
import { CanvasRenderer } from 'echarts/renderers';
import SelectUser from '@/component/common/SelectUser.vue';
export default defineComponent({
components: {
SelectUser
},
setup() {
const store:any = useStore()
let filter:any = reactive({
dataList:computed(()=>{
return store.state.adminPage.allUserList
}),
})
let filterData:any = reactive({

View File

@@ -11,13 +11,13 @@
<a-form-item label="ID">
<a-input v-model:value="searchForm.id" allow-clear placeholder="Input the id" />
</a-form-item>
<a-form-item label="Name">
<!-- <a-form-item label="Name">
<a-input
v-model:value="searchForm.name"
allow-clear
placeholder="Input the name"
/>
</a-form-item>
</a-form-item> -->
<a-form-item label="Time Range">
<a-range-picker
v-model:value="searchForm.dateRange"
@@ -48,15 +48,7 @@
</a-select>
</a-form-item>
<a-form-item label="Admin Account">
<a-select
v-model:value="searchForm.adminAccId"
allow-clear
show-search
:filter-option="filterOption"
placeholder="Select Account"
style="width: 180px"
:options="allUserList"
></a-select>
<SelectUser v-model="searchForm.adminAccId" labelKey="email" />
</a-form-item>
<a-form-item label="Status">
<a-select
@@ -68,6 +60,18 @@
:options="statusOption"
/>
</a-form-item>
<a-form-item label="Country or Region">
<a-select
v-model:value="searchForm.countryOrRegion"
:allowClear="true"
show-search
style="width: 250px"
:filter-option="filterOption"
placeholder="Select the country or region"
max-tag-count="responsive"
:options="countryList"
/>
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">Search</a-button>
@@ -114,9 +118,6 @@
{{ record.status }}
</a-tag>
</template>
<template v-if="column.key === 'adminAccId'">
{{ allUserList.find(item => item.value === record.adminAccId)?.label }}
</template>
<template v-else-if="column.key === 'actions'">
<a-space>
@@ -226,19 +227,26 @@
</div>
<div class="admin_state_item">
<span>
Admin Account:
Country Or Region:
<span>*</span>
</span>
<a-select
v-model:value="formState.adminAccId"
placeholder="Select the admin account"
v-model:value="formState.countryOrRegion"
placeholder="Select the country or region"
allow-clear
show-search
:filter-option="filterOption"
style="width: 250px"
:options="allUserList"
:options="countryList"
></a-select>
</div>
<div class="admin_state_item">
<span>
Admin Account:
<span>*</span>
</span>
<SelectUser ref="userRef" v-model="formState.adminAccId" labelKey="email" />
</div>
<div class="admin_state_item">
<span>
Start Time:
@@ -247,6 +255,7 @@
<a-date-picker
v-model:value="formState.currentPeriodStart"
value-format="X"
:disabled="isEditMode && formState.status !== 'PENDING'"
style="width: 250px"
:disabledDate="disabledDate"
:show-time="{ format: 'HH:mm' }"
@@ -267,7 +276,9 @@
v-model:value="formState.currentPeriodEnd"
value-format="X"
:show-time="{ format: 'HH:mm' }"
:disabledDate="disabledDate"
:disabledDate="disableEndDate"
:disabledTime="disableEndTime"
:show-now="!isEditMode"
style="width: 250px"
class="range_picker"
placeholder="Select the end time"
@@ -279,7 +290,7 @@
</div>
<div class="admin_state_item">
<span>
Account Num:
Sub-Account Num:
<span>*</span>
</span>
<a-input-number
@@ -386,6 +397,7 @@ import {
nextTick,
useTemplateRef
} from 'vue'
import SelectUser from '@/component/common/SelectUser.vue'
import { message } from 'ant-design-vue'
import { Https } from '@/tool/https'
import { formatTime } from '@/tool/util'
@@ -409,9 +421,8 @@ interface SubscriptionPlan {
endStamp: number
}
const disabledDate = (current: Dayjs) => {
return current && current < dayjs().subtract(1, 'days').endOf('day')
}
const countryList = ref([])
const userRef = ref(null)
const searchForm = reactive({
name: '',
@@ -421,6 +432,7 @@ const searchForm = reactive({
adminAccId: undefined as string | undefined,
status: [] as PlanStatus[] | [],
id: '',
countryOrRegion: null,
page: 1,
size: 10,
total: 0
@@ -443,7 +455,8 @@ const formState = reactive({
adminAccId: undefined as string | undefined,
creditLimit: null as number | null,
accountNum: null as number | null,
status: undefined as PlanStatus | undefined
status: undefined as PlanStatus | undefined,
countryOrRegion: null as string | null
})
const organizationModalVisible = ref(false)
@@ -478,6 +491,64 @@ const statusOption = ref([
}
])
const disabledDate = (current: Dayjs) => {
return current && current < dayjs().subtract(1, 'days').endOf('day')
}
const disableEndDate = (current: Dayjs) => {
if (isEditMode.value) {
const specificTime = dayjs(formState.currentPeriodEnd)
return current && current < dayjs(formState.currentPeriodEnd * 1000).startOf('day')
}
return disabledDate(current)
}
const range = (start: number, end: number) => {
const result = []
for (let i = start; i < end; i++) {
result.push(i)
}
return result
}
const disableEndTime = date => {
if (!formState.currentPeriodEnd || !isEditMode.value)
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => []
}
const specificTime = dayjs.unix(formState.currentPeriodEnd)
if (date && date.isSame(specificTime, 'day')) {
// 如果是指定日期当天,禁用时间戳之前的时间
const hour = specificTime.hour()
const minute = specificTime.minute()
const second = specificTime.second()
return {
disabledHours: () => Array.from({ length: hour }, (_, i) => i), // 禁用小时之前
disabledMinutes: selectedHour => {
if (selectedHour === hour) {
return Array.from({ length: minute }, (_, i) => i) // 同小时,禁用分钟之前
}
return []
},
disabledSeconds: (selectedHour, selectedMinute) => {
if (selectedHour === hour && selectedMinute === minute) {
return Array.from({ length: second }, (_, i) => i) // 同小时分钟,禁用秒之前
}
return []
}
}
}
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => []
}
}
const normalizeStatus = (status?: string): PlanStatus | undefined => {
if (!status) return undefined
const upper = status.toUpperCase() as PlanStatus
@@ -498,17 +569,27 @@ const columns = [
},
{
title: 'Admin Account',
dataIndex: 'adminAccId',
key: 'adminAccId',
dataIndex: 'adminAccEmail',
key: 'adminAccEmail',
align: 'center',
width: 180
width: 180,
ellipsis: true
},
{
title: 'Account Num',
title: 'Sub-Account Num',
dataIndex: 'accountNum',
key: 'accountNum',
align: 'center',
width: 120
width: 120,
ellipsis: true
},
{
title: 'Country or Region',
dataIndex: 'countryOrRegion',
key: 'countryOrRegion',
align: 'center',
width: 120,
ellipsis: true
},
{
title: 'Start Time',
@@ -558,6 +639,8 @@ onMounted(async () => {
await handleSearch()
calculateTableHeight()
window.addEventListener('resize', handleResize)
const list = sessionStorage.getItem('allCountry')
countryList.value = list ? JSON.parse(list) : []
})
onBeforeUnmount(() => {
@@ -585,6 +668,7 @@ const resetFormState = () => {
formState.creditLimit = null
formState.accountNum = null
formState.status = undefined
formState.countryOrRegion = null
}
const changePage = (pagination: any) => {
@@ -606,12 +690,10 @@ const handleReset = () => {
searchForm.adminAccId = undefined
searchForm.status = []
searchForm.id = ''
searchForm.countryOrRegion = null
handleSearch()
}
const allUserList = computed(() => {
return store.state.adminPage.allUserList
})
const openCreate = () => {
modalTitle.value = 'New Subscription Plan'
isEditMode.value = false
@@ -631,6 +713,7 @@ const openEdit = (record: SubscriptionPlan) => {
formState.accountNum = (record as any).accountNum || null
formState.status = record.status
formState.id = record.id
formState.countryOrRegion = (record as any).countryOrRegion || null
// 检查组织ID是否在已加载的组织列表中如果不在则添加临时项
if (record.organizationId) {
@@ -640,7 +723,6 @@ const openEdit = (record: SubscriptionPlan) => {
String(org.id) === String(record.organizationId)
)
if (!orgExists) {
// 从表格数据中获取组织名称,如果存在则添加临时项
const orgName = (record as any).organizationName
if (orgName) {
organizationOptions.value = [
@@ -653,8 +735,16 @@ const openEdit = (record: SubscriptionPlan) => {
}
}
}
modalVisible.value = true
if (record.adminAccId) {
nextTick(() => {
userRef.value.patchList({
label: record.name,
value: record.adminAccId,
email: record.adminAccEmail
})
})
}
}
const validateForm = (): boolean => {
@@ -949,9 +1039,15 @@ const filterOption = (input: string, option: any) => {
}
.subscriptionPlan_modal {
> .admin_state_item {
.form_content {
width: 100%;
display: flex;
flex-wrap: wrap;
row-gap: 2rem;
}
.admin_state_item {
> span {
width: 15rem;
width: 17rem !important;
}
}
.modal_title_text {

View File

@@ -37,26 +37,7 @@
</div>
<div class="admin_state_item">
<span>Email:</span>
<input
v-model="email"
placeholder="Please enter email"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>User Name:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
<SelectUser v-model="email" labelKey="email" valueKey="email" />
</div>
</div>
<div class="admin_search">
@@ -96,17 +77,17 @@ import { defineComponent, ref, createVNode, computed, reactive, toRefs, onMounte
import { formatTime } from "@/tool/util";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import SelectUser from '@/component/common/SelectUser.vue'
export default defineComponent({
components: {
SelectUser
},
setup() {
const store:any = useStore()
let filter:any = reactive({
dataList:[],
tableLoading:false,
allUserList: computed(()=>{
return store.state.adminPage.allUserList
}),
allCountry:[]
})
let filterData:any = reactive({

View File

@@ -207,7 +207,8 @@ export class BackgroundSizeCommand extends Command {
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
// 记录原尺寸
this.backgroundObject = findObjectById(this.canvas, this.bgLayer.fabricObject.id).object;
this.bgId = this.bgLayer.fabricObject?.id || this.bgLayer.fabricObjects?.[0]?.id;
this.backgroundObject = findObjectById(this.canvas, this.bgId).object;
this.oldWidth = this.backgroundObject.width;
this.oldHeight = this.backgroundObject.height;

View File

@@ -50,7 +50,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
);
this.layer = layer;
this.parent = parent;
console.log("==========",layer);
if (!layer) return false;
if(!isUndo){
@@ -69,7 +68,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
layer.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ absolutePositioned: true });
this.newFill = new fabric.Rect({
width: clippingMaskFabricObject.width,
@@ -117,7 +116,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.parent.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ absolutePositioned: true });
this.newFill = new fabric.Rect({
width: clippingMaskFabricObject.width,
@@ -222,7 +221,7 @@ export class FillGroupLayerBackgroundCommand extends Command {
this.parent?.clippingMask,
this.canvas
);
clipPath.clipPath = null;
// clipPath.clipPath = null;
clipPath.set({ absolutePositioned: true });
this.group.clipPath = clipPath;
}
@@ -343,10 +342,6 @@ export class FillGroupLayerBackgroundCommand extends Command {
minTop = Infinity,
maxRight = -Infinity,
maxBottom = -Infinity;
console.log(
"计算当前所有对象的边界信息:===>",
this.originalfabricObjects.length
);
this.originalfabricObjects?.forEach((obj) => {
const { object } = findObjectById(this.canvas, obj.id) || {};
if (object) {

View File

@@ -56,7 +56,7 @@ export class FillLayerBackgroundCommand extends Command {
layer.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
// 设置绝对定位

View File

@@ -0,0 +1,331 @@
import { Command } from "./Command";
import { findLayerRecursively } from "../utils/layerHelper";
import { fabric } from "fabric-with-all";
import {
findObjectById,
generateId,
insertObjectAtZIndex,
removeCanvasObjectByObject,
createPatternTransform,
imageAddGapToCanvas,
} from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper";
const scale = 0.3;// 默认缩放比例
export const FillSourceToBase64 = (source) => {
if (source?.toDataURL) {
return source.toDataURL?.();
} else if (source?.src) {
return source.src;
}
return source;
}
/**
* 填充图案平铺命令
* 填充重复属性repeat | repeat-x | repeat-y | no-repeat
* 默认缩放比例0.3
* 默认偏移量50%
*/
export class FillRepeatCommand extends Command {
constructor(options) {
super({ name: "填充图案平铺", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.fillRepeat = options.fillRepeat;
this.oldObjects = null;
this.oldLocked = null;
this.oldIsDisableUnlock = null;
}
async execute() {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || (object.type !== "rect" && object.type !== "image")) {
console.warn("当前对象不能平铺", object.type);
return false;
}
this.oldObjects = object;
if (this.fillRepeat === "no-repeat") {
const fill_ = object.fill_;
const image = await new Promise((resolve, reject) => {
fabric.Image.fromURL(
fill_.source,
v => resolve(v),
{ crossOrigin: "anonymous" }
);
});
image.set({
id: object.id,
layerId: object.layerId,
layerName: object.layerName,
...(fill_.originalInfo || {
top: object.top,
left: object.left,
})
});
layer.fabricObjects = [image.toObject(["id", "layerId", "layerName"])];
this.oldLocked = layer.locked;
layer.locked = false;
this.canvas.add(image);
this.canvas.remove(object);
} else {
const img = await new Promise((resolve, reject) => {
if (object.type === "rect") {
let source = object.fill.source;
resolve(source);
} else if (object.type === "image") {
const imgElement = object.getElement();
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = imgElement.width;
tcanvas.height = imgElement.height;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(imgElement, 0, 0);
resolve(tcanvas);
}
});
const fill_ = object.fill_ || {
source: FillSourceToBase64(img),
gapX: 0,
gapY: 0,
width: img.width,
height: img.height,
originalInfo: {
top: object.top,
left: object.left,
scaleX: object.scaleX,
scaleY: object.scaleY,
width: object.width,
height: object.height,
}
};
const fdObject = this.canvasManager.getFixedLayerObject();
const bgObject = this.canvasManager.getBackgroundLayerObject();
const tObject = fdObject || bgObject;
const pattern = new fabric.Pattern({
source: img,
repeat: this.fillRepeat,
patternTransform: object.fill?.hasOwnProperty("patternTransform") ? object.fill.patternTransform : createPatternTransform(scale, 0),
offsetX: object.fill?.hasOwnProperty("offsetX") ? object.fill.offsetX : tObject.width / 2, // 水平偏移
offsetY: object.fill?.hasOwnProperty("offsetY") ? object.fill.offsetY : tObject.height / 2, // 垂直偏移
});
const rect = new fabric.Rect({
id: object.id,
layerId: object.layerId,
layerName: object.layerName,
fill_,
});
layer.fabricObjects = [rect.toObject(["id", "layerId", "layerName"])];
this.oldLocked = layer.locked;
// this.oldIsDisableUnlock = layer.isDisableUnlock;
// layer.isDisableUnlock = true;
if (this.oldObjects.type === "rect") {
rect.set({
width: object.width,
height: object.height,
top: object.top,
left: object.left,
originX: object.originX,
originY: object.originY,
angle: object.angle,
scaleX: object.scaleX,
scaleY: object.scaleY,
flipX: object.flipX,
flipY: object.flipY,
});
} else {
let scaleX = tObject.scaleX || 1;
let scaleY = tObject.scaleY || 1;
rect.set({
width: tObject.width,
height: tObject.height,
top: tObject.top - tObject.height * scaleY / 2,
left: tObject.left - tObject.width * scaleX / 2,
scaleX,
scaleY,
});
layer.locked = true;
}
rect.set("fill", pattern);
this.canvas.add(rect);
this.canvas.remove(object);
}
await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.();
await this.canvasManager.thumbnailManager?.generateLayerThumbnail(
this.layerId
);
await this.layerManager.selectLayerObjects(this.layerId);
return true;
}
async undo() {
if (!this.oldObjects) {
console.warn("没有旧对象可恢复");
return false;
}
const { layer } = findLayerRecursively(this.layers.value, this.oldObjects.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
this.canvas.remove(object);
this.canvas.add(this.oldObjects);
layer.fabricObjects = [this.oldObjects.toObject(["id", "layerId", "layerName"])];
layer.locked = this.oldLocked;
// layer.isDisableUnlock = this.oldIsDisableUnlock;
await this.layerManager?.updateLayersObjectsInteractivity();
await this.layerManager?.sortLayersWithTool?.();
this.canvas.renderAll();
this.canvasManager.thumbnailManager?.generateLayerThumbnail(this.layerId);
return true;
}
}
/**
* 填充图案更改参数
*/
export class FillRepeatChangeCommand extends Command {
constructor(options) {
super({ name: "填充图案更改参数", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newPattern = options.newPattern;
this.oldPattern = null;
}
async execute() {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
this.oldPattern = object.oldPattern || object.get("fill");
delete object.oldPattern;
const oldPattern = { ...this.oldPattern };
delete oldPattern.id;
const pattern = new fabric.Pattern({
...oldPattern,
...this.newPattern,
});
object.set("fill", pattern);
this.canvas.renderAll();
return true;
}
async undo() {
if (!this.oldPattern) {
console.warn("没有旧图案可恢复");
return false;
}
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
const pattern = new fabric.Pattern({
...this.oldPattern
});
object.set("fill", pattern);
this.canvas.renderAll();
return true;
}
}
/**
* 填充图案更改间隙
*/
export class FillRepeatGapChangeCommand extends Command {
constructor(options) {
super({ name: "填充图案更改间隙", saveState: true });
this.canvas = options.canvas;
this.layers = options.layers;
this.canvasManager = options.canvasManager;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newGapX = options.newGapX;
this.newGapY = options.newGapY;
this.record = !!options.record;
this.oldGapX = null;
this.oldGapY = null;
}
async execute(isUndo = false) {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
if (!layer || !layer.fabricObjects || layer.fabricObjects.length === 0) {
console.warn("图层不存在或没有 fabric 对象");
return false;
}
const { object } = findObjectById(this.canvas, layer?.fabricObjects?.[0]?.id);
if (!object || object.type !== "rect") {
console.warn("当前对象不是矩形", object);
return false;
}
if (!object.fill_) {
object.fill_ = {
source: FillSourceToBase64(object.fill.source),
gapX: 0,
gapY: 0,
}
}
if (isUndo) {
object.fill_.gapX = this.oldGapX;
object.fill_.gapY = this.oldGapY;
} else {
if (!object.oldFill_ && this.record) {
object.oldFill_ = { ...object.fill_ };
}
this.oldGapX = object.fill_.gapX;
this.oldGapY = object.fill_.gapY;
object.fill_.gapX = this.newGapX;
object.fill_.gapY = this.newGapY;
}
const image = new Image();
image.crossOrigin = "anonymous";
image.src = object.fill_.source;
await image.decode();
object.fill_.width = image.width;
object.fill_.height = image.height;
const fill = object.get("fill");
fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
object.set("fill", new fabric.Pattern(fill));
this.canvas.renderAll();
return true;
}
async undo() {
if (this.oldGapX === null || this.oldGapY === null) {
console.warn("没有旧间隙可恢复");
return false;
}
await this.execute(true);
return true;
}
}

View File

@@ -10,6 +10,7 @@ import { AddObjectToLayerCommand } from "./ObjectLayerCommands";
import { ToolCommand } from "./ToolCommands";
import {
findObjectById,
findObjectByLayerId,
generateId,
getObjectZIndex,
insertObjectAtZIndex,
@@ -19,7 +20,7 @@ import {
} from "../utils/helper";
import { fabric } from "fabric-with-all";
import { restoreFabricObject } from "../utils/objectHelper";
import EventManager from "../utils/event.js";
/**
* 添加图层命令
*/
@@ -36,7 +37,7 @@ export class AddLayerCommand extends Command {
this.insertIndex = options.insertIndex;
this.oldActiveLayerId = null;
this.beforeLayers = [...this.layers.value]; // 备份原图层列表
this.beforeLayers = JSON.stringify(this.layers.value); // 备份原图层列表
this.options = options.options || {};
}
@@ -70,7 +71,7 @@ export class AddLayerCommand extends Command {
undo() {
// 从图层列表删除该图层
this.layers.value = [...this.beforeLayers];
this.layers.value = JSON.parse(this.beforeLayers);
// 恢复原活动图层
this.activeLayerId.value = this.oldActiveLayerId;
@@ -143,7 +144,7 @@ export class AddLayerCommand extends Command {
// 先在一级图层中查找
for (let i = 0; i < layers.length; i++) {
const layer = layers[i];
if (layer.isPrintTrimsGroup) continue;
if (layer.id === layerId) {
return {
layer: layer,
@@ -251,12 +252,12 @@ export class PasteLayerCommand extends Command {
(await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) ||
null;
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
clippingMaskFabricObject.dirty = true;
// clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
// 添加所有对象到画布
allObjects.forEach((obj) => {
@@ -523,6 +524,7 @@ export class RemoveLayerCommand extends Command {
this.layerId = options.layerId;
this.activeLayerId = options.activeLayerId;
this.layerManager = options.layerManager || null;
this.IsOnlyLayer = this.layers.value.filter((v => !v.isFixed && !v.isFixedOther && !v.isBackground)).length <= 1
// 查找要删除的图层
this.layerIndex = this.layers.value.findIndex(
@@ -599,7 +601,9 @@ export class RemoveLayerCommand extends Command {
);
// 从图层列表中删除
this.layers.value.splice(this.layerIndex, 1);
if(this.IsOnlyLayer){
this.addCmd = await this.layerManager?.createLayer?.(null, LayerType.EMPTY, {}, false);
}
// 如果删除的是当前活动图层,需要更新活动图层
if (this.isActiveLayer) {
// 查找最近的非背景层作为新的活动图层
@@ -632,6 +636,9 @@ export class RemoveLayerCommand extends Command {
async undo() {
// 恢复图层到原位置
if (this.layerIndex !== -1 && this.removedLayer) {
if(this.IsOnlyLayer && this.addCmd){
this.addCmd?.undo?.();
}
this.layers.value.splice(this.layerIndex, 0, this.removedLayer);
// 使用优化渲染批处理恢复真实对象到画布
@@ -649,7 +656,6 @@ export class RemoveLayerCommand extends Command {
}
});
});
await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas.renderAll();
@@ -802,15 +808,23 @@ export class ToggleLayerVisibilityCommand extends Command {
// 切换可见性
this.layer.visible = !this.layer.visible;
const ids = [this.layerId];
const childLayers = this.layer?.children || [];
childLayers.forEach((childLayer) => {
childLayer.visible = this.layer.visible;
ids.push(childLayer.id);
});
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.layer.visible;
});
this.canvas.getObjects().forEach((obj) => {
if (ids.includes(obj.layerId)) {
obj.getObjects?.()?.forEach((item) => {
item.visible = this.layer.visible;
});
obj.visible = this.layer.visible;
}
});
}
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity();
@@ -858,23 +872,24 @@ export class ToggleChildLayerVisibilityCommand extends Command {
// this.oldVisibility = this.childLayer ? this.childLayer.visible : null;
}
async execute() {
async execute(visible) {
if (!this.childLayer) {
throw new Error("找不到要切换可见性的子图层");
}
// 切换可见性
this.childLayer.visible = !this.childLayer.visible;
this.childLayer.visible = typeof visible === "boolean" ? visible : !this.childLayer.visible;
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.childLayer.visible;
});
this.canvas.getObjects().forEach((obj) => {
if (obj.layerId === this.layerId) {
obj.getObjects?.()?.forEach((item) => {
item.visible = this.childLayer.visible;
});
obj.visible = this.childLayer.visible;
}
});
}
// 更新画布上对象的可选择状态
@@ -1007,9 +1022,8 @@ export class LayerLockCommand extends Command {
// 如果是组图层,递归更新所有子图层
if (
layer.type === "group" &&
layer.children &&
Array.isArray(layer.children)
Array.isArray(layer.children) && layer.children.length > 0
) {
layer.children.forEach((child) => {
this._updateLayerLockState(child, locked);
@@ -1108,7 +1122,7 @@ export class SetLayerOpacityCommand extends Command {
this.canvas.renderAll();
}
EventManager.emit("object:opacity:execute", this.layerId, this.opacity);
return true;
}
@@ -1130,6 +1144,7 @@ export class SetLayerOpacityCommand extends Command {
this.canvas.renderAll();
}
}
EventManager.emit("object:opacity:undo", this.layerId, this.opacity);
}
getInfo() {
@@ -1371,7 +1386,7 @@ export class GroupLayersCommand extends Command {
// 备份原图层
this.originalLayers = [...this.layers.value];
// 新组ID
this.groupId =
this.groupId = options.id ||
generateId("group_layer_") ||
`group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
@@ -3669,7 +3684,7 @@ export class ChangeFixedImageCommand extends Command {
opacity: currentObj.opacity,
};
console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
// console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
}
}
@@ -3779,7 +3794,7 @@ export class ChangeFixedImageCommand extends Command {
false
);
if (insertSuccess) {
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(newImage);
@@ -4276,24 +4291,28 @@ export class RemoveChildLayerCommand extends Command {
}
// 恢复子图层到原位置
this.parentLayer.children.splice(this.childIndex, 0, this.removedChild);
optimizeCanvasRendering(this.canvas, async () => {
this.originalObjects.forEach((obj) => {
// 恢复对象到画布
this.canvas.add(obj);
// 恢复对象的图层信息
obj.layerId = this.layerId;
obj.layerName = this.removedChild.name;
obj.setCoords(); // 更新坐标
});
await new Promise((resolve) => {
optimizeCanvasRendering(this.canvas, async () => {
this.originalObjects.forEach((obj) => {
// 恢复对象到画布
this.canvas.add(obj);
// 恢复对象的图层信息
obj.layerId = this.layerId;
obj.layerName = this.removedChild.name;
obj.setCoords(); // 更新坐标
});
// 如果是原活动图层,恢复活动图层
if (this.isActiveLayer) {
this.activeLayerId.value = this.layerId;
}
// 如果是原活动图层,恢复活动图层
if (this.isActiveLayer) {
this.activeLayerId.value = this.layerId;
}
// 重新渲染画布
await this.layerManager?.updateLayersObjectsInteractivity(false);
// 重新渲染画布
await this.layerManager?.updateLayersObjectsInteractivity(false);
resolve(true);
});
});
return true;
}
getInfo() {
@@ -4434,3 +4453,90 @@ export class ChildLayerLockCommand extends Command {
};
}
}
/**
* 设置图层混合模式
*/
export class SetLayerCompositeCommand extends Command {
constructor(options) {
super({
name: "设置图层混合模式",
saveState: false,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.newValue = options.newValue;
this.oldValue = options.oldValue;
}
execute(isUndo = false) {
const { layer } = findLayerRecursively(this.layers.value, this.layerId);
const { object } = findObjectByLayerId(this.canvas, this.layerId);
if (!layer || !object) {
console.error(`图层${this.layerId}不存在`);
return false;
}
// console.log("==========", this.newValue, this.oldValue);
const value = isUndo ? this.oldValue : this.newValue;
layer.blendMode = value;
object.set("globalCompositeOperation", value);
this.canvas.renderAll();
const event = isUndo ? "object:composite:undo" : "object:composite:execute";
EventManager.emit(event, object);
return true;
}
undo() {
return this.execute(true);
}
}
/**
* 设置颜色图层颜色
*/
export class SetColorLayerFillCommand extends Command {
constructor(options) {
super({
name: "设置颜色图层颜色",
saveState: false,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.object = options.object;
this.layer = this.layerManager?.getLayerById(this.object.layerId);
this.newFill = options.newFill;
this.oldFill = JSON.parse(JSON.stringify(this.object.fill));
this.layer.blendMode = "multiply";
this.object.set("globalCompositeOperation", "multiply");
this.object.set("originColor", options.originColor);
}
async execute(isUndo = false) {
if (!this.object) {
console.error(`颜色图层不存在`);
return false;
}
const isVisible = this.layer?.visible;
if(!isVisible && this.layer) this.layer.visible = true;
const gradient = new fabric.Gradient({
type: "linear",
gradientUnits: "percentage",
...(isUndo ? this.oldFill : this.newFill),
});
this.object.setFill(gradient);
this.canvas.renderAll();
await this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.object.id
);
if(!isVisible && this.layer) this.layer.visible = false;
this.layerManager?.updateLayersObjectsInteractivity();
return true;
}
undo() {
this.execute(true);
return true;
}
}

View File

@@ -0,0 +1,50 @@
import { Command } from "./Command.js";
/**
* 对象移动命令
* 轻量级命令,只记录对象的移动属性变化(位置)
*/
export class ObjectMoveCommand extends Command {
constructor(options) {
super({
name: options.name || "对象移动",
description: options.description || "移动对象",
saveState: false, // 自己管理状态,避免递归
});
this.canvas = options.canvas;
this.initPos = options.initPos;
this.finalPos = options.finalPos;
}
/**
* 执行命令
*/
async execute() {
this.setObjectsPos(this.finalPos);
return true;
}
/**
* 撤销命令
* 应用初始状态
*/
async undo() {
this.setObjectsPos(this.initPos);
return true;
}
async setObjectsPos(pos) {
const objects = this.canvas.getObjects();
const arr = typeof pos === "object" ? [pos] : pos;
arr.forEach((item) => {
const obj = objects.find((o) => o.id === item.id);
if(obj) {
obj.set({
left: item.left,
top: item.top,
});
}
});
this.canvas.renderAll();
}
}

View File

@@ -234,7 +234,7 @@ export class AddObjectToLayerCommand extends Command {
parent.clippingMask,
this.canvas
);
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ absolutePositioned: true });
this.fabricObject.clipPath = clippingMaskFabricObject;
// 标记为脏对象
@@ -600,7 +600,7 @@ export class ChangeFixedImageCommand extends Command {
opacity: currentObj.opacity,
};
console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
// console.log(`保存渲染顺序: z-index = ${this.previousZIndex}`);
}
}
@@ -716,7 +716,7 @@ export class ChangeFixedImageCommand extends Command {
false
);
if (insertSuccess) {
console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
// console.log(`新图像插入到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(newImage);

View File

@@ -46,13 +46,13 @@ export class RasterizeLayerCommand extends Command {
this.layerId
);
this.layer = layer;
this.parentLayer = parent;
// this.parentLayer = parent;
// 新增:如果有父图层,则栅格化父图层及其所有子图层
if (this.parentLayer) {
this.layer = this.parentLayer;
this.layerId = this.parentLayer.id;
}
// // 新增:如果有父图层,则栅格化父图层及其所有子图层
// if (this.parentLayer) {
// this.layer = this.parentLayer;
// this.layerId = this.parentLayer.id;
// }
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
@@ -153,7 +153,7 @@ export class RasterizeLayerCommand extends Command {
});
// 恢复原始图层结构
this.layers.value = [...this.originalLayerStructure];
this.layers.value = JSON.parse(this.originalLayerStructure);
// 恢复原活动图层
this.activeLayerId.value = this.layerId;
@@ -191,7 +191,7 @@ export class RasterizeLayerCommand extends Command {
*/
_saveOriginalLayerStructure() {
// 只保存相关的图层结构,而不是整个图层数组
this.originalLayerStructure = JSON.parse(JSON.stringify(this.layers.value));
this.originalLayerStructure = JSON.stringify(this.layers.value);
}
/**
@@ -285,17 +285,15 @@ export class RasterizeLayerCommand extends Command {
// 提取排序后的对象
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
console.log(
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
);
console.log(
"🔢 对象z-index顺序:",
objectsWithZIndex.map((item) => ({
id: item.object.id,
type: item.object.type,
zIndex: item.zIndex,
}))
);
// console.log(`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合` );
// console.log(
// "🔢 对象z-index顺序:",
// objectsWithZIndex.map((item) => ({
// id: item.object.id,
// type: item.object.type,
// zIndex: item.zIndex,
// }))
// );
}
/**
@@ -517,12 +515,12 @@ export class ExportLayerToImageCommand extends Command {
this.canvas
);
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
clippingMaskFabricObject.dirty = true;
// clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
}
@@ -611,17 +609,17 @@ export class ExportLayerToImageCommand extends Command {
// 提取排序后的对象
this.objectsToRasterize = objectsWithZIndex.map((item) => item.object);
console.log(
`📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
);
console.log(
"🔢 对象z-index顺序:",
objectsWithZIndex.map((item) => ({
id: item.object.id,
type: item.object.type,
zIndex: item.zIndex,
}))
);
// console.log(
// `📊 收集到 ${this.layersToRasterize.length} 个图层,${this.objectsToRasterize.length} 个对象进行组合`
// );
// console.log(
// "🔢 对象z-index顺序:",
// objectsWithZIndex.map((item) => ({
// id: item.object.id,
// type: item.object.type,
// zIndex: item.zIndex,
// }))
// );
}
/**

View File

@@ -159,6 +159,8 @@ export class BatchInitializeRedGreenModeCommand extends Command {
absolutePositioned: true,
opacity: 0.01, // 设置为几乎透明
id: generateId("redGreenImageMask_"),
rx: 15,
ry: 15,
});
// this.canvas.add(this.redGreenImageMask);
this.canvas.clipPath = this.redGreenImageMask;

View File

@@ -2,6 +2,7 @@ import { findObjectById } from "../utils/helper";
import { findLayerRecursively } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper";
import { Command } from "./Command";
import EventManager from "../utils/event.js";
/**
* 对象变换命令
@@ -75,7 +76,7 @@ export class TransformCommand extends Command {
// 触发画布更新
this.canvas.renderAll();
EventManager.emit("object:modified:execute", targetObject);
return true;
}
@@ -113,7 +114,7 @@ export class TransformCommand extends Command {
}, 300);
// 触发画布更新
this.canvas.renderAll();
EventManager.emit("object:modified:undo", targetObject);
return true;
}
@@ -167,7 +168,7 @@ export class TransformCommand extends Command {
);
if (clippingMaskFabricObject) {
clippingMaskFabricObject.clipPath = null;
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});

Some files were not shown because too many files have changed in this diff Show More