stage
This commit is contained in:
@@ -31,3 +31,4 @@ nn/models/*.pt
|
||||
build/bin
|
||||
frontend/node_modules
|
||||
frontend/dist
|
||||
frontend/public
|
||||
|
||||
+3
-3
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<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>
|
||||
<script>
|
||||
<!-- <script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
</script> -->
|
||||
</head>
|
||||
<body>
|
||||
<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 |
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect } from 'preact/hooks'
|
||||
import { message } from '../utils/toast'
|
||||
import Pagination from './Pagination'
|
||||
import Modal from './Modal'
|
||||
import ImagePreview from './ImagePreview'
|
||||
import { message } from '../utils/toast'
|
||||
import type { HistoryItem } from '../preact'
|
||||
|
||||
|
||||
const formatDate = (timestamp: number) => {
|
||||
const date = new Date(timestamp * 1000)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
@@ -104,7 +104,7 @@ const History = () => {
|
||||
{historyList.map((item: HistoryItem) =>
|
||||
<div key={item.id} class="history-card">
|
||||
<div class="card-left">
|
||||
<img src="/images/0012.jpg" alt="" />
|
||||
<ImagePreview filename={item.img} />
|
||||
</div>
|
||||
<div class="card-right">
|
||||
<div class="right-top">
|
||||
@@ -122,9 +122,19 @@ const History = () => {
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
<div class="history-pagination">
|
||||
<Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
|
||||
</div>
|
||||
{historyList.length ?
|
||||
<div class="history-pagination">
|
||||
<Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
|
||||
</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>
|
||||
<Modal open={showDelete} title="信息" onClick={handleDeleteOk} onClose={handleDeleteClose}>
|
||||
<p>确定要删除当前记录?</p>
|
||||
|
||||
@@ -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
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useRef } from 'preact/hooks'
|
||||
import { message } from '../utils/toast'
|
||||
import type { DetectResult } from '../preact'
|
||||
|
||||
import ImagePreview from './ImagePreview'
|
||||
import { message } from '../utils/toast'
|
||||
|
||||
const Main = () => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
@@ -11,14 +11,53 @@ const Main = () => {
|
||||
|
||||
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) => {
|
||||
console.log('handleFileChange')
|
||||
const target = e.target as HTMLInputElement
|
||||
const file = target.files?.[0]
|
||||
if (file) {
|
||||
console.log('文件大小:', file.size / 1024)
|
||||
|
||||
const reader = new FileReader()
|
||||
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 result = await (window as any).go.main.App.UploadImage(
|
||||
@@ -94,7 +133,7 @@ const Main = () => {
|
||||
>
|
||||
{fileSrc.length > 0 && step == 1 ?
|
||||
<div id="previewContent">
|
||||
<img src={`/images/${fileSrc}`} id="previewImage" />
|
||||
<ImagePreview filename={fileSrc} id="previewImage" />
|
||||
<button onClick={handleRemovePhoto}>
|
||||
❌ 移除照片
|
||||
</button>
|
||||
@@ -147,13 +186,13 @@ const Main = () => {
|
||||
</div>
|
||||
<div class={`result${step != 3 ? ' hidden' : ''}`}>
|
||||
<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" />
|
||||
</svg>
|
||||
<h2>完成!</h2>
|
||||
</div>
|
||||
<div class="result-main">
|
||||
<img />
|
||||
<ImagePreview filename={fileSrc} />
|
||||
<div class="result-word">
|
||||
<div>
|
||||
<h3>{detectResult?.name}</h3>
|
||||
|
||||
@@ -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 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 (
|
||||
<nav class="app-navbar animate-fade-in">
|
||||
<nav class="app-navbar">
|
||||
<div class="navbar-container">
|
||||
<div class="container-left">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d={svgPath} />
|
||||
</svg>
|
||||
<span class="font-heading font-semibold text-xl text-text">分类喵</span>
|
||||
<span>分类<span>喵</span></span>
|
||||
</div>
|
||||
<div class="container-right">
|
||||
<a href="/">主页</a>
|
||||
<a href="/history">历史记录</a>
|
||||
<a href="/" onClick={handleNavColor}>主页</a>
|
||||
<a href="/history" onClick={handleNavColor}>历史记录</a>
|
||||
<button onClick={handleClick}>查看代码</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router, Route } from "preact-router"
|
||||
import { Navbar } from "../components/Navbar"
|
||||
import Navbar from "../components/Navbar"
|
||||
import Main from "../components/Main"
|
||||
import History from "../components/History"
|
||||
import { Footer } from "../components/Footer"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
html
|
||||
font-size: 16px
|
||||
text-align: center
|
||||
color: white
|
||||
|
||||
@@ -8,6 +9,39 @@ body
|
||||
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI"
|
||||
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-family: "Nunito"
|
||||
font-style: normal
|
||||
@@ -44,8 +78,7 @@ body
|
||||
display: inline-flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
width: 100%
|
||||
margin: 0 auto
|
||||
width: calc(100% - 2.4rem)
|
||||
padding: 0.6rem 1.2rem
|
||||
background-color: white
|
||||
backdrop-filter: blur(8px)
|
||||
@@ -57,13 +90,20 @@ body
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 0.75rem
|
||||
cursor: pointer
|
||||
|
||||
.icon
|
||||
width: 2rem
|
||||
height: 2rem
|
||||
color: var(--primary)
|
||||
|
||||
> span
|
||||
font-weight: 600
|
||||
font-size: 1.25rem
|
||||
color: var(--title)
|
||||
|
||||
> span
|
||||
color: var(--primary)
|
||||
|
||||
.container-right
|
||||
display: flex
|
||||
align-items: center
|
||||
@@ -76,8 +116,8 @@ body
|
||||
font-size: 0.9375rem
|
||||
cursor: pointer
|
||||
|
||||
&:hover
|
||||
color: var(--primary)
|
||||
.active
|
||||
color: var(--primary)
|
||||
|
||||
> button
|
||||
width: 7rem
|
||||
@@ -183,6 +223,8 @@ body
|
||||
color: var(--textsub)
|
||||
|
||||
#previewContent
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin-top: 0.5rem
|
||||
|
||||
> img
|
||||
@@ -213,10 +255,9 @@ body
|
||||
width: 4rem
|
||||
height: 4rem
|
||||
margin: 0 auto 1rem auto
|
||||
border-width: 4px
|
||||
border-color: var(--primary)
|
||||
border: 0.25rem solid var(--primary)
|
||||
border-top-color: transparent
|
||||
border-radius: 4rem
|
||||
border-radius: 50%
|
||||
animation: spin 1s linear infinite
|
||||
|
||||
@keyframes spin
|
||||
@@ -257,6 +298,7 @@ body
|
||||
|
||||
.result
|
||||
width: 35rem
|
||||
height: 1.5rem
|
||||
margin: 0 auto
|
||||
|
||||
.result-complete
|
||||
@@ -275,7 +317,7 @@ body
|
||||
> h2
|
||||
font-family: "SANS_SC_BOLD"
|
||||
font-weight: bold
|
||||
font-size: 1.5rem
|
||||
font-size: 1.375rem
|
||||
color: var(--title)
|
||||
|
||||
.result-main
|
||||
@@ -286,11 +328,11 @@ body
|
||||
background-color: white
|
||||
border-radius: 1rem
|
||||
border: 1px solid var(--color-border)
|
||||
gap: 2rem
|
||||
gap: 1.5rem
|
||||
|
||||
> img
|
||||
width: 8rem
|
||||
height: 8rem
|
||||
max-width: 8rem
|
||||
max-height: 8rem
|
||||
background-color: #333
|
||||
border-radius: 0.5rem
|
||||
|
||||
@@ -299,7 +341,7 @@ body
|
||||
flex-direction: column
|
||||
justify-content: flex-start
|
||||
align-items: flex-start
|
||||
width: calc(100% - 10rem)
|
||||
width: calc(100% - 9.5rem)
|
||||
|
||||
> div
|
||||
display: flex
|
||||
@@ -377,7 +419,6 @@ body
|
||||
justify-content: center
|
||||
position: fixed
|
||||
width: 100vw
|
||||
height: 5rem
|
||||
background-color: #131313
|
||||
padding: 2rem 1rem
|
||||
bottom: 0
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
width: calc(100% - 20rem)
|
||||
height: 5rem
|
||||
height: 3rem
|
||||
padding: 1rem
|
||||
margin-bottom: 0.6rem
|
||||
background-color: #FFFFFF
|
||||
@@ -49,16 +49,22 @@
|
||||
align-items: center
|
||||
width: 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
|
||||
max-width: 100%
|
||||
max-height: 100%
|
||||
width: 100%
|
||||
height: 100%
|
||||
object-fit: cover
|
||||
|
||||
.card-right
|
||||
display: flex
|
||||
flex-direction: column
|
||||
width: calc(100% - 5rem)
|
||||
width: calc(100% - 4rem)
|
||||
height: 5rem
|
||||
|
||||
.right-top
|
||||
@@ -112,4 +118,21 @@
|
||||
width: calc(100% - 20rem)
|
||||
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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user