🚧 adjust page route

This commit is contained in:
2025-04-02 21:21:41 +08:00
parent c80fa21139
commit 7e98d69f75
26 changed files with 914 additions and 38 deletions

41
components.d.ts vendored
View File

@@ -16,13 +16,13 @@ declare module 'vue' {
ACard: typeof import('ant-design-vue/es')['Card']
ACascader: typeof import('ant-design-vue/es')['Cascader']
AccountSetting: typeof import('./src/views/User/AccountSetting/AccountSetting.vue')['default']
AccountSettingBackup: typeof import('./src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue')['default']
AccountSettingHome: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/AccountSettingHome.vue')['default']
AccountSettingInfo: typeof import('./src/views/User/AccountSetting/components/AccountSettingInfo/AccountSettingInfo.vue')['default']
AccountSettingLog: typeof import('./src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue')['default']
AccountSettingSidebar: typeof import('./src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue')['default']
AccountSettingStorage: typeof import('./src/views/User/AccountSetting/components/AccountSettingStorage/AccountSettingStorage.vue')['default']
AccountSettingTask: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue')['default']
AccountSettingBackup: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingBackup/AccountSettingBackup.vue')['default']
AccountSettingHome: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingHome/AccountSettingHome.vue')['default']
AccountSettingInfo: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingInfo/AccountSettingInfo.vue')['default']
AccountSettingLog: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingLog/AccountSettingLog.vue')['default']
AccountSettingSidebar: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingSidebar/AccountSettingSidebar.vue')['default']
AccountSettingStorage: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingStorage/AccountSettingStorage.vue')['default']
AccountSettingTask: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingTask/AccountSettingTask.vue')['default']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
ACol: typeof import('ant-design-vue/es')['Col']
@@ -68,6 +68,7 @@ declare module 'vue' {
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
AStatistic: typeof import('ant-design-vue/es')['Statistic']
@@ -108,13 +109,13 @@ declare module 'vue' {
CommonPhoneUpload: typeof import('./src/views/Phone/CommonPhoneUpload/CommonPhoneUpload.vue')['default']
CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default']
Dashboard: typeof import('./src/views/Admin/System/Pages/Dashboard.vue')['default']
DeleteConfirmModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/DeleteConfirmModal.vue')['default']
DeleteConfirmModal: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingTask/components/DeleteConfirmModal.vue')['default']
DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined']
DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined']
DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined']
DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default']
EditOutlined: typeof import('@ant-design/icons-vue')['EditOutlined']
EmailModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/EmailModal.vue')['default']
EmailModal: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingHome/EmailModal.vue')['default']
ExclamationCircleOutlined: typeof import('@ant-design/icons-vue')['ExclamationCircleOutlined']
EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined']
EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined']
@@ -127,6 +128,7 @@ declare module 'vue' {
HeatmapPro: typeof import('./src/components/HeatmapPro/HeatmapPro.vue')['default']
ImageBed: typeof import('./src/views/ImageBed/index.vue')['default']
ImageEnhancer: typeof import('./src/components/ImageEnhancer/ImageEnhancer.vue')['default']
ImageEnhancerModal: typeof import('./src/components/ImageEnhancer/ImageEnhancerModal.vue')['default']
ImageShare: typeof import('./src/views/Share/ImageShare/ImageShare.vue')['default']
ImageToolbar: typeof import('./src/components/ImageToolbar/ImageToolbar.vue')['default']
ImageUpload: typeof import('./src/components/ImageUpload/ImageUpload.vue')['default']
@@ -154,7 +156,7 @@ declare module 'vue' {
PageError404: typeof import('./src/views/Admin/Error/PageError404.vue')['default']
PageError500: typeof import('./src/views/Admin/Error/PageError500.vue')['default']
ParameterSetting: typeof import('./src/views/Upscale/ParameterSetting.vue')['default']
PasswordModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/PasswordModal.vue')['default']
PasswordModal: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingHome/PasswordModal.vue')['default']
PeopleAlbumDetail: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumDetail.vue')['default']
PeopleAlbumIndex: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumIndex.vue')['default']
PeopleAlbumList: typeof import('./src/views/Album/PeopleAlbum/PeopleAlbumList.vue')['default']
@@ -164,7 +166,7 @@ declare module 'vue' {
PhoalbumDetail: typeof import('./src/views/Album/Phoalbum/PhoalbumDetail.vue')['default']
PhoalbumIndex: typeof import('./src/views/Album/Phoalbum/PhoalbumIndex.vue')['default']
PhoalbumList: typeof import('./src/views/Album/Phoalbum/PhoalbumList.vue')['default']
PhoneModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/PhoneModal.vue')['default']
PhoneModal: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingHome/PhoneModal.vue')['default']
PhotoStack: typeof import('./src/components/PhotoStack/PhotoStack.vue')['default']
PlusOutlined: typeof import('@ant-design/icons-vue')['PlusOutlined']
PlusSquareOutlined: typeof import('@ant-design/icons-vue')['PlusSquareOutlined']
@@ -199,29 +201,30 @@ declare module 'vue' {
ShareViewList: typeof import('./src/views/Share/ShareViewList/index.vue')['default']
Spin: typeof import('./src/components/MyUI/Spin/Spin.vue')['default']
StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default']
StorageCard: typeof import('./src/views/User/AccountSetting/components/AccountSettingStorage/StorageCard.vue')['default']
StorageCard: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingStorage/StorageCard.vue')['default']
StorageManagement: typeof import('./src/views/Admin/System/Pages/StorageManagement.vue')['default']
SystemHeader: typeof import('./src/views/Admin/System/Components/SystemHeader.vue')['default']
SystemLogs: typeof import('./src/views/Admin/System/Pages/SystemLogs.vue')['default']
SystemSidebar: typeof import('./src/views/Admin/System/Components/SystemSidebar.vue')['default']
TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined']
TaskCard: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskCard.vue')['default']
TaskForm: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskForm.vue')['default']
TaskCard: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingTask/components/TaskCard.vue')['default']
TaskForm: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingTask/components/TaskForm.vue')['default']
TaskSchedule: typeof import('./src/components/TaskSchedule/TaskSchedule.vue')['default']
TaskTypeSelector: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskTypeSelector.vue')['default']
TaskTypeSelector: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingTask/components/TaskTypeSelector.vue')['default']
TestPage: typeof import('./src/views/Test/TestPage.vue')['default']
ThingAlbumDetail: typeof import('./src/views/Album/ThingAlbum/ThingAlbumDetail.vue')['default']
ThingAlbumIndex: typeof import('./src/views/Album/ThingAlbum/ThingAlbumIndex.vue')['default']
ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default']
ThirdPartyLoginModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/ThirdPartyLoginModal.vue')['default']
ThirdPartyLoginModal: typeof import('./src/views/User/AccountSetting/Pages/AccountSettingHome/ThirdPartyLoginModal.vue')['default']
Tooltip: typeof import('./src/components/MyUI/Tooltip/Tooltip.vue')['default']
UploadImage: typeof import('./src/views/Upscale/UploadImage.vue')['default']
UploadSetting: typeof import('./src/components/ImageUpload/UploadSetting.vue')['default']
Upscale: typeof import('./src/views/Upscale/index.vue')['default']
UpscalePhoneUpload: typeof import('./src/views/Phone/UpscalePhoneUpload/UpscalePhoneUpload.vue')['default']
UserAnalysis: typeof import('./src/views/Admin/System/Pages/UserAnalysis.vue')['default']
UserCenterDynamic: typeof import('./src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue')['default']
UserCenterHome: typeof import('./src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue')['default']
UserCenterSetting: typeof import('./src/views/User/PersonalCenter/components/UserCenterSetting/UserCenterSetting.vue')['default']
UserCenterDynamic: typeof import('./src/views/User/PersonalCenter/Pages/UserCenterDynamic/UserCenterDynamic.vue')['default']
UserCenterHome: typeof import('./src/views/User/PersonalCenter/Pages/UserCenterHome/UserCenterHome.vue')['default']
UserCenterSetting: typeof import('./src/views/User/PersonalCenter/Pages/UserCenterSetting/UserCenterSetting.vue')['default']
UserInfoCard: typeof import('./src/components/CommentReply/src/UserInfoCard/UserInfoCard.vue')['default']
UserList: typeof import('./src/views/Admin/System/Pages/UserList.vue')['default']
UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']

View File

@@ -34,7 +34,7 @@
"@types/file-saver": "^2.0.7",
"@types/json-stringify-safe": "^5.0.3",
"@types/leaflet": "^1.9.17",
"@types/node": "^22.13.14",
"@types/node": "^22.13.17",
"@types/nprogress": "^0.2.3",
"@vladmandic/face-api": "^1.7.15",
"@vuepic/vue-datepicker": "^11.0.2",
@@ -89,11 +89,11 @@
"@vitejs/plugin-vue": "^5.2.3",
"eslint-plugin-vue": "^10.0.0",
"globals": "^16.0.0",
"sass": "^1.86.0",
"sass": "^1.86.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.28.0",
"typescript-eslint": "^8.29.0",
"unplugin-vue-components": "^28.4.1",
"vite": "^6.2.3",
"vite": "^6.2.4",
"vite-plugin-bundle-obfuscator": "1.4.2",
"vite-plugin-chunk-split": "^0.5.0",
"vue-tsc": "2.2.8"

View File

@@ -0,0 +1,852 @@
<template>
<div class="enhancer-modal-container">
<AFlex class="enhancer-modal-content" :vertical="false" align="center" justify="space-between">
<!-- 左侧控制面板 -->
<div class="enhancer-modal-left">
<ACard class="enhancer-modal-left-container" :bordered="false">
<!-- 上传区域 -->
<div class="enhancer-modal-upload">
<Spin :spinning="enhancer.uploading" indicator="magic-ring">
<AUploadDragger
name="image"
accept="image/*"
:multiple="false"
:directory="false"
:maxCount="1"
:beforeUpload="enhancer.beforeUpload"
:custom-request="enhancer.customUploadRequest"
:disabled="enhancer.uploading || enhancer.isProcessing"
:showUploadList="false">
<div class="enhancer-modal-upload-content">
<ABadge :offset="[-10, 10]">
<template #count>
<AAvatar :size="22" :src="successIcon" v-if="enhancer.imageData"/>
<AAvatar :size="22" :src="warnIcon" v-if="!enhancer.imageData"/>
</template>
<AAvatar shape="square" :size="60" :src="fileIcon"/>
</ABadge>
<span class="enhancer-modal-upload-text">
点击或拖拽上传图片
</span>
</div>
</AUploadDragger>
</Spin>
</div>
<!-- 功能选择区 -->
<ADivider orientation="center" :plain="true">
<span class="enhancer-modal-divider-title">增强功能</span>
</ADivider>
<div class="enhancer-modal-function-selector">
<ARadioGroup v-model:value="enhancer.selectedFunction" button-style="solid" size="small"
style="width: 100%">
<ARadioButton value="upscale">图像升级</ARadioButton>
<ARadioButton value="deblur">去模糊</ARadioButton>
<ARadioButton value="denoise">去噪</ARadioButton>
<ARadioButton value="lowlight">弱光增强</ARadioButton>
</ARadioGroup>
</div>
<!-- 参数设置区 -->
<ADivider orientation="center" :plain="true">
<span class="enhancer-modal-divider-title">参数设置</span>
</ADivider>
<!-- 图像升级参数 -->
<div v-if="enhancer.selectedFunction === 'upscale'" class="enhancer-modal-params">
<div class="enhancer-modal-params-item">
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">模型:</span>
<ASelect style="width: 100%" size="middle"
v-model:value="enhancer.upscaleParams.model"
:options="enhancer.upscaleModels">
</ASelect>
</div>
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">比例:</span>
<ASelect style="width: 100%" size="middle"
v-model:value="enhancer.upscaleParams.scale"
:options="enhancer.upscaleScales">
</ASelect>
</div>
</div>
</div>
<!-- 去模糊参数 -->
<div v-if="enhancer.selectedFunction === 'deblur'" class="enhancer-modal-params">
<div class="enhancer-modal-params-item">
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">强度:</span>
<ASlider v-model:value="enhancer.deblurParams.strength" :min="0" :max="100" :step="1"/>
</div>
</div>
</div>
<!-- 去噪参数 -->
<div v-if="enhancer.selectedFunction === 'denoise'" class="enhancer-modal-params">
<div class="enhancer-modal-params-item">
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">强度:</span>
<ASlider v-model:value="enhancer.denoiseParams.strength" :min="0" :max="100" :step="1"/>
</div>
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">保留细节:</span>
<ASlider v-model:value="enhancer.denoiseParams.preserveDetail" :min="0" :max="100" :step="1"/>
</div>
</div>
</div>
<!-- 弱光增强参数 -->
<div v-if="enhancer.selectedFunction === 'lowlight'" class="enhancer-modal-params">
<div class="enhancer-modal-params-item">
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">亮度:</span>
<ASlider v-model:value="enhancer.lowlightParams.brightness" :min="0" :max="100" :step="1"/>
</div>
<div class="enhancer-modal-params-item-content">
<span class="enhancer-modal-params-title">对比度:</span>
<ASlider v-model:value="enhancer.lowlightParams.contrast" :min="0" :max="100" :step="1"/>
</div>
</div>
</div>
<!-- 处理按钮 -->
<ADivider></ADivider>
<AButton style="width: 100%;" size="middle" shape="default" type="primary" :loading="enhancer.isProcessing"
:disabled="!enhancer.imageData"
@click="startEnhance">
<template #icon>
<AAvatar shape="square" :size="20" :src="runIcon"/>
</template>
<span class="enhancer-modal-params-btn">开始处理</span>
</AButton>
</ACard>
</div>
<!-- 右侧图像预览区 -->
<div class="enhancer-modal-right">
<div
ref="canvasContainer"
class="canvas-container"
@mousedown="startDragging"
@mouseup="stopDragging"
@mouseleave="stopDragging"
@mousemove="dragImage"
@wheel="resizeImage"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
<!-- 进度条 -->
<div class="canvas-progressbar">
<span class="canvas-progressbar-text">
{{ enhancer.msg }}
</span>
<AProgress
v-if="enhancer.isProcessing"
:stroke-color="{
'0%': '#108ee9',
'100%': '#87d068',}"
:percent="enhancer.progressBar"
:showInfo="false"
status="active"
/>
</div>
<!-- 图片 -->
<canvas ref="canvas"></canvas>
<!-- 拖动条 -->
<div
class="dragLine"
v-if="enhancer.isDone"
ref="dragLine">
<div class="dragBall"
@mousedown.stop="startDraggingLine"
@mousemove.stop="dragLineFn"
@mouseup.stop="stopDraggingLine"
>
<svg width="24" viewBox="0 0 27 20">
<path fill="#ff3484" d="M9.6 0L0 9.6l9.6 9.6z"></path>
<path fill="#5fb3e5" d="M17 19.2l9.5-9.6L16.9 0z"></path>
</svg>
</div>
</div>
<!-- 菜单 -->
<div class="floating-menu" @mousedown.stop v-if="enhancer.isDone && enhancer.processedImg">
<AFlex :vertical="false" align="center" justify="space-between" :gap="8">
<ATooltip placement="top" title="下载图片">
<AButton type="text" size="small" @click="downloadImage" class="menu-btn">
<template #icon>
<AAvatar :src="downloadIcon" class="menu-icon" :size="20"/>
</template>
</AButton>
</ATooltip>
<ATooltip placement="top" title="保存图片">
<AButton type="text" size="small" class="menu-btn" @click="saveImage">
<template #icon>
<AAvatar :src="saveIcon" :size="20" class="menu-icon"/>
</template>
</AButton>
</ATooltip>
<ATooltip placement="top" title="删除图片">
<AButton type="text" size="small" danger class="menu-btn" @click="deleteImage">
<template #icon>
<AAvatar :src="deleteIcon" :size="20" class="menu-icon"/>
</template>
</AButton>
</ATooltip>
</AFlex>
</div>
<!-- 图片信息 -->
<div class="image-info">
<ATag color="cyan" :bordered="false" v-if="enhancer.imageData">原图: {{ originalImageSize }}</ATag>
<ATag color="purple" :bordered="false" v-if="enhancer.processedImg">处理后: {{ processedImageSize }}</ATag>
</div>
</div>
</div>
</AFlex>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, defineEmits } from 'vue';
import { message } from "ant-design-vue";
import Spin from "@/components/MyUI/Spin/Spin.vue";
import Upscaler from 'upscaler';
// 图标导入
import fileIcon from "@/assets/svgs/file.svg";
import successIcon from '@/assets/svgs/success.svg';
import warnIcon from '@/assets/svgs/warn.svg';
import runIcon from '@/assets/svgs/run.svg';
import downloadIcon from '@/assets/svgs/download.svg';
import saveIcon from '@/assets/svgs/save.svg';
import deleteIcon from '@/assets/svgs/deleted.svg';
// 定义事件
const emit = defineEmits(['save-image', 'close']);
// DOM引用
const canvasContainer = ref<HTMLDivElement | null>(null);
const canvas = ref<HTMLCanvasElement | null>(null);
const dragLine = ref<HTMLDivElement | null>(null);
// 图像处理实例
let upscaler: any = null;
// 状态管理
const enhancer = reactive({
// 基本状态
uploading: false,
isProcessing: false,
isDone: false,
msg: "",
progressBar: 0,
// 图像数据
imageData: "",
fileData: "",
processedImg: "",
input: null as any,
// 拖拽状态
dragging: false,
linePosition: 0,
draggingLine: false,
// 功能选择
selectedFunction: "upscale",
// 图像升级参数
upscaleParams: {
model: "x4",
scale: 4
},
upscaleModels: [
{label: "通用模型 x2", value: "x2"},
{label: "通用模型 x4", value: "x4"},
{label: "照片增强", value: "photo"},
{label: "动漫风格", value: "anime"}
],
upscaleScales: [
{label: "2x", value: 2},
{label: "4x", value: 4}
],
// 去模糊参数
deblurParams: {
strength: 50,
radius: 5
},
// 去噪参数
denoiseParams: {
strength: 50,
preserveDetail: 70
},
// 弱光增强参数
lowlightParams: {
brightness: 60,
contrast: 50
},
// 图片上传前的校验
async beforeUpload(file: File) {
enhancer.uploading = true;
const urlData = URL.createObjectURL(file);
const image = new Image();
image.src = urlData;
// 等待图片加载完成
await new Promise(resolve => {
image.onload = resolve;
});
await clear();
enhancer.fileData = urlData;
enhancer.uploading = false;
return true;
},
// 自定义上传图片请求
async customUploadRequest(_file: any) {
enhancer.imageData = enhancer.fileData;
await loadImage();
}
});
// 图片尺寸信息
const originalImageSize = computed(() => {
if (!enhancer.imageData) return "";
const img = new Image();
img.src = enhancer.imageData;
return `${img.width}x${img.height}`;
});
const processedImageSize = computed(() => {
if (!enhancer.processedImg) return "";
const img = new Image();
img.src = enhancer.processedImg;
return `${img.width}x${img.height}`;
});
// 清空数据
async function clear() {
enhancer.imageData = "";
enhancer.processedImg = "";
enhancer.isDone = false;
enhancer.msg = "";
enhancer.progressBar = 0;
enhancer.isProcessing = false;
enhancer.dragging = false;
enhancer.linePosition = 0;
enhancer.draggingLine = false;
enhancer.input = null;
}
// 加载图片
async function loadImage() {
if (!canvas.value || !enhancer.imageData) return;
const ctx = canvas.value.getContext('2d');
if (!ctx) return;
const img = new Image();
img.src = enhancer.imageData;
await new Promise(resolve => {
img.onload = resolve;
});
canvas.value.width = img.width;
canvas.value.height = img.height;
ctx.drawImage(img, 0, 0);
// 初始化拖动线位置
enhancer.linePosition = img.width / 2;
// 初始化Upscaler
if (!upscaler) {
upscaler = new Upscaler();
}
}
// 开始图像增强处理
async function startEnhance() {
if (!enhancer.imageData || !canvas.value) {
message.warning("请先上传图片");
return;
}
enhancer.isProcessing = true;
enhancer.msg = "正在处理图片...";
const start = Date.now();
try {
const img = new Image();
img.src = enhancer.imageData;
await new Promise(resolve => {
img.onload = resolve;
});
let processedImage;
// 根据选择的功能进行不同的处理
switch (enhancer.selectedFunction) {
case 'upscale':
enhancer.msg = "正在进行图像升级...";
processedImage = await upscaler.upscale(img, {
model: enhancer.upscaleParams.model,
scale: enhancer.upscaleParams.scale,
progress: (progress: number) => {
enhancer.progressBar = Math.round(progress * 100);
}
});
break;
case 'deblur':
enhancer.msg = "正在进行去模糊处理...";
// 使用upscaler的去模糊功能
processedImage = await simulateImageProcessing(img, 'deblur');
break;
case 'denoise':
enhancer.msg = "正在进行去噪处理...";
processedImage = await simulateImageProcessing(img, 'denoise');
break;
case 'lowlight':
enhancer.msg = "正在进行弱光增强...";
processedImage = await simulateImageProcessing(img, 'lowlight');
break;
}
if (processedImage) {
enhancer.processedImg = processedImage.src || processedImage;
enhancer.isDone = true;
enhancer.msg = `处理完成! 用时: ${((Date.now() - start) / 1000).toFixed(2)}`;
// 更新画布显示处理后的图片
updateCanvasWithProcessedImage();
}
} catch (error) {
console.error("图像处理失败", error);
message.error("图像处理失败,请重试");
enhancer.msg = "处理失败";
} finally {
enhancer.isProcessing = false;
}
}
// 模拟图像处理(在实际实现中应替换为真实的处理逻辑)
async function simulateImageProcessing(img: HTMLImageElement, _type: string) {
// 模拟进度
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 5;
enhancer.progressBar = Math.min(Math.round(progress), 99);
if (progress >= 100) {
clearInterval(interval);
}
}, 200);
// 这里应该是实际的图像处理逻辑
// 目前使用upscaler的基本功能模拟其他处理效果
try {
const result = await upscaler.upscale(img, {
model: 'x2',
scale: 1.5,
progress: (p: number) => {
enhancer.progressBar = Math.round(p * 100);
}
});
clearInterval(interval);
return result;
} catch (error) {
clearInterval(interval);
throw error;
}
}
// 更新画布显示处理后的图片
function updateCanvasWithProcessedImage() {
if (!canvas.value || !enhancer.processedImg) return;
const ctx = canvas.value.getContext('2d');
if (!ctx) return;
const img = new Image();
img.src = enhancer.processedImg;
img.onload = () => {
canvas.value!.width = img.width;
canvas.value!.height = img.height;
ctx.drawImage(img, 0, 0);
// 重置拖动线位置
enhancer.linePosition = img.width / 2;
// 绘制对比效果
drawComparisonView();
};
}
// 绘制对比视图
function drawComparisonView() {
if (!canvas.value || !enhancer.imageData || !enhancer.processedImg || !enhancer.isDone) return;
const ctx = canvas.value.getContext('2d');
if (!ctx) return;
const originalImg = new Image();
originalImg.src = enhancer.imageData;
const processedImg = new Image();
processedImg.src = enhancer.processedImg;
originalImg.onload = () => {
processedImg.onload = () => {
// 清除画布
ctx.clearRect(0, 0, canvas.value!.width, canvas.value!.height);
// 绘制处理后的图片
ctx.drawImage(processedImg, 0, 0, canvas.value!.width, canvas.value!.height);
// 绘制原图(左侧部分)
ctx.drawImage(
originalImg,
0, 0, originalImg.width, originalImg.height,
0, 0, enhancer.linePosition, canvas.value!.height
);
// 绘制分割线
ctx.beginPath();
ctx.moveTo(enhancer.linePosition, 0);
ctx.lineTo(enhancer.linePosition, canvas.value!.height);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.stroke();
};
};
}
// 拖动相关函数
function startDragging(_e: MouseEvent) {
enhancer.dragging = true;
}
function stopDragging() {
enhancer.dragging = false;
}
function dragImage(e: MouseEvent) {
if (!enhancer.dragging || !canvasContainer.value) return;
if (enhancer.isDone) {
// 更新拖动线位置
const rect = canvasContainer.value.getBoundingClientRect();
const x = e.clientX - rect.left;
enhancer.linePosition = Math.max(0, Math.min(canvas.value?.width || 0, x));
drawComparisonView();
}
}
function startDraggingLine(e: MouseEvent) {
e.preventDefault();
enhancer.draggingLine = true;
}
function stopDraggingLine() {
enhancer.draggingLine = false;
}
function dragLineFn(e: MouseEvent) {
if (!enhancer.draggingLine || !canvas.value || !canvasContainer.value) return;
const rect = canvasContainer.value.getBoundingClientRect();
const x = e.clientX - rect.left;
// 限制拖动范围在画布内
enhancer.linePosition = Math.max(0, Math.min(canvas.value.width, x));
// 重新绘制对比视图
drawComparisonView();
}
// 缩放相关函数
function resizeImage(e: WheelEvent) {
e.preventDefault();
// 实现简单的缩放功能
if (!canvas.value || !enhancer.isDone) return;
// 这里可以实现缩放逻辑
}
// 触摸事件处理
function touchStart(e: TouchEvent) {
if (e.touches.length === 1) {
enhancer.dragging = true;
}
}
function touchMove(e: TouchEvent) {
if (!enhancer.dragging || !canvasContainer.value || e.touches.length !== 1) return;
const touch = e.touches[0];
const rect = canvasContainer.value.getBoundingClientRect();
const x = touch.clientX - rect.left;
if (enhancer.isDone) {
// 更新拖动线位置
enhancer.linePosition = Math.max(0, Math.min(canvas.value?.width || 0, x));
drawComparisonView();
}
}
function touchEnd() {
enhancer.dragging = false;
}
// 下载图片
function downloadImage() {
if (!enhancer.processedImg) return;
const link = document.createElement('a');
link.href = enhancer.processedImg;
link.download = `enhanced_image_${Date.now()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
message.success('图片下载成功');
}
// 保存图片
function saveImage() {
if (!enhancer.processedImg) return;
emit('save-image', enhancer.processedImg);
message.success('图片已保存');
}
// 删除图片
function deleteImage() {
clear();
message.success('图片已删除');
}
// 组件挂载和卸载
onMounted(() => {
// 初始化Upscaler
upscaler = new Upscaler();
});
onUnmounted(() => {
// 清理资源
if (enhancer.processedImg) {
URL.revokeObjectURL(enhancer.processedImg);
}
if (enhancer.imageData) {
URL.revokeObjectURL(enhancer.imageData);
}
});
</script>
<style scoped lang="scss">
.enhancer-modal-container {
width: 100%;
height: 100%;
padding: 10px;
.enhancer-modal-content {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
gap: 15px;
.enhancer-modal-left {
width: 35%;
height: 100%;
.enhancer-modal-left-container {
width: 100%;
height: 100%;
overflow: auto;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
padding: 15px;
.enhancer-modal-divider-title {
font-size: 13px;
color: rgba(80, 80, 90, 0.9);
font-weight: 500;
}
}
}
.enhancer-modal-right {
width: 65%;
height: 100%;
border-radius: 8px;
overflow: hidden;
background-color: rgba(245, 245, 245, 0.5);
backdrop-filter: blur(5px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
}
}
.enhancer-modal-upload {
width: 100%;
margin-bottom: 15px;
.enhancer-modal-upload-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 0;
gap: 10px;
.enhancer-modal-upload-text {
font-size: 14px;
color: rgba(80, 80, 90, 0.8);
margin-top: 8px;
}
}
}
.enhancer-modal-function-selector {
margin-bottom: 15px;
}
.enhancer-modal-params {
margin-bottom: 15px;
.enhancer-modal-params-item {
display: flex;
flex-direction: column;
gap: 12px;
.enhancer-modal-params-item-content {
display: flex;
flex-direction: column;
gap: 5px;
.enhancer-modal-params-title {
font-size: 13px;
color: rgba(80, 80, 90, 0.9);
margin-bottom: 2px;
}
}
}
}
.enhancer-modal-params-btn {
margin-left: 5px;
}
.canvas-container {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.02);
border-radius: 8px;
canvas {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.canvas-progressbar {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
z-index: 10;
background-color: rgba(255, 255, 255, 0.7);
padding: 8px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.canvas-progressbar-text {
font-size: 13px;
color: rgba(80, 80, 90, 0.9);
margin-bottom: 5px;
display: block;
}
}
.dragLine {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
background-color: #ffffff;
z-index: 5;
.dragBall {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: ew-resize;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 50%;
padding: 2px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}
}
.floating-menu {
position: absolute;
bottom: 15px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(5px);
border-radius: 8px;
padding: 5px 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 10;
.menu-btn {
margin: 0 2px;
}
.menu-icon {
opacity: 0.8;
transition: all 0.2s;
&:hover {
opacity: 1;
}
}
}
.image-info {
position: absolute;
top: 10px;
right: 10px;
display: flex;
flex-direction: column;
gap: 5px;
z-index: 10;
}
}
</style>

View File

@@ -1,3 +0,0 @@
import ImageEnhancer from './ImageEnhancer.vue';
export default ImageEnhancer;

View File

@@ -0,0 +1,11 @@
export default [
{
path: '/test',
name: 'testPage',
component: () => import('@/views/Test/TestPage.vue'),
meta: {
requiresAuth: false,
title: 'testPage',
}
},
];

View File

@@ -1,13 +1,13 @@
import UserCenterHome from "@/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue";
import UserCenterDynamic from "@/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue";
import UserCenterSetting from "@/views/User/PersonalCenter/components/UserCenterSetting/UserCenterSetting.vue";
import UserCenterHome from "@/views/User/PersonalCenter/Pages/UserCenterHome/UserCenterHome.vue";
import UserCenterDynamic from "@/views/User/PersonalCenter/Pages/UserCenterDynamic/UserCenterDynamic.vue";
import UserCenterSetting from "@/views/User/PersonalCenter/Pages/UserCenterSetting/UserCenterSetting.vue";
import AccountSettingHome from "@/views/User/AccountSetting/components/AccountSettingHome/AccountSettingHome.vue";
import AccountSettingInfo from "@/views/User/AccountSetting/components/AccountSettingInfo/AccountSettingInfo.vue";
import AccountSettingHome from "@/views/User/AccountSetting/Pages/AccountSettingHome/AccountSettingHome.vue";
import AccountSettingInfo from "@/views/User/AccountSetting/Pages/AccountSettingInfo/AccountSettingInfo.vue";
import AccountSettingStorage
from "@/views/User/AccountSetting/components/AccountSettingStorage/AccountSettingStorage.vue";
from "@/views/User/AccountSetting/Pages/AccountSettingStorage/AccountSettingStorage.vue";
import AccountSettingBackup
from "@/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue";
from '@/views/User/AccountSetting/Pages/AccountSettingBackup/AccountSettingBackup.vue';
export default [
{
@@ -99,7 +99,7 @@ export default [
{
path: '/main/user/setting/task',
name: 'AccountSettingTask',
component: () => import('@/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue'),
component: () => import('@/views/User/AccountSetting/Pages/AccountSettingTask/AccountSettingTask.vue'),
meta: {
requiresAuth: true,
title: '定时任务'
@@ -108,7 +108,7 @@ export default [
{
path: '/main/user/setting/log',
name: 'AccountSettingLog',
component: () => import('@/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue'),
component: () => import('@/views/User/AccountSetting/Pages/AccountSettingLog/AccountSettingLog.vue'),
meta: {
requiresAuth: true,
title: '执行记录'

View File

@@ -11,7 +11,7 @@ import phone_upload from "@/router/modules/phone_upload.ts";
import user from "@/router/modules/user.ts";
import system from "@/router/modules/system.ts";
import preview from "@/router/modules/preview.ts";
import test from "@/router/modules/test.ts";
const routes: Array<RouteRecordRaw> = [
...login,
...notFound,
@@ -21,6 +21,7 @@ const routes: Array<RouteRecordRaw> = [
...user,
...system,
...preview,
...test,
{
path: '/:pathMatch(.*)',
redirect: '/404',

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
import ImageEnhancerModal from "@/components/ImageEnhancer/ImageEnhancerModal.vue";
</script>
<template>
<ImageEnhancerModal/>
</template>
<style scoped lang="scss">
</style>

View File

@@ -14,7 +14,7 @@
<script setup lang="ts">
import Header from "@/layout/default/Header/Header.vue";
import AccountSettingSidebar
from "@/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue";
from "@/views/User/AccountSetting/Pages/AccountSettingSidebar/AccountSettingSidebar.vue";
</script>
<style scoped lang="scss">
.account-setting {

View File

@@ -79,7 +79,7 @@
</template>
<script setup lang="ts">
import StorageCard from "@/views/User/AccountSetting/components/AccountSettingStorage/StorageCard.vue";
import StorageCard from "@/views/User/AccountSetting/Pages/AccountSettingStorage/StorageCard.vue";
import {addStorageConfigApi, listUserStorageConfigApi} from "@/api/storage";
import {message} from "ant-design-vue";