|
|
|
|
@ -6,7 +6,7 @@ |
|
|
|
|
:show-close="true" |
|
|
|
|
@close="handleClose" |
|
|
|
|
@opened="initCanvas" |
|
|
|
|
qappend-to-body |
|
|
|
|
append-to-body |
|
|
|
|
> |
|
|
|
|
<el-form label-width="80px"> |
|
|
|
|
<el-form-item label="签名区域"> |
|
|
|
|
@ -40,13 +40,16 @@ |
|
|
|
|
ref="fileInput" |
|
|
|
|
type="file" |
|
|
|
|
accept="image/*" |
|
|
|
|
class="file-input" |
|
|
|
|
@change="handleFileChange" |
|
|
|
|
style="display: none" |
|
|
|
|
@change="handleFileSelect" |
|
|
|
|
/> |
|
|
|
|
</el-dialog> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<script> |
|
|
|
|
import { postUpdateOpsSignatureUpdate } from "@/api/login"; |
|
|
|
|
import { uploadFile } from "@/utils/requestMinio"; |
|
|
|
|
import { setLoginInfo } from "@/utils/auth"; |
|
|
|
|
export default { |
|
|
|
|
name: "ESignatureDialog", |
|
|
|
|
data() { |
|
|
|
|
@ -220,45 +223,100 @@ export default { |
|
|
|
|
this.$refs.fileInput.click(); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
handleFileChange(e) { |
|
|
|
|
const file = e.target.files[0]; |
|
|
|
|
if (file && this.ctx) { |
|
|
|
|
// 文件选择处理 |
|
|
|
|
async handleFileSelect(event) { |
|
|
|
|
const file = event.target.files[0]; |
|
|
|
|
if (!file) return; |
|
|
|
|
try { |
|
|
|
|
await this.loadImageToCanvas(file); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("图片加载失败:", error); |
|
|
|
|
this.$modal.msgError("图片加载失败: " + error.message); |
|
|
|
|
} finally { |
|
|
|
|
// 清空文件输入 |
|
|
|
|
event.target.value = ""; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 将图片加载到画布上 |
|
|
|
|
async loadImageToCanvas(file) { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
const reader = new FileReader(); |
|
|
|
|
reader.onload = (event) => { |
|
|
|
|
reader.onload = (e) => { |
|
|
|
|
const img = new Image(); |
|
|
|
|
img.onload = () => { |
|
|
|
|
const canvas = this.$refs.signatureCanvas; |
|
|
|
|
|
|
|
|
|
const ctx = this.ctx; |
|
|
|
|
|
|
|
|
|
// 清除画布 |
|
|
|
|
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
|
|
|
|
|
// 计算缩放比例,保持图片比例 |
|
|
|
|
const scale = Math.min( |
|
|
|
|
canvas.width / img.width, |
|
|
|
|
canvas.height / img.height, |
|
|
|
|
1 |
|
|
|
|
); |
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
|
|
|
|
|
// 计算图片缩放比例,保持宽高比 |
|
|
|
|
const scale = Math.min(canvas.width / img.width, canvas.height / img.height); |
|
|
|
|
const x = (canvas.width - img.width * scale) / 2; |
|
|
|
|
const y = (canvas.height - img.height * scale) / 2; |
|
|
|
|
|
|
|
|
|
this.ctx.drawImage( |
|
|
|
|
img, |
|
|
|
|
x, |
|
|
|
|
y, |
|
|
|
|
img.width * scale, |
|
|
|
|
img.height * scale |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 绘制图片到画布中央 |
|
|
|
|
ctx.drawImage(img, x, y, img.width * scale, img.height * scale); |
|
|
|
|
|
|
|
|
|
// 保存状态 |
|
|
|
|
this.saveState(); |
|
|
|
|
resolve(); |
|
|
|
|
}; |
|
|
|
|
img.src = event.target.result; |
|
|
|
|
img.onerror = () => reject(new Error("图片加载失败")); |
|
|
|
|
img.src = e.target.result; |
|
|
|
|
}; |
|
|
|
|
reader.onerror = () => reject(new Error("文件读取失败")); |
|
|
|
|
reader.readAsDataURL(file); |
|
|
|
|
} |
|
|
|
|
// 清空 input 值,允许重复选择同一文件 |
|
|
|
|
e.target.value = ""; |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
// 上传头像到 MinIO |
|
|
|
|
async uploadAvatar(file) { |
|
|
|
|
try { |
|
|
|
|
// 确保 MinIO 配置已加载 |
|
|
|
|
await this.ensureMinioInitialized(); |
|
|
|
|
const config = this.$store.getters.config; |
|
|
|
|
const bucket = config.MINIO_BUCKET_AVATAR || "remote-avatar-test"; |
|
|
|
|
const timestamp = Date.now(); |
|
|
|
|
const ext = file.name.split(".").pop() || "png"; |
|
|
|
|
const fileName = `${timestamp}.${ext}`; |
|
|
|
|
const objectName = `head_portrait/${fileName}`; |
|
|
|
|
|
|
|
|
|
console.log( |
|
|
|
|
`Uploading avatar to bucket: ${bucket}, object: ${objectName}` |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 使用 MinIO 上传 |
|
|
|
|
const result = await uploadFile( |
|
|
|
|
bucket, |
|
|
|
|
objectName, |
|
|
|
|
file, |
|
|
|
|
{}, |
|
|
|
|
(percent) => {} |
|
|
|
|
); |
|
|
|
|
console.log("MinIO 上传成功:", result); |
|
|
|
|
let avatar = config.MINIO_BUCKET_AVATAR + "/" + result.objectName; |
|
|
|
|
// 上传成功后调用业务接口更新用户信息 |
|
|
|
|
await postUpdateOpsSignatureUpdate({ |
|
|
|
|
// 签名图片地址 |
|
|
|
|
signature: avatar, |
|
|
|
|
// 签名图片的数字摘要值 |
|
|
|
|
sign_hash: avatar, |
|
|
|
|
}); |
|
|
|
|
// 更新头像显示 |
|
|
|
|
|
|
|
|
|
submit() { |
|
|
|
|
} catch (error) { |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 确保 MinIO 客户端已初始化 |
|
|
|
|
async ensureMinioInitialized() { |
|
|
|
|
const config = this.$store.getters.config; |
|
|
|
|
if (!config || !config.MINIO_ENDPOINT) { |
|
|
|
|
console.log("MinIO config not loaded, fetching..."); |
|
|
|
|
await this.$store.dispatch("GetNetConfig"); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
async submit() { |
|
|
|
|
const canvas = this.$refs.signatureCanvas; |
|
|
|
|
if (!canvas || !this.ctx) return; |
|
|
|
|
|
|
|
|
|
@ -282,10 +340,85 @@ export default { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.signatureData = canvas.toDataURL("image/png"); |
|
|
|
|
this.$message.success("签名上传成功"); |
|
|
|
|
this.$emit("submit", this.signatureData); |
|
|
|
|
this.handleClose(); |
|
|
|
|
try { |
|
|
|
|
// 将画布内容转换为blob |
|
|
|
|
const blob = await new Promise((resolve) => { |
|
|
|
|
canvas.toBlob(resolve, "image/png"); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// 上传到文件服务器 |
|
|
|
|
await this.uploadSignatureToServer(blob); |
|
|
|
|
|
|
|
|
|
this.$message.success("签名上传成功"); |
|
|
|
|
this.$emit("submit", canvas.toDataURL("image/png")); |
|
|
|
|
this.handleClose(); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("签名上传失败:", error); |
|
|
|
|
this.$message.error("签名上传失败: " + error.message); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 上传签名到文件服务器 |
|
|
|
|
async uploadSignatureToServer(blob) { |
|
|
|
|
// 确保 MinIO 配置已加载 |
|
|
|
|
await this.ensureMinioInitialized(); |
|
|
|
|
const config = this.$store.getters.config; |
|
|
|
|
|
|
|
|
|
// 构建文件路径:MINIO_BUCKET_KERNEL/esign/用户名_esign_时间戳.png |
|
|
|
|
const bucket = config.MINIO_BUCKET_KERNEL || "remote-kernel-test"; |
|
|
|
|
const loginInfo = this.$store.state.user.loginInfo; |
|
|
|
|
const username = loginInfo?.username || "unknown"; |
|
|
|
|
const timestamp = Date.now(); |
|
|
|
|
const objectName = `esign/${username}_esign_${timestamp}.png`; |
|
|
|
|
|
|
|
|
|
console.log(`Uploading signature to bucket: ${bucket}, object: ${objectName}`); |
|
|
|
|
|
|
|
|
|
// 使用 MinIO 上传 |
|
|
|
|
const result = await uploadFile( |
|
|
|
|
bucket, |
|
|
|
|
objectName, |
|
|
|
|
blob, |
|
|
|
|
{}, |
|
|
|
|
(percent) => {} |
|
|
|
|
); |
|
|
|
|
console.log("MinIO 上传成功:", result); |
|
|
|
|
|
|
|
|
|
// 构建签名图片地址 |
|
|
|
|
const signature = `${bucket}/${result.objectName}`; |
|
|
|
|
|
|
|
|
|
// 计算文件哈希值 |
|
|
|
|
const signHash = await this.calculateFileHash(blob); |
|
|
|
|
|
|
|
|
|
// 调用更新接口 |
|
|
|
|
await postUpdateOpsSignatureUpdate({ |
|
|
|
|
signature: signature, |
|
|
|
|
sign_hash: signHash, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// 更新 loginInfo 中的签名信息 |
|
|
|
|
this.updateLoginInfoSignature(signature); |
|
|
|
|
}, |
|
|
|
|
// 更新 loginInfo 中的签名信息 |
|
|
|
|
updateLoginInfoSignature(signature) { |
|
|
|
|
const loginInfo = { ...this.$store.state.user.loginInfo }; |
|
|
|
|
loginInfo.signature = signature; |
|
|
|
|
|
|
|
|
|
// 更新 Vuex store |
|
|
|
|
this.$store.commit('SET_LOGIN_INFO', loginInfo); |
|
|
|
|
|
|
|
|
|
// 更新本地存储 |
|
|
|
|
try { |
|
|
|
|
setLoginInfo(loginInfo); |
|
|
|
|
} catch (e) { |
|
|
|
|
console.error("更新本地存储失败:", e); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 计算文件 SHA-256 哈希值 |
|
|
|
|
async calculateFileHash(blob) { |
|
|
|
|
const arrayBuffer = await blob.arrayBuffer(); |
|
|
|
|
const hashBuffer = await crypto.subtle.digest("SHA-256", arrayBuffer); |
|
|
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer)); |
|
|
|
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); |
|
|
|
|
return hashHex; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
handleClose() { |
|
|
|
|
|