♿ optimize and improve image enhancement
This commit is contained in:
7
components.d.ts
vendored
7
components.d.ts
vendored
@@ -62,7 +62,6 @@ declare module 'vue' {
|
|||||||
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
CommentList: typeof import('./src/components/CommentReply/src/CommentList/CommentList.vue')['default']
|
||||||
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
CommentReply: typeof import('./src/components/CommentReply/index.vue')['default']
|
||||||
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
|
||||||
CompareResult: typeof import('./src/views/Upscale/CompareResult.vue')['default']
|
|
||||||
Countdown: typeof import('./src/components/MyUI/Countdown/Countdown.vue')['default']
|
Countdown: typeof import('./src/components/MyUI/Countdown/Countdown.vue')['default']
|
||||||
DatePicker: typeof import('./src/components/MyUI/DatePicker/DatePicker.vue')['default']
|
DatePicker: typeof import('./src/components/MyUI/DatePicker/DatePicker.vue')['default']
|
||||||
Descriptions: typeof import('./src/components/MyUI/Descriptions/Descriptions.vue')['default']
|
Descriptions: typeof import('./src/components/MyUI/Descriptions/Descriptions.vue')['default']
|
||||||
@@ -85,6 +84,7 @@ declare module 'vue' {
|
|||||||
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default']
|
||||||
List: typeof import('./src/components/MyUI/List/List.vue')['default']
|
List: typeof import('./src/components/MyUI/List/List.vue')['default']
|
||||||
LoadingBar: typeof import('./src/components/MyUI/LoadingBar/LoadingBar.vue')['default']
|
LoadingBar: typeof import('./src/components/MyUI/LoadingBar/LoadingBar.vue')['default']
|
||||||
|
LoadingGraphic: typeof import('./src/components/LoadingGraphic/LoadingGraphic.vue')['default']
|
||||||
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
|
LocationAlbum: typeof import('./src/views/Album/LocationAlbum/LocationAlbum.vue')['default']
|
||||||
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
|
LockOutlined: typeof import('@ant-design/icons-vue')['LockOutlined']
|
||||||
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
|
LoginFooter: typeof import('./src/views/Login/LoginFooter.vue')['default']
|
||||||
@@ -97,11 +97,11 @@ declare module 'vue' {
|
|||||||
Notification: typeof import('./src/components/MyUI/Notification/Notification.vue')['default']
|
Notification: typeof import('./src/components/MyUI/Notification/Notification.vue')['default']
|
||||||
NumberAnimation: typeof import('./src/components/MyUI/NumberAnimation/NumberAnimation.vue')['default']
|
NumberAnimation: typeof import('./src/components/MyUI/NumberAnimation/NumberAnimation.vue')['default']
|
||||||
Pagination: typeof import('./src/components/MyUI/Pagination/Pagination.vue')['default']
|
Pagination: typeof import('./src/components/MyUI/Pagination/Pagination.vue')['default']
|
||||||
|
ParameterSetting: typeof import('./src/views/Upscale/ParameterSetting.vue')['default']
|
||||||
PeopleAlbum: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbum.vue')['default']
|
PeopleAlbum: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbum.vue')['default']
|
||||||
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
Phoalbum: typeof import('./src/views/Album/Phoalbum/Phoalbum.vue')['default']
|
||||||
Popconfirm: typeof import('./src/components/MyUI/Popconfirm/Popconfirm.vue')['default']
|
Popconfirm: typeof import('./src/components/MyUI/Popconfirm/Popconfirm.vue')['default']
|
||||||
Popover: typeof import('./src/components/MyUI/Popover/Popover.vue')['default']
|
Popover: typeof import('./src/components/MyUI/Popover/Popover.vue')['default']
|
||||||
ProcessResult: typeof import('./src/views/Upscale/ProcessResult.vue')['default']
|
|
||||||
Progress: typeof import('./src/components/MyUI/Progress/Progress.vue')['default']
|
Progress: typeof import('./src/components/MyUI/Progress/Progress.vue')['default']
|
||||||
QRCode: typeof import('./src/components/MyUI/QRCode/QRCode.vue')['default']
|
QRCode: typeof import('./src/components/MyUI/QRCode/QRCode.vue')['default']
|
||||||
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
QRLogin: typeof import('./src/views/QRLogin/QRLogin.vue')['default']
|
||||||
@@ -134,7 +134,6 @@ declare module 'vue' {
|
|||||||
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
|
||||||
Tabs: typeof import('./src/components/MyUI/Tabs/Tabs.vue')['default']
|
Tabs: typeof import('./src/components/MyUI/Tabs/Tabs.vue')['default']
|
||||||
Tag: typeof import('./src/components/MyUI/Tag/Tag.vue')['default']
|
Tag: typeof import('./src/components/MyUI/Tag/Tag.vue')['default']
|
||||||
TestCompare: typeof import('./src/views/Upscale/TestCompare.vue')['default']
|
|
||||||
Textarea: typeof import('./src/components/MyUI/Textarea/Textarea.vue')['default']
|
Textarea: typeof import('./src/components/MyUI/Textarea/Textarea.vue')['default']
|
||||||
TextScroll: typeof import('./src/components/MyUI/TextScroll/TextScroll.vue')['default']
|
TextScroll: typeof import('./src/components/MyUI/TextScroll/TextScroll.vue')['default']
|
||||||
ThingAlbum: typeof import('./src/views/Album/ThingAlbum/ThingAlbum.vue')['default']
|
ThingAlbum: typeof import('./src/views/Album/ThingAlbum/ThingAlbum.vue')['default']
|
||||||
@@ -143,7 +142,7 @@ declare module 'vue' {
|
|||||||
TreeChart: typeof import('./src/components/MyUI/TreeChart/TreeChart.vue')['default']
|
TreeChart: typeof import('./src/components/MyUI/TreeChart/TreeChart.vue')['default']
|
||||||
Upload: typeof import('./src/components/MyUI/Upload/Upload.vue')['default']
|
Upload: typeof import('./src/components/MyUI/Upload/Upload.vue')['default']
|
||||||
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
|
||||||
Upscale: typeof import('./src/views/Upscale/Upscale.vue')['default']
|
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
|
||||||
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
|
||||||
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
|
||||||
Video: typeof import('./src/components/MyUI/Video/Video.vue')['default']
|
Video: typeof import('./src/components/MyUI/Video/Video.vue')['default']
|
||||||
|
12
package.json
12
package.json
@@ -33,7 +33,7 @@
|
|||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"eslint": "9.16.0",
|
"eslint": "9.17.0",
|
||||||
"go-captcha-vue": "^2.0.5",
|
"go-captcha-vue": "^2.0.5",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"nsfwjs": "^4.2.1",
|
"nsfwjs": "^4.2.1",
|
||||||
"pinia": "^2.3.0",
|
"pinia": "^2.3.0",
|
||||||
"pinia-plugin-persistedstate-2": "^2.0.27",
|
"pinia-plugin-persistedstate-2": "^2.0.28",
|
||||||
"qrcode": "^1",
|
"qrcode": "^1",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"swiper": "^11.1.15",
|
"swiper": "^11.1.15",
|
||||||
@@ -58,17 +58,17 @@
|
|||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.16.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
"globals": "^15.13.0",
|
"globals": "^15.13.0",
|
||||||
"sass": "^1.83.0",
|
"sass": "^1.83.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.6.3",
|
||||||
"typescript-eslint": "^8.18.0",
|
"typescript-eslint": "^8.18.0",
|
||||||
"unplugin-vue-components": "^0.27.5",
|
"unplugin-vue-components": "^0.28.0",
|
||||||
"vite": "^6.0.3",
|
"vite": "^6.0.3",
|
||||||
"vite-plugin-bundle-obfuscator": "1.4.0",
|
"vite-plugin-bundle-obfuscator": "1.4.0",
|
||||||
"vite-plugin-chunk-split": "^0.5.0",
|
"vite-plugin-chunk-split": "^0.5.0",
|
||||||
"vue-tsc": "https://pkg.pr.new/vuejs/language-tools/vue-tsc@3fb59af"
|
"vue-tsc": "2.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1 +1,11 @@
|
|||||||
<svg t="1733761095875" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="50680" width="200" height="200"><path d="M471.87259 871.430008a41.629505 41.629505 0 0 0 41.629506 41.629506 40.98575 40.98575 0 0 0 38.625314-41.629506 40.55658 40.55658 0 0 0-39.912825-40.98575h-1.072925a40.98575 40.98575 0 0 0-39.26907 40.98575z" fill="#B8BDCC" p-id="50681"></path><path d="M738.601844 55.792121a77.036044 77.036044 0 0 1 77.036044 77.036043v758.343672a77.036044 77.036044 0 0 1-77.036044 77.036043H285.398156a77.036044 77.036044 0 0 1-77.036044-77.036043V132.828164A77.036044 77.036044 0 0 1 285.398156 55.792121h453.203688m0-55.792121H285.398156A132.828164 132.828164 0 0 0 152.569992 132.828164v758.343672a132.828164 132.828164 0 0 0 132.828164 132.828164h453.203688a132.828164 132.828164 0 0 0 132.828164-132.828164V132.828164A132.828164 132.828164 0 0 0 738.601844 0z" fill="#B8BDCC" p-id="50682"></path><path d="M713.49539 793.964795H310.50461a39.483655 39.483655 0 0 1-41.629505-36.694049v-519.295893a39.26907 39.26907 0 0 1 41.629505-36.479463h402.99078a39.26907 39.26907 0 0 1 41.629505 36.479463v518.437553A39.483655 39.483655 0 0 1 713.49539 793.964795z" fill="#E3E5EB" p-id="50683"></path><path d="M425.736798 103.430008m25.964795 0l120.596814 0q25.964795 0 25.964795 25.964795l0 0q0 25.964795-25.964795 25.964795l-120.596814 0q-25.964795 0-25.964795-25.964795l0 0q0-25.964795 25.964795-25.964795Z" fill="#B8BDCC" p-id="50684"></path></svg>
|
<svg t="1733761095875" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="50680" width="200" height="200">
|
||||||
|
<path d="M471.87259 871.430008a41.629505 41.629505 0 0 0 41.629506 41.629506 40.98575 40.98575 0 0 0 38.625314-41.629506 40.55658 40.55658 0 0 0-39.912825-40.98575h-1.072925a40.98575 40.98575 0 0 0-39.26907 40.98575z"
|
||||||
|
fill="#B8BDCC" p-id="50681"></path>
|
||||||
|
<path d="M738.601844 55.792121a77.036044 77.036044 0 0 1 77.036044 77.036043v758.343672a77.036044 77.036044 0 0 1-77.036044 77.036043H285.398156a77.036044 77.036044 0 0 1-77.036044-77.036043V132.828164A77.036044 77.036044 0 0 1 285.398156 55.792121h453.203688m0-55.792121H285.398156A132.828164 132.828164 0 0 0 152.569992 132.828164v758.343672a132.828164 132.828164 0 0 0 132.828164 132.828164h453.203688a132.828164 132.828164 0 0 0 132.828164-132.828164V132.828164A132.828164 132.828164 0 0 0 738.601844 0z"
|
||||||
|
fill="#B8BDCC" p-id="50682"></path>
|
||||||
|
<path d="M713.49539 793.964795H310.50461a39.483655 39.483655 0 0 1-41.629505-36.694049v-519.295893a39.26907 39.26907 0 0 1 41.629505-36.479463h402.99078a39.26907 39.26907 0 0 1 41.629505 36.479463v518.437553A39.483655 39.483655 0 0 1 713.49539 793.964795z"
|
||||||
|
fill="#E3E5EB" p-id="50683"></path>
|
||||||
|
<path d="M425.736798 103.430008m25.964795 0l120.596814 0q25.964795 0 25.964795 25.964795l0 0q0 25.964795-25.964795 25.964795l-120.596814 0q-25.964795 0-25.964795-25.964795l0 0q0-25.964795 25.964795-25.964795Z"
|
||||||
|
fill="#B8BDCC" p-id="50684"></path>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1 +1 @@
|
|||||||
<svg t="1733748093314" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3982" width="200" height="200"><path d="M170.666667 0h682.666666c93.866667 0 170.666667 76.8 170.666667 170.666667v682.666666c0 93.866667-76.8 170.666667-170.666667 170.666667H170.666667c-93.866667 0-170.666667-76.8-170.666667-170.666667V170.666667c0-93.866667 76.8-170.666667 170.666667-170.666667z" fill="#32B84C" opacity=".2" p-id="3983"></path><path d="M200.533333 72.533333h622.933334c72.533333 0 128 55.466667 128 128v622.933334c0 72.533333-55.466667 128-128 128H200.533333c-72.533333 0-128-55.466667-128-128V200.533333c0-68.266667 59.733333-128 128-128z" fill="#32B84C" opacity=".3" p-id="3984"></path><path d="M230.4 145.066667h558.933333c46.933333 0 85.333333 38.4 85.333334 85.333333v558.933333c0 46.933333-38.4 85.333333-85.333334 85.333334H230.4c-46.933333 0-85.333333-38.4-85.333333-85.333334V230.4c0-46.933333 38.4-85.333333 85.333333-85.333333z" fill="#32B84C" opacity=".38" p-id="3985"></path><path d="M669.866667 537.6l-234.666667 149.333333c-29.866667 21.333333-68.266667-8.533333-68.266667-42.666666V341.333333c0-34.133333 38.4-64 68.266667-42.666666l234.666667 149.333333c34.133333 21.333333 34.133333 68.266667 0 89.6" fill="#26AE40" p-id="3986"></path></svg>
|
<svg t="1734340504075" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21926" width="200" height="200"><path d="M512 1024a512 512 0 1 1 512-512 512 512 0 0 1-512 512z m192-577.194667l-225.066667-178.730666a58.154667 58.154667 0 0 0-61.653333-7.296 55.125333 55.125333 0 0 0-33.194667 51.2v357.461333a55.125333 55.125333 0 0 0 33.194667 51.2 58.112 58.112 0 0 0 61.653333-7.296L704 534.613333a55.466667 55.466667 0 0 0 0-87.808z" fill="#00CAA0" p-id="21927"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 515 B |
1
src/assets/svgs/success.svg
Normal file
1
src/assets/svgs/success.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1734340833280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27444" width="200" height="200"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="27445"></path><path d="M955.733333 512c0 245.077333-198.656 443.733333-443.733333 443.733333S68.266667 757.077333 68.266667 512 266.922667 68.266667 512 68.266667s443.733333 198.656 443.733333 443.733333z" fill="#11AA66" p-id="27446"></path><path d="M512 102.4C285.7984 102.4 102.4 285.7984 102.4 512s183.3984 409.6 409.6 409.6 409.6-183.3984 409.6-409.6S738.2016 102.4 512 102.4zM34.133333 512C34.133333 248.081067 248.081067 34.133333 512 34.133333s477.866667 213.947733 477.866667 477.866667-213.947733 477.866667-477.866667 477.866667S34.133333 775.918933 34.133333 512z" fill="#11AA66" p-id="27447"></path><path d="M787.114667 339.285333a51.2 51.2 0 0 1 0 72.362667l-307.2 307.2a51.2 51.2 0 0 1-72.362667 0l-170.666667-170.666667a51.2 51.2 0 0 1 72.362667-72.362666L443.733333 610.235733l271.018667-271.018666a51.2 51.2 0 0 1 72.362667 0z" fill="#FFFFFF" p-id="27448"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/svgs/warn.svg
Normal file
1
src/assets/svgs/warn.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1734341153983" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="33694" width="200" height="200"><path d="M512 58.181818C260.654545 58.181818 58.181818 260.654545 58.181818 512S260.654545 965.818182 512 965.818182 965.818182 763.345455 965.818182 512 763.345455 58.181818 512 58.181818zM546.909091 768c0 18.618182-16.290909 34.909091-34.909091 34.909091s-34.909091-16.290909-34.909091-34.909091V395.636364c0-18.618182 16.290909-34.909091 34.909091-34.909091s34.909091 16.290909 34.909091 34.909091v372.363636zM512 325.818182c-25.6 0-46.545455-20.945455-46.545455-46.545455s20.945455-46.545455 46.545455-46.545454 46.545455 20.945455 46.545455 46.545454-20.945455 46.545455-46.545455 46.545455z" fill="#A8ABB0" p-id="33695"></path></svg>
|
After Width: | Height: | Size: 787 B |
145
src/components/LoadingGraphic/LoadingGraphic.vue
Normal file
145
src/components/LoadingGraphic/LoadingGraphic.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul class="loading-animation" hidden="" style="display: none;">
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="loading-animation alternate" style="display: block;">
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.loading-animation {
|
||||||
|
list-style: none;
|
||||||
|
position: relative;
|
||||||
|
height: 1px;
|
||||||
|
width: 200px;
|
||||||
|
border-bottom: 1px dashed #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li {
|
||||||
|
margin-top: -3px;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #000000;
|
||||||
|
opacity: 0;
|
||||||
|
box-shadow: 0 0 4px rgba(126, 126, 135, 0.99), 0 0 8px rgba(126, 126, 135, 0.99), 0 0 12px rgba(126, 126, 135, 0.99), 0 0 18px rgba(126, 126, 135, 0.99);
|
||||||
|
position: absolute;
|
||||||
|
left: -10%;
|
||||||
|
animation-name: loading;
|
||||||
|
animation-duration: 2s;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* alternate animation */
|
||||||
|
.loading-animation.alternate li {
|
||||||
|
animation-name: loading2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li:nth-child(1) {
|
||||||
|
animation-delay: 250ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li:nth-child(2) {
|
||||||
|
animation-delay: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li:nth-child(3) {
|
||||||
|
animation-delay: 750ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li:nth-child(4) {
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation li:nth-child(5) {
|
||||||
|
animation-delay: 1.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fading only */
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20%, 60% {
|
||||||
|
left: 50%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
left: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shrinking and fading */
|
||||||
|
@keyframes loading2 {
|
||||||
|
0% {
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
height: 1px;
|
||||||
|
width: 1px;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
20%, 60% {
|
||||||
|
left: 50%;
|
||||||
|
opacity: 1;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
margin-top: -3px;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
left: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
height: 1px;
|
||||||
|
width: 1px;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all of this is extra for the demo */
|
||||||
|
.loading-animation {
|
||||||
|
margin: 100px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation:before, .loading-animation:after {
|
||||||
|
color: grey;
|
||||||
|
font-family: "Merienda One",serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation:before {
|
||||||
|
//content: "Loading";
|
||||||
|
position: absolute;
|
||||||
|
top: -32px;
|
||||||
|
left: 39%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation:after {
|
||||||
|
//content: "Please wait";
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 36%;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -599,7 +599,7 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-info {
|
.icon-info {
|
||||||
color: @themeColor;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-success {
|
.icon-success {
|
||||||
|
@@ -19,7 +19,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: '/main/photo/upscale',
|
path: '/main/photo/upscale',
|
||||||
name: 'upscale',
|
name: 'upscale',
|
||||||
component: () => import('@/views/Upscale/Upscale.vue'),
|
component: () => import('@/views/Upscale/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
title: '图像修复'
|
title: '图像修复'
|
||||||
|
@@ -5,131 +5,120 @@ import i18n from "@/locales";
|
|||||||
import {NSFWJS} from "nsfwjs";
|
import {NSFWJS} from "nsfwjs";
|
||||||
import localForage from "localforage";
|
import localForage from "localforage";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
import Module from "@/workers/imghelper.ts";
|
|
||||||
import Img from "@/workers/image.ts";
|
import Img from "@/workers/image.ts";
|
||||||
|
import Module from "@/workers/imghelper.ts";
|
||||||
|
|
||||||
|
|
||||||
export const useUpscaleStore = defineStore(
|
export const useUpscaleStore = defineStore(
|
||||||
'upscale',
|
'upscale',
|
||||||
() => {
|
() => {
|
||||||
const image: HTMLImageElement = document.createElement('img');
|
const image: HTMLImageElement = document.createElement('img');
|
||||||
const imageList = ref<string[]>([]);
|
const imageData = ref<string>();
|
||||||
const fileList = ref<string[]>([]);
|
const fileData = ref<string>();
|
||||||
const uploading = ref<boolean>(false);
|
const uploading = ref<boolean>(false);
|
||||||
|
|
||||||
// 加载图片数据
|
// 加载图片数据
|
||||||
const img = ref<HTMLImageElement>(new Image());
|
|
||||||
const wasmModule = ref<any>();
|
|
||||||
const hasAlpha = ref(false);
|
const hasAlpha = ref(false);
|
||||||
const input = ref<Img | null>(null);
|
const input = ref<Img | null>(null);
|
||||||
const inputAlpha = ref<Img | null>(null);
|
const inputAlpha = ref<Img | null>(null);
|
||||||
|
|
||||||
|
const wasmModule = ref<any>(null);
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
||||||
|
|
||||||
const imgLoaded = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 处理后的图片
|
// 处理后的图片
|
||||||
const processedImg = ref<HTMLImageElement>(new Image());
|
const processedImg = ref<string>('');
|
||||||
|
const isDone = ref<boolean>(false);
|
||||||
|
const isProcessing = ref<boolean>(false);
|
||||||
|
const msg = ref<string>("");
|
||||||
|
const progressBar = ref<number>(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片上传前的校验
|
* 图片上传前的校验
|
||||||
* @param file
|
* @param file
|
||||||
*/
|
*/
|
||||||
async function beforeUpload(file: any) {
|
async function beforeUpload(file: File) {
|
||||||
if (fileList.value.length >= 5) {
|
uploading.value = true;
|
||||||
|
const urlData = URL.createObjectURL(file);
|
||||||
|
image.src = urlData;
|
||||||
|
// 图片 NSFW 检测
|
||||||
|
const nsfw: NSFWJS = await initNSFWJs();
|
||||||
|
const isNSFW: boolean = await predictNSFW(nsfw, image);
|
||||||
|
if (isNSFW) {
|
||||||
|
message.error(i18n.global.t('comment.illegalImage'));
|
||||||
|
fileData.value = '';
|
||||||
|
uploading.value = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uploading.value = true;
|
fileData.value = urlData;
|
||||||
if (!window.FileReader) return false;
|
await loadImg(image);
|
||||||
const reader = new FileReader();
|
uploading.value = false;
|
||||||
reader.readAsDataURL(file); // 文件转换
|
|
||||||
reader.onload = async function () {
|
imageData.value = "";
|
||||||
image.src = reader.result as string;
|
processedImg.value = "";
|
||||||
// 图片 NSFW 检测
|
isDone.value = false;
|
||||||
const nsfw: NSFWJS = await initNSFWJs();
|
msg.value = "";
|
||||||
const isNSFW: boolean = await predictNSFW(nsfw, image);
|
progressBar.value = 0;
|
||||||
if (isNSFW) {
|
|
||||||
message.error(i18n.global.t('comment.illegalImage'));
|
return true;
|
||||||
fileList.value.pop();
|
|
||||||
uploading.value = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fileList.value.push(image.src);
|
|
||||||
// 加载图片
|
|
||||||
await loadImg(image.src);
|
|
||||||
uploading.value = false;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义上传图片请求
|
* 自定义上传图片请求
|
||||||
*/
|
*/
|
||||||
async function customUploadRequest() {
|
async function customUploadRequest(_file: any) {
|
||||||
imageList.value = fileList.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
imageData.value = fileData.value;
|
||||||
* 移除图片
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
async function removeImage(index: number) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
imageList.value.splice(index, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载图片
|
* 加载图片
|
||||||
* @param src
|
* @param img
|
||||||
*/
|
*/
|
||||||
async function loadImg(src: string) {
|
async function loadImg(img: HTMLImageElement) {
|
||||||
img.value.src = src;
|
wasmModule.value = await Module();
|
||||||
img.value.onload = async () => {
|
if (ctx && wasmModule.value) {
|
||||||
wasmModule.value = await Module();
|
canvas.width = img.width;
|
||||||
if (ctx) {
|
canvas.height = img.height;
|
||||||
canvas.width = img.value.width;
|
ctx.drawImage(img, 0, 0);
|
||||||
canvas.height = img.value.height;
|
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
||||||
ctx.drawImage(img.value, 0, 0);
|
const data = new Uint8Array(imageData.data.buffer);
|
||||||
const imageData = ctx.getImageData(0, 0, img.value.width, img.value.height);
|
input.value = new Img(img.width, img.height, data);
|
||||||
const data = new Uint8Array(imageData.data.buffer);
|
const numPixels = input.value.width * input.value.height;
|
||||||
input.value = new Img(img.value.width, img.value.height, data);
|
const bytesPerImage = numPixels * 4;
|
||||||
const numPixels = input.value.width * input.value.height;
|
const sourcePtr = wasmModule.value._malloc(bytesPerImage);
|
||||||
const bytesPerImage = numPixels * 4;
|
const targetPtr = wasmModule.value._malloc(bytesPerImage);
|
||||||
const sourcePtr = wasmModule.value._malloc(bytesPerImage);
|
wasmModule.value.HEAPU8.set(input.value.data, sourcePtr);
|
||||||
const targetPtr = wasmModule.value._malloc(bytesPerImage);
|
hasAlpha.value = wasmModule.value._check_alpha(sourcePtr, numPixels);
|
||||||
wasmModule.value.HEAPU8.set(input.value.data, sourcePtr);
|
if (hasAlpha.value) {
|
||||||
hasAlpha.value = wasmModule.value._check_alpha(sourcePtr, numPixels);
|
inputAlpha.value = new Img(img.width, img.height);
|
||||||
if (hasAlpha.value) {
|
wasmModule.value._copy_alpha_to_rgb(sourcePtr, targetPtr, numPixels);
|
||||||
inputAlpha.value = new Img(img.value.width, img.value.height);
|
inputAlpha.value.data.set(
|
||||||
wasmModule.value._copy_alpha_to_rgb(sourcePtr, targetPtr, numPixels);
|
wasmModule.value.HEAPU8.subarray(targetPtr, targetPtr + bytesPerImage)
|
||||||
inputAlpha.value.data.set(
|
);
|
||||||
wasmModule.value.HEAPU8.subarray(targetPtr, targetPtr + bytesPerImage)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
wasmModule.value._free(sourcePtr);
|
|
||||||
wasmModule.value._free(targetPtr);
|
|
||||||
imgLoaded.value = true;
|
|
||||||
}
|
}
|
||||||
};
|
wasmModule.value._free(sourcePtr);
|
||||||
|
wasmModule.value._free(targetPtr);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
imageList,
|
|
||||||
fileList,
|
|
||||||
uploading,
|
uploading,
|
||||||
|
imageData,
|
||||||
input,
|
input,
|
||||||
hasAlpha,
|
hasAlpha,
|
||||||
inputAlpha,
|
inputAlpha,
|
||||||
wasmModule,
|
|
||||||
img,
|
|
||||||
processedImg,
|
processedImg,
|
||||||
imgLoaded,
|
wasmModule,
|
||||||
|
isDone,
|
||||||
|
isProcessing,
|
||||||
|
msg,
|
||||||
|
progressBar,
|
||||||
beforeUpload,
|
beforeUpload,
|
||||||
customUploadRequest,
|
customUploadRequest,
|
||||||
removeImage,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,15 +126,10 @@ export const useUpscaleStore = defineStore(
|
|||||||
{
|
{
|
||||||
// 开启数据持久化
|
// 开启数据持久化
|
||||||
persistedState: {
|
persistedState: {
|
||||||
persist: true,
|
persist: false,
|
||||||
storage: localForage,
|
storage: localForage,
|
||||||
key: 'upscale',
|
key: 'upscale',
|
||||||
includePaths: [
|
includePaths: [],
|
||||||
'imageList',
|
|
||||||
'fileList',
|
|
||||||
'img',
|
|
||||||
'processedImg',
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<CompareImage/>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss" src="./index.scss">
|
<style scoped lang="scss" src="./index.scss">
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
<style scoped lang="scss" src="./index.scss">
|
<style scoped lang="scss" src="./index.scss">
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
ref="canvasContainer"
|
ref="canvasContainer"
|
||||||
class="canvas-container drag-over bg dark"
|
class="canvas-container bg drag-over dark"
|
||||||
@mousedown="startDragging"
|
@mousedown="startDragging"
|
||||||
@mouseup="stopDragging"
|
@mouseup="stopDragging"
|
||||||
@mouseleave="stopDragging"
|
@mouseleave="stopDragging"
|
||||||
@@ -11,9 +11,23 @@
|
|||||||
@touchmove="touchMove"
|
@touchmove="touchMove"
|
||||||
@touchend="touchEnd"
|
@touchend="touchEnd"
|
||||||
>
|
>
|
||||||
<canvas ref="canvas"></canvas>
|
<div v-if="store.isProcessing" class="canvas-progressbar">
|
||||||
|
<span class="canvas-progressbar-text">
|
||||||
|
{{ store.msg }}
|
||||||
|
</span>
|
||||||
|
<AProgress
|
||||||
|
:stroke-color="{
|
||||||
|
'0%': '#108ee9',
|
||||||
|
'100%': '#87d068',
|
||||||
|
}"
|
||||||
|
:percent="store.progressBar"
|
||||||
|
:showInfo="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<canvas ref="canvas" v-if="store.imageData || store.processedImg"></canvas>
|
||||||
<div
|
<div
|
||||||
class="dragLine"
|
class="dragLine"
|
||||||
|
v-if="store.isDone"
|
||||||
ref="dragLine">
|
ref="dragLine">
|
||||||
<div class="dragBall"
|
<div class="dragBall"
|
||||||
@mousedown.stop="startDraggingLine"
|
@mousedown.stop="startDraggingLine"
|
||||||
@@ -26,11 +40,20 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="canvas-qr" v-if="!store.isDone && !store.imageData">
|
||||||
|
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)" :size="200"
|
||||||
|
:value="'https://git.landaiqing.cn'"
|
||||||
|
:icon="phone"
|
||||||
|
:iconSize="40"
|
||||||
|
/>
|
||||||
|
<span class="canvas-qr-text">手机扫码上传</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import img1 from "@/assets/images/1.png";
|
import useStore from "@/store";
|
||||||
import img2 from "@/assets/images/2.png";
|
import phone from '@/assets/svgs/qr-phone.svg';
|
||||||
|
|
||||||
const canvasContainer = ref<HTMLDivElement | null>(null);
|
const canvasContainer = ref<HTMLDivElement | null>(null);
|
||||||
const dragging = ref<boolean>(false);
|
const dragging = ref<boolean>(false);
|
||||||
@@ -52,6 +75,7 @@ const touchStartY = ref(0);
|
|||||||
const touchStartDistance = ref(0);
|
const touchStartDistance = ref(0);
|
||||||
const imgScaleStart = ref(1);
|
const imgScaleStart = ref(1);
|
||||||
|
|
||||||
|
const store = useStore().upscale;
|
||||||
const img = ref<HTMLImageElement>(new Image());
|
const img = ref<HTMLImageElement>(new Image());
|
||||||
const processedImg = ref<HTMLImageElement>(new Image());
|
const processedImg = ref<HTMLImageElement>(new Image());
|
||||||
|
|
||||||
@@ -395,30 +419,48 @@ function initCanvasSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
img.value.src = img1;
|
|
||||||
processedImg.value.src = img2;
|
|
||||||
initCanvasSize();
|
|
||||||
|
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => store.imageData,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
img.value.src = newValue;
|
||||||
|
img.value.onload = () => {
|
||||||
|
initCanvasSize();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => store.processedImg,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
processedImg.value.src = newValue;
|
||||||
|
processedImg.value.onload = () => {
|
||||||
|
initCanvasSize();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener("resize", handleResize);
|
||||||
|
store.isDone = false;
|
||||||
|
store.imageData = "";
|
||||||
|
store.processedImg = "";
|
||||||
|
store.isProcessing = false;
|
||||||
|
store.msg = "";
|
||||||
|
store.progressBar = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
img.value.onload = () => {
|
|
||||||
drawImage();
|
|
||||||
};
|
|
||||||
processedImg.value.onload = () => {
|
|
||||||
drawImage();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.canvas-container {
|
.canvas-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -496,6 +538,18 @@ canvas {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canvas-qr {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-qr-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(126, 126, 135, 0.48);
|
||||||
|
}
|
||||||
|
|
||||||
.dragLine {
|
.dragLine {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -508,6 +562,7 @@ canvas {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dragLine:hover {
|
.dragLine:hover {
|
||||||
@@ -527,6 +582,28 @@ canvas {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canvas-progressbar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
//left: 50%;
|
||||||
|
//transform: translate(-50%, -50%);
|
||||||
|
width: 300px;
|
||||||
|
//height: 100px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
//background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
//padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-progressbar-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
298
src/views/Upscale/ParameterSetting.vue
Normal file
298
src/views/Upscale/ParameterSetting.vue
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
<template>
|
||||||
|
<div class="upscale-params">
|
||||||
|
<div class="upscale-params-item">
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">类型:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="model_type"
|
||||||
|
:options="TypeData.map(item => ({label: item, value: item}))">
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">模型:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="model"
|
||||||
|
:options="modes.map((item: any) => ({label: item, value: item}))">
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-params-item">
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">比列:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="factor"
|
||||||
|
:options="scales.map((item: any) => ({label: item, value: item}))">
|
||||||
|
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">分块大小:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="tile_size"
|
||||||
|
:options="tileSize.map((item: any) => ({label: item, value: item}))">
|
||||||
|
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-params-item">
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">重复:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="min_lap"
|
||||||
|
:options="overlapList.map((item: any) => ({label: item, value: item}))">
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-params-item-content">
|
||||||
|
<span class="upscale-params-title">运行环境:</span>
|
||||||
|
<ASelect style="width: 100%" size="large"
|
||||||
|
v-model:value="backend"
|
||||||
|
:options="backendList.map((item: any) => ({label: item, value: item}))">
|
||||||
|
</ASelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ADivider></ADivider>
|
||||||
|
<AButton style="width: 100%;" size="large" shape="default" type="default" :loading="upscale.isProcessing"
|
||||||
|
@click="startTask">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar shape="square" :size="25" :src="run"/>
|
||||||
|
</template>
|
||||||
|
<span class="upscale-params-btn">开始</span>
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {message} from "ant-design-vue";
|
||||||
|
import Img from "@/workers/image.ts";
|
||||||
|
import useStore from "@/store";
|
||||||
|
import run from '@/assets/svgs/run.svg';
|
||||||
|
|
||||||
|
const upscale = useStore().upscale;
|
||||||
|
// ***************参数设置***************
|
||||||
|
const TypeData = ['realesrgan', 'realcugan'];
|
||||||
|
|
||||||
|
const model_type = ref<string>(TypeData[0]);
|
||||||
|
|
||||||
|
const ModelConfig = reactive({
|
||||||
|
realesrgan: {
|
||||||
|
model: ["anime_fast", "anime_plus", "general_fast", "general_plus"],
|
||||||
|
factor: [4],
|
||||||
|
tile_size: [32, 48, 64, 96, 128, 192, 256],
|
||||||
|
},
|
||||||
|
realcugan: {
|
||||||
|
factor: [2, 4],
|
||||||
|
denoise: {
|
||||||
|
2: [
|
||||||
|
"conservative",
|
||||||
|
"no-denoise",
|
||||||
|
"denoise1x",
|
||||||
|
"denoise2x",
|
||||||
|
"denoise3x",
|
||||||
|
],
|
||||||
|
3: ["conservative", "denoise3x"],
|
||||||
|
4: ["conservative", "no-denoise", "denoise3x"],
|
||||||
|
},
|
||||||
|
tile_size: [32, 48, 64, 96, 128, 192, 256, 384, 512],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const factor = ref<number>(4);
|
||||||
|
const model = ref(ModelConfig[model_type.value].model[0]);
|
||||||
|
const modes = computed(() => {
|
||||||
|
if (model_type.value === "realesrgan") {
|
||||||
|
return ModelConfig[model_type.value].model;
|
||||||
|
} else {
|
||||||
|
return ModelConfig[model_type.value].denoise[factor.value];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(model_type, val => {
|
||||||
|
if (model_type.value === "realesrgan") {
|
||||||
|
model.value = ModelConfig[val].model[0];
|
||||||
|
} else {
|
||||||
|
model.value = ModelConfig[val].denoise[factor.value][0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
const scales = computed(() => {
|
||||||
|
return ModelConfig[model_type.value].factor;
|
||||||
|
});
|
||||||
|
|
||||||
|
//tile size
|
||||||
|
const tile_size = ref<number>(128);
|
||||||
|
const tileSize = computed(() => {
|
||||||
|
return ModelConfig[model_type.value].tile_size;
|
||||||
|
});
|
||||||
|
|
||||||
|
// overlap
|
||||||
|
const overlapList = [0, 4, 8, 12, 16, 20];
|
||||||
|
const min_lap = ref<number>(overlapList[3]);
|
||||||
|
|
||||||
|
// run on
|
||||||
|
const backendList = ['webgl', 'webgpu'];
|
||||||
|
const backend = ref<string>(backendList[0]);
|
||||||
|
|
||||||
|
// ********************处理图片*******************
|
||||||
|
const outputData = ref<any>();
|
||||||
|
const imgCanvas = document.createElement("canvas");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebWorker 处理图片
|
||||||
|
*/
|
||||||
|
async function startTask() {
|
||||||
|
console.log(upscale.input);
|
||||||
|
if (upscale.input === null) return;
|
||||||
|
upscale.isProcessing = true;
|
||||||
|
const start = Date.now();
|
||||||
|
const worker = new Worker(new URL("@/workers/upscale.worker.ts", import.meta.url), {
|
||||||
|
type: "module",
|
||||||
|
});
|
||||||
|
worker.onmessage = (e: MessageEvent<any>) => {
|
||||||
|
const {progress, done, output, error, info} = e.data;
|
||||||
|
if (info) {
|
||||||
|
upscale.msg = info;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
message.error(error);
|
||||||
|
upscale.isProcessing = false;
|
||||||
|
worker.terminate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upscale.progressBar = progress;
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
if (!upscale.hasAlpha || (upscale.hasAlpha && upscale.inputAlpha)) {
|
||||||
|
if (upscale.input) {
|
||||||
|
outputData.value = new Img(
|
||||||
|
factor.value * upscale.input.width,
|
||||||
|
factor.value * upscale.input.height,
|
||||||
|
new Uint8Array(output)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upscale.msg = "Processing Image...";
|
||||||
|
if (upscale.inputAlpha) {
|
||||||
|
worker.postMessage(
|
||||||
|
{
|
||||||
|
input: upscale.inputAlpha.data.buffer,
|
||||||
|
factor: factor.value,
|
||||||
|
tile_size: tile_size.value,
|
||||||
|
min_lap: min_lap.value,
|
||||||
|
model_type: model_type.value,
|
||||||
|
width: upscale.inputAlpha.width,
|
||||||
|
height: upscale.inputAlpha.height,
|
||||||
|
model: model.value,
|
||||||
|
backend: backend.value,
|
||||||
|
hasAlpha: true,
|
||||||
|
},
|
||||||
|
[upscale.inputAlpha.data.buffer]
|
||||||
|
);
|
||||||
|
upscale.inputAlpha = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (upscale.hasAlpha && upscale.wasmModule) {
|
||||||
|
const outputArray = new Uint8Array(output);
|
||||||
|
const sourcePtr = upscale.wasmModule._malloc(outputArray.length);
|
||||||
|
const targetPtr = upscale.wasmModule._malloc(outputArray.length);
|
||||||
|
const numPixels = outputArray.length / 4;
|
||||||
|
upscale.wasmModule.HEAPU8.set(outputArray, sourcePtr);
|
||||||
|
upscale.wasmModule.HEAPU8.set(outputData.value.data, targetPtr);
|
||||||
|
upscale.wasmModule._copy_alpha_channel(sourcePtr, targetPtr, numPixels);
|
||||||
|
outputData.value.data.set(
|
||||||
|
upscale.wasmModule.HEAPU8.subarray(
|
||||||
|
targetPtr,
|
||||||
|
targetPtr + outputArray.length
|
||||||
|
)
|
||||||
|
);
|
||||||
|
upscale.wasmModule._free(sourcePtr);
|
||||||
|
upscale.wasmModule._free(targetPtr);
|
||||||
|
upscale.wasmModule = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgCtx = imgCanvas.getContext("2d");
|
||||||
|
if (imgCtx) {
|
||||||
|
imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height);
|
||||||
|
imgCanvas.width = outputData.value.width;
|
||||||
|
imgCanvas.height = outputData.value.height;
|
||||||
|
let outImg = imgCtx.createImageData(
|
||||||
|
outputData.value.width,
|
||||||
|
outputData.value.height
|
||||||
|
);
|
||||||
|
outImg.data.set(outputData.value.data);
|
||||||
|
upscale.input = null;
|
||||||
|
upscale.inputAlpha = null;
|
||||||
|
outputData.value = null;
|
||||||
|
imgCtx.putImageData(outImg, 0, 0);
|
||||||
|
let type = "image/jpeg";
|
||||||
|
const quality = 0.92;
|
||||||
|
if (upscale.hasAlpha) type = "image/png";
|
||||||
|
|
||||||
|
imgCanvas.toBlob(
|
||||||
|
(blob: any) => {
|
||||||
|
upscale.processedImg = URL.createObjectURL(blob);
|
||||||
|
},
|
||||||
|
type,
|
||||||
|
quality
|
||||||
|
);
|
||||||
|
upscale.msg = "Done! Time used: " + (Date.now() - start) / 1000 + "s";
|
||||||
|
upscale.isProcessing = false;
|
||||||
|
upscale.isDone = true;
|
||||||
|
worker.terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (upscale.input) {
|
||||||
|
worker.postMessage(
|
||||||
|
{
|
||||||
|
input: upscale.input.data.buffer,
|
||||||
|
factor: factor.value,
|
||||||
|
tile_size: tile_size.value,
|
||||||
|
min_lap: min_lap.value,
|
||||||
|
model_type: model_type.value,
|
||||||
|
width: upscale.input.width,
|
||||||
|
height: upscale.input.height,
|
||||||
|
model: model.value,
|
||||||
|
backend: backend.value,
|
||||||
|
hasAlpha: false,
|
||||||
|
},
|
||||||
|
[upscale.input.data.buffer]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.upscale-params {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
|
||||||
|
.upscale-params-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.upscale-params-item-content {
|
||||||
|
width: 49%;
|
||||||
|
|
||||||
|
.upscale-params-title {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(126, 126, 135, 0.99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-params-btn {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,184 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="upscale-comparison-result">
|
|
||||||
<div class="upscale-comparison-top">
|
|
||||||
<div class="upscale-comparison-top-left">
|
|
||||||
|
|
||||||
<VueCompareImage :left-image="bg" :right-image="bg2"
|
|
||||||
style="width: 45vw; height: 60vh;border-radius: 10px;box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);"
|
|
||||||
rightImageLabel="修复后"
|
|
||||||
leftImageLabel="原图"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="upscale-comparison-top-right" ref="avatarContainer">
|
|
||||||
<ABadge style="margin-top: 5px;" v-for="(item, index) in upscale.imageList"
|
|
||||||
:key="index">
|
|
||||||
<template #count>
|
|
||||||
<AButton type="text" size="small" class="upscale-file-btn" @click="upscale.removeImage(index)">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="20" :src="remove"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</template>
|
|
||||||
<AAvatar shape="square" :size="avatarSize" @click="bg = item">
|
|
||||||
<template #icon>
|
|
||||||
<AImage :src="item" width="100%" height="100%" :preview="true"/>
|
|
||||||
</template>
|
|
||||||
</AAvatar>
|
|
||||||
</ABadge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ADivider orientation="center" :plain="true"><span class="upscale-comparison-divider-text">保存下载</span>
|
|
||||||
</ADivider>
|
|
||||||
<div class="upscale-comparison-bottom">
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between" gap="large">
|
|
||||||
<AButton type="text" size="large" style="width: 50px; height: 50px;">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="40" :src="download"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
<AButton type="text" style="width: 50px; height: 50px;">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="40" :src="save"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
<AButton type="text" style="width: 50px; height: 50px;">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="35" :src="share"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
<AButton type="text" style="width: 50px; height: 50px;">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="35" :src="packageDownload"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</AFlex>
|
|
||||||
<ADivider type="vertical" style="height: 30px;"/>
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between" gap="large" style="margin-left: 1%">
|
|
||||||
<AFlex :vertical="true" align="flex-start" justify="flex-start" gap="small">
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between">
|
|
||||||
<span class="upscale-comparison-bottom-text">处理数量:</span>
|
|
||||||
<ATag color="orange">{{ 5 }}</ATag>
|
|
||||||
</AFlex>
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between">
|
|
||||||
<span class="upscale-comparison-bottom-text">处理大小:</span>
|
|
||||||
<ATag color="green">{{ upscale.imageList.length }} /mb</ATag>
|
|
||||||
|
|
||||||
</AFlex>
|
|
||||||
</AFlex>
|
|
||||||
<AFlex :vertical="true" align="flex-start" justify="center" gap="small">
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between">
|
|
||||||
<span class="upscale-comparison-bottom-text">消耗时间:</span>
|
|
||||||
<ATag color="cyan">{{ upscale.imageList.length }} /s</ATag>
|
|
||||||
</AFlex>
|
|
||||||
<AFlex :vertical="false" align="center" justify="space-between">
|
|
||||||
<span class="upscale-comparison-bottom-text">内存消耗:</span>
|
|
||||||
<ATag color="purple">{{ upscale.imageList.length }} /mb</ATag>
|
|
||||||
</AFlex>
|
|
||||||
</AFlex>
|
|
||||||
</AFlex>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import {VueCompareImage} from "@/components/VueCompareImage";
|
|
||||||
import bg1 from "@/assets/images/background.jpg";
|
|
||||||
import bg2 from "@/assets/images/background.png";
|
|
||||||
import useStore from "@/store";
|
|
||||||
import remove from "@/assets/svgs/remove.svg";
|
|
||||||
import download from "@/assets/svgs/download.svg";
|
|
||||||
import save from "@/assets/svgs/save.svg";
|
|
||||||
import share from "@/assets/svgs/share.svg";
|
|
||||||
import packageDownload from "@/assets/svgs/package-download.svg";
|
|
||||||
|
|
||||||
const upscale = useStore().upscale;
|
|
||||||
const avatarSize = ref<number>(60);
|
|
||||||
const avatarContainer = ref<HTMLDivElement | null>();
|
|
||||||
const bg = ref<string>(bg1);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新大小
|
|
||||||
*/
|
|
||||||
const updateSize = () => {
|
|
||||||
if (avatarContainer.value) {
|
|
||||||
// 设置图片列表大小
|
|
||||||
const container = avatarContainer.value?.clientWidth || 0;
|
|
||||||
avatarSize.value = Math.min(0.7 * container, 300); // 设置头像大小为容器宽度的10%,最大不超过100
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
updateSize();
|
|
||||||
window.addEventListener('resize', updateSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('resize', updateSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.upscale-comparison-result {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.upscale-comparison-top {
|
|
||||||
width: 100%;
|
|
||||||
height: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
|
|
||||||
.upscale-comparison-top-left {
|
|
||||||
width: 85%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.upscale-comparison-image {
|
|
||||||
width: 40vw;
|
|
||||||
height: 60vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-comparison-top-right {
|
|
||||||
width: 14%;
|
|
||||||
height: 60vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
overflow-y: scroll;
|
|
||||||
border: 1px solid rgba(126, 126, 135, 0.48);
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-comparison-divider-text {
|
|
||||||
font-size: 13px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-comparison-bottom {
|
|
||||||
width: 100%;
|
|
||||||
height: 20%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
|
|
||||||
.upscale-comparison-bottom-text {
|
|
||||||
font-size: 13px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,106 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="upscale-upload-container">
|
<div class="upscale-upload-container" ref="containerRef">
|
||||||
<div class="upscale-upload-content">
|
<div class="upscale-upload-content" ref="uploadDraggerRef">
|
||||||
<Spin :spinning="upscale.uploading" indicator="magic-ring">
|
<Spin :spinning="upscale.uploading" indicator="magic-ring">
|
||||||
<AUploadDragger
|
<AUploadDragger
|
||||||
name="image"
|
name="image"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
:directory="false"
|
:directory="false"
|
||||||
:maxCount="5"
|
:maxCount="1"
|
||||||
:beforeUpload="upscale.beforeUpload"
|
:beforeUpload="upscale.beforeUpload"
|
||||||
:custom-request="upscale.customUploadRequest"
|
:custom-request="upscale.customUploadRequest"
|
||||||
:disabled="upscale.uploading"
|
:disabled="upscale.uploading"
|
||||||
:showUploadList="false">
|
:showUploadList="false">
|
||||||
<div class="upscale-upload-content-main">
|
<div class="upscale-upload-content-main">
|
||||||
<div class="upscale-upload-content-left">
|
<ABadge :offset="[-10, 10]">
|
||||||
<ABadge :count="upscale.imageList.length" :offset="[-10, 10]">
|
<template #count>
|
||||||
<AAvatar shape="square" :size="70" :src="file"/>
|
<AAvatar :size="25" :src="sueccess" v-if="upscale.imageData"/>
|
||||||
</ABadge>
|
<AAvatar :size="25" :src="warn" v-if="!upscale.imageData"/>
|
||||||
<span class="upscale-upload-text">
|
</template>
|
||||||
|
<AAvatar shape="square" :size="70" :src="file"/>
|
||||||
|
</ABadge>
|
||||||
|
<span class="upscale-upload-text">
|
||||||
点击或拖拽上传图片
|
点击或拖拽上传图片
|
||||||
</span>
|
</span>
|
||||||
<span class="upscale-upload-tip">最多一次处理5张图片</span>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-upload-content-right" ref="qrcodeContainer">
|
|
||||||
<AQrcode :value="'https://www.baidu.com'"
|
|
||||||
class="upscale-upload-qrcode"
|
|
||||||
:icon="qrphone"
|
|
||||||
:iconSize="iconSize"
|
|
||||||
:bordered="false"
|
|
||||||
:size="qrcodeSize"
|
|
||||||
color="rgba(126, 126, 135, 1)"/>
|
|
||||||
<span class="upscale-upload-qr-text">手机扫码上传</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</AUploadDragger>
|
</AUploadDragger>
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ADivider orientation="center" :plain="true"><span class="upscale-file-list-title">图片列表</span></ADivider>
|
|
||||||
<div class="upscale-file-list" ref="fileListContainer">
|
|
||||||
<div v-if="upscale.imageList.length > 0">
|
|
||||||
<AImagePreviewGroup>
|
|
||||||
<ABadge style="margin-left: 10px;" v-for="(item, index) in upscale.imageList"
|
|
||||||
:key="index">
|
|
||||||
<template #count>
|
|
||||||
<AButton type="text" size="small" class="upscale-file-btn" @click="upscale.removeImage(index)">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="20" :src="remove"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</template>
|
|
||||||
<AAvatar shape="square" :size="avatarSize" v-if="item">
|
|
||||||
<template #icon>
|
|
||||||
<AImage :src="item" width="100%" height="100%"/>
|
|
||||||
</template>
|
|
||||||
</AAvatar>
|
|
||||||
</ABadge>
|
|
||||||
</AImagePreviewGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AEmpty :image="empty" v-if="upscale.imageList.length === 0" :description="null"/>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import file from "@/assets/svgs/file.svg";
|
import file from "@/assets/svgs/file.svg";
|
||||||
import qrphone from "@/assets/svgs/qr-phone.svg";
|
|
||||||
import empty from "@/assets/svgs/empty.svg";
|
|
||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
import Spin from "@/components/MyUI/Spin/Spin.vue";
|
import Spin from "@/components/MyUI/Spin/Spin.vue";
|
||||||
import remove from '@/assets/svgs/remove.svg';
|
import sueccess from '@/assets/svgs/success.svg';
|
||||||
|
import warn from '@/assets/svgs/warn.svg';
|
||||||
|
|
||||||
const upscale = useStore().upscale;
|
const upscale = useStore().upscale;
|
||||||
const qrcodeSize = ref<number>(160);
|
|
||||||
const iconSize = ref<number>(30);
|
|
||||||
const qrcodeContainer = ref<HTMLElement | null>(null);
|
|
||||||
const avatarSize = ref<number>(80);
|
|
||||||
const fileListContainer = ref<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
/**
|
const uploadDraggerRef = ref<HTMLDivElement | null>(null);
|
||||||
* 更新大小
|
const containerRef = ref<HTMLDivElement | null>(null);
|
||||||
*/
|
|
||||||
const updateSize = () => {
|
|
||||||
if (qrcodeContainer.value && fileListContainer.value) {
|
|
||||||
// 设置 QRCode 大小
|
|
||||||
const containerWidth = qrcodeContainer.value.clientWidth;
|
|
||||||
qrcodeSize.value = containerWidth * 0.5; // 设置 QRCode 为父盒子宽度的80%
|
|
||||||
iconSize.value = Math.min(containerWidth * 0.1, 50); // 设置 icon 大小为父盒子宽度的10%
|
|
||||||
|
|
||||||
// 设置图片列表大小
|
|
||||||
const container = fileListContainer.value.clientWidth;
|
|
||||||
avatarSize.value = Math.min(0.17 * container, 300); // 设置头像大小为容器宽度的10%,最大不超过100
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
updateSize();
|
|
||||||
window.addEventListener('resize', updateSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('resize', updateSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -122,44 +62,24 @@ onBeforeUnmount(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
.upscale-upload-content-left {
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
|
|
||||||
.upscale-upload-text {
|
.upscale-upload-text {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-upload-btn {
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-upload-tip {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.upscale-upload-content-right {
|
.upscale-upload-btn {
|
||||||
width: 50%;
|
width: 60%;
|
||||||
display: flex;
|
}
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.upscale-upload-qr-text {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.upscale-upload-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(126, 126, 135, 0.99);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,8 +124,5 @@ onBeforeUnmount(() => {
|
|||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
}
|
}
|
||||||
.upscale-upload-content-right {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,388 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="upscale-container">
|
|
||||||
<AFlex :vertical="false" align="center" justify="flex-start">
|
|
||||||
<AAvatar shape="square" :size="22" :src="ai"/>
|
|
||||||
<span class="upscale-title">图像修复</span>
|
|
||||||
</AFlex>
|
|
||||||
<AFlex class="upscale-content" :vertical="false" align="center" justify="flex-start">
|
|
||||||
<div class="upscale-content-left">
|
|
||||||
<ACard class="upscale-content-left-container">
|
|
||||||
<UploadImage/>
|
|
||||||
<ADivider orientation="center" :plain="true"><span class="upscale-divider-title">参数设置</span></ADivider>
|
|
||||||
<div class="upscale-content-left-params">
|
|
||||||
<div class="upscale-content-params-left">
|
|
||||||
<div class="upscale-content-params-item">
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">类型:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="model_type"
|
|
||||||
:options="TypeData.map(item => ({label: item, value: item}))">
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">模型:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="model"
|
|
||||||
:options="modes.map((item: any) => ({label: item, value: item}))">
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-item">
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">比列:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="factor"
|
|
||||||
:options="scales.map((item: any) => ({label: item, value: item}))">
|
|
||||||
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">分块大小:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="tile_size"
|
|
||||||
:options="tileSize.map((item: any) => ({label: item, value: item}))">
|
|
||||||
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-item">
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">重复:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="min_lap"
|
|
||||||
:options="overlapList.map((item: any) => ({label: item, value: item}))">
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-item-content">
|
|
||||||
<span class="upscale-content-params-title">运行环境:</span>
|
|
||||||
<ASelect style="width: 100%" size="default"
|
|
||||||
v-model:value="backend"
|
|
||||||
:options="backendList.map((item: any) => ({label: item, value: item}))">
|
|
||||||
</ASelect>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-params-right">
|
|
||||||
<AButton type="text" style="width: 60px;height: 60px;">
|
|
||||||
<template #icon>
|
|
||||||
<AAvatar shape="square" :size="50" :src="run" @click="startTask()"/>
|
|
||||||
</template>
|
|
||||||
</AButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ACard>
|
|
||||||
</div>
|
|
||||||
<div class="upscale-content-right">
|
|
||||||
<ACard class="upscale-content-right-container">
|
|
||||||
<ProcessResult/>
|
|
||||||
</ACard>
|
|
||||||
</div>
|
|
||||||
</AFlex>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import UploadImage from "@/views/Upscale/UploadImage.vue";
|
|
||||||
import ai from "@/assets/svgs/ai.svg";
|
|
||||||
import run from "@/assets/svgs/run.svg";
|
|
||||||
import ProcessResult from "@/views/Upscale/ProcessResult.vue";
|
|
||||||
import useStore from "@/store";
|
|
||||||
import {message} from "ant-design-vue";
|
|
||||||
import Img from "@/workers/image.ts";
|
|
||||||
|
|
||||||
|
|
||||||
const upscale = useStore().upscale;
|
|
||||||
|
|
||||||
// ***************参数设置***************
|
|
||||||
const TypeData = ['realesrgan', 'realcugan'];
|
|
||||||
|
|
||||||
const model_type = ref<string>(TypeData[0]);
|
|
||||||
|
|
||||||
const ModelConfig = reactive({
|
|
||||||
realesrgan: {
|
|
||||||
model: ["anime_fast", "anime_plus", "general_fast", "general_plus"],
|
|
||||||
factor: [4],
|
|
||||||
tile_size: [32, 48, 64, 96, 128, 192, 256],
|
|
||||||
},
|
|
||||||
realcugan: {
|
|
||||||
factor: [2, 4],
|
|
||||||
denoise: {
|
|
||||||
2: [
|
|
||||||
"conservative",
|
|
||||||
"no-denoise",
|
|
||||||
"denoise1x",
|
|
||||||
"denoise2x",
|
|
||||||
"denoise3x",
|
|
||||||
],
|
|
||||||
3: ["conservative", "denoise3x"],
|
|
||||||
4: ["conservative", "no-denoise", "denoise3x"],
|
|
||||||
},
|
|
||||||
tile_size: [32, 48, 64, 96, 128, 192, 256, 384, 512],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const factor = ref<number>(4);
|
|
||||||
const model = ref(ModelConfig[model_type.value].model[0]);
|
|
||||||
const modes = computed(() => {
|
|
||||||
if (model_type.value === "realesrgan") {
|
|
||||||
return ModelConfig[model_type.value].model;
|
|
||||||
} else {
|
|
||||||
return ModelConfig[model_type.value].denoise[factor.value];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(model_type, val => {
|
|
||||||
if (model_type.value === "realesrgan") {
|
|
||||||
model.value = ModelConfig[val].model[0];
|
|
||||||
} else {
|
|
||||||
model.value = ModelConfig[val].denoise[factor.value][0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Scale
|
|
||||||
const scales = computed(() => {
|
|
||||||
return ModelConfig[model_type.value].factor;
|
|
||||||
});
|
|
||||||
|
|
||||||
//tile size
|
|
||||||
const tile_size = ref<number>(128);
|
|
||||||
const tileSize = computed(() => {
|
|
||||||
return ModelConfig[model_type.value].tile_size;
|
|
||||||
});
|
|
||||||
|
|
||||||
// overlap
|
|
||||||
const overlapList = [0, 4, 8, 12, 16, 20];
|
|
||||||
const min_lap = ref<number>(overlapList[3]);
|
|
||||||
|
|
||||||
// run on
|
|
||||||
const backendList = ['webgl', 'webgpu'];
|
|
||||||
const backend = ref<string>(backendList[0]);
|
|
||||||
|
|
||||||
// ********************处理图片*******************
|
|
||||||
const isProcessing = ref<boolean>(false);
|
|
||||||
const msg = ref<string>("");
|
|
||||||
const progressBar = ref<number>(0);
|
|
||||||
const outputData = ref<any>();
|
|
||||||
const isDone = ref<boolean>(false);
|
|
||||||
const imgCanvas = document.createElement("canvas");
|
|
||||||
|
|
||||||
const worker = new Worker(new URL("@/workers/upscale.worker.ts", import.meta.url), {
|
|
||||||
type: "module",
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebWorker 处理图片
|
|
||||||
*/
|
|
||||||
async function startTask() {
|
|
||||||
if (upscale.input === null) return;
|
|
||||||
isProcessing.value = true;
|
|
||||||
const start = Date.now();
|
|
||||||
worker.onmessage = (e: MessageEvent<any>) => {
|
|
||||||
const {progress, done, output, error, info} = e.data;
|
|
||||||
if (info) {
|
|
||||||
message.info(info);
|
|
||||||
msg.value = info;
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
message.error(error);
|
|
||||||
isProcessing.value = false;
|
|
||||||
worker.terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
progressBar.value = progress;
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
if (!upscale.hasAlpha || (upscale.hasAlpha && upscale.inputAlpha)) {
|
|
||||||
if (upscale.input) {
|
|
||||||
outputData.value = new Img(
|
|
||||||
factor.value * upscale.input.width,
|
|
||||||
factor.value * upscale.input.height,
|
|
||||||
new Uint8Array(output)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg.value = "Processing Image...";
|
|
||||||
if (upscale.inputAlpha) {
|
|
||||||
worker.postMessage(
|
|
||||||
{
|
|
||||||
input: upscale.inputAlpha.data.buffer,
|
|
||||||
factor: factor.value,
|
|
||||||
tile_size: tile_size.value,
|
|
||||||
min_lap: min_lap.value,
|
|
||||||
model_type: model_type.value,
|
|
||||||
width: upscale.inputAlpha.width,
|
|
||||||
height: upscale.inputAlpha.height,
|
|
||||||
model: model.value,
|
|
||||||
backend: backend.value,
|
|
||||||
hasAlpha: true,
|
|
||||||
},
|
|
||||||
[upscale.inputAlpha.data.buffer]
|
|
||||||
);
|
|
||||||
upscale.inputAlpha = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (upscale.hasAlpha && upscale.wasmModule) {
|
|
||||||
const outputArray = new Uint8Array(output);
|
|
||||||
const sourcePtr = upscale.wasmModule._malloc(outputArray.length);
|
|
||||||
const targetPtr = upscale.wasmModule._malloc(outputArray.length);
|
|
||||||
const numPixels = outputArray.length / 4;
|
|
||||||
upscale.wasmModule.HEAPU8.set(outputArray, sourcePtr);
|
|
||||||
upscale.wasmModule.HEAPU8.set(outputData.value.data, targetPtr);
|
|
||||||
upscale.wasmModule._copy_alpha_channel(sourcePtr, targetPtr, numPixels);
|
|
||||||
outputData.value.data.set(
|
|
||||||
upscale.wasmModule.HEAPU8.subarray(
|
|
||||||
targetPtr,
|
|
||||||
targetPtr + outputArray.length
|
|
||||||
)
|
|
||||||
);
|
|
||||||
upscale.wasmModule._free(sourcePtr);
|
|
||||||
upscale.wasmModule._free(targetPtr);
|
|
||||||
upscale.wasmModule = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imgCtx = imgCanvas.getContext("2d");
|
|
||||||
if (imgCtx) {
|
|
||||||
imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height);
|
|
||||||
imgCanvas.width = outputData.value.width;
|
|
||||||
imgCanvas.height = outputData.value.height;
|
|
||||||
let outImg = imgCtx.createImageData(
|
|
||||||
outputData.value.width,
|
|
||||||
outputData.value.height
|
|
||||||
);
|
|
||||||
outImg.data.set(outputData.value.data);
|
|
||||||
upscale.input = null;
|
|
||||||
upscale.inputAlpha = null;
|
|
||||||
outputData.value = null;
|
|
||||||
imgCtx.putImageData(outImg, 0, 0);
|
|
||||||
let type = "image/jpeg";
|
|
||||||
const quality = 0.92;
|
|
||||||
if (upscale.hasAlpha) type = "image/png";
|
|
||||||
if (upscale.processedImg) {
|
|
||||||
imgCanvas.toBlob(
|
|
||||||
(blob: any) => {
|
|
||||||
upscale.processedImg.src = URL.createObjectURL(blob);
|
|
||||||
},
|
|
||||||
type,
|
|
||||||
quality
|
|
||||||
);
|
|
||||||
upscale.processedImg.onload = () => {
|
|
||||||
msg.value = "Done! Time used: " + (Date.now() - start) / 1000 + "s";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
isProcessing.value = false;
|
|
||||||
isDone.value = true;
|
|
||||||
worker.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (upscale.input) {
|
|
||||||
worker.postMessage(
|
|
||||||
{
|
|
||||||
input: upscale.input.data.buffer,
|
|
||||||
factor: factor.value,
|
|
||||||
tile_size: tile_size.value,
|
|
||||||
min_lap: min_lap.value,
|
|
||||||
model_type: model_type.value,
|
|
||||||
width: upscale.input.width,
|
|
||||||
height: upscale.input.height,
|
|
||||||
model: model.value,
|
|
||||||
backend: backend.value,
|
|
||||||
hasAlpha: false,
|
|
||||||
},
|
|
||||||
[upscale.input.data.buffer]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.upscale-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
|
|
||||||
.upscale-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-content {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin-top: 5px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.upscale-content-left {
|
|
||||||
width: 49%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.upscale-content-left-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.upscale-divider-title {
|
|
||||||
font-size: 13px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-content-left-params {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.upscale-content-params-left {
|
|
||||||
width: 80%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.upscale-content-params-item {
|
|
||||||
width: 30%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.upscale-content-params-title {
|
|
||||||
font-size: 13px;
|
|
||||||
color: rgba(126, 126, 135, 0.99);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-content-params-right {
|
|
||||||
width: 20%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upscale-content-right {
|
|
||||||
width: 50%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.upscale-content-right-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
78
src/views/Upscale/index.vue
Normal file
78
src/views/Upscale/index.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div class="upscale-container">
|
||||||
|
<AFlex class="upscale-content" :vertical="false" align="center" justify="flex-start">
|
||||||
|
<div class="upscale-content-left">
|
||||||
|
<ACard class="upscale-content-left-container">
|
||||||
|
<div class="upscale-content-left-upload">
|
||||||
|
<UploadImage/>
|
||||||
|
</div>
|
||||||
|
<ADivider orientation="center" :plain="true">
|
||||||
|
<span class="upscale-divider-title">参数设置</span>
|
||||||
|
</ADivider>
|
||||||
|
<ParameterSetting/>
|
||||||
|
</ACard>
|
||||||
|
</div>
|
||||||
|
<div class="upscale-content-right">
|
||||||
|
<CompareImage style="border-radius: 10px"/>
|
||||||
|
</div>
|
||||||
|
</AFlex>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import UploadImage from "@/views/Upscale/UploadImage.vue";
|
||||||
|
import CompareImage from "@/views/Upscale/CompareImage.vue";
|
||||||
|
import ParameterSetting from "@/views/Upscale/ParameterSetting.vue";
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.upscale-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
|
||||||
|
//.upscale-title {
|
||||||
|
// font-size: 16px;
|
||||||
|
// font-weight: bold;
|
||||||
|
// margin-left: 5px;
|
||||||
|
//}
|
||||||
|
|
||||||
|
.upscale-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.upscale-content-left {
|
||||||
|
width: 29%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.upscale-content-left-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.upscale-divider-title {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(126, 126, 135, 0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-content-right {
|
||||||
|
width: 70%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
Reference in New Issue
Block a user