chore: 前端工程化 + Git hooks + 对齐 PathEditor 规范

- 新增 .gitattributes(CRLF 统一)+ rust-toolchain.toml
- 新增 Prettier + ESLint + markdownlint 配置
- 新增 Husky Git hooks(pre-commit lint-staged + commit-msg commitlint)
- 新增 vitest 前端测试(12 tests, utils/qrText.ts)
- 新增 @ 路径别名(vite + tsconfig)
- 新增 ROADMAP / SUPPORT / CODEOWNERS / FUNDING / dependabot
- 更新 .gitignore + .editorconfig
- 更新 package.json(新增 lint/format/test 脚本)
- 全项目 prettier 格式化 + eslint 通过
- 更新 CLAUDE.md + README.md
This commit is contained in:
2026-06-19 19:42:13 +08:00
parent ce8063431e
commit c3956f0f36
40 changed files with 4034 additions and 148 deletions
+35 -12
View File
@@ -30,7 +30,10 @@ export default function ExportPanel() {
filters: [{ name: 'PNG 图片', extensions: ['png'] }],
defaultPath: 'qrcode.png',
});
if (!filePath) { setExporting(false); return; }
if (!filePath) {
setExporting(false);
return;
}
const bytes: number[] = await invoke('export_png', {
text: buildEncodedText(state.mode, state.formData),
@@ -74,7 +77,12 @@ export default function ExportPanel() {
<select
value={state.config.level}
onChange={e => dispatch({ type: 'SET_CONFIG', payload: { level: e.target.value as QrConfig['level'] } })}
onChange={(e) =>
dispatch({
type: 'SET_CONFIG',
payload: { level: e.target.value as QrConfig['level'] },
})
}
className="w-full mt-1 px-2 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm outline-none focus:ring-2 focus:ring-blue-500/30"
>
<option value="L">L 7%</option>
@@ -87,9 +95,13 @@ export default function ExportPanel() {
<label className="text-xs text-gray-600 dark:text-gray-400">
: {state.config.moduleSize}px
<input
type="range" min={2} max={20}
type="range"
min={2}
max={20}
value={state.config.moduleSize}
onChange={e => dispatch({ type: 'SET_CONFIG', payload: { moduleSize: +e.target.value } })}
onChange={(e) =>
dispatch({ type: 'SET_CONFIG', payload: { moduleSize: +e.target.value } })
}
className="w-full mt-1 accent-blue-500"
/>
</label>
@@ -97,23 +109,34 @@ export default function ExportPanel() {
<label className="text-xs text-gray-600 dark:text-gray-400">
: {state.config.margin}
<input
type="range" min={1} max={10}
type="range"
min={1}
max={10}
value={state.config.margin}
onChange={e => dispatch({ type: 'SET_CONFIG', payload: { margin: +e.target.value } })}
onChange={(e) => dispatch({ type: 'SET_CONFIG', payload: { margin: +e.target.value } })}
className="w-full mt-1 accent-blue-500"
/>
</label>
<button onClick={handleCopySvg} disabled={!state.preview}
className="w-full py-2 rounded-lg bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-40 transition-all">
<button
onClick={handleCopySvg}
disabled={!state.preview}
className="w-full py-2 rounded-lg bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-40 transition-all"
>
SVG
</button>
<button onClick={handleExportPng} disabled={!state.preview || exporting}
className="w-full py-2 rounded-lg bg-green-500 text-white text-sm font-medium hover:bg-green-600 disabled:opacity-40 transition-all">
<button
onClick={handleExportPng}
disabled={!state.preview || exporting}
className="w-full py-2 rounded-lg bg-green-500 text-white text-sm font-medium hover:bg-green-600 disabled:opacity-40 transition-all"
>
{exporting ? '导出中...' : '导出 PNG'}
</button>
<button onClick={handleExportSvg} disabled={!state.preview}
className="w-full py-2 rounded-lg bg-purple-500 text-white text-sm font-medium hover:bg-purple-600 disabled:opacity-40 transition-all">
<button
onClick={handleExportSvg}
disabled={!state.preview}
className="w-full py-2 rounded-lg bg-purple-500 text-white text-sm font-medium hover:bg-purple-600 disabled:opacity-40 transition-all"
>
SVG
</button>
</div>