图片裁剪不行

This commit is contained in:
2025-11-19 18:26:07 +08:00
parent e571d031ff
commit 725cd07a45
8 changed files with 287 additions and 3 deletions
+138
View File
@@ -12,9 +12,11 @@
"@tabler/icons-vue": "^3.35.0", "@tabler/icons-vue": "^3.35.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"bootstrap": "^5.3.8", "bootstrap": "^5.3.8",
"cropperjs": "^2.1.0",
"litepicker": "^2.0.12", "litepicker": "^2.0.12",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.22", "vue": "^3.5.22",
"vue-cropper": "^0.6.5",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.12",
"vue-router": "^4.6.3" "vue-router": "^4.6.3"
}, },
@@ -483,6 +485,126 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@cropper/element": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.1.0.tgz",
"integrity": "sha512-2zELddqHQNmlvkPoiYzE5nxEjPE+C8nXoTPuvV3FvLp3YjBinc7qb73Icg9UXP0o9qC4+h9q96JgGo0AyMO/Ng==",
"license": "MIT",
"dependencies": {
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-canvas": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.1.0.tgz",
"integrity": "sha512-el+rfJpZxsD2q5XxDBA4fRczcrOqB65Lb7roqXOq8LKufwf4bPWA9C6DjNJJahh/TP94dsLIEy3tSkgRMDv3Aw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-crosshair": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.1.0.tgz",
"integrity": "sha512-0V589dAx8uZAfvJwdINLn76gfPQEafPH94ukjJ76uX0FCUovLaAVX+VRD/MDSYn0Mza/xejzmL9Dhd1DfemvmA==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-grid": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.1.0.tgz",
"integrity": "sha512-dEnk0rO+vp553LMvsPYgfrqVFcYXeVFrgFeavBYYEhAXtO40p7kN4rmLYLMMjaN+T/Mx2BATv6kUQpALKy2HLw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-handle": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.1.0.tgz",
"integrity": "sha512-8BklWA4C/2GGAULupIWleSnGutECvYt3vx9flodqDfZpDEozws4LgLqmmzVuQmVkRVUdLnXdtx28kjgWLtzkHg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-image": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.1.0.tgz",
"integrity": "sha512-mXOV8ixJvG0XtTxLebYAKDjEkFbFOQnsF02hXPZk1yQSV0J+LLhN7a2NePrtKnoTsEV19fhhX3UorMoyGGxvzg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/element-canvas": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-selection": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.1.0.tgz",
"integrity": "sha512-mtFtBl6HIa/s9TWohXw+Z5eJoeYTqylrIcHvS7oVv0uM7IyeRwBW65Q7z+KtLfq/LW+2Sw/XDyvR+VN/DawBPw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/element-canvas": "^2.1.0",
"@cropper/element-image": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-shade": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.1.0.tgz",
"integrity": "sha512-zMdyqbb0lc0Vd1oj2Z1miIZvhyZG41OXMHvrNt0hNwblh0dVdrvtw48lnFDgRv+672vt2CNx7Q04GuvCQfPlgg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/element-canvas": "^2.1.0",
"@cropper/element-selection": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/element-viewer": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.1.0.tgz",
"integrity": "sha512-XnxlQuqHitd1FOFZ6E0yXAF5NYd/LyIvONLLHI9p1rJw747WYKUPxQaSYtFKF7IOizJu/8mMj++Zc1dZ5ZP3YQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/element-canvas": "^2.1.0",
"@cropper/element-image": "^2.1.0",
"@cropper/element-selection": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/@cropper/elements": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.1.0.tgz",
"integrity": "sha512-qvzlYDn3VQgPPpsCu6Gi1XUO0v3vpXQFSjjxcVijbXeNsl/eiKrN7H9/CEiRgi5vr8kXfd7ZvgYxBjUBbH+y+w==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.1.0",
"@cropper/element-canvas": "^2.1.0",
"@cropper/element-crosshair": "^2.1.0",
"@cropper/element-grid": "^2.1.0",
"@cropper/element-handle": "^2.1.0",
"@cropper/element-image": "^2.1.0",
"@cropper/element-selection": "^2.1.0",
"@cropper/element-shade": "^2.1.0",
"@cropper/element-viewer": "^2.1.0"
}
},
"node_modules/@cropper/utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.1.0.tgz",
"integrity": "sha512-wLtpZ4/UWgo+fGmG8NBWge8x5ehjfDe9ovleDfLy8kpwFaw43XXOEXQtRL1UNr0u4JZxaeO8FcXcolRWUUrlRQ==",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12", "version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
@@ -1843,6 +1965,16 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/cropperjs": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.1.0.tgz",
"integrity": "sha512-SsSDqdVRl+mjbIBkGWlk1gCGcc+HzBqCbH5EQ+1tkAFUdxq2KUGukXF1RqhmvXrrdrX7PDwSUkWgXS7E36KvGQ==",
"license": "MIT",
"dependencies": {
"@cropper/elements": "^2.1.0",
"@cropper/utils": "^2.1.0"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -2999,6 +3131,12 @@
} }
} }
}, },
"node_modules/vue-cropper": {
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-0.6.5.tgz",
"integrity": "sha512-lSvY6IpeA/Tv/iPZ/FOkMHVRBPSlm7t57nuHEZFBMRNOH8ElvfqVlnHGDOAMlvPhh9gHiddiQoASS+fY0MFX0g==",
"license": "ISC"
},
"node_modules/vue-i18n": { "node_modules/vue-i18n": {
"version": "11.1.12", "version": "11.1.12",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.12.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.12.tgz",
+2
View File
@@ -16,9 +16,11 @@
"@tabler/icons-vue": "^3.35.0", "@tabler/icons-vue": "^3.35.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"bootstrap": "^5.3.8", "bootstrap": "^5.3.8",
"cropperjs": "^2.1.0",
"litepicker": "^2.0.12", "litepicker": "^2.0.12",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"vue": "^3.5.22", "vue": "^3.5.22",
"vue-cropper": "^0.6.5",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.12",
"vue-router": "^4.6.3" "vue-router": "^4.6.3"
}, },
@@ -65,7 +65,7 @@ defineExpose({
</span> </span>
<input <input
class="form-control" class="form-control"
placeholder="Select a date" :placeholder="t('message.select_date')"
ref="datepicker" ref="datepicker"
value="" value=""
/> />
@@ -0,0 +1,131 @@
<template>
<div class="image-cropper">
<div class="cropper-actions">
<input
type="file"
ref="fileInput"
@change="handleFileSelect"
accept="image/*"
style="display: none"
/>
<button @click="$refs.fileInput.click()">选择图片</button>
<div v-if="haveImageSrc" class="action-buttons">
<button @click="setAspectRatio(1)">1:1</button>
<button @click="setAspectRatio(16 / 9)">16:9</button>
<button @click="setAspectRatio(4 / 3)">4:3</button>
<button @click="rotate(-90)"></button>
<button @click="rotate(90)"></button>
<button @click="crop">裁剪</button>
<button @click="reset">重置</button>
</div>
</div>
<div v-if="haveImageSrc" class="cropper-container">
<img ref="imageEl" alt="裁剪图片" />
</div>
</div>
</template>
<script setup>
import { ref, nextTick } from "vue";
import Cropper from "cropperjs";
const emit = defineEmits(["cropped", "error"]);
const imageEl = ref(null);
const fileInput = ref(null);
const haveImageSrc = ref(false);
const resultImage = ref("");
let cropperInstance = null;
// 读取文件为DataURL
const readFileAsDataURL = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
};
// 选择文件
const handleFileSelect = async (event) => {
const file = event.target.files[0];
if (!file) return;
if (!file.type.startsWith("image/")) {
emit("error", "请选择图片文件");
return;
}
console.log("选择了文件:", file.name);
try {
const dataUrl = await readFileAsDataURL(file);
haveImageSrc.value = true;
console.log("图片加载完成,初始化裁剪器");
await nextTick();
if (cropperInstance) {
cropperInstance.destroy();
console.log("销毁旧的Cropper实例");
}
console.log("加载图片进行裁剪");
imageEl.value.src = dataUrl;
cropperInstance = new Cropper(imageEl.value, {
aspectRatio: 1,
viewMode: 2,
autoCropArea: 0.8,
zoomable: true,
zoomOnWheel: true,
zoomOnTouch: true,
wheelZoomRatio: 0.1,
ready: function () {
console.log("Cropper 初始化成功");
},
crop: function (event) {
// 可以在这里获取裁剪区域的信息
console.log(event.detail);
},
});
} catch (error) {
emit("error", "图片加载失败");
}
};
</script>
<style scoped>
.image-cropper {
max-width: 600px;
margin: 0 auto;
}
.cropper-actions {
margin-bottom: 20px;
}
.action-buttons {
margin-top: 10px;
}
.action-buttons button {
margin: 0 5px 5px 0;
padding: 5px 10px;
}
.cropper-container {
max-height: 400px;
overflow: hidden;
}
.result-container {
margin-top: 20px;
text-align: center;
}
.result-container img {
max-width: 300px;
max-height: 300px;
border: 1px solid #ddd;
margin: 10px 0;
}
</style>
+2 -1
View File
@@ -45,7 +45,8 @@
"user_home": "Profile", "user_home": "Profile",
"user_settings": "Settings", "user_settings": "Settings",
"preferences": "Preferences", "preferences": "Preferences",
"administrator": "Administrator" "administrator": "Administrator",
"select_date":"Select a date"
}, },
"settings": { "settings": {
"account_settings": "Account Settings", "account_settings": "Account Settings",
+2 -1
View File
@@ -45,7 +45,8 @@
"user_home": "个人主页", "user_home": "个人主页",
"user_settings": "个人资料", "user_settings": "个人资料",
"preferences": "偏好设置", "preferences": "偏好设置",
"administrator": "管理员" "administrator": "管理员",
"select_date":"选择日期"
}, },
"settings": { "settings": {
"account_settings": "个人设置", "account_settings": "个人设置",
@@ -3,6 +3,7 @@ import { ref } from "vue";
import { my_network_func } from "@/my_network_func"; import { my_network_func } from "@/my_network_func";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import MyOffcanvas from "@/components/MyOffcanvas.vue"; import MyOffcanvas from "@/components/MyOffcanvas.vue";
import imageCropper from "@/components/imageCropper.vue";
const user = useUserStore(); const user = useUserStore();
const mos = ref(); const mos = ref();
@@ -32,5 +33,6 @@ function tt(){
<button @click="tt">333</button> <button @click="tt">333</button>
{{ user.userInfo }} {{ user.userInfo }}
</main> </main>
<imageCropper />
<MyOffcanvas ref="mos" /> <MyOffcanvas ref="mos" />
</template> </template>
@@ -3,6 +3,7 @@ import { onMounted, watch, ref } from "vue";
import settingNavigation from "@/components/settingNavigation.vue"; import settingNavigation from "@/components/settingNavigation.vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import datePicker from "@/components/datePicker.vue"; import datePicker from "@/components/datePicker.vue";
import imageCropper from "@/components/imageCropper.vue";
const { t } = useI18n(); const { t } = useI18n();
const birthday = ref(); const birthday = ref();
@@ -46,6 +47,13 @@ function updataInfo() {
console.log("备注:", userremarkValue); console.log("备注:", userremarkValue);
console.log("生日:", birthdayValue); console.log("生日:", birthdayValue);
} }
onMounted(()=>{
//console.log("account mounted");
//username.value.value="Kevin";
})
</script> </script>
<template> <template>
@@ -74,6 +82,7 @@ function updataInfo() {
style="background-image: url(./static/avatars/000m.jpg)" style="background-image: url(./static/avatars/000m.jpg)"
></span> ></span>
</div> </div>
<imageCropper />
<div class="col-auto"> <div class="col-auto">
<button class="btn"> <button class="btn">
{{ t("settings.change_avatar") }} {{ t("settings.change_avatar") }}