This commit is contained in:
2026-07-23 16:03:29 +08:00
parent 9f313a54b1
commit 44a7e48c60
12 changed files with 194 additions and 41 deletions
+1
View File
@@ -31,3 +31,4 @@ nn/models/*.pt
build/bin build/bin
frontend/node_modules frontend/node_modules
frontend/dist frontend/dist
frontend/public
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -3,9 +3,9 @@
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/> <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<script src="https://cdn.tailwindcss.com"></script> <!-- <script src="https://cdn.tailwindcss.com"></script> -->
<title>dissertation</title> <title>dissertation</title>
<script> <!-- <script>
tailwind.config = { tailwind.config = {
theme: { theme: {
extend: { extend: {
@@ -24,7 +24,7 @@
}, },
}, },
} }
</script> </script> -->
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

+14 -4
View File
@@ -1,10 +1,10 @@
import { useState, useEffect } from 'preact/hooks' import { useState, useEffect } from 'preact/hooks'
import { message } from '../utils/toast'
import Pagination from './Pagination' import Pagination from './Pagination'
import Modal from './Modal' import Modal from './Modal'
import ImagePreview from './ImagePreview'
import { message } from '../utils/toast'
import type { HistoryItem } from '../preact' import type { HistoryItem } from '../preact'
const formatDate = (timestamp: number) => { const formatDate = (timestamp: number) => {
const date = new Date(timestamp * 1000) const date = new Date(timestamp * 1000)
return date.toLocaleString('zh-CN', { return date.toLocaleString('zh-CN', {
@@ -104,7 +104,7 @@ const History = () => {
{historyList.map((item: HistoryItem) => {historyList.map((item: HistoryItem) =>
<div key={item.id} class="history-card"> <div key={item.id} class="history-card">
<div class="card-left"> <div class="card-left">
<img src="/images/0012.jpg" alt="" /> <ImagePreview filename={item.img} />
</div> </div>
<div class="card-right"> <div class="card-right">
<div class="right-top"> <div class="right-top">
@@ -122,9 +122,19 @@ const History = () => {
</div> </div>
</div>) </div>)
} }
{historyList.length ?
<div class="history-pagination"> <div class="history-pagination">
<Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} /> <Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
</div> </div> : null
}
{!historyList.length ?
<div class="history-none">
<svg viewBox="0 0 24 24" fill="none" stroke="#c0c0c0" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z"/>
</svg>
<p></p>
</div> : null
}
</div> </div>
<Modal open={showDelete} title="信息" onClick={handleDeleteOk} onClose={handleDeleteClose}> <Modal open={showDelete} title="信息" onClick={handleDeleteOk} onClose={handleDeleteClose}>
<p></p> <p></p>
+27
View File
@@ -0,0 +1,27 @@
import { useState, useEffect } from 'preact/hooks'
interface Props {
filename: string
class?: string
id?: string
}
const ImagePreview = ({ filename, ...props }: Props) => {
const [src, setSrc] = useState<string>('')
useEffect(() => {
const loadImage = async () => {
const result = await (window as any).go.main.App.GetImage(filename)
if (result.code === 0) {
setSrc(result.data)
}
}
loadImage()
}, [filename])
if (!src) return <div {...props} />
return <img src={src} {...props} />
}
export default ImagePreview
+45 -6
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from 'preact/hooks' import { useState, useRef } from 'preact/hooks'
import { message } from '../utils/toast'
import type { DetectResult } from '../preact' import type { DetectResult } from '../preact'
import ImagePreview from './ImagePreview'
import { message } from '../utils/toast'
const Main = () => { const Main = () => {
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
@@ -11,14 +11,53 @@ const Main = () => {
const handleUploadClick = () => fileInputRef.current?.click() const handleUploadClick = () => fileInputRef.current?.click()
function loadImage(file: File): Promise<HTMLImageElement> {
return new Promise((resolve) => {
const img = new Image()
img.onload = () => resolve(img)
img.src = URL.createObjectURL(file)
})
}
const resizeImage = async(file: File, maxWidth = 224): Promise<File> => {
const img = await loadImage(file)
if (img.width <= maxWidth) {
return file
}
const canvas = document.createElement('canvas')
const ratio = maxWidth / img.width
canvas.width = maxWidth
canvas.height = Math.round(img.height * ratio)
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
return new Promise((resolve) => {
canvas.toBlob((blob: Blob | null) => {
if (!blob) {
resolve(file)
return
}
resolve(new File([blob], file.name, { type: file.type }))
}, file.type, 0.9)
})
}
const handleFileChange = (e: Event) => { const handleFileChange = (e: Event) => {
console.log('handleFileChange') console.log('handleFileChange')
const target = e.target as HTMLInputElement const target = e.target as HTMLInputElement
const file = target.files?.[0] const file = target.files?.[0]
if (file) { if (file) {
console.log('文件大小:', file.size / 1024)
const reader = new FileReader() const reader = new FileReader()
reader.onload = async function() { reader.onload = async function() {
const arrayBuffer = reader.result as ArrayBuffer const processedFile = await resizeImage(file, 224)
const arrayBuffer = await processedFile.arrayBuffer()
const uint8Array = new Uint8Array(arrayBuffer) const uint8Array = new Uint8Array(arrayBuffer)
const result = await (window as any).go.main.App.UploadImage( const result = await (window as any).go.main.App.UploadImage(
@@ -94,7 +133,7 @@ const Main = () => {
> >
{fileSrc.length > 0 && step == 1 ? {fileSrc.length > 0 && step == 1 ?
<div id="previewContent"> <div id="previewContent">
<img src={`/images/${fileSrc}`} id="previewImage" /> <ImagePreview filename={fileSrc} id="previewImage" />
<button onClick={handleRemovePhoto}> <button onClick={handleRemovePhoto}>
</button> </button>
@@ -147,13 +186,13 @@ const Main = () => {
</div> </div>
<div class={`result${step != 3 ? ' hidden' : ''}`}> <div class={`result${step != 3 ? ' hidden' : ''}`}>
<div class="result-complete"> <div class="result-complete">
<svg viewBox="0 0 24 24" fill="currentColor"> <svg viewBox="0 0 24 24" fill="#00b740ff">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</svg> </svg>
<h2></h2> <h2></h2>
</div> </div>
<div class="result-main"> <div class="result-main">
<img /> <ImagePreview filename={fileSrc} />
<div class="result-word"> <div class="result-word">
<div> <div>
<h3>{detectResult?.name}</h3> <h3>{detectResult?.name}</h3>
+17 -5
View File
@@ -1,23 +1,35 @@
export const Navbar = () => { const Navbar = () => {
const svgPath = "M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4.5 4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm9 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-9.5 3.5c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zm11 0c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zM12 18c-2.21 0-4 1.79-4 4h8c0-2.21-1.79-4-4-4z" const svgPath = "M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4.5 4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm9 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-9.5 3.5c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zm11 0c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zM12 18c-2.21 0-4 1.79-4 4h8c0-2.21-1.79-4-4-4z"
const handleClick = () => window.open('https://git.leonstack.com/owner/Collage-Images', '_blank') const handleClick = () => window.open('https://git.leonstack.com/owner/Collage-Images', '_blank')
const handleNavColor = (e: Event) => {
const current = e.currentTarget as HTMLElement
const parent = current.parentElement
if (parent) {
parent.querySelectorAll('a').forEach(a => a.classList.remove('active'))
}
current.classList.add('active')
}
return ( return (
<nav class="app-navbar animate-fade-in"> <nav class="app-navbar">
<div class="navbar-container"> <div class="navbar-container">
<div class="container-left"> <div class="container-left">
<svg class="icon" viewBox="0 0 24 24" fill="currentColor"> <svg class="icon" viewBox="0 0 24 24" fill="currentColor">
<path d={svgPath} /> <path d={svgPath} />
</svg> </svg>
<span class="font-heading font-semibold text-xl text-text"></span> <span><span></span></span>
</div> </div>
<div class="container-right"> <div class="container-right">
<a href="/"></a> <a href="/" onClick={handleNavColor}></a>
<a href="/history"></a> <a href="/history" onClick={handleNavColor}></a>
<button onClick={handleClick}></button> <button onClick={handleClick}></button>
</div> </div>
</div> </div>
</nav> </nav>
) )
} }
export default Navbar
+1 -1
View File
@@ -1,5 +1,5 @@
import { Router, Route } from "preact-router" import { Router, Route } from "preact-router"
import { Navbar } from "../components/Navbar" import Navbar from "../components/Navbar"
import Main from "../components/Main" import Main from "../components/Main"
import History from "../components/History" import History from "../components/History"
import { Footer } from "../components/Footer" import { Footer } from "../components/Footer"
+54 -13
View File
@@ -1,4 +1,5 @@
html html
font-size: 16px
text-align: center text-align: center
color: white color: white
@@ -8,6 +9,39 @@ body
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI" font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI"
overflow: hidden overflow: hidden
body, ul, ol, li, p, h1, h2, h3, h4, h5, h6,
form, fieldset, img, div, pre
margin: 0
padding: 0
outline: 0
border: 0
ul, li, ol
list-style-type: none
select, textarea, button
background: transparent
border: none
outline: none
button:focus,input:focus, code:focus, pre:focus, video
outline: none
button, i, img
cursor: pointer
user-select: none
-webkit-user-select: none
-moz-user-select: none
-khtml-user-select: none
-ms-user-select: none
a
text-decoration: none
color: #5470c6
.hidden
display: none
@font-face @font-face
font-family: "Nunito" font-family: "Nunito"
font-style: normal font-style: normal
@@ -44,8 +78,7 @@ body
display: inline-flex display: inline-flex
align-items: center align-items: center
justify-content: space-between justify-content: space-between
width: 100% width: calc(100% - 2.4rem)
margin: 0 auto
padding: 0.6rem 1.2rem padding: 0.6rem 1.2rem
background-color: white background-color: white
backdrop-filter: blur(8px) backdrop-filter: blur(8px)
@@ -57,13 +90,20 @@ body
display: flex display: flex
align-items: center align-items: center
gap: 0.75rem gap: 0.75rem
cursor: pointer
.icon .icon
width: 2rem width: 2rem
height: 2rem height: 2rem
color: var(--primary) color: var(--primary)
> span
font-weight: 600
font-size: 1.25rem
color: var(--title)
> span
color: var(--primary)
.container-right .container-right
display: flex display: flex
align-items: center align-items: center
@@ -76,7 +116,7 @@ body
font-size: 0.9375rem font-size: 0.9375rem
cursor: pointer cursor: pointer
&:hover .active
color: var(--primary) color: var(--primary)
> button > button
@@ -183,6 +223,8 @@ body
color: var(--textsub) color: var(--textsub)
#previewContent #previewContent
display: flex
flex-direction: column
margin-top: 0.5rem margin-top: 0.5rem
> img > img
@@ -213,10 +255,9 @@ body
width: 4rem width: 4rem
height: 4rem height: 4rem
margin: 0 auto 1rem auto margin: 0 auto 1rem auto
border-width: 4px border: 0.25rem solid var(--primary)
border-color: var(--primary)
border-top-color: transparent border-top-color: transparent
border-radius: 4rem border-radius: 50%
animation: spin 1s linear infinite animation: spin 1s linear infinite
@keyframes spin @keyframes spin
@@ -257,6 +298,7 @@ body
.result .result
width: 35rem width: 35rem
height: 1.5rem
margin: 0 auto margin: 0 auto
.result-complete .result-complete
@@ -275,7 +317,7 @@ body
> h2 > h2
font-family: "SANS_SC_BOLD" font-family: "SANS_SC_BOLD"
font-weight: bold font-weight: bold
font-size: 1.5rem font-size: 1.375rem
color: var(--title) color: var(--title)
.result-main .result-main
@@ -286,11 +328,11 @@ body
background-color: white background-color: white
border-radius: 1rem border-radius: 1rem
border: 1px solid var(--color-border) border: 1px solid var(--color-border)
gap: 2rem gap: 1.5rem
> img > img
width: 8rem max-width: 8rem
height: 8rem max-height: 8rem
background-color: #333 background-color: #333
border-radius: 0.5rem border-radius: 0.5rem
@@ -299,7 +341,7 @@ body
flex-direction: column flex-direction: column
justify-content: flex-start justify-content: flex-start
align-items: flex-start align-items: flex-start
width: calc(100% - 10rem) width: calc(100% - 9.5rem)
> div > div
display: flex display: flex
@@ -377,7 +419,6 @@ body
justify-content: center justify-content: center
position: fixed position: fixed
width: 100vw width: 100vw
height: 5rem
background-color: #131313 background-color: #131313
padding: 2rem 1rem padding: 2rem 1rem
bottom: 0 bottom: 0
+28 -5
View File
@@ -34,7 +34,7 @@
display: flex display: flex
justify-content: space-between justify-content: space-between
width: calc(100% - 20rem) width: calc(100% - 20rem)
height: 5rem height: 3rem
padding: 1rem padding: 1rem
margin-bottom: 0.6rem margin-bottom: 0.6rem
background-color: #FFFFFF background-color: #FFFFFF
@@ -49,16 +49,22 @@
align-items: center align-items: center
width: 3rem width: 3rem
height: 3rem height: 3rem
background-color: #333333 background-image: conic-gradient(#f0f0f0 0% 25%, transparent 0% 50%, #f0f0f0 50% 75%, transparent 0% 100%)
background-size: 20px 20px
opacity: 1
border-radius: 0.5rem
overflow: hidden
position: relative
> img > img
max-width: 100% width: 100%
max-height: 100% height: 100%
object-fit: cover
.card-right .card-right
display: flex display: flex
flex-direction: column flex-direction: column
width: calc(100% - 5rem) width: calc(100% - 4rem)
height: 5rem height: 5rem
.right-top .right-top
@@ -112,4 +118,21 @@
width: calc(100% - 20rem) width: calc(100% - 20rem)
margin-top: 0.6rem margin-top: 0.6rem
.history-none
display: flex
flex-direction: column
justify-content: center
align-items: center
align-content: center
margin-top: 12rem
> svg
width: 4rem
height: 4rem
> p
text-align: center
font-size: 0.825rem
color: #c0c0c0