This commit is contained in:
2025-12-17 00:50:51 +08:00
parent 65f72a8c4c
commit 0edb09db43
10 changed files with 513 additions and 911 deletions
-7
View File
@@ -67,7 +67,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -1066,7 +1065,6 @@
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.19.tgz", "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.19.tgz",
"integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==", "integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"preact": "~10.12.1" "preact": "~10.12.1"
} }
@@ -1535,7 +1533,6 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
@@ -2263,7 +2260,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.8.19", "baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751", "caniuse-lite": "^1.0.30001751",
@@ -3408,7 +3404,6 @@
"integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==", "integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@bufbuild/protobuf": "^2.5.0", "@bufbuild/protobuf": "^2.5.0",
"buffer-builder": "^0.2.0", "buffer-builder": "^0.2.0",
@@ -3955,7 +3950,6 @@
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -4134,7 +4128,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz",
"integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.24", "@vue/compiler-dom": "3.5.24",
"@vue/compiler-sfc": "3.5.24", "@vue/compiler-sfc": "3.5.24",
@@ -292,6 +292,17 @@ onMounted(() => {
{{ t("appname.schedule") }} {{ t("appname.schedule") }}
</span> </span>
</router-link> </router-link>
<router-link
to="/warehouse"
class="nav-item nav-link"
active-class="active"
>
<span class="nav-link-title">
{{ t("appname.warehouse") }}
</span>
</router-link>
<router-link <router-link
to="/purchase" to="/purchase"
class="nav-item nav-link" class="nav-item nav-link"
@@ -301,6 +312,8 @@ onMounted(() => {
{{ t("appname.purchase") }} {{ t("appname.purchase") }}
</span> </span>
</router-link> </router-link>
</div> </div>
<div class="ms-auto"> <div class="ms-auto">
+126 -124
View File
@@ -1,3 +1,126 @@
<script setup>
import "cropperjs";
import { computed, ref } from "vue";
const fileObj = ref({});
const croppercanvas = ref();
const cropperimage = ref();
const cropperselection = ref();
/**
* 选区逻辑
*/
// 是否正在开始选区
const isCropperSelection = ref(false);
const isCropperMove = ref(true);
// 判断当前是移动还是选区
const currentType = computed(() => (isCropperMove.value ? "move" : "select"));
/**
* 按钮方法
*/
// 旋转
function handleRotate() {
cropperimage.value.$rotate("90deg");
cropperimage.value.$center("contain");
}
// 裁剪
function handleCropper() {
isCropperMove.value = false;
if (isCropperMove.value) {
cropperselection.value.$clear();
} else {
const cropperCanvas = croppercanvas.value;
const cropperCanvasRect = cropperCanvas.getBoundingClientRect();
const cropperImage = cropperimage.value;
const cropperImageRect = cropperImage.getBoundingClientRect();
const maxSelection = {
x: cropperImageRect.left - cropperCanvasRect.left,
y: cropperImageRect.top - cropperCanvasRect.top,
width: cropperImageRect.width,
height: cropperImageRect.height,
};
cropperselection.value.$change(
maxSelection.x,
maxSelection.y,
maxSelection.width,
maxSelection.height
);
}
}
// 移动
function handleMove() {
if (!isCropperMove.value) {
isCropperMove.value = true;
// 如果想要点击移动,清除选区,可以打开下面的代码注释
// cropperselection.value.$clear();
}
}
/**
* 监听选择区变化
* @param event
*/
function onCropperSelectionChange(event) {
if (event.detail.width && event.detail.height) {
isCropperSelection.value = true;
} else {
isCropperSelection.value = false;
}
}
/**
* 确认裁剪
*/
const emit = defineEmits(["success"]);
async function handleConfirm() {
if (isCropperSelection.value) {
const res = await cropperselection.value.$toCanvas();
const dataImage = res.toDataURL("image/png");
const file = dataURLtoFile(dataImage, fileObj.value.name);
emit("success", {
...fileObj.value,
file: file,
fileShow: dataImage,
});
}
}
// 将data:image转成新的file
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], { type: mime });
const file = new File([blob], filename, { type: mime });
return file;
}
/**
* 文件上传
*/
const input_form = ref();
function handleUploadSuccess() {
const files = input_form.value.files;
if (files.length) {
fileObj.value = {
name: files[0].name,
file: files[0],
fileShow: URL.createObjectURL(files[0]),
};
}
}
</script>
<template> <template>
<div class="basic_container"> <div class="basic_container">
<div class="tool_wrap"> <div class="tool_wrap">
@@ -6,7 +129,7 @@
</button> </button>
<button @click="handleRotate">旋转</button> <button @click="handleRotate">旋转</button>
<button :class="{ active: isCropperSelection }" @click="handleCropper"> <button :class="{ active: isCropperSelection }" @click="handleCropper">
{{ isCropperSelection ? '重置选区' : '裁剪' }} {{ isCropperSelection ? "重置选区" : "裁剪" }}
</button> </button>
</div> </div>
<div class="dialog_wrap"> <div class="dialog_wrap">
@@ -21,6 +144,8 @@
skewable skewable
translatable translatable
></cropper-image> ></cropper-image>
<cropper-shade hidden ref="cropperShade"></cropper-shade> <cropper-shade hidden ref="cropperShade"></cropper-shade>
<cropper-handle :action="currentType" plain></cropper-handle> <cropper-handle :action="currentType" plain></cropper-handle>
<cropper-selection <cropper-selection
@@ -65,129 +190,6 @@
</div> </div>
</template> </template>
<script setup>
import 'cropperjs';
import { computed, ref } from 'vue';
const fileObj = ref({});
const croppercanvas = ref();
const cropperimage = ref();
const cropperselection = ref();
/**
* 选区逻辑
*/
// 是否正在开始选区
const isCropperSelection = ref(false);
const isCropperMove = ref(true);
// 判断当前是移动还是选区
const currentType = computed(() => (isCropperMove.value ? 'move' : 'select'));
/**
* 按钮方法
*/
// 旋转
function handleRotate() {
cropperimage.value.$rotate('90deg');
cropperimage.value.$center('contain');
}
// 裁剪
function handleCropper() {
isCropperMove.value = false;
if (isCropperMove.value) {
cropperselection.value.$clear();
} else {
const cropperCanvas = croppercanvas.value;
const cropperCanvasRect = cropperCanvas.getBoundingClientRect();
const cropperImage = cropperimage.value;
const cropperImageRect = cropperImage.getBoundingClientRect();
const maxSelection = {
x: cropperImageRect.left - cropperCanvasRect.left,
y: cropperImageRect.top - cropperCanvasRect.top,
width: cropperImageRect.width,
height: cropperImageRect.height,
};
cropperselection.value.$change(
maxSelection.x,
maxSelection.y,
maxSelection.width,
maxSelection.height,
);
}
}
// 移动
function handleMove() {
if (!isCropperMove.value) {
isCropperMove.value = true;
// 如果想要点击移动,清除选区,可以打开下面的代码注释
// cropperselection.value.$clear();
}
}
/**
* 监听选择区变化
* @param event
*/
function onCropperSelectionChange(event) {
if (event.detail.width && event.detail.height) {
isCropperSelection.value = true;
} else {
isCropperSelection.value = false;
}
}
/**
* 确认裁剪
*/
const emit = defineEmits(['success']);
async function handleConfirm() {
if (isCropperSelection.value) {
const res = await cropperselection.value.$toCanvas();
const dataImage = res.toDataURL('image/png');
const file = dataURLtoFile(dataImage, fileObj.value.name);
emit('success', {
...fileObj.value,
file: file,
fileShow: dataImage,
});
}
}
// 将data:image转成新的file
function dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], { type: mime });
const file = new File([blob], filename, { type: mime });
return file;
}
/**
* 文件上传
*/
const input_form = ref();
function handleUploadSuccess() {
const files = input_form.value.files;
if (files.length) {
fileObj.value = {
name: files[0].name,
file: files[0],
fileShow: URL.createObjectURL(files[0]),
};
}
}
</script>
<style scoped> <style scoped>
.dialog_wrap { .dialog_wrap {
display: flex; display: flex;
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -5,7 +5,8 @@
"forgot_password": "Forgot Password", "forgot_password": "Forgot Password",
"register": "Register", "register": "Register",
"schedule":"Schedule", "schedule":"Schedule",
"purchase":"Purchase" "purchase":"Purchase",
"warehouse":"Warehouse"
}, },
"purchase":{ "purchase":{
"purchase_list":"Purchase List", "purchase_list":"Purchase List",
+2 -1
View File
@@ -5,7 +5,8 @@
"forgot_password": "忘记密码", "forgot_password": "忘记密码",
"register": "注册", "register": "注册",
"schedule":"日程", "schedule":"日程",
"purchase":"采购" "purchase":"采购",
"warehouse":"仓库"
}, },
"purchase":{ "purchase":{
"purchase_list":"采购列表", "purchase_list":"采购列表",
File diff suppressed because one or more lines are too long
+5
View File
@@ -60,6 +60,11 @@ const router = createRouter({
path: "/purchase", path: "/purchase",
name: "purchase", name: "purchase",
component: () => import("../views/purchaseView.vue"), component: () => import("../views/purchaseView.vue"),
},
{
path: "/warehouse",
name: "warehouse",
component: () => import("../views/warehouse.vue"),
} }
], ],
}); });
+9 -5
View File
@@ -1,16 +1,20 @@
<script setup> <script setup>
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import imageCropper from '@/components/imageCropper.vue';
import croppertest from '@/components/croppertest.vue';
const user = useUserStore() const user = useUserStore()
function t(){
user.login();
}
</script> </script>
<template> <template>
test test
<button @click="t">222</button>
{{ user.isLoggedIn }} <imageCropper></imageCropper>
</template> </template>
+70 -3
View File
@@ -1,5 +1,72 @@
<template> <template>
<div> <div class="page-wrapper">
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<h2 class="page-title">Cards</h2>
</div>
</div>
</div>
</div> </div>
</template> <!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-md-6 col-lg-3">
<div class="card">
<!-- Photo -->
<div
class="img-responsive img-responsive-21x9 card-img-top"
style="
background-image: url(./static/photos/home-office-desk-with-macbook-iphone-calendar-watch-and-organizer.jpg);
"
></div>
<div class="card-body">
<h3 class="card-title">Card with top image</h3>
<p class="text-secondary card_text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</p>
</div>
</div>
</div>
<div class="col-md-6 col-lg-3">
<div class="card">
<!-- Photo -->
<div
class="img-responsive img-responsive-21x9 card-img-top"
style="
background-image: url(./static/photos/home-office-desk-with-macbook-iphone-calendar-watch-and-organizer.jpg);
"
></div>
<div class="card-body">
<h3 class="card-title">Card with top image</h3>
<p class="text-secondary card_text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Aperiam deleniti fugit incidunt, iste, itaque minima neque
pariatur perferendis sed suscipit velit vitae voluptatem. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illo optio et fuga omnis ipsa, odit repellendus iste doloremque est, nam eius quisquam perspiciatis deserunt. Quasi tempore velit architecto corporis voluptatibus!
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.card_text{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* 限制显示4行 */
overflow: hidden;
line-height: 1.5;
min-height: calc(1.5em * 2); /* 最小高度 */
}
</style>