frontend finishded
This commit is contained in:
@@ -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.
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +1,9 @@
|
||||
import { useState, useEffect } from 'preact/hooks'
|
||||
import { message } from '../utils/toast'
|
||||
import Pagination from './Pagination'
|
||||
import Modal from './Modal'
|
||||
import type { HistoryItem } from '../preact'
|
||||
|
||||
|
||||
const formatDate = (timestamp: number) => {
|
||||
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 [page, setPage] = useState<number>(1)
|
||||
const pageSize = 5
|
||||
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(() => {
|
||||
if (!(window as any).go?.main?.App?.GetHistory) {
|
||||
@@ -52,46 +49,90 @@ const History = () => {
|
||||
fetchData(page)
|
||||
}
|
||||
|
||||
const handleDelete = async(id: number) => {
|
||||
const result = await (window as any).go.main.App.DeleteOneHistory(id)
|
||||
const handleShowDelete = (id: number) => {
|
||||
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) {
|
||||
message.success('删除成功')
|
||||
setCurrentId(null)
|
||||
fetchData(page)
|
||||
} else if (result.code === 1) {
|
||||
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 (
|
||||
<div class="app-history">
|
||||
<div class="history-clear">
|
||||
<a>清除全部</a>
|
||||
</div>
|
||||
{historyList.map((item: IHistoryItem) =>
|
||||
<div key={item.id} class="history-card">
|
||||
<div class="card-left">
|
||||
<img src="../assets/images/0012.jpg" />
|
||||
</div>
|
||||
<div class="card-right">
|
||||
<div class="right-top">
|
||||
<h3>{item.name}</h3>
|
||||
<div class="card-actions">
|
||||
<span>{formatDate(item.date)}</span>
|
||||
<button onClick={() => handleDelete(item.id)} title="删除">
|
||||
×
|
||||
</button>
|
||||
<>
|
||||
<div class="app-history">
|
||||
<div class="history-clear">
|
||||
<button onClick={handleShowModal} title="清空历史">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="#616569ff" width="16" height="16">
|
||||
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{historyList.map((item: HistoryItem) =>
|
||||
<div key={item.id} class="history-card">
|
||||
<div class="card-left">
|
||||
<img src="/images/0012.jpg" alt="" />
|
||||
</div>
|
||||
<div class="card-right">
|
||||
<div class="right-top">
|
||||
<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 class="right-overview">
|
||||
{item.brief}
|
||||
</div>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
<div class="history-pagination">
|
||||
<Pagination page={page} pagesize={pageSize} total={total} onChange={handlePageChange} />
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
import { useState, useRef } from 'preact/hooks'
|
||||
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 fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const [fileName, setFileName] = useState<string>('')
|
||||
const [fileSrc, setFileSrc] = useState<string>('')
|
||||
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 handleFileChange = (e: Event) => {
|
||||
console.log('handleFileChange')
|
||||
const target = e.target as HTMLInputElement
|
||||
const file = target.files?.[0]
|
||||
if (file) {
|
||||
@@ -31,15 +25,11 @@ const Main = () => {
|
||||
Array.from(uint8Array),
|
||||
file.name
|
||||
)
|
||||
|
||||
if (result.code === 0) {
|
||||
setFileName(result.data)
|
||||
|
||||
const imgResult = await (window as any).go.main.App.GetImage(result.data)
|
||||
if (imgResult.code === 0) {
|
||||
setFileSrc(imgResult.data)
|
||||
setStep(1)
|
||||
}
|
||||
setFileSrc(result.data)
|
||||
setStep(1)
|
||||
} else if (result.code === 1) {
|
||||
message.error(result.message)
|
||||
}
|
||||
}
|
||||
reader.readAsArrayBuffer(file)
|
||||
@@ -63,7 +53,7 @@ const Main = () => {
|
||||
setStep(2)
|
||||
|
||||
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) {
|
||||
setDetectResult(result.data)
|
||||
setStep(3)
|
||||
@@ -104,7 +94,7 @@ const Main = () => {
|
||||
>
|
||||
{fileSrc.length > 0 && step == 1 ?
|
||||
<div id="previewContent">
|
||||
<img src={fileSrc} id="previewImage" />
|
||||
<img src={`/images/${fileSrc}`} id="previewImage" />
|
||||
<button onClick={handleRemovePhoto}>
|
||||
❌ 移除照片
|
||||
</button>
|
||||
|
||||
@@ -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
|
||||
@@ -1,18 +1,13 @@
|
||||
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 = ({
|
||||
total,
|
||||
onChange,
|
||||
page,
|
||||
pagesize,
|
||||
}: IPagination) => {
|
||||
}: PaginationProps) => {
|
||||
const [value, setValue] = useState<string>('1')
|
||||
const [num, setNum] = useState<number>(1)
|
||||
const [seqNums, setSeqNums] = useState<[number, number][]>([])
|
||||
|
||||
Vendored
+31
@@ -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
|
||||
}
|
||||
@@ -387,6 +387,72 @@ body
|
||||
font-size: 0.8rem
|
||||
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
|
||||
width: 100%
|
||||
height: 2rem
|
||||
|
||||
@@ -3,18 +3,32 @@
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
height: 651px
|
||||
padding: 6.5rem 1rem 4rem 1rem
|
||||
padding: 7.25rem 1rem 4rem 1rem
|
||||
|
||||
.history-clear
|
||||
width: calc(100% - 20rem)
|
||||
text-align: right
|
||||
display: flex
|
||||
justify-content: flex-end
|
||||
align-items: center
|
||||
margin-bottom: 0.5rem
|
||||
position: absolute
|
||||
top: 19rem
|
||||
right: 1rem
|
||||
z-index: 1000
|
||||
|
||||
> a
|
||||
font-size: 0.875rem
|
||||
color: var(--primary)
|
||||
> button
|
||||
display: flex
|
||||
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
|
||||
// text-decoration: underline
|
||||
|
||||
> svg
|
||||
cursor: pointer
|
||||
|
||||
.history-card
|
||||
display: flex
|
||||
@@ -30,10 +44,17 @@
|
||||
border: 1px solid var(--border)
|
||||
|
||||
.card-left
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
width: 3rem
|
||||
height: 3rem
|
||||
background-color: #333333
|
||||
|
||||
> img
|
||||
max-width: 100%
|
||||
max-height: 100%
|
||||
|
||||
.card-right
|
||||
display: flex
|
||||
flex-direction: column
|
||||
@@ -74,7 +95,6 @@
|
||||
padding: 0
|
||||
background: none
|
||||
border: none
|
||||
border-radius: 0.35rem
|
||||
color: #9ca3af
|
||||
cursor: pointer
|
||||
|
||||
@@ -90,6 +110,6 @@
|
||||
|
||||
.history-pagination
|
||||
width: calc(100% - 20rem)
|
||||
margin-top: 0.5rem
|
||||
margin-top: 0.6rem
|
||||
|
||||
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@
|
||||
import {main} from '../models';
|
||||
import {gorm} from '../models';
|
||||
|
||||
export function ClearHistory():Promise<main.Response>;
|
||||
|
||||
export function DeleteOneHistory(arg1:number):Promise<main.Response>;
|
||||
|
||||
export function Detect(arg1:string):Promise<main.Response>;
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function ClearHistory() {
|
||||
return window['go']['main']['App']['ClearHistory']();
|
||||
}
|
||||
|
||||
export function DeleteOneHistory(arg1) {
|
||||
return window['go']['main']['App']['DeleteOneHistory'](arg1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user