✨ complete mobile image upload
This commit is contained in:
@@ -3,9 +3,8 @@ VITE_NODE_ENV='development'
|
|||||||
|
|
||||||
# 开发环境
|
# 开发环境
|
||||||
VITE_APP_BASE_API='/sys'
|
VITE_APP_BASE_API='/sys'
|
||||||
|
# 网站域名
|
||||||
# 页面 title 前缀
|
VITE_APP_WEB_URL='http://localhost:5173'
|
||||||
VITE_APP_TITLE=开发环境
|
|
||||||
|
|
||||||
# 网络请求公用地址
|
# 网络请求公用地址
|
||||||
VITE_API_BASE_URL='http://127.0.0.1:80'
|
VITE_API_BASE_URL='http://127.0.0.1:80'
|
||||||
@@ -20,4 +19,6 @@ VITE_QR_SOCKET_URL='ws://127.0.0.1:80/api/ws/qrcode'
|
|||||||
|
|
||||||
VITE_MESSAGE_SOCKET_URL='ws://127.0.0.1:80/api/ws/message'
|
VITE_MESSAGE_SOCKET_URL='ws://127.0.0.1:80/api/ws/message'
|
||||||
|
|
||||||
|
VITE_FILE_SOCKET_URL='ws://127.0.0.1:80/api/ws/file'
|
||||||
|
|
||||||
VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx'
|
VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx'
|
||||||
|
@@ -3,8 +3,8 @@ VITE_NODE_ENV='production'
|
|||||||
# 生产环境
|
# 生产环境
|
||||||
VITE_APP_BASE_API='/sys'
|
VITE_APP_BASE_API='/sys'
|
||||||
|
|
||||||
# 页面 title 前缀
|
# 网站域名
|
||||||
VITE_APP_TITLE=生产环境
|
VITE_APP_WEB_URL='http://localhost:5173'
|
||||||
|
|
||||||
# 网络请求公用地址
|
# 网络请求公用地址
|
||||||
VITE_API_BASE_URL='https://landaiqing.cn'
|
VITE_API_BASE_URL='https://landaiqing.cn'
|
||||||
@@ -18,5 +18,8 @@ VITE_APP_TOKEN_KEY='Bearer'
|
|||||||
VITE_QR_SOCKET_URL='wss://landaiqing.cn/api/ws/qr_ws'
|
VITE_QR_SOCKET_URL='wss://landaiqing.cn/api/ws/qr_ws'
|
||||||
|
|
||||||
VITE_MESSAGE_SOCKET_URL='wss://landaiqing.cn/api/ws/message_ws'
|
VITE_MESSAGE_SOCKET_URL='wss://landaiqing.cn/api/ws/message_ws'
|
||||||
|
|
||||||
|
VITE_FILE_SOCKET_URL='ws://127.0.0.1:80/api/ws/file'
|
||||||
|
|
||||||
# 签名密钥
|
# 签名密钥
|
||||||
VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx'
|
VITE_FINGERPRINT_KEY='idm0jdoau38lwourb4pbjk4dxkat0kcx'
|
||||||
|
15
components.d.ts
vendored
15
components.d.ts
vendored
@@ -27,21 +27,30 @@ declare module 'vue' {
|
|||||||
AInput: typeof import('ant-design-vue/es')['Input']
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
Alert: typeof import('./src/components/MyUI/Alert/Alert.vue')['default']
|
Alert: typeof import('./src/components/MyUI/Alert/Alert.vue')['default']
|
||||||
|
AList: typeof import('ant-design-vue/es')['List']
|
||||||
|
AListItem: typeof import('ant-design-vue/es')['ListItem']
|
||||||
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
|
AllPhoto: typeof import('./src/views/Photograph/AllPhoto/AllPhoto.vue')['default']
|
||||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||||
AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup']
|
AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup']
|
||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
AnimatedNature: typeof import('./src/components/AnimatedNature/AnimatedNature.vue')['default']
|
||||||
|
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||||
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
AQrcode: typeof import('ant-design-vue/es')['QRCode']
|
||||||
|
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||||
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
|
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
|
||||||
|
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||||
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
ATabs: typeof import('ant-design-vue/es')['Tabs']
|
||||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
||||||
Avatar: typeof import('./src/components/MyUI/Avatar/Avatar.vue')['default']
|
Avatar: typeof import('./src/components/MyUI/Avatar/Avatar.vue')['default']
|
||||||
BackgroundAnimation: typeof import('./src/components/BackgroundAnimation/BackgroundAnimation.vue')['default']
|
BackgroundAnimation: typeof import('./src/components/BackgroundAnimation/BackgroundAnimation.vue')['default']
|
||||||
@@ -55,6 +64,7 @@ declare module 'vue' {
|
|||||||
Carousel: typeof import('./src/components/MyUI/Carousel/Carousel.vue')['default']
|
Carousel: typeof import('./src/components/MyUI/Carousel/Carousel.vue')['default']
|
||||||
Cascader: typeof import('./src/components/MyUI/Cascader/Cascader.vue')['default']
|
Cascader: typeof import('./src/components/MyUI/Cascader/Cascader.vue')['default']
|
||||||
Checkbox: typeof import('./src/components/MyUI/Checkbox/Checkbox.vue')['default']
|
Checkbox: typeof import('./src/components/MyUI/Checkbox/Checkbox.vue')['default']
|
||||||
|
CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined']
|
||||||
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default']
|
||||||
Col: typeof import('./src/components/MyUI/Grid/Col.vue')['default']
|
Col: typeof import('./src/components/MyUI/Grid/Col.vue')['default']
|
||||||
Collapse: typeof import('./src/components/MyUI/Collapse/Collapse.vue')['default']
|
Collapse: typeof import('./src/components/MyUI/Collapse/Collapse.vue')['default']
|
||||||
@@ -71,6 +81,7 @@ declare module 'vue' {
|
|||||||
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
|
||||||
Ellipsis: typeof import('./src/components/MyUI/Ellipsis/Ellipsis.vue')['default']
|
Ellipsis: typeof import('./src/components/MyUI/Ellipsis/Ellipsis.vue')['default']
|
||||||
Empty: typeof import('./src/components/MyUI/Empty/Empty.vue')['default']
|
Empty: typeof import('./src/components/MyUI/Empty/Empty.vue')['default']
|
||||||
|
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
|
||||||
Flex: typeof import('./src/components/MyUI/Flex/Flex.vue')['default']
|
Flex: typeof import('./src/components/MyUI/Flex/Flex.vue')['default']
|
||||||
FloatButton: typeof import('./src/components/MyUI/FloatButton/FloatButton.vue')['default']
|
FloatButton: typeof import('./src/components/MyUI/FloatButton/FloatButton.vue')['default']
|
||||||
Folder: typeof import('./src/components/Folder/Folder.vue')['default']
|
Folder: typeof import('./src/components/Folder/Folder.vue')['default']
|
||||||
@@ -100,6 +111,8 @@ declare module 'vue' {
|
|||||||
ParameterSetting: typeof import('./src/views/Upscale/ParameterSetting.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']
|
||||||
|
PhoneUpload: typeof import('./src/views/PhoneUpload/PhoneUpload.vue')['default']
|
||||||
|
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
|
||||||
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']
|
||||||
Progress: typeof import('./src/components/MyUI/Progress/Progress.vue')['default']
|
Progress: typeof import('./src/components/MyUI/Progress/Progress.vue')['default']
|
||||||
@@ -121,6 +134,7 @@ declare module 'vue' {
|
|||||||
Scrollbar: typeof import('./src/components/MyUI/Scrollbar/Scrollbar.vue')['default']
|
Scrollbar: typeof import('./src/components/MyUI/Scrollbar/Scrollbar.vue')['default']
|
||||||
Segmented: typeof import('./src/components/MyUI/Segmented/Segmented.vue')['default']
|
Segmented: typeof import('./src/components/MyUI/Segmented/Segmented.vue')['default']
|
||||||
Select: typeof import('./src/components/MyUI/Select/Select.vue')['default']
|
Select: typeof import('./src/components/MyUI/Select/Select.vue')['default']
|
||||||
|
SendOutlined: typeof import('@ant-design/icons-vue')['SendOutlined']
|
||||||
Skeleton: typeof import('./src/components/MyUI/Skeleton/Skeleton.vue')['default']
|
Skeleton: typeof import('./src/components/MyUI/Skeleton/Skeleton.vue')['default']
|
||||||
Slider: typeof import('./src/components/MyUI/Slider/Slider.vue')['default']
|
Slider: typeof import('./src/components/MyUI/Slider/Slider.vue')['default']
|
||||||
Space: typeof import('./src/components/MyUI/Space/Space.vue')['default']
|
Space: typeof import('./src/components/MyUI/Space/Space.vue')['default']
|
||||||
@@ -147,6 +161,7 @@ declare module 'vue' {
|
|||||||
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']
|
||||||
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default']
|
||||||
|
WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined']
|
||||||
Waterfall: typeof import('./src/components/MyUI/Waterfall/Waterfall.vue')['default']
|
Waterfall: typeof import('./src/components/MyUI/Waterfall/Waterfall.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
|
"docker-build": "docker build -t schisandra/schisandra-cloud-album-front ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-axios": "^2.0.11",
|
"@alova/adapter-axios": "^2.0.12",
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@tensorflow/tfjs": "^4.22.0",
|
"@tensorflow/tfjs": "^4.22.0",
|
||||||
"@tensorflow/tfjs-backend-webgl": "^4.22.0",
|
"@tensorflow/tfjs-backend-webgl": "^4.22.0",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"@vuepic/vue-datepicker": "^10.0.0",
|
"@vuepic/vue-datepicker": "^10.0.0",
|
||||||
"@vueuse/core": "^12.0.0",
|
"@vueuse/core": "^12.0.0",
|
||||||
"@vueuse/integrations": "^12.0.0",
|
"@vueuse/integrations": "^12.0.0",
|
||||||
"alova": "^3.2.6",
|
"alova": "^3.2.7",
|
||||||
"animejs": "^3.2.2",
|
"animejs": "^3.2.2",
|
||||||
"ant-design-vue": "^4.2.6",
|
"ant-design-vue": "^4.2.6",
|
||||||
"autofit.js": "^3.2.2",
|
"autofit.js": "^3.2.2",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"globals": "^15.13.0",
|
"globals": "^15.13.0",
|
||||||
"sass": "^1.83.0",
|
"sass": "^1.83.0",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"typescript-eslint": "^8.18.0",
|
"typescript-eslint": "^8.18.1",
|
||||||
"unplugin-vue-components": "^0.28.0",
|
"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",
|
||||||
|
15
src/api/upscale/index.ts
Normal file
15
src/api/upscale/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import {service} from "@/utils/alova/service.ts";
|
||||||
|
import {uploadImageRequest} from "@/types/upscale";
|
||||||
|
|
||||||
|
export const uploadImage = (data: uploadImageRequest) => {
|
||||||
|
return service.Post('/api/auth/upscale/upload', {
|
||||||
|
image: data.image,
|
||||||
|
access_token: data.access_token,
|
||||||
|
user_id: data.user_id,
|
||||||
|
}, {
|
||||||
|
meta: {
|
||||||
|
ignoreToken: false,
|
||||||
|
signature: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
1
src/assets/svgs/deleted.svg
Normal file
1
src/assets/svgs/deleted.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1734406724014" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9426" width="200" height="200"><path d="M714.9568 903.9872H310.8864c-63.2832 0-114.5856-51.3024-114.5856-114.5856V233.0624h633.2416v556.3392c0 63.2832-51.3024 114.5856-114.5856 114.5856z" fill="#FF5F5F" p-id="9427"></path><path d="M905.6768 217.7024H726.528V170.496c0-42.9568-34.9696-77.9264-77.9264-77.9264h-271.36c-42.9568 0-77.9264 34.9696-77.9264 77.9264v47.2064H120.1664c-8.4992 0-15.36 6.8608-15.36 15.36s6.8608 15.36 15.36 15.36h60.7744v540.9792c0 71.6288 58.3168 129.9456 129.9456 129.9456h404.0704c71.6288 0 129.9456-58.3168 129.9456-129.9456V248.4224h60.7744c8.4992 0 15.36-6.8608 15.36-15.36s-6.8608-15.36-15.36-15.36zM330.0352 170.496c0-26.0096 21.1968-47.2064 47.2064-47.2064h271.4112c26.0096 0 47.2064 21.1968 47.2064 47.2064v47.2064H330.0352V170.496z m484.1472 618.9056c0 54.7328-44.4928 99.2256-99.2256 99.2256H310.8864c-54.7328 0-99.2256-44.4928-99.2256-99.2256V248.4224h602.5216v540.9792z" fill="#424242" p-id="9428"></path><path d="M379.3408 734.3104c-8.4992 0-15.36-6.8608-15.36-15.36V402.8928c0-8.4992 6.8608-15.36 15.36-15.36s15.36 6.8608 15.36 15.36v316.0576c0 8.448-6.912 15.36-15.36 15.36zM524.0832 734.3104c-8.4992 0-15.36-6.8608-15.36-15.36V402.8928c0-8.4992 6.8608-15.36 15.36-15.36s15.36 6.8608 15.36 15.36v316.0576c0 8.448-6.912 15.36-15.36 15.36zM670.5152 734.3104c-8.4992 0-15.36-6.8608-15.36-15.36V402.8928c0-8.4992 6.8608-15.36 15.36-15.36s15.36 6.8608 15.36 15.36v316.0576c0 8.448-6.912 15.36-15.36 15.36z" fill="#FFFFFF" p-id="9429"></path></svg>
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,11 +1 @@
|
|||||||
<svg t="1733761095875" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
<svg t="1734409010371" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16420" width="200" height="200"><path d="M820.409449 797.228346q0 25.19685-10.07874 46.866142t-27.716535 38.299213-41.322835 26.204724-50.897638 9.574803l-357.795276 0q-27.212598 0-50.897638-9.574803t-41.322835-26.204724-27.716535-38.299213-10.07874-46.866142l0-675.275591q0-25.19685 10.07874-47.370079t27.716535-38.80315 41.322835-26.204724 50.897638-9.574803l357.795276 0q27.212598 0 50.897638 9.574803t41.322835 26.204724 27.716535 38.80315 10.07874 47.370079l0 675.275591zM738.771654 170.330709l-455.559055 0 0 577.511811 455.559055 0 0-577.511811zM510.992126 776.062992q-21.165354 0-36.787402 15.11811t-15.622047 37.291339q0 21.165354 15.622047 36.787402t36.787402 15.622047q22.173228 0 37.291339-15.622047t15.11811-36.787402q0-22.173228-15.11811-37.291339t-37.291339-15.11811zM591.622047 84.661417q0-8.062992-5.03937-12.598425t-11.086614-4.535433l-128 0q-5.03937 0-10.582677 4.535433t-5.543307 12.598425 5.03937 12.598425 11.086614 4.535433l128 0q6.047244 0 11.086614-4.535433t5.03937-12.598425z" p-id="16421" fill="#707070"></path></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.1 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<AMenu
|
<AMenu
|
||||||
:selectedKeys="[router.currentRoute.value.path.split('/').slice(-2).join('/')]"
|
:selectedKeys="[route.path.split('/').slice(-2).join('/')]"
|
||||||
:selectable="true"
|
:selectable="true"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
mode="vertical"
|
mode="vertical"
|
||||||
@@ -96,8 +96,11 @@ import recyclingbin from '@/assets/svgs/recyclingbin.svg';
|
|||||||
import Folder from "@/components/Folder/Folder.vue";
|
import Folder from "@/components/Folder/Folder.vue";
|
||||||
import ai from '@/assets/svgs/ai.svg';
|
import ai from '@/assets/svgs/ai.svg';
|
||||||
import share from '@/assets/svgs/share.svg';
|
import share from '@/assets/svgs/share.svg';
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -131,5 +131,9 @@ export default {
|
|||||||
recyclingBin: 'Recycling Bin',
|
recyclingBin: 'Recycling Bin',
|
||||||
upscale: 'Image Inpainting',
|
upscale: 'Image Inpainting',
|
||||||
share: 'Quickly Share'
|
share: 'Quickly Share'
|
||||||
|
},
|
||||||
|
upload:{
|
||||||
|
uploadSuccess: 'upload success!',
|
||||||
|
uploadError: 'upload failed!',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -118,6 +118,10 @@ export default {
|
|||||||
recyclingBin: '回收站',
|
recyclingBin: '回收站',
|
||||||
upscale: '图像修复',
|
upscale: '图像修复',
|
||||||
share: '快传'
|
share: '快传'
|
||||||
|
},
|
||||||
|
upload:{
|
||||||
|
uploadSuccess: '上传成功!',
|
||||||
|
uploadError: '上传失败!',
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -21,16 +21,16 @@ export default [
|
|||||||
name: 'upscale',
|
name: 'upscale',
|
||||||
component: () => import('@/views/Upscale/index.vue'),
|
component: () => import('@/views/Upscale/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: true,
|
||||||
title: '图像修复'
|
title: '图像修复'
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/main/photo/share',
|
path: '/main/photo/share',
|
||||||
name: 'share',
|
name: 'share',
|
||||||
component: () => import('@/views/ImageShare/ImageShare.vue'),
|
component: () => import('@/views/ImageShare/ImageShare.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: true,
|
||||||
title: '快传'
|
title: '快传'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/router/modules/phone_upload.ts
Normal file
11
src/router/modules/phone_upload.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: '/upscale/app',
|
||||||
|
name: 'upscaleApp',
|
||||||
|
component: () => import('@/views/PhoneUpload/PhoneUpload.vue'),
|
||||||
|
meta: {
|
||||||
|
requiresAuth: false,
|
||||||
|
title: '手机上传'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
@@ -7,12 +7,14 @@ import notFound from "./modules/not_found.ts";
|
|||||||
import landing from "./modules/landing.ts";
|
import landing from "./modules/landing.ts";
|
||||||
import mainRouter from "./modules/main_router.ts";
|
import mainRouter from "./modules/main_router.ts";
|
||||||
import i18n from "@/locales";
|
import i18n from "@/locales";
|
||||||
|
import phone_upload from "@/router/modules/phone_upload.ts";
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
...login,
|
...login,
|
||||||
...notFound,
|
...notFound,
|
||||||
...landing,
|
...landing,
|
||||||
...mainRouter,
|
...mainRouter,
|
||||||
|
...phone_upload,
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)',
|
path: '/:pathMatch(.*)',
|
||||||
redirect: '/404',
|
redirect: '/404',
|
||||||
|
@@ -54,13 +54,7 @@ export const useUpscaleStore = defineStore(
|
|||||||
fileData.value = urlData;
|
fileData.value = urlData;
|
||||||
await loadImg(image);
|
await loadImg(image);
|
||||||
uploading.value = false;
|
uploading.value = false;
|
||||||
|
await clear();
|
||||||
imageData.value = "";
|
|
||||||
processedImg.value = "";
|
|
||||||
isDone.value = false;
|
|
||||||
msg.value = "";
|
|
||||||
progressBar.value = 0;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +62,21 @@ export const useUpscaleStore = defineStore(
|
|||||||
* 自定义上传图片请求
|
* 自定义上传图片请求
|
||||||
*/
|
*/
|
||||||
async function customUploadRequest(_file: any) {
|
async function customUploadRequest(_file: any) {
|
||||||
|
|
||||||
imageData.value = fileData.value;
|
imageData.value = fileData.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空数据
|
||||||
|
*/
|
||||||
|
async function clear() {
|
||||||
|
imageData.value = "";
|
||||||
|
processedImg.value = "";
|
||||||
|
isDone.value = false;
|
||||||
|
msg.value = "";
|
||||||
|
progressBar.value = 0;
|
||||||
|
isProcessing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载图片
|
* 加载图片
|
||||||
* @param img
|
* @param img
|
||||||
@@ -117,6 +122,7 @@ export const useUpscaleStore = defineStore(
|
|||||||
isProcessing,
|
isProcessing,
|
||||||
msg,
|
msg,
|
||||||
progressBar,
|
progressBar,
|
||||||
|
loadImg,
|
||||||
beforeUpload,
|
beforeUpload,
|
||||||
customUploadRequest,
|
customUploadRequest,
|
||||||
};
|
};
|
||||||
|
@@ -10,6 +10,9 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
|||||||
wsService: null as WebSocketService | null,
|
wsService: null as WebSocketService | null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const readyState = ref<number>(WebSocket.CLOSED);
|
||||||
|
|
||||||
|
|
||||||
function initialize(options: {
|
function initialize(options: {
|
||||||
url: string;
|
url: string;
|
||||||
protocols?: string | string[];
|
protocols?: string | string[];
|
||||||
@@ -17,6 +20,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
|||||||
}) {
|
}) {
|
||||||
state.wsService = new WebSocketService(options);
|
state.wsService = new WebSocketService(options);
|
||||||
state.wsService?.open();
|
state.wsService?.open();
|
||||||
|
readyState.value = WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessage(data: any) {
|
function sendMessage(data: any) {
|
||||||
@@ -33,12 +37,9 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
|||||||
|
|
||||||
function close(isActiveClose: boolean) {
|
function close(isActiveClose: boolean) {
|
||||||
state.wsService?.close(isActiveClose);
|
state.wsService?.close(isActiveClose);
|
||||||
|
readyState.value = WebSocket.CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增的获取 WebSocket 状态的方法
|
|
||||||
function getReadyState() {
|
|
||||||
return state.wsService ? state.wsService.getReadyState() : WebSocket.CLOSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialize,
|
initialize,
|
||||||
@@ -46,7 +47,7 @@ export const useWebSocketStore = defineStore('websocket', () => {
|
|||||||
on,
|
on,
|
||||||
onEvent,
|
onEvent,
|
||||||
close,
|
close,
|
||||||
getReadyState
|
readyState,
|
||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
persistedState: {
|
persistedState: {
|
||||||
|
15
src/types/upscale.d.ts
vendored
15
src/types/upscale.d.ts
vendored
@@ -1,12 +1,5 @@
|
|||||||
export interface ImageData {
|
export interface uploadImageRequest {
|
||||||
model_type: string;
|
image: string;
|
||||||
model: string;
|
access_token: string;
|
||||||
factor: number;
|
user_id: string;
|
||||||
tile_size: number;
|
|
||||||
backend: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
input: any;
|
|
||||||
hasAlpha: boolean;
|
|
||||||
min_lap: number;
|
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@ export const localforageStorageAdapter = {
|
|||||||
await localforage.setItem(key, value);
|
await localforage.setItem(key, value);
|
||||||
},
|
},
|
||||||
async get(key: string) {
|
async get(key: string) {
|
||||||
const res: any = await localforage.getItem(key);
|
return await localforage.getItem(key);
|
||||||
return res ? JSON.parse(res) : null;
|
|
||||||
},
|
},
|
||||||
async remove(key: any) {
|
async remove(key: any) {
|
||||||
await localforage.removeItem(key);
|
await localforage.removeItem(key);
|
||||||
|
22
src/utils/imageUtils/blobToBase64.ts
Normal file
22
src/utils/imageUtils/blobToBase64.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export async function blobToBase64(blobUrl: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(blobUrl);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
reader.onload = function () {
|
||||||
|
// 直接使用 reader.result,包含 MIME 类型前缀
|
||||||
|
const base64StringWithPrefix = reader.result!.toString();
|
||||||
|
resolve(base64StringWithPrefix);
|
||||||
|
};
|
||||||
|
reader.onerror = function () {
|
||||||
|
reject("File could not be read");
|
||||||
|
};
|
||||||
|
// 读取 Blob 文件到 Data URL 格式
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Error fetching blob from URL: " + error);
|
||||||
|
}
|
||||||
|
}
|
146
src/views/PhoneUpload/PhoneUpload.vue
Normal file
146
src/views/PhoneUpload/PhoneUpload.vue
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<div class="upscale-upload-content">
|
||||||
|
<AUploadDragger
|
||||||
|
name="image"
|
||||||
|
accept="image/*"
|
||||||
|
:multiple="false"
|
||||||
|
:directory="false"
|
||||||
|
:maxCount="1"
|
||||||
|
:beforeUpload="upscale.beforeUpload"
|
||||||
|
:custom-request="upscale.customUploadRequest"
|
||||||
|
:disabled="upscale.uploading"
|
||||||
|
:showUploadList="false">
|
||||||
|
<div class="upscale-upload-content-main">
|
||||||
|
<ABadge :offset="[-10, 10]">
|
||||||
|
<template #count>
|
||||||
|
<AAvatar :size="25" :src="sueccess" v-if="upscale.imageData"/>
|
||||||
|
<AAvatar :size="25" :src="warn" v-if="!upscale.imageData"/>
|
||||||
|
</template>
|
||||||
|
<AAvatar shape="square" :size="70" :src="file"/>
|
||||||
|
</ABadge>
|
||||||
|
<span class="upscale-upload-text">
|
||||||
|
点击上传图片
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AUploadDragger>
|
||||||
|
<ADivider orientation="center" :plain="true">
|
||||||
|
<span class="upscale-divider-title">图片预览</span>
|
||||||
|
</ADivider>
|
||||||
|
<div class="upscale-upload-image" v-if="upscale.imageData">
|
||||||
|
<ABadge>
|
||||||
|
<template #count>
|
||||||
|
<AButton type="text" size="small" class="upscale-file-btn" @click="upscale.imageData = ''">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar shape="square" :size="25" :src="remove"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</template>
|
||||||
|
<AAvatar shape="square" :size="100">
|
||||||
|
<template #icon>
|
||||||
|
<AImage :src="upscale.imageData" width="100%" height="100%"/>
|
||||||
|
</template>
|
||||||
|
</AAvatar>
|
||||||
|
</ABadge>
|
||||||
|
</div>
|
||||||
|
<AEmpty v-else :image="empty" :description="null"/>
|
||||||
|
<ADivider/>
|
||||||
|
<div>
|
||||||
|
<AButton type="primary" size="large" :disabled="!upscale.imageData" :loading="upscale.uploading"
|
||||||
|
@click="sendImage()"
|
||||||
|
class="upscale-upload-btn">
|
||||||
|
发送图片
|
||||||
|
</AButton>
|
||||||
|
</div>
|
||||||
|
<ADivider/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import file from "@/assets/svgs/file.svg";
|
||||||
|
import useStore from "@/store";
|
||||||
|
import sueccess from '@/assets/svgs/success.svg';
|
||||||
|
import warn from '@/assets/svgs/warn.svg';
|
||||||
|
import remove from '@/assets/svgs/remove.svg';
|
||||||
|
import empty from '@/assets/svgs/empty.svg';
|
||||||
|
import {blobToBase64} from "@/utils/imageUtils/blobToBase64.ts";
|
||||||
|
import {uploadImage} from "@/api/upscale";
|
||||||
|
import {uploadImageRequest} from "@/types/upscale";
|
||||||
|
import {message} from "ant-design-vue";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
|
const upscale = useStore().upscale;
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
async function sendImage() {
|
||||||
|
if (!upscale.imageData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const base64 = await blobToBase64(upscale.imageData);
|
||||||
|
const data: uploadImageRequest = {
|
||||||
|
image: base64,
|
||||||
|
access_token: route.query.token as string,
|
||||||
|
user_id: route.query.user_id as string,
|
||||||
|
};
|
||||||
|
const res: any = await uploadImage(data);
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
message.success(t('upload.uploadSuccess'));
|
||||||
|
} else {
|
||||||
|
message.error(res.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.upscale-upload-content {
|
||||||
|
width: 90%;
|
||||||
|
height: 40vh;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.upscale-upload-content-main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
|
||||||
|
.upscale-upload-text {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-upload-btn {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-upload-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(126, 126, 135, 0.99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-divider-title {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(126, 126, 135, 0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-upload-image {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upscale-upload-btn {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<CommentReply/>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
</script>
|
</script>
|
||||||
|
@@ -11,20 +11,24 @@
|
|||||||
@touchmove="touchMove"
|
@touchmove="touchMove"
|
||||||
@touchend="touchEnd"
|
@touchend="touchEnd"
|
||||||
>
|
>
|
||||||
<div v-if="store.isProcessing" class="canvas-progressbar">
|
<!-- 进度条 -->
|
||||||
|
<div class="canvas-progressbar">
|
||||||
<span class="canvas-progressbar-text">
|
<span class="canvas-progressbar-text">
|
||||||
{{ store.msg }}
|
{{ store.msg }}
|
||||||
</span>
|
</span>
|
||||||
<AProgress
|
<AProgress
|
||||||
|
v-if="store.isProcessing"
|
||||||
:stroke-color="{
|
:stroke-color="{
|
||||||
'0%': '#108ee9',
|
'0%': '#108ee9',
|
||||||
'100%': '#87d068',
|
'100%': '#87d068',}"
|
||||||
}"
|
|
||||||
:percent="store.progressBar"
|
:percent="store.progressBar"
|
||||||
:showInfo="false"
|
:showInfo="false"
|
||||||
|
status="active"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<canvas ref="canvas" v-if="store.imageData || store.processedImg"></canvas>
|
<!-- 图片 -->
|
||||||
|
<canvas ref="canvas"></canvas>
|
||||||
|
<!-- 拖动条 -->
|
||||||
<div
|
<div
|
||||||
class="dragLine"
|
class="dragLine"
|
||||||
v-if="store.isDone"
|
v-if="store.isDone"
|
||||||
@@ -40,20 +44,58 @@
|
|||||||
</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"
|
<div class="canvas-qr">
|
||||||
:value="'https://git.landaiqing.cn'"
|
<AQrcode :bordered="false" color="rgba(126, 126, 135, 0.48)"
|
||||||
|
:size="qrcodeSize"
|
||||||
|
:value="generateQrCodeUrl()"
|
||||||
:icon="phone"
|
:icon="phone"
|
||||||
:iconSize="40"
|
:iconSize="iconSize"
|
||||||
/>
|
/>
|
||||||
<span class="canvas-qr-text">手机扫码上传</span>
|
<span class="canvas-qr-text">手机扫码上传</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 菜单 -->
|
||||||
|
<div class="floating-menu" @mousedown.stop v-if="store.isDone && store.processedImg">
|
||||||
|
<AFlex :vertical="false" align="center" justify="space-between" :gap="12">
|
||||||
|
<ATooltip placement="top" title="下载图片">
|
||||||
|
<AButton type="text" size="large" @click="downloadImage" class="menu-btn">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar :src="download" class="menu-icon"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</ATooltip>
|
||||||
|
<ATooltip placement="top" title="分享图片">
|
||||||
|
<AButton type="text" size="large" class="menu-btn">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar :src="share" :size="28" class="menu-icon"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</ATooltip>
|
||||||
|
<ATooltip placement="top" title="保存图片">
|
||||||
|
<AButton type="text" size="large" class="menu-btn">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar :src="save" :size="30" class="menu-icon"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</ATooltip>
|
||||||
|
<ATooltip placement="top" title="删除图片">
|
||||||
|
<AButton type="text" size="large" danger class="menu-btn" @click="deletedImage">
|
||||||
|
<template #icon>
|
||||||
|
<AAvatar :src="deleted" :size="28" class="menu-icon"/>
|
||||||
|
</template>
|
||||||
|
</AButton>
|
||||||
|
</ATooltip>
|
||||||
|
</AFlex>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
import phone from '@/assets/svgs/qr-phone.svg';
|
import phone from '@/assets/svgs/qr-phone.svg';
|
||||||
|
import download from '@/assets/svgs/download.svg';
|
||||||
|
import share from '@/assets/svgs/share.svg';
|
||||||
|
import save from '@/assets/svgs/save.svg';
|
||||||
|
import deleted from '@/assets/svgs/deleted.svg';
|
||||||
|
|
||||||
const canvasContainer = ref<HTMLDivElement | null>(null);
|
const canvasContainer = ref<HTMLDivElement | null>(null);
|
||||||
const dragging = ref<boolean>(false);
|
const dragging = ref<boolean>(false);
|
||||||
@@ -76,9 +118,68 @@ const touchStartDistance = ref(0);
|
|||||||
const imgScaleStart = ref(1);
|
const imgScaleStart = ref(1);
|
||||||
|
|
||||||
const store = useStore().upscale;
|
const store = useStore().upscale;
|
||||||
|
const user = useStore().user;
|
||||||
const img = ref<HTMLImageElement>(new Image());
|
const img = ref<HTMLImageElement>(new Image());
|
||||||
const processedImg = ref<HTMLImageElement>(new Image());
|
const processedImg = ref<HTMLImageElement>(new Image());
|
||||||
|
|
||||||
|
const qrcodeSize = ref<number>(250);
|
||||||
|
const iconSize = ref<number>(30);
|
||||||
|
/**
|
||||||
|
* 更新二维码大小
|
||||||
|
*/
|
||||||
|
const updateQrcodeSize = () => {
|
||||||
|
if (canvasContainer.value) {
|
||||||
|
// 设置 QRCode 大小
|
||||||
|
const containerWidth = canvasContainer.value.clientWidth;
|
||||||
|
qrcodeSize.value = containerWidth * 0.3; // 设置 QRCode 为父盒子宽度的80%
|
||||||
|
iconSize.value = Math.min(containerWidth * 0.1, 40); // 设置 icon 大小为父盒子宽度的10%
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function generateQrCodeUrl(): string {
|
||||||
|
return import.meta.env.VITE_APP_WEB_URL + "/upscale/app?user_id=" + user.user.uid + "&token=" + user.user.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(generateQrCodeUrl());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载图片
|
||||||
|
*/
|
||||||
|
function downloadImage() {
|
||||||
|
if (!store.processedImg) return;
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = store.processedImg;
|
||||||
|
if (store.hasAlpha) a.download = "output.png";
|
||||||
|
else a.download = "output.jpg";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除图片
|
||||||
|
*/
|
||||||
|
function deletedImage() {
|
||||||
|
if (store.processedImg && store.imageData) {
|
||||||
|
store.imageData = '';
|
||||||
|
store.processedImg = '';
|
||||||
|
}
|
||||||
|
store.isDone = false;
|
||||||
|
store.isProcessing = false;
|
||||||
|
store.progressBar = 0;
|
||||||
|
store.msg = "";
|
||||||
|
draggingLine.value = false;
|
||||||
|
dragging.value = false;
|
||||||
|
imgX.value = 0;
|
||||||
|
imgY.value = 0;
|
||||||
|
imgScale.value = 1;
|
||||||
|
imgInitScale.value = 1;
|
||||||
|
img.value = new Image();
|
||||||
|
processedImg.value = new Image();
|
||||||
|
initCanvasSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始拖动
|
* 开始拖动
|
||||||
* @param event
|
* @param event
|
||||||
@@ -409,6 +510,7 @@ function updateCanvasSize() {
|
|||||||
*/
|
*/
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
updateCanvasSize();
|
updateCanvasSize();
|
||||||
|
updateQrcodeSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -464,6 +566,7 @@ onBeforeUnmount(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg {
|
.bg {
|
||||||
@@ -536,13 +639,19 @@ onBeforeUnmount(() => {
|
|||||||
canvas {
|
canvas {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-qr {
|
.canvas-qr {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-qr-text {
|
.canvas-qr-text {
|
||||||
@@ -563,6 +672,7 @@ canvas {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dragLine:hover {
|
.dragLine:hover {
|
||||||
@@ -588,17 +698,11 @@ canvas {
|
|||||||
.canvas-progressbar {
|
.canvas-progressbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
//left: 50%;
|
|
||||||
//transform: translate(-50%, -50%);
|
|
||||||
width: 300px;
|
width: 300px;
|
||||||
//height: 100px;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
//background-color: rgba(255, 255, 255, 0.5);
|
|
||||||
//padding: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-progressbar-text {
|
.canvas-progressbar-text {
|
||||||
@@ -606,4 +710,41 @@ canvas {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-menu {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
opacity: 0.8;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: white;
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0%);
|
||||||
|
z-index: 4;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn {
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -52,6 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ADivider></ADivider>
|
<ADivider></ADivider>
|
||||||
<AButton style="width: 100%;" size="large" shape="default" type="default" :loading="upscale.isProcessing"
|
<AButton style="width: 100%;" size="large" shape="default" type="default" :loading="upscale.isProcessing"
|
||||||
|
:disabled="!upscale.input"
|
||||||
@click="startTask">
|
@click="startTask">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AAvatar shape="square" :size="25" :src="run"/>
|
<AAvatar shape="square" :size="25" :src="run"/>
|
||||||
@@ -142,7 +143,6 @@ const imgCanvas = document.createElement("canvas");
|
|||||||
* WebWorker 处理图片
|
* WebWorker 处理图片
|
||||||
*/
|
*/
|
||||||
async function startTask() {
|
async function startTask() {
|
||||||
console.log(upscale.input);
|
|
||||||
if (upscale.input === null) return;
|
if (upscale.input === null) return;
|
||||||
upscale.isProcessing = true;
|
upscale.isProcessing = true;
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@@ -226,7 +226,7 @@ async function startTask() {
|
|||||||
outputData.value = null;
|
outputData.value = null;
|
||||||
imgCtx.putImageData(outImg, 0, 0);
|
imgCtx.putImageData(outImg, 0, 0);
|
||||||
let type = "image/jpeg";
|
let type = "image/jpeg";
|
||||||
const quality = 0.92;
|
const quality = 1.0;
|
||||||
if (upscale.hasAlpha) type = "image/png";
|
if (upscale.hasAlpha) type = "image/png";
|
||||||
|
|
||||||
imgCanvas.toBlob(
|
imgCanvas.toBlob(
|
||||||
|
@@ -39,7 +39,7 @@ import warn from '@/assets/svgs/warn.svg';
|
|||||||
const upscale = useStore().upscale;
|
const upscale = useStore().upscale;
|
||||||
|
|
||||||
const uploadDraggerRef = ref<HTMLDivElement | null>(null);
|
const uploadDraggerRef = ref<HTMLDivElement | null>(null);
|
||||||
const containerRef = ref<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -19,9 +19,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import UploadImage from "@/views/Upscale/UploadImage.vue";
|
|
||||||
import CompareImage from "@/views/Upscale/CompareImage.vue";
|
import useStore from "@/store";
|
||||||
import ParameterSetting from "@/views/Upscale/ParameterSetting.vue";
|
|
||||||
|
const websocket = useStore().websocket;
|
||||||
|
const user = useStore().user;
|
||||||
|
const upscale = useStore().upscale;
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
const wsOptions = {
|
||||||
|
url: import.meta.env.VITE_FILE_SOCKET_URL + "?user_id=" + user.user.uid,
|
||||||
|
protocols: [user.user.access_token],
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
websocket.initialize(wsOptions);
|
||||||
|
websocket.on("message", async (res: any) => {
|
||||||
|
if (res && res.code === 200) {
|
||||||
|
const {data} = res;
|
||||||
|
img.src = data;
|
||||||
|
await upscale.loadImg(img);
|
||||||
|
upscale.imageData = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
websocket.close(false);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.upscale-container {
|
.upscale-container {
|
||||||
@@ -30,13 +55,6 @@ import ParameterSetting from "@/views/Upscale/ParameterSetting.vue";
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
|
||||||
//.upscale-title {
|
|
||||||
// font-size: 16px;
|
|
||||||
// font-weight: bold;
|
|
||||||
// margin-left: 5px;
|
|
||||||
//}
|
|
||||||
|
|
||||||
.upscale-content {
|
.upscale-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
3
src/vite-env.d.ts
vendored
3
src/vite-env.d.ts
vendored
@@ -1,7 +1,6 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
declare interface ImportMetaEnv {
|
declare interface ImportMetaEnv {
|
||||||
readonly VITE_APP_BASE_API: string;
|
readonly VITE_APP_BASE_API: string;
|
||||||
readonly VITE_APP_TITLE: string;
|
|
||||||
readonly VITE_API_BASE_URL: string;
|
readonly VITE_API_BASE_URL: string;
|
||||||
readonly VITE_NODE_ENV: string;
|
readonly VITE_NODE_ENV: string;
|
||||||
readonly VITE_TITLE_NAME: string;
|
readonly VITE_TITLE_NAME: string;
|
||||||
@@ -9,6 +8,8 @@ declare interface ImportMetaEnv {
|
|||||||
readonly VITE_QR_SOCKET_URL: string;
|
readonly VITE_QR_SOCKET_URL: string;
|
||||||
readonly VITE_MESSAGE_SOCKET_URL: string;
|
readonly VITE_MESSAGE_SOCKET_URL: string;
|
||||||
readonly VITE_FINGERPRINT_KEY: string;
|
readonly VITE_FINGERPRINT_KEY: string;
|
||||||
|
readonly VITE_FILE_SOCKET_URL: string;
|
||||||
|
readonly VITE_APP_WEB_URL: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
@@ -24,7 +24,6 @@ self.onmessage = async function (e: MessageEvent): Promise<void> {
|
|||||||
let Model: tf.GraphModel;
|
let Model: tf.GraphModel;
|
||||||
try {
|
try {
|
||||||
Model = await tf.loadGraphModel(`indexeddb://${model_name}`);
|
Model = await tf.loadGraphModel(`indexeddb://${model_name}`);
|
||||||
console.log("Model loaded successfully");
|
|
||||||
self.postMessage({info: "Model loaded from cache successfully"});
|
self.postMessage({info: "Model loaded from cache successfully"});
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
self.postMessage({info: "Downloading model..."});
|
self.postMessage({info: "Downloading model..."});
|
||||||
@@ -221,7 +220,6 @@ self.onmessage = async function (e: MessageEvent): Promise<void> {
|
|||||||
const factor = data?.factor || 4;
|
const factor = data?.factor || 4;
|
||||||
const tile_size = data?.tile_size || 64;
|
const tile_size = data?.tile_size || 64;
|
||||||
const min_lap = data?.min_lap || 12;
|
const min_lap = data?.min_lap || 12;
|
||||||
const start = Date.now();
|
|
||||||
let output: any;
|
let output: any;
|
||||||
try {
|
try {
|
||||||
output = await enlargeImageWithFixedInput(
|
output = await enlargeImageWithFixedInput(
|
||||||
@@ -237,8 +235,6 @@ self.onmessage = async function (e: MessageEvent): Promise<void> {
|
|||||||
if (withPadding) {
|
if (withPadding) {
|
||||||
output.cropToOriginalSize(width_ori * factor, height_ori * factor);
|
output.cropToOriginalSize(width_ori * factor, height_ori * factor);
|
||||||
}
|
}
|
||||||
const end = Date.now();
|
|
||||||
console.log("Time:", end - start);
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
progress: 100,
|
progress: 100,
|
||||||
|
Reference in New Issue
Block a user