frontend finishded

This commit is contained in:
2026-07-22 17:48:49 +08:00
parent 6f6bde4a24
commit 9f313a54b1
17 changed files with 284 additions and 76 deletions
BIN
View File
Binary file not shown.
+20 -2
View File
@@ -12,6 +12,10 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
var (
publicImagePath = "./frontend/public/images"
)
func NewApp() *App { func NewApp() *App {
return &App{} return &App{}
} }
@@ -33,7 +37,7 @@ func (a *App) GormDB() (*gorm.DB, error) {
} }
func (a *App) UploadImage(data []byte, filename string) Response { func (a *App) UploadImage(data []byte, filename string) Response {
uploadsDir := "./uploads" uploadsDir := publicImagePath
err := os.MkdirAll(uploadsDir, 0755) err := os.MkdirAll(uploadsDir, 0755)
if err != nil { if err != nil {
return Response{Code: 1, Message: "failed", Data: err.Error()} return Response{Code: 1, Message: "failed", Data: err.Error()}
@@ -52,7 +56,7 @@ func (a *App) UploadImage(data []byte, filename string) Response {
} }
func (a *App) GetImage(filename string) Response { func (a *App) GetImage(filename string) Response {
filePath := filepath.Join("./uploads", filename) filePath := filepath.Join(publicImagePath, filename)
data, err := os.ReadFile(filePath) data, err := os.ReadFile(filePath)
if err != nil { if err != nil {
@@ -139,3 +143,17 @@ func (a *App) DeleteOneHistory(id uint) Response {
return Response{Code: 0, Message: "success"} return Response{Code: 0, Message: "success"}
} }
func (a *App) ClearHistory() Response {
db, err := a.GormDB()
if err != nil {
return Response{Code: 1, Message: err.Error()}
}
result := db.Exec("DELETE FROM history_test")
if result.Error != nil {
return Response{Code: 1, Message: result.Error.Error()}
}
return Response{Code: 0, Message: "success"}
}
+1 -1
View File
@@ -1 +1 @@
d517c8a0f1aee8820be9290e2d54cb8b f535f0817b95f10a6229620a63d82985

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

+79 -38
View File
@@ -1,6 +1,9 @@
import { useState, useEffect } from 'preact/hooks' import { useState, useEffect } from 'preact/hooks'
import { message } from '../utils/toast' import { message } from '../utils/toast'
import Pagination from './Pagination' import Pagination from './Pagination'
import Modal from './Modal'
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)
@@ -14,20 +17,14 @@ const formatDate = (timestamp: number) => {
}) })
} }
interface IHistoryItem {
id: number
img: string
breed: number
date: number
name: string
brief: string
}
const History = () => { const History = () => {
const [page, setPage] = useState<number>(1) const [page, setPage] = useState<number>(1)
const pageSize = 5 const pageSize = 5
const [total, setTotal] = useState<number>(50) const [total, setTotal] = useState<number>(50)
const [historyList, setHistoryList] = useState<IHistoryItem[]>([]) const [historyList, setHistoryList] = useState<HistoryItem[]>([])
const [showDelete, setShowDelete] = useState<boolean>(false)
const [currentId, setCurrentId] = useState<number | null>(null)
const [showClear, setShowClear] = useState<boolean>(false)
useEffect(() => { useEffect(() => {
if (!(window as any).go?.main?.App?.GetHistory) { if (!(window as any).go?.main?.App?.GetHistory) {
@@ -52,46 +49,90 @@ const History = () => {
fetchData(page) fetchData(page)
} }
const handleDelete = async(id: number) => { const handleShowDelete = (id: number) => {
const result = await (window as any).go.main.App.DeleteOneHistory(id) setCurrentId(id)
setShowDelete(true)
}
const handleDeleteOk = async() => {
setShowDelete(false)
if (!currentId) {
message.error('currentId为空')
return
}
const result = await (window as any).go.main.App.DeleteOneHistory(currentId)
if (result.code === 0) { if (result.code === 0) {
message.success('删除成功') message.success('删除成功')
setCurrentId(null)
fetchData(page) fetchData(page)
} else if (result.code === 1) { } else if (result.code === 1) {
message.error(result.message) message.error(result.message)
} }
} }
const handleDeleteClose = () => {
setShowDelete(false)
setCurrentId(null)
}
const handleShowModal = () => setShowClear(true)
const handleClearOk = async() => {
setShowClear(false)
const result = await (window as any).go.main.App.ClearHistory()
if (result.code === 0) {
message.success('已清空历史')
setCurrentId(null)
fetchData(page)
} else if (result.code === 1) {
message.error(result.message)
}
}
const handleClearClose = () => setShowClear(false)
return ( return (
<div class="app-history"> <>
<div class="history-clear"> <div class="app-history">
<a></a> <div class="history-clear">
</div> <button onClick={handleShowModal} title="清空历史">
{historyList.map((item: IHistoryItem) => <svg class="icon" viewBox="0 0 24 24" fill="#616569ff" width="16" height="16">
<div key={item.id} class="history-card"> <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
<div class="card-left"> </svg>
<img src="../assets/images/0012.jpg" /> </button>
</div> </div>
<div class="card-right"> {historyList.map((item: HistoryItem) =>
<div class="right-top"> <div key={item.id} class="history-card">
<h3>{item.name}</h3> <div class="card-left">
<div class="card-actions"> <img src="/images/0012.jpg" alt="" />
<span>{formatDate(item.date)}</span> </div>
<button onClick={() => handleDelete(item.id)} title="删除"> <div class="card-right">
× <div class="right-top">
</button> <h3>{item.name}</h3>
<div class="card-actions">
<span>{formatDate(item.date)}</span>
<button onClick={() => handleShowDelete(item.id)} title="删除">
×
</button>
</div>
</div>
<div class="right-overview">
{item.brief}
</div> </div>
</div> </div>
<div class="right-overview"> </div>)
{item.brief} }
</div> <div class="history-pagination">
</div> <Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
</div>) </div>
}
<div class="history-pagination">
<Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
</div> </div>
</div> <Modal open={showDelete} title="信息" onClick={handleDeleteOk} onClose={handleDeleteClose}>
<p></p>
</Modal>
<Modal open={showClear} title="信息" onClick={handleClearOk} onClose={handleClearClose}>
<p></p>
</Modal>
</>
) )
} }
+9 -19
View File
@@ -1,24 +1,18 @@
import { useState, useRef } from 'preact/hooks' import { useState, useRef } from 'preact/hooks'
import { message } from '../utils/toast' import { message } from '../utils/toast'
import type { DetectResult } from '../preact'
interface IDetectResult {
id: number
code: string
name: number
brief: string
confidence_level: number
}
const Main = () => { const Main = () => {
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const [fileName, setFileName] = useState<string>('')
const [fileSrc, setFileSrc] = useState<string>('') const [fileSrc, setFileSrc] = useState<string>('')
const [step, setStep] = useState<number>(0) const [step, setStep] = useState<number>(0)
const [detectResult, setDetectResult] = useState<IDetectResult | null>(null) const [detectResult, setDetectResult] = useState<DetectResult | null>(null)
const handleUploadClick = () => fileInputRef.current?.click() const handleUploadClick = () => fileInputRef.current?.click()
const handleFileChange = (e: Event) => { const handleFileChange = (e: Event) => {
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) {
@@ -31,15 +25,11 @@ const Main = () => {
Array.from(uint8Array), Array.from(uint8Array),
file.name file.name
) )
if (result.code === 0) { if (result.code === 0) {
setFileName(result.data) setFileSrc(result.data)
setStep(1)
const imgResult = await (window as any).go.main.App.GetImage(result.data) } else if (result.code === 1) {
if (imgResult.code === 0) { message.error(result.message)
setFileSrc(imgResult.data)
setStep(1)
}
} }
} }
reader.readAsArrayBuffer(file) reader.readAsArrayBuffer(file)
@@ -63,7 +53,7 @@ const Main = () => {
setStep(2) setStep(2)
setTimeout(async() => { setTimeout(async() => {
const result = await (window as any).go.main.App.Detect(fileName) const result = await (window as any).go.main.App.Detect(fileSrc)
if (result.code === 0) { if (result.code === 0) {
setDetectResult(result.data) setDetectResult(result.data)
setStep(3) setStep(3)
@@ -104,7 +94,7 @@ const Main = () => {
> >
{fileSrc.length > 0 && step == 1 ? {fileSrc.length > 0 && step == 1 ?
<div id="previewContent"> <div id="previewContent">
<img src={fileSrc} id="previewImage" /> <img src={`/images/${fileSrc}`} id="previewImage" />
<button onClick={handleRemovePhoto}> <button onClick={handleRemovePhoto}>
</button> </button>
+41
View File
@@ -0,0 +1,41 @@
import type { ModalProps } from '../preact'
const Modal = ({ open, title, onClick, onClose, children }: ModalProps) => {
const handleClose = () => {
onClose?.()
}
const handleConfirm = () => {
onClick?.()
handleClose()
}
const handleMaskClick = (e: MouseEvent) => {
if (e.target === e.currentTarget) {
handleClose()
}
}
return (
<>
{open ?
<>
<div class="app-modal" onClick={handleMaskClick}>
<div class="title">
<h3>{title}</h3>
<span onClick={handleClose}>×</span>
</div>
{children}
<div class="bottom">
<button onClick={handleClose}></button>
<button onClick={handleConfirm}></button>
</div>
</div>
<div class="app-mask" onClick={handleMaskClick}></div>
</> : null
}
</>
)
}
export default Modal
+2 -7
View File
@@ -1,18 +1,13 @@
import { useState, useEffect } from 'preact/hooks' import { useState, useEffect } from 'preact/hooks'
import type { PaginationProps } from '../preact'
interface IPagination {
total: number
onChange: (page: number) => void
page: number
pagesize: number
}
const Pagination = ({ const Pagination = ({
total, total,
onChange, onChange,
page, page,
pagesize, pagesize,
}: IPagination) => { }: PaginationProps) => {
const [value, setValue] = useState<string>('1') const [value, setValue] = useState<string>('1')
const [num, setNum] = useState<number>(1) const [num, setNum] = useState<number>(1)
const [seqNums, setSeqNums] = useState<[number, number][]>([]) const [seqNums, setSeqNums] = useState<[number, number][]>([])
+31
View File
@@ -0,0 +1,31 @@
export interface HistoryItem {
id: number
img: string
breed: number
date: number
name: string
brief: string
}
export interface DetectResult {
id: number
code: string
name: number
brief: string
confidence_level: number
}
export interface PaginationProps {
total: number
onChange: (page: number) => void
page: number
pagesize: number
}
interface ModalProps {
open: boolean
title: string
onClick?: () => void
onClose?: () => void
children?: preact.ComponentChildren
}
+66
View File
@@ -387,6 +387,72 @@ body
font-size: 0.8rem font-size: 0.8rem
margin: 0 0.5rem margin: 0 0.5rem
.app-modal
width: 20rem
padding: 0.75rem 1.25rem
position: fixed
top: 14rem
left: calc(50% - 10rem)
background-color: white
border-radius: 0.5rem
z-index: 1002
.title
display: flex
justify-content: space-between
align-items: center
> h3
text-align: left
color: var(--title)
font-size: 1rem
font-weight: bold
> span
color: #9ca3af
font-size: 1.25rem
cursor: pointer
> p
padding: 1rem 0rem
margin-bottom: 0.5rem
color: var(--text)
font-size: 0.9375rem
text-align: left
.bottom
display: flex
justify-content: flex-end
margin-bottom: 0.25rem
gap: 0.5rem
> button
width: 4.5rem
height: 1.875rem
font-size: 0.875rem
border-radius: 0.25rem
&:first-child
color: var(--text)
border: 1px solid #c9c9c9
background-color: none
&:last-child
color: white
background-color: var(--primary)
.app-mask
width: 100%
height: 100vh
position: fixed
top: 0
left: 0
background-color: black
opacity: 0.5
z-index: 1001
.message .message
width: 100% width: 100%
height: 2rem height: 2rem
+29 -9
View File
@@ -3,18 +3,32 @@
flex-direction: column flex-direction: column
align-items: center align-items: center
height: 651px height: 651px
padding: 6.5rem 1rem 4rem 1rem padding: 7.25rem 1rem 4rem 1rem
.history-clear .history-clear
width: calc(100% - 20rem) display: flex
text-align: right justify-content: flex-end
align-items: center
margin-bottom: 0.5rem margin-bottom: 0.5rem
position: absolute
top: 19rem
right: 1rem
z-index: 1000
> a > button
font-size: 0.875rem display: flex
color: var(--primary) justify-content: center
align-items: center
padding: 0.75rem
background-color: white
backdrop-filter: blur(8px)
border-radius: 0.375rem
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)
border: 1px solid transparent
cursor: pointer cursor: pointer
// text-decoration: underline
> svg
cursor: pointer
.history-card .history-card
display: flex display: flex
@@ -30,10 +44,17 @@
border: 1px solid var(--border) border: 1px solid var(--border)
.card-left .card-left
display: flex
justify-content: center
align-items: center
width: 3rem width: 3rem
height: 3rem height: 3rem
background-color: #333333 background-color: #333333
> img
max-width: 100%
max-height: 100%
.card-right .card-right
display: flex display: flex
flex-direction: column flex-direction: column
@@ -74,7 +95,6 @@
padding: 0 padding: 0
background: none background: none
border: none border: none
border-radius: 0.35rem
color: #9ca3af color: #9ca3af
cursor: pointer cursor: pointer
@@ -90,6 +110,6 @@
.history-pagination .history-pagination
width: calc(100% - 20rem) width: calc(100% - 20rem)
margin-top: 0.5rem margin-top: 0.6rem
+2
View File
@@ -3,6 +3,8 @@
import {main} from '../models'; import {main} from '../models';
import {gorm} from '../models'; import {gorm} from '../models';
export function ClearHistory():Promise<main.Response>;
export function DeleteOneHistory(arg1:number):Promise<main.Response>; export function DeleteOneHistory(arg1:number):Promise<main.Response>;
export function Detect(arg1:string):Promise<main.Response>; export function Detect(arg1:string):Promise<main.Response>;
+4
View File
@@ -2,6 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function ClearHistory() {
return window['go']['main']['App']['ClearHistory']();
}
export function DeleteOneHistory(arg1) { export function DeleteOneHistory(arg1) {
return window['go']['main']['App']['DeleteOneHistory'](arg1); return window['go']['main']['App']['DeleteOneHistory'](arg1);
} }