hrp-ui-base 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.cjs +5 -5
- package/dist/components.es.js +3326 -2756
- package/dist/style.css +1 -1
- package/package.json +3 -1
- package/src/components/layout/LayoutContainer.vue +5 -1
- package/src/components/layout/personal-sign/ImageCropper.vue +187 -0
- package/src/components/layout/personal-sign/PersonalSignDialog.vue +658 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hrp-ui-base",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "HRP 前端公共组件、工具方法和基础样式包",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -81,8 +81,10 @@
|
|
|
81
81
|
"lodash": "^4.17.21",
|
|
82
82
|
"mathjs": "^13.1.1",
|
|
83
83
|
"moment": "^2.29.4",
|
|
84
|
+
"qrcode.vue": "^3.6.0",
|
|
84
85
|
"qs": "^6.11.0",
|
|
85
86
|
"sm-crypto": "^0.3.11",
|
|
87
|
+
"vue-cropper": "^1.1.4",
|
|
86
88
|
"vuedraggable": "^4.1.0"
|
|
87
89
|
},
|
|
88
90
|
"devDependencies": {
|
|
@@ -77,6 +77,8 @@
|
|
|
77
77
|
<ExportCenterDrawer v-model:visible="exportDrawerVisible" />
|
|
78
78
|
<!-- 审批中心抽屉 -->
|
|
79
79
|
<ApprovalCenterDrawer v-model:visible="approvalDrawerVisible" />
|
|
80
|
+
<!-- 个人签名弹窗 -->
|
|
81
|
+
<PersonalSignDialog ref="personalSignDialogRef" />
|
|
80
82
|
</template>
|
|
81
83
|
|
|
82
84
|
<script lang="ts" setup>
|
|
@@ -97,6 +99,7 @@ import SideMenu from "./sideMenu.vue";
|
|
|
97
99
|
import PersonalizationGuideDialog from "./personalization-guide-dialog.vue";
|
|
98
100
|
import ExportCenterDrawer from "./ExportCenterDrawer.vue";
|
|
99
101
|
import ApprovalCenterDrawer from "./ApprovalCenterDrawer.vue";
|
|
102
|
+
import PersonalSignDialog from "./personal-sign/PersonalSignDialog.vue";
|
|
100
103
|
import { useLayoutConfigStore } from "./stores/useLayoutConfigStore";
|
|
101
104
|
import { useLayoutMenuStore } from "./stores/useLayoutMenuStore";
|
|
102
105
|
import { useLayoutTabsStore } from "./stores/useLayoutTabsStore";
|
|
@@ -273,8 +276,9 @@ const handleMessageUpdateCount = async () => {
|
|
|
273
276
|
updateNum.value += 1;
|
|
274
277
|
};
|
|
275
278
|
|
|
279
|
+
const personalSignDialogRef = ref<InstanceType<typeof PersonalSignDialog>>();
|
|
276
280
|
const handlePersonalSign = () => {
|
|
277
|
-
|
|
281
|
+
personalSignDialogRef.value?.open();
|
|
278
282
|
};
|
|
279
283
|
|
|
280
284
|
const handleCleanCache = () => {
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-dialog v-model="dialogVisible" title="裁剪签名图片" width="560px" @close="closeDialog">
|
|
3
|
+
<div class="cropper-content">
|
|
4
|
+
<div class="cropper-container">
|
|
5
|
+
<vue-cropper
|
|
6
|
+
v-if="showCropper"
|
|
7
|
+
class="crop"
|
|
8
|
+
ref="cropperRef"
|
|
9
|
+
:autoCrop="option.autoCrop"
|
|
10
|
+
:autoCropHeight="option.autoCropHeight"
|
|
11
|
+
:autoCropWidth="option.autoCropWidth"
|
|
12
|
+
:canMove="option.canMove"
|
|
13
|
+
:canScale="option.canScale"
|
|
14
|
+
:centerBox="option.centerBox"
|
|
15
|
+
:fixed="option.fixed"
|
|
16
|
+
:fixedBox="option.fixedBox"
|
|
17
|
+
:fixedNumber="option.fixedNumber"
|
|
18
|
+
:img="currentFile"
|
|
19
|
+
:info-true="option.infoTrue"
|
|
20
|
+
:mode="option.mode"
|
|
21
|
+
:origin="option.origin"
|
|
22
|
+
:outputSize="option.outputSize"
|
|
23
|
+
:outputType="option.outputType"
|
|
24
|
+
></vue-cropper>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- 旋转控制按钮 -->
|
|
28
|
+
<div class="rotate-controls">
|
|
29
|
+
<el-button-group>
|
|
30
|
+
<el-button size="small" @click="rotateLeft" :icon="RefreshLeft">
|
|
31
|
+
左旋转90°
|
|
32
|
+
</el-button>
|
|
33
|
+
<el-button size="small" @click="rotateRight" :icon="RefreshRight">
|
|
34
|
+
右旋转90°
|
|
35
|
+
</el-button>
|
|
36
|
+
</el-button-group>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<template #footer>
|
|
41
|
+
<el-button @click="dialogVisible = false">取消</el-button>
|
|
42
|
+
<el-button type="primary" @click="confirmCrop" :loading="submitLoading">确定</el-button>
|
|
43
|
+
</template>
|
|
44
|
+
</el-dialog>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { ref, nextTick } from "vue";
|
|
49
|
+
import { ElMessage } from "element-plus";
|
|
50
|
+
import { RefreshLeft, RefreshRight } from '@element-plus/icons-vue';
|
|
51
|
+
|
|
52
|
+
const emits = defineEmits(["success"]);
|
|
53
|
+
|
|
54
|
+
const submitLoading = ref<boolean>(false)
|
|
55
|
+
|
|
56
|
+
const option = ref({
|
|
57
|
+
autoCrop: true,
|
|
58
|
+
autoCropHeight: 100,
|
|
59
|
+
autoCropWidth: 133,
|
|
60
|
+
canMove: true,
|
|
61
|
+
canScale: true,
|
|
62
|
+
centerBox: true,
|
|
63
|
+
fixed: true,
|
|
64
|
+
fixedBox: false,
|
|
65
|
+
fixedNumber: [133, 100],
|
|
66
|
+
infoTrue: true,
|
|
67
|
+
mode: "contain",
|
|
68
|
+
origin: false,
|
|
69
|
+
outputSize: 1,
|
|
70
|
+
outputType: "png",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const showCropper = ref(false);
|
|
74
|
+
const dialogVisible = ref(false);
|
|
75
|
+
const cropperRef = ref<any>();
|
|
76
|
+
const currentFile = ref<any>();
|
|
77
|
+
|
|
78
|
+
// 打开裁剪弹框
|
|
79
|
+
const open = async (file: File | Blob) => {
|
|
80
|
+
try {
|
|
81
|
+
dialogVisible.value = true;
|
|
82
|
+
currentFile.value = await fileToBase64(file);
|
|
83
|
+
await nextTick();
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
showCropper.value = true;
|
|
86
|
+
}, 10);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('打开裁剪器失败:', error);
|
|
89
|
+
ElMessage.error("打开裁剪器失败,请重试!");
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// 文件转 Base64
|
|
94
|
+
const fileToBase64 = (file: File | Blob): Promise<string> => {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const reader = new FileReader();
|
|
97
|
+
reader.readAsDataURL(file);
|
|
98
|
+
reader.onload = () => resolve(reader.result as string);
|
|
99
|
+
reader.onerror = (error) => reject(error);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Base64 转 Blob
|
|
104
|
+
function base64ToBlob(base64String: string, mimeType: string): Blob {
|
|
105
|
+
const byteString = atob(base64String.split(',')[1]);
|
|
106
|
+
const ab = new ArrayBuffer(byteString.length);
|
|
107
|
+
const ia = new Uint8Array(ab);
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < byteString.length; i++) {
|
|
110
|
+
ia[i] = byteString.charCodeAt(i);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return new Blob([ab], { type: mimeType });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 确认裁剪
|
|
117
|
+
const confirmCrop = async () => {
|
|
118
|
+
submitLoading.value = true;
|
|
119
|
+
try {
|
|
120
|
+
cropperRef.value?.getCropData((data: string) => {
|
|
121
|
+
if (data) {
|
|
122
|
+
const croppedBlob = base64ToBlob(data, 'image/png');
|
|
123
|
+
emits("success", croppedBlob);
|
|
124
|
+
dialogVisible.value = false;
|
|
125
|
+
showCropper.value = false;
|
|
126
|
+
}
|
|
127
|
+
submitLoading.value = false;
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
ElMessage.error("裁剪图片失败!");
|
|
131
|
+
submitLoading.value = false;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// 左旋转90度
|
|
136
|
+
const rotateLeft = () => {
|
|
137
|
+
cropperRef.value?.rotateLeft();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// 右旋转90度
|
|
141
|
+
const rotateRight = () => {
|
|
142
|
+
cropperRef.value?.rotateRight();
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// 关闭弹框
|
|
146
|
+
const closeDialog = () => {
|
|
147
|
+
showCropper.value = false;
|
|
148
|
+
currentFile.value = null;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
defineExpose({
|
|
152
|
+
open,
|
|
153
|
+
});
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<style scoped lang="scss">
|
|
157
|
+
.cropper-content {
|
|
158
|
+
display: flex;
|
|
159
|
+
flex-direction: column;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 16px;
|
|
162
|
+
width: 100%;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.cropper-container {
|
|
166
|
+
width: 500px;
|
|
167
|
+
height: 400px;
|
|
168
|
+
position: relative;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.crop {
|
|
172
|
+
width: 100%;
|
|
173
|
+
height: 100%;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.rotate-controls {
|
|
177
|
+
display: flex;
|
|
178
|
+
justify-content: center;
|
|
179
|
+
align-items: center;
|
|
180
|
+
padding: 8px 0;
|
|
181
|
+
|
|
182
|
+
.el-button-group {
|
|
183
|
+
display: flex;
|
|
184
|
+
gap: 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
</style>
|