增加印章
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
2025-06-29 03:01:56 +08:00
parent 803139a1c9
commit a262c52c73
11 changed files with 1942 additions and 230 deletions

View File

@ -10,7 +10,7 @@
:class="[ :class="[
'px-3 py-2 text-sm font-medium rounded transition-all', 'px-3 py-2 text-sm font-medium rounded transition-all',
activeAlgorithm === algorithm.type activeAlgorithm === algorithm.type
? 'bg-primary text-white shadow-sm' ? 'bg-primary-500 text-white shadow-sm'
: 'bg-block text-secondary border hover:bg-hover' : 'bg-block text-secondary border hover:bg-hover'
]" ]"
@click="setActiveAlgorithm(algorithm.type)" @click="setActiveAlgorithm(algorithm.type)"
@ -27,7 +27,7 @@
<button <button
:class="[ :class="[
'flex-1 px-4 py-2 text-sm font-medium rounded transition-all', 'flex-1 px-4 py-2 text-sm font-medium rounded transition-all',
!isDecoding ? 'bg-primary text-white' : 'bg-block text-secondary border' !isDecoding ? 'bg-primary-500 text-white' : 'bg-block text-secondary border'
]" ]"
@click="isDecoding = false" @click="isDecoding = false"
> >
@ -36,7 +36,7 @@
<button <button
:class="[ :class="[
'flex-1 px-4 py-2 text-sm font-medium rounded transition-all', 'flex-1 px-4 py-2 text-sm font-medium rounded transition-all',
isDecoding ? 'bg-primary text-white' : 'bg-block text-secondary border' isDecoding ? 'bg-primary-500 text-white' : 'bg-block text-secondary border'
]" ]"
@click="isDecoding = true" @click="isDecoding = true"
> >

View File

@ -5,8 +5,8 @@
<div class="flex items-center bg-card rounded-md p-1 border"> <div class="flex items-center bg-card rounded-md p-1 border">
<button <button
:class="[ :class="[
'px-4 py-2 text-sm font-medium transition-all rounded-l-md', 'px-4 py-2 text-sm font-medium rounded-l transition-all',
mode === 'diff' ? 'bg-primary text-white shadow-sm' : 'text-secondary hover:text-primary' mode === 'diff' ? 'bg-primary-500 text-white shadow-sm' : 'text-secondary hover:text-primary'
]" ]"
@click="mode = 'diff'" @click="mode = 'diff'"
> >
@ -14,8 +14,8 @@
</button> </button>
<button <button
:class="[ :class="[
'px-4 py-2 text-sm font-medium transition-all rounded-r-md', 'px-4 py-2 text-sm font-medium rounded-r transition-all',
mode === 'add' ? 'bg-primary text-white shadow-sm' : 'text-secondary hover:text-primary' mode === 'add' ? 'bg-primary-500 text-white shadow-sm' : 'text-secondary hover:text-primary'
]" ]"
@click="mode = 'add'" @click="mode = 'add'"
> >

View File

@ -14,7 +14,7 @@
:key="'from-' + base.id" :key="'from-' + base.id"
:class="[ :class="[
'px-3 py-2 text-sm rounded transition-all', 'px-3 py-2 text-sm rounded transition-all',
fromBase === base.id ? 'bg-primary text-white' : 'btn-secondary' fromBase === base.id ? 'bg-primary-500 text-white' : 'btn-secondary'
]" ]"
@click="setFromBase(base.id)" @click="setFromBase(base.id)"
> >
@ -44,7 +44,7 @@
:key="'to-' + base.id" :key="'to-' + base.id"
:class="[ :class="[
'px-3 py-2 text-sm rounded transition-all', 'px-3 py-2 text-sm rounded transition-all',
toBase === base.id ? 'bg-primary text-white' : 'btn-secondary' toBase === base.id ? 'bg-primary-500 text-white' : 'btn-secondary'
]" ]"
@click="setToBase(base.id)" @click="setToBase(base.id)"
> >

View File

@ -176,7 +176,7 @@
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<span class="text-xs text-tertiary min-w-[40px]">完整:</span> <span class="text-xs text-tertiary min-w-[40px]">完整:</span>
<code class="text-sm text-primary bg-primary/10 px-1 rounded break-all"> <code class="text-sm text-primary-600 bg-primary-100 px-1 rounded break-all">
{{ match[0] || '' }} {{ match[0] || '' }}
</code> </code>
</div> </div>
@ -187,7 +187,7 @@
class="flex items-start gap-2" class="flex items-start gap-2"
> >
<span class="text-xs text-tertiary min-w-[40px]"> {{ group }}:</span> <span class="text-xs text-tertiary min-w-[40px]"> {{ group }}:</span>
<code class="text-sm text-primary bg-primary/10 px-1 rounded break-all"> <code class="text-sm text-primary-600 bg-primary-100 px-1 rounded break-all">
{{ match[group] || '(空)' }} {{ match[group] || '(空)' }}
</code> </code>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -99,114 +99,54 @@ export default {
unicode_decode: 'Unicode Decode' unicode_decode: 'Unicode Decode'
}, },
chrome_bookmark_recovery: { chrome_bookmark_recovery: {
title: 'Chrome Bookmarks Recovery Tool', title: 'Chrome Bookmark Recovery',
description: 'Recover your lost Chrome bookmarks from local backup files or Google server', description: 'Chrome browser bookmark file recovery tool',
upload_file: 'Upload Bookmark File',
// 标签页 file_analysis: 'File Analysis',
windows: 'Windows', bookmark_list: 'Bookmark List',
mac: 'Mac', export_bookmarks: 'Export Bookmarks',
linux: 'Linux', recovery_success: 'Recovery Successful',
google: 'Google Server', file_format_error: 'File Format Error'
// 文件上传区域
fileUpload: {
title: 'Choose a file or drag it here',
dragHint: 'Drag Chrome bookmark files here',
chooseFile: 'Choose file',
dropHint: 'Search with the keyword of "Bookmarks" and select all the files found.',
loading: 'Loading...',
ready: 'Download ready',
downloaded: 'Downloaded',
error: 'Error! The file is invalid. Try again!',
bookmarksFound: 'bookmarks found'
}, },
seal_generator: {
// 操作系统说明 title: 'Seal Generator',
instructions: { description: 'Electronic seal creation tool, supports company and personal seals',
windows: { basic_settings: 'Basic Settings',
title: 'Here is how to recover your lost bookmarks (Windows):', text_settings: 'Text Settings',
steps: [ border_settings: 'Border Settings',
'Copy C:\\Users\\%username%\\AppData\\Local\\Google\\Chrome\\User Data into File Explorer.', seal_type: 'Seal Type',
'In search bar, type Bookmarks, you will see a list of files named Bookmarks and/or Bookmarks.bak. (Note: If there is more than one user using the same Chrome, bookmarks from other users will be listed too)', company_round: 'Company Round Seal',
'Select all the files with mouse and drag them to the block below.', company_oval: 'Company Oval Seal',
'Download all the HTML files.', personal_square: 'Personal Square Seal',
'Open each HTML file with Chrome and determine the HTML file that contains your bookmarks. (Note: The largest file is most likely the correct one)', personal_round: 'Personal Round Seal',
'In your Chrome browser, click the Chrome menu icon and go to Bookmarks > Bookmark Manager.', seal_size: 'Seal Size',
'Click the menu icon beside search bar and click "Import Bookmarks".', seal_color: 'Seal Color',
'Select the HTML file that contains your bookmarks.', background_color: 'Background Color',
'Your bookmarks should now be imported back to Chrome.' main_text: 'Main Text',
] center_text: 'Center Text',
}, header_text: 'Header Text',
mac: { sub_text: 'Sub Text',
title: 'Here is how to recover your lost bookmarks (Mac):', font_size: 'Font Size',
steps: [ main_font_size: 'Main Font Size',
'In the Mac menu bar at the top of the screen, click Go.', center_font_size: 'Center Font Size',
'Select Go to Folder.', outer_border_width: 'Outer Border Width',
'Type ~/Library/Application Support/Google/Chrome and click Go.', inner_border_width: 'Inner Border Width',
'In search bar, type Bookmarks, you will see a list of files named Bookmarks and/or Bookmarks.bak. (Note: If there is more than one user using the same Chrome, bookmarks from other users will be listed too)', show_star: 'Show Star',
'Select all the files with mouse and drag them to the block below.', generate_seal: 'Generate Seal',
'Download all the HTML files.', download_seal: 'Download Seal',
'Open each HTML file with Chrome and determine the HTML file that contains your bookmarks. (Note: The largest file is most likely the correct one)', clear_all: 'Clear All',
'In your Chrome browser, click the Chrome menu icon and go to Bookmarks > Bookmark Manager.', seal_preview: 'Seal Preview',
'Click the menu icon beside search bar and click "Import Bookmarks".', company_name_placeholder: 'Company Name',
'Select the HTML file that contains your bookmarks.', personal_name_placeholder: 'Name',
'Your bookmarks should now be imported back to Chrome.' center_text_placeholder: 'Seal',
] header_text_placeholder: 'e.g., Head Office, Branch',
}, sub_text_placeholder: 'e.g., Co., Ltd., Corp.',
linux: { preview_tip: 'Please fill in seal information and click generate',
title: 'Here is how to recover your lost bookmarks (Linux):', usage_instructions: 'Usage Instructions',
steps: [ company_seal_features: 'Company Seal Features',
'Open your file explorer (Nautilus, Dolphin ...).', personal_seal_features: 'Personal Seal Features',
'Go to ~/.config/google-chrome (Use Ctrl+L on Nautilus).', important_notice: 'Important Notice',
'Search for Bookmarks*, you will see a list of files named Bookmarks and/or Bookmarks.bak. (Note: If there is more than one user using the same Chrome, bookmarks from other users will be listed too)', legal_notice: 'The seals generated by this tool are for learning and testing purposes only. Do not use for illegal purposes. Official company seals need to be registered with relevant authorities.'
'Select all the files with mouse and drag them to the block below.',
'Download all the HTML files.',
'Open each HTML file with Chrome and determine the HTML file that contains your bookmarks. (Note: The largest file is most likely the correct one)',
'In your Chrome browser, click the Chrome menu icon and go to Bookmarks > Bookmark Manager.',
'Click the menu icon beside search bar and click "Import Bookmarks".',
'Select the HTML file that contains your bookmarks.',
'Your bookmarks should now be imported back to Chrome.'
]
},
google: {
title: 'If you are sure that Chrome sync data stored in Google server still have your bookmarks, here is how to retrieve them from Google server:',
steps: [
'Go to Google Takeout and sign in to the Google Account used for Chrome Sync.',
'Click the "Select None" button.',
'Check "Chrome" in product list.',
'Expand "All Chrome data types".',
'Click the "Select Chrome data" option.',
'In the popup, check the "Bookmarks" option and uncheck the rest.',
'Scroll down to the bottom of the page and click "Next" button.',
'Click the "Create archive" button.',
'Download and unzip the archive.',
'In the unzipped folder, navigate to "Chrome" folder. The HTML file named "Bookmarks" is the one we need.',
'In your Chrome browser, click the Chrome menu icon and go to Bookmarks > Bookmark Manager.',
'Click the menu icon beside search bar and click "Import Bookmarks".',
'Select the HTML file that contains your bookmarks.',
'Your bookmarks should now be imported back to Chrome.'
]
}
},
// 其他恢复方式
otherMethods: {
title: 'Other ways to recover bookmarks',
personalBackup: {
title: 'Personal backup',
content: 'In the case when backups of your computer are available, find the bookmarks files in the backups, upload them to this tool and continue with the steps above.'
},
oldDevices: {
title: 'Recover from old devices',
content: 'Do you have a device (e.g: old laptop) that is signed in to your Google Account and it hasn\'t been synced since the moment your bookmarks were lost? If so, find the files in this device, upload them to this tool and continue with the steps above.'
},
tipsAndTricks: {
title: 'Tips and tricks',
content: 'You can use this tool or Export Bookmarks function in Bookmark Manager to generate HTML file that can be used as archive/backup of your bookmarks. Regularly making backup is useful in case your bookmarks get lost again in the future.'
}
},
disclaimer: 'Disclaimer: this is not an official Google product.'
} }
} }
} }

View File

@ -550,114 +550,54 @@ export default {
validate: '验证' validate: '验证'
}, },
chrome_bookmark_recovery: { chrome_bookmark_recovery: {
title: 'Chrome书签恢复工具', title: 'Chrome书签恢复',
description: '恢复丢失的Chrome书签支持从本地备份文件或Google服务器导入', description: 'Chrome浏览器书签文件恢复工具',
upload_file: '上传书签文件',
// 标签页 file_analysis: '文件分析',
windows: 'Windows', bookmark_list: '书签列表',
mac: 'Mac', export_bookmarks: '导出书签',
linux: 'Linux', recovery_success: '恢复成功',
google: 'Google服务器', file_format_error: '文件格式错误'
// 文件上传区域
fileUpload: {
title: '选择文件或拖拽到此处',
dragHint: '拖拽Chrome书签文件到此处',
chooseFile: '选择文件',
dropHint: '使用关键词"Bookmarks"搜索并选择找到的所有文件',
loading: '加载中...',
ready: '准备下载',
downloaded: '已下载',
error: '错误!文件无效,请重试!',
bookmarksFound: '个书签'
}, },
seal_generator: {
// 操作系统说明 title: '印章生成器',
instructions: { description: '电子印章制作工具,支持公司印章和个人印章',
windows: { basic_settings: '基础参数',
title: '以下是恢复丢失书签的方法 (Windows):', text_settings: '文字设置',
steps: [ border_settings: '边框设置',
'复制 C:\\Users\\%username%\\AppData\\Local\\Google\\Chrome\\User Data 到文件资源管理器', seal_type: '印章样式',
'在搜索栏中输入 Bookmarks你会看到名为 Bookmarks 和/或 Bookmarks.bak 的文件列表注意如果有多个用户使用同一个Chrome其他用户的书签也会被列出', company_round: '公司圆章',
'用鼠标选择所有文件并拖拽到下面的区域', company_oval: '公司椭圆章',
'下载所有HTML文件', personal_square: '个人方章',
'用Chrome打开每个HTML文件确定包含你的书签的HTML文件注意最大的文件很可能是正确的', personal_round: '个人圆章',
'在Chrome浏览器中点击Chrome菜单图标进入 书签 > 书签管理器', seal_size: '印章大小',
'点击搜索栏旁边的菜单图标,点击"导入书签"', seal_color: '印章颜色',
'选择包含你的书签的HTML文件', background_color: '背景颜色',
'你的书签现在应该已导入回Chrome' main_text: '主文字',
] center_text: '中心文字',
}, header_text: '抬头文字',
mac: { sub_text: '副文字',
title: '以下是恢复丢失书签的方法 (Mac):', font_size: '字体大小',
steps: [ main_font_size: '主文字大小',
'在屏幕顶部的Mac菜单栏中点击前往', center_font_size: '中心文字大小',
'选择前往文件夹', outer_border_width: '外边线宽度',
'输入 ~/Library/Application Support/Google/Chrome 并点击前往', inner_border_width: '内边线宽度',
'在搜索栏中输入 Bookmarks你会看到名为 Bookmarks 和/或 Bookmarks.bak 的文件列表注意如果有多个用户使用同一个Chrome其他用户的书签也会被列出', show_star: '显示五角星',
'用鼠标选择所有文件并拖拽到下面的区域', generate_seal: '生成印章',
'下载所有HTML文件', download_seal: '下载印章',
'用Chrome打开每个HTML文件确定包含你的书签的HTML文件注意最大的文件很可能是正确的', clear_all: '清空',
'在Chrome浏览器中点击Chrome菜单图标进入 书签 > 书签管理器', seal_preview: '印章预览',
'点击搜索栏旁边的菜单图标,点击"导入书签"', company_name_placeholder: '公司名称',
'选择包含你的书签的HTML文件', personal_name_placeholder: '姓名',
'你的书签现在应该已导入回Chrome' center_text_placeholder: '印',
] header_text_placeholder: '例:总公司、分公司',
}, sub_text_placeholder: '例:有限公司、股份有限公司',
linux: { preview_tip: '请填写印章信息并点击生成',
title: '以下是恢复丢失书签的方法 (Linux):', usage_instructions: '使用说明',
steps: [ company_seal_features: '公司印章功能',
'打开你的文件管理器Nautilus, Dolphin ...', personal_seal_features: '个人印章功能',
'进入 ~/.config/google-chrome在Nautilus中使用Ctrl+L', important_notice: '重要提示',
'搜索 Bookmarks*,你会看到名为 Bookmarks 和/或 Bookmarks.bak 的文件列表注意如果有多个用户使用同一个Chrome其他用户的书签也会被列出', legal_notice: '本工具生成的印章仅供学习和测试使用,请勿用于非法用途。正式的公司印章需要到相关部门进行备案注册。'
'用鼠标选择所有文件并拖拽到下面的区域',
'下载所有HTML文件',
'用Chrome打开每个HTML文件确定包含你的书签的HTML文件注意最大的文件很可能是正确的',
'在Chrome浏览器中点击Chrome菜单图标进入 书签 > 书签管理器',
'点击搜索栏旁边的菜单图标,点击"导入书签"',
'选择包含你的书签的HTML文件',
'你的书签现在应该已导入回Chrome'
]
},
google: {
title: '如果你确信存储在Google服务器中的Chrome同步数据仍有你的书签以下是从Google服务器检索它们的方法:',
steps: [
'前往 Google Takeout 并登录用于Chrome同步的Google账户',
'点击"全不选"按钮',
'在产品列表中勾选"Chrome"',
'展开"所有Chrome数据类型"',
'点击"选择Chrome数据"选项',
'在弹窗中,勾选"书签"选项并取消选择其余选项',
'滚动到页面底部并点击"下一步"按钮',
'点击"创建归档"按钮',
'下载并解压归档文件',
'在解压的文件夹中,导航到"Chrome"文件夹。名为"Bookmarks"的HTML文件就是我们需要的',
'在Chrome浏览器中点击Chrome菜单图标进入 书签 > 书签管理器',
'点击搜索栏旁边的菜单图标,点击"导入书签"',
'选择包含你的书签的HTML文件',
'你的书签现在应该已导入回Chrome'
]
}
},
// 其他恢复方式
otherMethods: {
title: '其他恢复书签的方法',
personalBackup: {
title: '个人备份',
content: '在计算机备份可用的情况下,在备份中找到书签文件,将它们上传到此工具并继续上述步骤。'
},
oldDevices: {
title: '从旧设备恢复',
content: '你是否有一台设备例如旧笔记本电脑已登录到你的Google账户并且自你的书签丢失以来没有同步过如果是这样在该设备中找到文件将它们上传到此工具并继续上述步骤。'
},
tipsAndTricks: {
title: '提示和技巧',
content: '你可以使用此工具或书签管理器中的"导出书签"功能来生成可用作书签存档/备份的HTML文件。定期制作备份对于防止将来再次丢失书签很有用。'
}
},
disclaimer: '免责声明这不是Google的官方产品。'
} }
} }
} }

View File

@ -2,7 +2,7 @@ import {
faCode, faExchangeAlt, faClock, faGlobe, faLink, faLock, faCode, faExchangeAlt, faClock, faGlobe, faLink, faLock,
faImage, faCogs, faFileCode, faKey, faFont, faImage, faCogs, faFileCode, faKey, faFont,
faCalendarAlt, faPalette, faEdit, faRuler, faNetworkWired, faCalendarAlt, faPalette, faEdit, faRuler, faNetworkWired,
faEraser, faCalculator, faFileAlt, faBookmark faEraser, faCalculator, faFileAlt, faBookmark, faStamp
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import type { Tool } from '@/types/tools' import type { Tool } from '@/types/tools'
@ -181,6 +181,12 @@ const tools: Tool[] = [
icon: faBookmark, icon: faBookmark,
category: ['common', 'file'], category: ['common', 'file'],
keywords: ['chrome书签', '书签恢复', '书签导入', '书签', 'chrome', 'bookmark', '书签备份', '书签导出', 'shuqian', 'sq', 'huifu', 'hf', 'chrome书签恢复', 'chromehuifu'] keywords: ['chrome书签', '书签恢复', '书签导入', '书签', 'chrome', 'bookmark', '书签备份', '书签导出', 'shuqian', 'sq', 'huifu', 'hf', 'chrome书签恢复', 'chromehuifu']
},
{
code: 'seal_generator',
icon: faStamp,
category: ['common', 'image'],
keywords: ['印章', '印章生成', '电子印章', '公司印章', '个人印章', '印章制作', 'seal', 'stamp', 'yinzhang', 'yz', 'yinzhangshengcheng', 'yzsc', 'gongsiyz', 'gerenyinzhang', 'seal generator']
} }
] ]

View File

@ -19,7 +19,8 @@ import {
faExternalLinkAlt, faCopy, faCheck, faCompress, faExpand, faExternalLinkAlt, faCopy, faCheck, faCompress, faExpand,
faTrash, faSync, faFolderOpen, faFolder, faSpinner, faTrash, faSync, faFolderOpen, faFolder, faSpinner,
faSave, faHistory, faEdit as faEditIcon, faTrashAlt, faSave, faHistory, faEdit as faEditIcon, faTrashAlt,
faSun, faMoon, faHome, faArrowLeft, faArrowUp, faClipboard faSun, faMoon, faHome, faArrowLeft, faArrowUp, faClipboard,
faStamp, faDownload, faEye, faInfoCircle, faExclamationTriangle
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { faStar as farStar } from '@fortawesome/free-regular-svg-icons' import { faStar as farStar } from '@fortawesome/free-regular-svg-icons'
@ -39,6 +40,7 @@ library.add(
faTrash, faSync, faFolderOpen, faFolder, faSpinner, faTrash, faSync, faFolderOpen, faFolder, faSpinner,
faSave, faHistory, faEditIcon, faTrashAlt, faSave, faHistory, faEditIcon, faTrashAlt,
faSun, faMoon, faHome, faArrowLeft, faArrowUp, faClipboard, faSun, faMoon, faHome, faArrowLeft, faArrowUp, faClipboard,
faStamp, faDownload, faEye, faInfoCircle, faExclamationTriangle,
farStar, faGithub farStar, faGithub
) )

View File

@ -150,7 +150,8 @@ const toolComponentMap: Record<string, () => Promise<any>> = {
image_to_ico: () => import('@/components/tools/ImageToIco.vue'), image_to_ico: () => import('@/components/tools/ImageToIco.vue'),
cron_generator: () => import('@/components/tools/CronGenerator.vue'), cron_generator: () => import('@/components/tools/CronGenerator.vue'),
http_tester: () => import('@/components/tools/HttpTester.vue'), http_tester: () => import('@/components/tools/HttpTester.vue'),
chrome_bookmark_recovery: () => import('@/components/tools/ChromeBookmarkRecovery.vue') chrome_bookmark_recovery: () => import('@/components/tools/ChromeBookmarkRecovery.vue'),
seal_generator: () => import('@/components/tools/SealGenerator.vue')
} }
// 加载工具组件 // 加载工具组件

290
test-seal.html Normal file
View File

@ -0,0 +1,290 @@
<!DOCTYPE html>
<html lang="zh">
<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;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 20px;
}
.form-group {
display: flex;
flex-direction: column;
}
label {
margin-bottom: 5px;
font-weight: bold;
}
input, select, button {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background: #6366f1;
color: white;
cursor: pointer;
border: none;
}
button:hover {
background: #5855eb;
}
.preview {
text-align: center;
padding: 20px;
background: #f9f9f9;
border-radius: 8px;
}
canvas {
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<div class="container">
<h1>🖃 印章生成器测试</h1>
<div class="controls">
<div class="form-group">
<label for="sealType">印章类型</label>
<select id="sealType">
<option value="company_round">公司圆章</option>
<option value="company_oval">公司椭圆章</option>
<option value="personal_square">个人方章</option>
<option value="personal_round">个人圆章</option>
</select>
</div>
<div class="form-group">
<label for="mainText">主文字</label>
<input type="text" id="mainText" placeholder="输入公司名称或姓名" value="子归云科技">
</div>
<div class="form-group">
<label for="centerText">中心文字</label>
<input type="text" id="centerText" placeholder="印" value="印" maxlength="2">
</div>
<div class="form-group">
<label for="sealSize">印章大小</label>
<input type="range" id="sealSize" min="200" max="600" value="300">
<span id="sizeValue">300px</span>
</div>
<div class="form-group">
<label for="sealColor">印章颜色</label>
<input type="color" id="sealColor" value="#FF0000">
</div>
<div class="form-group">
<button onclick="generateSeal()">🎨 生成印章</button>
</div>
</div>
<div class="preview">
<h3>印章预览</h3>
<canvas id="sealCanvas" width="300" height="300"></canvas>
<br><br>
<button onclick="downloadSeal()" style="display:none;" id="downloadBtn">📥 下载印章</button>
</div>
</div>
<script>
const canvas = document.getElementById('sealCanvas');
const ctx = canvas.getContext('2d');
const sizeSlider = document.getElementById('sealSize');
const sizeValue = document.getElementById('sizeValue');
// 更新大小显示
sizeSlider.addEventListener('input', function() {
sizeValue.textContent = this.value + 'px';
canvas.width = this.value;
canvas.height = this.value;
});
function generateSeal() {
const sealType = document.getElementById('sealType').value;
const mainText = document.getElementById('mainText').value;
const centerText = document.getElementById('centerText').value;
const sealSize = parseInt(document.getElementById('sealSize').value);
const sealColor = document.getElementById('sealColor').value;
if (!mainText.trim()) {
alert('请输入主文字!');
return;
}
// 清空画布
ctx.clearRect(0, 0, sealSize, sealSize);
// 设置背景
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, sealSize, sealSize);
// 设置印章颜色
ctx.strokeStyle = sealColor;
ctx.fillStyle = sealColor;
const centerX = sealSize / 2;
const centerY = sealSize / 2;
if (sealType.startsWith('company')) {
drawCompanySeal(centerX, centerY, sealSize, mainText, centerText, sealColor);
} else {
drawPersonalSeal(centerX, centerY, sealSize, mainText, sealColor);
}
document.getElementById('downloadBtn').style.display = 'inline-block';
}
function drawCompanySeal(centerX, centerY, sealSize, mainText, centerText, sealColor) {
const radius = sealSize * 0.4;
// 绘制外边框
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
ctx.stroke();
// 绘制内边框
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(centerX, centerY, radius - 15, 0, 2 * Math.PI);
ctx.stroke();
// 绘制弧形主文字
drawCurvedText(mainText, centerX, centerY, radius - 25, 24);
// 绘制五角星
drawStar(centerX, centerY - radius * 0.1, 15);
// 绘制中心文字
if (centerText.trim()) {
ctx.font = 'bold 40px SimSun, serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(centerText, centerX, centerY + radius * 0.2);
}
}
function drawPersonalSeal(centerX, centerY, sealSize, mainText, sealColor) {
const size = sealSize * 0.8;
const sealType = document.getElementById('sealType').value;
// 绘制边框
ctx.lineWidth = 3;
ctx.beginPath();
if (sealType === 'personal_round') {
ctx.arc(centerX, centerY, size / 2, 0, 2 * Math.PI);
} else {
const halfSize = size / 2;
ctx.rect(centerX - halfSize, centerY - halfSize, size, size);
}
ctx.stroke();
// 绘制文字
const text = mainText.trim();
const textLength = text.length;
const fontSize = 32;
ctx.font = `bold ${fontSize}px SimSun, serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
if (textLength === 2) {
// 两字:上下排列
ctx.fillText(text[0], centerX, centerY - fontSize * 0.6);
ctx.fillText(text[1], centerX, centerY + fontSize * 0.6);
} else if (textLength === 3) {
// 三字:品字形
ctx.fillText(text[0], centerX, centerY - fontSize * 0.8);
ctx.fillText(text[1], centerX - fontSize * 0.7, centerY + fontSize * 0.4);
ctx.fillText(text[2], centerX + fontSize * 0.7, centerY + fontSize * 0.4);
} else if (textLength === 4) {
// 四字:田字格
ctx.fillText(text[0], centerX - fontSize * 0.6, centerY - fontSize * 0.6);
ctx.fillText(text[1], centerX + fontSize * 0.6, centerY - fontSize * 0.6);
ctx.fillText(text[2], centerX - fontSize * 0.6, centerY + fontSize * 0.6);
ctx.fillText(text[3], centerX + fontSize * 0.6, centerY + fontSize * 0.6);
} else {
// 其他情况:单行
ctx.fillText(text, centerX, centerY);
}
}
function drawCurvedText(text, centerX, centerY, radius, fontSize) {
ctx.font = `${fontSize}px SimSun, serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const angleStep = (Math.PI * 1.4) / text.length;
const startOffset = -Math.PI * 0.7;
for (let i = 0; i < text.length; i++) {
const angle = startOffset + i * angleStep;
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle + Math.PI / 2);
ctx.fillText(text[i], 0, 0);
ctx.restore();
}
}
function drawStar(centerX, centerY, radius) {
const spikes = 5;
const outerRadius = radius;
const innerRadius = radius * 0.4;
ctx.beginPath();
for (let i = 0; i < spikes * 2; i++) {
const angle = (i * Math.PI) / spikes - Math.PI / 2;
const r = i % 2 === 0 ? outerRadius : innerRadius;
const x = centerX + Math.cos(angle) * r;
const y = centerY + Math.sin(angle) * r;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.fill();
}
function downloadSeal() {
const link = document.createElement('a');
link.download = `seal-${Date.now()}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
}
// 初始生成一个示例印章
setTimeout(() => {
generateSeal();
}, 100);
</script>
</body>
</html>