用户可以在web上签名,然后保存为图片
阿里千问AI查到的
前端HTML代码:
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户签名</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
}
h1 {
margin-bottom: 20px;
}
#signatureCanvas {
border: 2px solid #333;
background-color: white;
cursor: crosshair;
touch-action: none; /* 禁用触摸滚动/缩放 */
}
.controls {
margin-top: 15px;
}
button {
padding: 10px 20px;
margin: 0 10px;
font-size: 16px;
cursor: pointer;
}
button:hover {
opacity: 0.9;
}
</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
</head>
<body>
<h1>请在此签名</h1>
<canvas id="signatureCanvas" width="500" height="200"></canvas>
<div class="controls">
<button id="clearBtn">清除</button>
<button id="saveBtn">保存签名</button>
</div>
<div id="res"></div>
<script>
const canvas = document.getElementById('signatureCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
// 设置画笔样式
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.strokeStyle = '#000';
// 鼠标事件
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// 触摸事件(移动端)
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('touchend', handleTouchEnd);
function getMousePos(e) {
const rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
function getTouchPos(touch) {
const rect = canvas.getBoundingClientRect();
return {
x: touch.clientX - rect.left,
y: touch.clientY - rect.top
};
}
function startDrawing(e) {
isDrawing = true;
const pos = getMousePos(e);
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
}
function draw(e) {
if (!isDrawing) return;
const pos = getMousePos(e);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
}
function stopDrawing() {
isDrawing = false;
}
// 触摸事件处理
function handleTouchStart(e) {
e.preventDefault();
const touch = e.touches[0];
const pos = getTouchPos(touch);
isDrawing = true;
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
}
function handleTouchMove(e) {
e.preventDefault();
if (!isDrawing) return;
const touch = e.touches[0];
const pos = getTouchPos(touch);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
}
function handleTouchEnd(e) {
e.preventDefault();
isDrawing = false;
}
// 清除画布
document.getElementById('clearBtn').addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
// 保存签名
document.getElementById('saveBtn').addEventListener('click', () => {
const dataURL = canvas.toDataURL('image/png'); // 获取 base64 字符串
// 显示图片预览(可选)
// const img = new Image();
// img.src = dataURL;
// document.body.appendChild(img);
// 发送 AJAX 到后端
$.ajax({
url: '/home/index', // 根据实际情况调整 URL 路径
type: 'POST',
contentType: 'application/json;charset=UTF-8',
data: JSON.stringify({
Signature: dataURL // 传递给后端的数据
}),
success: function (response) {
if (response.success) {
alert("签名上传成功!\n文件地址:" + response.url);
// 可以在此处处理返回的文件URL,比如显示在页面上等
$('#res').html("<a href='"+response.url+"'>"+response.url+"</a>")
} else {
alert("签名上传失败!");
}
},
error: function (xhr, status, error) {
console.error('签名上传时发生错误:', error);
alert("签名上传时发生错误,请重试!");
}
});
});
</script>
</body>
</html>
后端C#代码:
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Text.RegularExpressions;
using userqianming.Models;
namespace userqianming.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Index([FromBody] SignatureRequest request)
{
if (string.IsNullOrEmpty(request?.Signature))
{
return BadRequest("签名数据为空");
}
try
{
// 提取 base64 部分(去掉 data URL 前缀)
var base64Data = request.Signature;
if (base64Data.StartsWith("data:image"))
{
// 使用正则或 Split 移除前缀
var match = Regex.Match(base64Data, @"^data:image\/\w+;base64,(.*)$");
if (match.Success)
{
base64Data = match.Groups[1].Value;
}
else
{
return BadRequest("无效的 Base64 图片格式");
}
}
// 解码 Base64
byte[] imageBytes = Convert.FromBase64String(base64Data);
// 设置保存路径(例如:wwwroot/signatures/)
var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "signatures");
Directory.CreateDirectory(folderPath); // 确保目录存在
// 生成唯一文件名
string fileName = $"signature_{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid().ToString("N")[..8]}.png";
string filePath = Path.Combine(folderPath, fileName);
// 保存文件
await System.IO.File.WriteAllBytesAsync(filePath, imageBytes);
// 可选:返回保存的 URL 或成功信息
var fileUrl = $"/signatures/{fileName}";
return Ok(new { success = true, url = fileUrl });
}
catch (Exception ex)
{
// 记录日志(可集成 ILogger)
Console.WriteLine($"保存签名失败: {ex.Message}");
return StatusCode(500, "保存签名时发生错误");
}
}
}
// DTO 用于接收 JSON 数据
public class SignatureRequest
{
public string? Signature { get; set; }
}
}